diff --git a/gulpfile.mjs b/gulpfile.mjs index dca06a8d1..4359467d9 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -2396,6 +2396,71 @@ gulp.task("lint-chmod", function (done) { done(); }); +gulp.task("lint-bom", async function () { + console.log("\n### Checking for UTF-8 byte order marks"); + + // Cover untracked-but-not-ignored files too, so a BOM in a freshly created + // file is caught before `git add`. + const files = execSync("git ls-files -coz --exclude-standard", { + encoding: "utf8", + maxBuffer: 1 << 28, + }) + .split("\0") + .filter(Boolean); + + // Only the first three bytes matter: a leading EF BB BF is a UTF-8 BOM. + async function hasBOM(file) { + let handle; + try { + handle = await fs.promises.open(file, "r"); + } catch { + return false; // Directory, broken symlink, removed file, etc. + } + try { + const { bytesRead, buffer } = await handle.read(Buffer.alloc(3), 0, 3, 0); + return ( + bytesRead === 3 && + buffer[0] === 0xef && + buffer[1] === 0xbb && + buffer[2] === 0xbf + ); + } finally { + await handle.close(); + } + } + + // Don't exhaust file descriptors. + const offenders = []; + for (let i = 0; i < files.length; i += 256) { + const chunk = files.slice(i, i + 256); + const flags = await Promise.all(chunk.map(hasBOM)); + chunk.forEach((file, j) => flags[j] && offenders.push(file)); + } + offenders.sort(); + + if (offenders.length === 0) { + console.log("files checked, no errors found"); + return; + } + + if (!process.argv.includes("--fix")) { + for (const file of offenders) { + console.log(` Unexpected byte order mark: ${file}`); + } + throw new Error("BOM check failed (run `gulp lint-bom --fix` to clear)."); + } + + // Strip the BOM on disk and let the user stage the change. + await Promise.all( + offenders.map(async file => { + const content = await fs.promises.readFile(file); + await fs.promises.writeFile(file, content.subarray(3)); + console.log(` removed byte order mark: ${file}`); + }) + ); + console.log(`done: ${offenders.length} file(s) updated`); +}); + gulp.task("lint", function (done) { console.log("\n### Linting JS/CSS/JSON/SVG/HTML files"); @@ -2460,7 +2525,7 @@ gulp.task("lint", function (done) { return; } - gulp.series("lint-licenses", "lint-chmod")(done); + gulp.series("lint-licenses", "lint-chmod", "lint-bom")(done); }); }); diff --git a/web/viewer-geckoview.html b/web/viewer-geckoview.html index b0033f74c..b7bb83c0e 100644 --- a/web/viewer-geckoview.html +++ b/web/viewer-geckoview.html @@ -1,4 +1,4 @@ - +