@analogjs/vite-plugin-angular 3.0.0-alpha.49 → 3.0.0-alpha.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analogjs/vite-plugin-angular",
3
- "version": "3.0.0-alpha.49",
3
+ "version": "3.0.0-alpha.50",
4
4
  "description": "Vite Plugin for Angular",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -2,8 +2,18 @@ import * as ts from "typescript";
2
2
  import { ComponentRegistry } from "./registry.js";
3
3
  /** Recursively collect all DeferredBlock nodes from a template AST. */
4
4
  export declare function collectDeferBlocks(nodes: any[]): any[];
5
- /** Collect element tag names from template AST nodes recursively. */
6
- export declare function collectElementNames(nodes: any[]): Set<string>;
5
+ /**
6
+ * Collect element tag names from template AST nodes recursively.
7
+ *
8
+ * When `excludeDeferBlocks` is true, the deferred body of any DeferredBlock is
9
+ * skipped — its children are treated as deferred, not eager. The fallback
10
+ * subtrees (`@placeholder`, `@loading`, `@error`) are still walked: Angular
11
+ * treats them as *eager* dependencies because the placeholder must render in
12
+ * the main bundle before the defer trigger fires. A component shared between
13
+ * the defer body and a fallback block must stay in static imports, otherwise
14
+ * the fallback render breaks.
15
+ */
16
+ export declare function collectElementNames(nodes: any[], excludeDeferBlocks?: boolean): Set<string>;
7
17
  /** Information about how a class is imported. */
