- 旧哈希静态资源随版本升级被删除,被缓存的 HTML 仍请求旧文件名导致 FileNotFoundException 500,补 compat/assets 历史资源并在构建时合并 - postbuild 增加静态资源引用校验,缺失即构建失败 - ClickEffect 改为常驻配置节点 + data 属性驱动,避免配置未落库时静默失效; 强制回流修正 transition 不触发;特效层挂到 body 下避免被导航栏遮挡 - 新增 scripts: postbuild/snapshot-assets/package/smoke-clickfx - 版本 1.2.1
81 lines
2.8 KiB
JavaScript
81 lines
2.8 KiB
JavaScript
/**
|
||
* 构建后置处理:
|
||
* 1. 复制 logo.png 到 assets(保留历史行为)
|
||
* 2. 合并 compat/assets 下的历史版本静态资源,避免站点/代理缓存中的旧 HTML
|
||
* 仍然引用上一版本的哈希文件名时出现 500(FileNotFoundException)
|
||
* 3. 校验所有 templates/*.html 引用的静态资源真实存在,缺失即构建失败
|
||
*/
|
||
import fs from "node:fs";
|
||
import path from "node:path";
|
||
import { fileURLToPath } from "node:url";
|
||
|
||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||
const assetsDir = path.join(root, "templates", "assets");
|
||
const compatDir = path.join(root, "compat", "assets");
|
||
const templatesDir = path.join(root, "templates");
|
||
|
||
fs.mkdirSync(assetsDir, { recursive: true });
|
||
|
||
// 1. logo
|
||
const logoSrc = path.join(templatesDir, "logo.png");
|
||
if (fs.existsSync(logoSrc)) {
|
||
fs.copyFileSync(logoSrc, path.join(assetsDir, "logo.png"));
|
||
}
|
||
|
||
// 2. 历史资源合并(已存在的同名文件以当前构建为准,不覆盖)
|
||
if (fs.existsSync(compatDir)) {
|
||
let copied = 0;
|
||
let skipped = 0;
|
||
for (const entry of fs.readdirSync(compatDir, { withFileTypes: true })) {
|
||
if (!entry.isFile()) continue;
|
||
const src = path.join(compatDir, entry.name);
|
||
const dst = path.join(assetsDir, entry.name);
|
||
if (fs.existsSync(dst)) {
|
||
skipped++;
|
||
continue;
|
||
}
|
||
fs.copyFileSync(src, dst);
|
||
copied++;
|
||
}
|
||
console.log(`[compat] 历史静态资源:复制 ${copied} 个,当前构建已覆盖 ${skipped} 个`);
|
||
}
|
||
|
||
// 3. 资源引用校验
|
||
const missing = [];
|
||
const checked = new Set();
|
||
/** 主题静态资源前缀,必须与 astro.config.mjs 的 base 保持一致 */
|
||
const prefixes = ["/themes/warm-island/assets/"];
|
||
|
||
/** Thymeleaf 的 @{...} 链接表达式同样需要校验 */
|
||
const REFERENCE_RE =
|
||
/(?:href|src)=["']([^"']*?\/(?:themes\/[^/]+)\/assets\/[^"']+?)["']/g;
|
||
|
||
for (const file of fs.readdirSync(templatesDir)) {
|
||
if (!file.endsWith(".html")) continue;
|
||
const html = fs.readFileSync(path.join(templatesDir, file), "utf8");
|
||
REFERENCE_RE.lastIndex = 0;
|
||
let match;
|
||
while ((match = REFERENCE_RE.exec(html)) !== null) {
|
||
let ref = match[1];
|
||
if (ref.includes("@{")) continue; // 由 Halo 运行时解析,跳过
|
||
for (const prefix of prefixes) {
|
||
if (!ref.startsWith(prefix)) continue;
|
||
const rel = ref.slice(prefix.length).split("#")[0].split("?")[0];
|
||
const key = `${file}:${rel}`;
|
||
if (checked.has(key)) continue;
|
||
checked.add(key);
|
||
if (!fs.existsSync(path.join(assetsDir, rel))) {
|
||
missing.push(`${file} -> ${ref}`);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (missing.length > 0) {
|
||
console.error(`[verify] 发现 ${missing.length} 处静态资源缺失:`);
|
||
for (const item of missing) console.error(` - ${item}`);
|
||
process.exit(1);
|
||
}
|
||
|
||
console.log(`[verify] 静态资源引用校验通过(${checked.size} 条引用)`);
|