#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const repoRoot = process.cwd(); const pkgPath = path.join(repoRoot, 'package.json'); const publicDir = path.join(repoRoot, 'public'); if (!fs.existsSync(pkgPath) || !fs.existsSync(publicDir)) process.exit(0); const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); const version = pkg.version; if (!version) { console.error('package.json version missing'); process.exit(1); } function walkHtmlFiles(dir) { const out = []; for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { const full = path.join(dir, entry.name); if (entry.isDirectory()) out.push(...walkHtmlFiles(full)); else if (entry.isFile() && entry.name.endsWith('.html')) out.push(full); } return out; } function isLocalAsset(rawUrl) { return !/^(?:[a-z]+:|\/\/|#)/i.test(rawUrl); } function withVersion(rawUrl) { const [baseWithQuery, hash = ''] = rawUrl.split('#'); const [pathname, query = ''] = baseWithQuery.split('?'); const params = new URLSearchParams(query); params.set('v', version); const qs = params.toString(); return pathname + (qs ? '?' + qs : '') + (hash ? '#' + hash : ''); } const htmlFiles = walkHtmlFiles(publicDir); let changed = 0; for (const file of htmlFiles) { let content = fs.readFileSync(file, 'utf8'); const next = content .replace(/(]*href=["'])([^"']+\.css(?:\?[^"']*)?)(["'][^>]*>)/gi, (m, p1, url, p3) => { return isLocalAsset(url) ? p1 + withVersion(url) + p3 : m; }) .replace(/(]*type=["']application\/json["'])[^>]*\bsrc=["'])([^"']+\.js(?:\?[^"']*)?)(["'][^>]*>\s*<\/script>)/gi, (m, p1, url, p3) => { return isLocalAsset(url) ? p1 + withVersion(url) + p3 : m; }); if (next !== content) { fs.writeFileSync(file, next); changed += 1; } } if (changed > 0) console.log('Synced versioned assets in ' + changed + ' HTML file(s).');