From 2f74624f8c47f7c0998a90e409912bfd59c302bc Mon Sep 17 00:00:00 2001 From: Marcos Caceres Date: Mon, 4 May 2026 20:48:28 +1000 Subject: [PATCH 1/4] fix: unify noToc/noTOC config to canonical noTOC The config property was split: style movers used `noToc` (lowercase c) while structure.js used `noTOC` (uppercase C). A spec author setting one would not affect the other. Canonical name is `noTOC` (matching the documentation and the acronym convention used by `edDraftURI`). The old `noToc` name is normalized to `noTOC` in each profile's defaults module for backwards compatibility. Closes #5258 --- src/aom/defaults.js | 1 + src/aom/style.js | 2 +- src/dini/defaults.js | 1 + src/dini/style.js | 2 +- src/geonovum/defaults.js | 2 + src/geonovum/style.js | 2 +- src/type-helper.d.ts | 84 +++++++++++++++++++----------- src/w3c/defaults.js | 1 + src/w3c/style.js | 2 +- tests/spec/aom/style-spec.js | 4 +- tests/spec/core/id-headers-spec.js | 2 +- tests/spec/geonovum/style-spec.js | 4 +- tests/spec/w3c/style-spec.js | 4 +- 13 files changed, 70 insertions(+), 41 deletions(-) diff --git a/src/aom/defaults.js b/src/aom/defaults.js index 4468ed6d94..4f9598598a 100644 --- a/src/aom/defaults.js +++ b/src/aom/defaults.js @@ -64,6 +64,7 @@ export function run(conf) { lint, }); + if ("noToc" in conf) conf.noTOC = conf.noToc; // computed properties Object.assign(conf, computeProps(/** @type {NormalizedConf} */ (conf))); } diff --git a/src/aom/style.js b/src/aom/style.js index 479004a483..8f73269bd4 100644 --- a/src/aom/style.js +++ b/src/aom/style.js @@ -132,7 +132,7 @@ export function run(conf) { } // Attach W3C fixup script after we are done. - if (!conf.noToc) { + if (!conf.noTOC) { sub( "end-all", () => { diff --git a/src/dini/defaults.js b/src/dini/defaults.js index 3c42c3fe5d..42316a5d0f 100644 --- a/src/dini/defaults.js +++ b/src/dini/defaults.js @@ -78,6 +78,7 @@ export function run(conf) { lint, }); + if ("noToc" in conf) conf.noTOC = conf.noToc; // computed properties Object.assign(conf, computeProps(/** @type {NormalizedConf} */ (conf))); } diff --git a/src/dini/style.js b/src/dini/style.js index e205a86d51..c499feb653 100644 --- a/src/dini/style.js +++ b/src/dini/style.js @@ -132,7 +132,7 @@ export function run(conf) { } // Attach W3C fixup script after we are done. - if (!conf.noToc) { + if (!conf.noTOC) { sub( "end-all", () => { diff --git a/src/geonovum/defaults.js b/src/geonovum/defaults.js index 6e16e6fa4b..3eccdc4a03 100644 --- a/src/geonovum/defaults.js +++ b/src/geonovum/defaults.js @@ -83,5 +83,7 @@ export function run(conf) { lint, }); // computed properties + + if ("noToc" in conf) conf.noTOC = conf.noToc; Object.assign(conf, computeProps(/** @type {NormalizedConf} */ (conf))); } diff --git a/src/geonovum/style.js b/src/geonovum/style.js index caf66e142a..2a9a937fae 100644 --- a/src/geonovum/style.js +++ b/src/geonovum/style.js @@ -152,7 +152,7 @@ export function run(conf) { styleFile = "base.css"; } - if (!conf.noToc) { + if (!conf.noTOC) { sub( "end-all", () => { diff --git a/src/type-helper.d.ts b/src/type-helper.d.ts index d7f3cd1c66..7b79232eaf 100644 --- a/src/type-helper.d.ts +++ b/src/type-helper.d.ts @@ -97,9 +97,13 @@ interface StoredBiblioEntry extends BiblioData { * `true`, `false`, or `"warn"` / `"error"`. * Supports string indexing so linter-rule files can look up individual rules. */ -type LintConfig = false | ({ [ruleName: string]: boolean | string }); +type LintConfig = false | { [ruleName: string]: boolean | string }; -type ProcessFn = (config: Conf, doc: Document, utils?: unknown) => Promise | void; +type ProcessFn = ( + config: Conf, + doc: Document, + utils?: unknown +) => Promise | void; /** Configuration object type */ interface Conf { @@ -119,6 +123,7 @@ interface Conf { afterEnd?: ProcessFn; specStatus?: string; wgId?: string; + /** @deprecated Use noTOC instead */ noToc?: boolean; noTOC?: boolean; /** Disables injecting ReSpec styles */ @@ -131,24 +136,26 @@ interface Conf { /** The URL of the pull request, if applicable */ prUrl?: string; /** The GitHub configuration object */ - github?: string | { - /** The URL of the GitHub repository */ - repoURL: string; - /** The default branch name */ - branch?: string; - /** Optional custom pulls URL (for monorepo scenarios) */ - pullsURL?: string; - /** Optional custom commit history URL (for monorepo scenarios) */ - commitHistoryURL?: string; - /** The API base URL */ - apiBase?: string; - /** The full name of the repo (e.g. "w3c/my-spec") */ - fullName?: string; - /** The issues URL */ - issuesURL?: string; - /** The new issues URL */ - newIssuesURL?: string; - }; + github?: + | string + | { + /** The URL of the GitHub repository */ + repoURL: string; + /** The default branch name */ + branch?: string; + /** Optional custom pulls URL (for monorepo scenarios) */ + pullsURL?: string; + /** Optional custom commit history URL (for monorepo scenarios) */ + commitHistoryURL?: string; + /** The API base URL */ + apiBase?: string; + /** The full name of the repo (e.g. "w3c/my-spec") */ + fullName?: string; + /** The issues URL */ + issuesURL?: string; + /** The new issues URL */ + newIssuesURL?: string; + }; /** The title of the document */ title?: string; @@ -349,15 +356,21 @@ interface Conf { /** Linting configuration */ lint?: LintConfig; /** caniuse.com feature configuration */ - caniuse?: string | { - feature?: string; - browsers?: string[] | Record; - maxAge?: number; - removeOnSave?: boolean; - [key: string]: unknown; - }; + caniuse?: + | string + | { + feature?: string; + browsers?: string[] | Record; + maxAge?: number; + removeOnSave?: boolean; + [key: string]: unknown; + }; /** External cross-reference configuration */ - xref?: boolean | string | string[] | { url?: string; specs?: string[]; profile?: string }; + xref?: + | boolean + | string + | string[] + | { url?: string; specs?: string[]; profile?: string }; /** Whether to include JSON-LD metadata */ doJsonLd?: boolean; /** Whether to highlight variables */ @@ -373,9 +386,20 @@ interface Conf { /** Accessibility linting configuration */ a11y?: boolean | Record; /** MDN annotation configuration */ - mdn?: boolean | string | { key?: string; specMapUrl?: string; baseJsonPath?: string; maxAge?: number }; + mdn?: + | boolean + | string + | { + key?: string; + specMapUrl?: string; + baseJsonPath?: string; + maxAge?: number; + }; /** Web Monetization configuration */ - monetization?: boolean | string | { paymentPointer?: string; removeOnSave?: boolean }; + monetization?: + | boolean + | string + | { paymentPointer?: string; removeOnSave?: boolean }; /** RFC 2119 usage tracking object, keyed by term */ respecRFC2119?: Record; /** Whether to place the SotD additional content after the WG info */ diff --git a/src/w3c/defaults.js b/src/w3c/defaults.js index 49f9e2686d..eb3de93b4c 100644 --- a/src/w3c/defaults.js +++ b/src/w3c/defaults.js @@ -75,6 +75,7 @@ export function run(conf) { lint, }); + if ("noToc" in conf) conf.noTOC = conf.noToc; if (conf.specStatus !== "unofficial" && !conf.hasOwnProperty("license")) { conf.license = "w3c-software-doc"; } diff --git a/src/w3c/style.js b/src/w3c/style.js index cb083f7a10..4119ee70ae 100644 --- a/src/w3c/style.js +++ b/src/w3c/style.js @@ -105,7 +105,7 @@ function styleMover(linkURL) { */ export function run(conf) { // Attach W3C fixup script after we are done. - if (!conf.noToc) { + if (!conf.noTOC) { sub("end-all", attachFixupScript, { once: true }); } diff --git a/tests/spec/aom/style-spec.js b/tests/spec/aom/style-spec.js index f0e96434c1..9dc7a4a342 100644 --- a/tests/spec/aom/style-spec.js +++ b/tests/spec/aom/style-spec.js @@ -52,8 +52,8 @@ describe("AOM - Style", () => { expect(elem.content).toBe(expectedStr); }); - it("doesn't include fixup.js when noToc is set", async () => { - const ops = makeStandardAomOps({ noToc: true }); + it("doesn't include fixup.js when noTOC is set", async () => { + const ops = makeStandardAomOps({ noTOC: true }); const doc = await makeRSDoc(ops); const query = "script[src^='https://www.w3.org/scripts/TR/2016/fixup.js']"; const elem = doc.querySelector(query); diff --git a/tests/spec/core/id-headers-spec.js b/tests/spec/core/id-headers-spec.js index 4da2f92a30..a6ca97b193 100644 --- a/tests/spec/core/id-headers-spec.js +++ b/tests/spec/core/id-headers-spec.js @@ -58,7 +58,7 @@ describe("Core - ID headers", () => { ariaLabel = deepAppendix.getAttribute("aria-label"); expect(ariaLabel).toBe("Permalink for Appendix A.1"); - // marked as noToc + // marked as noTOC const deepH4 = doc.querySelector("h4 + a.self-link"); ariaLabel = deepH4.getAttribute("aria-label"); expect(ariaLabel).toBe("Permalink for this Section"); diff --git a/tests/spec/geonovum/style-spec.js b/tests/spec/geonovum/style-spec.js index 20da417bac..8b8a2d590a 100644 --- a/tests/spec/geonovum/style-spec.js +++ b/tests/spec/geonovum/style-spec.js @@ -87,10 +87,10 @@ describe("Geonovum - Style", () => { ); }); - it("shouldn't include fixup.js when noToc is set", async () => { + it("shouldn't include fixup.js when noTOC is set", async () => { const ops = makeStandardGeoOps(); const newProps = { - noToc: true, + noTOC: true, }; Object.assign(ops.config, newProps); const doc = await makeRSDoc(ops); diff --git a/tests/spec/w3c/style-spec.js b/tests/spec/w3c/style-spec.js index f4d9b833f7..bf210e3f52 100644 --- a/tests/spec/w3c/style-spec.js +++ b/tests/spec/w3c/style-spec.js @@ -250,10 +250,10 @@ describe("W3C - Style", () => { expect(linkBase.nextElementSibling).toBe(linkDarkMode); }); - it("shouldn't include fixup.js when noToc is set", async () => { + it("shouldn't include fixup.js when noTOC is set", async () => { const ops = makeStandardOps(); const newProps = { - noToc: true, + noTOC: true, }; Object.assign(ops.config, newProps); const doc = await makeRSDoc(ops, "spec/core/simple.html"); From b4c05b08db938df4b7a5a3846f6651a9cf592d5f Mon Sep 17 00:00:00 2001 From: Marcos Caceres Date: Tue, 5 May 2026 12:12:58 +1000 Subject: [PATCH 2/4] Address review: guard legacy noToc normalization, add compat tests Only copy noToc into noTOC when noTOC is not already explicitly set. Add backward-compat tests verifying legacy noToc: true still suppresses fixup.js across all profiles. --- src/aom/defaults.js | 2 +- src/dini/defaults.js | 2 +- src/geonovum/defaults.js | 2 +- src/w3c/defaults.js | 2 +- tests/spec/aom/style-spec.js | 8 ++++++++ tests/spec/geonovum/style-spec.js | 9 +++++++++ tests/spec/w3c/style-spec.js | 9 +++++++++ 7 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/aom/defaults.js b/src/aom/defaults.js index 4f9598598a..39997e0fe7 100644 --- a/src/aom/defaults.js +++ b/src/aom/defaults.js @@ -64,7 +64,7 @@ export function run(conf) { lint, }); - if ("noToc" in conf) conf.noTOC = conf.noToc; + if ("noToc" in conf && !("noTOC" in conf)) conf.noTOC = conf.noToc; // computed properties Object.assign(conf, computeProps(/** @type {NormalizedConf} */ (conf))); } diff --git a/src/dini/defaults.js b/src/dini/defaults.js index 42316a5d0f..701abaad93 100644 --- a/src/dini/defaults.js +++ b/src/dini/defaults.js @@ -78,7 +78,7 @@ export function run(conf) { lint, }); - if ("noToc" in conf) conf.noTOC = conf.noToc; + if ("noToc" in conf && !("noTOC" in conf)) conf.noTOC = conf.noToc; // computed properties Object.assign(conf, computeProps(/** @type {NormalizedConf} */ (conf))); } diff --git a/src/geonovum/defaults.js b/src/geonovum/defaults.js index 3eccdc4a03..0f40eb2a44 100644 --- a/src/geonovum/defaults.js +++ b/src/geonovum/defaults.js @@ -84,6 +84,6 @@ export function run(conf) { }); // computed properties - if ("noToc" in conf) conf.noTOC = conf.noToc; + if ("noToc" in conf && !("noTOC" in conf)) conf.noTOC = conf.noToc; Object.assign(conf, computeProps(/** @type {NormalizedConf} */ (conf))); } diff --git a/src/w3c/defaults.js b/src/w3c/defaults.js index eb3de93b4c..3919077929 100644 --- a/src/w3c/defaults.js +++ b/src/w3c/defaults.js @@ -75,7 +75,7 @@ export function run(conf) { lint, }); - if ("noToc" in conf) conf.noTOC = conf.noToc; + if ("noToc" in conf && !("noTOC" in conf)) conf.noTOC = conf.noToc; if (conf.specStatus !== "unofficial" && !conf.hasOwnProperty("license")) { conf.license = "w3c-software-doc"; } diff --git a/tests/spec/aom/style-spec.js b/tests/spec/aom/style-spec.js index 9dc7a4a342..cb4ade5704 100644 --- a/tests/spec/aom/style-spec.js +++ b/tests/spec/aom/style-spec.js @@ -59,4 +59,12 @@ describe("AOM - Style", () => { const elem = doc.querySelector(query); expect(elem).toBeNull(); }); + + it("doesn't include fixup.js when legacy noToc is set", async () => { + const ops = makeStandardAomOps({ noToc: true }); + const doc = await makeRSDoc(ops); + const query = "script[src^='https://www.w3.org/scripts/TR/2016/fixup.js']"; + const elem = doc.querySelector(query); + expect(elem).toBeNull(); + }); }); diff --git a/tests/spec/geonovum/style-spec.js b/tests/spec/geonovum/style-spec.js index 8b8a2d590a..64384b6e7b 100644 --- a/tests/spec/geonovum/style-spec.js +++ b/tests/spec/geonovum/style-spec.js @@ -98,4 +98,13 @@ describe("Geonovum - Style", () => { const elem = doc.querySelector(query); expect(elem).toBeNull(); }); + + it("shouldn't include fixup.js when legacy noToc is set", async () => { + const ops = makeStandardGeoOps(); + Object.assign(ops.config, { noToc: true }); + const doc = await makeRSDoc(ops); + const query = "script[src^='https://www.w3.org/scripts/TR/2016/fixup.js']"; + const elem = doc.querySelector(query); + expect(elem).toBeNull(); + }); }); diff --git a/tests/spec/w3c/style-spec.js b/tests/spec/w3c/style-spec.js index bf210e3f52..fd19b93eda 100644 --- a/tests/spec/w3c/style-spec.js +++ b/tests/spec/w3c/style-spec.js @@ -262,6 +262,15 @@ describe("W3C - Style", () => { expect(elem).toBeNull(); }); + it("shouldn't include fixup.js when legacy noToc is set", async () => { + const ops = makeStandardOps(); + Object.assign(ops.config, { noToc: true }); + const doc = await makeRSDoc(ops, "spec/core/simple.html"); + const query = "script[src^='https://www.w3.org/scripts/TR/2021/fixup.js']"; + const elem = doc.querySelector(query); + expect(elem).toBeNull(); + }); + it("does not append empty text nodes to head when moving stylesheets on export", async () => { const ops = makeStandardOps({}); const doc = await getExportedDoc(await makeRSDoc(ops)); From a4b852404b87865e41e7a8fb7195227944b9f6d5 Mon Sep 17 00:00:00 2001 From: Marcos Caceres Date: Tue, 5 May 2026 18:59:26 +1000 Subject: [PATCH 3/4] fix(tests): remove simple.html fixture from noTOC tests The tests used simple.html which has its own embedded respecConfig that doesn't include noTOC. The external config passed via makeRSDoc doesn't override the embedded config when a custom HTML file is used. Fix by using the default makeRSDoc() without a custom file. --- tests/spec/w3c/style-spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/spec/w3c/style-spec.js b/tests/spec/w3c/style-spec.js index fd19b93eda..e09960c505 100644 --- a/tests/spec/w3c/style-spec.js +++ b/tests/spec/w3c/style-spec.js @@ -256,7 +256,7 @@ describe("W3C - Style", () => { noTOC: true, }; Object.assign(ops.config, newProps); - const doc = await makeRSDoc(ops, "spec/core/simple.html"); + const doc = await makeRSDoc(ops); const query = "script[src^='https://www.w3.org/scripts/TR/2021/fixup.js']"; const elem = doc.querySelector(query); expect(elem).toBeNull(); @@ -265,7 +265,7 @@ describe("W3C - Style", () => { it("shouldn't include fixup.js when legacy noToc is set", async () => { const ops = makeStandardOps(); Object.assign(ops.config, { noToc: true }); - const doc = await makeRSDoc(ops, "spec/core/simple.html"); + const doc = await makeRSDoc(ops); const query = "script[src^='https://www.w3.org/scripts/TR/2021/fixup.js']"; const elem = doc.querySelector(query); expect(elem).toBeNull(); From a8ec754ceedfa61cb428c5b31fbaecd4c0459c5c Mon Sep 17 00:00:00 2001 From: Marcos Caceres Date: Tue, 5 May 2026 22:57:10 +1000 Subject: [PATCH 4/4] chore(ci): build all profiles before running tests CI only built w3c and geonovum profiles, leaving AOM and DINI bundles stale from whatever was last committed. Tests for those profiles ran against outdated builds, causing false failures when source changes hadn't been reflected in the committed bundles. --- .github/workflows/pr.yml | 2 +- .github/workflows/push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 8a99e92056..6eb3723315 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -64,7 +64,7 @@ jobs: - uses: actions/setup-node@v6 with: { node-version-file: '.nvmrc', cache: pnpm } - run: pnpm i --frozen-lockfile - - run: pnpm build:w3c & pnpm build:geonovum + - run: pnpm build:w3c & pnpm build:geonovum & pnpm build:aom & pnpm build:dini - run: pnpm test:unit env: BROWSERS: ${{ matrix.browser }} diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index d1334956d6..9cafa69906 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -57,7 +57,7 @@ jobs: - uses: actions/setup-node@v6 with: { node-version-file: '.nvmrc', cache: pnpm } - run: pnpm i --frozen-lockfile - - run: pnpm build:w3c & pnpm build:geonovum + - run: pnpm build:w3c & pnpm build:geonovum & pnpm build:aom & pnpm build:dini - run: pnpm test env: BROWSERS: ChromeHeadless