feat: implement URLPattern.generate() (tentative spec)#21
Merged
Conversation
Reverses an exec: given a component name and a mapping of named-group
values, ``generate`` emits the canonical-form URL component string that
fed back through ``exec`` would produce those same groups. Useful for
URL builders that want to round-trip via the pattern.
Algorithm is a single pass over the per-component parsed part-list
(already built at construction time). For each part:
- FIXED_TEXT with no modifier → emit the canonical value verbatim
- Any part with a modifier (?, *, +) → TypeError (not uniquely
reversible)
- Standalone full-wildcard or anonymous (numeric-named) regexp →
TypeError (no named group to substitute)
- Missing required group → TypeError
- Otherwise: encode the supplied value via the component's encoding
callback, validate the encoded value satisfies the part's own
constraint (segment-wildcard rejects path delimiters, custom regex
fullmatches), then emit prefix + encoded + suffix.
Validation regexes are compiled lazily — ``generate`` is a cold path,
so paying the construction-time cost for every pattern is not worth
the speedup on a method most callers never reach.
The implementation needed three small data additions to
``_ComponentMatcher``:
- ``parts``: the pre-parsed part list (was discarded after compile)
- ``encoder``: the per-component literal-encoding callback
- ``segment_wildcard_regex``: the ``[^<delim>]+?`` body for this
component's options
All three are populated for free during ``_compile_component`` — no
extra work on the test()/exec() hot path.
Conformance: 19 / 19 cases in the upstream WPT corpus
(``urlpattern-generate.tentative.any.js``) pass. The test driver
previously skipped these unless ``WHATWG_URLPATTERN_RUN_TENTATIVE=1``;
now runs unconditionally alongside the rest of the WPT suite.
Updated ``SPEC_DEVIATIONS.md`` to reflect that the only tentative-spec
gap is now closed.
Test count: 580 → 599 (+19).
Closes the last v0.2.0 roadmap item.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
URLPattern.generate(component, groups)— the last unimplemented WHATWG URLPattern method (tentative spec).urlpattern-generate.tentative.any.js) now pass; previously skipped behindWHATWG_URLPATTERN_RUN_TENTATIVE=1.What
generate()doesReverses an
exec(): given a component name and a mapping of named-group values, emit the canonical-form URL component string that fed back throughexec()would yield the same groups.Useful for URL builders that want to round-trip values through the same pattern they match against.
Algorithm
Single pass over the per-component parsed part-list (already built at construction time). For each part:
?,*,+) →TypeError(not uniquely reversible).TypeError(no named group to substitute into).TypeError.prefix + encoded + suffix.Per-part validators are compiled lazily —
generate()is a cold path; paying compile cost on every constructor for a method most callers never reach is not worth the speedup.Implementation impact on the hot path
Zero. Three small data fields are added to
_ComponentMatcher(parts,encoder,segment_wildcard_regex) — all populated from values that were already being computed at compile time.test()andexec()are untouched.Documentation updates
SPEC_DEVIATIONS.md— flippedgenerate()from "Not implemented; 19 cases xfail" to "Implemented; 19 / 19 WPT cases pass".README.md— auxiliary-suites badge84/84 → 103/103; tentative-API badgetracked → implemented; WPT runners table now shows19 / 19for the generate suite; API surface table marksgenerate()as ✓ Implemented; tentative-not-implemented legend entry removed.docs/wpt-compliance.md— regenerated. All 19 generate cases flip from skip to pass. Footer legend simplified.scripts/generate_compliance_report.py—_run_generate_casenow actually exercises the implementation (was hard-coded toskip).tests/test_wpt_generate.py— dropped theWHATWG_URLPATTERN_RUN_TENTATIVE=1env-var gate; suite runs by default.Test plan
uv run pytest tests/test_wpt_generate.py -v— 19 / 19 passuv run pytest -q— 599 passed, 0 skipped (was 580 + 19 skipped)just lint— all green (ruff, mypy, pyright, ty, semgrep, shellcheck, rumdl, codespell, interrogate, validate-pyproject)just compliance-report— regeneratesdocs/wpt-compliance.mdas469 cases, 0 failingCloses the v0.2.0 roadmap.