@analogjs/vite-plugin-angular 3.0.0-alpha.5 → 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.
Files changed (194) hide show
  1. package/README.md +30 -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 +41 -12
  11. package/src/index.d.ts +3 -2
  12. package/src/index.js +6 -2
  13. package/src/index.js.map +1 -1
  14. package/src/lib/angular-build-optimizer-plugin.d.ts +4 -4
  15. package/src/lib/angular-build-optimizer-plugin.js +48 -62
  16. package/src/lib/angular-build-optimizer-plugin.js.map +1 -1
  17. package/src/lib/angular-jit-plugin.d.ts +3 -3
  18. package/src/lib/angular-jit-plugin.js +39 -37
  19. package/src/lib/angular-jit-plugin.js.map +1 -1
  20. package/src/lib/angular-pending-tasks.plugin.d.ts +7 -7
  21. package/src/lib/angular-pending-tasks.plugin.js +17 -18
  22. package/src/lib/angular-pending-tasks.plugin.js.map +1 -1
  23. package/src/lib/angular-vite-plugin.d.ts +180 -44
  24. package/src/lib/angular-vite-plugin.js +1233 -969
  25. package/src/lib/angular-vite-plugin.js.map +1 -1
  26. package/src/lib/angular-vitest-plugin.d.ts +19 -15
  27. package/src/lib/angular-vitest-plugin.js +99 -114
  28. package/src/lib/angular-vitest-plugin.js.map +1 -1
  29. package/src/lib/compilation-api/compilation-api-plugin.d.ts +29 -0
  30. package/src/lib/compilation-api/compilation-api-plugin.js +469 -0
  31. package/src/lib/compilation-api/compilation-api-plugin.js.map +1 -0
  32. package/src/lib/compilation-api/index.d.ts +1 -0
  33. package/src/lib/compilation-api/index.js +1 -0
  34. package/src/lib/compiler/angular-version.d.ts +19 -0
  35. package/src/lib/compiler/angular-version.js +16 -0
  36. package/src/lib/compiler/angular-version.js.map +1 -0
  37. package/src/lib/compiler/class-field-lowering.d.ts +23 -0
  38. package/src/lib/compiler/class-field-lowering.js +131 -0
  39. package/src/lib/compiler/class-field-lowering.js.map +1 -0
  40. package/src/lib/compiler/compile.d.ts +44 -0
  41. package/src/lib/compiler/compile.js +731 -0
  42. package/src/lib/compiler/compile.js.map +1 -0
  43. package/src/lib/compiler/constants.d.ts +18 -0
  44. package/src/lib/compiler/constants.js +52 -0
  45. package/src/lib/compiler/constants.js.map +1 -0
  46. package/src/lib/compiler/debug.d.ts +22 -0
  47. package/src/lib/compiler/debug.js +20 -0
  48. package/src/lib/compiler/debug.js.map +1 -0
  49. package/src/lib/compiler/defer.d.ts +57 -0
  50. package/src/lib/compiler/defer.js +154 -0
  51. package/src/lib/compiler/defer.js.map +1 -0
  52. package/src/lib/compiler/dts-reader.d.ts +35 -0
  53. package/src/lib/compiler/dts-reader.js +365 -0
  54. package/src/lib/compiler/dts-reader.js.map +1 -0
  55. package/src/lib/compiler/hmr.d.ts +16 -0
  56. package/src/lib/compiler/hmr.js +69 -0
  57. package/src/lib/compiler/hmr.js.map +1 -0
  58. package/src/lib/compiler/index.d.ts +7 -0
  59. package/src/lib/compiler/index.js +7 -0
  60. package/src/lib/compiler/jit-metadata.d.ts +14 -0
  61. package/src/lib/compiler/jit-metadata.js +146 -0
  62. package/src/lib/compiler/jit-metadata.js.map +1 -0
  63. package/src/lib/compiler/jit-transform.d.ts +24 -0
  64. package/src/lib/compiler/jit-transform.js +200 -0
  65. package/src/lib/compiler/jit-transform.js.map +1 -0
  66. package/src/lib/compiler/js-emitter.d.ts +10 -0
  67. package/src/lib/compiler/js-emitter.js +445 -0
  68. package/src/lib/compiler/js-emitter.js.map +1 -0
  69. package/src/lib/compiler/metadata.d.ts +45 -0
  70. package/src/lib/compiler/metadata.js +633 -0
  71. package/src/lib/compiler/metadata.js.map +1 -0
  72. package/src/lib/compiler/registry.d.ts +49 -0
  73. package/src/lib/compiler/registry.js +164 -0
  74. package/src/lib/compiler/registry.js.map +1 -0
  75. package/src/lib/compiler/resource-inliner.d.ts +21 -0
  76. package/src/lib/compiler/resource-inliner.js +152 -0
  77. package/src/lib/compiler/resource-inliner.js.map +1 -0
  78. package/src/lib/compiler/style-ast.d.ts +8 -0
  79. package/src/lib/compiler/style-ast.js +54 -0
  80. package/src/lib/compiler/style-ast.js.map +1 -0
  81. package/src/lib/compiler/styles.d.ts +13 -0
  82. package/src/lib/compiler/test-helpers.d.ts +7 -0
  83. package/src/lib/compiler/type-elision.d.ts +26 -0
  84. package/src/lib/compiler/type-elision.js +211 -0
  85. package/src/lib/compiler/type-elision.js.map +1 -0
  86. package/src/lib/compiler/utils.d.ts +10 -0
  87. package/src/lib/compiler/utils.js +35 -0
  88. package/src/lib/compiler/utils.js.map +1 -0
  89. package/src/lib/compiler-plugin.d.ts +11 -11
  90. package/src/lib/compiler-plugin.js +43 -44
  91. package/src/lib/compiler-plugin.js.map +1 -1
  92. package/src/lib/component-resolvers.d.ts +23 -5
  93. package/src/lib/component-resolvers.js +153 -63
  94. package/src/lib/component-resolvers.js.map +1 -1
  95. package/src/lib/encapsulation-plugin.d.ts +13 -0
  96. package/src/lib/encapsulation-plugin.js +48 -0
  97. package/src/lib/encapsulation-plugin.js.map +1 -0
  98. package/src/lib/fast-compile-plugin.d.ts +28 -0
  99. package/src/lib/fast-compile-plugin.js +289 -0
  100. package/src/lib/fast-compile-plugin.js.map +1 -0
  101. package/src/lib/host.d.ts +10 -8
  102. package/src/lib/host.js +113 -101
  103. package/src/lib/host.js.map +1 -1
  104. package/src/lib/live-reload-plugin.d.ts +5 -5
  105. package/src/lib/live-reload-plugin.js +61 -62
  106. package/src/lib/live-reload-plugin.js.map +1 -1
  107. package/src/lib/models.d.ts +9 -9
  108. package/src/lib/nx-folder-plugin.d.ts +5 -5
  109. package/src/lib/nx-folder-plugin.js +18 -16
  110. package/src/lib/nx-folder-plugin.js.map +1 -1
  111. package/src/lib/plugins/file-replacements.plugin.d.ts +4 -4
  112. package/src/lib/plugins/file-replacements.plugin.js +40 -62
  113. package/src/lib/plugins/file-replacements.plugin.js.map +1 -1
  114. package/src/lib/router-plugin.d.ts +1 -1
  115. package/src/lib/router-plugin.js +23 -23
  116. package/src/lib/router-plugin.js.map +1 -1
  117. package/src/lib/style-pipeline.d.ts +15 -0
  118. package/src/lib/style-pipeline.js +31 -0
  119. package/src/lib/style-pipeline.js.map +1 -0
  120. package/src/lib/style-preprocessor.d.ts +35 -0
  121. package/src/lib/style-preprocessor.js +35 -0
  122. package/src/lib/style-preprocessor.js.map +1 -0
  123. package/src/lib/stylesheet-registry.d.ts +73 -0
  124. package/src/lib/stylesheet-registry.js +168 -0
  125. package/src/lib/stylesheet-registry.js.map +1 -0
  126. package/src/lib/tailwind-plugin.d.ts +34 -0
  127. package/src/lib/tailwind-plugin.js +116 -0
  128. package/src/lib/tailwind-plugin.js.map +1 -0
  129. package/src/lib/template-class-binding-guard-plugin.d.ts +30 -0
  130. package/src/lib/template-class-binding-guard-plugin.js +237 -0
  131. package/src/lib/template-class-binding-guard-plugin.js.map +1 -0
  132. package/src/lib/tools/package.json +2 -7
  133. package/src/lib/tools/src/builders/vite/vite-build.impl.js +31 -38
  134. package/src/lib/tools/src/builders/vite/vite-build.impl.js.map +1 -1
  135. package/src/lib/tools/src/builders/vite-dev-server/dev-server.impl.js +51 -62
  136. package/src/lib/tools/src/builders/vite-dev-server/dev-server.impl.js.map +1 -1
  137. package/src/lib/tools/src/index.js +0 -2
  138. package/src/lib/utils/compilation-shared.d.ts +58 -0
  139. package/src/lib/utils/compilation-shared.js +133 -0
  140. package/src/lib/utils/compilation-shared.js.map +1 -0
  141. package/src/lib/utils/compiler-plugin-options.d.ts +11 -11
  142. package/src/lib/utils/debug-harness.d.ts +23 -0
  143. package/src/lib/utils/debug-harness.js +88 -0
  144. package/src/lib/utils/debug-harness.js.map +1 -0
  145. package/src/lib/utils/debug-log-file.d.ts +5 -0
  146. package/src/lib/utils/debug-log-file.js +56 -0
  147. package/src/lib/utils/debug-log-file.js.map +1 -0
  148. package/src/lib/utils/debug.d.ts +28 -0
  149. package/src/lib/utils/debug.js +39 -0
  150. package/src/lib/utils/debug.js.map +1 -0
  151. package/src/lib/utils/devkit.d.ts +6 -6
  152. package/src/lib/utils/devkit.js +34 -38
  153. package/src/lib/utils/devkit.js.map +1 -1
  154. package/src/lib/utils/hmr-candidates.d.ts +28 -28
  155. package/src/lib/utils/plugin-config.d.ts +37 -0
  156. package/src/lib/utils/plugin-config.js +71 -0
  157. package/src/lib/utils/plugin-config.js.map +1 -0
  158. package/src/lib/utils/rolldown.d.ts +2 -0
  159. package/src/lib/utils/rolldown.js +12 -0
  160. package/src/lib/utils/rolldown.js.map +1 -0
  161. package/src/lib/utils/safe-module-paths.d.ts +16 -0
  162. package/src/lib/utils/safe-module-paths.js +29 -0
  163. package/src/lib/utils/safe-module-paths.js.map +1 -0
  164. package/src/lib/utils/source-file-cache.d.ts +8 -15
  165. package/src/lib/utils/source-file-cache.js +35 -37
  166. package/src/lib/utils/source-file-cache.js.map +1 -1
  167. package/src/lib/utils/tailwind-reference.d.ts +12 -0
  168. package/src/lib/utils/tailwind-reference.js +99 -0
  169. package/src/lib/utils/tailwind-reference.js.map +1 -0
  170. package/src/lib/utils/tsconfig-resolver.d.ts +28 -0
  171. package/src/lib/utils/tsconfig-resolver.js +191 -0
  172. package/src/lib/utils/tsconfig-resolver.js.map +1 -0
  173. package/src/lib/utils/virtual-ids.d.ts +4 -0
  174. package/src/lib/utils/virtual-ids.js +23 -0
  175. package/src/lib/utils/virtual-ids.js.map +1 -0
  176. package/src/lib/utils/virtual-resources.d.ts +19 -0
  177. package/src/lib/utils/virtual-resources.js +38 -0
  178. package/src/lib/utils/virtual-resources.js.map +1 -0
  179. package/src/lib/virtual-modules-plugin.d.ts +5 -0
  180. package/src/lib/virtual-modules-plugin.js +23 -0
  181. package/src/lib/virtual-modules-plugin.js.map +1 -0
  182. package/src/test-setup.d.ts +2 -0
  183. package/setup-vitest.d.ts +0 -4
  184. package/setup-vitest.js +0 -215
  185. package/setup-vitest.js.map +0 -1
  186. package/src/lib/models.js +0 -1
  187. package/src/lib/models.js.map +0 -1
  188. package/src/lib/tools/README.md +0 -3
  189. package/src/lib/tools/src/index.d.ts +0 -0
  190. package/src/lib/tools/src/index.js.map +0 -1
  191. package/src/lib/utils/compiler-plugin-options.js +0 -1
  192. package/src/lib/utils/compiler-plugin-options.js.map +0 -1
  193. package/src/lib/utils/hmr-candidates.js +0 -272
  194. package/src/lib/utils/hmr-candidates.js.map +0 -1
