Skip to content

fix(compiler): keep defaults, async, rest in metadata arrow functions - #555

Merged
Brooooooklyn merged 3 commits into
mainfrom
fix/issue-512-arrow-params-async
Oct 7, 2026
Merged

Brooooooklyn merged 3 commits into
mainfrom
fix/issue-512-arrow-params-async

Conversation

@Brooooooklyn

Copy link
Copy Markdown
Member

Problem

Arrow functions copied into decorator metadata lost parameter defaults, async, and rest params:

@Input({ transform: (v: string = 'a') => v.length })   // → (v) => v.length
@Input({ transform: async (v: any) => 1 })             // → (v) => 1
providers: [{ useFactory: async (x: number = 1) => x }] // → (x) => x

This changes runtime behavior: an async factory returns a value instead of a Promise, and defaults stop applying. Root cause: convert_arrow_function_expression only read param.pattern — the default lives on FormalParameter.initializer, async on arrow.r#async, and rest on params.rest, none of which the output ArrowFunctionExpr/FnParam can represent.

Fix

Emit the source verbatim (types stripped) via the existing make_raw_source fallback when the arrow is async, has a rest param, or any param has an initializer — the same escape hatch destructured params and non-arrow function expressions already take. No output-AST changes needed; upstream's FnParam can't represent these either, it emits WrappedNodeExpr (raw source) for metadata expressions.

During review, a latent defect surfaced in the same path: extract_param_dependency passed source_text: None to convert_oxc_expression for pipe constructor @Inject(...) args, so the new fallback would have returned None there and degraded the dep to ɵɵinvalidFactoryDep. extract_pipe_metadata_in already received the source text but discarded it — it's now threaded through extract_constructor_deps → extract_param_dependency.

Verification

  • New arrow_metadata_emit_test.rs — 8 tests: input transform default/async (full + partial), useFactory async+default+rest, pipe @Inject arrow token (full + partial, asserting no invalidFactoryDep), and a guard that plain arrows still take the structured emit.
  • cargo test --all-features green (44 suites), cargo check clean both feature configs, cargo fmt clean, conformance 1264/1264.
  • Codex adversarial review flagged the pipe source_text: None path; fixed by threading it through as described above.

Fixes #512

With compilationMode: 'partial', components lost @ViewChild/
@ContentChild, signal queries, and `queries:` decorator metadata —
the declaration map simply never carried them, so the linker had
nothing to put into contentQueries/viewQuery on ɵɵdefineComponent.

Upstream builds the component definition map on top of the directive
map (partial/component.ts createComponentDefinitionMap), so the fix
reuses the directive emitter's compile_queries_array: extracted view
and content queries are threaded through PartialComponentInputs and
emitted between providers and exportAs, matching upstream field order
(directive.ts:74-80, verified against GOLDEN_PARTIAL.js).

Signal queries bump minVersion to 17.2.0, the same rule and version
the directive path already applies (directive.ts:146-149).

Fixes #513
`forwardRef(() => X)` query predicates were unwrapped at extraction and
emitted bare — for components a new divergence, for directives a
pre-existing one the compile_query comment already documented. A bare
predicate is a TDZ hazard: ɵɵngDeclareComponent evaluates at
class-definition time, before a later-declared `X` is initialized.

Track `is_forward_ref` on R3QueryMetadata (set at the three
try_unwrap_forward_ref sites: member decorators, signal queries, and
decorator `queries:`/`member_query`), and re-wrap in compile_query via
wrap_forward_ref — upstream's convertFromMaybeForwardRefExpression.
Full-mode emit is untouched: its query calls run at first render, after
class init, so the bare predicate is correct there.

Covers the P1 review finding on #554.
Arrows copied into decorator metadata lost what the output AST can't
express: `FormalParameter.initializer` was never read (defaults dropped),
`arrow.r#async` and `params.rest` were ignored. `(v: string = 'a') => v`
emitted as `(v) => v` — an async factory then returned a value, not a
Promise.

Fall back to `make_raw_source` (types-stripped verbatim) when the arrow
is async, has a rest param, or any param has an initializer — the same
fallback destructured params and non-arrow function expressions already
take.

Also thread `source_text` through `extract_pipe_metadata_in` →
`extract_constructor_deps` → `extract_param_dependency`: the pipe
constructor's `@Inject(...)` arg conversion previously passed `None`, so
the new fallback would have returned `None` there and degraded the dep
to `ɵɵinvalidFactoryDep`.

Fixes #512
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-07T06:33:48.408994Z f958f24 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Brooooooklyn
Brooooooklyn merged commit f3e505f into main Oct 7, 2026
10 checks passed
@Brooooooklyn
Brooooooklyn deleted the fix/issue-512-arrow-params-async branch October 7, 2026 07:05
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.

fix(emit): arrow functions in metadata lose default parameter values and async

1 participant