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

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

Ouverte
#313 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

bug rust
Langage dominant
Rust
Étoiles
228
Forks
20
Merge moyen
1 j 15 h
PR mergées (30 j)
36

Description

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

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez dans crates/oxc_angular_compiler/src/component/defer_resolver.rs et comparez sa gestion existante de explicit-deferredImports avec la logique collectDeferredSymbols de ngc dans handler.ts:2344-2475. Exécutez les cas de forme d’importation des dépendances de @defer dans napi/angular-compiler/test/analog-compat/component.spec.ts, notamment les cas avec alias, default-import et @switch imbriqués. Le travail est terminé lorsque les imports uniquement différés produisent des deferBlockDependencies avec la forme d’export lazy correcte, tandis que les dépendances normales les excluent.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
angular, rust, typescript
Domaine
compilers, frontend, testing-qa
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
Calme
Clarté
Clairement spécifiée
Accessibilité débutants
56/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.