@@ -1,69 +1,159 @@
1
- import { dirname, resolve } from 'node:path';
2
- import { Project, SyntaxKind, } from 'ts-morph';
3
- import { normalizePath } from 'vite';
4
- export class StyleUrlsResolver {
5
- // These resolvers may be called multiple times during the same
6
- // compilation for the same files. Caching is required because these
7
- // resolvers use synchronous system calls to the filesystem, which can
8
- // degrade performance when running compilations for multiple files.
9
- styleUrlsCache = new Map();
10
- resolve(code, id) {
11
- // Given the code is the following:
12
- // @Component({
13
- // styleUrls: [
14
- // './app.component.scss'
15
- // ]
16
- // })
17
- // The `matchedStyleUrls` would result in: `styleUrls: [\n './app.component.scss'\n ]`.
18
- const matchedStyleUrls = getStyleUrls(code);
19
- const entry = this.styleUrlsCache.get(id);
20
- // We're using `matchedStyleUrls` as a key because the code may be changing continuously,
21
- // resulting in the resolver being called multiple times. While the code changes, the
22
- // `styleUrls` may remain constant, which means we should always return the previously
23
- // resolved style URLs.
24
- if (entry && entry.matchedStyleUrls === matchedStyleUrls) {
25
- return entry.styleUrls;
26
- }
27
- const styleUrls = matchedStyleUrls.map((styleUrlPath) => {
28
- return `${styleUrlPath}|${normalizePath(resolve(dirname(id), styleUrlPath))}`;
29
- });
30
- this.styleUrlsCache.set(id, { styleUrls, matchedStyleUrls });
31
- return styleUrls;
32
- }
1
+ import { dirname, resolve } from "node:path";
2
+ import { normalizePath } from "vite";
3
+ import { parseSync } from "oxc-parser";
4
+ import { Visitor } from "rolldown/utils";
5
+ //#region packages/vite-plugin-angular/src/lib/component-resolvers.ts
6
+ /**
7
+ * Extracts a string value from an ESTree AST node.
8
+ *
9
+ * Handles three forms that Angular decorators may use:
10
+ * - `Literal` with a string value → `'./foo.css'` / `"./foo.css"`
11
+ * - `StringLiteral` (OXC-specific) same representation
12
+ * - `TemplateLiteral` with zero expressions → `` `./foo.css` ``
13
+ *
14
+ * Uses `any` because OXC's AST mixes standard ESTree nodes with
15
+ * OXC-specific variants (e.g. `StringLiteral`), and the project's
16
+ * tsconfig enforces `noPropertyAccessFromIndexSignature`.
17
+ */
18
+ function getStringValue(node) {
19
+ if (!node) return void 0;
20
+ if (node.type === "Literal" && typeof node.value === "string") return node.value;
21
+ if (node.type === "StringLiteral") return node.value;
22
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) return node.quasis[0].value.cooked ?? node.quasis[0].value.raw;
33
23
  }
