@analogjs/vite-plugin-angular 3.0.0-alpha.2 → 3.0.0-alpha.20

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 (79) hide show
  1. package/migrations/migrate-setup-vitest/migrate-setup-vitest.d.ts +2 -0
  2. package/migrations/migrate-setup-vitest/migrate-setup-vitest.js +49 -0
  3. package/migrations/migrate-setup-vitest/migrate-setup-vitest.js.map +1 -0
  4. package/migrations/migration.json +7 -1
  5. package/package.json +8 -11
  6. package/src/index.d.ts +3 -2
  7. package/src/index.js +6 -2
  8. package/src/index.js.map +1 -1
  9. package/src/lib/angular-build-optimizer-plugin.d.ts +4 -4
  10. package/src/lib/angular-build-optimizer-plugin.js +48 -62
  11. package/src/lib/angular-build-optimizer-plugin.js.map +1 -1
  12. package/src/lib/angular-jit-plugin.d.ts +3 -3
  13. package/src/lib/angular-jit-plugin.js +31 -37
  14. package/src/lib/angular-jit-plugin.js.map +1 -1
  15. package/src/lib/angular-pending-tasks.plugin.d.ts +7 -7
  16. package/src/lib/angular-pending-tasks.plugin.js +17 -18
  17. package/src/lib/angular-pending-tasks.plugin.js.map +1 -1
  18. package/src/lib/angular-vite-plugin.d.ts +145 -45
  19. package/src/lib/angular-vite-plugin.js +822 -963
  20. package/src/lib/angular-vite-plugin.js.map +1 -1
  21. package/src/lib/angular-vitest-plugin.d.ts +20 -16
  22. package/src/lib/angular-vitest-plugin.js +88 -114
  23. package/src/lib/angular-vitest-plugin.js.map +1 -1
  24. package/src/lib/compiler-plugin.d.ts +11 -11
  25. package/src/lib/compiler-plugin.js +43 -44
  26. package/src/lib/compiler-plugin.js.map +1 -1
  27. package/src/lib/component-resolvers.d.ts +8 -6
  28. package/src/lib/component-resolvers.js +93 -64
  29. package/src/lib/component-resolvers.js.map +1 -1
  30. package/src/lib/host.d.ts +10 -8
  31. package/src/lib/host.js +70 -101
  32. package/src/lib/host.js.map +1 -1
  33. package/src/lib/live-reload-plugin.d.ts +5 -5
  34. package/src/lib/live-reload-plugin.js +51 -62
  35. package/src/lib/live-reload-plugin.js.map +1 -1
  36. package/src/lib/models.d.ts +9 -9
  37. package/src/lib/nx-folder-plugin.d.ts +5 -5
  38. package/src/lib/nx-folder-plugin.js +18 -16
  39. package/src/lib/nx-folder-plugin.js.map +1 -1
  40. package/src/lib/plugins/file-replacements.plugin.d.ts +4 -4
  41. package/src/lib/plugins/file-replacements.plugin.js +35 -62
  42. package/src/lib/plugins/file-replacements.plugin.js.map +1 -1
  43. package/src/lib/router-plugin.d.ts +1 -1
  44. package/src/lib/router-plugin.js +23 -23
  45. package/src/lib/router-plugin.js.map +1 -1
  46. package/src/lib/style-preprocessor.d.ts +1 -0
  47. package/src/lib/tools/package.json +2 -7
  48. package/src/lib/tools/src/builders/vite/vite-build.impl.js +31 -35
  49. package/src/lib/tools/src/builders/vite/vite-build.impl.js.map +1 -1
  50. package/src/lib/tools/src/builders/vite-dev-server/dev-server.impl.js +51 -59
  51. package/src/lib/tools/src/builders/vite-dev-server/dev-server.impl.js.map +1 -1
  52. package/src/lib/tools/src/index.js +0 -1
  53. package/src/lib/utils/compiler-plugin-options.d.ts +11 -11
  54. package/src/lib/utils/devkit.d.ts +4 -4
  55. package/src/lib/utils/devkit.js +34 -38
  56. package/src/lib/utils/devkit.js.map +1 -1
  57. package/src/lib/utils/hmr-candidates.d.ts +28 -28
  58. package/src/lib/utils/rolldown.d.ts +2 -0
  59. package/src/lib/utils/rolldown.js +12 -0
  60. package/src/lib/utils/rolldown.js.map +1 -0
  61. package/src/lib/utils/source-file-cache.d.ts +8 -15
  62. package/src/lib/utils/source-file-cache.js +35 -37
  63. package/src/lib/utils/source-file-cache.js.map +1 -1
  64. package/src/test-setup.d.ts +2 -0
  65. package/setup-vitest.d.ts +0 -4
  66. package/setup-vitest.js +0 -217
  67. package/setup-vitest.js.map +0 -1
  68. package/src/lib/models.js +0 -1
  69. package/src/lib/models.js.map +0 -1
  70. package/src/lib/tools/README.md +0 -3
  71. package/src/lib/tools/src/index.d.ts +0 -0
  72. package/src/lib/tools/src/index.js.map +0 -1
  73. package/src/lib/utils/compiler-plugin-options.js +0 -1
  74. package/src/lib/utils/compiler-plugin-options.js.map +0 -1
  75. package/src/lib/utils/hmr-candidates.js +0 -272
  76. package/src/lib/utils/hmr-candidates.js.map +0 -1
  77. package/vite.config.d.ts +0 -2
  78. package/vite.config.js +0 -22
  79. package/vite.config.js.map +0 -1
