Skip to content

feat(inlines): support [[[SPEC#id]]] for cross-spec section links#5146

Open
marcoscaceres wants to merge 5 commits intomainfrom
feat/inlines-cross-spec
Open

feat(inlines): support [[[SPEC#id]]] for cross-spec section links#5146
marcoscaceres wants to merge 5 commits intomainfrom
feat/inlines-cross-spec

Conversation

@marcoscaceres
Copy link
Copy Markdown
Contributor

Summary

  • Extends the [[[...]]] inline expansion syntax to allow [[[SPEC#fragment]]]
  • Resolves to the spec's href from the bibliography with the #fragment appended
  • Example: [[[fetch#data-fetch]]] → link to the Fetch spec's #data-fetch section, with link text taken from the spec title

Test plan

  • Run unit tests: pnpm test:unit
  • Verify [[[fetch#data-fetch]]] produces a link to https://fetch.spec.whatwg.org/#data-fetch

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends ReSpec’s [[[...]]] inline expansion parsing so authors can cite a spec and link directly to a section using a fragment (e.g., [[[fetch#data-fetch]]]), leveraging existing data-cite fragment handling.

Changes:

  • Updated the [[[...]]] inline-expansion regex to allow an optional #fragment suffix.
  • Added a unit test asserting that [[[fetch#data-fetch]]] resolves to the spec URL with the fragment and uses the spec title as link text.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/core/inlines.js Broadens inlineExpansion matching to include SPEC#fragment forms.
tests/spec/core/inlines-spec.js Adds coverage for cross-spec section linking via [[[SPEC#id]]].

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@marcoscaceres
Copy link
Copy Markdown
Contributor Author

@sidvishnoi Good point. The spec title alone isn't useful for section-level links. The intended behavior for [[[fetch#cookie-header]]] is to display the actual heading text, like "§ 3.1.1 Cookie header (Fetch Standard)".

The data for this is available in w3c/webref's headings data. I've opened speced/respec-web-services#469 to add a /xref/headings endpoint that looks up {spec, id}{title, number, href, specTitle}.

Holding this PR until the headings API is available and the inlines.js expansion can use real heading text. Will also add alias support ([[[fetch#cookie-header|cookie handling]]]).

Extends the inline expansion regex to allow [[[SPEC#fragment-id]]], e.g.
[[[fetch#data-fetch]]]. The result is a link to that spec's specific
section (href resolved via the existing data-cite machinery), with the
spec title as the link text. Existing [[[SPEC]]] and [[[#local-id]]]
syntax is unchanged.
Tighten the inlineExpansion regex to use explicit alternation:
- [[[#id]]] — in-doc fragment (starts with #, no second #)
- [[[SPEC]]] or [[[SPEC#id]]] — cross-spec (no leading #)

Previously, #?[\w-.]+(?:#[\w-.]+)? allowed [[[#foo#bar]]] to match,
which would produce an invalid URL fragment. Adds a test for this case.
[[[SPEC#id]]] now displays the actual section heading text (e.g.,
'§ 3.1.1 Cookie header') instead of just the spec title. Heading
text is fetched from POST /xref/headings/ (speced/respec-web-services#469).

Also adds alias support: [[[SPEC#id|custom text]]] uses the custom
text as link text, skipping the heading lookup.

Fallback chain: alias → heading text → spec title → raw ref.

Changes:
- inlines.js: regex updated for pipe alias, inlineRefMatches stores
  alias as data-lt
- data-cite.js: batch-fetches headings for cross-spec links, formats
  as '§ NUMBER TITLE' with spec title in a cite element
…al var instead of dataset

Self-review fixes:
- Remove unused headingElements map
- Cache toCiteDetails results to avoid calling twice per element
- Use local headingSpecTitle var instead of dataset for temp storage
- Guard heading lookup with frag check to avoid undefined key
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +187 to 189
const citeDetailsMap = new Map();
const headingQueries = [];
for (const elem of elems) {
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

headingElements is declared but never used. Please remove it (and the corresponding .set(...)) or use it for its intended purpose (e.g., deduping queries / mapping results back to elements) to avoid dead code and potential lint failures.

Copilot uses AI. Check for mistakes.
Comment on lines +192 to +196
// Collect elements that need heading text (triple-bracket cross-spec links)
if (
elem.localName === "a" &&
elem.textContent === "" &&
!elem.dataset.lt &&
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This heading-fetching branch triggers for any empty <a data-cite> with a fragment (not just [[[...]]] expansions). That changes the output semantics of existing markup like <a data-cite="fetch#foo"></a> by attempting to replace link text based on a remote headings API. Consider scoping this to the new triple-bracket feature (e.g., require data-matched-text to be present / start with [[[), or gating it behind an explicit config option.

Copilot uses AI. Check for mistakes.
Comment on lines +219 to +238
// Use heading text for cross-spec section links
let headingSpecTitle = null;
if (citeDetails.frag && elem.textContent === "") {
const spec = citeDetails.key.replace(/^[!?]/, "").toLowerCase();
const id = citeDetails.frag.replace("#", "");
const heading = headings.get(`${spec}#${id}`);
if (heading) {
const secNum = heading.number ? `§\u00A0${heading.number} ` : "§ ";
elem.textContent = `${secNum}${heading.title}`;
headingSpecTitle = heading.specTitle;
}
}
linkElem(elem, linkProps, citeDetails);
// Add spec title cite element for heading links
if (headingSpecTitle) {
const cite = document.createElement("cite");
cite.textContent = headingSpecTitle;
elem.before(cite);
cite.after(" ");
}
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a heading is found, this overwrites the anchor’s text (e.g., to § …) instead of the spec title. That conflicts with the PR description and the new test in tests/spec/core/inlines-spec.js which expects [[[fetch#data-fetch]]] to have link text Fetch Standard. Either adjust the behavior to keep spec-title link text (and just append #fragment), or update tests/docs and ensure the output is stable (doesn’t depend on network/API availability).

Suggested change
// Use heading text for cross-spec section links
let headingSpecTitle = null;
if (citeDetails.frag && elem.textContent === "") {
const spec = citeDetails.key.replace(/^[!?]/, "").toLowerCase();
const id = citeDetails.frag.replace("#", "");
const heading = headings.get(`${spec}#${id}`);
if (heading) {
const secNum = heading.number ? `§\u00A0${heading.number} ` : "§ ";
elem.textContent = `${secNum}${heading.title}`;
headingSpecTitle = heading.specTitle;
}
}
linkElem(elem, linkProps, citeDetails);
// Add spec title cite element for heading links
if (headingSpecTitle) {
const cite = document.createElement("cite");
cite.textContent = headingSpecTitle;
elem.before(cite);
cite.after(" ");
}
linkElem(elem, linkProps, citeDetails);

Copilot uses AI. Check for mistakes.
Comment on lines +235 to 239
cite.textContent = headingSpecTitle;
elem.before(cite);
cite.after(" ");
}
} else {
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inserting a separate <cite> node as a sibling before the anchor changes the DOM shape of inline expansions (from a single link to two nodes). If the intended output is just a link to SPEC#fragment (per PR description), this should be removed; otherwise, the expected structure should be documented and covered by tests since it can affect styling/screen-reader output.

Copilot uses AI. Check for mistakes.
Comment on lines +287 to +290
it("links to specific section of another spec using [[[SPEC#id]]] syntax", async () => {
const config = {
localBiblio: {
fetch: {
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New parsing supports [[[...|text]]] (including [[[SPEC#id|text]]]), but there’s no test coverage confirming that the custom link text is preserved for these triple-bracket expansions. Add at least one assertion for [[[fetch#data-fetch|Custom]]] (and/or [[[fetch|Custom]]]) so regressions in data-lt handling are caught.

Copilot uses AI. Check for mistakes.
…syntax

Tests for:
- Cross-spec alias: [[[fetch#data-fetch|fetching data]]] uses custom text
- Cross-spec without alias: falls back to spec title (headings API not deployed)
- In-document alias: [[[#section|see this]]] uses custom text
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants