voidzero-dev / voidzero-dev/oxc-angular-compiler

fix(defer): emit lazy dependencies for components used only inside @defer blocks

Aperta
#313 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

bug rust
Lingua principale
Rust
Stelle
228
Fork
20
Merge medio
1g 15h
PR unite (30g)
36

Descrizione

Summary

Angular's @defer block is supposed to lazy-load any standalone component that appears only inside it — the user gets import().then(m => m.X) instead of an eager import at the top of the file. OXC currently emits these references through the regular dependencies factory, so @defer parses correctly but does not actually defer: the deferred component lands in the initial bundle and the lazy boundary is lost.

This is the default ngc behavior and is not opt-in via @Component.deferredImports — the recent bb2735b commit added the explicit-deferredImports path but the implicit detection is still missing.

Reference behavior

ngc at packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts:2344-2475 (collectDeferredSymbols / surrounding logic):

  1. Walks the component's template AST and collects every standalone import (scope.directives, scope.pipes).
  2. For each import, checks whether all references appear inside DeferredBlock nodes — if so, the import is added to allDeferredDecls.
  3. Routes those imports through DeferBlockDepsEmitMode.PerComponent (or PerBlock for fine-grained mode), producing lazy import().then(...) resolvers instead of regular dependency entries.
  4. The normal dependencies factory is built from the non-deferred imports only.

Test that demonstrates the gap (in napi/angular-compiler/test/analog-compat/component.spec.ts:3262):

import { Component } from '@angular/core';
import { LazyCmp } from './lazy';
@Component({
  selector: 'app-parent',
  imports: [LazyCmp],
  template: '@defer { <app-lazy/> }',
})
export class Parent {}

Expected output (matching ngc):

ɵcmp = ɵɵdefineComponent({
  ...,
  dependencies: ɵɵgetComponentDepsFactory(Parent, []),
  deferBlockDependencies: [() => import('./lazy').then(m => m.LazyCmp)],
});

Current OXC output:

ɵcmp = ɵɵdefineComponent({
  ...,
  dependencies: ɵɵgetComponentDepsFactory(Parent, [LazyCmp]),  // eager
});

Required work

  1. Defer-only detection — extend the existing template walk in crates/oxc_angular_compiler/src/component/defer_resolver.rs to flag each imports: [...] entry that is referenced only inside DeferredBlock AST nodes.
  2. Route to lazy emit — currently defer_resolver.rs only handles @Component.deferredImports. Generalize it to also accept implicitly-deferred imports from step 1.
  3. Emit deferBlockDependencies — wire the lazy import().then(m => m.X) resolver into the defineComponent emit. Use original export name (not local alias) — see napi/angular-compiler/test/analog-compat/component.spec.ts:3284 for the aliased-import case.
  4. Default-import shapem.default rather than m.X for default-imported components — covered by napi/angular-compiler/test/analog-compat/component.spec.ts:3312.

Tests

The four currently-failing tests under @defer dependency import shape in component.spec.ts (3262, 3284, 3312, plus the @defer inside @switch/@case cases at 1764-area) cover the matrix.

Reference

  • ngc impl: packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts:1312 (DeferBlockDepsEmitMode.PerComponent), :2344-2475 (allDeferredDecls collection)
  • DefersBlock AST: packages/compiler/src/render3/r3_ast.ts
  • OXC defer entry: crates/oxc_angular_compiler/src/component/defer_resolver.rs (added in #307)
  • Related upstream commit: bb2735b feat(defer): emit lazy resolver and async metadata for @Component.deferredImports (#307) — explicit-deferredImports path; this issue covers the implicit case

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia da crates/oxc_angular_compiler/src/component/defer_resolver.rs e confronta la gestione esistente di explicit-deferredImports con la logica collectDeferredSymbols di ngc in handler.ts:2344-2475. Esegui i casi relativi alla forma degli import delle dipendenze di @defer in napi/angular-compiler/test/analog-compat/component.spec.ts, inclusi i casi con alias, default-import e @switch annidati. Il lavoro è completo quando gli import esclusivamente differiti producono deferBlockDependencies con la forma corretta dell’export lazy, mentre le dipendenze normali li escludono.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
angular, rust, typescript
Ambito
compilers, frontend, testing-qa
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Tranquilla
Chiarezza
Specificata chiaramente
Idoneità per principianti
56/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.