8
18
  export interface ImportInfo {
9
19
  /** Module path from the import statement. */
@@ -9,6 +9,7 @@ function collectDeferBlocks(nodes) {
9
9
  if (node.constructor?.name === "DeferredBlock") result.push(node);
10
10
  if (Array.isArray(node.children)) node.children.forEach(walk);
11
11
  if (Array.isArray(node.branches)) node.branches.forEach(walk);
12
+ if (Array.isArray(node.groups)) node.groups.forEach(walk);
12
13
  if (Array.isArray(node.cases)) node.cases.forEach(walk);
13
14
  if (node.empty?.children) node.empty.children.forEach(walk);
14
15
  if (node.placeholder?.children) node.placeholder.children.forEach(walk);
@@ -18,16 +19,36 @@ function collectDeferBlocks(nodes) {
18
19
  nodes.forEach(walk);
19
20
  return result;
20
21
  }
21
- /** Collect element tag names from template AST nodes recursively. */
22
- function collectElementNames(nodes) {
22
+ /**
23
+ * Collect element tag names from template AST nodes recursively.
24
+ *
25
+ * When `excludeDeferBlocks` is true, the deferred body of any DeferredBlock is
26
+ * skipped — its children are treated as deferred, not eager. The fallback
27
+ * subtrees (`@placeholder`, `@loading`, `@error`) are still walked: Angular
28
+ * treats them as *eager* dependencies because the placeholder must render in
29
+ * the main bundle before the defer trigger fires. A component shared between
30
+ * the defer body and a fallback block must stay in static imports, otherwise
31
+ * the fallback render breaks.
32
+ */
33
+ function collectElementNames(nodes, excludeDeferBlocks = false) {
23
34
  const result = /* @__PURE__ */ new Set();
24
35
  function walk(node) {
25
36
  if (!node) return;
37
+ if (excludeDeferBlocks && node.constructor?.name === "DeferredBlock") {
38
+ if (node.placeholder?.children) node.placeholder.children.forEach(walk);
39
+ if (node.loading?.children) node.loading.children.forEach(walk);
40
+ if (node.error?.children) node.error.children.forEach(walk);
41
+ return;
42
+ }
26
43
  if (node.constructor?.name === "Element") result.add(node.name);
27
44
  if (Array.isArray(node.children)) node.children.forEach(walk);
28
45
  if (Array.isArray(node.branches)) node.branches.forEach(walk);
46
+ if (Array.isArray(node.groups)) node.groups.forEach(walk);
29
47
  if (Array.isArray(node.cases)) node.cases.forEach(walk);
30
48
  if (node.empty?.children) node.empty.children.forEach(walk);
49
+ if (node.placeholder?.children) node.placeholder.children.forEach(walk);
50
+ if (node.loading?.children) node.loading.children.forEach(walk);
51
+ if (node.error?.children) node.error.children.forEach(walk);
31
52
  }
32
53
  nodes.forEach(walk);
33
54
  return result;
@@ -81,17 +102,12 @@ function buildDeferDependencyMap(parsedTemplate, sourceFile, registry, localSele
81
102
  blocks: /* @__PURE__ */ new Map(),
82
103
  deferredImports: /* @__PURE__ */ new Set()
83
104
  };
84
- collectElementNames(parsedTemplate.nodes);
85
105
  const deferElements = /* @__PURE__ */ new Set();
86
106
  for (const block of deferBlocks) {
87
107
  const elements = collectElementNames(block.children || []);
88
108
  for (const el of elements) deferElements.add(el);
89
109
  }
90
- const eagerElements = /* @__PURE__ */ new Set();
91
- for (const node of parsedTemplate.nodes) if (node.constructor?.name !== "DeferredBlock") {
92
- const names = collectElementNames([node]);
93
- for (const n of names) eagerElements.add(n);
94
- }
110
+ const eagerElements = collectElementNames(parsedTemplate.nodes, true);
95
111
  const selectorToClass = /* @__PURE__ */ new Map();
96
112
  if (registry) {
97
113
  for (const [className, entry] of registry) if (entry.selector) selectorToClass.set(entry.selector, className);
@@ -1 +1 @@
1
- {"version":3,"file":"defer.js","names":[],"sources":["../../../../src/lib/compiler/defer.ts"],"sourcesContent":["import * as ts from 'typescript';\nimport * as o from '@angular/compiler';\nimport { ComponentRegistry } from './registry.js';\n\n/** Recursively collect all DeferredBlock nodes from a template AST. */\nexport function collectDeferBlocks(nodes: any[]): any[] {\n const result: any[] = [];\n function walk(node: any) {\n if (!node) return;\n if (node.constructor?.name === 'DeferredBlock') {\n result.push(node);\n }\n // Walk all possible child structures across block types:\n // Element/Template: children\n // IfBlock: branches (each has children)\n // ForLoopBlock: children, empty (has children)\n // SwitchBlock: cases (each has children)\n // DeferredBlock: children, placeholder/loading/error (each has children)\n if (Array.isArray(node.children)) node.children.forEach(walk);\n if (Array.isArray(node.branches)) node.branches.forEach(walk);\n if (Array.isArray(node.cases)) node.cases.forEach(walk);\n if (node.empty?.children) node.empty.children.forEach(walk);\n if (node.placeholder?.children) node.placeholder.children.forEach(walk);\n if (node.loading?.children) node.loading.children.forEach(walk);\n if (node.error?.children) node.error.children.forEach(walk);\n }\n nodes.forEach(walk);\n return result;\n}\n\n/** Collect element tag names from template AST nodes recursively. */\nexport function collectElementNames(nodes: any[]): Set<string> {\n const result = new Set<string>();\n function walk(node: any) {\n if (!node) return;\n if (node.constructor?.name === 'Element') result.add(node.name);\n if (Array.isArray(node.children)) node.children.forEach(walk);\n if (Array.isArray(node.branches)) node.branches.forEach(walk);\n if (Array.isArray(node.cases)) node.cases.forEach(walk);\n if (node.empty?.children) node.empty.children.forEach(walk);\n }\n nodes.forEach(walk);\n return result;\n}\n\n/** Information about how a class is imported. */\nexport interface ImportInfo {\n /** Module path from the import statement. */\n path: string;\n /** Whether the class was imported as a default import (`import X from`). */\n isDefault: boolean;\n /**\n * Original exported name on the source module's namespace. For\n * `import { HeavyWidget as Widget } from './heavy'`, the local\n * binding is `Widget` but the namespace exposes `HeavyWidget` —\n * `await import('./heavy')` returns `{ HeavyWidget }`, not\n * `{ Widget }`. Defer dependency emit must use the exported name,\n * not the local alias, or the lookup resolves to `undefined` at\n * runtime.\n */\n exportName: string;\n}\n\n/**\n * Build import info map: localName → { path, isDefault, exportName }\n * from source file imports. Tracks default vs named imports plus the\n * original exported name so the defer dependency generator can emit\n * `import('./p').then(m => m.default)` for default imports and\n * `import('./p').then(m => m.OriginalExportName)` for aliased named\n * imports.\n */\nexport function buildImportMap(sf: ts.SourceFile): Map<string, ImportInfo> {\n const result = new Map<string, ImportInfo>();\n for (const stmt of sf.statements) {\n if (!ts.isImportDeclaration(stmt) || !stmt.importClause) continue;\n const path = (stmt.moduleSpecifier as ts.StringLiteral).text;\n const clause = stmt.importClause;\n if (clause.name) {\n result.set(clause.name.text, {\n path,\n isDefault: true,\n exportName: 'default',\n });\n }\n if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {\n for (const el of clause.namedBindings.elements) {\n // `import { Foo as Bar }` → propertyName is `Foo`, name is `Bar`.\n // `import { Foo }` → propertyName is undefined.\n const exportName = el.propertyName?.text ?? el.name.text;\n const info: ImportInfo = { path, isDefault: false, exportName };\n // Key the map by BOTH the local binding and the original export\n // name. The defer-dep lookup uses the className from the\n // registry — which is always the original class name from the\n // source file (`HeavyWidget`), not the consumer's local alias\n // (`Widget`). Without the export-name key, aliased imports\n // would silently produce no defer dependency at all.\n result.set(el.name.text, info);\n if (exportName !== el.name.text) {\n result.set(exportName, info);\n }\n }\n }\n }\n return result;\n}\n\n/**\n * Build defer block dependency map with dynamic import() expressions.\n *\n * For each defer block, identifies which imported components are only used\n * inside defer blocks (not in the eager template), and generates dynamic\n * import expressions for lazy loading.\n *\n * Returns:\n * - blocks: Map<DeferredBlock, Expression | null> for compileComponentFromMetadata\n * - deferredImports: Set<string> of class names that should be removed from static imports\n */\nexport function buildDeferDependencyMap(\n parsedTemplate: any,\n sourceFile: ts.SourceFile,\n registry: ComponentRegistry | undefined,\n localSelectors: Map<string, string>,\n): { blocks: Map<any, any>; deferredImports: Set<string> } {\n const deferBlocks = collectDeferBlocks(parsedTemplate.nodes);\n if (deferBlocks.length === 0) {\n return { blocks: new Map(), deferredImports: new Set() };\n }\n\n // Collect all element names in eager (non-defer) template parts\n const allElements = collectElementNames(parsedTemplate.nodes);\n const deferElements = new Set<string>();\n for (const block of deferBlocks) {\n const elements = collectElementNames(block.children || []);\n for (const el of elements) deferElements.add(el);\n }\n // Eager elements = all elements minus those only in defer blocks\n // (An element is eager if it appears anywhere outside defer blocks too)\n // Simple approach: collect elements from non-defer top-level nodes\n const eagerElements = new Set<string>();\n for (const node of parsedTemplate.nodes) {\n if (node.constructor?.name !== 'DeferredBlock') {\n const names = collectElementNames([node]);\n for (const n of names) eagerElements.add(n);\n }\n }\n\n // Build selector → className map from registry + local selectors\n const selectorToClass = new Map<string, string>();\n if (registry) {\n for (const [className, entry] of registry) {\n if (entry.selector) selectorToClass.set(entry.selector, className);\n }\n }\n for (const [className, selector] of localSelectors) {\n selectorToClass.set(selector, className);\n }\n\n // Build className → importPath map\n const importMap = buildImportMap(sourceFile);\n\n // Find defer-only component class names\n const deferredImports = new Set<string>();\n const deferOnlyElements = new Set<string>();\n for (const el of deferElements) {\n if (!eagerElements.has(el)) deferOnlyElements.add(el);\n }\n\n for (const el of deferOnlyElements) {\n const className = selectorToClass.get(el);\n if (className && importMap.has(className)) {\n deferredImports.add(className);\n }\n }\n\n // Build the blocks map with dependency functions\n const blocks = new Map<any, any>();\n for (const block of deferBlocks) {\n const blockElements = collectElementNames(block.children || []);\n const blockDeps: any[] = [];\n // Within a single block, dedupe by `${path}#${symbolName}` so two\n // refs to the same component don't generate two import expressions.\n const seenInBlock = new Set<string>();\n\n for (const el of blockElements) {\n const className = selectorToClass.get(el);\n if (!className || !deferredImports.has(className)) continue;\n const info = importMap.get(className)!;\n // Use the original exported name (tracked by buildImportMap) so\n // aliased imports like `import { HeavyWidget as Widget }` resolve\n // to `m.HeavyWidget` instead of `m.Widget` (which would be\n // `undefined` on the module namespace).\n const symbolName = info.exportName;\n const key = `${info.path}#${symbolName}`;\n if (seenInBlock.has(key)) continue;\n seenInBlock.add(key);\n // import('./path').then(m => m.ClassName) — or `m.default` for\n // default imports. Without the `.then(...)` resolver Angular's\n // runtime gets a module namespace and can't find the class.\n // `FnParam` and the `variable()` helper aren't on the top-level\n // namespace; they live in the `outputAst` sub-namespace.\n const ast: any = (o as any).outputAst;\n const mVar = ast.variable('m');\n const innerFn = ast.arrowFn(\n [new ast.FnParam('m', ast.DYNAMIC_TYPE)],\n new o.ReadPropExpr(mVar, symbolName),\n );\n const importExpr = new o.InvokeFunctionExpr(\n new o.ReadPropExpr(\n new o.DynamicImportExpr(new o.LiteralExpr(info.path)),\n 'then',\n ),\n [innerFn],\n );\n blockDeps.push(new o.ArrowFunctionExpr([], importExpr));\n }\n\n if (blockDeps.length > 0) {\n // () => [import('./a').then(m => m.A), import('./b').then(m => m.default)]\n blocks.set(\n block,\n new o.ArrowFunctionExpr([], new o.LiteralArrayExpr(blockDeps)),\n );\n } else {\n blocks.set(block, null);\n }\n }\n\n return { blocks, deferredImports };\n}\n"],"mappings":";;;;AAKA,SAAgB,mBAAmB,OAAqB;CACtD,MAAM,SAAgB,EAAE;CACxB,SAAS,KAAK,MAAW;AACvB,MAAI,CAAC,KAAM;AACX,MAAI,KAAK,aAAa,SAAS,gBAC7B,QAAO,KAAK,KAAK;AAQnB,MAAI,MAAM,QAAQ,KAAK,SAAS,CAAE,MAAK,SAAS,QAAQ,KAAK;AAC7D,MAAI,MAAM,QAAQ,KAAK,SAAS,CAAE,MAAK,SAAS,QAAQ,KAAK;AAC7D,MAAI,MAAM,QAAQ,KAAK,MAAM,CAAE,MAAK,MAAM,QAAQ,KAAK;AACvD,MAAI,KAAK,OAAO,SAAU,MAAK,MAAM,SAAS,QAAQ,KAAK;AAC3D,MAAI,KAAK,aAAa,SAAU,MAAK,YAAY,SAAS,QAAQ,KAAK;AACvE,MAAI,KAAK,SAAS,SAAU,MAAK,QAAQ,SAAS,QAAQ,KAAK;AAC/D,MAAI,KAAK,OAAO,SAAU,MAAK,MAAM,SAAS,QAAQ,KAAK;;AAE7D,OAAM,QAAQ,KAAK;AACnB,QAAO;;;AAIT,SAAgB,oBAAoB,OAA2B;CAC7D,MAAM,yBAAS,IAAI,KAAa;CAChC,SAAS,KAAK,MAAW;AACvB,MAAI,CAAC,KAAM;AACX,MAAI,KAAK,aAAa,SAAS,UAAW,QAAO,IAAI,KAAK,KAAK;AAC/D,MAAI,MAAM,QAAQ,KAAK,SAAS,CAAE,MAAK,SAAS,QAAQ,KAAK;AAC7D,MAAI,MAAM,QAAQ,KAAK,SAAS,CAAE,MAAK,SAAS,QAAQ,KAAK;AAC7D,MAAI,MAAM,QAAQ,KAAK,MAAM,CAAE,MAAK,MAAM,QAAQ,KAAK;AACvD,MAAI,KAAK,OAAO,SAAU,MAAK,MAAM,SAAS,QAAQ,KAAK;;AAE7D,OAAM,QAAQ,KAAK;AACnB,QAAO;;;;;;;;;;AA6BT,SAAgB,eAAe,IAA4C;CACzE,MAAM,yBAAS,IAAI,KAAyB;AAC5C,MAAK,MAAM,QAAQ,GAAG,YAAY;AAChC,MAAI,CAAC,GAAG,oBAAoB,KAAK,IAAI,CAAC,KAAK,aAAc;EACzD,MAAM,OAAQ,KAAK,gBAAqC;EACxD,MAAM,SAAS,KAAK;AACpB,MAAI,OAAO,KACT,QAAO,IAAI,OAAO,KAAK,MAAM;GAC3B;GACA,WAAW;GACX,YAAY;GACb,CAAC;AAEJ,MAAI,OAAO,iBAAiB,GAAG,eAAe,OAAO,cAAc,CACjE,MAAK,MAAM,MAAM,OAAO,cAAc,UAAU;GAG9C,MAAM,aAAa,GAAG,cAAc,QAAQ,GAAG,KAAK;GACpD,MAAM,OAAmB;IAAE;IAAM,WAAW;IAAO;IAAY;AAO/D,UAAO,IAAI,GAAG,KAAK,MAAM,KAAK;AAC9B,OAAI,eAAe,GAAG,KAAK,KACzB,QAAO,IAAI,YAAY,KAAK;;;AAKpC,QAAO;;;;;;;;;;;;;AAcT,SAAgB,wBACd,gBACA,YACA,UACA,gBACyD;CACzD,MAAM,cAAc,mBAAmB,eAAe,MAAM;AAC5D,KAAI,YAAY,WAAW,EACzB,QAAO;EAAE,wBAAQ,IAAI,KAAK;EAAE,iCAAiB,IAAI,KAAK;EAAE;AAItC,qBAAoB,eAAe,MAAM;CAC7D,MAAM,gCAAgB,IAAI,KAAa;AACvC,MAAK,MAAM,SAAS,aAAa;EAC/B,MAAM,WAAW,oBAAoB,MAAM,YAAY,EAAE,CAAC;AAC1D,OAAK,MAAM,MAAM,SAAU,eAAc,IAAI,GAAG;;CAKlD,MAAM,gCAAgB,IAAI,KAAa;AACvC,MAAK,MAAM,QAAQ,eAAe,MAChC,KAAI,KAAK,aAAa,SAAS,iBAAiB;EAC9C,MAAM,QAAQ,oBAAoB,CAAC,KAAK,CAAC;AACzC,OAAK,MAAM,KAAK,MAAO,eAAc,IAAI,EAAE;;CAK/C,MAAM,kCAAkB,IAAI,KAAqB;AACjD,KAAI;OACG,MAAM,CAAC,WAAW,UAAU,SAC/B,KAAI,MAAM,SAAU,iBAAgB,IAAI,MAAM,UAAU,UAAU;;AAGtE,MAAK,MAAM,CAAC,WAAW,aAAa,eAClC,iBAAgB,IAAI,UAAU,UAAU;CAI1C,MAAM,YAAY,eAAe,WAAW;CAG5C,MAAM,kCAAkB,IAAI,KAAa;CACzC,MAAM,oCAAoB,IAAI,KAAa;AAC3C,MAAK,MAAM,MAAM,cACf,KAAI,CAAC,cAAc,IAAI,GAAG,CAAE,mBAAkB,IAAI,GAAG;AAGvD,MAAK,MAAM,MAAM,mBAAmB;EAClC,MAAM,YAAY,gBAAgB,IAAI,GAAG;AACzC,MAAI,aAAa,UAAU,IAAI,UAAU,CACvC,iBAAgB,IAAI,UAAU;;CAKlC,MAAM,yBAAS,IAAI,KAAe;AAClC,MAAK,MAAM,SAAS,aAAa;EAC/B,MAAM,gBAAgB,oBAAoB,MAAM,YAAY,EAAE,CAAC;EAC/D,MAAM,YAAmB,EAAE;EAG3B,MAAM,8BAAc,IAAI,KAAa;AAErC,OAAK,MAAM,MAAM,eAAe;GAC9B,MAAM,YAAY,gBAAgB,IAAI,GAAG;AACzC,OAAI,CAAC,aAAa,CAAC,gBAAgB,IAAI,UAAU,CAAE;GACnD,MAAM,OAAO,UAAU,IAAI,UAAU;GAKrC,MAAM,aAAa,KAAK;GACxB,MAAM,MAAM,GAAG,KAAK,KAAK,GAAG;AAC5B,OAAI,YAAY,IAAI,IAAI,CAAE;AAC1B,eAAY,IAAI,IAAI;GAMpB,MAAM,MAAY,EAAU;GAC5B,MAAM,OAAO,IAAI,SAAS,IAAI;GAC9B,MAAM,UAAU,IAAI,QAClB,CAAC,IAAI,IAAI,QAAQ,KAAK,IAAI,aAAa,CAAC,EACxC,IAAI,EAAE,aAAa,MAAM,WAAW,CACrC;GACD,MAAM,aAAa,IAAI,EAAE,mBACvB,IAAI,EAAE,aACJ,IAAI,EAAE,kBAAkB,IAAI,EAAE,YAAY,KAAK,KAAK,CAAC,EACrD,OACD,EACD,CAAC,QAAQ,CACV;AACD,aAAU,KAAK,IAAI,EAAE,kBAAkB,EAAE,EAAE,WAAW,CAAC;;AAGzD,MAAI,UAAU,SAAS,EAErB,QAAO,IACL,OACA,IAAI,EAAE,kBAAkB,EAAE,EAAE,IAAI,EAAE,iBAAiB,UAAU,CAAC,CAC/D;MAED,QAAO,IAAI,OAAO,KAAK;;AAI3B,QAAO;EAAE;EAAQ;EAAiB"}
1
+ {"version":3,"file":"defer.js","names":[],"sources":["../../../../src/lib/compiler/defer.ts"],"sourcesContent":["import * as ts from 'typescript';\nimport * as o from '@angular/compiler';\nimport { ComponentRegistry } from './registry.js';\n\n/** Recursively collect all DeferredBlock nodes from a template AST. */\nexport function collectDeferBlocks(nodes: any[]): any[] {\n const result: any[] = [];\n function walk(node: any) {\n if (!node) return;\n if (node.constructor?.name === 'DeferredBlock') {\n result.push(node);\n }\n // Walk all possible child structures across block types:\n // Element/Template: children\n // IfBlock: branches (each has children)\n // ForLoopBlock: children, empty (has children)\n // SwitchBlock: groups (Angular v21+, each is a SwitchBlockCaseGroup with\n // children) or cases (Angular v18-v20, each has children)\n // DeferredBlock: children, placeholder/loading/error (each has children)\n if (Array.isArray(node.children)) node.children.forEach(walk);\n if (Array.isArray(node.branches)) node.branches.forEach(walk);\n if (Array.isArray(node.groups)) node.groups.forEach(walk);\n if (Array.isArray(node.cases)) node.cases.forEach(walk);\n if (node.empty?.children) node.empty.children.forEach(walk);\n if (node.placeholder?.children) node.placeholder.children.forEach(walk);\n if (node.loading?.children) node.loading.children.forEach(walk);\n if (node.error?.children) node.error.children.forEach(walk);\n }\n nodes.forEach(walk);\n return result;\n}\n\n/**\n * Collect element tag names from template AST nodes recursively.\n *\n * When `excludeDeferBlocks` is true, the deferred body of any DeferredBlock is\n * skipped — its children are treated as deferred, not eager. The fallback\n * subtrees (`@placeholder`, `@loading`, `@error`) are still walked: Angular\n * treats them as *eager* dependencies because the placeholder must render in\n * the main bundle before the defer trigger fires. A component shared between\n * the defer body and a fallback block must stay in static imports, otherwise\n * the fallback render breaks.\n */\nexport function collectElementNames(\n nodes: any[],\n excludeDeferBlocks = false,\n): Set<string> {\n const result = new Set<string>();\n function walk(node: any) {\n if (!node) return;\n if (excludeDeferBlocks && node.constructor?.name === 'DeferredBlock') {\n // Skip the deferred body but keep walking the fallback subtrees so\n // components referenced in `@placeholder` / `@loading` / `@error`\n // stay in the eager set.\n if (node.placeholder?.children) node.placeholder.children.forEach(walk);\n if (node.loading?.children) node.loading.children.forEach(walk);\n if (node.error?.children) node.error.children.forEach(walk);\n return;\n }\n if (node.constructor?.name === 'Element') result.add(node.name);\n if (Array.isArray(node.children)) node.children.forEach(walk);\n if (Array.isArray(node.branches)) node.branches.forEach(walk);\n if (Array.isArray(node.groups)) node.groups.forEach(walk);\n if (Array.isArray(node.cases)) node.cases.forEach(walk);\n if (node.empty?.children) node.empty.children.forEach(walk);\n if (node.placeholder?.children) node.placeholder.children.forEach(walk);\n if (node.loading?.children) node.loading.children.forEach(walk);\n if (node.error?.children) node.error.children.forEach(walk);\n }\n nodes.forEach(walk);\n return result;\n}\n\n/** Information about how a class is imported. */\nexport interface ImportInfo {\n /** Module path from the import statement. */\n path: string;\n /** Whether the class was imported as a default import (`import X from`). */\n isDefault: boolean;\n /**\n * Original exported name on the source module's namespace. For\n * `import { HeavyWidget as Widget } from './heavy'`, the local\n * binding is `Widget` but the namespace exposes `HeavyWidget` —\n * `await import('./heavy')` returns `{ HeavyWidget }`, not\n * `{ Widget }`. Defer dependency emit must use the exported name,\n * not the local alias, or the lookup resolves to `undefined` at\n * runtime.\n */\n exportName: string;\n}\n\n/**\n * Build import info map: localName → { path, isDefault, exportName }\n * from source file imports. Tracks default vs named imports plus the\n * original exported name so the defer dependency generator can emit\n * `import('./p').then(m => m.default)` for default imports and\n * `import('./p').then(m => m.OriginalExportName)` for aliased named\n * imports.\n */\nexport function buildImportMap(sf: ts.SourceFile): Map<string, ImportInfo> {\n const result = new Map<string, ImportInfo>();\n for (const stmt of sf.statements) {\n if (!ts.isImportDeclaration(stmt) || !stmt.importClause) continue;\n const path = (stmt.moduleSpecifier as ts.StringLiteral).text;\n const clause = stmt.importClause;\n if (clause.name) {\n result.set(clause.name.text, {\n path,\n isDefault: true,\n exportName: 'default',\n });\n }\n if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {\n for (const el of clause.namedBindings.elements) {\n // `import { Foo as Bar }` → propertyName is `Foo`, name is `Bar`.\n // `import { Foo }` → propertyName is undefined.\n const exportName = el.propertyName?.text ?? el.name.text;\n const info: ImportInfo = { path, isDefault: false, exportName };\n // Key the map by BOTH the local binding and the original export\n // name. The defer-dep lookup uses the className from the\n // registry — which is always the original class name from the\n // source file (`HeavyWidget`), not the consumer's local alias\n // (`Widget`). Without the export-name key, aliased imports\n // would silently produce no defer dependency at all.\n result.set(el.name.text, info);\n if (exportName !== el.name.text) {\n result.set(exportName, info);\n }\n }\n }\n }\n return result;\n}\n\n/**\n * Build defer block dependency map with dynamic import() expressions.\n *\n * For each defer block, identifies which imported components are only used\n * inside defer blocks (not in the eager template), and generates dynamic\n * import expressions for lazy loading.\n *\n * Returns:\n * - blocks: Map<DeferredBlock, Expression | null> for compileComponentFromMetadata\n * - deferredImports: Set<string> of class names that should be removed from static imports\n */\nexport function buildDeferDependencyMap(\n parsedTemplate: any,\n sourceFile: ts.SourceFile,\n registry: ComponentRegistry | undefined,\n localSelectors: Map<string, string>,\n): { blocks: Map<any, any>; deferredImports: Set<string> } {\n const deferBlocks = collectDeferBlocks(parsedTemplate.nodes);\n if (deferBlocks.length === 0) {\n return { blocks: new Map(), deferredImports: new Set() };\n }\n\n const deferElements = new Set<string>();\n for (const block of deferBlocks) {\n const elements = collectElementNames(block.children || []);\n for (const el of elements) deferElements.add(el);\n }\n // Eager elements = elements reachable without crossing into a DeferredBlock.\n // Pass `excludeDeferBlocks` so a defer block nested inside @switch / @if /\n // @for is not unfolded into the eager set. Without this, a component used\n // only inside such a nested defer would be misclassified as eager and lose\n // its lazy-load dependency function.\n const eagerElements = collectElementNames(parsedTemplate.nodes, true);\n\n // Build selector → className map from registry + local selectors\n const selectorToClass = new Map<string, string>();\n if (registry) {\n for (const [className, entry] of registry) {\n if (entry.selector) selectorToClass.set(entry.selector, className);\n }\n }\n for (const [className, selector] of localSelectors) {\n selectorToClass.set(selector, className);\n }\n\n // Build className → importPath map\n const importMap = buildImportMap(sourceFile);\n\n // Find defer-only component class names\n const deferredImports = new Set<string>();\n const deferOnlyElements = new Set<string>();\n for (const el of deferElements) {\n if (!eagerElements.has(el)) deferOnlyElements.add(el);\n }\n\n for (const el of deferOnlyElements) {\n const className = selectorToClass.get(el);\n if (className && importMap.has(className)) {\n deferredImports.add(className);\n }\n }\n\n // Build the blocks map with dependency functions\n const blocks = new Map<any, any>();\n for (const block of deferBlocks) {\n const blockElements = collectElementNames(block.children || []);\n const blockDeps: any[] = [];\n // Within a single block, dedupe by `${path}#${symbolName}` so two\n // refs to the same component don't generate two import expressions.\n const seenInBlock = new Set<string>();\n\n for (const el of blockElements) {\n const className = selectorToClass.get(el);\n if (!className || !deferredImports.has(className)) continue;\n const info = importMap.get(className)!;\n // Use the original exported name (tracked by buildImportMap) so\n // aliased imports like `import { HeavyWidget as Widget }` resolve\n // to `m.HeavyWidget` instead of `m.Widget` (which would be\n // `undefined` on the module namespace).\n const symbolName = info.exportName;\n const key = `${info.path}#${symbolName}`;\n if (seenInBlock.has(key)) continue;\n seenInBlock.add(key);\n // import('./path').then(m => m.ClassName) — or `m.default` for\n // default imports. Without the `.then(...)` resolver Angular's\n // runtime gets a module namespace and can't find the class.\n // `FnParam` and the `variable()` helper aren't on the top-level\n // namespace; they live in the `outputAst` sub-namespace.\n const ast: any = (o as any).outputAst;\n const mVar = ast.variable('m');\n const innerFn = ast.arrowFn(\n [new ast.FnParam('m', ast.DYNAMIC_TYPE)],\n new o.ReadPropExpr(mVar, symbolName),\n );\n const importExpr = new o.InvokeFunctionExpr(\n new o.ReadPropExpr(\n new o.DynamicImportExpr(new o.LiteralExpr(info.path)),\n 'then',\n ),\n [innerFn],\n );\n blockDeps.push(new o.ArrowFunctionExpr([], importExpr));\n }\n\n if (blockDeps.length > 0) {\n // () => [import('./a').then(m => m.A), import('./b').then(m => m.default)]\n blocks.set(\n block,\n new o.ArrowFunctionExpr([], new o.LiteralArrayExpr(blockDeps)),\n );\n } else {\n blocks.set(block, null);\n }\n }\n\n return { blocks, deferredImports };\n}\n"],"mappings":"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"}
@@ -206,6 +206,28 @@ function childNeedsParens(parentOp, child, isRhs) {
206
206
  }
207
207
  return false;
208
208
  }
209
+ /**
210
+ * Wrap a receiver expression in parens when emitting `<receiver>.name` or
211
+ * `<receiver>.name(args)`. Two cases produce a JS parse error otherwise:
212
+ *
213
+ * 1. A `BinaryOperatorExpr` receiver: `a / 3600.toFixed(2)` parses as
214
+ * `a / 3600.toFixed(2)` where `3600.toFixed` is read as the start of a
215
+ * decimal literal followed by an identifier — an "Invalid characters
216
+ * after number" error. Wrapping gives `(a / 3600).toFixed(2)`.
217
+ * 2. A non-negative integer `LiteralExpr` receiver: `42.toFixed(2)` is
218
+ * invalid for the same decimal-literal-ambiguity reason. Wrapping gives
219
+ * `(42).toFixed(2)`. Negative numbers are already emitted as `(-42)` by
220
+ * `visitLiteralExpr`. Floats (e.g. `4.5.toFixed`) and strings parse fine.
221
+ *
222
+ * Other expression kinds either already self-wrap (`ConditionalExpr`,
223
+ * `UnaryOperatorExpr`) or are primary forms that don't need parens
224
+ * (`ReadVarExpr`, `ReadPropExpr`, `InvokeFunctionExpr`, etc.).
225
+ */
226
+ function emitReceiverForMemberAccess(receiver, emitted) {
227
+ if (receiver instanceof o.BinaryOperatorExpr) return "(" + emitted + ")";
228
+ if (receiver instanceof o.LiteralExpr && typeof receiver.value === "number" && receiver.value >= 0) return "(" + emitted + ")";
229
+ return emitted;
230
+ }
209
231
  /** Polyfill for `__makeTemplateObject` used in downleveled `$localize` calls. */
210
232
  var MAKE_TEMPLATE_OBJECT_POLYFILL = "(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,\"raw\",{value:t}):e.raw=t,e})";
211
233
  var SINGLE_QUOTE_ESCAPE_RE = /[\\'\n\r]/g;
@@ -287,10 +309,12 @@ var JSEmitter = class JSEmitter {
287
309
  return ast.name;
288
310
  }
289
311
  visitReadPropExpr(ast) {
290
- return ast.receiver.visitExpression(this, null) + "." + ast.name;
312
+ const receiver = ast.receiver.visitExpression(this, null);
313
+ return emitReceiverForMemberAccess(ast.receiver, receiver) + "." + ast.name;
291
314
  }
292
315
  visitReadKeyExpr(ast) {
293
- return ast.receiver.visitExpression(this, null) + "[" + ast.index.visitExpression(this, null) + "]";
316
+ const receiver = ast.receiver.visitExpression(this, null);
317
+ return emitReceiverForMemberAccess(ast.receiver, receiver) + "[" + ast.index.visitExpression(this, null) + "]";
294
318
  }
295
319
  visitConditionalExpr(ast) {
296
320
  return "(" + ast.condition.visitExpression(this, null) + " ? " + ast.trueCase.visitExpression(this, null) + " : " + ast.falseCase.visitExpression(this, null) + ")";
@@ -329,7 +353,8 @@ var JSEmitter = class JSEmitter {
329
353
  return "(" + ast.receiver.visitExpression(this, null) + "[" + ast.index.visitExpression(this, null) + "] = " + ast.value.visitExpression(this, null) + ")";
330
354
  }
331
355
  visitInvokeMethodExpr(ast) {
332
- return ast.receiver.visitExpression(this, null) + "." + ast.name + "(" + ast.args.map((a) => this.emitExpr(a)).join(", ") + ")";
356
+ const receiver = ast.receiver.visitExpression(this, null);
357
+ return emitReceiverForMemberAccess(ast.receiver, receiver) + "." + ast.name + "(" + ast.args.map((a) => this.emitExpr(a)).join(", ") + ")";
333
358
  }
334
359
  visitTypeofExpr(ast) {
335
360
  return "typeof " + ast.expr.visitExpression(this, null);
@@ -1 +1 @@
1
- {"version":3,"file":"js-emitter.js","names":[],"sources":["../../../../src/lib/compiler/js-emitter.ts"],"sourcesContent":["import * as ts from 'typescript';\nimport * as o from '@angular/compiler';\n\n/** Shared printer — only used as fallback for complex WrappedNodeExpr (e.g. decorator args). */\nconst sharedPrinter = ts.createPrinter({ removeComments: true });\nconst emptySourceFile = ts.createSourceFile(\n '_.ts',\n '',\n ts.ScriptTarget.Latest,\n false,\n);\n\n// Operator metadata table — name, JS string, and precedence (higher = tighter\n// binding). Used to build version-aware lookup maps below.\n//\n// Why a runtime build instead of static `[o.BinaryOperator.Equals]: '=='`\n// literals: Angular's `BinaryOperator` enum gains members between major\n// versions (Assign and the 9 compound assignments were added in v21,\n// Exponentiation/In in v20, InstanceOf in v21). On older Angular versions\n// the missing members evaluate to `undefined` at module load time, and\n// every `[undefined]: '...'` entry collides on the single string key\n// `\"undefined\"` — last write wins. Pre-Phase-1, that gave the v19 install\n// `BINARY_OP_STR[\"undefined\"] === '??='`, so any expression with an\n// unknown operator emitted `??=` instead of failing loudly. Building the\n// table by iterating known names and skipping `undefined` values\n// eliminates the collision: members that don't exist on the installed\n// Angular are simply absent from the map.\n//\n// Precedence values match MDN's table, skipping levels not used by Angular\n// (bitwise XOR, shifts).\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence\nconst OP_DEFINITIONS: ReadonlyArray<\n readonly [name: string, str: string, precedence: number]\n> = [\n ['Assign', '=', 2],\n ['AdditionAssignment', '+=', 2],\n ['SubtractionAssignment', '-=', 2],\n ['MultiplicationAssignment', '*=', 2],\n ['DivisionAssignment', '/=', 2],\n ['RemainderAssignment', '%=', 2],\n ['ExponentiationAssignment', '**=', 2],\n ['AndAssignment', '&&=', 2],\n ['OrAssignment', '||=', 2],\n ['NullishCoalesceAssignment', '??=', 2],\n ['NullishCoalesce', '??', 4],\n ['Or', '||', 4],\n ['And', '&&', 5],\n ['BitwiseOr', '|', 6],\n ['BitwiseAnd', '&', 8],\n ['Equals', '==', 9],\n ['NotEquals', '!=', 9],\n ['Identical', '===', 9],\n ['NotIdentical', '!==', 9],\n ['Lower', '<', 10],\n ['LowerEquals', '<=', 10],\n ['Bigger', '>', 10],\n ['BiggerEquals', '>=', 10],\n ['In', 'in', 10],\n ['InstanceOf', 'instanceof', 10],\n ['Plus', '+', 12],\n ['Minus', '-', 12],\n ['Multiply', '*', 13],\n ['Divide', '/', 13],\n ['Modulo', '%', 13],\n ['Exponentiation', '**', 14],\n];\n\n/** Operator integer values that represent assignment forms (`=`, `+=`, etc.). */\nconst ASSIGNMENT_OP_NAMES: ReadonlySet<string> = new Set([\n 'Assign',\n 'AdditionAssignment',\n 'SubtractionAssignment',\n 'MultiplicationAssignment',\n 'DivisionAssignment',\n 'RemainderAssignment',\n 'ExponentiationAssignment',\n 'AndAssignment',\n 'OrAssignment',\n 'NullishCoalesceAssignment',\n]);\n\nconst BINARY_OP_STR = new Map<number, string>();\nconst BINARY_PRECEDENCE = new Map<number, number>();\nconst ASSIGNMENT_OP_VALUES = new Set<number>();\n\nfor (const [name, str, precedence] of OP_DEFINITIONS) {\n const value = (o.BinaryOperator as Record<string, unknown>)[name];\n if (typeof value !== 'number') continue;\n BINARY_OP_STR.set(value, str);\n BINARY_PRECEDENCE.set(value, precedence);\n if (ASSIGNMENT_OP_NAMES.has(name)) {\n ASSIGNMENT_OP_VALUES.add(value);\n }\n}\n\n/** Returns true when `op` represents any kind of assignment (`=`, `+=`, etc.). */\nfunction isAssignmentOperator(op: o.BinaryOperator): boolean {\n return ASSIGNMENT_OP_VALUES.has(op);\n}\n\n/**\n * Determine whether a child expression needs parentheses when it appears\n * as an operand of `parentOp`.\n */\nfunction childNeedsParens(\n parentOp: o.BinaryOperator,\n child: o.Expression,\n isRhs: boolean,\n): boolean {\n if (!(child instanceof o.BinaryOperatorExpr)) return false;\n\n const childOp = child.operator;\n const parentPrec = BINARY_PRECEDENCE.get(parentOp);\n const childPrec = BINARY_PRECEDENCE.get(childOp);\n if (parentPrec === undefined || childPrec === undefined) return false;\n\n // ?? cannot appear with || or && without explicit grouping (JS spec)\n if (\n (parentOp === o.BinaryOperator.NullishCoalesce &&\n (childOp === o.BinaryOperator.And || childOp === o.BinaryOperator.Or)) ||\n (childOp === o.BinaryOperator.NullishCoalesce &&\n (parentOp === o.BinaryOperator.And || parentOp === o.BinaryOperator.Or))\n ) {\n return true;\n }\n\n // Child has strictly lower precedence\n if (childPrec < parentPrec) return true;\n\n // Same precedence — depends on associativity and position\n if (childPrec === parentPrec) {\n // ** is right-associative: LHS same-prec needs parens, RHS does not\n if (parentOp === o.BinaryOperator.Exponentiation) return !isRhs;\n // All other ops are left-associative: RHS same-prec needs parens\n return isRhs;\n }\n\n return false;\n}\n\n/** Polyfill for `__makeTemplateObject` used in downleveled `$localize` calls. */\nconst MAKE_TEMPLATE_OBJECT_POLYFILL =\n '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,\"raw\",{value:t}):e.raw=t,e})';\n\nconst SINGLE_QUOTE_ESCAPE_RE = /[\\\\'\\n\\r]/g;\n\n/** Escape a string for use as a single-quoted string literal. */\nfunction escapeForLocalize(input: string): string {\n const body = input.replace(SINGLE_QUOTE_ESCAPE_RE, (match) => {\n if (match === '\\n') return '\\\\n';\n if (match === '\\r') return '\\\\r';\n return `\\\\${match}`;\n });\n return `'${body}'`;\n}\n\n/**\n * Emits Angular output AST directly to JavaScript strings, bypassing\n * ts.factory node creation and ts.Printer serialization (~4x faster).\n */\nclass JSEmitter implements o.ExpressionVisitor, o.StatementVisitor {\n /** Set by compile() so WrappedNodeExpr fallback can print with correct source context. */\n static _currentSourceFile: ts.SourceFile | undefined;\n /** Set by compile() so OXC-based WrappedNodeExpr can slice original source. */\n static _currentSourceCode: string | undefined;\n\n private emitExpr(e: any): string {\n if (!e) return 'null';\n if (typeof e.visitExpression === 'function')\n return e.visitExpression(this, null);\n // Angular v21 LiteralMapPropertyAssignment: {key, value, quoted}\n if ('key' in e && 'value' in e) {\n const key = e.quoted ? JSON.stringify(e.key) : e.key;\n return key + ': ' + this.emitExpr(e.value);\n }\n return 'null';\n }\n visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>) {\n const node = ast.node;\n\n // Raw source text: already resolved to a string at wrap time\n if (typeof node === 'string') return node;\n\n // OXC AST nodes: identified by string `type` property + numeric start/end\n if (typeof node.type === 'string' && typeof node.start === 'number') {\n const src = JSEmitter._currentSourceCode;\n if (node.type === 'Identifier') return node.name;\n if (node.type === 'StringLiteral') return JSON.stringify(node.value);\n if (node.type === 'NumericLiteral') return String(node.value);\n if (node.type === 'BooleanLiteral') return node.value ? 'true' : 'false';\n if (node.type === 'NullLiteral') return 'null';\n if (node.type === 'TemplateLiteral' && !node.expressions?.length)\n return '`' + node.quasis[0].value.raw + '`';\n // Fallback: slice original source for complex OXC nodes\n if (src) return src.slice(node.start, node.end);\n return 'null';\n }\n\n // TypeScript AST nodes: identified by numeric `kind` property\n if (node.kind === ts.SyntaxKind.Identifier)\n return (node as ts.Identifier).escapedText as string;\n if (node.kind === ts.SyntaxKind.StringLiteral)\n return JSON.stringify((node as ts.StringLiteral).text);\n if (node.kind === ts.SyntaxKind.NumericLiteral)\n return (node as ts.NumericLiteral).text;\n if (node.kind === ts.SyntaxKind.TrueKeyword) return 'true';\n if (node.kind === ts.SyntaxKind.FalseKeyword) return 'false';\n if (node.kind === ts.SyntaxKind.NullKeyword) return 'null';\n if (node.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral)\n return '`' + (node as ts.NoSubstitutionTemplateLiteral).text + '`';\n // Fallback for complex wrapped TS nodes (e.g. decorator arguments, array literals).\n // Use currentSourceFile when available for correct position-based printing.\n return sharedPrinter.printNode(\n ts.EmitHint.Unspecified,\n node,\n JSEmitter._currentSourceFile || emptySourceFile,\n );\n }\n visitExternalExpr(ast: o.ExternalExpr) {\n const name = ast.value.name;\n if (name === 'ngDevMode') return name;\n // When name is null/undefined, this is a bare module reference (e.g. ngImport: i0)\n if (!name) return 'i0';\n return 'i0.' + name;\n }\n visitLiteralExpr(ast: o.LiteralExpr) {\n const v = ast.value;\n if (typeof v === 'string') return JSON.stringify(v);\n if (typeof v === 'number') return v < 0 ? '(-' + -v + ')' : '' + v;\n if (typeof v === 'boolean') return v ? 'true' : 'false';\n if (v === undefined) return 'void 0';\n return 'null';\n }\n visitLiteralArrayExpr(ast: o.LiteralArrayExpr) {\n return '[' + ast.entries.map((e) => this.emitExpr(e)).join(', ') + ']';\n }\n visitLiteralMapExpr(ast: o.LiteralMapExpr) {\n return '{' + ast.entries.map((e) => this.emitExpr(e)).join(', ') + '}';\n }\n visitInvokeFunctionExpr(ast: o.InvokeFunctionExpr) {\n const fn = ast.fn.visitExpression(this, null);\n const args = ast.args.map((a: any) => this.emitExpr(a)).join(', ');\n // Wrap arrow/function expressions in parens for valid IIFE syntax\n if (\n ast.fn instanceof o.ArrowFunctionExpr ||\n ast.fn instanceof o.FunctionExpr\n ) {\n return '(' + fn + ')(' + args + ')';\n }\n return fn + '(' + args + ')';\n }\n visitReadVarExpr(ast: o.ReadVarExpr) {\n if (ast.name === 'this') return 'this';\n if (ast.name === 'super') return 'super';\n return ast.name!;\n }\n visitReadPropExpr(ast: o.ReadPropExpr) {\n return ast.receiver.visitExpression(this, null) + '.' + ast.name;\n }\n visitReadKeyExpr(ast: o.ReadKeyExpr) {\n return (\n ast.receiver.visitExpression(this, null) +\n '[' +\n ast.index.visitExpression(this, null) +\n ']'\n );\n }\n visitConditionalExpr(ast: o.ConditionalExpr) {\n return (\n '(' +\n ast.condition.visitExpression(this, null) +\n ' ? ' +\n ast.trueCase.visitExpression(this, null) +\n ' : ' +\n ast.falseCase!.visitExpression(this, null) +\n ')'\n );\n }\n visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr) {\n // `BINARY_OP_STR.get(...)` returns `undefined` when the operator value\n // doesn't exist in the installed Angular's `BinaryOperator` enum (the\n // enum is missing 13 members on v19, 11 on v20). Falling back to `'='`\n // would silently produce wrong code; instead, we throw so the error\n // surfaces in the DEBUG output and the calling test fails loudly.\n const op = BINARY_OP_STR.get(ast.operator);\n if (op === undefined) {\n throw new Error(\n `[fast-compile] Unsupported BinaryOperator value ${ast.operator} ` +\n `on @angular/compiler ${o.VERSION?.full ?? '(unknown version)'}`,\n );\n }\n\n const lhsRaw = ast.lhs.visitExpression(this, null);\n const rhsRaw = ast.rhs.visitExpression(this, null);\n\n const lhs = childNeedsParens(ast.operator, ast.lhs, false)\n ? '(' + lhsRaw + ')'\n : lhsRaw;\n const rhs = childNeedsParens(ast.operator, ast.rhs, true)\n ? '(' + rhsRaw + ')'\n : rhsRaw;\n\n const expr = lhs + ' ' + op + ' ' + rhs;\n\n // Wrap assignments in parens so they work correctly as ternary conditions:\n // (tmp = val) ? a : b vs tmp = val ? a : b\n //\n // `isAssignmentOperator` is built at module load time from the operator\n // names that actually exist on the installed Angular's enum. This\n // works whether `Assign` is present (v21+) or absent (v19/v20) — on\n // older versions the assignment branch is simply unreachable because\n // Angular's compiler never constructs an Assign-operator expression.\n if (isAssignmentOperator(ast.operator)) {\n return '(' + expr + ')';\n }\n return expr;\n }\n visitNotExpr(ast: o.NotExpr) {\n return '!(' + ast.condition.visitExpression(this, null) + ')';\n }\n visitFunctionExpr(ast: o.FunctionExpr) {\n return (\n '(' +\n ast.params.map((p: any) => p.name).join(', ') +\n ') => {' +\n ast.statements.map((s: any) => s.visitStatement(this, null)).join(' ') +\n '}'\n );\n }\n visitArrowFunctionExpr(ast: o.ArrowFunctionExpr) {\n const params = '(' + ast.params.map((p: any) => p.name).join(', ') + ')';\n if (Array.isArray(ast.body))\n return (\n params +\n ' => {' +\n ast.body.map((s: any) => s.visitStatement(this, null)).join(' ') +\n '}'\n );\n const bodyExpr = (ast.body as o.Expression).visitExpression(this, null);\n // Wrap object literals in parens so `() => {key: val}` isn't parsed\n // as a block with a labeled statement.\n if (ast.body instanceof o.LiteralMapExpr) {\n return params + ' => (' + bodyExpr + ')';\n }\n return params + ' => ' + bodyExpr;\n }\n // `Write*Expr` visitors must wrap the assignment in parentheses so that\n // it composes correctly when nested inside higher-precedence parents\n // (e.g. as the test of a `ConditionalExpr`). Without the wrapping,\n // tmp = ctx.data() ? 0 : -1\n // parses as `tmp = (ctx.data() ? 0 : -1)` because assignment binds\n // looser than the ternary, which silently assigns the wrong value to\n // `tmp`. Angular's own `AbstractEmitterVisitor.visitWriteVarExpr`\n // applies the same context-sensitive wrapping (it skips the parens\n // when the assignment is a top-level statement to avoid `(x = 1);`\n // noise; we always wrap for simplicity — the redundant parens at\n // statement level are harmless).\n //\n // This matters specifically on Angular v19/v20, where assignment is\n // represented in the IR via `WriteVarExpr` / `AssignTemporaryExpr →\n // WriteVarExpr`. On v21+ the same construct is represented via\n // `BinaryOperatorExpr(Assign, ...)`, which `visitBinaryOperatorExpr`\n // already wraps via `isAssignmentOperator`. Without these wrappers,\n // `@if (data(); as item)` and similar conditional alias forms emit\n // runtime-broken code on v19/v20.\n visitWriteVarExpr(ast: any) {\n return '(' + ast.name + ' = ' + ast.value.visitExpression(this, null) + ')';\n }\n visitWritePropExpr(ast: any) {\n return (\n '(' +\n ast.receiver.visitExpression(this, null) +\n '.' +\n ast.name +\n ' = ' +\n ast.value.visitExpression(this, null) +\n ')'\n );\n }\n visitWriteKeyExpr(ast: any) {\n return (\n '(' +\n ast.receiver.visitExpression(this, null) +\n '[' +\n ast.index.visitExpression(this, null) +\n '] = ' +\n ast.value.visitExpression(this, null) +\n ')'\n );\n }\n visitInvokeMethodExpr(ast: any) {\n return (\n ast.receiver.visitExpression(this, null) +\n '.' +\n ast.name +\n '(' +\n ast.args.map((a: any) => this.emitExpr(a)).join(', ') +\n ')'\n );\n }\n visitTypeofExpr(ast: o.TypeofExpr) {\n return 'typeof ' + ast.expr.visitExpression(this, null);\n }\n visitUnaryOperatorExpr(ast: o.UnaryOperatorExpr) {\n return '-(' + ast.expr.visitExpression(this, null) + ')';\n }\n visitInstantiateExpr(ast: o.InstantiateExpr) {\n return (\n 'new (' +\n ast.classExpr.visitExpression(this, null) +\n ')(' +\n ast.args.map((a: any) => this.emitExpr(a)).join(', ') +\n ')'\n );\n }\n visitCommaExpr(ast: o.CommaExpr) {\n return ast.parts.map((p: any) => p.visitExpression(this, null)).join(', ');\n }\n visitParenthesizedExpr(ast: o.ParenthesizedExpr) {\n return '(' + ast.expr.visitExpression(this, null) + ')';\n }\n visitVoidExpr(ast: o.VoidExpr) {\n return 'void ' + ast.expr.visitExpression(this, null);\n }\n visitSpreadElementExpr(ast: o.SpreadElementExpr) {\n return '...' + (ast as any).expression.visitExpression(this, null);\n }\n visitDynamicImportExpr(ast: o.DynamicImportExpr) {\n return (\n 'import(' +\n (typeof ast.url === 'string'\n ? JSON.stringify(ast.url)\n : ast.url.visitExpression(this, null)) +\n ')'\n );\n }\n visitTemplateLiteralExpr(ast: o.TemplateLiteralExpr) {\n return (\n '`' +\n ast.elements[0].text +\n ast.expressions\n .map(\n (e: any, i: number) =>\n '${' +\n e.visitExpression(this, null) +\n '}' +\n ast.elements[i + 1].text,\n )\n .join('') +\n '`'\n );\n }\n visitTaggedTemplateLiteralExpr(ast: any) {\n const elements = ast.template.elements;\n const expressions = ast.template.expressions;\n const head = elements[0].text;\n const spans = expressions\n .map(\n (e: any, i: number) =>\n '${' + e.visitExpression(this, null) + '}' + elements[i + 1].text,\n )\n .join('');\n return ast.tag.visitExpression(this, null) + '`' + head + spans + '`';\n }\n visitLocalizedString(ast: o.LocalizedString) {\n // Emit the downleveled form:\n // $localize(makeTemplateObject(cooked, raw), expr1, expr2, ...)\n const parts: { cooked: string; raw: string }[] = [ast.serializeI18nHead()];\n for (let i = 1; i < ast.messageParts.length; i++) {\n parts.push(ast.serializeI18nTemplatePart(i));\n }\n const cooked = parts.map((p) => escapeForLocalize(p.cooked)).join(', ');\n const raw = parts.map((p) => escapeForLocalize(p.raw)).join(', ');\n const exprs = ast.expressions.map((e) => this.emitExpr(e));\n return (\n '$localize(' +\n MAKE_TEMPLATE_OBJECT_POLYFILL +\n '([' +\n cooked +\n '], [' +\n raw +\n '])' +\n (exprs.length > 0 ? ', ' + exprs.join(', ') : '') +\n ')'\n );\n }\n visitRegularExpressionLiteral(ast: any) {\n return '/' + (ast.body ?? ast.pattern) + '/' + ast.flags;\n }\n visitTemplateLiteralElementExpr(ast: o.TemplateLiteralElementExpr) {\n return JSON.stringify(ast.text);\n }\n // Statement visitors\n visitReturnStmt(stmt: o.ReturnStatement) {\n return 'return ' + stmt.value.visitExpression(this, null) + ';';\n }\n visitExpressionStmt(stmt: o.ExpressionStatement) {\n return stmt.expr.visitExpression(this, null) + ';';\n }\n visitIfStmt(stmt: o.IfStmt) {\n let s =\n 'if (' +\n stmt.condition.visitExpression(this, null) +\n ') {' +\n stmt.trueCase.map((s2: any) => s2.visitStatement(this, null)).join(' ') +\n '}';\n if (stmt.falseCase.length)\n s +=\n ' else {' +\n stmt.falseCase\n .map((s2: any) => s2.visitStatement(this, null))\n .join(' ') +\n '}';\n return s;\n }\n visitDeclareVarStmt(stmt: o.DeclareVarStmt) {\n const kw = stmt.hasModifier(o.StmtModifier.Final) ? 'const' : 'let';\n return (\n kw +\n ' ' +\n stmt.name +\n (stmt.value ? ' = ' + stmt.value.visitExpression(this, null) : '') +\n ';'\n );\n }\n visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt) {\n return (\n 'function ' +\n stmt.name +\n '(' +\n stmt.params.map((p: any) => p.name).join(', ') +\n ') {' +\n stmt.statements.map((s: any) => s.visitStatement(this, null)).join(' ') +\n '}'\n );\n }\n}\n\nconst stringEmitter = new JSEmitter();\n\n/** Emit Angular output AST expression directly to a JavaScript string. */\nexport function emitAngularExpr(expr: o.Expression): string {\n return expr.visitExpression(stringEmitter, null);\n}\n\n/** Emit Angular output AST statement directly to a JavaScript string. */\nexport function emitAngularStmt(stmt: o.Statement): string {\n return stmt.visitStatement(stringEmitter, null);\n}\n\n/** Set the current source file for WrappedNodeExpr fallback printing. */\nexport function setEmitterSourceFile(sf: ts.SourceFile | undefined): void {\n JSEmitter._currentSourceFile = sf;\n}\n\n/** Set the current source code for OXC-based WrappedNodeExpr slice fallback. */\nexport function setEmitterSourceCode(code: string | undefined): void {\n JSEmitter._currentSourceCode = code;\n}\n"],"mappings":";;;;AAIA,IAAM,gBAAgB,GAAG,cAAc,EAAE,gBAAgB,MAAM,CAAC;AAChE,IAAM,kBAAkB,GAAG,iBACzB,QACA,IACA,GAAG,aAAa,QAChB,MACD;AAqBD,IAAM,iBAEF;CACF;EAAC;EAAU;EAAK;EAAE;CAClB;EAAC;EAAsB;EAAM;EAAE;CAC/B;EAAC;EAAyB;EAAM;EAAE;CAClC;EAAC;EAA4B;EAAM;EAAE;CACrC;EAAC;EAAsB;EAAM;EAAE;CAC/B;EAAC;EAAuB;EAAM;EAAE;CAChC;EAAC;EAA4B;EAAO;EAAE;CACtC;EAAC;EAAiB;EAAO;EAAE;CAC3B;EAAC;EAAgB;EAAO;EAAE;CAC1B;EAAC;EAA6B;EAAO;EAAE;CACvC;EAAC;EAAmB;EAAM;EAAE;CAC5B;EAAC;EAAM;EAAM;EAAE;CACf;EAAC;EAAO;EAAM;EAAE;CAChB;EAAC;EAAa;EAAK;EAAE;CACrB;EAAC;EAAc;EAAK;EAAE;CACtB;EAAC;EAAU;EAAM;EAAE;CACnB;EAAC;EAAa;EAAM;EAAE;CACtB;EAAC;EAAa;EAAO;EAAE;CACvB;EAAC;EAAgB;EAAO;EAAE;CAC1B;EAAC;EAAS;EAAK;EAAG;CAClB;EAAC;EAAe;EAAM;EAAG;CACzB;EAAC;EAAU;EAAK;EAAG;CACnB;EAAC;EAAgB;EAAM;EAAG;CAC1B;EAAC;EAAM;EAAM;EAAG;CAChB;EAAC;EAAc;EAAc;EAAG;CAChC;EAAC;EAAQ;EAAK;EAAG;CACjB;EAAC;EAAS;EAAK;EAAG;CAClB;EAAC;EAAY;EAAK;EAAG;CACrB;EAAC;EAAU;EAAK;EAAG;CACnB;EAAC;EAAU;EAAK;EAAG;CACnB;EAAC;EAAkB;EAAM;EAAG;CAC7B;;AAGD,IAAM,sBAA2C,IAAI,IAAI;CACvD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,IAAM,gCAAgB,IAAI,KAAqB;AAC/C,IAAM,oCAAoB,IAAI,KAAqB;AACnD,IAAM,uCAAuB,IAAI,KAAa;AAE9C,KAAK,MAAM,CAAC,MAAM,KAAK,eAAe,gBAAgB;CACpD,MAAM,QAAS,EAAE,eAA2C;AAC5D,KAAI,OAAO,UAAU,SAAU;AAC/B,eAAc,IAAI,OAAO,IAAI;AAC7B,mBAAkB,IAAI,OAAO,WAAW;AACxC,KAAI,oBAAoB,IAAI,KAAK,CAC/B,sBAAqB,IAAI,MAAM;;;AAKnC,SAAS,qBAAqB,IAA+B;AAC3D,QAAO,qBAAqB,IAAI,GAAG;;;;;;AAOrC,SAAS,iBACP,UACA,OACA,OACS;AACT,KAAI,EAAE,iBAAiB,EAAE,oBAAqB,QAAO;CAErD,MAAM,UAAU,MAAM;CACtB,MAAM,aAAa,kBAAkB,IAAI,SAAS;CAClD,MAAM,YAAY,kBAAkB,IAAI,QAAQ;AAChD,KAAI,eAAe,KAAA,KAAa,cAAc,KAAA,EAAW,QAAO;AAGhE,KACG,aAAa,EAAE,eAAe,oBAC5B,YAAY,EAAE,eAAe,OAAO,YAAY,EAAE,eAAe,OACnE,YAAY,EAAE,eAAe,oBAC3B,aAAa,EAAE,eAAe,OAAO,aAAa,EAAE,eAAe,IAEtE,QAAO;AAIT,KAAI,YAAY,WAAY,QAAO;AAGnC,KAAI,cAAc,YAAY;AAE5B,MAAI,aAAa,EAAE,eAAe,eAAgB,QAAO,CAAC;AAE1D,SAAO;;AAGT,QAAO;;;AAIT,IAAM,gCACJ;AAEF,IAAM,yBAAyB;;AAG/B,SAAS,kBAAkB,OAAuB;AAMhD,QAAO,IALM,MAAM,QAAQ,yBAAyB,UAAU;AAC5D,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,UAAU,KAAM,QAAO;AAC3B,SAAO,KAAK;GACZ,CACc;;;;;;AAOlB,IAAM,YAAN,MAAM,UAA6D;;CAEjE,OAAO;;CAEP,OAAO;CAEP,SAAiB,GAAgB;AAC/B,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,OAAO,EAAE,oBAAoB,WAC/B,QAAO,EAAE,gBAAgB,MAAM,KAAK;AAEtC,MAAI,SAAS,KAAK,WAAW,EAE3B,SADY,EAAE,SAAS,KAAK,UAAU,EAAE,IAAI,GAAG,EAAE,OACpC,OAAO,KAAK,SAAS,EAAE,MAAM;AAE5C,SAAO;;CAET,qBAAqB,KAA6B;EAChD,MAAM,OAAO,IAAI;AAGjB,MAAI,OAAO,SAAS,SAAU,QAAO;AAGrC,MAAI,OAAO,KAAK,SAAS,YAAY,OAAO,KAAK,UAAU,UAAU;GACnE,MAAM,MAAM,UAAU;AACtB,OAAI,KAAK,SAAS,aAAc,QAAO,KAAK;AAC5C,OAAI,KAAK,SAAS,gBAAiB,QAAO,KAAK,UAAU,KAAK,MAAM;AACpE,OAAI,KAAK,SAAS,iBAAkB,QAAO,OAAO,KAAK,MAAM;AAC7D,OAAI,KAAK,SAAS,iBAAkB,QAAO,KAAK,QAAQ,SAAS;AACjE,OAAI,KAAK,SAAS,cAAe,QAAO;AACxC,OAAI,KAAK,SAAS,qBAAqB,CAAC,KAAK,aAAa,OACxD,QAAO,MAAM,KAAK,OAAO,GAAG,MAAM,MAAM;AAE1C,OAAI,IAAK,QAAO,IAAI,MAAM,KAAK,OAAO,KAAK,IAAI;AAC/C,UAAO;;AAIT,MAAI,KAAK,SAAS,GAAG,WAAW,WAC9B,QAAQ,KAAuB;AACjC,MAAI,KAAK,SAAS,GAAG,WAAW,cAC9B,QAAO,KAAK,UAAW,KAA0B,KAAK;AACxD,MAAI,KAAK,SAAS,GAAG,WAAW,eAC9B,QAAQ,KAA2B;AACrC,MAAI,KAAK,SAAS,GAAG,WAAW,YAAa,QAAO;AACpD,MAAI,KAAK,SAAS,GAAG,WAAW,aAAc,QAAO;AACrD,MAAI,KAAK,SAAS,GAAG,WAAW,YAAa,QAAO;AACpD,MAAI,KAAK,SAAS,GAAG,WAAW,8BAC9B,QAAO,MAAO,KAA0C,OAAO;AAGjE,SAAO,cAAc,UACnB,GAAG,SAAS,aACZ,MACA,UAAU,sBAAsB,gBACjC;;CAEH,kBAAkB,KAAqB;EACrC,MAAM,OAAO,IAAI,MAAM;AACvB,MAAI,SAAS,YAAa,QAAO;AAEjC,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,QAAQ;;CAEjB,iBAAiB,KAAoB;EACnC,MAAM,IAAI,IAAI;AACd,MAAI,OAAO,MAAM,SAAU,QAAO,KAAK,UAAU,EAAE;AACnD,MAAI,OAAO,MAAM,SAAU,QAAO,IAAI,IAAI,OAAO,CAAC,IAAI,MAAM,KAAK;AACjE,MAAI,OAAO,MAAM,UAAW,QAAO,IAAI,SAAS;AAChD,MAAI,MAAM,KAAA,EAAW,QAAO;AAC5B,SAAO;;CAET,sBAAsB,KAAyB;AAC7C,SAAO,MAAM,IAAI,QAAQ,KAAK,MAAM,KAAK,SAAS,EAAE,CAAC,CAAC,KAAK,KAAK,GAAG;;CAErE,oBAAoB,KAAuB;AACzC,SAAO,MAAM,IAAI,QAAQ,KAAK,MAAM,KAAK,SAAS,EAAE,CAAC,CAAC,KAAK,KAAK,GAAG;;CAErE,wBAAwB,KAA2B;EACjD,MAAM,KAAK,IAAI,GAAG,gBAAgB,MAAM,KAAK;EAC7C,MAAM,OAAO,IAAI,KAAK,KAAK,MAAW,KAAK,SAAS,EAAE,CAAC,CAAC,KAAK,KAAK;AAElE,MACE,IAAI,cAAc,EAAE,qBACpB,IAAI,cAAc,EAAE,aAEpB,QAAO,MAAM,KAAK,OAAO,OAAO;AAElC,SAAO,KAAK,MAAM,OAAO;;CAE3B,iBAAiB,KAAoB;AACnC,MAAI,IAAI,SAAS,OAAQ,QAAO;AAChC,MAAI,IAAI,SAAS,QAAS,QAAO;AACjC,SAAO,IAAI;;CAEb,kBAAkB,KAAqB;AACrC,SAAO,IAAI,SAAS,gBAAgB,MAAM,KAAK,GAAG,MAAM,IAAI;;CAE9D,iBAAiB,KAAoB;AACnC,SACE,IAAI,SAAS,gBAAgB,MAAM,KAAK,GACxC,MACA,IAAI,MAAM,gBAAgB,MAAM,KAAK,GACrC;;CAGJ,qBAAqB,KAAwB;AAC3C,SACE,MACA,IAAI,UAAU,gBAAgB,MAAM,KAAK,GACzC,QACA,IAAI,SAAS,gBAAgB,MAAM,KAAK,GACxC,QACA,IAAI,UAAW,gBAAgB,MAAM,KAAK,GAC1C;;CAGJ,wBAAwB,KAA2B;EAMjD,MAAM,KAAK,cAAc,IAAI,IAAI,SAAS;AAC1C,MAAI,OAAO,KAAA,EACT,OAAM,IAAI,MACR,mDAAmD,IAAI,SAAS,wBACtC,EAAE,SAAS,QAAQ,sBAC9C;EAGH,MAAM,SAAS,IAAI,IAAI,gBAAgB,MAAM,KAAK;EAClD,MAAM,SAAS,IAAI,IAAI,gBAAgB,MAAM,KAAK;EAElD,MAAM,MAAM,iBAAiB,IAAI,UAAU,IAAI,KAAK,MAAM,GACtD,MAAM,SAAS,MACf;EACJ,MAAM,MAAM,iBAAiB,IAAI,UAAU,IAAI,KAAK,KAAK,GACrD,MAAM,SAAS,MACf;EAEJ,MAAM,OAAO,MAAM,MAAM,KAAK,MAAM;AAUpC,MAAI,qBAAqB,IAAI,SAAS,CACpC,QAAO,MAAM,OAAO;AAEtB,SAAO;;CAET,aAAa,KAAgB;AAC3B,SAAO,OAAO,IAAI,UAAU,gBAAgB,MAAM,KAAK,GAAG;;CAE5D,kBAAkB,KAAqB;AACrC,SACE,MACA,IAAI,OAAO,KAAK,MAAW,EAAE,KAAK,CAAC,KAAK,KAAK,GAC7C,WACA,IAAI,WAAW,KAAK,MAAW,EAAE,eAAe,MAAM,KAAK,CAAC,CAAC,KAAK,IAAI,GACtE;;CAGJ,uBAAuB,KAA0B;EAC/C,MAAM,SAAS,MAAM,IAAI,OAAO,KAAK,MAAW,EAAE,KAAK,CAAC,KAAK,KAAK,GAAG;AACrE,MAAI,MAAM,QAAQ,IAAI,KAAK,CACzB,QACE,SACA,UACA,IAAI,KAAK,KAAK,MAAW,EAAE,eAAe,MAAM,KAAK,CAAC,CAAC,KAAK,IAAI,GAChE;EAEJ,MAAM,WAAY,IAAI,KAAsB,gBAAgB,MAAM,KAAK;AAGvE,MAAI,IAAI,gBAAgB,EAAE,eACxB,QAAO,SAAS,UAAU,WAAW;AAEvC,SAAO,SAAS,SAAS;;CAqB3B,kBAAkB,KAAU;AAC1B,SAAO,MAAM,IAAI,OAAO,QAAQ,IAAI,MAAM,gBAAgB,MAAM,KAAK,GAAG;;CAE1E,mBAAmB,KAAU;AAC3B,SACE,MACA,IAAI,SAAS,gBAAgB,MAAM,KAAK,GACxC,MACA,IAAI,OACJ,QACA,IAAI,MAAM,gBAAgB,MAAM,KAAK,GACrC;;CAGJ,kBAAkB,KAAU;AAC1B,SACE,MACA,IAAI,SAAS,gBAAgB,MAAM,KAAK,GACxC,MACA,IAAI,MAAM,gBAAgB,MAAM,KAAK,GACrC,SACA,IAAI,MAAM,gBAAgB,MAAM,KAAK,GACrC;;CAGJ,sBAAsB,KAAU;AAC9B,SACE,IAAI,SAAS,gBAAgB,MAAM,KAAK,GACxC,MACA,IAAI,OACJ,MACA,IAAI,KAAK,KAAK,MAAW,KAAK,SAAS,EAAE,CAAC,CAAC,KAAK,KAAK,GACrD;;CAGJ,gBAAgB,KAAmB;AACjC,SAAO,YAAY,IAAI,KAAK,gBAAgB,MAAM,KAAK;;CAEzD,uBAAuB,KAA0B;AAC/C,SAAO,OAAO,IAAI,KAAK,gBAAgB,MAAM,KAAK,GAAG;;CAEvD,qBAAqB,KAAwB;AAC3C,SACE,UACA,IAAI,UAAU,gBAAgB,MAAM,KAAK,GACzC,OACA,IAAI,KAAK,KAAK,MAAW,KAAK,SAAS,EAAE,CAAC,CAAC,KAAK,KAAK,GACrD;;CAGJ,eAAe,KAAkB;AAC/B,SAAO,IAAI,MAAM,KAAK,MAAW,EAAE,gBAAgB,MAAM,KAAK,CAAC,CAAC,KAAK,KAAK;;CAE5E,uBAAuB,KAA0B;AAC/C,SAAO,MAAM,IAAI,KAAK,gBAAgB,MAAM,KAAK,GAAG;;CAEtD,cAAc,KAAiB;AAC7B,SAAO,UAAU,IAAI,KAAK,gBAAgB,MAAM,KAAK;;CAEvD,uBAAuB,KAA0B;AAC/C,SAAO,QAAS,IAAY,WAAW,gBAAgB,MAAM,KAAK;;CAEpE,uBAAuB,KAA0B;AAC/C,SACE,aACC,OAAO,IAAI,QAAQ,WAChB,KAAK,UAAU,IAAI,IAAI,GACvB,IAAI,IAAI,gBAAgB,MAAM,KAAK,IACvC;;CAGJ,yBAAyB,KAA4B;AACnD,SACE,MACA,IAAI,SAAS,GAAG,OAChB,IAAI,YACD,KACE,GAAQ,MACP,OACA,EAAE,gBAAgB,MAAM,KAAK,GAC7B,MACA,IAAI,SAAS,IAAI,GAAG,KACvB,CACA,KAAK,GAAG,GACX;;CAGJ,+BAA+B,KAAU;EACvC,MAAM,WAAW,IAAI,SAAS;EAC9B,MAAM,cAAc,IAAI,SAAS;EACjC,MAAM,OAAO,SAAS,GAAG;EACzB,MAAM,QAAQ,YACX,KACE,GAAQ,MACP,OAAO,EAAE,gBAAgB,MAAM,KAAK,GAAG,MAAM,SAAS,IAAI,GAAG,KAChE,CACA,KAAK,GAAG;AACX,SAAO,IAAI,IAAI,gBAAgB,MAAM,KAAK,GAAG,MAAM,OAAO,QAAQ;;CAEpE,qBAAqB,KAAwB;EAG3C,MAAM,QAA2C,CAAC,IAAI,mBAAmB,CAAC;AAC1E,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,aAAa,QAAQ,IAC3C,OAAM,KAAK,IAAI,0BAA0B,EAAE,CAAC;EAE9C,MAAM,SAAS,MAAM,KAAK,MAAM,kBAAkB,EAAE,OAAO,CAAC,CAAC,KAAK,KAAK;EACvE,MAAM,MAAM,MAAM,KAAK,MAAM,kBAAkB,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK;EACjE,MAAM,QAAQ,IAAI,YAAY,KAAK,MAAM,KAAK,SAAS,EAAE,CAAC;AAC1D,SACE,eACA,gCACA,OACA,SACA,SACA,MACA,QACC,MAAM,SAAS,IAAI,OAAO,MAAM,KAAK,KAAK,GAAG,MAC9C;;CAGJ,8BAA8B,KAAU;AACtC,SAAO,OAAO,IAAI,QAAQ,IAAI,WAAW,MAAM,IAAI;;CAErD,gCAAgC,KAAmC;AACjE,SAAO,KAAK,UAAU,IAAI,KAAK;;CAGjC,gBAAgB,MAAyB;AACvC,SAAO,YAAY,KAAK,MAAM,gBAAgB,MAAM,KAAK,GAAG;;CAE9D,oBAAoB,MAA6B;AAC/C,SAAO,KAAK,KAAK,gBAAgB,MAAM,KAAK,GAAG;;CAEjD,YAAY,MAAgB;EAC1B,IAAI,IACF,SACA,KAAK,UAAU,gBAAgB,MAAM,KAAK,GAC1C,QACA,KAAK,SAAS,KAAK,OAAY,GAAG,eAAe,MAAM,KAAK,CAAC,CAAC,KAAK,IAAI,GACvE;AACF,MAAI,KAAK,UAAU,OACjB,MACE,YACA,KAAK,UACF,KAAK,OAAY,GAAG,eAAe,MAAM,KAAK,CAAC,CAC/C,KAAK,IAAI,GACZ;AACJ,SAAO;;CAET,oBAAoB,MAAwB;AAE1C,UADW,KAAK,YAAY,EAAE,aAAa,MAAM,GAAG,UAAU,SAG5D,MACA,KAAK,QACJ,KAAK,QAAQ,QAAQ,KAAK,MAAM,gBAAgB,MAAM,KAAK,GAAG,MAC/D;;CAGJ,yBAAyB,MAA6B;AACpD,SACE,cACA,KAAK,OACL,MACA,KAAK,OAAO,KAAK,MAAW,EAAE,KAAK,CAAC,KAAK,KAAK,GAC9C,QACA,KAAK,WAAW,KAAK,MAAW,EAAE,eAAe,MAAM,KAAK,CAAC,CAAC,KAAK,IAAI,GACvE;;;AAKN,IAAM,gBAAgB,IAAI,WAAW;;AAGrC,SAAgB,gBAAgB,MAA4B;AAC1D,QAAO,KAAK,gBAAgB,eAAe,KAAK;;;AAIlD,SAAgB,gBAAgB,MAA2B;AACzD,QAAO,KAAK,eAAe,eAAe,KAAK;;;AAIjD,SAAgB,qBAAqB,IAAqC;AACxE,WAAU,qBAAqB;;;AAIjC,SAAgB,qBAAqB,MAAgC;AACnE,WAAU,qBAAqB"}
1
+ {"version":3,"file":"js-emitter.js","names":[],"sources":["../../../../src/lib/compiler/js-emitter.ts"],"sourcesContent":["import * as ts from 'typescript';\nimport * as o from '@angular/compiler';\n\n/** Shared printer — only used as fallback for complex WrappedNodeExpr (e.g. decorator args). */\nconst sharedPrinter = ts.createPrinter({ removeComments: true });\nconst emptySourceFile = ts.createSourceFile(\n '_.ts',\n '',\n ts.ScriptTarget.Latest,\n false,\n);\n\n// Operator metadata table — name, JS string, and precedence (higher = tighter\n// binding). Used to build version-aware lookup maps below.\n//\n// Why a runtime build instead of static `[o.BinaryOperator.Equals]: '=='`\n// literals: Angular's `BinaryOperator` enum gains members between major\n// versions (Assign and the 9 compound assignments were added in v21,\n// Exponentiation/In in v20, InstanceOf in v21). On older Angular versions\n// the missing members evaluate to `undefined` at module load time, and\n// every `[undefined]: '...'` entry collides on the single string key\n// `\"undefined\"` — last write wins. Pre-Phase-1, that gave the v19 install\n// `BINARY_OP_STR[\"undefined\"] === '??='`, so any expression with an\n// unknown operator emitted `??=` instead of failing loudly. Building the\n// table by iterating known names and skipping `undefined` values\n// eliminates the collision: members that don't exist on the installed\n// Angular are simply absent from the map.\n//\n// Precedence values match MDN's table, skipping levels not used by Angular\n// (bitwise XOR, shifts).\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence\nconst OP_DEFINITIONS: ReadonlyArray<\n readonly [name: string, str: string, precedence: number]\n> = [\n ['Assign', '=', 2],\n ['AdditionAssignment', '+=', 2],\n ['SubtractionAssignment', '-=', 2],\n ['MultiplicationAssignment', '*=', 2],\n ['DivisionAssignment', '/=', 2],\n ['RemainderAssignment', '%=', 2],\n ['ExponentiationAssignment', '**=', 2],\n ['AndAssignment', '&&=', 2],\n ['OrAssignment', '||=', 2],\n ['NullishCoalesceAssignment', '??=', 2],\n ['NullishCoalesce', '??', 4],\n ['Or', '||', 4],\n ['And', '&&', 5],\n ['BitwiseOr', '|', 6],\n ['BitwiseAnd', '&', 8],\n ['Equals', '==', 9],\n ['NotEquals', '!=', 9],\n ['Identical', '===', 9],\n ['NotIdentical', '!==', 9],\n ['Lower', '<', 10],\n ['LowerEquals', '<=', 10],\n ['Bigger', '>', 10],\n ['BiggerEquals', '>=', 10],\n ['In', 'in', 10],\n ['InstanceOf', 'instanceof', 10],\n ['Plus', '+', 12],\n ['Minus', '-', 12],\n ['Multiply', '*', 13],\n ['Divide', '/', 13],\n ['Modulo', '%', 13],\n ['Exponentiation', '**', 14],\n];\n\n/** Operator integer values that represent assignment forms (`=`, `+=`, etc.). */\nconst ASSIGNMENT_OP_NAMES: ReadonlySet<string> = new Set([\n 'Assign',\n 'AdditionAssignment',\n 'SubtractionAssignment',\n 'MultiplicationAssignment',\n 'DivisionAssignment',\n 'RemainderAssignment',\n 'ExponentiationAssignment',\n 'AndAssignment',\n 'OrAssignment',\n 'NullishCoalesceAssignment',\n]);\n\nconst BINARY_OP_STR = new Map<number, string>();\nconst BINARY_PRECEDENCE = new Map<number, number>();\nconst ASSIGNMENT_OP_VALUES = new Set<number>();\n\nfor (const [name, str, precedence] of OP_DEFINITIONS) {\n const value = (o.BinaryOperator as Record<string, unknown>)[name];\n if (typeof value !== 'number') continue;\n BINARY_OP_STR.set(value, str);\n BINARY_PRECEDENCE.set(value, precedence);\n if (ASSIGNMENT_OP_NAMES.has(name)) {\n ASSIGNMENT_OP_VALUES.add(value);\n }\n}\n\n/** Returns true when `op` represents any kind of assignment (`=`, `+=`, etc.). */\nfunction isAssignmentOperator(op: o.BinaryOperator): boolean {\n return ASSIGNMENT_OP_VALUES.has(op);\n}\n\n/**\n * Determine whether a child expression needs parentheses when it appears\n * as an operand of `parentOp`.\n */\nfunction childNeedsParens(\n parentOp: o.BinaryOperator,\n child: o.Expression,\n isRhs: boolean,\n): boolean {\n if (!(child instanceof o.BinaryOperatorExpr)) return false;\n\n const childOp = child.operator;\n const parentPrec = BINARY_PRECEDENCE.get(parentOp);\n const childPrec = BINARY_PRECEDENCE.get(childOp);\n if (parentPrec === undefined || childPrec === undefined) return false;\n\n // ?? cannot appear with || or && without explicit grouping (JS spec)\n if (\n (parentOp === o.BinaryOperator.NullishCoalesce &&\n (childOp === o.BinaryOperator.And || childOp === o.BinaryOperator.Or)) ||\n (childOp === o.BinaryOperator.NullishCoalesce &&\n (parentOp === o.BinaryOperator.And || parentOp === o.BinaryOperator.Or))\n ) {\n return true;\n }\n\n // Child has strictly lower precedence\n if (childPrec < parentPrec) return true;\n\n // Same precedence — depends on associativity and position\n if (childPrec === parentPrec) {\n // ** is right-associative: LHS same-prec needs parens, RHS does not\n if (parentOp === o.BinaryOperator.Exponentiation) return !isRhs;\n // All other ops are left-associative: RHS same-prec needs parens\n return isRhs;\n }\n\n return false;\n}\n\n/**\n * Wrap a receiver expression in parens when emitting `<receiver>.name` or\n * `<receiver>.name(args)`. Two cases produce a JS parse error otherwise:\n *\n * 1. A `BinaryOperatorExpr` receiver: `a / 3600.toFixed(2)` parses as\n * `a / 3600.toFixed(2)` where `3600.toFixed` is read as the start of a\n * decimal literal followed by an identifier — an \"Invalid characters\n * after number\" error. Wrapping gives `(a / 3600).toFixed(2)`.\n * 2. A non-negative integer `LiteralExpr` receiver: `42.toFixed(2)` is\n * invalid for the same decimal-literal-ambiguity reason. Wrapping gives\n * `(42).toFixed(2)`. Negative numbers are already emitted as `(-42)` by\n * `visitLiteralExpr`. Floats (e.g. `4.5.toFixed`) and strings parse fine.\n *\n * Other expression kinds either already self-wrap (`ConditionalExpr`,\n * `UnaryOperatorExpr`) or are primary forms that don't need parens\n * (`ReadVarExpr`, `ReadPropExpr`, `InvokeFunctionExpr`, etc.).\n */\nfunction emitReceiverForMemberAccess(\n receiver: o.Expression,\n emitted: string,\n): string {\n if (receiver instanceof o.BinaryOperatorExpr) return '(' + emitted + ')';\n if (\n receiver instanceof o.LiteralExpr &&\n typeof receiver.value === 'number' &&\n receiver.value >= 0\n ) {\n return '(' + emitted + ')';\n }\n return emitted;\n}\n\n/** Polyfill for `__makeTemplateObject` used in downleveled `$localize` calls. */\nconst MAKE_TEMPLATE_OBJECT_POLYFILL =\n '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,\"raw\",{value:t}):e.raw=t,e})';\n\nconst SINGLE_QUOTE_ESCAPE_RE = /[\\\\'\\n\\r]/g;\n\n/** Escape a string for use as a single-quoted string literal. */\nfunction escapeForLocalize(input: string): string {\n const body = input.replace(SINGLE_QUOTE_ESCAPE_RE, (match) => {\n if (match === '\\n') return '\\\\n';\n if (match === '\\r') return '\\\\r';\n return `\\\\${match}`;\n });\n return `'${body}'`;\n}\n\n/**\n * Emits Angular output AST directly to JavaScript strings, bypassing\n * ts.factory node creation and ts.Printer serialization (~4x faster).\n */\nclass JSEmitter implements o.ExpressionVisitor, o.StatementVisitor {\n /** Set by compile() so WrappedNodeExpr fallback can print with correct source context. */\n static _currentSourceFile: ts.SourceFile | undefined;\n /** Set by compile() so OXC-based WrappedNodeExpr can slice original source. */\n static _currentSourceCode: string | undefined;\n\n private emitExpr(e: any): string {\n if (!e) return 'null';\n if (typeof e.visitExpression === 'function')\n return e.visitExpression(this, null);\n // Angular v21 LiteralMapPropertyAssignment: {key, value, quoted}\n if ('key' in e && 'value' in e) {\n const key = e.quoted ? JSON.stringify(e.key) : e.key;\n return key + ': ' + this.emitExpr(e.value);\n }\n return 'null';\n }\n visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>) {\n const node = ast.node;\n\n // Raw source text: already resolved to a string at wrap time\n if (typeof node === 'string') return node;\n\n // OXC AST nodes: identified by string `type` property + numeric start/end\n if (typeof node.type === 'string' && typeof node.start === 'number') {\n const src = JSEmitter._currentSourceCode;\n if (node.type === 'Identifier') return node.name;\n if (node.type === 'StringLiteral') return JSON.stringify(node.value);\n if (node.type === 'NumericLiteral') return String(node.value);\n if (node.type === 'BooleanLiteral') return node.value ? 'true' : 'false';\n if (node.type === 'NullLiteral') return 'null';\n if (node.type === 'TemplateLiteral' && !node.expressions?.length)\n return '`' + node.quasis[0].value.raw + '`';\n // Fallback: slice original source for complex OXC nodes\n if (src) return src.slice(node.start, node.end);\n return 'null';\n }\n\n // TypeScript AST nodes: identified by numeric `kind` property\n if (node.kind === ts.SyntaxKind.Identifier)\n return (node as ts.Identifier).escapedText as string;\n if (node.kind === ts.SyntaxKind.StringLiteral)\n return JSON.stringify((node as ts.StringLiteral).text);\n if (node.kind === ts.SyntaxKind.NumericLiteral)\n return (node as ts.NumericLiteral).text;\n if (node.kind === ts.SyntaxKind.TrueKeyword) return 'true';\n if (node.kind === ts.SyntaxKind.FalseKeyword) return 'false';\n if (node.kind === ts.SyntaxKind.NullKeyword) return 'null';\n if (node.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral)\n return '`' + (node as ts.NoSubstitutionTemplateLiteral).text + '`';\n // Fallback for complex wrapped TS nodes (e.g. decorator arguments, array literals).\n // Use currentSourceFile when available for correct position-based printing.\n return sharedPrinter.printNode(\n ts.EmitHint.Unspecified,\n node,\n JSEmitter._currentSourceFile || emptySourceFile,\n );\n }\n visitExternalExpr(ast: o.ExternalExpr) {\n const name = ast.value.name;\n if (name === 'ngDevMode') return name;\n // When name is null/undefined, this is a bare module reference (e.g. ngImport: i0)\n if (!name) return 'i0';\n return 'i0.' + name;\n }\n visitLiteralExpr(ast: o.LiteralExpr) {\n const v = ast.value;\n if (typeof v === 'string') return JSON.stringify(v);\n if (typeof v === 'number') return v < 0 ? '(-' + -v + ')' : '' + v;\n if (typeof v === 'boolean') return v ? 'true' : 'false';\n if (v === undefined) return 'void 0';\n return 'null';\n }\n visitLiteralArrayExpr(ast: o.LiteralArrayExpr) {\n return '[' + ast.entries.map((e) => this.emitExpr(e)).join(', ') + ']';\n }\n visitLiteralMapExpr(ast: o.LiteralMapExpr) {\n return '{' + ast.entries.map((e) => this.emitExpr(e)).join(', ') + '}';\n }\n visitInvokeFunctionExpr(ast: o.InvokeFunctionExpr) {\n const fn = ast.fn.visitExpression(this, null);\n const args = ast.args.map((a: any) => this.emitExpr(a)).join(', ');\n // Wrap arrow/function expressions in parens for valid IIFE syntax\n if (\n ast.fn instanceof o.ArrowFunctionExpr ||\n ast.fn instanceof o.FunctionExpr\n ) {\n return '(' + fn + ')(' + args + ')';\n }\n return fn + '(' + args + ')';\n }\n visitReadVarExpr(ast: o.ReadVarExpr) {\n if (ast.name === 'this') return 'this';\n if (ast.name === 'super') return 'super';\n return ast.name!;\n }\n visitReadPropExpr(ast: o.ReadPropExpr) {\n const receiver = ast.receiver.visitExpression(this, null);\n return emitReceiverForMemberAccess(ast.receiver, receiver) + '.' + ast.name;\n }\n visitReadKeyExpr(ast: o.ReadKeyExpr) {\n const receiver = ast.receiver.visitExpression(this, null);\n return (\n emitReceiverForMemberAccess(ast.receiver, receiver) +\n '[' +\n ast.index.visitExpression(this, null) +\n ']'\n );\n }\n visitConditionalExpr(ast: o.ConditionalExpr) {\n return (\n '(' +\n ast.condition.visitExpression(this, null) +\n ' ? ' +\n ast.trueCase.visitExpression(this, null) +\n ' : ' +\n ast.falseCase!.visitExpression(this, null) +\n ')'\n );\n }\n visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr) {\n // `BINARY_OP_STR.get(...)` returns `undefined` when the operator value\n // doesn't exist in the installed Angular's `BinaryOperator` enum (the\n // enum is missing 13 members on v19, 11 on v20). Falling back to `'='`\n // would silently produce wrong code; instead, we throw so the error\n // surfaces in the DEBUG output and the calling test fails loudly.\n const op = BINARY_OP_STR.get(ast.operator);\n if (op === undefined) {\n throw new Error(\n `[fast-compile] Unsupported BinaryOperator value ${ast.operator} ` +\n `on @angular/compiler ${o.VERSION?.full ?? '(unknown version)'}`,\n );\n }\n\n const lhsRaw = ast.lhs.visitExpression(this, null);\n const rhsRaw = ast.rhs.visitExpression(this, null);\n\n const lhs = childNeedsParens(ast.operator, ast.lhs, false)\n ? '(' + lhsRaw + ')'\n : lhsRaw;\n const rhs = childNeedsParens(ast.operator, ast.rhs, true)\n ? '(' + rhsRaw + ')'\n : rhsRaw;\n\n const expr = lhs + ' ' + op + ' ' + rhs;\n\n // Wrap assignments in parens so they work correctly as ternary conditions:\n // (tmp = val) ? a : b vs tmp = val ? a : b\n //\n // `isAssignmentOperator` is built at module load time from the operator\n // names that actually exist on the installed Angular's enum. This\n // works whether `Assign` is present (v21+) or absent (v19/v20) — on\n // older versions the assignment branch is simply unreachable because\n // Angular's compiler never constructs an Assign-operator expression.\n if (isAssignmentOperator(ast.operator)) {\n return '(' + expr + ')';\n }\n return expr;\n }\n visitNotExpr(ast: o.NotExpr) {\n return '!(' + ast.condition.visitExpression(this, null) + ')';\n }\n visitFunctionExpr(ast: o.FunctionExpr) {\n return (\n '(' +\n ast.params.map((p: any) => p.name).join(', ') +\n ') => {' +\n ast.statements.map((s: any) => s.visitStatement(this, null)).join(' ') +\n '}'\n );\n }\n visitArrowFunctionExpr(ast: o.ArrowFunctionExpr) {\n const params = '(' + ast.params.map((p: any) => p.name).join(', ') + ')';\n if (Array.isArray(ast.body))\n return (\n params +\n ' => {' +\n ast.body.map((s: any) => s.visitStatement(this, null)).join(' ') +\n '}'\n );\n const bodyExpr = (ast.body as o.Expression).visitExpression(this, null);\n // Wrap object literals in parens so `() => {key: val}` isn't parsed\n // as a block with a labeled statement.\n if (ast.body instanceof o.LiteralMapExpr) {\n return params + ' => (' + bodyExpr + ')';\n }\n return params + ' => ' + bodyExpr;\n }\n // `Write*Expr` visitors must wrap the assignment in parentheses so that\n // it composes correctly when nested inside higher-precedence parents\n // (e.g. as the test of a `ConditionalExpr`). Without the wrapping,\n // tmp = ctx.data() ? 0 : -1\n // parses as `tmp = (ctx.data() ? 0 : -1)` because assignment binds\n // looser than the ternary, which silently assigns the wrong value to\n // `tmp`. Angular's own `AbstractEmitterVisitor.visitWriteVarExpr`\n // applies the same context-sensitive wrapping (it skips the parens\n // when the assignment is a top-level statement to avoid `(x = 1);`\n // noise; we always wrap for simplicity — the redundant parens at\n // statement level are harmless).\n //\n // This matters specifically on Angular v19/v20, where assignment is\n // represented in the IR via `WriteVarExpr` / `AssignTemporaryExpr →\n // WriteVarExpr`. On v21+ the same construct is represented via\n // `BinaryOperatorExpr(Assign, ...)`, which `visitBinaryOperatorExpr`\n // already wraps via `isAssignmentOperator`. Without these wrappers,\n // `@if (data(); as item)` and similar conditional alias forms emit\n // runtime-broken code on v19/v20.\n visitWriteVarExpr(ast: any) {\n return '(' + ast.name + ' = ' + ast.value.visitExpression(this, null) + ')';\n }\n visitWritePropExpr(ast: any) {\n return (\n '(' +\n ast.receiver.visitExpression(this, null) +\n '.' +\n ast.name +\n ' = ' +\n ast.value.visitExpression(this, null) +\n ')'\n );\n }\n visitWriteKeyExpr(ast: any) {\n return (\n '(' +\n ast.receiver.visitExpression(this, null) +\n '[' +\n ast.index.visitExpression(this, null) +\n '] = ' +\n ast.value.visitExpression(this, null) +\n ')'\n );\n }\n visitInvokeMethodExpr(ast: any) {\n const receiver = ast.receiver.visitExpression(this, null);\n return (\n emitReceiverForMemberAccess(ast.receiver, receiver) +\n '.' +\n ast.name +\n '(' +\n ast.args.map((a: any) => this.emitExpr(a)).join(', ') +\n ')'\n );\n }\n visitTypeofExpr(ast: o.TypeofExpr) {\n return 'typeof ' + ast.expr.visitExpression(this, null);\n }\n visitUnaryOperatorExpr(ast: o.UnaryOperatorExpr) {\n return '-(' + ast.expr.visitExpression(this, null) + ')';\n }\n visitInstantiateExpr(ast: o.InstantiateExpr) {\n return (\n 'new (' +\n ast.classExpr.visitExpression(this, null) +\n ')(' +\n ast.args.map((a: any) => this.emitExpr(a)).join(', ') +\n ')'\n );\n }\n visitCommaExpr(ast: o.CommaExpr) {\n return ast.parts.map((p: any) => p.visitExpression(this, null)).join(', ');\n }\n visitParenthesizedExpr(ast: o.ParenthesizedExpr) {\n return '(' + ast.expr.visitExpression(this, null) + ')';\n }\n visitVoidExpr(ast: o.VoidExpr) {\n return 'void ' + ast.expr.visitExpression(this, null);\n }\n visitSpreadElementExpr(ast: o.SpreadElementExpr) {\n return '...' + (ast as any).expression.visitExpression(this, null);\n }\n visitDynamicImportExpr(ast: o.DynamicImportExpr) {\n return (\n 'import(' +\n (typeof ast.url === 'string'\n ? JSON.stringify(ast.url)\n : ast.url.visitExpression(this, null)) +\n ')'\n );\n }\n visitTemplateLiteralExpr(ast: o.TemplateLiteralExpr) {\n return (\n '`' +\n ast.elements[0].text +\n ast.expressions\n .map(\n (e: any, i: number) =>\n '${' +\n e.visitExpression(this, null) +\n '}' +\n ast.elements[i + 1].text,\n )\n .join('') +\n '`'\n );\n }\n visitTaggedTemplateLiteralExpr(ast: any) {\n const elements = ast.template.elements;\n const expressions = ast.template.expressions;\n const head = elements[0].text;\n const spans = expressions\n .map(\n (e: any, i: number) =>\n '${' + e.visitExpression(this, null) + '}' + elements[i + 1].text,\n )\n .join('');\n return ast.tag.visitExpression(this, null) + '`' + head + spans + '`';\n }\n visitLocalizedString(ast: o.LocalizedString) {\n // Emit the downleveled form:\n // $localize(makeTemplateObject(cooked, raw), expr1, expr2, ...)\n const parts: { cooked: string; raw: string }[] = [ast.serializeI18nHead()];\n for (let i = 1; i < ast.messageParts.length; i++) {\n parts.push(ast.serializeI18nTemplatePart(i));\n }\n const cooked = parts.map((p) => escapeForLocalize(p.cooked)).join(', ');\n const raw = parts.map((p) => escapeForLocalize(p.raw)).join(', ');\n const exprs = ast.expressions.map((e) => this.emitExpr(e));\n return (\n '$localize(' +\n MAKE_TEMPLATE_OBJECT_POLYFILL +\n '([' +\n cooked +\n '], [' +\n raw +\n '])' +\n (exprs.length > 0 ? ', ' + exprs.join(', ') : '') +\n ')'\n );\n }\n visitRegularExpressionLiteral(ast: any) {\n return '/' + (ast.body ?? ast.pattern) + '/' + ast.flags;\n }\n visitTemplateLiteralElementExpr(ast: o.TemplateLiteralElementExpr) {\n return JSON.stringify(ast.text);\n }\n // Statement visitors\n visitReturnStmt(stmt: o.ReturnStatement) {\n return 'return ' + stmt.value.visitExpression(this, null) + ';';\n }\n visitExpressionStmt(stmt: o.ExpressionStatement) {\n return stmt.expr.visitExpression(this, null) + ';';\n }\n visitIfStmt(stmt: o.IfStmt) {\n let s =\n 'if (' +\n stmt.condition.visitExpression(this, null) +\n ') {' +\n stmt.trueCase.map((s2: any) => s2.visitStatement(this, null)).join(' ') +\n '}';\n if (stmt.falseCase.length)\n s +=\n ' else {' +\n stmt.falseCase\n .map((s2: any) => s2.visitStatement(this, null))\n .join(' ') +\n '}';\n return s;\n }\n visitDeclareVarStmt(stmt: o.DeclareVarStmt) {\n const kw = stmt.hasModifier(o.StmtModifier.Final) ? 'const' : 'let';\n return (\n kw +\n ' ' +\n stmt.name +\n (stmt.value ? ' = ' + stmt.value.visitExpression(this, null) : '') +\n ';'\n );\n }\n visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt) {\n return (\n 'function ' +\n stmt.name +\n '(' +\n stmt.params.map((p: any) => p.name).join(', ') +\n ') {' +\n stmt.statements.map((s: any) => s.visitStatement(this, null)).join(' ') +\n '}'\n );\n }\n}\n\nconst stringEmitter = new JSEmitter();\n\n/** Emit Angular output AST expression directly to a JavaScript string. */\nexport function emitAngularExpr(expr: o.Expression): string {\n return expr.visitExpression(stringEmitter, null);\n}\n\n/** Emit Angular output AST statement directly to a JavaScript string. */\nexport function emitAngularStmt(stmt: o.Statement): string {\n return stmt.visitStatement(stringEmitter, null);\n}\n\n/** Set the current source file for WrappedNodeExpr fallback printing. */\nexport function setEmitterSourceFile(sf: ts.SourceFile | undefined): void {\n JSEmitter._currentSourceFile = sf;\n}\n\n/** Set the current source code for OXC-based WrappedNodeExpr slice fallback. */\nexport function setEmitterSourceCode(code: string | undefined): void {\n JSEmitter._currentSourceCode = code;\n}\n"],"mappings":"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"}
@@ -109,7 +109,23 @@ function fastCompilePlugin(pluginOptions) {
109
109
  }
110
110
  }
111
111
  async function handleFastCompileTransform(code, id) {
112
- if (!/(Component|Directive|Pipe|Injectable|NgModule)\(/.test(code)) return;
112
+ if (!/(Component|Directive|Pipe|Injectable|NgModule)\(/.test(code)) {
113
+ const stripped = vite.transformWithOxc ? await vite.transformWithOxc(code, id, {
114
+ lang: "ts",
115
+ sourcemap: true,
116
+ decorator: {
117
+ legacy: false,
118
+ emitDecoratorMetadata: false
119
+ }
120
+ }) : await vite.transformWithEsbuild(code, id, {
121
+ loader: "ts",
122
+ sourcemap: true
123
+ });
124
+ return {
125
+ code: stripped.code,
126
+ map: stripped.map
127
+ };
128
+ }
113
129
  if (pluginOptions.jit) {
114
130
  const result = jitTransform(code, id);
115
131
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"fast-compile-plugin.js","names":[],"sources":["../../../src/lib/fast-compile-plugin.ts"],"sourcesContent":["import { promises as fsPromises } from 'node:fs';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport * as vite from 'vite';\n\nimport * as compilerCli from '@angular/compiler-cli';\nimport {\n defaultClientConditions,\n normalizePath,\n Plugin,\n preprocessCSS,\n ResolvedConfig,\n} from 'vite';\n\nimport {\n compile,\n scanFile,\n scanPackageDts,\n collectImportedPackages,\n collectRelativeReExports,\n jitTransform,\n inlineResourceUrls,\n extractInlineStyles,\n generateHmrCode,\n debugCompile,\n debugRegistry,\n type ComponentRegistry,\n} from './compiler/index.js';\n\nimport {\n TS_EXT_REGEX,\n getTsConfigPath,\n createDepOptimizerConfig,\n type TsConfigResolutionContext,\n} from './utils/plugin-config.js';\nimport { VIRTUAL_RAW_PREFIX, toVirtualRawId } from './utils/virtual-ids.js';\nimport {\n loadVirtualRawModule,\n rewriteHtmlRawImport,\n} from './utils/virtual-resources.js';\nimport { markStylePathSafe } from './utils/safe-module-paths.js';\n\ndeclare global {\n /**\n * Shared convention for out-of-tree compilers (e.g. `@tsrx/analog`) that\n * produce Angular Ivy definitions from a non-TS source format. Populate\n * this map with directive/component metadata for any class fastCompile\n * can't reach through its own tsconfig-driven scan, and the per-compile\n * registry lookup in `fastCompilePlugin` will merge those entries in —\n * so TS `@Component({ imports: [X] })` references to such classes\n * resolve statically instead of hitting the `_unresolved-${className}`\n * sentinel.\n */\n // eslint-disable-next-line no-var\n var __ANALOG_EXTERNAL_REGISTRY__: ComponentRegistry | undefined;\n}\n\nexport interface FastCompilePluginOptions {\n tsconfigGetter: () => string;\n workspaceRoot: string;\n inlineStylesExtension: string;\n jit: boolean;\n liveReload: boolean;\n supportedBrowsers: string[];\n transformFilter?: (code: string, id: string) => boolean;\n isTest: boolean;\n isAstroIntegration: boolean;\n fastCompileMode?: 'full' | 'partial';\n}\n\nexport function fastCompilePlugin(\n pluginOptions: FastCompilePluginOptions,\n): Plugin {\n let resolvedConfig: ResolvedConfig;\n let tsConfigResolutionContext: TsConfigResolutionContext | null = null;\n let watchMode = false;\n\n // fast-compile plugin state\n const registry: ComponentRegistry = new Map();\n const resourceToSource = new Map<string, string>();\n const scannedDtsPackages = new Set<string>();\n let projectRoot = '';\n let useDefineForClassFields = true;\n\n /**\n * Scan a file into the registry, then recursively walk its relative\n * `export *` / `export { … } from './x'` chain so any underlying\n * directive classes also land in the registry. Used both at\n * `buildStart` (for tsconfig path entries) and at dev time (file\n * `add` and `handleHotUpdate`) so newly added barrels stay in sync\n * without requiring a server restart.\n *\n * The `visited` set prevents infinite recursion within a single\n * top-level call. Each fresh scan should pass an empty set (so HMR\n * re-scans aren't blocked by buildStart's earlier visits).\n */\n async function scanBarrelExports(\n file: string,\n visited: Set<string> = new Set(),\n overwrite = false,\n ): Promise<void> {\n if (visited.has(file)) return;\n visited.add(file);\n let code: string;\n try {\n code = await fsPromises.readFile(file, 'utf-8');\n } catch (e) {\n if (debugRegistry.enabled) {\n debugRegistry(\n 'scanBarrelExports: failed to read %s: %s',\n file,\n (e as Error)?.message,\n );\n }\n return;\n }\n const entries = scanFile(code, file);\n for (const entry of entries) {\n // At buildStart we want stable registry entries (don't overwrite\n // an earlier scan with a barrel re-scan); HMR explicitly asks\n // for overwrite so updated metadata replaces stale entries.\n if (overwrite || !registry.has(entry.className)) {\n registry.set(entry.className, entry);\n }\n }\n // Collect every relative re-export specifier via OXC AST so\n // recursive scans can't trip over each other (a shared `/g` regex\n // would have its `lastIndex` reset by each recursive call and\n // silently skip half of an outer barrel's re-exports, which\n // previously left directives like `HlmRadioGroup` unregistered).\n const dir = dirname(file);\n for (const rel of collectRelativeReExports(code, file)) {\n // NodeNext-style libraries write `export * from './foo.js'`\n // even though the source is `./foo.ts`. Strip the ESM\n // extension before probing or the candidates would be\n // `foo.js.ts` / `foo.js/index.ts`, which never exist.\n const normalizedRel = rel.replace(/\\.(?:js|mjs)$/u, '');\n const reExportCandidates = [\n resolve(dir, normalizedRel + '.ts'),\n resolve(dir, normalizedRel, 'index.ts'),\n ];\n let resolved = false;\n for (const candidate of reExportCandidates) {\n try {\n await fsPromises.access(candidate);\n await scanBarrelExports(candidate, visited, overwrite);\n resolved = true;\n break;\n } catch {\n // try next candidate\n }\n }\n if (!resolved && debugRegistry.enabled) {\n debugRegistry(\n 'scanBarrelExports: %s re-export %s did not resolve to %o',\n file,\n rel,\n reExportCandidates,\n );\n }\n }\n }\n\n async function initFastCompile() {\n if (pluginOptions.jit) return; // JIT: no registry scan needed\n\n // Scan all source files to build the registry\n registry.clear();\n scannedDtsPackages.clear();\n const resolvedTsConfigPath = resolveTsConfigPath();\n projectRoot = dirname(resolvedTsConfigPath);\n const config = compilerCli.readConfiguration(resolvedTsConfigPath);\n useDefineForClassFields = config.options?.useDefineForClassFields ?? true;\n\n // Collect candidate files: tsconfig rootNames PLUS the entry points\n // named in `compilerOptions.paths`. App tsconfigs typically only\n // include the app's own sources, so workspace library entry barrels\n // (e.g. `HlmSelectImports = [HlmSelect, HlmSelectContent, ...] as\n // const`) live outside `rootNames` and would otherwise miss the\n // initial scan. The compiler then can't see that `HlmSelectImports`\n // is a tuple barrel and emits the bare identifier into the parent\n // component's `dependencies()` list, where Angular's runtime\n // silently drops it because arrays don't have a directive def.\n const candidates = new Set<string>(config.rootNames);\n const tsPaths = config.options?.paths;\n const baseUrl = (config.options?.baseUrl ?? projectRoot) as string;\n if (tsPaths) {\n for (const targets of Object.values(tsPaths)) {\n for (const target of targets as string[]) {\n // Skip wildcard patterns — entry barrels are normally exact\n // file paths like \"libs/helm/select/src/index.ts\".\n if (target.includes('*')) continue;\n candidates.add(resolve(baseUrl, target));\n }\n }\n }\n const results = await Promise.all(\n Array.from(candidates).map(async (file) => {\n try {\n const code = await fsPromises.readFile(file, 'utf-8');\n return scanFile(code, file);\n } catch (e) {\n if (debugRegistry.enabled) {\n debugRegistry(\n 'initFastCompile: skipping unreadable %s: %s',\n file,\n (e as Error)?.message,\n );\n }\n return []; // Skip unreadable files\n }\n }),\n );\n\n for (const entries of results) {\n for (const entry of entries) {\n registry.set(entry.className, entry);\n }\n }\n\n // Library barrels typically `export * from './lib/...'` rather than\n // declaring directives directly, so the entry file alone gives us\n // the tuple consts but not the directive classes they reference.\n // Walk the relative `export *` chain so the underlying classes also\n // land in the registry. Use a SHARED visited set across all\n // barrels so recursive walks don't double-scan a file that's\n // re-exported from multiple entry points.\n const buildStartVisited = new Set<string>();\n if (tsPaths) {\n const barrelCandidates: string[] = [];\n for (const targets of Object.values(tsPaths)) {\n for (const target of targets as string[]) {\n if (target.includes('*')) continue;\n barrelCandidates.push(resolve(baseUrl, target));\n }\n }\n await Promise.all(\n barrelCandidates.map((c) => scanBarrelExports(c, buildStartVisited)),\n );\n }\n debugRegistry(\n 'initFastCompile done: %d entries from %d candidate files',\n registry.size,\n candidates.size,\n );\n }\n\n function ensureDtsRegistryForSource(code: string, id: string) {\n for (const pkg of collectImportedPackages(code, id)) {\n if (scannedDtsPackages.has(pkg)) continue;\n scannedDtsPackages.add(pkg);\n\n try {\n const dtsEntries = scanPackageDts(pkg, projectRoot);\n for (const entry of dtsEntries) {\n if (!registry.has(entry.className)) {\n registry.set(entry.className, entry);\n }\n }\n } catch {\n // Package may not have .d.ts files or may not be Angular\n }\n }\n }\n\n async function handleFastCompileTransform(\n code: string,\n id: string,\n ): Promise<{ code: string; map: any } | undefined> {\n if (!/(Component|Directive|Pipe|Injectable|NgModule)\\(/.test(code)) {\n // Non-Angular file — leave it alone so a downstream plugin (or\n // Vite's built-in TS handler) can process it.\n return undefined;\n }\n\n // JIT mode\n if (pluginOptions.jit) {\n const result = jitTransform(code, id);\n return { code: result.code, map: result.map };\n }\n\n // Inline external templateUrl/styleUrl(s) into the source before compilation\n code = inlineResourceUrls(code, id);\n\n // Pre-resolve inline styles that need preprocessing (SCSS/Sass/Less)\n let resolvedStyles: Map<string, string> | undefined;\n let resolvedInlineStyles: Map<number, string> | undefined;\n\n if (pluginOptions.inlineStylesExtension !== 'css') {\n const styleStrings = extractInlineStyles(code, id);\n\n if (styleStrings.length > 0) {\n resolvedInlineStyles = new Map();\n for (let i = 0; i < styleStrings.length; i++) {\n try {\n const fakePath = id.replace(\n /\\.ts$/,\n `.inline-${i}.${pluginOptions.inlineStylesExtension}`,\n );\n const processed = await preprocessCSS(\n styleStrings[i],\n fakePath,\n resolvedConfig,\n );\n resolvedInlineStyles.set(i, processed.code);\n } catch (e) {\n if (debugCompile.enabled) {\n debugCompile(\n 'inline style #%d preprocessing failed in %s: %s',\n i,\n id,\n (e as Error)?.message,\n );\n }\n // Skip styles that can't be preprocessed\n }\n }\n if (resolvedInlineStyles.size === 0) resolvedInlineStyles = undefined;\n }\n }\n\n ensureDtsRegistryForSource(code, id);\n\n // Merge entries from the shared external-registry global into this\n // compile's lookup view. Convention: out-of-tree compilers populate\n // `globalThis.__ANALOG_EXTERNAL_REGISTRY__` with directive metadata\n // for classes fastCompile can't reach through its tsconfig-driven\n // scan (e.g. `.tsrx` files compiled by `@tsrx/analog`). Without this\n // merge, a TS `@Component({ imports: [X] })` that references such a\n // class hits `_unresolved-${className}` as its selector and the tag\n // never matches at runtime.\n let compileRegistry: ComponentRegistry = registry;\n const externalRegistry = globalThis.__ANALOG_EXTERNAL_REGISTRY__;\n if (externalRegistry && externalRegistry.size > 0) {\n compileRegistry = new Map(registry);\n for (const [k, v] of externalRegistry) compileRegistry.set(k, v);\n }\n\n const result = compile(code, id, {\n registry: compileRegistry,\n resolvedStyles,\n resolvedInlineStyles,\n useDefineForClassFields,\n compilationMode: pluginOptions.fastCompileMode,\n });\n\n // Track resource dependencies for HMR\n for (const dep of result.resourceDependencies) {\n resourceToSource.set(dep, id);\n }\n\n // Strip TypeScript-only syntax\n const stripped = vite.transformWithOxc\n ? await vite.transformWithOxc(result.code, id, {\n lang: 'ts',\n sourcemap: false,\n decorator: { legacy: false, emitDecoratorMetadata: false },\n })\n : await vite.transformWithEsbuild(result.code, id, {\n loader: 'ts',\n sourcemap: false,\n });\n let outputCode = stripped.code;\n\n // Append HMR code in dev mode\n if (watchMode && pluginOptions.liveReload) {\n const fileDeclarations = [...registry.values()].filter(\n (e) => e.fileName === id,\n );\n if (fileDeclarations.length > 0) {\n const localDepClassNames = fileDeclarations.map((e) => e.className);\n outputCode += generateHmrCode(fileDeclarations, localDepClassNames);\n }\n }\n\n return { code: outputCode, map: result.map };\n }\n\n function resolveTsConfigPath() {\n const { root, isProd, isLib } = tsConfigResolutionContext!;\n return getTsConfigPath(\n root,\n pluginOptions.tsconfigGetter(),\n isProd,\n pluginOptions.isTest,\n isLib,\n );\n }\n\n return {\n name: '@analogjs/vite-plugin-angular-fast-compile',\n enforce: 'pre' as const,\n async config(config, { command }) {\n watchMode = command === 'serve';\n const isProd =\n config.mode === 'production' ||\n process.env['NODE_ENV'] === 'production';\n\n tsConfigResolutionContext = {\n root: config.root || '.',\n isProd,\n isLib: !!config?.build?.lib,\n };\n\n const preliminaryTsConfigPath = resolveTsConfigPath();\n\n const depOptimizer = createDepOptimizerConfig({\n tsconfig: preliminaryTsConfigPath,\n isProd,\n jit: pluginOptions.jit,\n watchMode,\n isTest: pluginOptions.isTest,\n isAstroIntegration: pluginOptions.isAstroIntegration,\n });\n\n return {\n ...(vite.rolldownVersion ? { oxc: {} as any } : { esbuild: false }),\n ...depOptimizer,\n resolve: {\n conditions: [\n ...depOptimizer.resolve.conditions,\n ...(config.resolve?.conditions || defaultClientConditions),\n ],\n },\n };\n },\n configResolved(config) {\n resolvedConfig = config;\n },\n configureServer(server) {\n // Watch for new .ts files and scan them into the registry. Use\n // the barrel-aware scanner so a newly added re-export entry\n // (`export * from './x'`) also expands its underlying directive\n // classes — otherwise the registry stays stale until restart.\n server.watcher.on('add', async (filePath) => {\n if (\n filePath.endsWith('.ts') &&\n !filePath.endsWith('.spec.ts') &&\n !filePath.endsWith('.d.ts')\n ) {\n await scanBarrelExports(filePath, new Set(), true);\n }\n });\n },\n async buildStart() {\n await initFastCompile();\n },\n async handleHotUpdate(ctx) {\n // Resource file changes → invalidate parent .ts module\n if (resourceToSource.has(ctx.file)) {\n const parentSource = resourceToSource.get(ctx.file)!;\n const parentModule = ctx.server.moduleGraph.getModuleById(parentSource);\n if (parentModule) {\n return [parentModule];\n }\n }\n\n if (TS_EXT_REGEX.test(ctx.file)) {\n const [fileId] = ctx.file.split('?');\n\n // Remove old entries from this file\n const oldEntries = [...registry.entries()]\n .filter(([_, v]) => v.fileName === fileId)\n .map(([k]) => k);\n for (const key of oldEntries) {\n registry.delete(key);\n }\n\n // Rescan the changed file via the barrel-aware scanner so an\n // edited barrel re-export picks up newly-referenced files.\n // Pass overwrite=true so updated metadata replaces stale\n // entries from the previous scan.\n await scanBarrelExports(fileId, new Set(), true);\n }\n\n // Let Vite handle the rest — the transform hook will recompile\n return ctx.modules;\n },\n resolveId(id, importer) {\n if (id.startsWith(VIRTUAL_RAW_PREFIX)) {\n return `\\0${id}`;\n }\n\n if (pluginOptions.jit && id.startsWith('angular:jit:')) {\n const filePath = normalizePath(\n resolve(dirname(importer as string), id.split(';')[1]),\n );\n if (id.includes(':style')) {\n markStylePathSafe(resolvedConfig, filePath);\n return filePath + '?inline';\n }\n return toVirtualRawId(filePath);\n }\n\n const rawRewrite = rewriteHtmlRawImport(id, importer);\n if (rawRewrite) return rawRewrite;\n\n // User `.scss?inline` / `.css?inline` imports: resolve and mark\n // safe so Vite's native CSS pipeline handles them.\n if (/\\.(css|scss|sass|less)\\?inline$/.test(id) && importer) {\n const filePath = id.split('?')[0];\n const resolved = isAbsolute(filePath)\n ? normalizePath(filePath)\n : normalizePath(resolve(dirname(importer), filePath));\n markStylePathSafe(resolvedConfig, resolved);\n return resolved + '?inline';\n }\n\n return undefined;\n },\n async load(id) {\n const rawModule = await loadVirtualRawModule(this, id);\n if (rawModule !== undefined) return rawModule;\n\n // Vitest fallback: module-runner can skip resolveId, so the bare\n // ?inline query reaches load. Mark safe and let Vite handle it.\n if (/\\.(css|scss|sass|less)\\?inline$/.test(id)) {\n markStylePathSafe(resolvedConfig, id.split('?')[0]);\n }\n\n return;\n },\n transform: {\n filter: {\n id: {\n include: [TS_EXT_REGEX],\n exclude: [/node_modules/, 'type=script', '@ng/component'],\n },\n },\n async handler(code, id) {\n if (\n pluginOptions.transformFilter &&\n !(pluginOptions.transformFilter(code, id) ?? true)\n ) {\n return;\n }\n\n if (id.includes('.ts?')) {\n id = id.replace(/\\?(.*)/, '');\n }\n return handleFastCompileTransform(code, id);\n },\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"fast-compile-plugin.js","names":[],"sources":["../../../src/lib/fast-compile-plugin.ts"],"sourcesContent":["import { promises as fsPromises } from 'node:fs';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport * as vite from 'vite';\n\nimport * as compilerCli from '@angular/compiler-cli';\nimport {\n defaultClientConditions,\n normalizePath,\n Plugin,\n preprocessCSS,\n ResolvedConfig,\n} from 'vite';\n\nimport {\n compile,\n scanFile,\n scanPackageDts,\n collectImportedPackages,\n collectRelativeReExports,\n jitTransform,\n inlineResourceUrls,\n extractInlineStyles,\n generateHmrCode,\n debugCompile,\n debugRegistry,\n type ComponentRegistry,\n} from './compiler/index.js';\n\nimport {\n TS_EXT_REGEX,\n getTsConfigPath,\n createDepOptimizerConfig,\n type TsConfigResolutionContext,\n} from './utils/plugin-config.js';\nimport { VIRTUAL_RAW_PREFIX, toVirtualRawId } from './utils/virtual-ids.js';\nimport {\n loadVirtualRawModule,\n rewriteHtmlRawImport,\n} from './utils/virtual-resources.js';\nimport { markStylePathSafe } from './utils/safe-module-paths.js';\n\ndeclare global {\n /**\n * Shared convention for out-of-tree compilers (e.g. `@tsrx/analog`) that\n * produce Angular Ivy definitions from a non-TS source format. Populate\n * this map with directive/component metadata for any class fastCompile\n * can't reach through its own tsconfig-driven scan, and the per-compile\n * registry lookup in `fastCompilePlugin` will merge those entries in —\n * so TS `@Component({ imports: [X] })` references to such classes\n * resolve statically instead of hitting the `_unresolved-${className}`\n * sentinel.\n */\n // eslint-disable-next-line no-var\n var __ANALOG_EXTERNAL_REGISTRY__: ComponentRegistry | undefined;\n}\n\nexport interface FastCompilePluginOptions {\n tsconfigGetter: () => string;\n workspaceRoot: string;\n inlineStylesExtension: string;\n jit: boolean;\n liveReload: boolean;\n supportedBrowsers: string[];\n transformFilter?: (code: string, id: string) => boolean;\n isTest: boolean;\n isAstroIntegration: boolean;\n fastCompileMode?: 'full' | 'partial';\n}\n\nexport function fastCompilePlugin(\n pluginOptions: FastCompilePluginOptions,\n): Plugin {\n let resolvedConfig: ResolvedConfig;\n let tsConfigResolutionContext: TsConfigResolutionContext | null = null;\n let watchMode = false;\n\n // fast-compile plugin state\n const registry: ComponentRegistry = new Map();\n const resourceToSource = new Map<string, string>();\n const scannedDtsPackages = new Set<string>();\n let projectRoot = '';\n let useDefineForClassFields = true;\n\n /**\n * Scan a file into the registry, then recursively walk its relative\n * `export *` / `export { … } from './x'` chain so any underlying\n * directive classes also land in the registry. Used both at\n * `buildStart` (for tsconfig path entries) and at dev time (file\n * `add` and `handleHotUpdate`) so newly added barrels stay in sync\n * without requiring a server restart.\n *\n * The `visited` set prevents infinite recursion within a single\n * top-level call. Each fresh scan should pass an empty set (so HMR\n * re-scans aren't blocked by buildStart's earlier visits).\n */\n async function scanBarrelExports(\n file: string,\n visited: Set<string> = new Set(),\n overwrite = false,\n ): Promise<void> {\n if (visited.has(file)) return;\n visited.add(file);\n let code: string;\n try {\n code = await fsPromises.readFile(file, 'utf-8');\n } catch (e) {\n if (debugRegistry.enabled) {\n debugRegistry(\n 'scanBarrelExports: failed to read %s: %s',\n file,\n (e as Error)?.message,\n );\n }\n return;\n }\n const entries = scanFile(code, file);\n for (const entry of entries) {\n // At buildStart we want stable registry entries (don't overwrite\n // an earlier scan with a barrel re-scan); HMR explicitly asks\n // for overwrite so updated metadata replaces stale entries.\n if (overwrite || !registry.has(entry.className)) {\n registry.set(entry.className, entry);\n }\n }\n // Collect every relative re-export specifier via OXC AST so\n // recursive scans can't trip over each other (a shared `/g` regex\n // would have its `lastIndex` reset by each recursive call and\n // silently skip half of an outer barrel's re-exports, which\n // previously left directives like `HlmRadioGroup` unregistered).\n const dir = dirname(file);\n for (const rel of collectRelativeReExports(code, file)) {\n // NodeNext-style libraries write `export * from './foo.js'`\n // even though the source is `./foo.ts`. Strip the ESM\n // extension before probing or the candidates would be\n // `foo.js.ts` / `foo.js/index.ts`, which never exist.\n const normalizedRel = rel.replace(/\\.(?:js|mjs)$/u, '');\n const reExportCandidates = [\n resolve(dir, normalizedRel + '.ts'),\n resolve(dir, normalizedRel, 'index.ts'),\n ];\n let resolved = false;\n for (const candidate of reExportCandidates) {\n try {\n await fsPromises.access(candidate);\n await scanBarrelExports(candidate, visited, overwrite);\n resolved = true;\n break;\n } catch {\n // try next candidate\n }\n }\n if (!resolved && debugRegistry.enabled) {\n debugRegistry(\n 'scanBarrelExports: %s re-export %s did not resolve to %o',\n file,\n rel,\n reExportCandidates,\n );\n }\n }\n }\n\n async function initFastCompile() {\n if (pluginOptions.jit) return; // JIT: no registry scan needed\n\n // Scan all source files to build the registry\n registry.clear();\n scannedDtsPackages.clear();\n const resolvedTsConfigPath = resolveTsConfigPath();\n projectRoot = dirname(resolvedTsConfigPath);\n const config = compilerCli.readConfiguration(resolvedTsConfigPath);\n useDefineForClassFields = config.options?.useDefineForClassFields ?? true;\n\n // Collect candidate files: tsconfig rootNames PLUS the entry points\n // named in `compilerOptions.paths`. App tsconfigs typically only\n // include the app's own sources, so workspace library entry barrels\n // (e.g. `HlmSelectImports = [HlmSelect, HlmSelectContent, ...] as\n // const`) live outside `rootNames` and would otherwise miss the\n // initial scan. The compiler then can't see that `HlmSelectImports`\n // is a tuple barrel and emits the bare identifier into the parent\n // component's `dependencies()` list, where Angular's runtime\n // silently drops it because arrays don't have a directive def.\n const candidates = new Set<string>(config.rootNames);\n const tsPaths = config.options?.paths;\n const baseUrl = (config.options?.baseUrl ?? projectRoot) as string;\n if (tsPaths) {\n for (const targets of Object.values(tsPaths)) {\n for (const target of targets as string[]) {\n // Skip wildcard patterns — entry barrels are normally exact\n // file paths like \"libs/helm/select/src/index.ts\".\n if (target.includes('*')) continue;\n candidates.add(resolve(baseUrl, target));\n }\n }\n }\n const results = await Promise.all(\n Array.from(candidates).map(async (file) => {\n try {\n const code = await fsPromises.readFile(file, 'utf-8');\n return scanFile(code, file);\n } catch (e) {\n if (debugRegistry.enabled) {\n debugRegistry(\n 'initFastCompile: skipping unreadable %s: %s',\n file,\n (e as Error)?.message,\n );\n }\n return []; // Skip unreadable files\n }\n }),\n );\n\n for (const entries of results) {\n for (const entry of entries) {\n registry.set(entry.className, entry);\n }\n }\n\n // Library barrels typically `export * from './lib/...'` rather than\n // declaring directives directly, so the entry file alone gives us\n // the tuple consts but not the directive classes they reference.\n // Walk the relative `export *` chain so the underlying classes also\n // land in the registry. Use a SHARED visited set across all\n // barrels so recursive walks don't double-scan a file that's\n // re-exported from multiple entry points.\n const buildStartVisited = new Set<string>();\n if (tsPaths) {\n const barrelCandidates: string[] = [];\n for (const targets of Object.values(tsPaths)) {\n for (const target of targets as string[]) {\n if (target.includes('*')) continue;\n barrelCandidates.push(resolve(baseUrl, target));\n }\n }\n await Promise.all(\n barrelCandidates.map((c) => scanBarrelExports(c, buildStartVisited)),\n );\n }\n debugRegistry(\n 'initFastCompile done: %d entries from %d candidate files',\n registry.size,\n candidates.size,\n );\n }\n\n function ensureDtsRegistryForSource(code: string, id: string) {\n for (const pkg of collectImportedPackages(code, id)) {\n if (scannedDtsPackages.has(pkg)) continue;\n scannedDtsPackages.add(pkg);\n\n try {\n const dtsEntries = scanPackageDts(pkg, projectRoot);\n for (const entry of dtsEntries) {\n if (!registry.has(entry.className)) {\n registry.set(entry.className, entry);\n }\n }\n } catch {\n // Package may not have .d.ts files or may not be Angular\n }\n }\n }\n\n async function handleFastCompileTransform(\n code: string,\n id: string,\n ): Promise<{ code: string; map: any } | undefined> {\n if (!/(Component|Directive|Pipe|Injectable|NgModule)\\(/.test(code)) {\n // Non-Angular file — strip TS-only syntax ourselves so barrels\n // like `export { Foo, type Bar } from './x'` and other TS-only\n // forms don't leak unstripped to Rolldown. In rolldown-vite the\n // built-in `vite:oxc` strip is registered as a Rust-side native\n // plugin (`viteTransformPlugin` from `rolldown/experimental`); if\n // its hook-filter treats files our `transform.filter.id.include`\n // claimed as already-handled, no JS-side fallback runs and raw\n // TS reaches the parser → `SyntaxError: Unexpected identifier`.\n const stripped = vite.transformWithOxc\n ? await vite.transformWithOxc(code, id, {\n lang: 'ts',\n sourcemap: true,\n decorator: { legacy: false, emitDecoratorMetadata: false },\n })\n : await vite.transformWithEsbuild(code, id, {\n loader: 'ts',\n sourcemap: true,\n });\n return { code: stripped.code, map: stripped.map };\n }\n\n // JIT mode\n if (pluginOptions.jit) {\n const result = jitTransform(code, id);\n return { code: result.code, map: result.map };\n }\n\n // Inline external templateUrl/styleUrl(s) into the source before compilation\n code = inlineResourceUrls(code, id);\n\n // Pre-resolve inline styles that need preprocessing (SCSS/Sass/Less)\n let resolvedStyles: Map<string, string> | undefined;\n let resolvedInlineStyles: Map<number, string> | undefined;\n\n if (pluginOptions.inlineStylesExtension !== 'css') {\n const styleStrings = extractInlineStyles(code, id);\n\n if (styleStrings.length > 0) {\n resolvedInlineStyles = new Map();\n for (let i = 0; i < styleStrings.length; i++) {\n try {\n const fakePath = id.replace(\n /\\.ts$/,\n `.inline-${i}.${pluginOptions.inlineStylesExtension}`,\n );\n const processed = await preprocessCSS(\n styleStrings[i],\n fakePath,\n resolvedConfig,\n );\n resolvedInlineStyles.set(i, processed.code);\n } catch (e) {\n if (debugCompile.enabled) {\n debugCompile(\n 'inline style #%d preprocessing failed in %s: %s',\n i,\n id,\n (e as Error)?.message,\n );\n }\n // Skip styles that can't be preprocessed\n }\n }\n if (resolvedInlineStyles.size === 0) resolvedInlineStyles = undefined;\n }\n }\n\n ensureDtsRegistryForSource(code, id);\n\n // Merge entries from the shared external-registry global into this\n // compile's lookup view. Convention: out-of-tree compilers populate\n // `globalThis.__ANALOG_EXTERNAL_REGISTRY__` with directive metadata\n // for classes fastCompile can't reach through its tsconfig-driven\n // scan (e.g. `.tsrx` files compiled by `@tsrx/analog`). Without this\n // merge, a TS `@Component({ imports: [X] })` that references such a\n // class hits `_unresolved-${className}` as its selector and the tag\n // never matches at runtime.\n let compileRegistry: ComponentRegistry = registry;\n const externalRegistry = globalThis.__ANALOG_EXTERNAL_REGISTRY__;\n if (externalRegistry && externalRegistry.size > 0) {\n compileRegistry = new Map(registry);\n for (const [k, v] of externalRegistry) compileRegistry.set(k, v);\n }\n\n const result = compile(code, id, {\n registry: compileRegistry,\n resolvedStyles,\n resolvedInlineStyles,\n useDefineForClassFields,\n compilationMode: pluginOptions.fastCompileMode,\n });\n\n // Track resource dependencies for HMR\n for (const dep of result.resourceDependencies) {\n resourceToSource.set(dep, id);\n }\n\n // Strip TypeScript-only syntax\n const stripped = vite.transformWithOxc\n ? await vite.transformWithOxc(result.code, id, {\n lang: 'ts',\n sourcemap: false,\n decorator: { legacy: false, emitDecoratorMetadata: false },\n })\n : await vite.transformWithEsbuild(result.code, id, {\n loader: 'ts',\n sourcemap: false,\n });\n let outputCode = stripped.code;\n\n // Append HMR code in dev mode\n if (watchMode && pluginOptions.liveReload) {\n const fileDeclarations = [...registry.values()].filter(\n (e) => e.fileName === id,\n );\n if (fileDeclarations.length > 0) {\n const localDepClassNames = fileDeclarations.map((e) => e.className);\n outputCode += generateHmrCode(fileDeclarations, localDepClassNames);\n }\n }\n\n return { code: outputCode, map: result.map };\n }\n\n function resolveTsConfigPath() {\n const { root, isProd, isLib } = tsConfigResolutionContext!;\n return getTsConfigPath(\n root,\n pluginOptions.tsconfigGetter(),\n isProd,\n pluginOptions.isTest,\n isLib,\n );\n }\n\n return {\n name: '@analogjs/vite-plugin-angular-fast-compile',\n enforce: 'pre' as const,\n async config(config, { command }) {\n watchMode = command === 'serve';\n const isProd =\n config.mode === 'production' ||\n process.env['NODE_ENV'] === 'production';\n\n tsConfigResolutionContext = {\n root: config.root || '.',\n isProd,\n isLib: !!config?.build?.lib,\n };\n\n const preliminaryTsConfigPath = resolveTsConfigPath();\n\n const depOptimizer = createDepOptimizerConfig({\n tsconfig: preliminaryTsConfigPath,\n isProd,\n jit: pluginOptions.jit,\n watchMode,\n isTest: pluginOptions.isTest,\n isAstroIntegration: pluginOptions.isAstroIntegration,\n });\n\n return {\n ...(vite.rolldownVersion ? { oxc: {} as any } : { esbuild: false }),\n ...depOptimizer,\n resolve: {\n conditions: [\n ...depOptimizer.resolve.conditions,\n ...(config.resolve?.conditions || defaultClientConditions),\n ],\n },\n };\n },\n configResolved(config) {\n resolvedConfig = config;\n },\n configureServer(server) {\n // Watch for new .ts files and scan them into the registry. Use\n // the barrel-aware scanner so a newly added re-export entry\n // (`export * from './x'`) also expands its underlying directive\n // classes — otherwise the registry stays stale until restart.\n server.watcher.on('add', async (filePath) => {\n if (\n filePath.endsWith('.ts') &&\n !filePath.endsWith('.spec.ts') &&\n !filePath.endsWith('.d.ts')\n ) {\n await scanBarrelExports(filePath, new Set(), true);\n }\n });\n },\n async buildStart() {\n await initFastCompile();\n },\n async handleHotUpdate(ctx) {\n // Resource file changes → invalidate parent .ts module\n if (resourceToSource.has(ctx.file)) {\n const parentSource = resourceToSource.get(ctx.file)!;\n const parentModule = ctx.server.moduleGraph.getModuleById(parentSource);\n if (parentModule) {\n return [parentModule];\n }\n }\n\n if (TS_EXT_REGEX.test(ctx.file)) {\n const [fileId] = ctx.file.split('?');\n\n // Remove old entries from this file\n const oldEntries = [...registry.entries()]\n .filter(([_, v]) => v.fileName === fileId)\n .map(([k]) => k);\n for (const key of oldEntries) {\n registry.delete(key);\n }\n\n // Rescan the changed file via the barrel-aware scanner so an\n // edited barrel re-export picks up newly-referenced files.\n // Pass overwrite=true so updated metadata replaces stale\n // entries from the previous scan.\n await scanBarrelExports(fileId, new Set(), true);\n }\n\n // Let Vite handle the rest — the transform hook will recompile\n return ctx.modules;\n },\n resolveId(id, importer) {\n if (id.startsWith(VIRTUAL_RAW_PREFIX)) {\n return `\\0${id}`;\n }\n\n if (pluginOptions.jit && id.startsWith('angular:jit:')) {\n const filePath = normalizePath(\n resolve(dirname(importer as string), id.split(';')[1]),\n );\n if (id.includes(':style')) {\n markStylePathSafe(resolvedConfig, filePath);\n return filePath + '?inline';\n }\n return toVirtualRawId(filePath);\n }\n\n const rawRewrite = rewriteHtmlRawImport(id, importer);\n if (rawRewrite) return rawRewrite;\n\n // User `.scss?inline` / `.css?inline` imports: resolve and mark\n // safe so Vite's native CSS pipeline handles them.\n if (/\\.(css|scss|sass|less)\\?inline$/.test(id) && importer) {\n const filePath = id.split('?')[0];\n const resolved = isAbsolute(filePath)\n ? normalizePath(filePath)\n : normalizePath(resolve(dirname(importer), filePath));\n markStylePathSafe(resolvedConfig, resolved);\n return resolved + '?inline';\n }\n\n return undefined;\n },\n async load(id) {\n const rawModule = await loadVirtualRawModule(this, id);\n if (rawModule !== undefined) return rawModule;\n\n // Vitest fallback: module-runner can skip resolveId, so the bare\n // ?inline query reaches load. Mark safe and let Vite handle it.\n if (/\\.(css|scss|sass|less)\\?inline$/.test(id)) {\n markStylePathSafe(resolvedConfig, id.split('?')[0]);\n }\n\n return;\n },\n transform: {\n filter: {\n id: {\n include: [TS_EXT_REGEX],\n exclude: [/node_modules/, 'type=script', '@ng/component'],\n },\n },\n async handler(code, id) {\n if (\n pluginOptions.transformFilter &&\n !(pluginOptions.transformFilter(code, id) ?? true)\n ) {\n return;\n }\n\n if (id.includes('.ts?')) {\n id = id.replace(/\\?(.*)/, '');\n }\n return handleFastCompileTransform(code, id);\n },\n },\n };\n}\n"],"mappings":"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"}