Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions scripts/check-web.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,30 @@ import { readFileSync } from "node:fs";

const clean = readFileSync(new URL("web-clean.txt", import.meta.url), "utf8")
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
const tsc = "node_modules/typescript/bin/tsc";
const run = spawnSync("node", [tsc, "--noEmit", "-p", "tsconfig.web.json"], { encoding: "utf8" });
const run = spawnSync("node", [tsc, "--noEmit", "-p", "tsconfig.web.json"], {
encoding: "utf8",
// Fits the whole list, 328 KB today: spawnSync's own 1 MiB truncates the output and reports no status.
maxBuffer: 64 * 1024 * 1024,
});
const findings = (run.stdout + run.stderr).trim();
const reported = findings.split("\n");

if (findings) {
console.log(findings);
}

const broken = findings.split("\n").filter((line) => clean.some((path) => line.startsWith(path)));
const broken = reported.filter((line) => clean.some((path) => line.startsWith(path)));
// A configuration error exits 2 as well and names the config, not a file under web/, so error lines that are not
// all on web/ mean the run read no config it could check. `process.exitCode`, not `exit`: an exit drops the pipe.
const config = reported.filter((line) => line.includes("error TS") && !line.startsWith("web/"));

if (broken.length) {
console.log(`\nthe gate holds these clean:\n${broken.join("\n")}`);
process.exit(1);
}

if (run.status !== 0 && run.status !== 2) {
process.exit(1);
if (broken.length || config.length || (run.status !== 0 && run.status !== 2)) {
process.exitCode = 1;
}
8 changes: 6 additions & 2 deletions test/fence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ test("a closing tag loses its <, whatever stands between it and the /", () => {
test("a closing tag loses its < behind every category of invisible character the class names", () => {
// One representative per category, 81 of them, so the 80-character branch cannot defang it and only the class
// can: a control character, a format character, a lone surrogate, a private-use character, an unassigned code
// point, a variation selector supplement, the braille blank, the Hangul fillers, and the combining grapheme
// joiner. Narrowing the class fails here.
// point, a variation selector and its supplement, the braille blank, three Hangul fillers, the combining
// grapheme joiner, a Khmer inherent vowel, and a Mongolian free variation selector. Narrowing the class fails.
const hides = (character: string, what: string) => {
const invisible = character.repeat(81);

Expand All @@ -40,6 +40,10 @@ test("a closing tag loses its < behind every category of invisible character the
hides("\u3164", "the Hangul filler");
hides("\uFFA0", "the halfwidth Hangul filler");
hides("\u034F", "the combining grapheme joiner");
hides("\uFE00", "a variation selector");
hides("\u115F", "the Hangul choseong filler");
hides("\u17B4", "a Khmer inherent vowel");
hides("\u180B", "a Mongolian free variation selector");
});

test("a closing tag with more than 80 characters between < and / is defanged only by the invisible run", () => {
Expand Down
Loading