34
- function getTextByProperty(name, properties) {
35
- return properties
36
- .filter((property) => property.getName() === name)
37
- .map((property) => property.getInitializer()?.getText().replace(/['"`]/g, ''))
38
- .filter((url) => url !== undefined);
24
+ /**
25
+ * Parses TypeScript/JS source with OXC and collects `styleUrl`, `styleUrls`,
26
+ * and `templateUrl` property values from Angular `@Component()` decorators
27
+ * in a single AST pass.
28
+ *
29
+ * This replaces the previous ts-morph implementation — OXC parses natively
30
+ * via Rust NAPI bindings, avoiding the overhead of spinning up a full
31
+ * TypeScript `Project` for each file.
32
+ */
33
+ function collectComponentUrls(code) {
34
+ const { program } = parseSync("cmp.ts", code);
35
+ const styleUrls = [];
36
+ const templateUrls = [];
37
+ const inlineTemplates = [];
38
+ new Visitor({ ClassDeclaration(node) {
39
+ const decorators = node.decorators ?? [];
40
+ for (const decorator of decorators) {
41
+ const expression = decorator.expression;
42
+ if (expression?.type !== "CallExpression" || expression.callee?.type !== "Identifier" || expression.callee.name !== "Component") continue;
43
+ const componentArg = expression.arguments?.[0];
44
+ if (componentArg?.type !== "ObjectExpression") continue;
45
+ for (const property of componentArg.properties ?? []) {
46
+ if (property?.type !== "Property" || property.key?.type !== "Identifier") continue;
47
+ const name = property.key.name;
48
+ if (name === "styleUrls" && property.value?.type === "ArrayExpression") for (const el of property.value.elements) {
49
+ const val = getStringValue(el);
50
+ if (val !== void 0) styleUrls.push(val);
51
+ }
52
+ if (name === "styleUrl") {
53
+ const val = getStringValue(property.value);
54
+ if (val !== void 0) styleUrls.push(val);
55
+ }
56
+ if (name === "templateUrl") {
57
+ const val = getStringValue(property.value);
58
+ if (val !== void 0) templateUrls.push(val);
59
+ }
60
+ if (name === "template") {
61
+ const val = getStringValue(property.value);
62
+ if (val !== void 0) inlineTemplates.push(val);
63
+ }
64
+ }
65
+ }
66
+ } }).visit(program);
67
+ return {
68
+ styleUrls,
69
+ templateUrls,
70
+ inlineTemplates
71
+ };
39
72
  }
40
- export function getStyleUrls(code) {
41
- const project = new Project({ useInMemoryFileSystem: true });
42
- const sourceFile = project.createSourceFile('cmp.ts', code);
43
- const properties = sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAssignment);
44
- const styleUrl = getTextByProperty('styleUrl', properties);
45
- const styleUrls = properties
46
- .filter((property) => property.getName() === 'styleUrls')
47
- .map((property) => property.getInitializer())
48
- .flatMap((array) => array.getElements().map((el) => el.getText().replace(/['"`]/g, '')));
49
- return [...styleUrls, ...styleUrl];
73
+ /**
74
+ * Extract Angular component identities from raw source code before Angular's
75
+ * compilation pipeline strips decorators. This is used for dev-time
76
+ * diagnostics such as duplicate selectors, duplicate component class names,
77
+ * selectorless shared components, and inline-template validation.
78
+ */
79
+ function getAngularComponentMetadata(code) {
80
+ const { program } = parseSync("cmp.ts", code);
81
+ const components = [];
82
+ new Visitor({ ClassDeclaration(node) {
83
+ const decorators = node.decorators ?? [];
84
+ for (const decorator of decorators) {
85
+ const expression = decorator.expression;
86
+ if (expression?.type !== "CallExpression" || expression.callee?.type !== "Identifier" || expression.callee.name !== "Component") continue;
87
+ const componentArg = expression.arguments?.[0];
88
+ if (componentArg?.type !== "ObjectExpression") continue;
89
+ const metadata = {
90
+ className: node.id?.name ?? "(anonymous)",
91
+ styleUrls: [],
92
+ templateUrls: [],
93
+ inlineTemplates: []
94
+ };
95
+ for (const property of componentArg.properties ?? []) {
96
+ if (property?.type !== "Property" || property.key?.type !== "Identifier") continue;
97
+ const name = property.key.name;
98
+ if (name === "selector") metadata.selector = getStringValue(property.value);
99
+ else if (name === "styleUrl") {
100
+ const val = getStringValue(property.value);
101
+ if (val !== void 0) metadata.styleUrls.push(val);
102
+ } else if (name === "styleUrls" && property.value?.type === "ArrayExpression") for (const el of property.value.elements ?? []) {
103
+ const val = getStringValue(el);
104
+ if (val !== void 0) metadata.styleUrls.push(val);
105
+ }
106
+ else if (name === "templateUrl") {
107
+ const val = getStringValue(property.value);
108
+ if (val !== void 0) metadata.templateUrls.push(val);
109
+ } else if (name === "template") {
110
+ const val = getStringValue(property.value);
111
+ if (val !== void 0) metadata.inlineTemplates.push(val);
112
+ }
113
+ }
114
+ components.push(metadata);
115
+ }
116
+ } }).visit(program);
117
+ return components;
50
118
  }
51
- export function getTemplateUrls(code) {
52
- const project = new Project({ useInMemoryFileSystem: true });
53
- const sourceFile = project.createSourceFile('cmp.ts', code);
54
- const properties = sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAssignment);
55
- return getTextByProperty('templateUrl', properties);
119
+ /** Extract all `styleUrl` / `styleUrls` values from Angular component source. */
120
+ function getStyleUrls(code) {
121
+ return collectComponentUrls(code).styleUrls;
56
122
  }
57
- export class TemplateUrlsResolver {
58
- templateUrlsCache = new Map();
59
- resolve(code, id) {
60
- const entry = this.templateUrlsCache.get(id);
61
- if (entry?.code === code) {
62
- return entry.templateUrlPaths;
63
- }
64
- const templateUrlPaths = getTemplateUrls(code).map((url) => `${url}|${normalizePath(resolve(dirname(id), url))}`);
65
- this.templateUrlsCache.set(id, { code, templateUrlPaths });
66
- return templateUrlPaths;
67
- }
123
+ /** Extract all `templateUrl` values from Angular component source. */
124
+ function getTemplateUrls(code) {
125
+ return collectComponentUrls(code).templateUrls;
68
126
  }
127
+ var StyleUrlsResolver = class {
128
+ styleUrlsCache = /* @__PURE__ */ new Map();
129
+ resolve(code, id) {
130
+ const matchedStyleUrls = getStyleUrls(code);
131
+ const entry = this.styleUrlsCache.get(id);
132
+ if (entry && entry.matchedStyleUrls === matchedStyleUrls) return entry.styleUrls;
133
+ const styleUrls = matchedStyleUrls.map((styleUrlPath) => {
134
+ return `${styleUrlPath}|${normalizePath(resolve(dirname(id), styleUrlPath))}`;
135
+ });
136
+ this.styleUrlsCache.set(id, {
137
+ styleUrls,
138
+ matchedStyleUrls
139
+ });
140
+ return styleUrls;
141
+ }
142
+ };
143
+ var TemplateUrlsResolver = class {
144
+ templateUrlsCache = /* @__PURE__ */ new Map();
145
+ resolve(code, id) {
146
+ const entry = this.templateUrlsCache.get(id);
147
+ if (entry?.code === code) return entry.templateUrlPaths;
148
+ const templateUrlPaths = getTemplateUrls(code).map((url) => `${url}|${normalizePath(resolve(dirname(id), url))}`);
149
+ this.templateUrlsCache.set(id, {
150
+ code,
151
+ templateUrlPaths
152
+ });
153
+ return templateUrlPaths;
154
+ }
155
+ };
156
+ //#endregion
157
+ export { StyleUrlsResolver, TemplateUrlsResolver, getAngularComponentMetadata };
158
+
69
159
  //# sourceMappingURL=component-resolvers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"component-resolvers.js","sourceRoot":"","sources":["../../../../../packages/vite-plugin-angular/src/lib/component-resolvers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"component-resolvers.js","names":[],"sources":["../../../src/lib/component-resolvers.ts"],"sourcesContent":["import { dirname, resolve } from 'node:path';\n// OXC parser (native Rust, NAPI-RS) replaces ts-morph for AST extraction.\n// It is ~10-50x faster for the narrow task of pulling property values from\n// Angular component decorators. The Visitor helper from Rolldown walks the\n// ESTree-compatible AST that OXC produces.\nimport { parseSync } from 'oxc-parser';\nimport { Visitor } from 'rolldown/utils';\nimport { normalizePath } from 'vite';\n\n// ---------------------------------------------------------------------------\n// AST helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extracts a string value from an ESTree AST node.\n *\n * Handles three forms that Angular decorators may use:\n * - `Literal` with a string value → `'./foo.css'` / `\"./foo.css\"`\n * - `StringLiteral` (OXC-specific) → same representation\n * - `TemplateLiteral` with zero expressions → `` `./foo.css` ``\n *\n * Uses `any` because OXC's AST mixes standard ESTree nodes with\n * OXC-specific variants (e.g. `StringLiteral`), and the project's\n * tsconfig enforces `noPropertyAccessFromIndexSignature`.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction getStringValue(node: any): string | undefined {\n if (!node) return undefined;\n // Standard ESTree Literal (string value)\n if (node.type === 'Literal' && typeof node.value === 'string') {\n return node.value;\n }\n // OXC-specific StringLiteral node\n if (node.type === 'StringLiteral') {\n return node.value;\n }\n // Template literal with no interpolation (e.g., `./foo.css`)\n if (\n node.type === 'TemplateLiteral' &&\n node.expressions.length === 0 &&\n node.quasis.length === 1\n ) {\n return node.quasis[0].value.cooked ?? node.quasis[0].value.raw;\n }\n return undefined;\n}\n\n/**\n * Parses TypeScript/JS source with OXC and collects `styleUrl`, `styleUrls`,\n * and `templateUrl` property values from Angular `@Component()` decorators\n * in a single AST pass.\n *\n * This replaces the previous ts-morph implementation — OXC parses natively\n * via Rust NAPI bindings, avoiding the overhead of spinning up a full\n * TypeScript `Project` for each file.\n */\nfunction collectComponentUrls(code: string): {\n styleUrls: string[];\n templateUrls: string[];\n inlineTemplates: string[];\n} {\n const { program } = parseSync('cmp.ts', code);\n const styleUrls: string[] = [];\n const templateUrls: string[] = [];\n const inlineTemplates: string[] = [];\n\n const visitor = new Visitor({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ClassDeclaration(node: any) {\n const decorators = node.decorators ?? [];\n for (const decorator of decorators) {\n const expression = decorator.expression;\n if (\n expression?.type !== 'CallExpression' ||\n expression.callee?.type !== 'Identifier' ||\n expression.callee.name !== 'Component'\n ) {\n continue;\n }\n\n const componentArg = expression.arguments?.[0];\n if (componentArg?.type !== 'ObjectExpression') {\n continue;\n }\n\n for (const property of componentArg.properties ?? []) {\n if (\n property?.type !== 'Property' ||\n property.key?.type !== 'Identifier'\n ) {\n continue;\n }\n\n const name = property.key.name;\n\n if (\n name === 'styleUrls' &&\n property.value?.type === 'ArrayExpression'\n ) {\n for (const el of property.value.elements) {\n const val = getStringValue(el);\n if (val !== undefined) styleUrls.push(val);\n }\n }\n\n if (name === 'styleUrl') {\n const val = getStringValue(property.value);\n if (val !== undefined) styleUrls.push(val);\n }\n\n if (name === 'templateUrl') {\n const val = getStringValue(property.value);\n if (val !== undefined) templateUrls.push(val);\n }\n\n if (name === 'template') {\n const val = getStringValue(property.value);\n if (val !== undefined) inlineTemplates.push(val);\n }\n }\n }\n },\n });\n visitor.visit(program);\n\n return { styleUrls, templateUrls, inlineTemplates };\n}\n\nexport interface AngularComponentMetadata {\n className: string;\n selector?: string;\n styleUrls: string[];\n templateUrls: string[];\n inlineTemplates: string[];\n}\n\n/**\n * Extract Angular component identities from raw source code before Angular's\n * compilation pipeline strips decorators. This is used for dev-time\n * diagnostics such as duplicate selectors, duplicate component class names,\n * selectorless shared components, and inline-template validation.\n */\nexport function getAngularComponentMetadata(\n code: string,\n): AngularComponentMetadata[] {\n const { program } = parseSync('cmp.ts', code);\n const components: AngularComponentMetadata[] = [];\n\n const visitor = new Visitor({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ClassDeclaration(node: any) {\n const decorators = node.decorators ?? [];\n for (const decorator of decorators) {\n const expression = decorator.expression;\n if (\n expression?.type !== 'CallExpression' ||\n expression.callee?.type !== 'Identifier' ||\n expression.callee.name !== 'Component'\n ) {\n continue;\n }\n\n const componentArg = expression.arguments?.[0];\n if (componentArg?.type !== 'ObjectExpression') {\n continue;\n }\n\n const metadata: AngularComponentMetadata = {\n className: node.id?.name ?? '(anonymous)',\n styleUrls: [],\n templateUrls: [],\n inlineTemplates: [],\n };\n\n for (const property of componentArg.properties ?? []) {\n if (\n property?.type !== 'Property' ||\n property.key?.type !== 'Identifier'\n ) {\n continue;\n }\n\n const name = property.key.name;\n if (name === 'selector') {\n metadata.selector = getStringValue(property.value);\n } else if (name === 'styleUrl') {\n const val = getStringValue(property.value);\n if (val !== undefined) {\n metadata.styleUrls.push(val);\n }\n } else if (\n name === 'styleUrls' &&\n property.value?.type === 'ArrayExpression'\n ) {\n for (const el of property.value.elements ?? []) {\n const val = getStringValue(el);\n if (val !== undefined) {\n metadata.styleUrls.push(val);\n }\n }\n } else if (name === 'templateUrl') {\n const val = getStringValue(property.value);\n if (val !== undefined) {\n metadata.templateUrls.push(val);\n }\n } else if (name === 'template') {\n const val = getStringValue(property.value);\n if (val !== undefined) {\n metadata.inlineTemplates.push(val);\n }\n }\n }\n\n components.push(metadata);\n }\n },\n });\n visitor.visit(program);\n\n return components;\n}\n\n/** Extract all `styleUrl` / `styleUrls` values from Angular component source. */\nexport function getStyleUrls(code: string): string[] {\n return collectComponentUrls(code).styleUrls;\n}\n\n/** Extract all `templateUrl` values from Angular component source. */\nexport function getTemplateUrls(code: string): string[] {\n return collectComponentUrls(code).templateUrls;\n}\n\n/** Extract inline `template` strings from Angular component source. */\nexport function getInlineTemplates(code: string): string[] {\n return collectComponentUrls(code).inlineTemplates;\n}\n\n// ---------------------------------------------------------------------------\n// Resolver caches\n// ---------------------------------------------------------------------------\n\ninterface StyleUrlsCacheEntry {\n matchedStyleUrls: string[];\n styleUrls: string[];\n}\n\nexport class StyleUrlsResolver {\n // These resolvers may be called multiple times during the same\n // compilation for the same files. Caching is required because these\n // resolvers use synchronous system calls to the filesystem, which can\n // degrade performance when running compilations for multiple files.\n private readonly styleUrlsCache = new Map<string, StyleUrlsCacheEntry>();\n\n resolve(code: string, id: string): string[] {\n const matchedStyleUrls = getStyleUrls(code);\n const entry = this.styleUrlsCache.get(id);\n // We're using `matchedStyleUrls` as a key because the code may be changing continuously,\n // resulting in the resolver being called multiple times. While the code changes, the\n // `styleUrls` may remain constant, which means we should always return the previously\n // resolved style URLs.\n if (entry && entry.matchedStyleUrls === matchedStyleUrls) {\n return entry.styleUrls;\n }\n\n const styleUrls = matchedStyleUrls.map((styleUrlPath) => {\n return `${styleUrlPath}|${normalizePath(\n resolve(dirname(id), styleUrlPath),\n )}`;\n });\n\n this.styleUrlsCache.set(id, { styleUrls, matchedStyleUrls });\n return styleUrls;\n }\n}\n\ninterface TemplateUrlsCacheEntry {\n code: string;\n templateUrlPaths: string[];\n}\n\nexport class TemplateUrlsResolver {\n private readonly templateUrlsCache = new Map<\n string,\n TemplateUrlsCacheEntry\n >();\n\n resolve(code: string, id: string): string[] {\n const entry = this.templateUrlsCache.get(id);\n if (entry?.code === code) {\n return entry.templateUrlPaths;\n }\n\n const templateUrlPaths = getTemplateUrls(code).map(\n (url) => `${url}|${normalizePath(resolve(dirname(id), url))}`,\n );\n\n this.templateUrlsCache.set(id, { code, templateUrlPaths });\n return templateUrlPaths;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { Plugin } from "vite";
2
+ export declare function isComponentStyleSheet(id: string): boolean;
3
+ export declare function getComponentStyleSheetMeta(id: string): {
4
+ componentId: string;
5
+ encapsulation: "emulated" | "shadow" | "none";
6
+ };
7
+ /**
8
+ * Encapsulation runs in enforce: 'post' so that @tailwindcss/vite
9
+ * (enforce: 'pre') fully resolves @apply directives — including those
10
+ * inside :host {} — before Angular's ShadowCss rewrites selectors.
11
+ * (#2293)
12
+ */
13
+ export declare function encapsulationPlugin(shouldExternalizeStyles: () => boolean): Plugin;
@@ -0,0 +1,48 @@
1
+ import { debugStylesV } from "./utils/debug.js";
2
+ import * as ngCompiler from "@angular/compiler";
3
+ //#region packages/vite-plugin-angular/src/lib/encapsulation-plugin.ts
4
+ function isComponentStyleSheet(id) {
5
+ return id.includes("ngcomp=");
6
+ }
7
+ function getComponentStyleSheetMeta(id) {
8
+ const params = new URL(id, "http://localhost").searchParams;
9
+ return {
10
+ componentId: params.get("ngcomp"),
11
+ encapsulation: {
12
+ "0": "emulated",
13
+ "2": "none",
14
+ "3": "shadow"
15
+ }[params.get("e")]
16
+ };
17
+ }
18
+ /**
19
+ * Encapsulation runs in enforce: 'post' so that @tailwindcss/vite
20
+ * (enforce: 'pre') fully resolves @apply directives — including those
21
+ * inside :host {} — before Angular's ShadowCss rewrites selectors.
22
+ * (#2293)
23
+ */
24
+ function encapsulationPlugin(shouldExternalizeStyles) {
25
+ return {
26
+ name: "@analogjs/vite-plugin-angular:encapsulation",
27
+ enforce: "post",
28
+ transform(code, id) {
29
+ if (shouldExternalizeStyles() && isComponentStyleSheet(id)) {
30
+ const { encapsulation, componentId } = getComponentStyleSheetMeta(id);
31
+ if (encapsulation === "emulated" && componentId) {
32
+ debugStylesV("applying emulated view encapsulation (post)", {
33
+ stylesheet: id.split("?")[0],
34
+ componentId
35
+ });
36
+ return {
37
+ code: ngCompiler.encapsulateStyle(code, componentId),
38
+ map: null
39
+ };
40
+ }
41
+ }
42
+ }
43
+ };
44
+ }
45
+ //#endregion
46
+ export { encapsulationPlugin, getComponentStyleSheetMeta, isComponentStyleSheet };
47
+
48
+ //# sourceMappingURL=encapsulation-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encapsulation-plugin.js","names":[],"sources":["../../../src/lib/encapsulation-plugin.ts"],"sourcesContent":["import * as ngCompiler from '@angular/compiler';\nimport { Plugin } from 'vite';\nimport { debugStylesV } from './utils/debug.js';\n\nexport function isComponentStyleSheet(id: string): boolean {\n return id.includes('ngcomp=');\n}\n\nexport function getComponentStyleSheetMeta(id: string): {\n componentId: string;\n encapsulation: 'emulated' | 'shadow' | 'none';\n} {\n const params = new URL(id, 'http://localhost').searchParams;\n const encapsulationMapping = {\n '0': 'emulated',\n '2': 'none',\n '3': 'shadow',\n };\n return {\n componentId: params.get('ngcomp')!,\n encapsulation: encapsulationMapping[\n params.get('e') as keyof typeof encapsulationMapping\n ] as 'emulated' | 'shadow' | 'none',\n };\n}\n\n/**\n * Encapsulation runs in enforce: 'post' so that @tailwindcss/vite\n * (enforce: 'pre') fully resolves @apply directives — including those\n * inside :host {} — before Angular's ShadowCss rewrites selectors.\n * (#2293)\n */\nexport function encapsulationPlugin(\n shouldExternalizeStyles: () => boolean,\n): Plugin {\n return {\n name: '@analogjs/vite-plugin-angular:encapsulation',\n enforce: 'post',\n transform(code: string, id: string) {\n if (shouldExternalizeStyles() && isComponentStyleSheet(id)) {\n const { encapsulation, componentId } = getComponentStyleSheetMeta(id);\n if (encapsulation === 'emulated' && componentId) {\n debugStylesV('applying emulated view encapsulation (post)', {\n stylesheet: id.split('?')[0],\n componentId,\n });\n const encapsulated = ngCompiler.encapsulateStyle(code, componentId);\n return {\n code: encapsulated,\n map: null,\n };\n }\n }\n },\n };\n}\n"],"mappings":";;;AAIA,SAAgB,sBAAsB,IAAqB;AACzD,QAAO,GAAG,SAAS,UAAU;;AAG/B,SAAgB,2BAA2B,IAGzC;CACA,MAAM,SAAS,IAAI,IAAI,IAAI,mBAAmB,CAAC;AAM/C,QAAO;EACL,aAAa,OAAO,IAAI,SAAS;EACjC,eAP2B;GAC3B,KAAK;GACL,KAAK;GACL,KAAK;GACN,CAIG,OAAO,IAAI,IAAI;EAElB;;;;;;;;AASH,SAAgB,oBACd,yBACQ;AACR,QAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,MAAc,IAAY;AAClC,OAAI,yBAAyB,IAAI,sBAAsB,GAAG,EAAE;IAC1D,MAAM,EAAE,eAAe,gBAAgB,2BAA2B,GAAG;AACrE,QAAI,kBAAkB,cAAc,aAAa;AAC/C,kBAAa,+CAA+C;MAC1D,YAAY,GAAG,MAAM,IAAI,CAAC;MAC1B;MACD,CAAC;AAEF,YAAO;MACL,MAFmB,WAAW,iBAAiB,MAAM,YAAY;MAGjE,KAAK;MACN;;;;EAIR"}
@@ -0,0 +1,28 @@
1
+ import { Plugin } from "vite";
2
+ import { type ComponentRegistry } from "./compiler/index.js";
3
+ declare global {
4
+ /**
5
+ * Shared convention for out-of-tree compilers (e.g. `@tsrx/analog`) that
6
+ * produce Angular Ivy definitions from a non-TS source format. Populate
7
+ * this map with directive/component metadata for any class fastCompile
8
+ * can't reach through its own tsconfig-driven scan, and the per-compile
9
+ * registry lookup in `fastCompilePlugin` will merge those entries in —
10
+ * so TS `@Component({ imports: [X] })` references to such classes
11
+ * resolve statically instead of hitting the `_unresolved-${className}`
12
+ * sentinel.
13
+ */
14
+ var __ANALOG_EXTERNAL_REGISTRY__: ComponentRegistry | undefined;
15
+ }
16
+ export interface FastCompilePluginOptions {
17
+ tsconfigGetter: () => string;
18
+ workspaceRoot: string;
19
+ inlineStylesExtension: string;
20
+ jit: boolean;
21
+ liveReload: boolean;
22
+ supportedBrowsers: string[];
23
+ transformFilter?: (code: string, id: string) => boolean;
24
+ isTest: boolean;
25
+ isAstroIntegration: boolean;
26
+ fastCompileMode?: "full" | "partial";
27
+ }
28
+ export declare function fastCompilePlugin(pluginOptions: FastCompilePluginOptions): Plugin;
@@ -0,0 +1,289 @@
1
+ import { TS_EXT_REGEX, createDepOptimizerConfig, getTsConfigPath } from "./utils/plugin-config.js";
2
+ import { toVirtualRawId } from "./utils/virtual-ids.js";
3
+ import { loadVirtualRawModule, rewriteHtmlRawImport } from "./utils/virtual-resources.js";
4
+ import { debugCompile, debugRegistry } from "./compiler/debug.js";
5
+ import { compile } from "./compiler/compile.js";
6
+ import { scanFile } from "./compiler/registry.js";
7
+ import { collectImportedPackages, collectRelativeReExports, scanPackageDts } from "./compiler/dts-reader.js";
8
+ import { jitTransform } from "./compiler/jit-transform.js";
9
+ import { generateHmrCode } from "./compiler/hmr.js";
10
+ import { extractInlineStyles, inlineResourceUrls } from "./compiler/resource-inliner.js";
11
+ import "./compiler/index.js";
12
+ import { markStylePathSafe } from "./utils/safe-module-paths.js";
13
+ import { promises } from "node:fs";
14
+ import { dirname, isAbsolute, resolve } from "node:path";
15
+ import * as compilerCli from "@angular/compiler-cli";
16
+ import * as vite from "vite";
17
+ import { defaultClientConditions, normalizePath, preprocessCSS } from "vite";
18
+ //#region packages/vite-plugin-angular/src/lib/fast-compile-plugin.ts
19
+ function fastCompilePlugin(pluginOptions) {
20
+ let resolvedConfig;
21
+ let tsConfigResolutionContext = null;
22
+ let watchMode = false;
23
+ const registry = /* @__PURE__ */ new Map();
24
+ const resourceToSource = /* @__PURE__ */ new Map();
25
+ const scannedDtsPackages = /* @__PURE__ */ new Set();
26
+ let projectRoot = "";
27
+ let useDefineForClassFields = true;
28
+ /**
29
+ * Scan a file into the registry, then recursively walk its relative
30
+ * `export *` / `export { … } from './x'` chain so any underlying
31
+ * directive classes also land in the registry. Used both at
32
+ * `buildStart` (for tsconfig path entries) and at dev time (file
33
+ * `add` and `handleHotUpdate`) so newly added barrels stay in sync
34
+ * without requiring a server restart.
35
+ *
36
+ * The `visited` set prevents infinite recursion within a single
37
+ * top-level call. Each fresh scan should pass an empty set (so HMR
38
+ * re-scans aren't blocked by buildStart's earlier visits).
39
+ */
40
+ async function scanBarrelExports(file, visited = /* @__PURE__ */ new Set(), overwrite = false) {
41
+ if (visited.has(file)) return;
42
+ visited.add(file);
43
+ let code;
44
+ try {
45
+ code = await promises.readFile(file, "utf-8");
46
+ } catch (e) {
47
+ if (debugRegistry.enabled) debugRegistry("scanBarrelExports: failed to read %s: %s", file, e?.message);
48
+ return;
49
+ }
50
+ const entries = scanFile(code, file);
51
+ for (const entry of entries) if (overwrite || !registry.has(entry.className)) registry.set(entry.className, entry);
52
+ const dir = dirname(file);
53
+ for (const rel of collectRelativeReExports(code, file)) {
54
+ const normalizedRel = rel.replace(/\.(?:js|mjs)$/u, "");
55
+ const reExportCandidates = [resolve(dir, normalizedRel + ".ts"), resolve(dir, normalizedRel, "index.ts")];
56
+ let resolved = false;
57
+ for (const candidate of reExportCandidates) try {
58
+ await promises.access(candidate);
59
+ await scanBarrelExports(candidate, visited, overwrite);
60
+ resolved = true;
61
+ break;
62
+ } catch {}
63
+ if (!resolved && debugRegistry.enabled) debugRegistry("scanBarrelExports: %s re-export %s did not resolve to %o", file, rel, reExportCandidates);
64
+ }
65
+ }
66
+ async function initFastCompile() {
67
+ if (pluginOptions.jit) return;
68
+ registry.clear();
69
+ scannedDtsPackages.clear();
70
+ const resolvedTsConfigPath = resolveTsConfigPath();
71
+ projectRoot = dirname(resolvedTsConfigPath);
72
+ const config = compilerCli.readConfiguration(resolvedTsConfigPath);
73
+ useDefineForClassFields = config.options?.useDefineForClassFields ?? true;
74
+ const candidates = new Set(config.rootNames);
75
+ const tsPaths = config.options?.paths;
76
+ const baseUrl = config.options?.baseUrl ?? projectRoot;
77
+ if (tsPaths) for (const targets of Object.values(tsPaths)) for (const target of targets) {
78
+ if (target.includes("*")) continue;
79
+ candidates.add(resolve(baseUrl, target));
80
+ }
81
+ const results = await Promise.all(Array.from(candidates).map(async (file) => {
82
+ try {
83
+ return scanFile(await promises.readFile(file, "utf-8"), file);
84
+ } catch (e) {
85
+ if (debugRegistry.enabled) debugRegistry("initFastCompile: skipping unreadable %s: %s", file, e?.message);
86
+ return [];
87
+ }
88
+ }));
89
+ for (const entries of results) for (const entry of entries) registry.set(entry.className, entry);
90
+ const buildStartVisited = /* @__PURE__ */ new Set();
91
+ if (tsPaths) {
92
+ const barrelCandidates = [];
93
+ for (const targets of Object.values(tsPaths)) for (const target of targets) {
94
+ if (target.includes("*")) continue;
95
+ barrelCandidates.push(resolve(baseUrl, target));
96
+ }
97
+ await Promise.all(barrelCandidates.map((c) => scanBarrelExports(c, buildStartVisited)));
98
+ }
99
+ debugRegistry("initFastCompile done: %d entries from %d candidate files", registry.size, candidates.size);
100
+ }
101
+ function ensureDtsRegistryForSource(code, id) {
102
+ for (const pkg of collectImportedPackages(code, id)) {
103
+ if (scannedDtsPackages.has(pkg)) continue;
104
+ scannedDtsPackages.add(pkg);
105
+ try {
106
+ const dtsEntries = scanPackageDts(pkg, projectRoot);
107
+ for (const entry of dtsEntries) if (!registry.has(entry.className)) registry.set(entry.className, entry);
108
+ } catch {}
109
+ }
110
+ }
111
+ async function handleFastCompileTransform(code, id) {
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
+ }
129
+ if (pluginOptions.jit) {
130
+ const result = jitTransform(code, id);
131
+ return {
132
+ code: result.code,
133
+ map: result.map
134
+ };
135
+ }
136
+ code = inlineResourceUrls(code, id);
137
+ let resolvedStyles;
138
+ let resolvedInlineStyles;
139
+ if (pluginOptions.inlineStylesExtension !== "css") {
140
+ const styleStrings = extractInlineStyles(code, id);
141
+ if (styleStrings.length > 0) {
142
+ resolvedInlineStyles = /* @__PURE__ */ new Map();
143
+ for (let i = 0; i < styleStrings.length; i++) try {
144
+ const fakePath = id.replace(/\.ts$/, `.inline-${i}.${pluginOptions.inlineStylesExtension}`);
145
+ const processed = await preprocessCSS(styleStrings[i], fakePath, resolvedConfig);
146
+ resolvedInlineStyles.set(i, processed.code);
147
+ } catch (e) {
148
+ if (debugCompile.enabled) debugCompile("inline style #%d preprocessing failed in %s: %s", i, id, e?.message);
149
+ }
150
+ if (resolvedInlineStyles.size === 0) resolvedInlineStyles = void 0;
151
+ }
152
+ }
153
+ ensureDtsRegistryForSource(code, id);
154
+ let compileRegistry = registry;
155
+ const externalRegistry = globalThis.__ANALOG_EXTERNAL_REGISTRY__;
156
+ if (externalRegistry && externalRegistry.size > 0) {
157
+ compileRegistry = new Map(registry);
158
+ for (const [k, v] of externalRegistry) compileRegistry.set(k, v);
159
+ }
160
+ const result = compile(code, id, {
161
+ registry: compileRegistry,
162
+ resolvedStyles,
163
+ resolvedInlineStyles,
164
+ useDefineForClassFields,
165
+ compilationMode: pluginOptions.fastCompileMode
166
+ });
167
+ for (const dep of result.resourceDependencies) resourceToSource.set(dep, id);
168
+ let outputCode = (vite.transformWithOxc ? await vite.transformWithOxc(result.code, id, {
169
+ lang: "ts",
170
+ sourcemap: false,
171
+ decorator: {
172
+ legacy: false,
173
+ emitDecoratorMetadata: false
174
+ }
175
+ }) : await vite.transformWithEsbuild(result.code, id, {
176
+ loader: "ts",
177
+ sourcemap: false
178
+ })).code;
179
+ if (watchMode && pluginOptions.liveReload) {
180
+ const fileDeclarations = [...registry.values()].filter((e) => e.fileName === id);
181
+ if (fileDeclarations.length > 0) {
182
+ const localDepClassNames = fileDeclarations.map((e) => e.className);
183
+ outputCode += generateHmrCode(fileDeclarations, localDepClassNames);
184
+ }
185
+ }
186
+ return {
187
+ code: outputCode,
188
+ map: result.map
189
+ };
190
+ }
191
+ function resolveTsConfigPath() {
192
+ const { root, isProd, isLib } = tsConfigResolutionContext;
193
+ return getTsConfigPath(root, pluginOptions.tsconfigGetter(), isProd, pluginOptions.isTest, isLib);
194
+ }
195
+ return {
196
+ name: "@analogjs/vite-plugin-angular-fast-compile",
197
+ enforce: "pre",
198
+ async config(config, { command }) {
199
+ watchMode = command === "serve";
200
+ const isProd = config.mode === "production" || process.env.NODE_ENV === "production";
201
+ tsConfigResolutionContext = {
202
+ root: config.root || ".",
203
+ isProd,
204
+ isLib: !!config?.build?.lib
205
+ };
206
+ const depOptimizer = createDepOptimizerConfig({
207
+ tsconfig: resolveTsConfigPath(),
208
+ isProd,
209
+ jit: pluginOptions.jit,
210
+ watchMode,
211
+ isTest: pluginOptions.isTest,
212
+ isAstroIntegration: pluginOptions.isAstroIntegration
213
+ });
214
+ return {
215
+ ...vite.rolldownVersion ? { oxc: {} } : { esbuild: false },
216
+ ...depOptimizer,
217
+ resolve: { conditions: [...depOptimizer.resolve.conditions, ...config.resolve?.conditions || defaultClientConditions] }
218
+ };
219
+ },
220
+ configResolved(config) {
221
+ resolvedConfig = config;
222
+ },
223
+ configureServer(server) {
224
+ server.watcher.on("add", async (filePath) => {
225
+ if (filePath.endsWith(".ts") && !filePath.endsWith(".spec.ts") && !filePath.endsWith(".d.ts")) await scanBarrelExports(filePath, /* @__PURE__ */ new Set(), true);
226
+ });
227
+ },
228
+ async buildStart() {
229
+ await initFastCompile();
230
+ },
231
+ async handleHotUpdate(ctx) {
232
+ if (resourceToSource.has(ctx.file)) {
233
+ const parentSource = resourceToSource.get(ctx.file);
234
+ const parentModule = ctx.server.moduleGraph.getModuleById(parentSource);
235
+ if (parentModule) return [parentModule];
236
+ }
237
+ if (TS_EXT_REGEX.test(ctx.file)) {
238
+ const [fileId] = ctx.file.split("?");
239
+ const oldEntries = [...registry.entries()].filter(([_, v]) => v.fileName === fileId).map(([k]) => k);
240
+ for (const key of oldEntries) registry.delete(key);
241
+ await scanBarrelExports(fileId, /* @__PURE__ */ new Set(), true);
242
+ }
243
+ return ctx.modules;
244
+ },
245
+ resolveId(id, importer) {
246
+ if (id.startsWith("virtual:@analogjs/vite-plugin-angular:raw:")) return `\0${id}`;
247
+ if (pluginOptions.jit && id.startsWith("angular:jit:")) {
248
+ const filePath = normalizePath(resolve(dirname(importer), id.split(";")[1]));
249
+ if (id.includes(":style")) {
250
+ markStylePathSafe(resolvedConfig, filePath);
251
+ return filePath + "?inline";
252
+ }
253
+ return toVirtualRawId(filePath);
254
+ }
255
+ const rawRewrite = rewriteHtmlRawImport(id, importer);
256
+ if (rawRewrite) return rawRewrite;
257
+ if (/\.(css|scss|sass|less)\?inline$/.test(id) && importer) {
258
+ const filePath = id.split("?")[0];
259
+ const resolved = isAbsolute(filePath) ? normalizePath(filePath) : normalizePath(resolve(dirname(importer), filePath));
260
+ markStylePathSafe(resolvedConfig, resolved);
261
+ return resolved + "?inline";
262
+ }
263
+ },
264
+ async load(id) {
265
+ const rawModule = await loadVirtualRawModule(this, id);
266
+ if (rawModule !== void 0) return rawModule;
267
+ if (/\.(css|scss|sass|less)\?inline$/.test(id)) markStylePathSafe(resolvedConfig, id.split("?")[0]);
268
+ },
269
+ transform: {
270
+ filter: { id: {
271
+ include: [TS_EXT_REGEX],
272
+ exclude: [
273
+ /node_modules/,
274
+ "type=script",
275
+ "@ng/component"
276
+ ]
277
+ } },
278
+ async handler(code, id) {
279
+ if (pluginOptions.transformFilter && !(pluginOptions.transformFilter(code, id) ?? true)) return;
280
+ if (id.includes(".ts?")) id = id.replace(/\?(.*)/, "");
281
+ return handleFastCompileTransform(code, id);
282
+ }
283
+ }
284
+ };
285
+ }
286
+ //#endregion
287
+ export { fastCompilePlugin };
288
+
289
+ //# sourceMappingURL=fast-compile-plugin.js.map