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

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

オープン
#313 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

bug rust
主要言語
Rust
スター
228
フォーク
20
平均マージ
1日 15時間
マージ済み PR(30日)
36

説明

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

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

crates/oxc_angular_compiler/src/component/defer_resolver.rs から始め、そこに既存する explicit-deferredImports の処理を、handler.ts:2344-2475 にある ngc の collectDeferredSymbols ロジックと比較します。napi/angular-compiler/test/analog-compat/component.spec.ts にある @defer の依存関係インポート形式のケースを、エイリアス、default-import、ネストされた @switch のケースを含めて実行します。完了条件は、遅延専用のインポートが正しい lazy export 形式の deferBlockDependencies を生成し、通常の依存関係からはそれらが除外されることです。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
angular, rust, typescript
領域
compilers, frontend, testing-qa
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
静か
明瞭さ
明確に書かれている
初心者へのやさしさ
56/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。