voidzero-dev / voidzero-dev/oxc-angular-compiler
fix(defer): emit lazy dependencies for components used only inside @defer blocks
Nadie ha tomado este issue todavía.
- Lenguaje dominante
- Rust
- Estrellas
- 228
- Forks
- 20
- Merge medio
- 1 d 15 h
- PR fusionados (30 d)
- 36
Descripción
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):
- Walks the component's template AST and collects every standalone import (
scope.directives,scope.pipes). - For each import, checks whether all references appear inside
DeferredBlocknodes — if so, the import is added toallDeferredDecls. - Routes those imports through
DeferBlockDepsEmitMode.PerComponent(orPerBlockfor fine-grained mode), producing lazyimport().then(...)resolvers instead of regular dependency entries. - The normal
dependenciesfactory 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
- Defer-only detection — extend the existing template walk in
crates/oxc_angular_compiler/src/component/defer_resolver.rsto flag eachimports: [...]entry that is referenced only insideDeferredBlockAST nodes. - Route to lazy emit — currently
defer_resolver.rsonly handles@Component.deferredImports. Generalize it to also accept implicitly-deferred imports from step 1. - Emit
deferBlockDependencies— wire the lazyimport().then(m => m.X)resolver into thedefineComponentemit. Use original export name (not local alias) — seenapi/angular-compiler/test/analog-compat/component.spec.ts:3284for the aliased-import case. - Default-import shape —
m.defaultrather thanm.Xfor default-imported components — covered bynapi/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(allDeferredDeclscollection) - 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
Guía de contribución
No hay ninguna guía de contribución indexada para este repositorio
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Línea de trabajo
Empieza en crates/oxc_angular_compiler/src/component/defer_resolver.rs y compara su manejo existente de explicit-deferredImports con la lógica collectDeferredSymbols de ngc en handler.ts:2344-2475. Ejecuta los casos de forma de importación de dependencias de @defer en napi/angular-compiler/test/analog-compat/component.spec.ts, incluidos los casos con alias, default-import y @switch anidados. Se considera terminado cuando los imports exclusivos de diferimiento producen deferBlockDependencies con la forma de exportación lazy correcta, mientras que las dependencias normales los excluyen.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- angular, rust, typescript
- Área
- compilers, frontend, testing-qa
- Tipo de issue
- Error
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Estado de actividad
- Tranquilo
- Claridad
- Bien especificado
- Aptitud para principiantes
- 56/100