Repository navigation
✨ sphinx-codelinks: TypeScript and JavaScript comment type (moved from sphinx-codelinks#82) - #2166
Merged
Merged
Conversation
The original work of useblocks/sphinx-codelinks#82, squashed from its branch (issue/69-typescript-support) up to the 2026-08-07 merge of main: `comment_type = "ts"`, parsed with tree-sitter-typescript's TSX grammar, discovery of `.ts` and `.tsx`, scope node types for TypeScript (an exported declaration and a function-valued `const` bind), and tests. The full commit history stays reachable at useblocks/sphinx-codelinks#82.
The follow-up commits of useblocks/sphinx-codelinks#82 (2026-08-07 to 2026-08-19), squashed: the comment type covers `.mts`, `.cts`, `.js`, `.jsx`, `.mjs` and `.cjs`; the grammar is chosen per file suffix (TSX only for `.tsx`, `.jsx` and the JavaScript family, so a `<T>x` cast in a `.ts` file no longer swallows the rest of the file); legacy `<!-- ... -->` comments are captured; generated output is excluded from discovery by default for this comment type; declarative extraction fixtures for the TypeScript family; the FE_TS feature with its impl markers; docs. The full commit history stays reachable at useblocks/sphinx-codelinks#82.
The moved branch predates #2128, so its three oneline cases are re-captured as the real extraction output (data and `[warnings]` snapshot each), and the harness maps `js` like `typescript` and `tsx`. Since #2129 a block comment's `*/` is not marker text, so the cases' notes no longer warn about it. Multi-line needs work in TypeScript `//` runs and JSDoc blocks with no code change (prefix stripping goes by the comment's kind); two shared cases pin it, and the language lists say so.
…s declarations Review of useblocks/sphinx-codelinks#82: a marker above a declaration kind the scope table did not name walked on to the next unrelated function. Now: - interfaces, enums, type aliases, abstract classes, generator functions, function signatures (overloads, `declare function`), modules, namespaces and interface/abstract method signatures are scopes; - a `const`/`let`/`var`, a class field, an assignment (`module.exports = function () {}`, `Foo.prototype.bar = …`) and an anonymous default export are scopes when they hold a function, generator or class, seen through `as`, `satisfies`, `!`, `<T>` and parentheses; - `declare …` and an expression statement are searched like `export …`. The TypeScript-only rules move out of the shared traversal into two per-language tables, SCOPE_CONTAINER_TYPES and SCOPE_PREDICATES; a comment type without a SCOPE_NODE_TYPES entry has no scopes rather than C++'s. The five grammars whose comments are one `comment` node share one query, the test fixtures build their parsers through init_tree_sitter (so the TSX fixture tests the grammar analysis picks), and a test fails when a comment type misses one of the tables. scope.yaml pins every TypeScript and JavaScript association.
…ript Since #2129 a block comment's closing `*/` is not marker text, so the one-row forms useblocks/sphinx-codelinks#82 documented as unsupported now work: `/* @t, ID, impl */` and the JSX comment `{/* @t, ID, impl */}`. A legacy `<!-- … -->` comment's `-->` is not marker text either. oneline.yaml pins the three, and a `.ts` case with a `<T>x` cast pins, through production wiring, that `.ts` parses with the TypeScript grammar (under TSX every marker after the cast is lost). The feature page says what is supported now.
Documentation build overview
26 files changed ·
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2166 +/- ##
==========================================
+ Coverage 93.35% 93.37% +0.01%
==========================================
Files 140 140
Lines 20974 21059 +85
==========================================
+ Hits 19581 19664 +83
- Misses 1393 1395 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The last open finding of useblocks/sphinx-codelinks#82: with the default `@` start sequence, `@param a First operand, the augend` in a JSDoc comment became a need with the id `the augend`. As the Python docstring rule of #2129 does for Epydoc fields, a line of a JSDoc comment (opened by exactly `/**`, under `comment_type = "ts"`) whose start sequence is directly followed by a JSDoc tag ending at a space, a tab, `{`, `}` or the line's end is a `jsdoc_tag` warning and yields no need; the warning says to choose a start sequence JSDoc does not use. The tag list covers JSDoc's block tags, TSDoc's, TypeScript's and the inline tags; matching is case-sensitive. `/* */`, `//` and `/*** */` comments are untouched, and so is C++ Doxygen. The new shared `jsdoc.yaml` pins it, and `test_jsdoc_tag` the tag shape.
ubmarco
marked this pull request as ready for review
October 9, 2026 13:21
… pinned A shared case for the shape ubCode's review (useblocks/ubcode#2764) found fragile in both engines: the `*` leader before `@need-ids:`. Both ids are references; the line is neither a need nor a `jsdoc_tag` warning.
… needs) into the TypeScript branch
…ace, pinned #2168 renders multi-line needs whatever the language; the branch pinned TypeScript blocks at extraction only. `test_typescript_blocks_render` adds a `ts` project beside the fixture's `src`: a `//` run in a `.ts` file with an RST body and a JSDoc block in a `.tsx` file with a MyST body, each traced on an RST page. Each need is recorded at its directive with the body's doctype and its links, its local URL at the block's open line, and its body renders the role link and the list.
…, pinned (red) `export declare function render(): void;` is the shape tsc writes into every `.d.ts`: an `ambient_declaration` inside an `export_statement`. The scope search looks one container deep, so the marker above it binds the next function instead. The shared case `typescript_export_declare` in `scope.yaml` pins the declaration; red until the search goes one container deeper.
… and C++ reads no JSDoc, pinned (`jsdoc_more_tags` red) Two shared cases in `jsdoc.yaml`. `jsdoc_more_tags`: `@jsxImportSource react, dom` and `@modifies state, cache` in a JSDoc comment are `jsdoc_tag` warnings, not needs; red until the tag list has them. `doxygen_in_cpp_is_no_jsdoc`: under `comment_type = "cpp"` a `/** */` comment's `@see Retry policy, IMPL_RETRY, impl` line is a need, so ubCode's parity replay sees the `ts`-only gate too (until now only a Sphinx-level test held it).
…hit it Under the TypeScript grammar the `//` and `/* */` in a `.tsx` file's JSX text read as comments and become needs; under TSX they are text. Until now only a `has_error` unit test held `.tsx` → TSX, and nothing held the per-file choice in a run over several files, the `.lower()` or the `.mts`/`.cts` suffixes. - `oneline.yaml` `jsx_text_oneliner_tsx`: one need, `IMPL_TSX_LOGIN`, its scope one line, and no `FAKE_*` (shared, so ubCode's replay holds it too). - `test_a_ts_and_a_tsx_file_in_one_run_each_get_their_grammar`: one `SourceAnalyse` over `a.ts` (a marker after a `<T>` cast) and `b.tsx` (JSX text holding `// @Fake, FAKE_ID, impl`), in both orders. - `test_ts_grammar_key`: `.ts`, `.mts`, `.cts`, `.d.ts` in any case → TypeScript; `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs` → TSX.
…method signature, pinned Four of the five predicates that make a declaration a scope only when it holds a function had no case, nor had `abstract_method_signature`: removing any of them left the suite green. Five shared cases in `scope.yaml`, one association each: `var limit = 3;`, a plain class field, `count = 1;` and `export default 42;` before a function or method do not take the marker, and an abstract method signature does.
…inned A legacy `<!--` comment runs to the end of its line, and the stripping table knows `//`, `#` and `/* */` only, so a block written in `<!--` lines is never opened: no need, no warning. That silence is now written into the shared corpus: `js_html_comment_run_is_no_block` in `multiline_needs.yaml`, and the header's sentence on runs says `"<!--" comments hold no block`.
`test_default_exclude_skips_generated_output` compared the configuration with the constant it was built from, so no change to the constant could fail it, and the test tree held only `dist/` and `node_modules/`. The tree now has `build/`, `out/`, `coverage/` and a declaration file `types/x.d.ts`, and the test asserts what discovery lists: the five output directories absent, `lib/` and `types/x.d.ts` present. A CLI test passes `-e '**/*.test.ts'` under `--comment-type ts`: the test file is dropped and `dist/app.js` is listed, as an explicit list replaces the default.
The scope search looked one container deep: past `export_statement` but not into the `ambient_declaration` inside it, the shape tsc writes into every `.d.ts`. So `export declare function render(): void;` had no scope, and in a `.ts` its marker bound the next function. When a container's child is itself a container other than `block`, its children are searched too. `typescript_export_declare` turns green; nothing else moves.
…join the shared tag list `@modifies state, cache` in a JSDoc comment became a need with the id `cache`: `modifies` is a JSDoc block tag the list lacked, as were TypeScript's JSX pragmas `jsx`, `jsxFrag`, `jsxImportSource` and `jsxRuntime`. The list, shared with ubCode, now has 112 names. `jsdoc_more_tags` turns green.
…ngelog line
Three test docstrings named findings of the old repository's review ("Regression guard for D2", "the D1 defect") that no reader here can resolve; they now say what each test pins. The `IMPL_TS_3` source marker sat above the `TYPE_SCRIPT_QUERY` constant and bound forward to `ts_grammar_key`; it moves into the TypeScript branch of `init_tree_sitter`, beside the Go and Bash ones. The multi-line needs bullet of the changelog loses its 106-character line.
- `exclude`'s default: `build/` and `out/` are excluded wherever they occur, so a hand-written `src/build/` is not analysed; the five names are all it knows, so a `tsc` `declarationDir` or `outDir` elsewhere is the project's to add; and a `:directory:` scope matches the patterns relative to its own directory. The changelog says "any directory of those five names, wherever it sits". - `discover.rst`'s TypeScript example no longer sets an `exclude` that silently replaced the default, and says why it leaves it out. - The suggested start sequence is `[[`: `@need` is the multi-line open word, and a configuration error once multi-line needs are on. `@` also starts `// @ts-ignore`, `// @ts-expect-error` and the decorators inside an `@example`. - `<!--`: legacy and one line long, valid only in classic scripts (TypeScript and ES modules reject it), read as a one-line comment, never holding a multi-line need. - Both tag bullets name the characters where ubCode's notion of alphanumeric differs: combining marks, circled letters, and letters newer than the interpreter's Unicode tables. - `FE_TS` covers multi-line annotations too.
…een read
Every JSDoc tag line was a `jsdoc_tag` warning, comma or not: one ordinary eight-line JSDoc comment with no comma anywhere gave six warnings, enough to fail a `-W` build, while the protection only matters where a line would otherwise have become a need. As `docstring_tag` was narrowed for Python, a line of a JSDoc comment is now a `jsdoc_tag` warning, and never a need, when all hold: the one-line start sequence is `@`, JSDoc's own sigil (under any other, the check does not run and `[[param a, b]]` is the marker its author wrote); nothing alphanumeric precedes the `@`; a tag of the shared list directly follows it and ends at a space, a tab, `{`, `}` or the line end; and the one-line parser would otherwise have read the line as a marker or warned about it. `@param a the augend` and `@returns the sum` stay silent.
The rule is written once, in `jsdoc.yaml`'s header, which ubCode mirrors. `jsdoc_tags` and `jsdoc_tag_end` lose the warnings of their comma-less tag lines; `jsdoc_with_other_start_sequence` gains ` * [[param a, b]]`, a need. The changelog, `oneline.rst` and the configuration note state the rule and its consequence.
`export declare const handle: () => void;` is what tsc writes into a `.d.ts` for a function-valued const, its JSDoc copied along. An ambient variable holds no value, so the plain-const rule (a `const` is a scope only when it holds a function) skips it, and since the search reaches nested containers its marker binds the next declaration. The shared case `typescript_declare_const_binds` in `scope.yaml` pins the const; red until an ambient variable counts as a scope.
…inned Under the default style "the one-line parser would read the line" and "the line holds a comma" coincide, so a check on the comma, or on markers alone, passed every case. `jsdoc_tag_asks_the_one_line_parser` in `jsdoc.yaml` sets `;` as the field separator: `@param a; b` warns, `@param a, b` stays silent, and `@param a; b; c; d; e` (too many fields, a parser warning) warns too.
`jsdoc_more_tags` held `@jsxImportSource` and `@modifies` only: removing `jsx`, `jsxFrag` or `jsxRuntime` from the list left every case green, so a typo in either tool would never show in the parity replay. The case gains `@jsx h, x`, `@jsxFrag Fragment, x` and `@jsxRuntime automatic, x`, each a `jsdoc_tag` warning.
A `const`/`let`/`var` declaration is a scope only when it holds a function, so that a plain `const flag = true;` does not take the marker of the function after it. An ambient one (`declare const handle: () => void;`, what tsc writes into a `.d.ts` for a function-valued const) holds no value and is itself the declared thing, so it is now a scope whatever its type. `typescript_declare_const_binds` turns green; nothing else moves.
- "choose a start sequence other than `@` … and no JSDoc line is ever read" said the opposite of the next sentence: under `[[` a JSDoc line is read, as a marker where it holds `[[`. The three copies (changelog, `oneline.rst`, the configuration note) now say the check never runs. - A `declarationDir` duplicates every marker a JSDoc comment holds; an `outDir` duplicates every marker. - The `TYPE_SCRIPT_QUERY` comment: `<!--` comments are legacy one-line comments, valid in classic scripts only. - `jsdoc.yaml`'s header says "opened by exactly `/**`" once.
…row ends a `//` run - `typescript_declare_const_binds` held `export declare const` only, so the arm could narrow to `const`/`let` with every case green. It gains `export declare let fn: (a: number) => void;` and `declare var version: string;`, each binding its own marker. Its comment says what the arm covers: a `declare`-prefixed `const`, `let` or `var`; a variable inside a `declare namespace`, `module` or `global` body, or a bare `export const x: T;` in a `.d.ts`, still binds the next declaration. - `js_html_row_breaks_a_line_run` in `multiline_needs.yaml`, written for the reference by ubCode's review: a `<!--` row between two `//` rows is no line comment, so it ends the run, and the open line above it is `multiline_need_unterminated`.
- `_declares_function`'s docstring said "an ambient one" in general; the arm covers a `declare`-prefixed `const`, `let` or `var` only. A variable inside a `declare namespace`, `module` or `global` body, or a bare `export const x: T;` in a `.d.ts`, is no scope and binds the next declaration. - The `jsdoc_tag` bullet and the configuration note suggest `[[` or `@@`, and say that under `[[` ECMAScript's internal-slot notation in a JSDoc line is read as a marker: `[[Prototype]]` alone is a `too_few_fields` warning, `[[Prototype]], [[Extensible]]` a need.
This was referenced Oct 9, 2026
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.
What
packages/sphinx-codelinks: moves useblocks/sphinx-codelinks#82 (TypeScript and JavaScript comment type, by @arnoox) into the monorepo with the recipe from #1871, and addresses that PR's open review on top.comment_type = "ts"analyses.ts,.tsx,.mts,.cts,.js,.jsx,.mjsand.cjs. The grammar is chosen per file: TypeScript for.ts/.mts/.cts, where a<T>xcast is legal, and TSX for every other suffix, which also reads JSX. Legacy one-line<!--comments (classic scripts only) are read too. For this comment typeexcludedefaults to the usual build-output directories.The move: the branch was 24 commits, 3 of them merges of
main, and #1898 measured five conflict stops on the oldmain. So it comes across as two commits with their authors kept: Arnaud Riess's work up to the 2026-08-07 merge, and Marco Heinemann's follow-ups. Both were filtered with the import'sgit filter-reporule, which puts them on the commit the import merged, and cherry-picked onto master. The full history stays at useblocks/sphinx-codelinks#82. The conflicts against master were resolved in place:docs/changelog.rst, written for the release.docs/components/.AGENTS.mdand the@AGENTS.mdCLAUDE.mdare kept; the PR's ownCLAUDE.mdis dropped.test_analyse_oneline_needstakes the PR's case dicts with master'sget_multiline_needs.uv.lockgains only thetree-sitter-typescriptentries.Then four commits (six, the takeover counted: a 📚 commit linking the changelog entry to this pull request follows the third, and a 🧪 commit pinning a
@need-ids:line in a JSDoc block follows the fourth):<!-- -->markers.jsdoc_tagwarning, not a need.The merge of master: master moved by one commit since this branch was cut,
132607bb(#2168:src-tracerenders multi-line needs). It is merged in (023dd70b), not rebased, so the eight commits above are unchanged. Two files conflicted. Indocs/changelog.rstthe multi-line needs bullet takes master's text with this branch's language list, and the TypeScript bullet follows master's newsrc-tracebullet. Intests/data/extraction/multiline_needs.yamlmaster's four language cases and this branch's two sit side by side afterbash_hash_run. Nothing else moved: against master the diff is this branch's own (+2974/−149) plus the takeover's commits below. A//run and a JSDoc block — the two shapes the corpus pins — now render throughsrc-trace:test_typescript_blocks_render.The review round (two reviews of the takeover; every commit after the merge):
export declare …binds the declaration it holds (find_next_scopelooks one container deeper, pinned red first bytypescript_export_declare), and adeclare-prefixedconst,letorvaris a scope, as it holds no value (pinned red first bytypescript_declare_const_binds, which now holds one of each); JSDoc's@modifiesand TypeScript's JSX pragmasjsx,jsxFrag,jsxImportSourceandjsxRuntimejoin the shared tag list (112 names); the grammar choice, the scope predicates, the C++ side of thejsdoc_taggate,<!--runs anddiscover -eget the pins they lacked, each shown red under the wrong version it catches, but for the<!--pin, which records today's behaviour; the narrowed rule's question to the one-line parser (jsdoc_tag_asks_the_one_line_parser, with;as the separator) and every JSX pragma of the tag list are pinned too; the docs say what was measured (theexcludedefault's width, thediscover.rstexample,[[rather than@needas the suggested start sequence, where<!--is valid); and, in a commit of its own, thejsdoc_tagrule is narrowed (item 15).Closes #1890
Why
The review of useblocks/sphinx-codelinks#82, open at the move. The first round (five threads) was fixed on that branch before it moved.
oneline.yamltype_assertion_oneliner_typescriptnow pins it through production wiring: under TSX, the marker after the cast is lost.TS_FUNCTION_VALUE_TYPESwas too narrow. Generator functions and class expressions count, seen throughas,satisfies,!,<T>and parentheses.init_tree_sitter. The declarative cases go through production wiring, and theirtagged_scopediffers under the wrong grammar.module.exports = function () {},Foo.prototype.bar = …andexport default () => {}bind..d.tsdeclarations had no scope.declare …is searched likeexport …, so ambient declarations bind..d.tsfiles stay discovered.SCOPE_CONTAINER_TYPESandSCOPE_PREDICATES. A comment type without aSCOPE_NODE_TYPESentry has no scopes, not C++'s.commentnode shareSIMPLE_COMMENT_QUERY. TypeScript's query differs, since it also captureshtml_comment.test_every_comment_type_is_wiredfails when aCommentTypemember misses a table, rather than failing as aKeyErrorin discovery.{/* @… */}and/* @… */forms failed. They work since 🐛 sphinx-codelinks: physical marker columns, need-ref spans, and Epydoc field tags in docstrings are not needs #2129 (the closing*/is no marker text) and are pinned now. The docs no longer call them unsupported.CLAUDE.mddescribed an architecture that does not exist. Dropped in the move.jsdoc_tagwarnings, enough to fail a-Wbuild, while the same text in/* */gave none). Asdocstring_tagwas narrowed for Python, a line of a JSDoc comment is now ajsdoc_tagwarning, and never a need, only when the one-line start sequence is@, nothing alphanumeric precedes it, a listed tag directly follows it and ends at a space, a tab,{,}or the line end, and the one-line parser would otherwise have read the line as a marker or warned about it.@param a the augendstays silent;@param a the first, thingstill warns. Under[[,[[param a, b]]is a need. The rule is written once, injsdoc.yaml's header. It is its own commit, so it can be dropped.Follow-up findings on that PR:
html_commentmarkers were dropped. Fixed on that branch, and a one-row<!-- … -->now drops its-->as a block comment drops*/.@need-ids:in a JSDoc block also produced a phantom need. Fixed on master by ✨ sphinx-codelinks: attach @need-ids references to the needs they name, during the build #2049: a reference-marker line is no one-line need.excludedefault.@param a First, secondgave a need with the idsecond. Now ajsdoc_tagwarning applies, following thedocstring_tagrule of 🐛 sphinx-codelinks: physical marker columns, need-ref spans, and Epydoc field tags in docstrings are not needs #2129. A line of a JSDoc comment whose start sequence is directly followed by a JSDoc tag warns and yields no need (narrowed by the takeover to the lines the one-line parser would have read, under@: item 15). A JSDoc comment is one opened by exactly/**, undercomment_type = "ts". The tag must end at a space, a tab,{,}or the line's end. The tag list covers JSDoc's block tags, TSDoc's, TypeScript's and the inline tags; matching is case-sensitive. The warning says to choose a start sequence JSDoc does not use./* */,//and/*** */comments are untouched, and so is C++ Doxygen.Tests
scope.yaml: 23 TypeScript and JavaScript cases (30 since the takeover'stypescript_export_declare,typescript_declare_const_binds, and one case per scope predicate and for the abstract method signature), one association each, among them every declaration kind above.oneline.yaml: 7 TypeScript-family cases: the three moved ones plus the one-row block, JSX inline,<!-- -->and cast cases (8 with the takeover'sjsx_text_oneliner_tsx: JSX text is no comment, so.tsx→ TSX is pinned).multiline_needs.yaml: a//run and a JSDoc block (4 with the takeover's two<!--pins: a run of<!--lines holds no block, and a<!--row ends a//run).jsdoc.yaml(new, 6 cases; 7 with the@need-ids:case, the takeover counted; 10 with itsjsdoc_more_tags,doxygen_in_cpp_is_no_jsdocandjsdoc_tag_asks_the_one_line_parser): tag lines warn and the non-tag marker is a need. It also covers where a tag ends, case sensitivity, comments that are no JSDoc, JavaScript, and a start sequence JSDoc does not use.test_jsdoc_tag: the tag shape, the same table as ubCode's.test_every_comment_type_is_wired.test_multiline_needs_render.py::test_typescript_blocks_render(the takeover's): atsproject beside the fixture'ssrc, a//run with an RST body in a.tsfile and a JSDoc block with a MyST body in a.tsxfile, each traced on an RST page. Each need is recorded at its directive with its body's doctype and its links, its local URL is at the block's open line, its body renders the role link and the list, and the build warns nothing. Withget_multiline_needsoff for the project both needs are absent and the test fails.test_ts_grammar_key(.ts,.mts,.cts,.d.tsin any case → TypeScript; the rest → TSX);test_a_ts_and_a_tsx_file_in_one_run_each_get_their_grammar(one analysis, both orders);test_discover_cli_excludes_replace_the_ts_default; and the default-exclude test asserts what discovery lists,build/,out/,coverage/and a.d.tsincluded.poe test-codelinks1051 passed, 0 skipped, on Sphinx 9.1 and on Sphinx 7.4 / Python 3.11;poe docs-codelinksbuilds with no warning;poe import-check-codelinksimports the wheel withtree-sitter0.25.2 andtree-sitter-typescript0.23.2 resolved from PyPI.The corpus is shared with ubCode, whose parity test replays every case above (useblocks/ubcode#2764, re-vendored from this branch the same day; see ubCode below).
ubCode
The twin is useblocks/ubcode#2764 (Marco's, taken over the same day and merged onto
main). It re-vendors the shared corpus (SOURCE.md) from this branch while it is open and at this pull request's squash commit once it merges, and its parity test replays the TypeScript cases ofoneline.yaml,scope.yaml,jsdoc.yamlandmultiline_needs.yaml. Thejsdoc_tagrule (the tag list, the end class, the exactly-/**rule, and since the review round the@-only and would-have-been-read clauses) is written once, here, injsdoc.yaml's header, and ubCode implements the same text. From the re-vendor the twin also mirrors the two-level container walk (export declare …), the ambient-variable arm of the declaration predicate (adeclare-prefixedconst,letorvaris a scope) and the 112-name tag list.js_html_row_breaks_a_line_runinmultiline_needs.yamlwas written for the reference by ubCode's review of the twin.Checklist
uv run poe test-codelinks) and they pass.docs/changelog.rsthas an entry under Unreleased.uv run poe lintanduv run poe typecheckpass.