@analogjs/vite-plugin-angular 2.0.0-alpha.9 → 2.0.0-beta.2

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 (67) hide show
  1. package/package.json +9 -5
  2. package/src/index.d.ts +0 -2
  3. package/src/index.js +0 -2
  4. package/src/index.js.map +1 -1
  5. package/src/lib/angular-pending-tasks.plugin.js +2 -1
  6. package/src/lib/angular-pending-tasks.plugin.js.map +1 -1
  7. package/src/lib/angular-storybook-plugin.d.ts +2 -2
  8. package/src/lib/angular-storybook-plugin.js +11 -4
  9. package/src/lib/angular-storybook-plugin.js.map +1 -1
  10. package/src/lib/angular-vite-plugin.d.ts +13 -23
  11. package/src/lib/angular-vite-plugin.js +190 -214
  12. package/src/lib/angular-vite-plugin.js.map +1 -1
  13. package/src/lib/component-resolvers.js +1 -1
  14. package/src/lib/component-resolvers.js.map +1 -1
  15. package/src/lib/host.d.ts +0 -5
  16. package/src/lib/host.js +5 -80
  17. package/src/lib/host.js.map +1 -1
  18. package/src/lib/live-reload-plugin.d.ts +6 -0
  19. package/src/lib/live-reload-plugin.js +63 -0
  20. package/src/lib/live-reload-plugin.js.map +1 -0
  21. package/src/lib/models.d.ts +11 -0
  22. package/src/lib/models.js +2 -0
  23. package/src/lib/models.js.map +1 -0
  24. package/src/lib/nx-folder-plugin.d.ts +7 -0
  25. package/src/lib/nx-folder-plugin.js +18 -0
  26. package/src/lib/nx-folder-plugin.js.map +1 -0
  27. package/src/lib/plugins/file-replacements.plugin.d.ts +12 -0
  28. package/src/lib/plugins/file-replacements.plugin.js +64 -0
  29. package/src/lib/plugins/file-replacements.plugin.js.map +1 -0
  30. package/src/lib/tools/README.md +3 -0
  31. package/src/lib/tools/builders.json +15 -0
  32. package/src/lib/tools/package.json +8 -0
  33. package/src/lib/tools/src/builders/vite/schema.d.ts +6 -0
  34. package/src/lib/tools/src/builders/vite/schema.json +39 -0
  35. package/src/lib/tools/src/builders/vite/vite-build.impl.d.ts +2 -0
  36. package/src/lib/tools/src/builders/vite/vite-build.impl.js +38 -0
  37. package/src/lib/tools/src/builders/vite/vite-build.impl.js.map +1 -0
  38. package/src/lib/tools/src/builders/vite-dev-server/dev-server.impl.d.ts +2 -0
  39. package/src/lib/tools/src/builders/vite-dev-server/dev-server.impl.js +62 -0
  40. package/src/lib/tools/src/builders/vite-dev-server/dev-server.impl.js.map +1 -0
  41. package/src/lib/tools/src/builders/vite-dev-server/schema.d.ts +5 -0
  42. package/src/lib/tools/src/builders/vite-dev-server/schema.json +25 -0
  43. package/src/lib/tools/src/index.d.ts +0 -0
  44. package/src/lib/tools/src/index.js +1 -0
  45. package/src/lib/tools/src/index.js.map +1 -0
  46. package/src/lib/utils/devkit.js +3 -11
  47. package/src/lib/utils/devkit.js.map +1 -1
  48. package/src/lib/utils/hmr-candidates.js +40 -12
  49. package/src/lib/utils/hmr-candidates.js.map +1 -1
  50. package/esbuild.d.ts +0 -3
  51. package/esbuild.js +0 -31
  52. package/esbuild.js.map +0 -1
  53. package/src/lib/authoring/analog.d.ts +0 -1
  54. package/src/lib/authoring/analog.js +0 -550
  55. package/src/lib/authoring/analog.js.map +0 -1
  56. package/src/lib/authoring/constants.d.ts +0 -26
  57. package/src/lib/authoring/constants.js +0 -47
  58. package/src/lib/authoring/constants.js.map +0 -1
  59. package/src/lib/authoring/frontmatter.d.ts +0 -2
  60. package/src/lib/authoring/frontmatter.js +0 -26
  61. package/src/lib/authoring/frontmatter.js.map +0 -1
  62. package/src/lib/authoring/markdown-transform.d.ts +0 -4
  63. package/src/lib/authoring/markdown-transform.js +0 -19
  64. package/src/lib/authoring/markdown-transform.js.map +0 -1
  65. package/src/lib/authoring/marked-setup.service.d.ts +0 -23
  66. package/src/lib/authoring/marked-setup.service.js +0 -118
  67. package/src/lib/authoring/marked-setup.service.js.map +0 -1
