@ngtools/webpack 11.2.1 → 11.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngtools/webpack",
3
- "version": "11.2.1",
3
+ "version": "11.2.5",
4
4
  "description": "Webpack plugin that AoT compiles your Angular components and modules.",
5
5
  "main": "./src/index.js",
6
6
  "typings": "src/index.d.ts",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "homepage": "https://github.com/angular/angular-cli",
27
27
  "dependencies": {
28
- "@angular-devkit/core": "11.2.1",
28
+ "@angular-devkit/core": "11.2.5",
29
29
  "enhanced-resolve": "5.7.0",
30
30
  "webpack-sources": "2.2.0"
31
31
  },
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "engines": {
38
38
  "node": ">= 10.13.0",
39
- "npm": "^6.11.0",
39
+ "npm": "^6.11.0 || ^7.5.6",
40
40
  "yarn": ">= 1.13.0"
41
41
  }
42
42
  }
package/src/ivy/host.d.ts CHANGED
@@ -4,6 +4,21 @@ import { WebpackResourceLoader } from '../resource_loader';
4
4
  export declare function augmentHostWithResources(host: ts.CompilerHost, resourceLoader: WebpackResourceLoader, options?: {
5
5
  directTemplateLoading?: boolean;
6
6
  }): void;
7
+ /**
8
+ * Augments a TypeScript Compiler Host's resolveModuleNames function to collect dependencies
9
+ * of the containing file passed to the resolveModuleNames function. This process assumes
10
+ * that consumers of the Compiler Host will only call resolveModuleNames with modules that are
11
+ * actually present in a containing file.
12
+ * This process is a workaround for gathering a TypeScript SourceFile's dependencies as there
13
+ * is no currently exposed public method to do so. A BuilderProgram does have a `getAllDependencies`
14
+ * function. However, that function returns all transitive dependencies as well which can cause
15
+ * excessive Webpack rebuilds.
16
+ *
17
+ * @param host The CompilerHost to augment.
18
+ * @param dependencies A Map which will be used to store file dependencies.
19
+ * @param moduleResolutionCache An optional resolution cache to use when the host resolves a module.
20
+ */
21
+ export declare function augmentHostWithDependencyCollection(host: ts.CompilerHost, dependencies: Map<string, Set<string>>, moduleResolutionCache?: ts.ModuleResolutionCache): void;
7
22
  export declare function augmentHostWithNgcc(host: ts.CompilerHost, ngcc: NgccProcessor, moduleResolutionCache?: ts.ModuleResolutionCache): void;
8
23
  export declare function augmentHostWithReplacements(host: ts.CompilerHost, replacements: Record<string, string>, moduleResolutionCache?: ts.ModuleResolutionCache): void;
9
24
  export declare function augmentHostWithSubstitutions(host: ts.CompilerHost, substitutions: Record<string, string>): void;
package/src/ivy/host.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.augmentHostWithCaching = exports.augmentProgramWithVersioning = exports.augmentHostWithVersioning = exports.augmentHostWithSubstitutions = exports.augmentHostWithReplacements = exports.augmentHostWithNgcc = exports.augmentHostWithResources = void 0;
3
+ exports.augmentHostWithCaching = exports.augmentProgramWithVersioning = exports.augmentHostWithVersioning = exports.augmentHostWithSubstitutions = exports.augmentHostWithReplacements = exports.augmentHostWithNgcc = exports.augmentHostWithDependencyCollection = exports.augmentHostWithResources = void 0;
4
4
  const crypto_1 = require("crypto");
5
5
  const path = require("path");
6
6
  const ts = require("typescript");
@@ -49,6 +49,60 @@ function augmentResolveModuleNames(host, resolvedModuleModifier, moduleResolutio
49
49
  };
50
50
  }
51
51
  }
