@analogjs/vite-plugin-angular 3.0.0-alpha.9 → 3.0.0-alpha.90

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.
Files changed (184) hide show
  1. package/README.md +121 -0
  2. package/migrations/migrate-setup-vitest/migrate-setup-vitest.d.ts +2 -0
  3. package/migrations/migrate-setup-vitest/migrate-setup-vitest.js +49 -0
  4. package/migrations/migrate-setup-vitest/migrate-setup-vitest.js.map +1 -0
  5. package/migrations/migration.json +7 -1
  6. package/migrations/migrations.json +9 -0
  7. package/migrations/update-3-0-0/migrate-setup-vitest.d.ts +2 -0
  8. package/migrations/update-3-0-0/migrate-setup-vitest.js +36 -0
  9. package/migrations/update-3-0-0/migrate-setup-vitest.js.map +1 -0
  10. package/package.json +49 -8
  11. package/src/index.d.ts +7 -2
  12. package/src/index.js +3 -1
  13. package/src/index.js.map +1 -1
  14. package/src/lib/analog-plugin-interop.d.ts +78 -0
  15. package/src/lib/analog-plugin-interop.js +97 -0
  16. package/src/lib/analog-plugin-interop.js.map +1 -0
  17. package/src/lib/angular-build-optimizer-plugin.d.ts +8 -4
  18. package/src/lib/angular-build-optimizer-plugin.js +66 -31
  19. package/src/lib/angular-build-optimizer-plugin.js.map +1 -1
  20. package/src/lib/angular-jit-plugin.d.ts +3 -3
  21. package/src/lib/angular-jit-plugin.js +28 -6
  22. package/src/lib/angular-jit-plugin.js.map +1 -1
  23. package/src/lib/angular-pending-tasks.plugin.d.ts +7 -7
  24. package/src/lib/angular-pending-tasks.plugin.js.map +1 -1
  25. package/src/lib/angular-vite-plugin.d.ts +79 -69
  26. package/src/lib/angular-vite-plugin.js +774 -352
  27. package/src/lib/angular-vite-plugin.js.map +1 -1
  28. package/src/lib/angular-vitest-plugin.d.ts +26 -21
  29. package/src/lib/angular-vitest-plugin.js +34 -25
  30. package/src/lib/angular-vitest-plugin.js.map +1 -1
  31. package/src/lib/compilation-api/compilation-api-plugin.d.ts +24 -0
  32. package/src/lib/compilation-api/compilation-api-plugin.js +547 -0
  33. package/src/lib/compilation-api/compilation-api-plugin.js.map +1 -0
  34. package/src/lib/compilation-api/index.d.ts +1 -0
  35. package/src/lib/compilation-api/index.js +1 -0
  36. package/src/lib/compiler/angular-version.d.ts +19 -0
  37. package/src/lib/compiler/angular-version.js +31 -0
  38. package/src/lib/compiler/angular-version.js.map +1 -0
  39. package/src/lib/compiler/class-field-lowering.d.ts +23 -0
  40. package/src/lib/compiler/class-field-lowering.js +131 -0
  41. package/src/lib/compiler/class-field-lowering.js.map +1 -0
  42. package/src/lib/compiler/compile.d.ts +50 -0
  43. package/src/lib/compiler/compile.js +814 -0
  44. package/src/lib/compiler/compile.js.map +1 -0
  45. package/src/lib/compiler/constants.d.ts +24 -0
  46. package/src/lib/compiler/constants.js +59 -0
  47. package/src/lib/compiler/constants.js.map +1 -0
  48. package/src/lib/compiler/debug.d.ts +22 -0
  49. package/src/lib/compiler/debug.js +20 -0
  50. package/src/lib/compiler/debug.js.map +1 -0
  51. package/src/lib/compiler/defer.d.ts +57 -0
  52. package/src/lib/compiler/defer.js +154 -0
  53. package/src/lib/compiler/defer.js.map +1 -0
  54. package/src/lib/compiler/dts-reader.d.ts +45 -0
  55. package/src/lib/compiler/dts-reader.js +395 -0
  56. package/src/lib/compiler/dts-reader.js.map +1 -0
  57. package/src/lib/compiler/hmr.d.ts +16 -0
  58. package/src/lib/compiler/hmr.js +69 -0
  59. package/src/lib/compiler/hmr.js.map +1 -0
  60. package/src/lib/compiler/index.d.ts +8 -0
  61. package/src/lib/compiler/index.js +8 -0
  62. package/src/lib/compiler/jit-metadata.d.ts +14 -0
  63. package/src/lib/compiler/jit-metadata.js +168 -0
  64. package/src/lib/compiler/jit-metadata.js.map +1 -0
  65. package/src/lib/compiler/jit-transform.d.ts +24 -0
  66. package/src/lib/compiler/jit-transform.js +204 -0
  67. package/src/lib/compiler/jit-transform.js.map +1 -0
  68. package/src/lib/compiler/js-emitter.d.ts +10 -0
  69. package/src/lib/compiler/js-emitter.js +446 -0
  70. package/src/lib/compiler/js-emitter.js.map +1 -0
  71. package/src/lib/compiler/metadata.d.ts +36 -0
  72. package/src/lib/compiler/metadata.js +710 -0
  73. package/src/lib/compiler/metadata.js.map +1 -0
  74. package/src/lib/compiler/registry.d.ts +56 -0
  75. package/src/lib/compiler/registry.js +176 -0
  76. package/src/lib/compiler/registry.js.map +1 -0
  77. package/src/lib/compiler/resource-inliner.d.ts +43 -0
  78. package/src/lib/compiler/resource-inliner.js +230 -0
  79. package/src/lib/compiler/resource-inliner.js.map +1 -0
  80. package/src/lib/compiler/style-ast.d.ts +9 -0
  81. package/src/lib/compiler/style-ast.js +54 -0
  82. package/src/lib/compiler/style-ast.js.map +1 -0
  83. package/src/lib/compiler/styles.d.ts +13 -0
  84. package/src/lib/compiler/test-helpers.d.ts +7 -0
  85. package/src/lib/compiler/type-elision.d.ts +28 -0
  86. package/src/lib/compiler/type-elision.js +211 -0
  87. package/src/lib/compiler/type-elision.js.map +1 -0
  88. package/src/lib/compiler/utils.d.ts +40 -0
  89. package/src/lib/compiler/utils.js +76 -0
  90. package/src/lib/compiler/utils.js.map +1 -0
  91. package/src/lib/compiler-plugin.d.ts +13 -12
  92. package/src/lib/compiler-plugin.js +18 -18
  93. package/src/lib/compiler-plugin.js.map +1 -1
  94. package/src/lib/component-resolvers.d.ts +20 -4
  95. package/src/lib/component-resolvers.js +34 -20
  96. package/src/lib/component-resolvers.js.map +1 -1
  97. package/src/lib/encapsulation-plugin.d.ts +23 -0
  98. package/src/lib/encapsulation-plugin.js +68 -0
  99. package/src/lib/encapsulation-plugin.js.map +1 -0
  100. package/src/lib/fast-compile-plugin.d.ts +23 -0
  101. package/src/lib/fast-compile-plugin.js +433 -0
  102. package/src/lib/fast-compile-plugin.js.map +1 -0
  103. package/src/lib/host.d.ts +10 -8
  104. package/src/lib/host.js +59 -17
  105. package/src/lib/host.js.map +1 -1
  106. package/src/lib/live-reload-plugin.d.ts +5 -5
  107. package/src/lib/live-reload-plugin.js +10 -0
  108. package/src/lib/live-reload-plugin.js.map +1 -1
  109. package/src/lib/models.d.ts +9 -9
  110. package/src/lib/nx-folder-plugin.d.ts +5 -5
  111. package/src/lib/nx-folder-plugin.js.map +1 -1
  112. package/src/lib/plugins/file-replacements.plugin.d.ts +4 -4
  113. package/src/lib/plugins/file-replacements.plugin.js +6 -1
  114. package/src/lib/plugins/file-replacements.plugin.js.map +1 -1
  115. package/src/lib/router-plugin.d.ts +1 -1
  116. package/src/lib/router-plugin.js +14 -2
  117. package/src/lib/router-plugin.js.map +1 -1
  118. package/src/lib/style-preprocessor.d.ts +35 -0
  119. package/src/lib/style-preprocessor.js +35 -0
  120. package/src/lib/style-preprocessor.js.map +1 -0
  121. package/src/lib/stylesheet-registry.d.ts +74 -0
  122. package/src/lib/stylesheet-registry.js +174 -0
  123. package/src/lib/stylesheet-registry.js.map +1 -0
  124. package/src/lib/tools/package.json +1 -3
  125. package/src/lib/tools/src/builders/vite/vite-build.impl.js +7 -5
  126. package/src/lib/tools/src/builders/vite/vite-build.impl.js.map +1 -1
  127. package/src/lib/tools/src/builders/vite-dev-server/dev-server.impl.js +16 -19
  128. package/src/lib/tools/src/builders/vite-dev-server/dev-server.impl.js.map +1 -1
  129. package/src/lib/utils/compilation-shared.d.ts +62 -0
  130. package/src/lib/utils/compilation-shared.js +138 -0
  131. package/src/lib/utils/compilation-shared.js.map +1 -0
  132. package/src/lib/utils/compiler-plugin-options.d.ts +11 -11
  133. package/src/lib/utils/css-extension-resolver.d.ts +32 -0
  134. package/src/lib/utils/css-extension-resolver.js +73 -0
  135. package/src/lib/utils/css-extension-resolver.js.map +1 -0
  136. package/src/lib/utils/css-preprocessor-workers.d.ts +6 -0
  137. package/src/lib/utils/css-preprocessor-workers.js +29 -0
  138. package/src/lib/utils/css-preprocessor-workers.js.map +1 -0
  139. package/src/lib/utils/debug-harness.d.ts +23 -0
  140. package/src/lib/utils/debug-harness.js +88 -0
  141. package/src/lib/utils/debug-harness.js.map +1 -0
  142. package/src/lib/utils/debug-log-file.d.ts +5 -0
  143. package/src/lib/utils/debug-log-file.js +56 -0
  144. package/src/lib/utils/debug-log-file.js.map +1 -0
  145. package/src/lib/utils/debug.d.ts +26 -0
  146. package/src/lib/utils/debug.js +35 -0
  147. package/src/lib/utils/debug.js.map +1 -0
  148. package/src/lib/utils/devkit.d.ts +14 -6
  149. package/src/lib/utils/devkit.js +40 -2
  150. package/src/lib/utils/devkit.js.map +1 -1
  151. package/src/lib/utils/hmr-candidates.d.ts +28 -28
  152. package/src/lib/utils/jit-inline-styles.d.ts +3 -0
  153. package/src/lib/utils/jit-inline-styles.js +16 -0
  154. package/src/lib/utils/jit-inline-styles.js.map +1 -0
  155. package/src/lib/utils/plugin-config.d.ts +41 -0
  156. package/src/lib/utils/plugin-config.js +85 -0
  157. package/src/lib/utils/plugin-config.js.map +1 -0
  158. package/src/lib/utils/rolldown.d.ts +1 -1
  159. package/src/lib/utils/rolldown.js.map +1 -1
  160. package/src/lib/utils/safe-module-paths.d.ts +16 -0
  161. package/src/lib/utils/safe-module-paths.js +29 -0
  162. package/src/lib/utils/safe-module-paths.js.map +1 -0
  163. package/src/lib/utils/source-file-cache.d.ts +8 -15
  164. package/src/lib/utils/source-file-cache.js.map +1 -1
  165. package/src/lib/utils/transform-cache.d.ts +43 -0
  166. package/src/lib/utils/transform-cache.js +97 -0
  167. package/src/lib/utils/transform-cache.js.map +1 -0
  168. package/src/lib/utils/tsconfig-resolver.d.ts +34 -0
  169. package/src/lib/utils/tsconfig-resolver.js +205 -0
  170. package/src/lib/utils/tsconfig-resolver.js.map +1 -0
  171. package/src/lib/utils/virtual-ids.d.ts +4 -0
  172. package/src/lib/utils/virtual-ids.js +23 -0
  173. package/src/lib/utils/virtual-ids.js.map +1 -0
  174. package/src/lib/utils/virtual-resources.d.ts +19 -0
  175. package/src/lib/utils/virtual-resources.js +38 -0
  176. package/src/lib/utils/virtual-resources.js.map +1 -0
  177. package/src/lib/virtual-modules-plugin.d.ts +5 -0
  178. package/src/lib/virtual-modules-plugin.js +23 -0
  179. package/src/lib/virtual-modules-plugin.js.map +1 -0
  180. package/src/test-setup.d.ts +2 -0
  181. package/setup-vitest.d.ts +0 -4
  182. package/setup-vitest.js +0 -176
  183. package/setup-vitest.js.map +0 -1
  184. package/src/lib/tools/src/index.d.ts +0 -0