@@ -1,8 +1,8 @@
1
- import { dirname, relative, resolve } from 'node:path';
1
+ import { basename, dirname, isAbsolute, join, relative, resolve, } from 'node:path';
2
+ import { mkdirSync, writeFileSync } from 'node:fs';
2
3
  import * as compilerCli from '@angular/compiler-cli';
3
- import * as ts from 'typescript';
4
4
  import { createRequire } from 'node:module';
5
- import { normalizePath, preprocessCSS, } from 'vite';
5
+ import { normalizePath, preprocessCSS, defaultClientConditions, } from 'vite';
6
6
  import * as ngCompiler from '@angular/compiler';
7
7
  import { createCompilerPlugin } from './compiler-plugin.js';
8
8
  import { StyleUrlsResolver, TemplateUrlsResolver, } from './component-resolvers.js';
@@ -13,18 +13,17 @@ import { createJitResourceTransformer, SourceFileCache, angularMajor, } from './
13
13
  import { angularVitestPlugins } from './angular-vitest-plugin.js';
14
14
  import { angularStorybookPlugin } from './angular-storybook-plugin.js';
15
15
  const require = createRequire(import.meta.url);
16
- import { getFrontmatterMetadata } from './authoring/frontmatter.js';
17
- import { defaultMarkdownTemplateTransforms, } from './authoring/markdown-transform.js';
18
16
  import { routerPlugin } from './router-plugin.js';
19
17
  import { pendingTasksPlugin } from './angular-pending-tasks.plugin.js';
20
- import { analyzeFileUpdates } from './utils/hmr-candidates.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
21
  /**
22
22
  * TypeScript file extension regex
23
23
  * Match .(c or m)ts, .ts extensions with an optional ? for query params
24
24
  * Ignore .tsx extensions
25
25
  */
26
26
  const TS_EXT_REGEX = /\.[cm]?(ts|analog|ag)[^x]?\??/;
27
- const ANGULAR_COMPONENT_PREFIX = '/@ng/component';
28
27
  const classNames = new Map();
