fix(build): package manifest.json and drop dev files from zips
ci / test (pull_request) Successful in 2m48s

collectFiles() skipped manifest.json unconditionally, and the final zip is
produced by calling collectFiles() on the built target dir — so both
dist/*.zip shipped with no manifest.json at all and could not be installed.
The per-target manifest was written into the dir correctly, then filtered
back out on the way into the archive.

Split the exclusion list: EXCLUDED_DIRS plus DEV_ONLY_FILES (manifest.json,
AGENTS.md, README.md, package.json, package-lock.json), and only apply the
latter when reading the repo root. Reading a built target keeps its
generated manifest.

Firefox zip: 26 files/1.84 MB (no manifest) -> 23 files/1.68 MB.
Chrome zip gains its manifest.json and background.chrome.js as before.
This commit is contained in:
2026-09-28 02:23:12 +08:00
parent 989643cead
commit bd6c05d1b4
+22 -9
View File
@@ -20,18 +20,31 @@ const CHROME_BG_PARTS = [
'background.js', 'background.js',
]; ];
// Directories never copied into a build target.
const EXCLUDED_DIRS = new Set([
'.git', 'node_modules', 'dist', 'docs', 'scripts', 'test', 'web-ext-artifacts',
]);
// Repo-root files that are for contributors, not for the shipped extension.
// manifest.json is here because it is generated per target, not copied.
const DEV_ONLY_FILES = new Set([
'manifest.json', 'AGENTS.md', 'README.md', 'package.json', 'package-lock.json',
]);
// Everything to copy from ROOT into a target, relative paths. // Everything to copy from ROOT into a target, relative paths.
function collectFiles(srcDir, out = [], prefix = '') { // `excludeDev` is true when reading ROOT (skip contributor files) and false when
// reading a built target (its generated manifest.json MUST be packaged).
function collectFiles(srcDir, { dir = '', excludeDev = false, out = [] } = {}) {
for (const entry of fs.readdirSync(srcDir)) { for (const entry of fs.readdirSync(srcDir)) {
const abs = path.join(srcDir, entry); const abs = path.join(srcDir, entry);
const rel = prefix ? prefix + '/' + entry : entry; const rel = dir ? dir + '/' + entry : entry;
if (entry === '.git' || entry === 'node_modules' || entry === 'dist' || if (entry.startsWith('.') || EXCLUDED_DIRS.has(entry)) continue;
entry === 'docs' || entry === 'scripts' || entry === 'test' || if (excludeDev && DEV_ONLY_FILES.has(entry)) continue;
entry === 'manifest.json' || entry.startsWith('.')) { if (fs.statSync(abs).isDirectory()) {
continue; collectFiles(abs, { dir: rel, excludeDev, out });
} else {
out.push(rel);
} }
if (fs.statSync(abs).isDirectory()) collectFiles(abs, out, rel);
else out.push(rel);
} }
return out; return out;
} }
@@ -122,7 +135,7 @@ function buildTarget(target, version) {
fs.rmSync(dir, { recursive: true, force: true }); fs.rmSync(dir, { recursive: true, force: true });
fs.mkdirSync(dir, { recursive: true }); fs.mkdirSync(dir, { recursive: true });
const relList = collectFiles(ROOT); const relList = collectFiles(ROOT, { excludeDev: true });
copyDir(relList, dir); copyDir(relList, dir);
if (target === 'firefox') { if (target === 'firefox') {