@@ -1,982 +1,841 @@
1
- import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
- import { basename, dirname, isAbsolute, join, relative, resolve, } from 'node:path';
3
- import * as vite from 'vite';
4
- import * as compilerCli from '@angular/compiler-cli';
5
- import { createRequire } from 'node:module';
6
- import * as ngCompiler from '@angular/compiler';
7
- import { globSync } from 'tinyglobby';
8
- import { defaultClientConditions, normalizePath, preprocessCSS, } from 'vite';
9
- import { buildOptimizerPlugin } from './angular-build-optimizer-plugin.js';
10
- import { jitPlugin } from './angular-jit-plugin.js';
11
- import { createCompilerPlugin, createRolldownCompilerPlugin, } from './compiler-plugin.js';
12
- import { StyleUrlsResolver, TemplateUrlsResolver, } from './component-resolvers.js';
13
- import { augmentHostWithCaching, augmentHostWithResources, augmentProgramWithVersioning, mergeTransformers, } from './host.js';
14
- import { angularVitestPlugins } from './angular-vitest-plugin.js';
15
- import { createAngularCompilation, createJitResourceTransformer, SourceFileCache, angularFullVersion, } from './utils/devkit.js';
16
- const require = createRequire(import.meta.url);
17
- import { pendingTasksPlugin } from './angular-pending-tasks.plugin.js';
18
- import { liveReloadPlugin } from './live-reload-plugin.js';
19
- import { nxFolderPlugin } from './nx-folder-plugin.js';
20
- import { replaceFiles, } from './plugins/file-replacements.plugin.js';
21
- import { routerPlugin } from './router-plugin.js';
22
- import { createHash } from 'node:crypto';
23
- export var DiagnosticModes;
24
- (function (DiagnosticModes) {
25
- DiagnosticModes[DiagnosticModes["None"] = 0] = "None";
26
- DiagnosticModes[DiagnosticModes["Option"] = 1] = "Option";
27
- DiagnosticModes[DiagnosticModes["Syntactic"] = 2] = "Syntactic";
28
- DiagnosticModes[DiagnosticModes["Semantic"] = 4] = "Semantic";
29
- DiagnosticModes[DiagnosticModes["All"] = 7] = "All";
30
- })(DiagnosticModes || (DiagnosticModes = {}));
1
+ import { angularFullVersion, cjt, createAngularCompilation, sourceFileCache } from "./utils/devkit.js";
2
+ import { getJsTransformConfigKey, isRolldown } from "./utils/rolldown.js";
3
+ import { buildOptimizerPlugin } from "./angular-build-optimizer-plugin.js";
4
+ import { jitPlugin } from "./angular-jit-plugin.js";
5
+ import { createCompilerPlugin, createRolldownCompilerPlugin } from "./compiler-plugin.js";
6
+ import { StyleUrlsResolver, TemplateUrlsResolver } from "./component-resolvers.js";
7
+ import { augmentHostWithCaching, augmentHostWithResources, augmentProgramWithVersioning, mergeTransformers } from "./host.js";
8
+ import { angularVitestPlugins } from "./angular-vitest-plugin.js";
9
+ import { pendingTasksPlugin } from "./angular-pending-tasks.plugin.js";
10
+ import { liveReloadPlugin } from "./live-reload-plugin.js";
11
+ import { nxFolderPlugin } from "./nx-folder-plugin.js";
12
+ import { replaceFiles } from "./plugins/file-replacements.plugin.js";
13
+ import { routerPlugin } from "./router-plugin.js";
14
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
15
+ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
16
+ import * as compilerCli from "@angular/compiler-cli";
17
+ import { createRequire } from "node:module";
18
+ import * as ngCompiler from "@angular/compiler";
19
+ import { globSync } from "tinyglobby";
20
+ import { defaultClientConditions, normalizePath, preprocessCSS } from "vite";
21
+ import { createHash } from "node:crypto";
22
+ //#region packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts
23
+ var require = createRequire(import.meta.url);
24
+ var DiagnosticModes = /* @__PURE__ */ function(DiagnosticModes) {
25
+ DiagnosticModes[DiagnosticModes["None"] = 0] = "None";
26
+ DiagnosticModes[DiagnosticModes["Option"] = 1] = "Option";
27
+ DiagnosticModes[DiagnosticModes["Syntactic"] = 2] = "Syntactic";
28
+ DiagnosticModes[DiagnosticModes["Semantic"] = 4] = "Semantic";
29
+ DiagnosticModes[DiagnosticModes["All"] = 7] = "All";
30
+ return DiagnosticModes;
31
+ }({});
31
32
  /**
32
- * TypeScript file extension regex
33
- * Match .(c or m)ts, .ts extensions with an optional ? for query params
34
- * Ignore .tsx extensions
35
- */
36
- const TS_EXT_REGEX = /\.[cm]?(ts)[^x]?\??/;
37
- const classNames = new Map();
38
- export function angular(options) {
39
- /**
40
- * Normalize plugin options so defaults
41
- * are used for values not provided.
42
- */
43
- const pluginOptions = {
44
- tsconfigGetter: createTsConfigGetter(options?.tsconfig),
45
- workspaceRoot: options?.workspaceRoot ?? process.cwd(),
46
- inlineStylesExtension: options?.inlineStylesExtension ?? 'css',
47
- advanced: {
48
- tsTransformers: {
49
- before: options?.advanced?.tsTransformers?.before ?? [],
50
- after: options?.advanced?.tsTransformers?.after ?? [],
51
- afterDeclarations: options?.advanced?.tsTransformers?.afterDeclarations ?? [],
52
- },
53
- },
54
- supportedBrowsers: options?.supportedBrowsers ?? ['safari 15'],
55
- jit: options?.jit,
56
- include: options?.include ?? [],
57
- additionalContentDirs: options?.additionalContentDirs ?? [],
58
- liveReload: options?.liveReload ?? false,
59
- disableTypeChecking: options?.disableTypeChecking ?? true,
60
- fileReplacements: options?.fileReplacements ?? [],
61
- useAngularCompilationAPI: options?.experimental?.useAngularCompilationAPI ?? false,
62
- };
63
- let resolvedConfig;
64
- // Store config context needed for getTsConfigPath resolution
65
- let tsConfigResolutionContext = null;
66
- const ts = require('typescript');
67
- let builder;
68
- let nextProgram;
69
- // Caches (always rebuild Angular program per user request)
70
- const tsconfigOptionsCache = new Map();
71
- let cachedHost;
72
- let cachedHostKey;
73
- let includeCache = [];
74
- function invalidateFsCaches() {
75
- includeCache = [];
76
- }
77
- function invalidateTsconfigCaches() {
78
- tsconfigOptionsCache.clear();
79
- cachedHost = undefined;
80
- cachedHostKey = undefined;
81
- }
82
- let watchMode = false;
83
- let testWatchMode = isTestWatchMode();
84
- let inlineComponentStyles;
85
- let externalComponentStyles;
86
- const sourceFileCache = new SourceFileCache();
87
- const isTest = process.env['NODE_ENV'] === 'test' || !!process.env['VITEST'];
88
- const isVitestVscode = !!process.env['VITEST_VSCODE'];
89
- const isStackBlitz = !!process.versions['webcontainer'];
90
- const isAstroIntegration = process.env['ANALOG_ASTRO'] === 'true';
91
- const jit = typeof pluginOptions?.jit !== 'undefined' ? pluginOptions.jit : isTest;
92
- let viteServer;
93
- const styleUrlsResolver = new StyleUrlsResolver();
94
- const templateUrlsResolver = new TemplateUrlsResolver();
95
- let outputFile;
96
- const outputFiles = new Map();
97
- const fileEmitter = (file) => {
98
- outputFile?.(file);
99
- return outputFiles.get(normalizePath(file));
100
- };
101
- let initialCompilation = false;
102
- const declarationFiles = [];
103
- const fileTransformMap = new Map();
104
- let styleTransform;
105
- let pendingCompilation;
106
- let compilationLock = Promise.resolve();
107
- function angularPlugin() {
108
- let isProd = false;
109
- if (angularFullVersion < 190000 || isTest) {
110
- pluginOptions.liveReload = false;
111
- }
112
- if (pluginOptions.useAngularCompilationAPI) {
113
- if (angularFullVersion < 200100) {
114
- pluginOptions.useAngularCompilationAPI = false;
115
- console.warn('[@analogjs/vite-plugin-angular]: The Angular Compilation API is only available with Angular v20.1 and later');
116
- }
117
- if (pluginOptions.liveReload) {
118
- pluginOptions.liveReload = false;
119
- console.warn('[@analogjs-vite-plugin-angular]: Live reload is currently not compatible with the Angular Compilation API option');
120
- }
121
- if (pluginOptions.fileReplacements.length) {
122
- pluginOptions.fileReplacements = [];
123
- console.warn('[@analogjs-vite-plugin-angular]: File replacements are currently not compatible with the Angular Compilation API option');
124
- }
125
- }
126
- return {
127
- name: '@analogjs/vite-plugin-angular',
128
- async config(config, { command }) {
129
- watchMode = command === 'serve';
130
- isProd =
131
- config.mode === 'production' ||
132
- process.env['NODE_ENV'] === 'production';
133
- // Store the config context for later resolution in configResolved
134
- tsConfigResolutionContext = {
135
- root: config.root || '.',
136
- isProd,
137
- isLib: !!config?.build?.lib,
138
- };
139
- // Do a preliminary resolution for esbuild plugin (before configResolved)
140
- const preliminaryTsConfigPath = resolveTsConfigPath();
141
- const esbuild = pluginOptions.useAngularCompilationAPI
142
- ? undefined
143
- : (config.esbuild ?? false);
144
- const oxc = pluginOptions.useAngularCompilationAPI
145
- ? undefined
146
- : (config.oxc ?? false);
147
- const defineOptions = {
148
- ngJitMode: 'false',
149
- ngI18nClosureMode: 'false',
150
- ...(watchMode ? {} : { ngDevMode: 'false' }),
151
- };
152
- const rolldownOptions = {
153
- plugins: [
154
- createRolldownCompilerPlugin({
155
- tsconfig: preliminaryTsConfigPath,
156
- sourcemap: !isProd,
157
- advancedOptimizations: isProd,
158
- jit,
159
- incremental: watchMode,
160
- }),
161
- ],
162
- };
163
- const esbuildOptions = {
164
- plugins: [
165
- createCompilerPlugin({
166
- tsconfig: preliminaryTsConfigPath,
167
- sourcemap: !isProd,
168
- advancedOptimizations: isProd,
169
- jit,
170
- incremental: watchMode,
171
- }, isTest, !isAstroIntegration),
172
- ],
173
- define: defineOptions,
174
- };
175
- return {
176
- ...(vite.rolldownVersion ? { oxc } : { esbuild }),
177
- optimizeDeps: {
178
- include: ['rxjs/operators', 'rxjs'],
179
- exclude: ['@angular/platform-server'],
180
- ...(vite.rolldownVersion
181
- ? { rolldownOptions }
182
- : { esbuildOptions }),
183
- },
184
- resolve: {
185
- conditions: [
186
- 'style',
187
- ...(config.resolve?.conditions || defaultClientConditions),
188
- ],
189
- },
190
- };
191
- },
192
- configResolved(config) {
193
- resolvedConfig = config;
194
- if (pluginOptions.useAngularCompilationAPI) {
195
- externalComponentStyles = new Map();
196
- inlineComponentStyles = new Map();
197
- }
198
- if (!jit) {
199
- styleTransform = (code, filename) => preprocessCSS(code, filename, config);
200
- }
201
- if (isTest) {
202
- // set test watch mode
203
- // - vite override from vitest-angular
204
- // - @nx/vite executor set server.watch explicitly to undefined (watch)/null (watch=false)
205
- // - vite config for test.watch variable
206
- // - vitest watch mode detected from the command line
207
- testWatchMode =
208
- !(config.server.watch === null) ||
209
- config.test?.watch === true ||
210
- testWatchMode;
211
- }
212
- },
213
- configureServer(server) {
214
- viteServer = server;
215
- server.watcher.on('add', async () => {
216
- invalidateFsCaches();
217
- await performCompilation(resolvedConfig);
218
- });
219
- server.watcher.on('unlink', async () => {
220
- invalidateFsCaches();
221
- await performCompilation(resolvedConfig);
222
- });
223
- server.watcher.on('change', (file) => {
224
- if (file.includes('tsconfig')) {
225
- invalidateTsconfigCaches();
226
- }
227
- });
228
- },
229
- async buildStart() {
230
- // Defer the first compilation in test mode
231
- if (!isVitestVscode) {
232
- await performCompilation(resolvedConfig);
233
- pendingCompilation = null;
234
- initialCompilation = true;
235
- }
236
- },
237
- async handleHotUpdate(ctx) {
238
- if (TS_EXT_REGEX.test(ctx.file)) {
239
- let [fileId] = ctx.file.split('?');
240
- pendingCompilation = performCompilation(resolvedConfig, [fileId]);
241
- let result;
242
- if (pluginOptions.liveReload) {
243
- await pendingCompilation;
244
- pendingCompilation = null;
245
- result = fileEmitter(fileId);
246
- }
247
- if (pluginOptions.liveReload &&
248
- result?.hmrEligible &&
249
- classNames.get(fileId)) {
250
- const relativeFileId = `${relative(process.cwd(), fileId)}@${classNames.get(fileId)}`;
251
- sendHMRComponentUpdate(ctx.server, relativeFileId);
252
- return ctx.modules.map((mod) => {
253
- if (mod.id === ctx.file) {
254
- return markModuleSelfAccepting(mod);
255
- }
256
- return mod;
257
- });
258
- }
259
- }
260
- if (/\.(html|htm|css|less|sass|scss)$/.test(ctx.file)) {
261
- fileTransformMap.delete(ctx.file.split('?')[0]);
262
- /**
263
- * Check to see if this was a direct request
264
- * for an external resource (styles, html).
265
- */
266
- const isDirect = ctx.modules.find((mod) => ctx.file === mod.file && mod.id?.includes('?direct'));
267
- const isInline = ctx.modules.find((mod) => ctx.file === mod.file && mod.id?.includes('?inline'));
268
- if (isDirect || isInline) {
269
- if (pluginOptions.liveReload && isDirect?.id && isDirect.file) {
270
- const isComponentStyle = isDirect.type === 'css' && isComponentStyleSheet(isDirect.id);
271
- if (isComponentStyle) {
272
- const { encapsulation } = getComponentStyleSheetMeta(isDirect.id);
273
- // Track if the component uses ShadowDOM encapsulation
274
- // Shadow DOM components currently require a full reload.
275
- // Vite's CSS hot replacement does not support shadow root searching.
276
- if (encapsulation !== 'shadow') {
277
- ctx.server.ws.send({
278
- type: 'update',
279
- updates: [
280
- {
281
- type: 'css-update',
282
- timestamp: Date.now(),
283
- path: isDirect.url,
284
- acceptedPath: isDirect.file,
285
- },
286
- ],
287
- });
288
- return ctx.modules
289
- .filter((mod) => {
290
- // Component stylesheets will have 2 modules (*.component.scss and *.component.scss?direct&ngcomp=xyz&e=x)
291
- // We remove the module with the query params to prevent vite double logging the stylesheet name "hmr update *.component.scss, *.component.scss?direct&ngcomp=xyz&e=x"
292
- return mod.file !== ctx.file || mod.id !== isDirect.id;
293
- })
294
- .map((mod) => {
295
- if (mod.file === ctx.file) {
296
- return markModuleSelfAccepting(mod);
297
- }
298
- return mod;
299
- });
300
- }
301
- }
302
- }
303
- return ctx.modules;
304
- }
305
- const mods = [];
306
- const updates = [];
307
- ctx.modules.forEach((mod) => {
308
- mod.importers.forEach((imp) => {
309
- ctx.server.moduleGraph.invalidateModule(imp);
310
- if (pluginOptions.liveReload && classNames.get(imp.id)) {
311
- updates.push(imp.id);
312
- }
313
- else {
314
- mods.push(imp);
315
- }
316
- });
317
- });
318
- pendingCompilation = performCompilation(resolvedConfig, [
319
- ...mods.map((mod) => mod.id),
320
- ...updates,
321
- ]);
322
- if (updates.length > 0) {
323
- await pendingCompilation;
324
- pendingCompilation = null;
325
- updates.forEach((updateId) => {
326
- const impRelativeFileId = `${relative(process.cwd(), updateId)}@${classNames.get(updateId)}`;
327
- sendHMRComponentUpdate(ctx.server, impRelativeFileId);
328
- });
329
- return ctx.modules.map((mod) => {
330
- if (mod.id === ctx.file) {
331
- return markModuleSelfAccepting(mod);
332
- }
333
- return mod;
334
- });
335
- }
336
- return mods;
337
- }
338
- // clear HMR updates with a full reload
339
- classNames.clear();
340
- return ctx.modules;
341
- },
342
- resolveId(id, importer) {
343
- if (jit && id.startsWith('angular:jit:')) {
344
- const path = id.split(';')[1];
345
- return `${normalizePath(resolve(dirname(importer), path))}?${id.includes(':style') ? 'inline' : 'raw'}`;
346
- }
347
- // Map angular external styleUrls to the source file
348
- if (isComponentStyleSheet(id)) {
349
- const componentStyles = externalComponentStyles?.get(getFilenameFromPath(id));
350
- if (componentStyles) {
351
- return componentStyles + new URL(id, 'http://localhost').search;
352
- }
353
- }
354
- return undefined;
355
- },
356
- async load(id) {
357
- // Map angular inline styles to the source text
358
- if (isComponentStyleSheet(id)) {
359
- const componentStyles = inlineComponentStyles?.get(getFilenameFromPath(id));
360
- if (componentStyles) {
361
- return componentStyles;
362
- }
363
- }
364
- return;
365
- },
366
- transform: {
367
- filter: {
368
- id: {
369
- include: [TS_EXT_REGEX],
370
- exclude: [/node_modules/, 'type=script', '@ng/component'],
371
- },
372
- },
373
- async handler(code, id) {
374
- /**
375
- * Check for options.transformFilter
376
- */
377
- if (options?.transformFilter &&
378
- !(options?.transformFilter(code, id) ?? true)) {
379
- return;
380
- }
381
- if (pluginOptions.useAngularCompilationAPI) {
382
- const isAngular = /(Component|Directive|Pipe|Injectable|NgModule)\(/.test(code);
383
- if (!isAngular) {
384
- return;
385
- }
386
- }
387
- /**
388
- * Skip transforming content files
389
- */
390
- if (id.includes('?') && id.includes('analog-content-')) {
391
- return;
392
- }
393
- /**
394
- * Encapsulate component stylesheets that use emulated encapsulation
395
- */
396
- if (pluginOptions.liveReload && isComponentStyleSheet(id)) {
397
- const { encapsulation, componentId } = getComponentStyleSheetMeta(id);
398
- if (encapsulation === 'emulated' && componentId) {
399
- const encapsulated = ngCompiler.encapsulateStyle(code, componentId);
400
- return {
401
- code: encapsulated,
402
- map: null,
403
- };
404
- }
405
- }
406
- if (id.includes('.ts?')) {
407
- // Strip the query string off the ID
408
- // in case of a dynamically loaded file
409
- id = id.replace(/\?(.*)/, '');
410
- }
411
- fileTransformMap.set(id, code);
412
- /**
413
- * Re-analyze on each transform
414
- * for test(Vitest)
415
- */
416
- if (isTest) {
417
- if (isVitestVscode && !initialCompilation) {
418
- // Do full initial compilation
419
- pendingCompilation = performCompilation(resolvedConfig);
420
- initialCompilation = true;
421
- }
422
- const tsMod = viteServer?.moduleGraph.getModuleById(id);
423
- if (tsMod) {
424
- const invalidated = tsMod.lastInvalidationTimestamp;
425
- if (testWatchMode && invalidated) {
426
- pendingCompilation = performCompilation(resolvedConfig, [id]);
427
- }
428
- }
429
- }
430
- const hasComponent = code.includes('@Component');
431
- const templateUrls = hasComponent
432
- ? templateUrlsResolver.resolve(code, id)
433
- : [];
434
- const styleUrls = hasComponent
435
- ? styleUrlsResolver.resolve(code, id)
436
- : [];
437
- if (hasComponent && watchMode) {
438
- for (const urlSet of [...templateUrls, ...styleUrls]) {
439
- // `urlSet` is a string where a relative path is joined with an
440
- // absolute path using the `|` symbol.
441
- // For example: `./app.component.html|/home/projects/analog/src/app/app.component.html`.
442
- const [, absoluteFileUrl] = urlSet.split('|');
443
- this.addWatchFile(absoluteFileUrl);
444
- }
445
- }
446
- if (pendingCompilation) {
447
- await pendingCompilation;
448
- pendingCompilation = null;
449
- }
450
- const typescriptResult = fileEmitter(id);
451
- if (typescriptResult?.warnings &&
452
- typescriptResult?.warnings.length > 0) {
453
- this.warn(`${typescriptResult.warnings.join('\n')}`);
454
- }
455
- if (typescriptResult?.errors && typescriptResult?.errors.length > 0) {
456
- this.error(`${typescriptResult.errors.join('\n')}`);
457
- }
458
- // return fileEmitter
459
- let data = typescriptResult?.content ?? '';
460
- if (jit && data.includes('angular:jit:')) {
461
- data = data.replace(/angular:jit:style:inline;/g, 'virtual:angular:jit:style:inline;');
462
- templateUrls.forEach((templateUrlSet) => {
463
- const [templateFile, resolvedTemplateUrl] = templateUrlSet.split('|');
464
- data = data.replace(`angular:jit:template:file;${templateFile}`, `${resolvedTemplateUrl}?raw`);
465
- });
466
- styleUrls.forEach((styleUrlSet) => {
467
- const [styleFile, resolvedStyleUrl] = styleUrlSet.split('|');
468
- data = data.replace(`angular:jit:style:file;${styleFile}`, `${resolvedStyleUrl}?inline`);
469
- });
470
- }
471
- return {
472
- code: data,
473
- map: null,
474
- };
475
- },
476
- },
477
- closeBundle() {
478
- declarationFiles.forEach(({ declarationFileDir, declarationPath, data }) => {
479
- mkdirSync(declarationFileDir, { recursive: true });
480
- writeFileSync(declarationPath, data, 'utf-8');
481
- });
482
- },
483
- };
484
- }
485
- return [
486
- replaceFiles(pluginOptions.fileReplacements, pluginOptions.workspaceRoot),
487
- angularPlugin(),
488
- pluginOptions.liveReload && liveReloadPlugin({ classNames, fileEmitter }),
489
- ...(isTest && !isStackBlitz ? angularVitestPlugins() : []),
490
- (jit &&
491
- jitPlugin({
492
- inlineStylesExtension: pluginOptions.inlineStylesExtension,
493
- })),
494
- buildOptimizerPlugin({
495
- supportedBrowsers: pluginOptions.supportedBrowsers,
496
- jit,
497
- }),
498
- routerPlugin(),
499
- angularFullVersion < 190004 && pendingTasksPlugin(),
500
- nxFolderPlugin(),
501
- ].filter(Boolean);
502
- function findIncludes() {
503
- const workspaceRoot = normalizePath(resolve(pluginOptions.workspaceRoot));
504
- // Map include patterns to absolute workspace paths
505
- const globs = [
506
- ...pluginOptions.include.map((glob) => `${workspaceRoot}${glob}`),
507
- ];
508
- // Discover TypeScript files using tinyglobby
509
- return globSync(globs, {
510
- dot: true,
511
- absolute: true,
512
- });
513
- }
514
- function createTsConfigGetter(tsconfigOrGetter) {
515
- if (typeof tsconfigOrGetter === 'function') {
516
- return tsconfigOrGetter;
517
- }
518
- return () => tsconfigOrGetter || '';
519
- }
520
- function getTsConfigPath(root, tsconfig, isProd, isTest, isLib) {
521
- if (tsconfig && isAbsolute(tsconfig)) {
522
- if (!existsSync(tsconfig)) {
523
- console.error(`[@analogjs/vite-plugin-angular]: Unable to resolve tsconfig at ${tsconfig}. This causes compilation issues. Check the path or set the "tsconfig" property with an absolute path.`);
524
- }
525
- return tsconfig;
526
- }
527
- let tsconfigFilePath = './tsconfig.app.json';
528
- if (isLib) {
529
- tsconfigFilePath = isProd
530
- ? './tsconfig.lib.prod.json'
531
- : './tsconfig.lib.json';
532
- }
533
- if (isTest) {
534
- tsconfigFilePath = './tsconfig.spec.json';
535
- }
536
- if (tsconfig) {
537
- tsconfigFilePath = tsconfig;
538
- }
539
- const resolvedPath = resolve(root, tsconfigFilePath);
540
- if (!existsSync(resolvedPath)) {
541
- console.error(`[@analogjs/vite-plugin-angular]: Unable to resolve tsconfig at ${resolvedPath}. This causes compilation issues. Check the path or set the "tsconfig" property with an absolute path.`);
542
- }
543
- return resolvedPath;
544
- }
545
- function resolveTsConfigPath() {
546
- const tsconfigValue = pluginOptions.tsconfigGetter();
547
- return getTsConfigPath(tsConfigResolutionContext.root, tsconfigValue, tsConfigResolutionContext.isProd, isTest, tsConfigResolutionContext.isLib);
548
- }
549
- async function performAngularCompilation(config) {
550
- const compilation = await createAngularCompilation(!!pluginOptions.jit, false);
551
- const resolvedTsConfigPath = resolveTsConfigPath();
552
- const compilationResult = await compilation.initialize(resolvedTsConfigPath, {
553
- async transformStylesheet(data, containingFile, resourceFile, order, className) {
554
- if (pluginOptions.liveReload) {
555
- const id = createHash('sha256')
556
- .update(containingFile)
557
- .update(className)
558
- .update(String(order))
559
- .update(data)
560
- .digest('hex');
561
- const filename = id + '.' + pluginOptions.inlineStylesExtension;
562
- inlineComponentStyles.set(filename, data);
563
- return filename;
564
- }
565
- const filename = resourceFile ??
566
- containingFile.replace('.ts', `.${options?.inlineStylesExtension}`);
567
- let stylesheetResult;
568
- try {
569
- stylesheetResult = await preprocessCSS(data, `${filename}?direct`, resolvedConfig);
570
- }
571
- catch (e) {
572
- console.error(`${e}`);
573
- }
574
- return stylesheetResult?.code || '';
575
- },
576
- processWebWorker(workerFile, containingFile) {
577
- return '';
578
- },
579
- }, (tsCompilerOptions) => {
580
- if (pluginOptions.liveReload && watchMode) {
581
- tsCompilerOptions['_enableHmr'] = true;
582
- tsCompilerOptions['externalRuntimeStyles'] = true;
583
- // Workaround for https://github.com/angular/angular/issues/59310
584
- // Force extra instructions to be generated for HMR w/defer
585
- tsCompilerOptions['supportTestBed'] = true;
586
- }
587
- if (tsCompilerOptions.compilationMode === 'partial') {
588
- // These options can't be false in partial mode
589
- tsCompilerOptions['supportTestBed'] = true;
590
- tsCompilerOptions['supportJitMode'] = true;
591
- }
592
- if (!isTest && config.build?.lib) {
593
- tsCompilerOptions['declaration'] = true;
594
- tsCompilerOptions['declarationMap'] = watchMode;
595
- tsCompilerOptions['inlineSources'] = true;
596
- }
597
- if (isTest) {
598
- // Allow `TestBed.overrideXXX()` APIs.
599
- tsCompilerOptions['supportTestBed'] = true;
600
- }
601
- return tsCompilerOptions;
602
- });
603
- compilationResult.externalStylesheets?.forEach((value, key) => {
604
- externalComponentStyles?.set(`${value}.css`, key);
605
- });
606
- const diagnostics = await compilation.diagnoseFiles(pluginOptions.disableTypeChecking
607
- ? DiagnosticModes.All & ~DiagnosticModes.Semantic
608
- : DiagnosticModes.All);
609
- const errors = diagnostics.errors?.length ? diagnostics.errors : [];
610
- const warnings = diagnostics.warnings?.length ? diagnostics.warnings : [];
611
- for (const file of await compilation.emitAffectedFiles()) {
612
- outputFiles.set(file.filename, {
613
- content: file.contents,
614
- dependencies: [],
615
- errors: errors.map((error) => error.text || ''),
616
- warnings: warnings.map((warning) => warning.text || ''),
617
- });
618
- }
619
- compilation.close?.();
620
- }
621
- async function performCompilation(config, ids) {
622
- let resolve;
623
- const previousLock = compilationLock;
624
- compilationLock = new Promise((r) => {
625
- resolve = r;
626
- });
627
- try {
628
- await previousLock;
629
- await _doPerformCompilation(config, ids);
630
- }
631
- finally {
632
- resolve();
633
- }
634
- }
635
- /**
636
- * This method share mutable state and performs the actual compilation work.
637
- * It should not be called concurrently. Use `performCompilation` which wraps this method in a lock to ensure only one compilation runs at a time.
638
- */
639
- async function _doPerformCompilation(config, ids) {
640
- if (pluginOptions.useAngularCompilationAPI) {
641
- await performAngularCompilation(config);
642
- return;
643
- }
644
- const isProd = config.mode === 'production';
645
- const modifiedFiles = new Set(ids ?? []);
646
- sourceFileCache.invalidate(modifiedFiles);
647
- if (ids?.length) {
648
- for (const id of ids || []) {
649
- fileTransformMap.delete(id);
650
- }
651
- }
652
- // Cached include discovery (invalidated only on FS events)
653
- if (pluginOptions.include.length > 0 && includeCache.length === 0) {
654
- includeCache = findIncludes();
655
- }
656
- const resolvedTsConfigPath = resolveTsConfigPath();
657
- const tsconfigKey = [
658
- resolvedTsConfigPath,
659
- isProd ? 'prod' : 'dev',
660
- isTest ? 'test' : 'app',
661
- config.build?.lib ? 'lib' : 'nolib',
662
- ].join('|');
663
- let cached = tsconfigOptionsCache.get(tsconfigKey);
664
- if (!cached) {
665
- const read = compilerCli.readConfiguration(resolvedTsConfigPath, {
666
- suppressOutputPathCheck: true,
667
- outDir: undefined,
668
- sourceMap: false,
669
- inlineSourceMap: !isProd,
670
- inlineSources: !isProd,
671
- declaration: false,
672
- declarationMap: false,
673
- allowEmptyCodegenFiles: false,
674
- annotationsAs: 'decorators',
675
- enableResourceInlining: false,
676
- noEmitOnError: false,
677
- mapRoot: undefined,
678
- sourceRoot: undefined,
679
- supportTestBed: false,
680
- supportJitMode: false,
681
- });
682
- cached = { options: read.options, rootNames: read.rootNames };
683
- tsconfigOptionsCache.set(tsconfigKey, cached);
684
- }
685
- // Clone options before mutation (preserve cache purity)
686
- const tsCompilerOptions = { ...cached.options };
687
- let rootNames = [...cached.rootNames];
688
- if (pluginOptions.liveReload && watchMode) {
689
- tsCompilerOptions['_enableHmr'] = true;
690
- tsCompilerOptions['externalRuntimeStyles'] = true;
691
- // Workaround for https://github.com/angular/angular/issues/59310
692
- // Force extra instructions to be generated for HMR w/defer
693
- tsCompilerOptions['supportTestBed'] = true;
694
- }
695
- if (tsCompilerOptions['compilationMode'] === 'partial') {
696
- // These options can't be false in partial mode
697
- tsCompilerOptions['supportTestBed'] = true;
698
- tsCompilerOptions['supportJitMode'] = true;
699
- }
700
- if (!isTest && config.build?.lib) {
701
- tsCompilerOptions['declaration'] = true;
702
- tsCompilerOptions['declarationMap'] = watchMode;
703
- tsCompilerOptions['inlineSources'] = true;
704
- }
705
- if (isTest) {
706
- // Allow `TestBed.overrideXXX()` APIs.
707
- tsCompilerOptions['supportTestBed'] = true;
708
- }
709
- const replacements = pluginOptions.fileReplacements.map((rp) => join(pluginOptions.workspaceRoot, rp.ssr || rp.with));
710
- // Merge + dedupe root names
711
- rootNames = [...new Set([...rootNames, ...includeCache, ...replacements])];
712
- const hostKey = JSON.stringify(tsCompilerOptions);
713
- let host;
714
- if (cachedHost && cachedHostKey === hostKey) {
715
- host = cachedHost;
716
- }
717
- else {
718
- host = ts.createIncrementalCompilerHost(tsCompilerOptions, {
719
- ...ts.sys,
720
- readFile(path, encoding) {
721
- if (fileTransformMap.has(path)) {
722
- return fileTransformMap.get(path);
723
- }
724
- const file = ts.sys.readFile.call(null, path, encoding);
725
- if (file) {
726
- fileTransformMap.set(path, file);
727
- }
728
- return file;
729
- },
730
- });
731
- cachedHost = host;
732
- cachedHostKey = hostKey;
733
- // Only store cache if in watch mode
734
- if (watchMode) {
735
- augmentHostWithCaching(host, sourceFileCache);
736
- }
737
- }
738
- if (!jit) {
739
- inlineComponentStyles = tsCompilerOptions['externalRuntimeStyles']
740
- ? new Map()
741
- : undefined;
742
- externalComponentStyles = tsCompilerOptions['externalRuntimeStyles']
743
- ? new Map()
744
- : undefined;
745
- augmentHostWithResources(host, styleTransform, {
746
- inlineStylesExtension: pluginOptions.inlineStylesExtension,
747
- isProd,
748
- inlineComponentStyles,
749
- externalComponentStyles,
750
- sourceFileCache,
751
- });
752
- }
753
- /**
754
- * Creates a new NgtscProgram to analyze/re-analyze
755
- * the source files and create a file emitter.
756
- * This is shared between an initial build and a hot update.
757
- */
758
- let typeScriptProgram;
759
- let angularCompiler;
760
- const oldBuilder = builder ?? ts.readBuilderProgram(tsCompilerOptions, host);
761
- if (!jit) {
762
- // Create the Angular specific program that contains the Angular compiler
763
- const angularProgram = new compilerCli.NgtscProgram(rootNames, tsCompilerOptions, host, nextProgram);
764
- angularCompiler = angularProgram.compiler;
765
- typeScriptProgram = angularProgram.compiler.getCurrentProgram();
766
- augmentProgramWithVersioning(typeScriptProgram);
767
- builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(typeScriptProgram, host, oldBuilder);
768
- nextProgram = angularProgram;
769
- }
770
- else {
771
- builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, tsCompilerOptions, host, oldBuilder);
772
- typeScriptProgram = builder.getProgram();
773
- }
774
- if (!watchMode) {
775
- // When not in watch mode, the startup cost of the incremental analysis can be avoided by
776
- // using an abstract builder that only wraps a TypeScript program.
777
- builder = ts.createAbstractBuilder(typeScriptProgram, host, oldBuilder);
778
- }
779
- if (angularCompiler) {
780
- await angularCompiler.analyzeAsync();
781
- }
782
- const beforeTransformers = jit
783
- ? [
784
- compilerCli.constructorParametersDownlevelTransform(builder.getProgram()),
785
- createJitResourceTransformer(() => builder.getProgram().getTypeChecker()),
786
- ]
787
- : [];
788
- const transformers = mergeTransformers({ before: beforeTransformers }, jit ? {} : angularCompiler.prepareEmit().transformers);
789
- const fileMetadata = getFileMetadata(builder, angularCompiler, pluginOptions.liveReload, pluginOptions.disableTypeChecking);
790
- const writeFileCallback = (_filename, content, _a, _b, sourceFiles) => {
791
- if (!sourceFiles?.length) {
792
- return;
793
- }
794
- const filename = normalizePath(sourceFiles[0].fileName);
795
- if (filename.includes('ngtypecheck.ts') || filename.includes('.d.')) {
796
- return;
797
- }
798
- const metadata = watchMode ? fileMetadata(filename) : {};
799
- outputFiles.set(filename, {
800
- content,
801
- dependencies: [],
802
- errors: metadata.errors,
803
- warnings: metadata.warnings,
804
- hmrUpdateCode: metadata.hmrUpdateCode,
805
- hmrEligible: metadata.hmrEligible,
806
- });
807
- };
808
- const writeOutputFile = (id) => {
809
- const sourceFile = builder.getSourceFile(id);
810
- if (!sourceFile) {
811
- return;
812
- }
813
- let content = '';
814
- builder.emit(sourceFile, (filename, data) => {
815
- if (/\.[cm]?js$/.test(filename)) {
816
- content = data;
817
- }
818
- if (!watchMode &&
819
- !isTest &&
820
- /\.d\.ts/.test(filename) &&
821
- !filename.includes('.ngtypecheck.')) {
822
- // output to library root instead /src
823
- const declarationPath = resolve(config.root, config.build.outDir, relative(config.root, filename)).replace('/src/', '/');
824
- const declarationFileDir = declarationPath
825
- .replace(basename(filename), '')
826
- .replace('/src/', '/');
827
- declarationFiles.push({
828
- declarationFileDir,
829
- declarationPath,
830
- data,
831
- });
832
- }
833
- }, undefined /* cancellationToken */, undefined /* emitOnlyDtsFiles */, transformers);
834
- writeFileCallback(id, content, false, undefined, [sourceFile]);
835
- if (angularCompiler) {
836
- angularCompiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
837
- }
838
- };
839
- if (watchMode) {
840
- if (ids && ids.length > 0) {
841
- ids.forEach((id) => writeOutputFile(id));
842
- }
843
- else {
844
- /**
845
- * Only block the server from starting up
846
- * during testing.
847
- */
848
- if (isTest) {
849
- // TypeScript will loop until there are no more affected files in the program
850
- while (builder.emitNextAffectedFile(writeFileCallback, undefined, undefined, transformers)) {
851
- /* empty */
852
- }
853
- }
854
- }
855
- }
856
- if (!isTest) {
857
- /**
858
- * Perf: Output files on demand so the dev server
859
- * isn't blocked when emitting files.
860
- */
861
- outputFile = writeOutputFile;
862
- }
863
- }
33
+ * TypeScript file extension regex
34
+ * Match .(c or m)ts, .ts extensions with an optional ? for query params
35
+ * Ignore .tsx extensions
36
+ */
37
+ var TS_EXT_REGEX = /\.[cm]?(ts)[^x]?\??/;
38
+ var classNames = /* @__PURE__ */ new Map();
39
+ /**
40
+ * Builds a resolved stylePreprocessor function from plugin options.
41
+ * If `tailwindCss` is provided, creates an auto-reference injector.
42
+ * If `stylePreprocessor` is also provided, chains them (tailwind first, then user).
43
+ */
44
+ function buildStylePreprocessor(options) {
45
+ const userPreprocessor = options?.stylePreprocessor;
46
+ const tw = options?.tailwindCss;
47
+ if (!tw && !userPreprocessor) return;
48
+ let tailwindPreprocessor;
49
+ if (tw) {
50
+ const rootStylesheet = tw.rootStylesheet;
51
+ const prefixes = tw.prefixes;
52
+ tailwindPreprocessor = (code, filename) => {
53
+ if (code.includes("@reference") || code.includes("@import \"tailwindcss\"") || code.includes("@import 'tailwindcss'")) return code;
54
+ if (!(prefixes ? prefixes.some((prefix) => code.includes(prefix)) : code.includes("@apply"))) return code;
55
+ return `@reference "${relative(dirname(filename), rootStylesheet)}";\n${code}`;
56
+ };
57
+ }
58
+ if (tailwindPreprocessor && userPreprocessor) return (code, filename) => {
59
+ return userPreprocessor(tailwindPreprocessor(code, filename), filename);
60
+ };
61
+ return tailwindPreprocessor ?? userPreprocessor;
62
+ }
63
+ function angular(options) {
64
+ /**
65
+ * Normalize plugin options so defaults
66
+ * are used for values not provided.
67
+ */
68
+ const pluginOptions = {
69
+ tsconfigGetter: createTsConfigGetter(options?.tsconfig),
70
+ workspaceRoot: options?.workspaceRoot ?? process.cwd(),
71
+ inlineStylesExtension: options?.inlineStylesExtension ?? "css",
72
+ advanced: { tsTransformers: {
73
+ before: options?.advanced?.tsTransformers?.before ?? [],
74
+ after: options?.advanced?.tsTransformers?.after ?? [],
75
+ afterDeclarations: options?.advanced?.tsTransformers?.afterDeclarations ?? []
76
+ } },
77
+ supportedBrowsers: options?.supportedBrowsers ?? ["safari 15"],
78
+ jit: options?.jit,
79
+ include: options?.include ?? [],
80
+ additionalContentDirs: options?.additionalContentDirs ?? [],
81
+ liveReload: options?.liveReload ?? false,
82
+ disableTypeChecking: options?.disableTypeChecking ?? true,
83
+ fileReplacements: options?.fileReplacements ?? [],
84
+ useAngularCompilationAPI: options?.experimental?.useAngularCompilationAPI ?? false,
85
+ stylePreprocessor: buildStylePreprocessor(options)
86
+ };
87
+ let resolvedConfig;
88
+ let tsConfigResolutionContext = null;
89
+ const ts = require("typescript");
90
+ let builder;
91
+ let nextProgram;
92
+ const tsconfigOptionsCache = /* @__PURE__ */ new Map();
93
+ let cachedHost;
94
+ let cachedHostKey;
95
+ let includeCache = [];
96
+ function invalidateFsCaches() {
97
+ includeCache = [];
98
+ }
99
+ function invalidateTsconfigCaches() {
100
+ tsconfigOptionsCache.clear();
101
+ cachedHost = void 0;
102
+ cachedHostKey = void 0;
103
+ }
104
+ let watchMode = false;
105
+ let testWatchMode = isTestWatchMode();
106
+ let inlineComponentStyles;
107
+ let externalComponentStyles;
108
+ const sourceFileCache$1 = new sourceFileCache();
109
+ const isTest = process.env.NODE_ENV === "test" || !!process.env["VITEST"];
110
+ const isVitestVscode = !!process.env["VITEST_VSCODE"];
111
+ const isStackBlitz = !!process.versions["webcontainer"];
112
+ const isAstroIntegration = process.env["ANALOG_ASTRO"] === "true";
113
+ const jit = typeof pluginOptions?.jit !== "undefined" ? pluginOptions.jit : isTest;
114
+ let viteServer;
115
+ const styleUrlsResolver = new StyleUrlsResolver();
116
+ const templateUrlsResolver = new TemplateUrlsResolver();
117
+ let outputFile;
118
+ const outputFiles = /* @__PURE__ */ new Map();
119
+ const fileEmitter = (file) => {
120
+ outputFile?.(file);
121
+ return outputFiles.get(normalizePath(file));
122
+ };
123
+ let initialCompilation = false;
124
+ const declarationFiles = [];
125
+ const fileTransformMap = /* @__PURE__ */ new Map();
126
+ let styleTransform;
127
+ let pendingCompilation;
128
+ let compilationLock = Promise.resolve();
129
+ let angularCompilation;
130
+ function angularPlugin() {
131
+ let isProd = false;
132
+ if (angularFullVersion < 19e4 || isTest) pluginOptions.liveReload = false;
133
+ if (pluginOptions.useAngularCompilationAPI) {
134
+ if (angularFullVersion < 200100) {
135
+ pluginOptions.useAngularCompilationAPI = false;
136
+ console.warn("[@analogjs/vite-plugin-angular]: The Angular Compilation API is only available with Angular v20.1 and later");
137
+ }
138
+ }
139
+ return {
140
+ name: "@analogjs/vite-plugin-angular",
141
+ async config(config, { command }) {
142
+ watchMode = command === "serve";
143
+ isProd = config.mode === "production" || process.env.NODE_ENV === "production";
144
+ tsConfigResolutionContext = {
145
+ root: config.root || ".",
146
+ isProd,
147
+ isLib: !!config?.build?.lib
148
+ };
149
+ const preliminaryTsConfigPath = resolveTsConfigPath();
150
+ const esbuild = pluginOptions.useAngularCompilationAPI ? void 0 : config.esbuild ?? false;
151
+ const oxc = pluginOptions.useAngularCompilationAPI ? void 0 : config.oxc ?? false;
152
+ const defineOptions = {
153
+ ngJitMode: "false",
154
+ ngI18nClosureMode: "false",
155
+ ...watchMode ? {} : { ngDevMode: "false" }
156
+ };
157
+ const useRolldown = isRolldown();
158
+ const jsTransformConfigKey = getJsTransformConfigKey();
159
+ const jsTransformConfigValue = jsTransformConfigKey === "oxc" ? oxc : esbuild;
160
+ const rolldownOptions = { plugins: [createRolldownCompilerPlugin({
161
+ tsconfig: preliminaryTsConfigPath,
162
+ sourcemap: !isProd,
163
+ advancedOptimizations: isProd,
164
+ jit,
165
+ incremental: watchMode
166
+ }, !isAstroIntegration)] };
167
+ const esbuildOptions = {
168
+ plugins: [createCompilerPlugin({
169
+ tsconfig: preliminaryTsConfigPath,
170
+ sourcemap: !isProd,
171
+ advancedOptimizations: isProd,
172
+ jit,
173
+ incremental: watchMode
174
+ }, isTest, !isAstroIntegration)],
175
+ define: defineOptions
176
+ };
177
+ return {
178
+ [jsTransformConfigKey]: jsTransformConfigValue,
179
+ optimizeDeps: {
180
+ include: ["rxjs/operators", "rxjs"],
181
+ exclude: ["@angular/platform-server"],
182
+ ...useRolldown ? { rolldownOptions } : { esbuildOptions }
183
+ },
184
+ resolve: { conditions: ["style", ...config.resolve?.conditions || defaultClientConditions] }
185
+ };
186
+ },
187
+ configResolved(config) {
188
+ resolvedConfig = config;
189
+ if (pluginOptions.useAngularCompilationAPI) {
190
+ externalComponentStyles = /* @__PURE__ */ new Map();
191
+ inlineComponentStyles = /* @__PURE__ */ new Map();
192
+ }
193
+ if (!jit) styleTransform = (code, filename) => preprocessCSS(code, filename, config);
194
+ if (isTest) testWatchMode = !(config.server.watch === null) || config.test?.watch === true || testWatchMode;
195
+ },
196
+ configureServer(server) {
197
+ viteServer = server;
198
+ const invalidateCompilationOnFsChange = createFsWatcherCacheInvalidator(invalidateFsCaches, invalidateTsconfigCaches, () => performCompilation(resolvedConfig));
199
+ server.watcher.on("add", invalidateCompilationOnFsChange);
200
+ server.watcher.on("unlink", invalidateCompilationOnFsChange);
201
+ server.watcher.on("change", (file) => {
202
+ if (file.includes("tsconfig")) invalidateTsconfigCaches();
203
+ });
204
+ },
205
+ async buildStart() {
206
+ if (!isVitestVscode) {
207
+ await performCompilation(resolvedConfig);
208
+ pendingCompilation = null;
209
+ initialCompilation = true;
210
+ }
211
+ },
212
+ async handleHotUpdate(ctx) {
213
+ if (TS_EXT_REGEX.test(ctx.file)) {
214
+ const [fileId] = ctx.file.split("?");
215
+ pendingCompilation = performCompilation(resolvedConfig, [fileId]);
216
+ let result;
217
+ if (pluginOptions.liveReload) {
218
+ await pendingCompilation;
219
+ pendingCompilation = null;
220
+ result = fileEmitter(fileId);
221
+ }
222
+ if (pluginOptions.liveReload && result?.hmrEligible && classNames.get(fileId)) {
223
+ const relativeFileId = `${normalizePath(relative(process.cwd(), fileId))}@${classNames.get(fileId)}`;
224
+ sendHMRComponentUpdate(ctx.server, relativeFileId);
225
+ return ctx.modules.map((mod) => {
226
+ if (mod.id === ctx.file) return markModuleSelfAccepting(mod);
227
+ return mod;
228
+ });
229
+ }
230
+ }
231
+ if (/\.(html|htm|css|less|sass|scss)$/.test(ctx.file)) {
232
+ fileTransformMap.delete(ctx.file.split("?")[0]);
233
+ /**
234
+ * Check to see if this was a direct request
235
+ * for an external resource (styles, html).
236
+ */
237
+ const isDirect = ctx.modules.find((mod) => ctx.file === mod.file && mod.id?.includes("?direct"));
238
+ const isInline = ctx.modules.find((mod) => ctx.file === mod.file && mod.id?.includes("?inline"));
239
+ if (isDirect || isInline) {
240
+ if (pluginOptions.liveReload && isDirect?.id && isDirect.file) {
241
+ if (isDirect.type === "css" && isComponentStyleSheet(isDirect.id)) {
242
+ const { encapsulation } = getComponentStyleSheetMeta(isDirect.id);
243
+ if (encapsulation !== "shadow") {
244
+ ctx.server.ws.send({
245
+ type: "update",
246
+ updates: [{
247
+ type: "css-update",
248
+ timestamp: Date.now(),
249
+ path: isDirect.url,
250
+ acceptedPath: isDirect.file
251
+ }]
252
+ });
253
+ return ctx.modules.filter((mod) => {
254
+ return mod.file !== ctx.file || mod.id !== isDirect.id;
255
+ }).map((mod) => {
256
+ if (mod.file === ctx.file) return markModuleSelfAccepting(mod);
257
+ return mod;
258
+ });
259
+ }
260
+ }
261
+ }
262
+ return ctx.modules;
263
+ }
264
+ const mods = [];
265
+ const updates = [];
266
+ ctx.modules.forEach((mod) => {
267
+ mod.importers.forEach((imp) => {
268
+ ctx.server.moduleGraph.invalidateModule(imp);
269
+ if (pluginOptions.liveReload && classNames.get(imp.id)) updates.push(imp.id);
270
+ else mods.push(imp);
271
+ });
272
+ });
273
+ pendingCompilation = performCompilation(resolvedConfig, [...mods.map((mod) => mod.id), ...updates]);
274
+ if (updates.length > 0) {
275
+ await pendingCompilation;
276
+ pendingCompilation = null;
277
+ updates.forEach((updateId) => {
278
+ const impRelativeFileId = `${normalizePath(relative(process.cwd(), updateId))}@${classNames.get(updateId)}`;
279
+ sendHMRComponentUpdate(ctx.server, impRelativeFileId);
280
+ });
281
+ return ctx.modules.map((mod) => {
282
+ if (mod.id === ctx.file) return markModuleSelfAccepting(mod);
283
+ return mod;
284
+ });
285
+ }
286
+ return mods;
287
+ }
288
+ classNames.clear();
289
+ return ctx.modules;
290
+ },
291
+ resolveId(id, importer) {
292
+ if (jit && id.startsWith("angular:jit:")) {
293
+ const path = id.split(";")[1];
294
+ return `${normalizePath(resolve(dirname(importer), path))}?${id.includes(":style") ? "inline" : "raw"}`;
295
+ }
296
+ if (isComponentStyleSheet(id)) {
297
+ const componentStyles = externalComponentStyles?.get(getFilenameFromPath(id));
298
+ if (componentStyles) return componentStyles + new URL(id, "http://localhost").search;
299
+ }
300
+ },
301
+ async load(id) {
302
+ if (isComponentStyleSheet(id)) {
303
+ const componentStyles = inlineComponentStyles?.get(getFilenameFromPath(id));
304
+ if (componentStyles) return componentStyles;
305
+ }
306
+ },
307
+ transform: {
308
+ filter: { id: {
309
+ include: [TS_EXT_REGEX],
310
+ exclude: [
311
+ /node_modules/,
312
+ "type=script",
313
+ "@ng/component"
314
+ ]
315
+ } },
316
+ async handler(code, id) {
317
+ /**
318
+ * Check for options.transformFilter
319
+ */
320
+ if (options?.transformFilter && !(options?.transformFilter(code, id) ?? true)) return;
321
+ if (pluginOptions.useAngularCompilationAPI) {
322
+ if (!/(Component|Directive|Pipe|Injectable|NgModule)\(/.test(code)) return;
323
+ }
324
+ /**
325
+ * Skip transforming content files
326
+ */
327
+ if (id.includes("?") && id.includes("analog-content-")) return;
328
+ /**
329
+ * Encapsulate component stylesheets that use emulated encapsulation
330
+ */
331
+ if (pluginOptions.liveReload && isComponentStyleSheet(id)) {
332
+ const { encapsulation, componentId } = getComponentStyleSheetMeta(id);
333
+ if (encapsulation === "emulated" && componentId) return {
334
+ code: ngCompiler.encapsulateStyle(code, componentId),
335
+ map: null
336
+ };
337
+ }
338
+ if (id.includes(".ts?")) id = id.replace(/\?(.*)/, "");
339
+ fileTransformMap.set(id, code);
340
+ /**
341
+ * Re-analyze on each transform
342
+ * for test(Vitest)
343
+ */
344
+ if (isTest) {
345
+ if (isVitestVscode && !initialCompilation) {
346
+ pendingCompilation = performCompilation(resolvedConfig);
347
+ initialCompilation = true;
348
+ }
349
+ const tsMod = viteServer?.moduleGraph.getModuleById(id);
350
+ if (tsMod) {
351
+ const invalidated = tsMod.lastInvalidationTimestamp;
352
+ if (testWatchMode && invalidated) pendingCompilation = performCompilation(resolvedConfig, [id]);
353
+ }
354
+ }
355
+ const hasComponent = code.includes("@Component");
356
+ const templateUrls = hasComponent ? templateUrlsResolver.resolve(code, id) : [];
357
+ const styleUrls = hasComponent ? styleUrlsResolver.resolve(code, id) : [];
358
+ if (hasComponent && watchMode) for (const urlSet of [...templateUrls, ...styleUrls]) {
359
+ const [, absoluteFileUrl] = urlSet.split("|");
360
+ this.addWatchFile(absoluteFileUrl);
361
+ }
362
+ if (pendingCompilation) {
363
+ await pendingCompilation;
364
+ pendingCompilation = null;
365
+ }
366
+ const typescriptResult = fileEmitter(id);
367
+ if (typescriptResult?.warnings && typescriptResult?.warnings.length > 0) this.warn(`${typescriptResult.warnings.join("\n")}`);
368
+ if (typescriptResult?.errors && typescriptResult?.errors.length > 0) this.error(`${typescriptResult.errors.join("\n")}`);
369
+ let data = typescriptResult?.content ?? "";
370
+ if (jit && data.includes("angular:jit:")) {
371
+ data = data.replace(/angular:jit:style:inline;/g, "virtual:angular:jit:style:inline;");
372
+ templateUrls.forEach((templateUrlSet) => {
373
+ const [templateFile, resolvedTemplateUrl] = templateUrlSet.split("|");
374
+ data = data.replace(`angular:jit:template:file;${templateFile}`, `${resolvedTemplateUrl}?raw`);
375
+ });
376
+ styleUrls.forEach((styleUrlSet) => {
377
+ const [styleFile, resolvedStyleUrl] = styleUrlSet.split("|");
378
+ data = data.replace(`angular:jit:style:file;${styleFile}`, `${resolvedStyleUrl}?inline`);
379
+ });
380
+ }
381
+ return {
382
+ code: data,
383
+ map: null
384
+ };
385
+ }
386
+ },
387
+ closeBundle() {
388
+ declarationFiles.forEach(({ declarationFileDir, declarationPath, data }) => {
389
+ mkdirSync(declarationFileDir, { recursive: true });
390
+ writeFileSync(declarationPath, data, "utf-8");
391
+ });
392
+ angularCompilation?.close?.();
393
+ angularCompilation = void 0;
394
+ }
395
+ };
396
+ }
397
+ return [
398
+ replaceFiles(pluginOptions.fileReplacements, pluginOptions.workspaceRoot),
399
+ angularPlugin(),
400
+ pluginOptions.liveReload && liveReloadPlugin({
401
+ classNames,
402
+ fileEmitter
403
+ }),
404
+ ...isTest && !isStackBlitz ? angularVitestPlugins() : [],
405
+ jit && jitPlugin({ inlineStylesExtension: pluginOptions.inlineStylesExtension }),
406
+ buildOptimizerPlugin({
407
+ supportedBrowsers: pluginOptions.supportedBrowsers,
408
+ jit
409
+ }),
410
+ routerPlugin(),
411
+ angularFullVersion < 190004 && pendingTasksPlugin(),
412
+ nxFolderPlugin()
413
+ ].filter(Boolean);
414
+ function findIncludes() {
415
+ const workspaceRoot = normalizePath(resolve(pluginOptions.workspaceRoot));
416
+ return globSync([...pluginOptions.include.map((glob) => `${workspaceRoot}${glob}`)], {
417
+ dot: true,
418
+ absolute: true
419
+ });
420
+ }
421
+ function createTsConfigGetter(tsconfigOrGetter) {
422
+ if (typeof tsconfigOrGetter === "function") return tsconfigOrGetter;
423
+ return () => tsconfigOrGetter || "";
424
+ }
425
+ function getTsConfigPath(root, tsconfig, isProd, isTest, isLib) {
426
+ if (tsconfig && isAbsolute(tsconfig)) {
427
+ if (!existsSync(tsconfig)) console.error(`[@analogjs/vite-plugin-angular]: Unable to resolve tsconfig at ${tsconfig}. This causes compilation issues. Check the path or set the "tsconfig" property with an absolute path.`);
428
+ return tsconfig;
429
+ }
430
+ let tsconfigFilePath = "./tsconfig.app.json";
431
+ if (isLib) tsconfigFilePath = isProd ? "./tsconfig.lib.prod.json" : "./tsconfig.lib.json";
432
+ if (isTest) tsconfigFilePath = "./tsconfig.spec.json";
433
+ if (tsconfig) tsconfigFilePath = tsconfig;
434
+ const resolvedPath = resolve(root, tsconfigFilePath);
435
+ if (!existsSync(resolvedPath)) console.error(`[@analogjs/vite-plugin-angular]: Unable to resolve tsconfig at ${resolvedPath}. This causes compilation issues. Check the path or set the "tsconfig" property with an absolute path.`);
436
+ return resolvedPath;
437
+ }
438
+ function resolveTsConfigPath() {
439
+ const tsconfigValue = pluginOptions.tsconfigGetter();
440
+ return getTsConfigPath(tsConfigResolutionContext.root, tsconfigValue, tsConfigResolutionContext.isProd, isTest, tsConfigResolutionContext.isLib);
441
+ }
442
+ /**
443
+ * Perform compilation using Angular's private Compilation API.
444
+ *
445
+ * Key differences from the standard `performCompilation` path:
446
+ * 1. The compilation instance is reused across rebuilds (nullish-coalescing
447
+ * assignment below) so Angular retains prior state and can diff it to
448
+ * produce `templateUpdates` for HMR.
449
+ * 2. `ids` (modified files) are forwarded to both the source-file cache and
450
+ * `angularCompilation.update()` so that incremental re-analysis is
451
+ * scoped to what actually changed.
452
+ * 3. `fileReplacements` are converted and passed into Angular's host via
453
+ * `toAngularCompilationFileReplacements`.
454
+ * 4. `templateUpdates` from the compilation result are mapped back to
455
+ * file-level HMR metadata (`hmrUpdateCode`, `hmrEligible`, `classNames`).
456
+ */
457
+ async function performAngularCompilation(config, ids) {
458
+ angularCompilation ??= await createAngularCompilation(!!pluginOptions.jit, false);
459
+ const modifiedFiles = ids?.length ? new Set(ids.map((file) => normalizePath(file))) : void 0;
460
+ if (modifiedFiles?.size) sourceFileCache$1.invalidate(modifiedFiles);
461
+ if (modifiedFiles?.size && angularCompilation.update) await angularCompilation.update(modifiedFiles);
462
+ const resolvedTsConfigPath = resolveTsConfigPath();
463
+ const compilationResult = await angularCompilation.initialize(resolvedTsConfigPath, {
464
+ fileReplacements: toAngularCompilationFileReplacements(pluginOptions.fileReplacements, pluginOptions.workspaceRoot),
465
+ modifiedFiles,
466
+ async transformStylesheet(data, containingFile, resourceFile, order, className) {
467
+ const filename = resourceFile ?? containingFile.replace(".ts", `.${pluginOptions.inlineStylesExtension}`);
468
+ const preprocessedData = pluginOptions.stylePreprocessor ? pluginOptions.stylePreprocessor(data, filename) ?? data : data;
469
+ if (pluginOptions.liveReload && watchMode) {
470
+ const stylesheetId = createHash("sha256").update(containingFile).update(className).update(String(order)).update(preprocessedData).digest("hex") + "." + pluginOptions.inlineStylesExtension;
471
+ inlineComponentStyles.set(stylesheetId, preprocessedData);
472
+ return stylesheetId;
473
+ }
474
+ let stylesheetResult;
475
+ try {
476
+ stylesheetResult = await preprocessCSS(preprocessedData, `${filename}?direct`, resolvedConfig);
477
+ } catch (e) {
478
+ console.error(`${e}`);
479
+ }
480
+ return stylesheetResult?.code || "";
481
+ },
482
+ processWebWorker(workerFile, containingFile) {
483
+ return "";
484
+ }
485
+ }, (tsCompilerOptions) => {
486
+ if (pluginOptions.liveReload && watchMode) {
487
+ tsCompilerOptions["_enableHmr"] = true;
488
+ tsCompilerOptions["externalRuntimeStyles"] = true;
489
+ tsCompilerOptions["supportTestBed"] = true;
490
+ }
491
+ if (tsCompilerOptions.compilationMode === "partial") {
492
+ tsCompilerOptions["supportTestBed"] = true;
493
+ tsCompilerOptions["supportJitMode"] = true;
494
+ }
495
+ if (!isTest && config.build?.lib) {
496
+ tsCompilerOptions["declaration"] = true;
497
+ tsCompilerOptions["declarationMap"] = watchMode;
498
+ tsCompilerOptions["inlineSources"] = true;
499
+ }
500
+ if (isTest) tsCompilerOptions["supportTestBed"] = true;
501
+ return tsCompilerOptions;
502
+ });
503
+ compilationResult.externalStylesheets?.forEach((value, key) => {
504
+ externalComponentStyles?.set(`${value}.css`, key);
505
+ });
506
+ const diagnostics = await angularCompilation.diagnoseFiles(pluginOptions.disableTypeChecking ? DiagnosticModes.All & ~DiagnosticModes.Semantic : DiagnosticModes.All);
507
+ const errors = diagnostics.errors?.length ? diagnostics.errors : [];
508
+ const warnings = diagnostics.warnings?.length ? diagnostics.warnings : [];
509
+ const templateUpdates = mapTemplateUpdatesToFiles(compilationResult.templateUpdates);
510
+ for (const file of await angularCompilation.emitAffectedFiles()) {
511
+ const normalizedFilename = normalizePath(file.filename);
512
+ const templateUpdate = templateUpdates.get(normalizedFilename);
513
+ if (templateUpdate) classNames.set(normalizedFilename, templateUpdate.className);
514
+ outputFiles.set(normalizedFilename, {
515
+ content: file.contents,
516
+ dependencies: [],
517
+ errors: errors.map((error) => error.text || ""),
518
+ warnings: warnings.map((warning) => warning.text || ""),
519
+ hmrUpdateCode: templateUpdate?.code,
520
+ hmrEligible: !!templateUpdate?.code
521
+ });
522
+ }
523
+ }
524
+ async function performCompilation(config, ids) {
525
+ let resolve;
526
+ const previousLock = compilationLock;
527
+ compilationLock = new Promise((r) => {
528
+ resolve = r;
529
+ });
530
+ try {
531
+ await previousLock;
532
+ await _doPerformCompilation(config, ids);
533
+ } finally {
534
+ resolve();
535
+ }
536
+ }
537
+ /**
538
+ * This method share mutable state and performs the actual compilation work.
539
+ * It should not be called concurrently. Use `performCompilation` which wraps this method in a lock to ensure only one compilation runs at a time.
540
+ */
541
+ async function _doPerformCompilation(config, ids) {
542
+ if (pluginOptions.useAngularCompilationAPI) {
543
+ await performAngularCompilation(config, ids);
544
+ return;
545
+ }
546
+ const isProd = config.mode === "production";
547
+ const modifiedFiles = new Set(ids ?? []);
548
+ sourceFileCache$1.invalidate(modifiedFiles);
549
+ if (ids?.length) for (const id of ids || []) fileTransformMap.delete(id);
550
+ if (pluginOptions.include.length > 0 && includeCache.length === 0) includeCache = findIncludes();
551
+ const resolvedTsConfigPath = resolveTsConfigPath();
552
+ const tsconfigKey = [
553
+ resolvedTsConfigPath,
554
+ isProd ? "prod" : "dev",
555
+ isTest ? "test" : "app",
556
+ config.build?.lib ? "lib" : "nolib"
557
+ ].join("|");
558
+ let cached = tsconfigOptionsCache.get(tsconfigKey);
559
+ if (!cached) {
560
+ const read = compilerCli.readConfiguration(resolvedTsConfigPath, {
561
+ suppressOutputPathCheck: true,
562
+ outDir: void 0,
563
+ sourceMap: false,
564
+ inlineSourceMap: !isProd,
565
+ inlineSources: !isProd,
566
+ declaration: false,
567
+ declarationMap: false,
568
+ allowEmptyCodegenFiles: false,
569
+ annotationsAs: "decorators",
570
+ enableResourceInlining: false,
571
+ noEmitOnError: false,
572
+ mapRoot: void 0,
573
+ sourceRoot: void 0,
574
+ supportTestBed: false,
575
+ supportJitMode: false
576
+ });
577
+ cached = {
578
+ options: read.options,
579
+ rootNames: read.rootNames
580
+ };
581
+ tsconfigOptionsCache.set(tsconfigKey, cached);
582
+ }
583
+ const tsCompilerOptions = { ...cached.options };
584
+ let rootNames = [...cached.rootNames];
585
+ if (pluginOptions.liveReload && watchMode) {
586
+ tsCompilerOptions["_enableHmr"] = true;
587
+ tsCompilerOptions["externalRuntimeStyles"] = true;
588
+ tsCompilerOptions["supportTestBed"] = true;
589
+ }
590
+ if (tsCompilerOptions["compilationMode"] === "partial") {
591
+ tsCompilerOptions["supportTestBed"] = true;
592
+ tsCompilerOptions["supportJitMode"] = true;
593
+ }
594
+ if (!isTest && config.build?.lib) {
595
+ tsCompilerOptions["declaration"] = true;
596
+ tsCompilerOptions["declarationMap"] = watchMode;
597
+ tsCompilerOptions["inlineSources"] = true;
598
+ }
599
+ if (isTest) tsCompilerOptions["supportTestBed"] = true;
600
+ const replacements = pluginOptions.fileReplacements.map((rp) => join(pluginOptions.workspaceRoot, rp.ssr || rp.with));
601
+ rootNames = [...new Set([
602
+ ...rootNames,
603
+ ...includeCache,
604
+ ...replacements
605
+ ])];
606
+ const hostKey = JSON.stringify(tsCompilerOptions);
607
+ let host;
608
+ if (cachedHost && cachedHostKey === hostKey) host = cachedHost;
609
+ else {
610
+ host = ts.createIncrementalCompilerHost(tsCompilerOptions, {
611
+ ...ts.sys,
612
+ readFile(path, encoding) {
613
+ if (fileTransformMap.has(path)) return fileTransformMap.get(path);
614
+ const file = ts.sys.readFile.call(null, path, encoding);
615
+ if (file) fileTransformMap.set(path, file);
616
+ return file;
617
+ }
618
+ });
619
+ cachedHost = host;
620
+ cachedHostKey = hostKey;
621
+ if (watchMode) augmentHostWithCaching(host, sourceFileCache$1);
622
+ }
623
+ if (!jit) {
624
+ inlineComponentStyles = tsCompilerOptions["externalRuntimeStyles"] ? /* @__PURE__ */ new Map() : void 0;
625
+ externalComponentStyles = tsCompilerOptions["externalRuntimeStyles"] ? /* @__PURE__ */ new Map() : void 0;
626
+ augmentHostWithResources(host, styleTransform, {
627
+ inlineStylesExtension: pluginOptions.inlineStylesExtension,
628
+ isProd,
629
+ inlineComponentStyles,
630
+ externalComponentStyles,
631
+ sourceFileCache: sourceFileCache$1,
632
+ stylePreprocessor: pluginOptions.stylePreprocessor
633
+ });
634
+ }
635
+ /**
636
+ * Creates a new NgtscProgram to analyze/re-analyze
637
+ * the source files and create a file emitter.
638
+ * This is shared between an initial build and a hot update.
639
+ */
640
+ let typeScriptProgram;
641
+ let angularCompiler;
642
+ const oldBuilder = builder ?? ts.readBuilderProgram(tsCompilerOptions, host);
643
+ if (!jit) {
644
+ const angularProgram = new compilerCli.NgtscProgram(rootNames, tsCompilerOptions, host, nextProgram);
645
+ angularCompiler = angularProgram.compiler;
646
+ typeScriptProgram = angularProgram.compiler.getCurrentProgram();
647
+ augmentProgramWithVersioning(typeScriptProgram);
648
+ builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(typeScriptProgram, host, oldBuilder);
649
+ nextProgram = angularProgram;
650
+ } else {
651
+ builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, tsCompilerOptions, host, oldBuilder);
652
+ typeScriptProgram = builder.getProgram();
653
+ }
654
+ if (!watchMode) builder = ts.createAbstractBuilder(typeScriptProgram, host, oldBuilder);
655
+ if (angularCompiler) await angularCompiler.analyzeAsync();
656
+ const transformers = mergeTransformers({ before: jit ? [compilerCli.constructorParametersDownlevelTransform(builder.getProgram()), cjt(() => builder.getProgram().getTypeChecker())] : [] }, jit ? {} : angularCompiler.prepareEmit().transformers);
657
+ const fileMetadata = getFileMetadata(builder, angularCompiler, pluginOptions.liveReload, pluginOptions.disableTypeChecking);
658
+ const writeFileCallback = (_filename, content, _a, _b, sourceFiles) => {
659
+ if (!sourceFiles?.length) return;
660
+ const filename = normalizePath(sourceFiles[0].fileName);
661
+ if (filename.includes("ngtypecheck.ts") || filename.includes(".d.")) return;
662
+ const metadata = watchMode ? fileMetadata(filename) : {};
663
+ outputFiles.set(filename, {
664
+ content,
665
+ dependencies: [],
666
+ errors: metadata.errors,
667
+ warnings: metadata.warnings,
668
+ hmrUpdateCode: metadata.hmrUpdateCode,
669
+ hmrEligible: metadata.hmrEligible
670
+ });
671
+ };
672
+ const writeOutputFile = (id) => {
673
+ const sourceFile = builder.getSourceFile(id);
674
+ if (!sourceFile) return;
675
+ let content = "";
676
+ builder.emit(sourceFile, (filename, data) => {
677
+ if (/\.[cm]?js$/.test(filename)) content = data;
678
+ if (!watchMode && !isTest && /\.d\.ts/.test(filename) && !filename.includes(".ngtypecheck.")) {
679
+ const declarationPath = resolve(config.root, config.build.outDir, relative(config.root, filename)).replace("/src/", "/");
680
+ const declarationFileDir = declarationPath.replace(basename(filename), "").replace("/src/", "/");
681
+ declarationFiles.push({
682
+ declarationFileDir,
683
+ declarationPath,
684
+ data
685
+ });
686
+ }
687
+ }, void 0, void 0, transformers);
688
+ writeFileCallback(id, content, false, void 0, [sourceFile]);
689
+ if (angularCompiler) angularCompiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
690
+ };
691
+ if (watchMode) {
692
+ if (ids && ids.length > 0) ids.forEach((id) => writeOutputFile(id));
693
+ else if (isTest) while (builder.emitNextAffectedFile(writeFileCallback, void 0, void 0, transformers));
694
+ }
695
+ if (!isTest)
696
+ /**
697
+ * Perf: Output files on demand so the dev server
698
+ * isn't blocked when emitting files.
699
+ */
700
+ outputFile = writeOutputFile;
701
+ }
702
+ }
703
+ function createFsWatcherCacheInvalidator(invalidateFsCaches, invalidateTsconfigCaches, performCompilation) {
704
+ return async () => {
705
+ invalidateFsCaches();
706
+ invalidateTsconfigCaches();
707
+ await performCompilation();
708
+ };
709
+ }
710
+ /**
711
+ * Convert Analog/Angular CLI-style file replacements into the flat record
712
+ * expected by `AngularHostOptions.fileReplacements`.
713
+ *
714
+ * Only browser replacements (`{ replace, with }`) are converted. SSR-only
715
+ * replacements (`{ replace, ssr }`) are left for the Vite runtime plugin to
716
+ * handle — they should not be baked into the Angular compilation host because
717
+ * that would apply them to both browser and server builds.
718
+ *
719
+ * Relative paths are resolved against `workspaceRoot` so that the host
720
+ * receives the same absolute paths it would get from the Angular CLI.
721
+ */
722
+ function toAngularCompilationFileReplacements(replacements, workspaceRoot) {
723
+ const mappedReplacements = replacements.flatMap((replacement) => {
724
+ if (!("with" in replacement)) return [];
725
+ return [[isAbsolute(replacement.replace) ? replacement.replace : resolve(workspaceRoot, replacement.replace), isAbsolute(replacement.with) ? replacement.with : resolve(workspaceRoot, replacement.with)]];
726
+ });
727
+ return mappedReplacements.length ? Object.fromEntries(mappedReplacements) : void 0;
728
+ }
729
+ /**
730
+ * Map Angular's `templateUpdates` (keyed by `encodedFilePath@ClassName`)
731
+ * back to absolute file paths with their associated HMR code and component
732
+ * class name.
733
+ *
734
+ * Angular's private Compilation API emits template update keys in the form
735
+ * `encodeURIComponent(relativePath + '@' + className)`. We decode and resolve
736
+ * them so the caller can look up updates by the same normalized absolute path
737
+ * used elsewhere in the plugin (`outputFiles`, `classNames`, etc.).
738
+ */
739
+ function mapTemplateUpdatesToFiles(templateUpdates) {
740
+ const updatesByFile = /* @__PURE__ */ new Map();
741
+ templateUpdates?.forEach((code, encodedUpdateId) => {
742
+ const [file, className = ""] = decodeURIComponent(encodedUpdateId).split("@");
743
+ const resolvedFile = normalizePath(resolve(process.cwd(), file));
744
+ updatesByFile.set(resolvedFile, {
745
+ className,
746
+ code
747
+ });
748
+ });
749
+ return updatesByFile;
864
750
  }
865
751
  function sendHMRComponentUpdate(server, id) {
866
- server.ws.send('angular:component-update', {
867
- id: encodeURIComponent(id),
868
- timestamp: Date.now(),
869
- });
870
- classNames.delete(id);
752
+ server.ws.send("angular:component-update", {
753
+ id: encodeURIComponent(id),
754
+ timestamp: Date.now()
755
+ });
756
+ classNames.delete(id);
871
757
  }
872
- export function getFileMetadata(program, angularCompiler, liveReload, disableTypeChecking) {
873
- const ts = require('typescript');
874
- return (file) => {
875
- const sourceFile = program.getSourceFile(file);
876
- if (!sourceFile) {
877
- return {};
878
- }
879
- const diagnostics = getDiagnosticsForSourceFile(sourceFile, !!disableTypeChecking, program, angularCompiler);
880
- const errors = diagnostics
881
- .filter((d) => d.category === ts.DiagnosticCategory?.Error)
882
- .map((d) => typeof d.messageText === 'object'
883
- ? d.messageText.messageText
884
- : d.messageText);
885
- const warnings = diagnostics
886
- .filter((d) => d.category === ts.DiagnosticCategory?.Warning)
887
- .map((d) => d.messageText);
888
- let hmrUpdateCode = undefined;
889
- let hmrEligible = false;
890
- if (liveReload) {
891
- for (const node of sourceFile.statements) {
892
- if (ts.isClassDeclaration(node) && node.name != null) {
893
- hmrUpdateCode = angularCompiler?.emitHmrUpdateModule(node);
894
- if (!!hmrUpdateCode) {
895
- classNames.set(file, node.name.getText());
896
- hmrEligible = true;
897
- }
898
- }
899
- }
900
- }
901
- return { errors, warnings, hmrUpdateCode, hmrEligible };
902
- };
758
+ function getFileMetadata(program, angularCompiler, liveReload, disableTypeChecking) {
759
+ const ts = require("typescript");
760
+ return (file) => {
761
+ const sourceFile = program.getSourceFile(file);
762
+ if (!sourceFile) return {};
763
+ const diagnostics = getDiagnosticsForSourceFile(sourceFile, !!disableTypeChecking, program, angularCompiler);
764
+ const errors = diagnostics.filter((d) => d.category === ts.DiagnosticCategory?.Error).map((d) => typeof d.messageText === "object" ? d.messageText.messageText : d.messageText);
765
+ const warnings = diagnostics.filter((d) => d.category === ts.DiagnosticCategory?.Warning).map((d) => d.messageText);
766
+ let hmrUpdateCode = void 0;
767
+ let hmrEligible = false;
768
+ if (liveReload) {
769
+ for (const node of sourceFile.statements) if (ts.isClassDeclaration(node) && node.name != null) {
770
+ hmrUpdateCode = angularCompiler?.emitHmrUpdateModule(node);
771
+ if (hmrUpdateCode) {
772
+ classNames.set(file, node.name.getText());
773
+ hmrEligible = true;
774
+ }
775
+ }
776
+ }
777
+ return {
778
+ errors,
779
+ warnings,
780
+ hmrUpdateCode,
781
+ hmrEligible
782
+ };
783
+ };
903
784
  }
904
785
  function getDiagnosticsForSourceFile(sourceFile, disableTypeChecking, program, angularCompiler) {
905
- const syntacticDiagnostics = program.getSyntacticDiagnostics(sourceFile);
906
- if (disableTypeChecking) {
907
- // Syntax errors are cheap to compute and the app will not run if there are any
908
- // So always show these types of errors regardless if type checking is disabled
909
- return syntacticDiagnostics;
910
- }
911
- const semanticDiagnostics = program.getSemanticDiagnostics(sourceFile);
912
- const angularDiagnostics = angularCompiler
913
- ? angularCompiler.getDiagnosticsForFile(sourceFile, 1)
914
- : [];
915
- return [
916
- ...syntacticDiagnostics,
917
- ...semanticDiagnostics,
918
- ...angularDiagnostics,
919
- ];
786
+ const syntacticDiagnostics = program.getSyntacticDiagnostics(sourceFile);
787
+ if (disableTypeChecking) return syntacticDiagnostics;
788
+ const semanticDiagnostics = program.getSemanticDiagnostics(sourceFile);
789
+ const angularDiagnostics = angularCompiler ? angularCompiler.getDiagnosticsForFile(sourceFile, 1) : [];
790
+ return [
791
+ ...syntacticDiagnostics,
792
+ ...semanticDiagnostics,
793
+ ...angularDiagnostics
794
+ ];
920
795
  }
921
796
  function markModuleSelfAccepting(mod) {
922
- // support Vite 6
923
- if ('_clientModule' in mod) {
924
- mod['_clientModule'].isSelfAccepting = true;
925
- }
926
- return {
927
- ...mod,
928
- isSelfAccepting: true,
929
- };
797
+ if ("_clientModule" in mod) mod["_clientModule"].isSelfAccepting = true;
798
+ return {
799
+ ...mod,
800
+ isSelfAccepting: true
801
+ };
930
802
  }
931
803
  function isComponentStyleSheet(id) {
932
- return id.includes('ngcomp=');
804
+ return id.includes("ngcomp=");
933
805
  }
934
806
  function getComponentStyleSheetMeta(id) {
935
- const params = new URL(id, 'http://localhost').searchParams;
936
- const encapsulationMapping = {
937
- '0': 'emulated',
938
- '2': 'none',
939
- '3': 'shadow',
940
- };
941
- return {
942
- componentId: params.get('ngcomp'),
943
- encapsulation: encapsulationMapping[params.get('e')],
944
- };
807
+ const params = new URL(id, "http://localhost").searchParams;
808
+ return {
809
+ componentId: params.get("ngcomp"),
810
+ encapsulation: {
811
+ "0": "emulated",
812
+ "2": "none",
813
+ "3": "shadow"
814
+ }[params.get("e")]
815
+ };
945
816
  }
946
817
  /**
947
- * Removes leading / and query string from a url path
948
- * e.g. /foo.scss?direct&ngcomp=ng-c3153525609&e=0 returns foo.scss
949
- * @param id
950
- */
818
+ * Removes leading / and query string from a url path
819
+ * e.g. /foo.scss?direct&ngcomp=ng-c3153525609&e=0 returns foo.scss
820
+ * @param id
821
+ */
951
822
  function getFilenameFromPath(id) {
952
- return new URL(id, 'http://localhost').pathname.replace(/^\//, '');
823
+ return new URL(id, "http://localhost").pathname.replace(/^\//, "");
953
824
  }
954
825
  /**
955
- * Checks for vitest run from the command line
956
- * @returns boolean
957
- */
958
- export function isTestWatchMode(args = process.argv) {
959
- // vitest --run
960
- const hasRun = args.find((arg) => arg.includes('--run'));
961
- if (hasRun) {
962
- return false;
963
- }
964
- // vitest --no-run
965
- const hasNoRun = args.find((arg) => arg.includes('--no-run'));
966
- if (hasNoRun) {
967
- return true;
968
- }
969
- // check for --watch=false or --no-watch
970
- const hasWatch = args.find((arg) => arg.includes('watch'));
971
- if (hasWatch && ['false', 'no'].some((neg) => hasWatch.includes(neg))) {
972
- return false;
973
- }
974
- // check for --watch false
975
- const watchIndex = args.findIndex((arg) => arg.includes('watch'));
976
- const watchArg = args[watchIndex + 1];
977
- if (watchArg && watchArg === 'false') {
978
- return false;
979
- }
980
- return true;
826
+ * Checks for vitest run from the command line
827
+ * @returns boolean
828
+ */
829
+ function isTestWatchMode(args = process.argv) {
830
+ if (args.find((arg) => arg.includes("--run"))) return false;
831
+ if (args.find((arg) => arg.includes("--no-run"))) return true;
832
+ const hasWatch = args.find((arg) => arg.includes("watch"));
833
+ if (hasWatch && ["false", "no"].some((neg) => hasWatch.includes(neg))) return false;
834
+ const watchArg = args[args.findIndex((arg) => arg.includes("watch")) + 1];
835
+ if (watchArg && watchArg === "false") return false;
836
+ return true;
981
837
  }
838
+ //#endregion
839
+ export { angular };
840
+
982
841
  //# sourceMappingURL=angular-vite-plugin.js.map