29
28
  export function angular(options) {
30
29
  /**
@@ -32,10 +31,7 @@ export function angular(options) {
32
31
  * are used for values not provided.
33
32
  */
34
33
  const pluginOptions = {
35
- tsconfig: options?.tsconfig ??
36
- (process.env['NODE_ENV'] === 'test'
37
- ? './tsconfig.spec.json'
38
- : './tsconfig.app.json'),
34
+ tsconfig: options?.tsconfig || '',
39
35
  workspaceRoot: options?.workspaceRoot ?? process.cwd(),
40
36
  inlineStylesExtension: options?.inlineStylesExtension ?? 'css',
41
37
  advanced: {
@@ -46,24 +42,14 @@ export function angular(options) {
46
42
  },
47
43
  },
48
44
  supportedBrowsers: options?.supportedBrowsers ?? ['safari 15'],
49
- jit: options?.experimental?.supportAnalogFormat ? false : options?.jit,
50
- supportAnalogFormat: options?.experimental?.supportAnalogFormat ?? false,
51
- markdownTemplateTransforms: options?.experimental
52
- ?.markdownTemplateTransforms?.length
53
- ? options.experimental.markdownTemplateTransforms
54
- : defaultMarkdownTemplateTransforms,
45
+ jit: options?.jit,
55
46
  include: options?.include ?? [],
56
47
  additionalContentDirs: options?.additionalContentDirs ?? [],
57
48
  liveReload: options?.liveReload ?? false,
58
49
  disableTypeChecking: options?.disableTypeChecking ?? true,
50
+ fileReplacements: options?.fileReplacements ?? [],
59
51
  };
60
- // The file emitter created during `onStart` that will be used during the build in `onLoad` callbacks for TS files
61
- let fileEmitter;
62
- let compilerOptions = {};
63
- const ts = require('typescript');
64
52
  let resolvedConfig;
65
- let rootNames;
66
- let host;
67
53
  let nextProgram;
68
54
  let builderProgram;
69
55
  let watchMode = false;
@@ -72,6 +58,7 @@ export function angular(options) {
72
58
  let externalComponentStyles;
73
59
  const sourceFileCache = new SourceFileCache();
74
60
  const isTest = process.env['NODE_ENV'] === 'test' || !!process.env['VITEST'];
61
+ const isVitestVscode = !!process.env['VITEST_VSCODE'];
75
62
  const isStackBlitz = !!process.versions['webcontainer'];
76
63
  const isAstroIntegration = process.env['ANALOG_ASTRO'] === 'true';
77
64
  const isStorybook = process.env['npm_lifecycle_script']?.includes('storybook') ||
@@ -80,9 +67,16 @@ export function angular(options) {
80
67
  process.env['ANALOG_STORYBOOK'] === 'true';
81
68
  const jit = typeof pluginOptions?.jit !== 'undefined' ? pluginOptions.jit : isTest;
82
69
  let viteServer;
83
- let styleTransform;
84
70
  const styleUrlsResolver = new StyleUrlsResolver();
85
71
  const templateUrlsResolver = new TemplateUrlsResolver();
72
+ let outputFile;
73
+ const outputFiles = new Map();
74
+ const fileEmitter = (file) => {
75
+ outputFile?.(file);
76
+ return outputFiles.get(normalizePath(file));
77
+ };
78
+ let initialCompilation = false;
79
+ const declarationFiles = [];
86
80
  function angularPlugin() {
87
81
  let isProd = false;
88
82
  if (angularMajor < 19 || isTest) {
@@ -90,21 +84,12 @@ export function angular(options) {
90
84
  }
91
85
  return {
92
86
  name: '@analogjs/vite-plugin-angular',
93
- async watchChange() {
94
- if (isTest) {
95
- await buildAndAnalyze();
96
- }
97
- },
98
87
  async config(config, { command }) {
99
88
  watchMode = command === 'serve';
100
89
  isProd =
101
90
  config.mode === 'production' ||
102
91
  process.env['NODE_ENV'] === 'production';
103
- pluginOptions.tsconfig =
104
- options?.tsconfig ??
105
- resolve(config.root || '.', process.env['NODE_ENV'] === 'test'
106
- ? './tsconfig.spec.json'
107
- : './tsconfig.app.json');
92
+ pluginOptions.tsconfig = getTsConfigPath(config.root || '.', pluginOptions, isProd, isTest, !!config?.build?.lib);
108
93
  return {
109
94
  esbuild: config.esbuild ?? false,
110
95
  optimizeDeps: {
@@ -128,7 +113,10 @@ export function angular(options) {
128
113
  },
129
114
  },
130
115
  resolve: {
131
- conditions: ['style'],
116
+ conditions: [
117
+ 'style',
118
+ ...(config.resolve?.conditions || defaultClientConditions),
119
+ ],
132
120
  },
133
121
  };
134
122
  },
@@ -149,76 +137,30 @@ export function angular(options) {
149
137
  configureServer(server) {
150
138
  viteServer = server;
151
139
  server.watcher.on('add', async () => {
152
- setupCompilation(resolvedConfig);
153
- await buildAndAnalyze();
140
+ await performCompilation(resolvedConfig);
154
141
  });
155
142
  server.watcher.on('unlink', async () => {
156
- setupCompilation(resolvedConfig);
157
- await buildAndAnalyze();
143
+ await performCompilation(resolvedConfig);
158
144
  });
159
- if (pluginOptions.liveReload) {
160
- const angularComponentMiddleware = async (req, res, next) => {
161
- if (req.url === undefined || res.writableEnded) {
162
- return;
163
- }
164
- if (!req.url.includes(ANGULAR_COMPONENT_PREFIX)) {
165
- next();
166
- return;
167
- }
168
- const requestUrl = new URL(req.url, 'http://localhost');
169
- const componentId = requestUrl.searchParams.get('c');
170
- if (!componentId) {
171
- res.statusCode = 400;
172
- res.end();
173
- return;
174
- }
175
- const [fileId] = decodeURIComponent(componentId).split('@');
176
- const resolvedId = resolve(process.cwd(), fileId);
177
- const invalidated = !!server.moduleGraph.getModuleById(resolvedId)
178
- ?.lastInvalidationTimestamp && classNames.get(resolvedId);
179
- // don't send an HMR update until the file has been invalidated
180
- if (!invalidated) {
181
- res.setHeader('Content-Type', 'text/javascript');
182
- res.setHeader('Cache-Control', 'no-cache');
183
- res.end('');
184
- return;
185
- }
186
- const result = await fileEmitter?.(resolvedId);
187
- res.setHeader('Content-Type', 'text/javascript');
188
- res.setHeader('Cache-Control', 'no-cache');
189
- res.end(`${result?.hmrUpdateCode || ''}`);
190
- };
191
- viteServer.middlewares.use(angularComponentMiddleware);
192
- }
193
145
  },
194
146
  async buildStart() {
195
- setupCompilation(resolvedConfig);
196
- // Only store cache if in watch mode
197
- if (watchMode) {
198
- augmentHostWithCaching(host, sourceFileCache);
147
+ // Defer the first compilation in test mode
148
+ if (!isVitestVscode) {
149
+ const { host } = await performCompilation(resolvedConfig);
150
+ initialCompilation = true;
151
+ // Only store cache if in watch mode
152
+ if (watchMode) {
153
+ augmentHostWithCaching(host, sourceFileCache);
154
+ }
199
155
  }
200
- await buildAndAnalyze();
201
156
  },
202
157
  async handleHotUpdate(ctx) {
203
- // The `handleHotUpdate` hook may be called before the `buildStart`,
204
- // which sets the compilation. As a result, the `host` may not be available
205
- // yet for use, leading to build errors such as "cannot read properties of undefined"
206
- // (because `host` is undefined).
207
- if (!host) {
208
- return;
209
- }
210
158
  if (TS_EXT_REGEX.test(ctx.file)) {
211
159
  let [fileId] = ctx.file.split('?');
212
- if (pluginOptions.supportAnalogFormat &&
213
- ['ag', 'analog', 'agx'].some((ext) => fileId.endsWith(ext))) {
214
- fileId += '.ts';
215
- }
216
- const stale = sourceFileCache.get(fileId);
217
- sourceFileCache.invalidate([fileId]);
218
- await buildAndAnalyze();
219
- const result = await fileEmitter?.(fileId, stale);
160
+ await performCompilation(resolvedConfig, [fileId]);
161
+ const result = fileEmitter(fileId);
220
162
  if (pluginOptions.liveReload &&
221
- !!result?.hmrEligible &&
163
+ result?.hmrEligible &&
222
164
  classNames.get(fileId)) {
223
165
  const relativeFileId = `${relative(process.cwd(), fileId)}@${classNames.get(fileId)}`;
224
166
  sendHMRComponentUpdate(ctx.server, relativeFileId);
@@ -236,7 +178,8 @@ export function angular(options) {
236
178
  * for an external resource (styles, html).
237
179
  */
238
180
  const isDirect = ctx.modules.find((mod) => ctx.file === mod.file && mod.id?.includes('?direct'));
239
- if (isDirect) {
181
+ const isInline = ctx.modules.find((mod) => ctx.file === mod.file && mod.id?.includes('?inline'));
182
+ if (isDirect || isInline) {
240
183
  if (pluginOptions.liveReload && isDirect?.id && isDirect.file) {
241
184
  const isComponentStyle = isDirect.type === 'css' && isComponentStyleSheet(isDirect.id);
242
185
  if (isComponentStyle) {
@@ -287,7 +230,10 @@ export function angular(options) {
287
230
  }
288
231
  });
289
232
  });
290
- await buildAndAnalyze();
233
+ await performCompilation(resolvedConfig, [
234
+ ...mods.map((mod) => mod.id),
235
+ ...updates,
236
+ ]);
291
237
  if (updates.length > 0) {
292
238
  updates.forEach((updateId) => {
293
239
  const impRelativeFileId = `${relative(process.cwd(), updateId)}@${classNames.get(updateId)}`;
@@ -320,7 +266,7 @@ export function angular(options) {
320
266
  }
321
267
  return undefined;
322
268
  },
323
- async load(id, options) {
269
+ async load(id) {
324
270
  // Map angular inline styles to the source text
325
271
  if (isComponentStyleSheet(id)) {
326
272
  const componentStyles = inlineComponentStyles?.get(getFilenameFromPath(id));
@@ -328,17 +274,6 @@ export function angular(options) {
328
274
  return componentStyles;
329
275
  }
330
276
  }
331
- if (pluginOptions.liveReload &&
332
- options?.ssr &&
333
- id.includes(ANGULAR_COMPONENT_PREFIX)) {
334
- const requestUrl = new URL(id.slice(1), 'http://localhost');
335
- const componentId = requestUrl.searchParams.get('c');
336
- if (!componentId) {
337
- return;
338
- }
339
- const result = await fileEmitter?.(resolve(process.cwd(), decodeURIComponent(componentId).split('@')[0]));
340
- return result?.hmrUpdateCode || '';
341
- }
342
277
  return;
343
278
  },
344
279
  async transform(code, id) {
@@ -394,12 +329,17 @@ export function angular(options) {
394
329
  * for test(Vitest)
395
330
  */
396
331
  if (isTest) {
332
+ if (isVitestVscode && !initialCompilation) {
333
+ // Do full initial compilation
334
+ await performCompilation(resolvedConfig);
335
+ initialCompilation = true;
336
+ }
397
337
  const tsMod = viteServer?.moduleGraph.getModuleById(id);
398
338
  if (tsMod) {
399
339
  const invalidated = tsMod.lastInvalidationTimestamp;
400
340
  if (testWatchMode && invalidated) {
401
341
  sourceFileCache.invalidate([id]);
402
- await buildAndAnalyze();
342
+ await performCompilation(resolvedConfig, [id]);
403
343
  }
404
344
  }
405
345
  }
@@ -414,7 +354,7 @@ export function angular(options) {
414
354
  this.addWatchFile(absoluteFileUrl);
415
355
  }
416
356
  }
417
- const typescriptResult = await fileEmitter?.(id);
357
+ const typescriptResult = fileEmitter(id);
418
358
  if (typescriptResult?.warnings &&
419
359
  typescriptResult?.warnings.length > 0) {
420
360
  this.warn(`${typescriptResult.warnings.join('\n')}`);
@@ -435,25 +375,6 @@ export function angular(options) {
435
375
  data = data.replace(`angular:jit:style:file;${styleFile}`, `${resolvedStyleUrl}?inline`);
436
376
  });
437
377
  }
438
- if (jit) {
439
- return {
440
- code: data,
441
- map: null,
442
- };
443
- }
444
- if ((id.endsWith('.analog') ||
445
- id.endsWith('.agx') ||
446
- id.endsWith('.ag')) &&
447
- pluginOptions.supportAnalogFormat &&
448
- fileEmitter) {
449
- sourceFileCache.invalidate([`${id}.ts`]);
450
- const ngFileResult = await fileEmitter(`${id}.ts`);
451
- data = ngFileResult?.content || '';
452
- if (id.includes('.agx')) {
453
- const metadata = await getFrontmatterMetadata(code, id, pluginOptions.markdownTemplateTransforms || []);
454
- data += metadata;
455
- }
456
- }
457
378
  return {
458
379
  code: data,
459
380
  map: null,
@@ -461,10 +382,18 @@ export function angular(options) {
461
382
  }
462
383
  return undefined;
463
384
  },
385
+ closeBundle() {
386
+ declarationFiles.forEach(({ declarationFileDir, declarationPath, data }) => {
387
+ mkdirSync(declarationFileDir, { recursive: true });
388
+ writeFileSync(declarationPath, data, 'utf-8');
389
+ });
390
+ },
464
391
  };
465
392
  }
466
393
  return [
394
+ replaceFiles(pluginOptions.fileReplacements, pluginOptions.workspaceRoot),
467
395
  angularPlugin(),
396
+ pluginOptions.liveReload && liveReloadPlugin({ classNames, fileEmitter }),
468
397
  ...(isTest && !isStackBlitz ? angularVitestPlugins() : []),
469
398
  (jit &&
470
399
  jitPlugin({
@@ -474,36 +403,12 @@ export function angular(options) {
474
403
  supportedBrowsers: pluginOptions.supportedBrowsers,
475
404
  jit,
476
405
  }),
477
- (isStorybook && angularStorybookPlugin()),
406
+ (isStorybook &&
407
+ angularStorybookPlugin(pluginOptions.workspaceRoot)),
478
408
  routerPlugin(),
479
409
  pendingTasksPlugin(),
410
+ nxFolderPlugin(),
480
411
  ].filter(Boolean);
481
- function findAnalogFiles(config) {
482
- const analogConfig = pluginOptions.supportAnalogFormat;
483
- if (!analogConfig) {
484
- return [];
485
- }
486
- let extraGlobs = [];
487
- if (typeof analogConfig === 'object') {
488
- if (analogConfig.include) {
489
- extraGlobs = analogConfig.include;
490
- }
491
- }
492
- const fg = require('fast-glob');
493
- const appRoot = normalizePath(resolve(pluginOptions.workspaceRoot, config.root || '.'));
494
- const workspaceRoot = normalizePath(resolve(pluginOptions.workspaceRoot));
495
- const globs = [
496
- `${appRoot}/**/*.{analog,agx,ag}`,
497
- ...extraGlobs.map((glob) => `${workspaceRoot}${glob}.{analog,agx,ag}`),
498
- ...(pluginOptions.additionalContentDirs || [])?.map((glob) => `${workspaceRoot}${glob}/**/*.agx`),
499
- ...pluginOptions.include.map((glob) => `${workspaceRoot}${glob}`.replace(/\.ts$/, '.analog')),
500
- ];
501
- return fg
502
- .sync(globs, {
503
- dot: true,
504
- })
505
- .map((file) => `${file}.ts`);
506
- }
507
412
  function findIncludes() {
508
413
  const fg = require('fast-glob');
509
414
  const workspaceRoot = normalizePath(resolve(pluginOptions.workspaceRoot));
@@ -514,11 +419,28 @@ export function angular(options) {
514
419
  dot: true,
515
420
  });
516
421
  }
517
- function setupCompilation(config, context) {
422
+ function getTsConfigPath(root, options, isProd, isTest, isLib) {
423
+ if (options.tsconfig && isAbsolute(options.tsconfig)) {
424
+ return options.tsconfig;
425
+ }
426
+ let tsconfigFilePath = './tsconfig.app.json';
427
+ if (isLib) {
428
+ tsconfigFilePath = isProd
429
+ ? './tsconfig.lib.prod.json'
430
+ : './tsconfig.lib.json';
431
+ }
432
+ if (isTest) {
433
+ tsconfigFilePath = './tsconfig.spec.json';
434
+ }
435
+ if (options.tsconfig) {
436
+ tsconfigFilePath = options.tsconfig;
437
+ }
438
+ return resolve(root, tsconfigFilePath);
439
+ }
440
+ async function performCompilation(config, ids) {
518
441
  const isProd = config.mode === 'production';
519
- const analogFiles = findAnalogFiles(config);
520
442
  const includeFiles = findIncludes();
521
- const { options: tsCompilerOptions, rootNames: rn } = compilerCli.readConfiguration(pluginOptions.tsconfig, {
443
+ let { options: tsCompilerOptions, rootNames } = compilerCli.readConfiguration(pluginOptions.tsconfig, {
522
444
  suppressOutputPathCheck: true,
523
445
  outDir: undefined,
524
446
  sourceMap: false,
@@ -535,12 +457,6 @@ export function angular(options) {
535
457
  supportTestBed: false,
536
458
  supportJitMode: false,
537
459
  });
538
- if (pluginOptions.supportAnalogFormat) {
539
- // Experimental Local Compilation is necessary
540
- // for the Angular compiler to work with
541
- // AOT and virtually compiled .analog files.
542
- tsCompilerOptions.compilationMode = 'experimental-local';
543
- }
544
460
  if (pluginOptions.liveReload && watchMode) {
545
461
  tsCompilerOptions['_enableHmr'] = true;
546
462
  tsCompilerOptions['externalRuntimeStyles'] = true;
@@ -553,11 +469,17 @@ export function angular(options) {
553
469
  tsCompilerOptions['supportTestBed'] = true;
554
470
  tsCompilerOptions['supportJitMode'] = true;
555
471
  }
556
- rootNames = rn.concat(analogFiles, includeFiles);
557
- compilerOptions = tsCompilerOptions;
558
- host = ts.createIncrementalCompilerHost(compilerOptions);
559
- styleTransform = (code, filename) => preprocessCSS(code, filename, config);
472
+ if (!isTest && config.build?.lib) {
473
+ tsCompilerOptions['declaration'] = true;
474
+ tsCompilerOptions['declarationMap'] = watchMode;
475
+ tsCompilerOptions['inlineSources'] = true;
476
+ }
477
+ const replacements = pluginOptions.fileReplacements.map((rp) => join(pluginOptions.workspaceRoot, rp.ssr || rp.with));
478
+ rootNames = rootNames.concat(includeFiles, replacements);
479
+ const ts = require('typescript');
480
+ const host = ts.createIncrementalCompilerHost(tsCompilerOptions);
560
481
  if (!jit) {
482
+ const styleTransform = (code, filename) => preprocessCSS(code, filename, config);
561
483
  inlineComponentStyles = tsCompilerOptions['externalRuntimeStyles']
562
484
  ? new Map()
563
485
  : undefined;
@@ -566,59 +488,119 @@ export function angular(options) {
566
488
  : undefined;
567
489
  augmentHostWithResources(host, styleTransform, {
568
490
  inlineStylesExtension: pluginOptions.inlineStylesExtension,
569
- supportAnalogFormat: pluginOptions.supportAnalogFormat,
570
491
  isProd,
571
- markdownTemplateTransforms: pluginOptions.markdownTemplateTransforms,
572
492
  inlineComponentStyles,
573
493
  externalComponentStyles,
574
494
  });
575
495
  }
576
- }
577
- /**
578
- * Creates a new NgtscProgram to analyze/re-analyze
579
- * the source files and create a file emitter.
580
- * This is shared between an initial build and a hot update.
581
- */
582
- async function buildAndAnalyze() {
496
+ /**
497
+ * Creates a new NgtscProgram to analyze/re-analyze
498
+ * the source files and create a file emitter.
499
+ * This is shared between an initial build and a hot update.
500
+ */
583
501
  let builder;
584
502
  let typeScriptProgram;
585
503
  let angularCompiler;
586
504
  if (!jit) {
587
505
  // Create the Angular specific program that contains the Angular compiler
588
- const angularProgram = new compilerCli.NgtscProgram(rootNames, compilerOptions, host, nextProgram);
506
+ const angularProgram = new compilerCli.NgtscProgram(rootNames, tsCompilerOptions, host, nextProgram);
589
507
  angularCompiler = angularProgram.compiler;
590
508
  typeScriptProgram = angularProgram.getTsProgram();
591
509
  augmentProgramWithVersioning(typeScriptProgram);
592
- builder = builderProgram =
593
- ts.createEmitAndSemanticDiagnosticsBuilderProgram(typeScriptProgram, host, builderProgram);
510
+ builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(typeScriptProgram, host, builderProgram);
594
511
  await angularCompiler.analyzeAsync();
595
512
  nextProgram = angularProgram;
513
+ builderProgram =
514
+ builder;
596
515
  }
597
516
  else {
598
- builder = builderProgram =
599
- ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, compilerOptions, host, nextProgram);
517
+ builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, tsCompilerOptions, host, nextProgram);
600
518
  typeScriptProgram = builder.getProgram();
601
- nextProgram = builderProgram;
602
519
  }
603
520
  if (!watchMode) {
604
521
  // When not in watch mode, the startup cost of the incremental analysis can be avoided by
605
522
  // using an abstract builder that only wraps a TypeScript program.
606
523
  builder = ts.createAbstractBuilder(typeScriptProgram, host);
607
524
  }
608
- const getTypeChecker = () => builder.getProgram().getTypeChecker();
609
- fileEmitter = createFileEmitter(builder, mergeTransformers({
610
- before: [
611
- ...(jit
612
- ? [
613
- compilerCli.constructorParametersDownlevelTransform(builder.getProgram()),
614
- createJitResourceTransformer(getTypeChecker),
615
- ]
616
- : []),
617
- ...pluginOptions.advanced.tsTransformers.before,
618
- ],
619
- after: pluginOptions.advanced.tsTransformers.after,
620
- afterDeclarations: pluginOptions.advanced.tsTransformers.afterDeclarations,
621
- }, jit ? {} : angularCompiler.prepareEmit().transformers), () => [], angularCompiler, pluginOptions.liveReload, pluginOptions.disableTypeChecking);
525
+ const beforeTransformers = jit
526
+ ? [
527
+ compilerCli.constructorParametersDownlevelTransform(builder.getProgram()),
528
+ createJitResourceTransformer(() => builder.getProgram().getTypeChecker()),
529
+ ]
530
+ : [];
531
+ const transformers = mergeTransformers({ before: beforeTransformers }, jit ? {} : angularCompiler.prepareEmit().transformers);
532
+ const fileMetadata = getFileMetadata(builder, angularCompiler, pluginOptions.liveReload, pluginOptions.disableTypeChecking);
533
+ const writeFileCallback = (_filename, content, _a, _b, sourceFiles) => {
534
+ if (!sourceFiles?.length) {
535
+ return;
536
+ }
537
+ const filename = normalizePath(sourceFiles[0].fileName);
538
+ if (filename.includes('ngtypecheck.ts') || filename.includes('.d.')) {
539
+ return;
540
+ }
541
+ const metadata = watchMode ? fileMetadata(filename) : {};
542
+ outputFiles.set(filename, {
543
+ content,
544
+ dependencies: [],
545
+ errors: metadata.errors,
546
+ warnings: metadata.warnings,
547
+ hmrUpdateCode: metadata.hmrUpdateCode,
548
+ hmrEligible: metadata.hmrEligible,
549
+ });
550
+ };
551
+ const writeOutputFile = (id) => {
552
+ const sourceFile = builder.getSourceFile(id);
553
+ if (!sourceFile) {
554
+ return;
555
+ }
556
+ let content = '';
557
+ builder.emit(sourceFile, (filename, data) => {
558
+ if (/\.[cm]?js$/.test(filename)) {
559
+ content = data;
560
+ }
561
+ if (!watchMode &&
562
+ !isTest &&
563
+ /\.d\.ts/.test(filename) &&
564
+ !filename.includes('.ngtypecheck.')) {
565
+ // output to library root instead /src
566
+ const declarationPath = resolve(config.root, config.build.outDir, relative(config.root, filename)).replace('/src/', '/');
567
+ const declarationFileDir = declarationPath
568
+ .replace(basename(filename), '')
569
+ .replace('/src/', '/');
570
+ declarationFiles.push({
571
+ declarationFileDir,
572
+ declarationPath,
573
+ data,
574
+ });
575
+ }
576
+ }, undefined /* cancellationToken */, undefined /* emitOnlyDtsFiles */, transformers);
577
+ writeFileCallback(id, content, false, undefined, [sourceFile]);
578
+ };
579
+ if (watchMode) {
580
+ if (ids && ids.length > 0) {
581
+ ids.forEach((id) => writeOutputFile(id));
582
+ }
583
+ else {
584
+ /**
585
+ * Only block the server from starting up
586
+ * during testing.
587
+ */
588
+ if (isTest) {
589
+ // TypeScript will loop until there are no more affected files in the program
590
+ while (builder.emitNextAffectedFile(writeFileCallback, undefined, undefined, transformers)) {
591
+ /* empty */
592
+ }
593
+ }
594
+ }
595
+ }
596
+ if (!isTest) {
597
+ /**
598
+ * Perf: Output files on demand so the dev server
599
+ * isn't blocked when emitting files.
600
+ */
601
+ outputFile = writeOutputFile;
602
+ }
603
+ return { host };
622
604
  }
623
605
  }
624
606
  function sendHMRComponentUpdate(server, id) {
@@ -628,15 +610,12 @@ function sendHMRComponentUpdate(server, id) {
628
610
  });
629
611
  classNames.delete(id);
630
612
  }
631
- export function createFileEmitter(program, transformers = {}, onAfterEmit, angularCompiler, liveReload, disableTypeChecking) {
632
- return async (file, stale) => {
613
+ export function getFileMetadata(program, angularCompiler, liveReload, disableTypeChecking) {
614
+ const ts = require('typescript');
615
+ return (file) => {
633
616
  const sourceFile = program.getSourceFile(file);
634
617
  if (!sourceFile) {
635
- return undefined;
636
- }
637
- if (stale) {
638
- const hmrEligible = !!analyzeFileUpdates(stale, sourceFile, angularCompiler);
639
- return { dependencies: [], hmrEligible };
618
+ return {};
640
619
  }
641
620
  const diagnostics = getDiagnosticsForSourceFile(sourceFile, !!disableTypeChecking, program, angularCompiler);
642
621
  const errors = diagnostics
@@ -648,22 +627,19 @@ export function createFileEmitter(program, transformers = {}, onAfterEmit, angul
648
627
  .filter((d) => d.category === ts.DiagnosticCategory?.Warning)
649
628
  .map((d) => d.messageText);
650
629
  let hmrUpdateCode = undefined;
630
+ let hmrEligible = false;
651
631
  if (liveReload) {
652
632
  for (const node of sourceFile.statements) {
653
633
  if (ts.isClassDeclaration(node) && node.name != null) {
654
634
  hmrUpdateCode = angularCompiler?.emitHmrUpdateModule(node);
655
- !!hmrUpdateCode && classNames.set(file, node.name.getText());
635
+ if (!!hmrUpdateCode) {
636
+ classNames.set(file, node.name.getText());
637
+ hmrEligible = true;
638
+ }
656
639
  }
657
640
  }
658
641
  }
659
- let content;
660
- program.emit(sourceFile, (filename, data) => {
661
- if (/\.[cm]?js$/.test(filename)) {
662
- content = data;
663
- }
664
- }, undefined /* cancellationToken */, undefined /* emitOnlyDtsFiles */, transformers);
665
- onAfterEmit?.(sourceFile);
666
- return { content, dependencies: [], errors, warnings, hmrUpdateCode };
642
+ return { errors, warnings, hmrUpdateCode, hmrEligible };
667
643
  };
668
644
  }
669
645
  function getDiagnosticsForSourceFile(sourceFile, disableTypeChecking, program, angularCompiler) {