52
+ /**
53
+ * Augments a TypeScript Compiler Host's resolveModuleNames function to collect dependencies
54
+ * of the containing file passed to the resolveModuleNames function. This process assumes
55
+ * that consumers of the Compiler Host will only call resolveModuleNames with modules that are
56
+ * actually present in a containing file.
57
+ * This process is a workaround for gathering a TypeScript SourceFile's dependencies as there
58
+ * is no currently exposed public method to do so. A BuilderProgram does have a `getAllDependencies`
59
+ * function. However, that function returns all transitive dependencies as well which can cause
60
+ * excessive Webpack rebuilds.
61
+ *
62
+ * @param host The CompilerHost to augment.
63
+ * @param dependencies A Map which will be used to store file dependencies.
64
+ * @param moduleResolutionCache An optional resolution cache to use when the host resolves a module.
65
+ */
66
+ function augmentHostWithDependencyCollection(host, dependencies, moduleResolutionCache) {
67
+ if (host.resolveModuleNames) {
68
+ const baseResolveModuleNames = host.resolveModuleNames;
69
+ host.resolveModuleNames = function (moduleNames, containingFile, ...parameters) {
70
+ const results = baseResolveModuleNames.call(host, moduleNames, containingFile, ...parameters);
71
+ const containingFilePath = paths_1.normalizePath(containingFile);
72
+ for (const result of results) {
73
+ if (result) {
74
+ const containingFileDependencies = dependencies.get(containingFilePath);
75
+ if (containingFileDependencies) {
76
+ containingFileDependencies.add(result.resolvedFileName);
77
+ }
78
+ else {
79
+ dependencies.set(containingFilePath, new Set([result.resolvedFileName]));
80
+ }
81
+ }
82
+ }
83
+ return results;
84
+ };
85
+ }
86
+ else {
87
+ host.resolveModuleNames = function (moduleNames, containingFile, _reusedNames, redirectedReference, options) {
88
+ return moduleNames.map((name) => {
89
+ const result = ts.resolveModuleName(name, containingFile, options, host, moduleResolutionCache, redirectedReference).resolvedModule;
90
+ if (result) {
91
+ const containingFilePath = paths_1.normalizePath(containingFile);
92
+ const containingFileDependencies = dependencies.get(containingFilePath);
93
+ if (containingFileDependencies) {
94
+ containingFileDependencies.add(result.resolvedFileName);
95
+ }
96
+ else {
97
+ dependencies.set(containingFilePath, new Set([result.resolvedFileName]));
98
+ }
99
+ }
100
+ return result;
101
+ });
102
+ };
103
+ }
104
+ }
105
+ exports.augmentHostWithDependencyCollection = augmentHostWithDependencyCollection;
52
106
  function augmentHostWithNgcc(host, ngcc, moduleResolutionCache) {
53
107
  augmentResolveModuleNames(host, (resolvedModule, moduleName) => {
54
108
  if (resolvedModule && ngcc) {
@@ -26,6 +26,7 @@ export declare class AngularWebpackPlugin {
26
26
  private sourceFileCache?;
27
27
  private buildTimestamp;
28
28
  private readonly lazyRouteMap;
29
+ private readonly fileDependencies;
29
30
  private readonly requiredFilesToEmit;
30
31
  private readonly requiredFilesToEmitCache;
31
32
  private readonly fileEmitHistory;
@@ -34,6 +35,7 @@ export declare class AngularWebpackPlugin {
34
35
  apply(compiler: Compiler & {
35
36
  watchMode?: boolean;
36
37
  }): void;
38
+ private markResourceUsed;
37
39
  private rebuildRequiredFiles;
38
40
  private loadConfiguration;
39
41
  private updateAotProgram;
package/src/ivy/plugin.js CHANGED
@@ -14,6 +14,7 @@ const crypto_1 = require("crypto");
14
14
  const path = require("path");
15
15
  const ts = require("typescript");
16
16
  const webpack_1 = require("webpack");
17
+ const lazy_routes_1 = require("../lazy_routes");
17
18
  const ngcc_processor_1 = require("../ngcc_processor");
18
19
  const paths_plugin_1 = require("../paths-plugin");
19
20
  const resource_loader_1 = require("../resource_loader");
@@ -42,6 +43,7 @@ const PLUGIN_NAME = 'angular-compiler';
42
43
  class AngularWebpackPlugin {
43
44
  constructor(options = {}) {
44
45
  this.lazyRouteMap = {};
46
+ this.fileDependencies = new Map();
45
47
  this.requiredFilesToEmit = new Set();
46
48
  this.requiredFilesToEmitCache = new Map();
47
49
  this.fileEmitHistory = new Map();
@@ -120,6 +122,10 @@ class AngularWebpackPlugin {
120
122
  if (cache) {
121
123
  // Invalidate existing cache based on compiler file timestamps
122
124
  changedFiles = cache.invalidate(compiler.fileTimestamps, this.buildTimestamp);
125
+ // Invalidate file dependencies of changed files
126
+ for (const changedFile of changedFiles) {
127
+ this.fileDependencies.delete(paths_1.normalizePath(changedFile));
128
+ }
123
129
  }
124
130
  else {
125
131
  // Initialize a new cache
@@ -132,6 +138,8 @@ class AngularWebpackPlugin {
132
138
  this.buildTimestamp = Date.now();
133
139
  host_1.augmentHostWithCaching(host, cache);
134
140
  const moduleResolutionCache = ts.createModuleResolutionCache(host.getCurrentDirectory(), host.getCanonicalFileName.bind(host), compilerOptions);
141
+ // Setup source file dependency collection
142
+ host_1.augmentHostWithDependencyCollection(host, this.fileDependencies, moduleResolutionCache);
135
143
  // Setup on demand ngcc
136
144
  host_1.augmentHostWithNgcc(host, ngccProcessor, moduleResolutionCache);
137
145
  // Setup resource loading
@@ -144,7 +152,7 @@ class AngularWebpackPlugin {
144
152
  host_1.augmentHostWithSubstitutions(host, this.pluginOptions.substitutions);
145
153
  // Create the file emitter used by the webpack loader
146
154
  const { fileEmitter, builder, internalFiles } = this.pluginOptions.jitMode
147
- ? this.updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter)
155
+ ? this.updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter, changedFiles)
148
156
  : this.updateAotProgram(compilerOptions, rootNames, host, diagnosticsReporter, resourceLoader);
149
157
  const allProgramFiles = builder
150
158
  .getSourceFiles()
@@ -165,13 +173,11 @@ class AngularWebpackPlugin {
165
173
  }
166
174
  const currentUnused = new Set(allProgramFiles
167
175
  .filter((sourceFile) => !sourceFile.isDeclarationFile)
168
- .map((sourceFile) => sourceFile.fileName));
176
+ .map((sourceFile) => paths_1.normalizePath(sourceFile.fileName)));
169
177
  modules.forEach(({ resource }) => {
170
- const sourceFile = resource && builder.getSourceFile(resource);
171
- if (!sourceFile) {
172
- return;
178
+ if (resource) {
179
+ this.markResourceUsed(paths_1.normalizePath(resource), currentUnused);
173
180
  }
174
- builder.getAllDependencies(sourceFile).forEach((dep) => currentUnused.delete(dep));
175
181
  });
176
182
  for (const unused of currentUnused) {
177
183
  if (previousUnused && previousUnused.has(unused)) {
@@ -186,6 +192,19 @@ class AngularWebpackPlugin {
186
192
  compilation[symbol_1.AngularPluginSymbol] = fileEmitter;
187
193
  });
188
194
  }
195
+ markResourceUsed(normalizedResourcePath, currentUnused) {
196
+ if (!currentUnused.has(normalizedResourcePath)) {
197
+ return;
198
+ }
199
+ currentUnused.delete(normalizedResourcePath);
200
+ const dependencies = this.fileDependencies.get(normalizedResourcePath);
201
+ if (!dependencies) {
202
+ return;
203
+ }
204
+ for (const dependency of dependencies) {
205
+ this.markResourceUsed(paths_1.normalizePath(dependency), currentUnused);
206
+ }
207
+ }
189
208
  async rebuildRequiredFiles(modules, compilation, fileEmitter) {
190
209
  if (this.requiredFilesToEmit.size === 0) {
191
210
  return;
@@ -319,7 +338,7 @@ class AngularWebpackPlugin {
319
338
  if (!sourceFile.isDeclarationFile &&
320
339
  !ignoreForEmit.has(sourceFile) &&
321
340
  !angularCompiler.incrementalDriver.safeToSkipEmit(sourceFile)) {
322
- this.requiredFilesToEmit.add(sourceFile.fileName);
341
+ this.requiredFilesToEmit.add(paths_1.normalizePath(sourceFile.fileName));
323
342
  }
324
343
  }
325
344
  // NOTE: This can be removed once support for the deprecated lazy route string format is removed
@@ -328,7 +347,7 @@ class AngularWebpackPlugin {
328
347
  this.lazyRouteMap[routeKey] = lazyRoute.referencedModule.filePath;
329
348
  }
330
349
  return this.createFileEmitter(builder, transformation_1.mergeTransformers(angularCompiler.prepareEmit().transformers, transformers), getDependencies, (sourceFile) => {
331
- this.requiredFilesToEmit.delete(sourceFile.fileName);
350
+ this.requiredFilesToEmit.delete(paths_1.normalizePath(sourceFile.fileName));
332
351
  angularCompiler.incrementalDriver.recordSuccessfulEmit(sourceFile);
333
352
  });
334
353
  });
@@ -342,7 +361,7 @@ class AngularWebpackPlugin {
342
361
  internalFiles: ignoreForEmit,
343
362
  };
344
363
  }
345
- updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter) {
364
+ updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter, changedFiles) {
346
365
  const builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, compilerOptions, host, this.builder);
347
366
  // Save for next rebuild
348
367
  if (this.watchMode) {
@@ -357,37 +376,56 @@ class AngularWebpackPlugin {
357
376
  ];
358
377
  diagnosticsReporter(diagnostics);
359
378
  const transformers = transformation_1.createJitTransformers(builder, this.pluginOptions);
360
- // Required to support asynchronous resource loading
361
- // Must be done before listing lazy routes
362
- // NOTE: This can be removed once support for the deprecated lazy route string format is removed
363
- const angularProgram = new program_1.NgtscProgram(rootNames, compilerOptions, host, this.ngtscNextProgram);
364
- const angularCompiler = angularProgram.compiler;
365
- const pendingAnalysis = angularCompiler.analyzeAsync().then(() => {
366
- for (const lazyRoute of angularCompiler.listLazyRoutes()) {
367
- const [routeKey] = lazyRoute.route.split('#');
368
- this.lazyRouteMap[routeKey] = lazyRoute.referencedModule.filePath;
379
+ // Only do a full, expensive Angular compiler string lazy route analysis on the first build
380
+ // `changedFiles` will be undefined on a first build
381
+ if (!changedFiles) {
382
+ // Required to support asynchronous resource loading
383
+ // Must be done before listing lazy routes
384
+ // NOTE: This can be removed once support for the deprecated lazy route string format is removed
385
+ const angularProgram = new program_1.NgtscProgram(rootNames, compilerOptions, host);
386
+ const angularCompiler = angularProgram.compiler;
387
+ const pendingAnalysis = angularCompiler.analyzeAsync().then(() => {
388
+ for (const lazyRoute of angularCompiler.listLazyRoutes()) {
389
+ const [routeKey] = lazyRoute.route.split('#');
390
+ this.lazyRouteMap[routeKey] = lazyRoute.referencedModule.filePath;
391
+ }
392
+ return this.createFileEmitter(builder, transformers, () => []);
393
+ });
394
+ const analyzingFileEmitter = async (file) => {
395
+ const innerFileEmitter = await pendingAnalysis;
396
+ return innerFileEmitter(file);
397
+ };
398
+ return {
399
+ fileEmitter: analyzingFileEmitter,
400
+ builder,
401
+ internalFiles: undefined,
402
+ };
403
+ }
404
+ else {
405
+ // Update lazy route map for changed files using fast but less accurate method
406
+ for (const changedFile of changedFiles) {
407
+ if (!builder.getSourceFile(changedFile)) {
408
+ continue;
409
+ }
410
+ const routes = lazy_routes_1.findLazyRoutes(changedFile, host, builder.getProgram());
411
+ for (const [routeKey, filePath] of Object.entries(routes)) {
412
+ this.lazyRouteMap[routeKey] = filePath;
413
+ }
369
414
  }
370
- return this.createFileEmitter(builder, transformers, () => []);
371
- });
372
- const analyzingFileEmitter = async (file) => {
373
- const innerFileEmitter = await pendingAnalysis;
374
- return innerFileEmitter(file);
375
- };
376
- if (this.watchMode) {
377
- this.ngtscNextProgram = angularProgram;
415
+ return {
416
+ fileEmitter: this.createFileEmitter(builder, transformers, () => []),
417
+ builder,
418
+ internalFiles: undefined,
419
+ };
378
420
  }
379
- return {
380
- fileEmitter: analyzingFileEmitter,
381
- builder,
382
- internalFiles: undefined,
383
- };
384
421
  }
385
422
  createFileEmitter(program, transformers = {}, getExtraDependencies, onAfterEmit) {
386
423
  return async (file) => {
387
- if (this.requiredFilesToEmitCache.has(file)) {
388
- return this.requiredFilesToEmitCache.get(file);
424
+ const filePath = paths_1.normalizePath(file);
425
+ if (this.requiredFilesToEmitCache.has(filePath)) {
426
+ return this.requiredFilesToEmitCache.get(filePath);
389
427
  }
390
- const sourceFile = program.getSourceFile(file);
428
+ const sourceFile = program.getSourceFile(filePath);
391
429
  if (!sourceFile) {
392
430
  return undefined;
393
431
  }
@@ -406,10 +444,10 @@ class AngularWebpackPlugin {
406
444
  if (content !== undefined && this.watchMode) {
407
445
  // Capture emit history info for Angular rebuild analysis
408
446
  hash = hashContent(content);
409
- this.fileEmitHistory.set(file, { length: content.length, hash });
447
+ this.fileEmitHistory.set(filePath, { length: content.length, hash });
410
448
  }
411
449
  const dependencies = [
412
- ...program.getAllDependencies(sourceFile),
450
+ ...(this.fileDependencies.get(filePath) || []),
413
451
  ...getExtraDependencies(sourceFile),
414
452
  ].map(paths_1.externalizePath);
415
453
  return { content, map, dependencies, hash };