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

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

Abierto
#313 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

bug rust
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):

  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

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. 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

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.