fix(queries): compile decorator queries: and match ngtsc's query options - #495
Open
ashley-hunter wants to merge 6 commits into
Open
ashley-hunter wants to merge 6 commits into
ashley-hunter wants to merge 6 commits into
Conversation
Brooooooklyn
added this pull request to stack #503
September 30, 2026 04:46
Brooooooklyn
force-pushed
the
feat/decorator-metadata-queries
branch
2 times, most recently
from
September 30, 2026 13:11
b8fd06a to
3d138f9
Compare
Brooooooklyn
force-pushed
the
feat/decorator-metadata-queries
branch
from
September 30, 2026 15:58
3d138f9 to
bf5288b
Compare
Brooooooklyn
force-pushed
the
feat/decorator-metadata-queries
branch
3 times, most recently
from
September 30, 2026 19:07
56e4adb to
7ae9dd3
Compare
Brooooooklyn
force-pushed
the
feat/decorator-metadata-queries
branch
from
September 30, 2026 19:38
7ae9dd3 to
df67666
Compare
…tions
`@Directive({ queries: { el: new ViewChild('el') } })` (and `@Component`) was
ignored, so those queries never ran. It's now parsed like ngtsc's
`extractQueriesFromDecorator`: `new ViewChild/ViewChildren/ContentChild/
ContentChildren(...)` imported from `@angular/core` (directly or through a
namespace import), appended after the member queries, with ngtsc's diagnostics
word for word (not an object literal, not a query type, missing/extra
arguments, non-object options, non-boolean `descendants`/`static`/
`emitDistinctChangesOnly`, uninterpretable predicates, and a signal query
declared for the same property).
Member query decorators now match ngtsc too:
- `descendants` and `emitDistinctChangesOnly` are honoured on `@ViewChild`/
`@ViewChildren` (they were hard-coded), which changes the emitted flags
- a predicate referencing a same-file string const (`@ViewChild(SELECTOR)`)
compiles to that selector rather than to a type predicate
- `forwardRef(function () { return X; })`, and `forwardRef` behind parentheses
or `as`, is unwrapped like the arrow form
`extract_class_queries` returns a class's queries as they're compiled; the
compiler and the NAPI `extractComponentMetadataSync` both use it, so the
metadata API reports the same queries.
- `ns.forwardRef(...)` is only unwrapped when `ns` is a namespace import of
`@angular/core`, like ngtsc; `util.forwardRef(() => X)` on any other
object is kept as the predicate. A bare `forwardRef` is still matched by
name.
- A `queries:` option or predicate element imported from another file
(`{descendants: FLAG}`, `new ViewChild([...SELS])`) now says OXC can't
read other files, as `inputs:`/`outputs:` do, instead of ngtsc's
wrong-type error for a value it never read.
- Member decorators through a namespace (`@core.Input()`) are matched with
the file's imports passed in, not a thread-local set by the compiler, so
`extractComponentMetadataSync` reads them too (and still ignores a
namespace that isn't `@angular/core`).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…y Angular's signal queries Checked against @angular/compiler-cli 22.1.7 (probes are in the fixture): - A bare `forwardRef(() => X)` in a query predicate was unwrapped by name. Like ngtsc's `tryUnwrapForwardRef`, it's now unwrapped only when it's imported from `@angular/core`, under any alias (`forwardRef as fr`); a local `forwardRef`, one from another module or an undeclared one is kept as written. Without the file's imports (the public `extract_view_queries` / `extract_content_queries`), it's still matched by name. - "Query is declared multiple times" fired for any member calling a function named `viewChild()`, `contentChild()`, ... It now fires only for Angular's, imported from `@angular/core` by name (under any alias) or through a namespace import, like the input/output checks. Signal query members are still compiled by name, as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s:` with the source text
Member query decorators (`@ViewChild`, `@ContentChildren`, ...) now go
through the same code as `queries:` (ngtsc uses one function for both,
`extractDecoratorQueryMetadata`):
- `static`, `descendants` and `emitDistinctChangesOnly` are evaluated, so
`{emitDistinctChangesOnly: FLAG}` with `const FLAG = false` gives flag 1
like ngtsc (it was 5), and parenthesized or `as` options are read.
- A non-boolean option, options that aren't an object literal
(`@ViewChild('x', OPTS)`), a predicate that can't be interpreted, and
missing or extra arguments report ngtsc's errors, at the node ngtsc
points to. An imported option reports the other-module error.
The public `extract_*` functions without the file's imports keep reading
literals only.
The diagnostic pass read `queries:` without the source text, so a
function-expression predicate (`new ViewChild(function Token() {})`) was
compiled but also reported as "predicate cannot be interpreted". It now
reads it the way the class is compiled.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ery options
`queries: {q: new ViewChild('x' || NAME)}`, `@ViewChild('x', {static: true || FLAG})`
and `{descendants: false && FLAG}` now compile like ngtsc (the evaluator fix
below this PR); these snapshot probes pin it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ith ngtsc Query selectors, `read` and `static` options read through `typeof NS.X` or `import S = NS.X` (in `queries:` and on members) now evaluate like ngtsc, and `NS.C` in a value position stays the expression it is. Add those cases to the ngtsc snapshot. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brooooooklyn
force-pushed
the
feat/decorator-metadata-queries
branch
from
October 1, 2026 02:43
df67666 to
023efb2
Compare
Brooooooklyn
approved these changes
Oct 1, 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.
Stack (3/5): #493 inputs/outputs → #494 transform validation → #495
queries:→ #496.d.tstransform types → #504 signal API import identity. This is #495.Compiles
queries:declared in@Directive/@Componentmetadata, and matches ngtsc on a few member-query options.The bug
compiled with no view query, so
elwas never set. Thequerieskey was ignored likeinputs:was.The fix
queries:is parsed like ngtsc'sextractQueriesFromDecorator:new ViewChild/ViewChildren/ContentChild/ContentChildren(...)imported from@angular/core(directly or through a namespace import), appended after the member queries. It raises ngtsc's diagnostics word for word:descendants,staticoremitDistinctChangesOnlyviewChild/contentChild..., imported from@angular/coredirectly, with an alias or through a namespace)An option or predicate element imported from another file (
{descendants: FLAG},new ViewChild([...SELS])) gets the same message as in #493 (... depends on 'FLAG', which is imported from another module. OXC compiles one file at a time ...) instead of ngtsc's wrong-type error for a value oxc never read.Member query decorators now also match ngtsc:
@ViewChild/@ViewChildrenhonourdescendantsandemitDistinctChangesOnly. They were hard-coded before, so this changes the emitted query flags for code that sets them.@ViewChild(SELECTOR)) compiles to that selector instead of a type predicate.forwardRef(function () { return X; }), andforwardRefbehind parentheses oras, is unwrapped like the arrow form.ns.forwardRef(...)is only unwrapped whennsis a namespace import of@angular/core;util.forwardRef(() => X)is kept as the predicate. A bareforwardRefis unwrapped only when it's imported from@angular/core(aliases too), like ngtsc; the publicextract_view_queries/extract_content_queries, which don't get the file's imports, still match it by name.extract_class_queriesreturns a class's queries as they're compiled. The compiler and the NAPIextractComponentMetadataSyncboth use it, so the metadata API now reports the same queries.Member decorators through a namespace (
@core.Input(),@core.ViewChild(), ...) are now matched using the file's imports, passed in throughStringConsts(extract_input_metadata_in,extract_output_metadata_in, and the query and host variants), instead of state set by the compiler. The compiler andextractComponentMetadataSyncboth use them, so the NAPI metadata now includes@core.Input()too, and still ignores a namespace that isn't@angular/core.Member query decorators read their options through the same code as
queries:, like ngtsc'sextractDecoratorQueryMetadata:constflags, parentheses andasare read, and wrong-type or non-object options, uninterpretable predicates and wrong argument counts report ngtsc's errors. This also changes emitted flags for code that passesconstoptions. The error check readsqueries:with the source text, so a function-expression predicate no longer gets a false "cannot be interpreted" error.Tests
Added to the ngtsc snapshot: Angular's
queriesspecs (ngtsc_spec's decorator-queries errors and "should generate queries for components/directives",forwardRef, and theauthoring_queries_speccollisions), plus probes of every error, the flags, the ordering andforwardRefthrough local,@angular/coreand other namespaces. One probe is skipped: ngtsc also reports its separate check for signal query functions called outside a member initializer, which oxc doesn't implement. A separate test covers the imported-value messages. A new NAPI test coversextractComponentMetadataSync: queries (this failed before the fix), and namespaced member decorators through@angular/coreversus another module.