Browser-based tool that generates W3C specifications. Authors write HTML plus a <script> tag; ReSpec handles boilerplate, cross-references, bibliography, and validation. Around half of W3C standards are produced with it, so a regression here reaches a lot of documents.
Build, test, code style, and the house style for pull requests, commits and comments live in .github/copilot-instructions.md, shared with other agents rather than duplicated here. Read it when a task touches any of those; it is a path rather than an import so it does not sit in context for tasks that do not.
Contributions written with AI are welcome and have a policy: read AI_POLICY.md before opening a pull request.
src/core/— profile-agnostic modules (markdown, xref, dfn, biblio, etc.)src/w3c/— W3C-specific modules (headers, SoTD, conformance, SEO)profiles/w3c.js— entry point that wires modules togetherworker/respec-worker.js— Web Worker for syntax highlighting (built tobuilds/); inlined as a string in the bundle via Rollup'stext!plugin — not loaded as a separate module at runtimesrc/core/base-runner.js— sequential module pipeline (run()called on each module)
The build output is an IIFE (builds/respec-w3c.js). Rollup captures document.currentScript at bundle time for import.meta.url resolution.
| Module | What it does |
|---|---|
src/core/dfn.js + dfn-map.js |
Definition registration and export |
src/core/link-to-dfn.js |
Resolves <a> tags to local <dfn> elements |
src/core/xref.js + xref-db.js |
External term resolution via respec.org/xref API + IndexedDB cache |
src/core/biblio.js |
Bibliography from api.specref.org + IndexedDB cache |
src/core/inlines.js |
All inline shorthand syntax: [= =], {{ }}, [[SPEC]], [[[...]]], variables |
src/core/webidl.js |
WebIDL parsing (via webidl2 npm), semantic markup, auto-dfn |
src/core/issues-notes.js |
GitHub issue integration, issue/note/warning blocks |
src/core/markdown.js |
Optional markdown processing via marked |
src/core/dfn-panel.js |
Interactive definition panels (hover/click) |
src/core/highlight.js + worker/ |
Syntax highlighting via highlight.js in a Web Worker |
src/w3c/headers.js |
Full W3C header/SoTD generation |
src/w3c/seo.js |
Schema.org JSON-LD, canonical link (doJsonLd: true to enable) |
// Standard test setup
import {
makeRSDoc,
makeStandardOps,
errorFilters,
warningFilters,
} from "../SpecHelper.js";
const pluginErrors = errorFilters.filter("module/name");
const pluginWarnings = warningFilters.filter("module/name");
const doc = await makeRSDoc(
makeStandardOps({ specStatus: "WD", group: "webapps" }, body)
);
const errors = pluginErrors(doc);
const warnings = pluginWarnings(doc);These patterns apply equally to ReSpec code:
catch (error)must narrowunknownwithinstanceof Errorbefore accessing.messagetoThrow()can't match messages on raw string throws — use try/catch- Env var save/restore in tests: reset per-test in
beforeEach, not shared across specs setIntervalfor async work risks overlap — use self-schedulingsetTimeoutwith in-flight guard- Mock objects must match the real interface — positional args vs options object
After any rebase with conflicts, verify the working tree matches expectations:
lsnew files to confirm they exist (rebase can silently drop additions)grepfor key additions (imports, function calls) in conflict-resolved files- Run
pnpm lintbefore pushing — never assume the rebase was clean
Almost always the branch being behind main. When the conflict is under builds/, do not resolve it: take main's bundles and rebuild if you need them, which is faster and cheaper than reading a diff of generated code.
git rebase main- For any conflict under
builds/:git checkout origin/main -- builds/ && git add builds/, thenpnpm build:w3conly if you need to run the integration suite locally git diff main...HEAD --name-only | xargs pnpm format --write— three dots, so it lists only what this branch changed. With one dot it also lists everything main changed, and on a stale branch that reformats files you never touched.
Never commit builds/: CI rebuilds it, and a dedicated check fails a pull request that touches it.
Patterns a reviewer will otherwise catch:
Null safety: Every querySelector(), closest(), getElementById(), getAttribute() result must be checked before use. Never use ?? "" on DOM queries (creates empty text nodes). Guard append() calls against null.
Template literals: No JSDoc inside html template literals. Wrap <tag> references in backticks in error messages so they display as code, not HTML.
JSDoc: @param before @returns, parameter order matches signature.
URLs: Use encodeURIComponent() for user values in URLs. Pin CDN versions (exception: web-features/data.json is intentionally unversioned to reflect current browser support).
Accessibility: Visual indicators need aria-label or equivalent. Don't rely on title alone.
Tests: Use local fixtures, never remote endpoints. Export behaviors need getExportedDoc() tests. Test names must match what the test body actually exercises.
DOM node reuse: html tagged template literals create DOM nodes. A DOM node can only exist in one place. If an SVG/element is used in a loop (e.g., browser icons), use a factory function (() => html\...``) instead of a shared constant.
Save/export data flow: When removeOnSave or beforesave rewrites content, ensure the rewritten HTML preserves feature-specific URLs and context, not just a generic fallback link.
Unused properties: Don't declare properties in constants (e.g., label in a Map entry) that are never read. Copilot flags these consistently.