/** * 构建后处理:给 build/js/load.m.js 加内容 hash 文件名(load.m..js), * 并把 build 下所有 HTML 里对 load.m.js 的引用一起改写为带 hash 的文件名。 * * 目的:load.m.js 在 public/ 下是逐字拷贝、文件名固定,CDN 会长期缓存, * 每次改完都要手动刷 CDN。加内容 hash 后「内容变 → 文件名变 → 新 URL」, * CDN 必然回源拉取新版本,无需再手动刷新。 * * 用法:craco build 之后执行(见 package.json 的 build/gzip 脚本)。 */ const fs = require("fs"); const path = require("path"); const crypto = require("crypto"); const buildDir = path.resolve(__dirname, "..", "build"); const loadmRel = path.join("js", "load.m.js"); const loadmAbs = path.join(buildDir, loadmRel); if (!fs.existsSync(loadmAbs)) { console.warn("[hash-loadm] 跳过:未找到 " + loadmAbs); process.exit(0); } const content = fs.readFileSync(loadmAbs); const hash = crypto.createHash("md5").update(content).digest("hex").slice(0, 10); const hashedName = "load.m." + hash + ".js"; const hashedAbs = path.join(buildDir, "js", hashedName); // 生成带 hash 的副本(保留原文件作为兜底,不影响已改写的引用) fs.copyFileSync(loadmAbs, hashedAbs); // 改写 build 下所有 HTML 对 load.m.js 的引用(兼容 /js/、./js/ 等任意前缀) let htmlCount = 0; const walk = (dir) => { for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { const p = path.join(dir, entry.name); if (entry.isDirectory()) { walk(p); } else if (entry.name.toLowerCase().endsWith(".html")) { const html = fs.readFileSync(p, "utf8"); const next = html.replace(/load\.m\.js\b/g, hashedName); if (next !== html) { fs.writeFileSync(p, next); htmlCount++; } } } }; walk(buildDir); console.log( "[hash-loadm] " + loadmRel + " -> js/" + hashedName + ",已改写 " + htmlCount + " 个 HTML" );