Skip to content

fix(domxref): build Web/API index to resolve API names#672

Open
caugner wants to merge 15 commits into
mainfrom
domxref-with-index
Open

fix(domxref): build Web/API index to resolve API names#672
caugner wants to merge 15 commits into
mainfrom
domxref-with-index

Conversation

@caugner
Copy link
Copy Markdown
Contributor

@caugner caugner commented May 11, 2026

Description

Updates the domxref macro to resolve API names via an Web/API index (rather than just composing Web/API/{arg}):

  • Web/API/Window/foo can be referenced via {{domxref("Window/foo")}} or {{domxref("foo")}}
  • Web/API/BackgroundFetchManager/fetch can be referenced via {{domxref("BackgroundFetchManager/fetch")}}
  • Web/API/Background_Synchronization_API/Reference/SyncEvent can be referenced via {{domxref("SyncEvent")}}

Note: The argument of the domxref macro continues to be normalized:

  • Space is replaced by underline (" ""_")
  • Parentheses are trimmed ("()""")
  • Prototype infix is removed (".prototype."".")
  • Dots are replaced by slashes (".""/")

Motivation

Support shortcuts like {{domxref("structuredClone")}} avoiding templ-redirected-link issues.

Additional details

Build comparison yielded:

Source main branch Δ
domxref + templ-redirected-link 6,531 5,697 −834
domxref + templ-broken-link 3,645 3,634 −11
Other sources ~12,856 ~12,861 ±5
Total ~23,035 ~22,192 −843

~13% of domxref redirected/broken issues cleared; other templates unchanged.

Related issues and pull requests

Part of #683.

Build a lazy en-US index of all `Web/API/*` page slugs keyed by both
full sub-slug (e.g. `Window/structuredClone`) and leaf segment (e.g.
`structuredClone`). `domxref` now resolves inputs against this map,
using the canonical slug on hit and falling back to the previous
capitalize-first-char URL construction on miss. Ambiguous lookups emit
a `templ-ambiguous-arg` warning and pick the first candidate.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 11, 2026

8b8bdda was deployed to: https://rari-pr672.review.mdn.allizom.net/

@caugner

This comment was marked as outdated.

@caugner caugner changed the title fix(domxref): resolve API names via Web/API index fix(domxref): build Web/API index to resolve API names May 11, 2026
@caugner caugner marked this pull request as ready for review May 11, 2026 12:49
@caugner caugner requested a review from a team as a code owner May 11, 2026 12:49
@caugner caugner requested a review from LeoMcA May 11, 2026 12:49
caugner added 8 commits May 11, 2026 14:57
The test fixture previously duplicated the per-slug indexing body of
`build_index`. Adding a new alias rule in `build_index` would silently
diverge from the fixture and tests would still pass. Pull the per-slug
work into `index_one` and have both `build_index` and the test fixture
call it.
When `Response/json` and `Response/json_static` collide under the
shared `response/json` key, both have equal sort keys, so `min_by_key`
falls back to insertion order. Document that this relies on the
ascending slug order produced by `SubPagesSorter::Slug` in `build_index`.
The `_static` suffix strip is applied unconditionally to any slug
matching the suffix. Document the implicit assumption that the suffix
always denotes a static member, so future readers know the alias is
not a structural guarantee.
Surface the `Window/*` leaf shortcut and the full-path requirement for
other interfaces' members on the `domxref` template itself, so authors
discover the behavior without reading the index implementation.
The pre-existing fallback used `api.char_indices().next()` to grab the
byte offset of the first char (always 0) and then sliced `api[0..0]` to
uppercase — i.e. uppercased the empty string, then appended the full
input verbatim. The intended behavior never ran. Build the URL from the
normalized input directly.
A failed index build silently degrades every subsequent `domxref` call
to the legacy URL-construction fallback. Escalate from `warn!` to
`error!` so the failure surfaces in `--deny-warnings` builds and is
harder to miss in logs.
The function returns `&'static str` because it borrows from the
`API_NAME_INDEX` `LazyLock`, which is never dropped. Spell that out so
future readers don't wonder where the lifetime comes from.
The fixture now lists slugs in the same ascending order that
`build_index` consumes them from `get_sub_pages`, matching the
tie-break-correctness invariant documented in `resolve_from_map`.
@caugner
Copy link
Copy Markdown
Contributor Author

caugner commented May 11, 2026

Took: 15.622264285s - 0 changes in 0 of 14390 files (0.0%)

Copy link
Copy Markdown
Member

@LeoMcA LeoMcA left a comment

Choose a reason for hiding this comment

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

Overall looks good, but I've got a few questions about the details:

let indexable = sub_slug
.split_once("/Reference/")
.map(|(_, after)| after)
.unwrap_or(sub_slug);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we want to include the sub_slug in the map? Perhaps that should be inserted more explicitly, and we should wrap the insert_unique below with if let Some(...?

Ok(pages) => pages,
Err(e) => {
error!("failed to build domxref API name index: {e}");
return HashMap::new();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this panic instead? Feels like this is a pretty catastrophic error which is worthy of killing the build

Comment on lines +81 to +83
if !entry.iter().any(|v| v == &value) {
entry.push(value);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we replace the Vec here with an IndexSet or a BTreeSet (if we don't care about preserving insertion order), to avoid having to manually dedup?

Comment on lines +116 to +118
// in iteration order. Correctness then relies on `build_index` iterating
// pages in ascending slug order (`SubPagesSorter::Slug`) so the canonical
// entry is inserted before its `_static`-suffixed sibling.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we avoid this by sorting by length, or explicitly check for strings ending with static?

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