@@ -0,0 +1,45 @@
1
+ import { parseSync } from "oxc-parser";
2
+ import { RegistryEntry } from "./registry.js";
3
+ /**
4
+ * Scan a single .d.ts file and extract Angular directive/component/pipe
5
+ * metadata from the static ɵdir / ɵcmp / ɵpipe type declarations.
6
+ *
7
+ * This mirrors what ngtsc's DtsMetadataReader does: it reads the type
8
+ * parameters of ɵɵDirectiveDeclaration / ɵɵComponentDeclaration /
9
+ * ɵɵPipeDeclaration to discover selectors, inputs, and outputs for
10
+ * pre-compiled Angular packages.
11
+ */
12
+ export declare function scanDtsFile(code: string, fileName: string): RegistryEntry[];
13
+ /**
14
+ * Resolve a package's directory in node_modules, scan all its .d.ts files,
15
+ * and return RegistryEntry[] for every Angular declaration found.
16
+ *
17
+ * @param packageName e.g. "@angular/router"
18
+ * @param basePath project root (where node_modules lives)
19
+ */
20
+ export declare function scanPackageDts(packageName: string, basePath: string, visited?: Set<string>): RegistryEntry[];
21
+ /**
22
+ * Parse a source file with OXC and return the set of bare-specifier package
23
+ * names it imports (e.g. "@angular/router", "rxjs"). Relative imports are
24
+ * skipped.
25
+ */
26
+ export declare function collectImportedPackages(code: string, fileName: string, program?: ReturnType<typeof parseSync>["program"]): Set<string>;
27
+ /**
28
+ * Parse a source file with OXC and return the relative-path specifiers it
29
+ * re-exports via `export * from './x'`, `export * as Ns from './x'`, or
30
+ * `export { … } from './x'`. Bare-specifier re-exports are skipped.
31
+ *
32
+ * Used by the analog Vite plugin to walk library entry barrels at startup
33
+ * (e.g. `helm/select/src/index.ts` → `./lib/hlm-select`) so the underlying
34
+ * directive classes land in the registry before any consumer is compiled.
35
+ */
36
+ export declare function collectRelativeReExports(code: string, fileName: string, program?: ReturnType<typeof parseSync>["program"]): string[];
37
+ /**
38
+ * Parse a source file with OXC and return every module specifier it imports or
39
+ * re-exports from (relative AND bare), including `import`, `export * from`, and
40
+ * `export { … } from`. Used to walk the transitive import graph at startup so
41
+ * the registry contains components/directives/pipes/modules from files not
42
+ * directly listed in tsconfig `files`/`include` (transitively-imported app
43
+ * sources) or behind wildcard `paths` (workspace libraries).
44
+ */
45
+ export declare function collectAllImports(code: string, fileName: string, program?: ReturnType<typeof parseSync>["program"]): string[];
@@ -0,0 +1,395 @@
1
+ import * as fs from "node:fs";
2
+ import * as path$1 from "node:path";
3
+ import { parseSync } from "oxc-parser";
4
+ //#region packages/vite-plugin-angular/src/lib/compiler/dts-reader.ts
5
+ /**
6
+ * Scan a single .d.ts file and extract Angular directive/component/pipe
7
+ * metadata from the static ɵdir / ɵcmp / ɵpipe type declarations.
8
+ *
9
+ * This mirrors what ngtsc's DtsMetadataReader does: it reads the type
10
+ * parameters of ɵɵDirectiveDeclaration / ɵɵComponentDeclaration /
11
+ * ɵɵPipeDeclaration to discover selectors, inputs, and outputs for
12
+ * pre-compiled Angular packages.
13
+ */
14
+ function scanDtsFile(code, fileName) {
15
+ if (!code.includes("DirectiveDeclaration") && !code.includes("ComponentDeclaration") && !code.includes("PipeDeclaration") && !code.includes("NgModuleDeclaration")) return [];
16
+ const { program } = parseSync(fileName, code);
17
+ const entries = [];
18
+ visitStatements(program.body, fileName, entries);
19
+ return entries;
20
+ }
21
+ /**
22
+ * Resolve a package's directory in node_modules, scan all its .d.ts files,
23
+ * and return RegistryEntry[] for every Angular declaration found.
24
+ *
25
+ * @param packageName e.g. "@angular/router"
26
+ * @param basePath project root (where node_modules lives)
27
+ */
28
+ function scanPackageDts(packageName, basePath, visited) {
29
+ let pkgDir = "";
30
+ let searchBase = basePath;
31
+ while (searchBase !== path$1.dirname(searchBase)) {
32
+ const candidate = path$1.join(searchBase, "node_modules", packageName);
33
+ if (fs.existsSync(candidate)) {
34
+ pkgDir = candidate;
35
+ break;
36
+ }
37
+ searchBase = path$1.dirname(searchBase);
38
+ }
39
+ if (!pkgDir) return [];
40
+ const classToImportPath = buildClassToImportMap(packageName, pkgDir);
41
+ const typesDir = path$1.join(pkgDir, "types");
42
+ const dtsFiles = collectDtsFiles(fs.existsSync(typesDir) ? typesDir : pkgDir);
43
+ const entries = [];
44
+ const referencedPackages = /* @__PURE__ */ new Set();
45
+ for (const file of dtsFiles) try {
46
+ const code = fs.readFileSync(file, "utf-8");
47
+ const fileEntries = scanDtsFile(code, file);
48
+ for (const entry of fileEntries) entry.sourcePackage = classToImportPath.get(entry.className) || subEntryFromFilePath(packageName, pkgDir, file) || packageName;
49
+ entries.push(...fileEntries);
50
+ if (visited && fileEntries.some((e) => e.kind === "ngmodule")) for (const p of collectImportedPackages(code, file)) referencedPackages.add(p);
51
+ } catch {}
52
+ if (visited) for (const p of referencedPackages) {
53
+ if (p === packageName || visited.has(p)) continue;
54
+ visited.add(p);
55
+ try {
56
+ entries.push(...scanPackageDts(p, basePath, visited));
57
+ } catch {}
58
+ }
59
+ return entries;
60
+ }
61
+ /**
62
+ * Read package.json exports to build a map from class name to the correct
63
+ * sub-entry import path (e.g. "MatTabNav" → "@angular/material/tabs").
64
+ *
65
+ * For each sub-entry that has a "types" field, the corresponding .d.ts file
66
+ * is read to collect declared class names and re-exported names.
67
+ */
68
+ function buildClassToImportMap(packageName, pkgDir) {
69
+ const map = /* @__PURE__ */ new Map();
70
+ try {
71
+ const pkgJson = JSON.parse(fs.readFileSync(path$1.join(pkgDir, "package.json"), "utf-8"));
72
+ if (!pkgJson.exports) return map;
73
+ for (const [key, value] of Object.entries(pkgJson.exports)) {
74
+ if (key !== "." && !key.startsWith("./")) continue;
75
+ let typesFile = typeof value === "object" && value !== null && value.types;
76
+ if (!typesFile && key === "." && typeof pkgJson.types === "string") typesFile = pkgJson.types;
77
+ if (!typesFile) continue;
78
+ const importPath = key === "." ? packageName : packageName + "/" + key.slice(2);
79
+ const fullTypesPath = path$1.resolve(pkgDir, typesFile);
80
+ if (!fs.existsSync(fullTypesPath)) continue;
81
+ collectExportedClassesFromDts(fullTypesPath, importPath, map);
82
+ }
83
+ } catch {}
84
+ return map;
85
+ }
86
+ /**
87
+ * Read a `.d.ts` entry file and add every class it exports to `map`,
88
+ * following `export * from './rel'` chains so re-exported classes from
89
+ * physically nested files (e.g. `lib/foo.d.ts`) are still mapped to the
90
+ * exporting entry's import path.
91
+ *
92
+ * Without this, packages whose root `index.d.ts` is just a barrel of
93
+ * `export * from './lib/...'` would never get their classes mapped, and the
94
+ * `subEntryFromFilePath` fallback would derive an invalid `${pkg}/lib`
95
+ * import for files living under `lib/`.
96
+ */
97
+ function collectExportedClassesFromDts(filePath, importPath, map, visited = /* @__PURE__ */ new Set()) {
98
+ if (visited.has(filePath)) return;
99
+ visited.add(filePath);
100
+ let code;
101
+ try {
102
+ code = fs.readFileSync(filePath, "utf-8");
103
+ } catch {
104
+ return;
105
+ }
106
+ let program;
107
+ try {
108
+ program = parseSync(filePath, code).program;
109
+ } catch {
110
+ return;
111
+ }
112
+ const dir = path$1.dirname(filePath);
113
+ for (const stmt of program.body || []) {
114
+ if (stmt.type === "ClassDeclaration" && stmt.id?.name) {
115
+ map.set(stmt.id.name, importPath);
116
+ continue;
117
+ }
118
+ if (stmt.type === "ExportNamedDeclaration") {
119
+ if (stmt.declaration?.type === "ClassDeclaration" && stmt.declaration.id?.name) map.set(stmt.declaration.id.name, importPath);
120
+ for (const spec of stmt.specifiers || []) {
121
+ const exportedNode = spec.exported;
122
+ const exportedName = exportedNode?.type === "Identifier" ? exportedNode.name : exportedNode?.type === "Literal" ? exportedNode.value : void 0;
123
+ if (typeof exportedName === "string") map.set(exportedName, importPath);
124
+ }
125
+ continue;
126
+ }
127
+ if (stmt.type === "ExportAllDeclaration") {
128
+ const rel = stmt.source?.value;
129
+ if (!rel || !rel.startsWith(".")) continue;
130
+ const normalizedRel = rel.replace(/\.(?:js|mjs)$/u, "");
131
+ const candidates = [path$1.resolve(dir, normalizedRel + ".d.ts"), path$1.resolve(dir, normalizedRel, "index.d.ts")];
132
+ for (const candidate of candidates) if (fs.existsSync(candidate)) {
133
+ collectExportedClassesFromDts(candidate, importPath, map, visited);
134
+ break;
135
+ }
136
+ continue;
137
+ }
138
+ }
139
+ }
140
+ /**
141
+ * Derive a sub-entry import path from a .d.ts file's location relative to the
142
+ * package root. This handles older Angular packages that keep .d.ts files in
143
+ * subdirectories (e.g. `@angular/material/tabs/index.d.ts`) instead of a
144
+ * top-level `types/` folder.
145
+ *
146
+ * Returns `undefined` when no sub-entry can be determined (e.g. file is at the
147
+ * package root, inside `types/`, or in a non-entry directory).
148
+ */
149
+ function subEntryFromFilePath(packageName, pkgDir, filePath) {
150
+ const NON_ENTRY_DIRS = new Set([
151
+ "types",
152
+ "fesm2022",
153
+ "fesm2020",
154
+ "fesm2015",
155
+ "esm2022",
156
+ "esm2020",
157
+ "esm2015",
158
+ "bundles",
159
+ "schematics",
160
+ "node_modules"
161
+ ]);
162
+ const segments = path$1.relative(pkgDir, filePath).split(path$1.sep);
163
+ if (segments.length < 2) return void 0;
164
+ const firstDir = segments[0];
165
+ if (NON_ENTRY_DIRS.has(firstDir) || firstDir.startsWith("_")) return void 0;
166
+ return packageName + "/" + firstDir;
167
+ }
168
+ /**
169
+ * Parse a source file with OXC and return the set of bare-specifier package
170
+ * names it imports (e.g. "@angular/router", "rxjs"). Relative imports are
171
+ * skipped.
172
+ */
173
+ function collectImportedPackages(code, fileName, program) {
174
+ const packages = /* @__PURE__ */ new Set();
175
+ program ??= parseSync(fileName, code).program;
176
+ for (const stmt of program.body) {
177
+ if (stmt.type !== "ImportDeclaration") continue;
178
+ const specifier = stmt.source?.value;
179
+ if (!specifier || specifier.startsWith(".")) continue;
180
+ const parts = specifier.split("/");
181
+ packages.add(specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0]);
182
+ }
183
+ return packages;
184
+ }
185
+ /**
186
+ * Parse a source file with OXC and return the relative-path specifiers it
187
+ * re-exports via `export * from './x'`, `export * as Ns from './x'`, or
188
+ * `export { … } from './x'`. Bare-specifier re-exports are skipped.
189
+ *
190
+ * Used by the analog Vite plugin to walk library entry barrels at startup
191
+ * (e.g. `helm/select/src/index.ts` → `./lib/hlm-select`) so the underlying
192
+ * directive classes land in the registry before any consumer is compiled.
193
+ */
194
+ function collectRelativeReExports(code, fileName, program) {
195
+ if (!program) try {
196
+ program = parseSync(fileName, code).program;
197
+ } catch {
198
+ return [];
199
+ }
200
+ const result = [];
201
+ for (const stmt of program.body || []) {
202
+ if (stmt.type !== "ExportAllDeclaration" && stmt.type !== "ExportNamedDeclaration") continue;
203
+ const specifier = stmt.source?.value;
204
+ if (!specifier || !specifier.startsWith(".")) continue;
205
+ result.push(specifier);
206
+ }
207
+ return result;
208
+ }
209
+ /**
210
+ * Parse a source file with OXC and return every module specifier it imports or
211
+ * re-exports from (relative AND bare), including `import`, `export * from`, and
212
+ * `export { … } from`. Used to walk the transitive import graph at startup so
213
+ * the registry contains components/directives/pipes/modules from files not
214
+ * directly listed in tsconfig `files`/`include` (transitively-imported app
215
+ * sources) or behind wildcard `paths` (workspace libraries).
216
+ */
217
+ function collectAllImports(code, fileName, program) {
218
+ if (!program) try {
219
+ program = parseSync(fileName, code).program;
220
+ } catch {
221
+ return [];
222
+ }
223
+ const result = [];
224
+ for (const stmt of program.body || []) if (stmt.type === "ImportDeclaration" || stmt.type === "ExportAllDeclaration" || stmt.type === "ExportNamedDeclaration") {
225
+ const specifier = stmt.source?.value;
226
+ if (specifier) result.push(specifier);
227
+ }
228
+ return result;
229
+ }
230
+ function visitStatements(stmts, fileName, entries) {
231
+ for (const stmt of stmts) {
232
+ const decl = stmt.type === "ExportNamedDeclaration" || stmt.type === "ExportDefaultDeclaration" ? stmt.declaration : stmt;
233
+ if (!decl) continue;
234
+ if (decl.type === "TSModuleDeclaration" && decl.body?.body) {
235
+ visitStatements(decl.body.body, fileName, entries);
236
+ continue;
237
+ }
238
+ if (decl.type !== "ClassDeclaration" || !decl.id?.name) continue;
239
+ const className = decl.id.name;
240
+ const members = decl.body?.body || [];
241
+ for (const member of members) {
242
+ if (member.type !== "PropertyDefinition" || !member.static) continue;
243
+ const propName = member.key?.name;
244
+ const typeRef = member.typeAnnotation?.typeAnnotation;
245
+ if (!typeRef || typeRef.type !== "TSTypeReference") continue;
246
+ const typeName = qualifiedName(typeRef.typeName);
247
+ const typeParams = typeRef.typeArguments?.params;
248
+ if ((propName === "ɵdir" || propName === "ɵcmp") && (typeName.includes("DirectiveDeclaration") || typeName.includes("ComponentDeclaration")) && typeParams && typeParams.length >= 5) {
249
+ const selector = literalString(typeParams[1]);
250
+ if (!selector) continue;
251
+ const inputs = extractInputs(typeParams[3]);
252
+ const outputs = extractOutputs(typeParams[4]);
253
+ entries.push({
254
+ selector: selector.split(",")[0].trim(),
255
+ kind: propName === "ɵcmp" ? "component" : "directive",
256
+ fileName,
257
+ className,
258
+ ...Object.keys(inputs).length > 0 ? { inputs } : {},
259
+ ...Object.keys(outputs).length > 0 ? { outputs } : {}
260
+ });
261
+ } else if (propName === "ɵpipe" && typeName.includes("PipeDeclaration") && typeParams && typeParams.length >= 2) {
262
+ const pipeName = literalString(typeParams[1]);
263
+ if (!pipeName) continue;
264
+ entries.push({
265
+ selector: pipeName,
266
+ kind: "pipe",
267
+ pipeName,
268
+ fileName,
269
+ className
270
+ });
271
+ } else if (propName === "ɵmod" && typeName.includes("NgModuleDeclaration") && typeParams && typeParams.length >= 4) {
272
+ const exportsParam = typeParams[3];
273
+ const exportedNames = [];
274
+ if (exportsParam?.type === "TSTupleType" && exportsParam.elementTypes) {
275
+ for (const el of exportsParam.elementTypes) if (el.type === "TSTypeQuery" && el.exprName?.type === "Identifier") exportedNames.push(el.exprName.name);
276
+ else if (el.type === "TSTypeQuery" && el.exprName?.type === "TSQualifiedName") {
277
+ const right = el.exprName.right;
278
+ if (right?.type === "Identifier") exportedNames.push(right.name);
279
+ }
280
+ }
281
+ entries.push({
282
+ selector: className,
283
+ kind: "ngmodule",
284
+ exports: exportedNames,
285
+ fileName,
286
+ className
287
+ });
288
+ }
289
+ }
290
+ }
291
+ }
292
+ /**
293
+ * Extract inputs from the type-argument at position [3].
294
+ *
295
+ * Handles two formats:
296
+ * v16+: { "prop": { "alias": "binding"; "required": false; "isSignal"?: true } }
297
+ * pre-v16: { "prop": "binding" }
298
+ */
299
+ function extractInputs(node) {
300
+ const inputs = {};
301
+ if (node?.type !== "TSTypeLiteral") return inputs;
302
+ for (const member of node.members) {
303
+ if (member.type !== "TSPropertySignature") continue;
304
+ const propName = keyName(member.key);
305
+ const innerType = member.typeAnnotation?.typeAnnotation;
306
+ if (!propName || !innerType) continue;
307
+ if (innerType.type === "TSTypeLiteral") {
308
+ let alias = propName;
309
+ let required = false;
310
+ let isSignal = false;
311
+ for (const inner of innerType.members) {
312
+ if (inner.type !== "TSPropertySignature") continue;
313
+ const key = keyName(inner.key);
314
+ const val = inner.typeAnnotation?.typeAnnotation;
315
+ if (!key || !val) continue;
316
+ if (key === "alias") alias = literalString(val) ?? propName;
317
+ else if (key === "required") required = literalBoolean(val) === true;
318
+ else if (key === "isSignal") isSignal = literalBoolean(val) === true;
319
+ }
320
+ inputs[propName] = {
321
+ classPropertyName: propName,
322
+ bindingPropertyName: alias,
323
+ isSignal,
324
+ required
325
+ };
326
+ } else {
327
+ const alias = literalString(innerType);
328
+ if (alias) inputs[propName] = {
329
+ classPropertyName: propName,
330
+ bindingPropertyName: alias,
331
+ isSignal: false,
332
+ required: false
333
+ };
334
+ }
335
+ }
336
+ return inputs;
337
+ }
338
+ /**
339
+ * Extract outputs from the type-argument at position [4].
340
+ * Format: { "propName": "eventName" }
341
+ */
342
+ function extractOutputs(node) {
343
+ const outputs = {};
344
+ if (node?.type !== "TSTypeLiteral") return outputs;
345
+ for (const member of node.members) {
346
+ if (member.type !== "TSPropertySignature") continue;
347
+ const propName = keyName(member.key);
348
+ const val = member.typeAnnotation?.typeAnnotation;
349
+ if (!propName || !val) continue;
350
+ const value = literalString(val);
351
+ if (value) outputs[propName] = value;
352
+ }
353
+ return outputs;
354
+ }
355
+ /** Get the string name from a property key (Identifier or Literal). */
356
+ function keyName(key) {
357
+ if (!key) return void 0;
358
+ if (key.type === "Identifier") return key.name;
359
+ if (key.type === "Literal" && typeof key.value === "string") return key.value;
360
+ }
361
+ /** Build a dotted name from an Identifier or TSQualifiedName. */
362
+ function qualifiedName(node) {
363
+ if (!node) return "";
364
+ if (node.type === "Identifier") return node.name;
365
+ if (node.type === "TSQualifiedName") return qualifiedName(node.left) + "." + qualifiedName(node.right);
366
+ return "";
367
+ }
368
+ /** Extract a string value from a TSLiteralType node. */
369
+ function literalString(node) {
370
+ if (node?.type === "TSLiteralType" && node.literal?.type === "Literal" && typeof node.literal.value === "string") return node.literal.value;
371
+ }
372
+ /** Extract a boolean value from a TSLiteralType node. */
373
+ function literalBoolean(node) {
374
+ if (node?.type === "TSLiteralType" && node.literal?.type === "Literal" && typeof node.literal.value === "boolean") return node.literal.value;
375
+ }
376
+ /** Recursively collect all .d.ts files under a directory. */
377
+ function collectDtsFiles(dir) {
378
+ const results = [];
379
+ let entries;
380
+ try {
381
+ entries = fs.readdirSync(dir, { withFileTypes: true });
382
+ } catch {
383
+ return results;
384
+ }
385
+ for (const entry of entries) {
386
+ const full = path$1.join(dir, entry.name);
387
+ if (entry.isDirectory() && entry.name !== "node_modules") results.push(...collectDtsFiles(full));
388
+ else if (entry.isFile() && entry.name.endsWith(".d.ts")) results.push(full);
389
+ }
390
+ return results;
391
+ }
392
+ //#endregion
393
+ export { collectAllImports, collectImportedPackages, collectRelativeReExports, scanDtsFile, scanPackageDts };
394
+
395
+ //# sourceMappingURL=dts-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dts-reader.js","names":[],"sources":["../../../../src/lib/compiler/dts-reader.ts"],"sourcesContent":["import { parseSync } from 'oxc-parser';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { RegistryEntry, RegistryInput } from './registry.js';\n\n/**\n * Scan a single .d.ts file and extract Angular directive/component/pipe\n * metadata from the static ɵdir / ɵcmp / ɵpipe type declarations.\n *\n * This mirrors what ngtsc's DtsMetadataReader does: it reads the type\n * parameters of ɵɵDirectiveDeclaration / ɵɵComponentDeclaration /\n * ɵɵPipeDeclaration to discover selectors, inputs, and outputs for\n * pre-compiled Angular packages.\n */\nexport function scanDtsFile(code: string, fileName: string): RegistryEntry[] {\n // Fast pre-filter — skip files that can't contain Angular declarations\n if (\n !code.includes('DirectiveDeclaration') &&\n !code.includes('ComponentDeclaration') &&\n !code.includes('PipeDeclaration') &&\n !code.includes('NgModuleDeclaration')\n )\n return [];\n\n const { program } = parseSync(fileName, code);\n const entries: RegistryEntry[] = [];\n\n visitStatements(program.body, fileName, entries);\n return entries;\n}\n\n/**\n * Resolve a package's directory in node_modules, scan all its .d.ts files,\n * and return RegistryEntry[] for every Angular declaration found.\n *\n * @param packageName e.g. \"@angular/router\"\n * @param basePath project root (where node_modules lives)\n */\nexport function scanPackageDts(\n packageName: string,\n basePath: string,\n visited?: Set<string>,\n): RegistryEntry[] {\n // Walk up from basePath to find the nearest node_modules containing the package.\n // This handles monorepos where node_modules is at the workspace root,\n // not in the project subdirectory.\n let pkgDir = '';\n let searchBase = basePath;\n while (searchBase !== path.dirname(searchBase)) {\n const candidate = path.join(searchBase, 'node_modules', packageName);\n if (fs.existsSync(candidate)) {\n pkgDir = candidate;\n break;\n }\n searchBase = path.dirname(searchBase);\n }\n if (!pkgDir) return [];\n\n // Build a map from className → correct import path using package.json exports.\n // This handles packages with sub-entry points (e.g. @angular/material/tabs)\n // where re-exported classes must be imported from their origin entry point.\n const classToImportPath = buildClassToImportMap(packageName, pkgDir);\n\n // Check for a \"types\" directory first (Angular packages use this),\n // then fall back to the package root.\n const typesDir = path.join(pkgDir, 'types');\n const searchDir = fs.existsSync(typesDir) ? typesDir : pkgDir;\n\n const dtsFiles = collectDtsFiles(searchDir);\n const entries: RegistryEntry[] = [];\n // Packages re-exported by this package's NgModule declarations. A library\n // NgModule can re-export another package's module (e.g. BrowserModule exports\n // CommonModule), so those packages must be scanned too or their directives\n // (NgIf, NgForOf, …) never reach the registry.\n const referencedPackages = new Set<string>();\n\n for (const file of dtsFiles) {\n try {\n const code = fs.readFileSync(file, 'utf-8');\n const fileEntries = scanDtsFile(code, file);\n for (const entry of fileEntries) {\n entry.sourcePackage =\n classToImportPath.get(entry.className) ||\n subEntryFromFilePath(packageName, pkgDir, file) ||\n packageName;\n }\n entries.push(...fileEntries);\n // Only follow cross-package imports of declaration files that actually\n // contain an NgModule — those are the ones whose exports can pull in\n // another package's directives/pipes. This keeps the walk bounded.\n if (visited && fileEntries.some((e) => e.kind === 'ngmodule')) {\n for (const p of collectImportedPackages(code, file)) {\n referencedPackages.add(p);\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n\n if (visited) {\n for (const p of referencedPackages) {\n if (p === packageName || visited.has(p)) continue;\n visited.add(p);\n try {\n entries.push(...scanPackageDts(p, basePath, visited));\n } catch {\n // Package may not have .d.ts or not be Angular\n }\n }\n }\n\n return entries;\n}\n\n/**\n * Read package.json exports to build a map from class name to the correct\n * sub-entry import path (e.g. \"MatTabNav\" → \"@angular/material/tabs\").\n *\n * For each sub-entry that has a \"types\" field, the corresponding .d.ts file\n * is read to collect declared class names and re-exported names.\n */\nfunction buildClassToImportMap(\n packageName: string,\n pkgDir: string,\n): Map<string, string> {\n const map = new Map<string, string>();\n try {\n const pkgJson = JSON.parse(\n fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8'),\n );\n if (!pkgJson.exports) return map;\n\n for (const [key, value] of Object.entries(pkgJson.exports) as [\n string,\n any,\n ][]) {\n if (key !== '.' && !key.startsWith('./')) continue;\n // Resolve types from the export entry. Some packages declare\n // `types` only at the package root (top-level package.json `types`\n // field) rather than per-export, so fall back to that for `.`.\n let typesFile: string | undefined =\n typeof value === 'object' && value !== null && value.types;\n if (!typesFile && key === '.' && typeof pkgJson.types === 'string') {\n typesFile = pkgJson.types;\n }\n if (!typesFile) continue;\n\n // Root entry (`.`) maps classes to the bare package name; sub-entries\n // (`./tabs`) map to `${packageName}/tabs`. Mapping root-exported\n // classes prevents `subEntryFromFilePath` from deriving an invalid\n // subpath like `ngx-scrollbar/lib` for files that physically live in\n // a `lib/` subdirectory but are only re-exported from the root entry.\n const importPath =\n key === '.' ? packageName : packageName + '/' + key.slice(2);\n const fullTypesPath = path.resolve(pkgDir, typesFile);\n if (!fs.existsSync(fullTypesPath)) continue;\n\n collectExportedClassesFromDts(fullTypesPath, importPath, map);\n }\n } catch {\n // No package.json or no exports — fall back to packageName\n }\n return map;\n}\n\n/**\n * Read a `.d.ts` entry file and add every class it exports to `map`,\n * following `export * from './rel'` chains so re-exported classes from\n * physically nested files (e.g. `lib/foo.d.ts`) are still mapped to the\n * exporting entry's import path.\n *\n * Without this, packages whose root `index.d.ts` is just a barrel of\n * `export * from './lib/...'` would never get their classes mapped, and the\n * `subEntryFromFilePath` fallback would derive an invalid `${pkg}/lib`\n * import for files living under `lib/`.\n */\nfunction collectExportedClassesFromDts(\n filePath: string,\n importPath: string,\n map: Map<string, string>,\n visited: Set<string> = new Set(),\n): void {\n if (visited.has(filePath)) return;\n visited.add(filePath);\n let code: string;\n try {\n code = fs.readFileSync(filePath, 'utf-8');\n } catch {\n return;\n }\n\n let program: any;\n try {\n program = parseSync(filePath, code).program;\n } catch {\n return;\n }\n\n const dir = path.dirname(filePath);\n for (const stmt of program.body || []) {\n // `declare class Foo` (top-level)\n if (stmt.type === 'ClassDeclaration' && stmt.id?.name) {\n map.set(stmt.id.name, importPath);\n continue;\n }\n\n // `export declare class Foo` / `export class Foo` /\n // `export { A, B as C } from '...'` / `export { A, B }`\n if (stmt.type === 'ExportNamedDeclaration') {\n if (\n stmt.declaration?.type === 'ClassDeclaration' &&\n stmt.declaration.id?.name\n ) {\n map.set(stmt.declaration.id.name, importPath);\n }\n for (const spec of stmt.specifiers || []) {\n // ExportSpecifier: { local, exported }\n const exportedNode = spec.exported;\n const exportedName =\n exportedNode?.type === 'Identifier'\n ? exportedNode.name\n : exportedNode?.type === 'Literal'\n ? exportedNode.value\n : undefined;\n if (typeof exportedName === 'string') {\n map.set(exportedName, importPath);\n }\n }\n continue;\n }\n\n // `export * from './rel'` and `export * as Ns from './rel'`\n if (stmt.type === 'ExportAllDeclaration') {\n const rel: string | undefined = stmt.source?.value;\n if (!rel || !rel.startsWith('.')) continue;\n // TypeScript preserves the original specifier text in emitted\n // `.d.ts` files, so NodeNext-style packages keep their `.js`\n // (or `.mjs`) extensions in re-exports — but the actual\n // declaration is at `foo.d.ts` / `foo/index.d.ts`. Strip the\n // ESM extension before probing.\n const normalizedRel = rel.replace(/\\.(?:js|mjs)$/u, '');\n const candidates = [\n path.resolve(dir, normalizedRel + '.d.ts'),\n path.resolve(dir, normalizedRel, 'index.d.ts'),\n ];\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n collectExportedClassesFromDts(candidate, importPath, map, visited);\n break;\n }\n }\n continue;\n }\n }\n}\n\n/**\n * Derive a sub-entry import path from a .d.ts file's location relative to the\n * package root. This handles older Angular packages that keep .d.ts files in\n * subdirectories (e.g. `@angular/material/tabs/index.d.ts`) instead of a\n * top-level `types/` folder.\n *\n * Returns `undefined` when no sub-entry can be determined (e.g. file is at the\n * package root, inside `types/`, or in a non-entry directory).\n */\nfunction subEntryFromFilePath(\n packageName: string,\n pkgDir: string,\n filePath: string,\n): string | undefined {\n const NON_ENTRY_DIRS = new Set([\n 'types',\n 'fesm2022',\n 'fesm2020',\n 'fesm2015',\n 'esm2022',\n 'esm2020',\n 'esm2015',\n 'bundles',\n 'schematics',\n 'node_modules',\n ]);\n const rel = path.relative(pkgDir, filePath);\n const segments = rel.split(path.sep);\n // File directly in pkgDir (no subdirectory) → no sub-entry\n if (segments.length < 2) return undefined;\n const firstDir = segments[0];\n // Skip internal/build directories and chunk files\n if (NON_ENTRY_DIRS.has(firstDir) || firstDir.startsWith('_'))\n return undefined;\n return packageName + '/' + firstDir;\n}\n\n/**\n * Parse a source file with OXC and return the set of bare-specifier package\n * names it imports (e.g. \"@angular/router\", \"rxjs\"). Relative imports are\n * skipped.\n */\nexport function collectImportedPackages(\n code: string,\n fileName: string,\n program?: ReturnType<typeof parseSync>['program'],\n): Set<string> {\n const packages = new Set<string>();\n program ??= parseSync(fileName, code).program;\n\n for (const stmt of program.body) {\n if (stmt.type !== 'ImportDeclaration') continue;\n const specifier: string = stmt.source?.value;\n if (!specifier || specifier.startsWith('.')) continue;\n\n const parts = specifier.split('/');\n packages.add(\n specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0],\n );\n }\n\n return packages;\n}\n\n/**\n * Parse a source file with OXC and return the relative-path specifiers it\n * re-exports via `export * from './x'`, `export * as Ns from './x'`, or\n * `export { … } from './x'`. Bare-specifier re-exports are skipped.\n *\n * Used by the analog Vite plugin to walk library entry barrels at startup\n * (e.g. `helm/select/src/index.ts` → `./lib/hlm-select`) so the underlying\n * directive classes land in the registry before any consumer is compiled.\n */\nexport function collectRelativeReExports(\n code: string,\n fileName: string,\n program?: ReturnType<typeof parseSync>['program'],\n): string[] {\n if (!program) {\n try {\n program = parseSync(fileName, code).program;\n } catch {\n return [];\n }\n }\n const result: string[] = [];\n for (const stmt of program.body || []) {\n if (\n stmt.type !== 'ExportAllDeclaration' &&\n stmt.type !== 'ExportNamedDeclaration'\n ) {\n continue;\n }\n // ExportNamedDeclaration without `source` is `export { x }` (no\n // re-export), which we don't care about here.\n const specifier: string | undefined = stmt.source?.value;\n if (!specifier || !specifier.startsWith('.')) continue;\n result.push(specifier);\n }\n return result;\n}\n\n/**\n * Parse a source file with OXC and return every module specifier it imports or\n * re-exports from (relative AND bare), including `import`, `export * from`, and\n * `export { … } from`. Used to walk the transitive import graph at startup so\n * the registry contains components/directives/pipes/modules from files not\n * directly listed in tsconfig `files`/`include` (transitively-imported app\n * sources) or behind wildcard `paths` (workspace libraries).\n */\nexport function collectAllImports(\n code: string,\n fileName: string,\n program?: ReturnType<typeof parseSync>['program'],\n): string[] {\n if (!program) {\n try {\n program = parseSync(fileName, code).program;\n } catch {\n return [];\n }\n }\n const result: string[] = [];\n for (const stmt of program.body || []) {\n if (\n stmt.type === 'ImportDeclaration' ||\n stmt.type === 'ExportAllDeclaration' ||\n stmt.type === 'ExportNamedDeclaration'\n ) {\n const specifier: string | undefined = stmt.source?.value;\n if (specifier) result.push(specifier);\n }\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction visitStatements(\n stmts: any[],\n fileName: string,\n entries: RegistryEntry[],\n) {\n for (const stmt of stmts) {\n // Unwrap exports: `export declare class ...` / `export default class ...`\n const decl =\n stmt.type === 'ExportNamedDeclaration' ||\n stmt.type === 'ExportDefaultDeclaration'\n ? stmt.declaration\n : stmt;\n\n if (!decl) continue;\n\n // Recurse into `declare module \"...\" { ... }` blocks\n if (decl.type === 'TSModuleDeclaration' && decl.body?.body) {\n visitStatements(decl.body.body, fileName, entries);\n continue;\n }\n\n if (decl.type !== 'ClassDeclaration' || !decl.id?.name) continue;\n const className: string = decl.id.name;\n const members: any[] = decl.body?.body || [];\n\n for (const member of members) {\n if (member.type !== 'PropertyDefinition' || !member.static) continue;\n const propName: string | undefined = member.key?.name;\n\n // Read the type annotation: e.g. `i0.ɵɵDirectiveDeclaration<...>`\n const typeRef = member.typeAnnotation?.typeAnnotation;\n if (!typeRef || typeRef.type !== 'TSTypeReference') continue;\n\n const typeName = qualifiedName(typeRef.typeName);\n const typeParams: any[] | undefined = typeRef.typeArguments?.params;\n\n if (\n (propName === 'ɵdir' || propName === 'ɵcmp') &&\n (typeName.includes('DirectiveDeclaration') ||\n typeName.includes('ComponentDeclaration')) &&\n typeParams &&\n typeParams.length >= 5\n ) {\n const selector = literalString(typeParams[1]);\n if (!selector) continue;\n\n const inputs = extractInputs(typeParams[3]);\n const outputs = extractOutputs(typeParams[4]);\n\n entries.push({\n selector: selector.split(',')[0].trim(),\n kind: propName === 'ɵcmp' ? 'component' : 'directive',\n fileName,\n className,\n ...(Object.keys(inputs).length > 0 ? { inputs } : {}),\n ...(Object.keys(outputs).length > 0 ? { outputs } : {}),\n });\n } else if (\n propName === 'ɵpipe' &&\n typeName.includes('PipeDeclaration') &&\n typeParams &&\n typeParams.length >= 2\n ) {\n const pipeName = literalString(typeParams[1]);\n if (!pipeName) continue;\n\n entries.push({\n selector: pipeName,\n kind: 'pipe',\n pipeName,\n fileName,\n className,\n });\n } else if (\n propName === 'ɵmod' &&\n typeName.includes('NgModuleDeclaration') &&\n typeParams &&\n typeParams.length >= 4\n ) {\n // ɵɵNgModuleDeclaration<Module, Declarations, Imports, Exports>\n // Extract exported class names from the 4th type param (index 3).\n const exportsParam = typeParams[3];\n const exportedNames: string[] = [];\n if (exportsParam?.type === 'TSTupleType' && exportsParam.elementTypes) {\n for (const el of exportsParam.elementTypes) {\n if (\n el.type === 'TSTypeQuery' &&\n el.exprName?.type === 'Identifier'\n ) {\n exportedNames.push(el.exprName.name);\n } else if (\n el.type === 'TSTypeQuery' &&\n el.exprName?.type === 'TSQualifiedName'\n ) {\n // e.g. typeof i2.BidiModule → extract \"BidiModule\"\n const right = el.exprName.right;\n if (right?.type === 'Identifier') {\n exportedNames.push(right.name);\n }\n }\n }\n }\n entries.push({\n selector: className,\n kind: 'ngmodule',\n exports: exportedNames,\n fileName,\n className,\n });\n }\n }\n }\n}\n\n/**\n * Extract inputs from the type-argument at position [3].\n *\n * Handles two formats:\n * v16+: { \"prop\": { \"alias\": \"binding\"; \"required\": false; \"isSignal\"?: true } }\n * pre-v16: { \"prop\": \"binding\" }\n */\nfunction extractInputs(node: any): Record<string, RegistryInput> {\n const inputs: Record<string, RegistryInput> = {};\n if (node?.type !== 'TSTypeLiteral') return inputs;\n\n for (const member of node.members) {\n if (member.type !== 'TSPropertySignature') continue;\n const propName = keyName(member.key);\n const innerType = member.typeAnnotation?.typeAnnotation;\n if (!propName || !innerType) continue;\n\n if (innerType.type === 'TSTypeLiteral') {\n // v16+ object format\n let alias = propName;\n let required = false;\n let isSignal = false;\n\n for (const inner of innerType.members) {\n if (inner.type !== 'TSPropertySignature') continue;\n const key = keyName(inner.key);\n const val = inner.typeAnnotation?.typeAnnotation;\n if (!key || !val) continue;\n\n if (key === 'alias') {\n alias = literalString(val) ?? propName;\n } else if (key === 'required') {\n required = literalBoolean(val) === true;\n } else if (key === 'isSignal') {\n isSignal = literalBoolean(val) === true;\n }\n }\n\n inputs[propName] = {\n classPropertyName: propName,\n bindingPropertyName: alias,\n isSignal,\n required,\n };\n } else {\n // pre-v16 string format\n const alias = literalString(innerType);\n if (alias) {\n inputs[propName] = {\n classPropertyName: propName,\n bindingPropertyName: alias,\n isSignal: false,\n required: false,\n };\n }\n }\n }\n\n return inputs;\n}\n\n/**\n * Extract outputs from the type-argument at position [4].\n * Format: { \"propName\": \"eventName\" }\n */\nfunction extractOutputs(node: any): Record<string, string> {\n const outputs: Record<string, string> = {};\n if (node?.type !== 'TSTypeLiteral') return outputs;\n\n for (const member of node.members) {\n if (member.type !== 'TSPropertySignature') continue;\n const propName = keyName(member.key);\n const val = member.typeAnnotation?.typeAnnotation;\n if (!propName || !val) continue;\n\n const value = literalString(val);\n if (value) outputs[propName] = value;\n }\n\n return outputs;\n}\n\n/** Get the string name from a property key (Identifier or Literal). */\nfunction keyName(key: any): string | undefined {\n if (!key) return undefined;\n if (key.type === 'Identifier') return key.name;\n if (key.type === 'Literal' && typeof key.value === 'string') return key.value;\n return undefined;\n}\n\n/** Build a dotted name from an Identifier or TSQualifiedName. */\nfunction qualifiedName(node: any): string {\n if (!node) return '';\n if (node.type === 'Identifier') return node.name;\n if (node.type === 'TSQualifiedName')\n return qualifiedName(node.left) + '.' + qualifiedName(node.right);\n return '';\n}\n\n/** Extract a string value from a TSLiteralType node. */\nfunction literalString(node: any): string | undefined {\n if (\n node?.type === 'TSLiteralType' &&\n node.literal?.type === 'Literal' &&\n typeof node.literal.value === 'string'\n ) {\n return node.literal.value;\n }\n return undefined;\n}\n\n/** Extract a boolean value from a TSLiteralType node. */\nfunction literalBoolean(node: any): boolean | undefined {\n if (\n node?.type === 'TSLiteralType' &&\n node.literal?.type === 'Literal' &&\n typeof node.literal.value === 'boolean'\n ) {\n return node.literal.value;\n }\n return undefined;\n}\n\n/** Recursively collect all .d.ts files under a directory. */\nfunction collectDtsFiles(dir: string): string[] {\n const results: string[] = [];\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return results;\n }\n\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory() && entry.name !== 'node_modules') {\n results.push(...collectDtsFiles(full));\n } else if (entry.isFile() && entry.name.endsWith('.d.ts')) {\n results.push(full);\n }\n }\n\n return results;\n}\n"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { RegistryEntry } from "./registry.js";
2
+ /**
3
+ * Generate HMR code using Angular's ɵɵreplaceMetadata for components,
4
+ * and simple field-swap + invalidation for directives/pipes.
5
+ *
6
+ * The applyMetadata callback dynamically copies all ɵ-prefixed static
7
+ * fields (ɵcmp, ɵfac, ɵdir, ɵpipe, etc.) from the newly compiled class
8
+ * to the old class reference that Angular's runtime is tracking.
9
+ *
10
+ * For components, ɵɵreplaceMetadata then merges the old/new definitions
11
+ * and recreates matching LViews in the component tree.
12
+ *
13
+ * For directives and pipes, ɵɵreplaceMetadata does not support them, so
14
+ * we fall back to a full page reload via import.meta.hot.invalidate().
15
+ */
16
+ export declare function generateHmrCode(declarations: RegistryEntry[], localDepClassNames?: string[]): string;
@@ -0,0 +1,69 @@
1
+ //#region packages/vite-plugin-angular/src/lib/compiler/hmr.ts
2
+ /**
3
+ * Generate HMR code using Angular's ɵɵreplaceMetadata for components,
4
+ * and simple field-swap + invalidation for directives/pipes.
5
+ *
6
+ * The applyMetadata callback dynamically copies all ɵ-prefixed static
7
+ * fields (ɵcmp, ɵfac, ɵdir, ɵpipe, etc.) from the newly compiled class
8
+ * to the old class reference that Angular's runtime is tracking.
9
+ *
10
+ * For components, ɵɵreplaceMetadata then merges the old/new definitions
11
+ * and recreates matching LViews in the component tree.
12
+ *
13
+ * For directives and pipes, ɵɵreplaceMetadata does not support them, so
14
+ * we fall back to a full page reload via import.meta.hot.invalidate().
15
+ */
16
+ function generateHmrCode(declarations, localDepClassNames = []) {
17
+ const components = declarations.filter((d) => d.kind === "component");
18
+ const nonComponents = declarations.filter((d) => d.kind !== "component");
19
+ const applyFns = declarations.map((c) => `
20
+ export function ɵhmr_${c.className}(type) {
21
+ for (const key of Object.getOwnPropertyNames(${c.className})) {
22
+ if (key.startsWith('ɵ')) type[key] = ${c.className}[key];
23
+ }
24
+ }`).join("\n");
25
+ const localDepsArray = localDepClassNames.length > 0 ? `[${localDepClassNames.join(", ")}]` : "[]";
26
+ const replaceBlocks = components.map((c) => `
27
+ try {
28
+ i0.ɵɵreplaceMetadata(
29
+ ${c.className},
30
+ newModule.ɵhmr_${c.className},
31
+ { i0 },
32
+ ${localDepsArray},
33
+ import.meta,
34
+ "${c.className}"
35
+ );
36
+ replaced = true;
37
+ } catch(e) {
38
+ // ɵɵreplaceMetadata failed — will fall back to page reload
39
+ }`).join("\n");
40
+ const swapBlocks = nonComponents.map((c) => `
41
+ try {
42
+ newModule.ɵhmr_${c.className}(${c.className});
43
+ swapped = true;
44
+ } catch(e) {}`).join("\n");
45
+ let acceptBody = `
46
+ if (!newModule) return;`;
47
+ if (components.length > 0) acceptBody += `
48
+ let replaced = false;${replaceBlocks}
49
+ if (!replaced) {
50
+ import.meta.hot.invalidate('Component HMR failed, reloading');
51
+ return;
52
+ }`;
53
+ if (nonComponents.length > 0) acceptBody += `
54
+ let swapped = false;${swapBlocks}
55
+ if (swapped) {
56
+ // Directive/pipe definitions updated — full reload needed for Angular
57
+ // to pick up the new behavior since ɵɵreplaceMetadata only supports components.
58
+ import.meta.hot.invalidate('Directive/pipe changed, reloading');
59
+ }`;
60
+ return `\n${applyFns}
61
+ if (import.meta.hot) {
62
+ import.meta.hot.accept((newModule) => {${acceptBody}
63
+ });
64
+ }`;
65
+ }
66
+ //#endregion
67
+ export { generateHmrCode };
68
+
69
+ //# sourceMappingURL=hmr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hmr.js","names":[],"sources":["../../../../src/lib/compiler/hmr.ts"],"sourcesContent":["import type { RegistryEntry } from './registry.js';\n\n/**\n * Generate HMR code using Angular's ɵɵreplaceMetadata for components,\n * and simple field-swap + invalidation for directives/pipes.\n *\n * The applyMetadata callback dynamically copies all ɵ-prefixed static\n * fields (ɵcmp, ɵfac, ɵdir, ɵpipe, etc.) from the newly compiled class\n * to the old class reference that Angular's runtime is tracking.\n *\n * For components, ɵɵreplaceMetadata then merges the old/new definitions\n * and recreates matching LViews in the component tree.\n *\n * For directives and pipes, ɵɵreplaceMetadata does not support them, so\n * we fall back to a full page reload via import.meta.hot.invalidate().\n */\nexport function generateHmrCode(\n declarations: RegistryEntry[],\n localDepClassNames: string[] = [],\n): string {\n const components = declarations.filter((d) => d.kind === 'component');\n const nonComponents = declarations.filter((d) => d.kind !== 'component');\n\n // Export applyMetadata functions so the accept callback can access them.\n // Dynamically copy all ɵ-prefixed static fields to handle ɵcmp, ɵfac,\n // ɵdir, ɵpipe, ɵmod, ɵinj, ɵprov, and any future Ivy fields.\n const applyFns = declarations\n .map(\n (c) => `\nexport function ɵhmr_${c.className}(type) {\n for (const key of Object.getOwnPropertyNames(${c.className})) {\n if (key.startsWith('ɵ')) type[key] = ${c.className}[key];\n }\n}`,\n )\n .join('\\n');\n\n // Components: use ɵɵreplaceMetadata for full LView recreation\n const localDepsArray =\n localDepClassNames.length > 0 ? `[${localDepClassNames.join(', ')}]` : '[]';\n\n const replaceBlocks = components\n .map(\n (c) => `\n try {\n i0.ɵɵreplaceMetadata(\n ${c.className},\n newModule.ɵhmr_${c.className},\n { i0 },\n ${localDepsArray},\n import.meta,\n \"${c.className}\"\n );\n replaced = true;\n } catch(e) {\n // ɵɵreplaceMetadata failed — will fall back to page reload\n }`,\n )\n .join('\\n');\n\n // Directives/pipes: swap static fields and invalidate\n const swapBlocks = nonComponents\n .map(\n (c) => `\n try {\n newModule.ɵhmr_${c.className}(${c.className});\n swapped = true;\n } catch(e) {}`,\n )\n .join('\\n');\n\n let acceptBody = `\n if (!newModule) return;`;\n\n if (components.length > 0) {\n acceptBody += `\n let replaced = false;${replaceBlocks}\n if (!replaced) {\n import.meta.hot.invalidate('Component HMR failed, reloading');\n return;\n }`;\n }\n\n if (nonComponents.length > 0) {\n acceptBody += `\n let swapped = false;${swapBlocks}\n if (swapped) {\n // Directive/pipe definitions updated — full reload needed for Angular\n // to pick up the new behavior since ɵɵreplaceMetadata only supports components.\n import.meta.hot.invalidate('Directive/pipe changed, reloading');\n }`;\n }\n\n return `\\n${applyFns}\nif (import.meta.hot) {\n import.meta.hot.accept((newModule) => {${acceptBody}\n });\n}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,SAAgB,gBACd,cACA,qBAA+B,EAAE,EACzB;CACR,MAAM,aAAa,aAAa,QAAQ,MAAM,EAAE,SAAS,YAAY;CACrE,MAAM,gBAAgB,aAAa,QAAQ,MAAM,EAAE,SAAS,YAAY;CAKxE,MAAM,WAAW,aACd,KACE,MAAM;uBACU,EAAE,UAAU;iDACc,EAAE,UAAU;2CAClB,EAAE,UAAU;;GAGlD,CACA,KAAK,KAAK;CAGb,MAAM,iBACJ,mBAAmB,SAAS,IAAI,IAAI,mBAAmB,KAAK,KAAK,CAAC,KAAK;CAEzE,MAAM,gBAAgB,WACnB,KACE,MAAM;;;YAGD,EAAE,UAAU;2BACG,EAAE,UAAU;;YAE3B,eAAe;;aAEd,EAAE,UAAU;;;;;SAMpB,CACA,KAAK,KAAK;CAGb,MAAM,aAAa,cAChB,KACE,MAAM;;yBAEY,EAAE,UAAU,GAAG,EAAE,UAAU;;qBAG/C,CACA,KAAK,KAAK;CAEb,IAAI,aAAa;;AAGjB,KAAI,WAAW,SAAS,EACtB,eAAc;2BACS,cAAc;;;;;AAOvC,KAAI,cAAc,SAAS,EACzB,eAAc;0BACQ,WAAW;;;;;;AAQnC,QAAO,KAAK,SAAS;;2CAEoB,WAAW"}
@@ -0,0 +1,8 @@
1
+ export { compile, type CompileResult, type CompileOptions } from "./compile.js";
2
+ export { ANGULAR_DECORATORS, ANGULAR_DECORATOR_CALL_RE } from "./constants.js";
3
+ export { scanFile, type RegistryEntry, type ComponentRegistry } from "./registry.js";
4
+ export { scanDtsFile, scanPackageDts, collectImportedPackages, collectRelativeReExports, collectAllImports } from "./dts-reader.js";
5
+ export { jitTransform, type JitTransformResult } from "./jit-transform.js";
6
+ export { generateHmrCode } from "./hmr.js";
7
+ export { inlineResourceUrls, extractInlineStyles } from "./resource-inliner.js";
8
+ export { debugCompile, debugRegistry, debugResolve, debugEmit } from "./debug.js";
@@ -0,0 +1,8 @@
1
+ import "./constants.js";
2
+ import "./debug.js";
3
+ import "./compile.js";
4
+ import "./registry.js";
5
+ import "./dts-reader.js";
6
+ import "./jit-transform.js";
7
+ import "./hmr.js";
8
+ import "./resource-inliner.js";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Build ctorParameters for constructor DI in JIT format:
3
+ * [{ type: ServiceA }, { type: ServiceB, decorators: [{type: Optional}] }]
4
+ *
5
+ * Accepts an OXC ClassDeclaration node and the original source string.
6
+ */
7
+ export declare function buildCtorParameters(classNode: any, sourceCode: string, typeOnlyImports: Set<string>): string | null;
8
+ /**
9
+ * Build propDecorators for field decorators + signal API downleveling.
10
+ * Returns a JS object literal string or null if no props.
11
+ *
12
+ * Accepts an OXC ClassDeclaration node and the original source string.
13
+ */
14
+ export declare function buildPropDecorators(classNode: any, sourceCode: string): string | null;