@ngtools/webpack 11.2.4 → 11.2.8

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.4",
3
+ "version": "11.2.8",
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.4",
28
+ "@angular-devkit/core": "11.2.8",
29
29
  "enhanced-resolve": "5.7.0",
30
30
  "webpack-sources": "2.2.0"
31
31
  },
package/src/ivy/host.d.ts CHANGED
@@ -1,9 +1,24 @@
1
1
  import * as ts from 'typescript';
2
2
  import { NgccProcessor } from '../ngcc_processor';
3
- import { WebpackResourceLoader } from '../resource_loader';
4
- export declare function augmentHostWithResources(host: ts.CompilerHost, resourceLoader: WebpackResourceLoader, options?: {
3
+ import { ResourceLoader } from '../resource_loader';
4
+ export declare function augmentHostWithResources(host: ts.CompilerHost, resourceLoader: ResourceLoader, 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();
@@ -89,7 +91,9 @@ class AngularWebpackPlugin {
89
91
  });
90
92
  });
91
93
  let ngccProcessor;
92
- const resourceLoader = new resource_loader_1.WebpackResourceLoader();
94
+ const resourceLoader = this.pluginOptions.jitMode
95
+ ? new resource_loader_1.NoopResourceLoader()
96
+ : new resource_loader_1.WebpackResourceLoader();
93
97
  let previousUnused;
94
98
  compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (thisCompilation) => {
95
99
  var _a;
@@ -120,6 +124,10 @@ class AngularWebpackPlugin {
120
124
  if (cache) {
121
125
  // Invalidate existing cache based on compiler file timestamps
122
126
  changedFiles = cache.invalidate(compiler.fileTimestamps, this.buildTimestamp);
127
+ // Invalidate file dependencies of changed files
128
+ for (const changedFile of changedFiles) {
129
+ this.fileDependencies.delete(paths_1.normalizePath(changedFile));
130
+ }
123
131
  }
124
132
  else {
125
133
  // Initialize a new cache
@@ -132,19 +140,21 @@ class AngularWebpackPlugin {
132
140
  this.buildTimestamp = Date.now();
133
141
  host_1.augmentHostWithCaching(host, cache);
134
142
  const moduleResolutionCache = ts.createModuleResolutionCache(host.getCurrentDirectory(), host.getCanonicalFileName.bind(host), compilerOptions);
143
+ // Setup source file dependency collection
144
+ host_1.augmentHostWithDependencyCollection(host, this.fileDependencies, moduleResolutionCache);
135
145
  // Setup on demand ngcc
136
146
  host_1.augmentHostWithNgcc(host, ngccProcessor, moduleResolutionCache);
137
147
  // Setup resource loading
138
148
  resourceLoader.update(compilation, changedFiles);
139
149
  host_1.augmentHostWithResources(host, resourceLoader, {
140
- directTemplateLoading: this.pluginOptions.directTemplateLoading,
150
+ directTemplateLoading: !this.pluginOptions.jitMode && this.pluginOptions.directTemplateLoading,
141
151
  });
142
152
  // Setup source file adjustment options
143
153
  host_1.augmentHostWithReplacements(host, this.pluginOptions.fileReplacements, moduleResolutionCache);
144
154
  host_1.augmentHostWithSubstitutions(host, this.pluginOptions.substitutions);
145
155
  // Create the file emitter used by the webpack loader
146
156
  const { fileEmitter, builder, internalFiles } = this.pluginOptions.jitMode
147
- ? this.updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter)
157
+ ? this.updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter, changedFiles)
148
158
  : this.updateAotProgram(compilerOptions, rootNames, host, diagnosticsReporter, resourceLoader);
149
159
  const allProgramFiles = builder
150
160
  .getSourceFiles()
@@ -165,13 +175,11 @@ class AngularWebpackPlugin {
165
175
  }
166
176
  const currentUnused = new Set(allProgramFiles
167
177
  .filter((sourceFile) => !sourceFile.isDeclarationFile)
168
- .map((sourceFile) => sourceFile.fileName));
178
+ .map((sourceFile) => paths_1.normalizePath(sourceFile.fileName)));
169
179
  modules.forEach(({ resource }) => {
170
- const sourceFile = resource && builder.getSourceFile(resource);
171
- if (!sourceFile) {
172
- return;
180
+ if (resource) {
181
+ this.markResourceUsed(paths_1.normalizePath(resource), currentUnused);
173
182
  }
174
- builder.getAllDependencies(sourceFile).forEach((dep) => currentUnused.delete(dep));
175
183
  });
176
184
  for (const unused of currentUnused) {
177
185
  if (previousUnused && previousUnused.has(unused)) {
@@ -186,6 +194,19 @@ class AngularWebpackPlugin {
186
194
  compilation[symbol_1.AngularPluginSymbol] = fileEmitter;
187
195
  });
188
196
  }
197
+ markResourceUsed(normalizedResourcePath, currentUnused) {
198
+ if (!currentUnused.has(normalizedResourcePath)) {
199
+ return;
200
+ }
201
+ currentUnused.delete(normalizedResourcePath);
202
+ const dependencies = this.fileDependencies.get(normalizedResourcePath);
203
+ if (!dependencies) {
204
+ return;
205
+ }
206
+ for (const dependency of dependencies) {
207
+ this.markResourceUsed(paths_1.normalizePath(dependency), currentUnused);
208
+ }
209
+ }
189
210
  async rebuildRequiredFiles(modules, compilation, fileEmitter) {
190
211
  if (this.requiredFilesToEmit.size === 0) {
191
212
  return;
@@ -342,7 +363,7 @@ class AngularWebpackPlugin {
342
363
  internalFiles: ignoreForEmit,
343
364
  };
344
365
  }
345
- updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter) {
366
+ updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter, changedFiles) {
346
367
  const builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, compilerOptions, host, this.builder);
347
368
  // Save for next rebuild
348
369
  if (this.watchMode) {
@@ -357,30 +378,48 @@ class AngularWebpackPlugin {
357
378
  ];
358
379
  diagnosticsReporter(diagnostics);
359
380
  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;
381
+ // Only do a full, expensive Angular compiler string lazy route analysis on the first build
382
+ // `changedFiles` will be undefined on a first build
383
+ if (!changedFiles) {
384
+ // Required to support asynchronous resource loading
385
+ // Must be done before listing lazy routes
386
+ // NOTE: This can be removed once support for the deprecated lazy route string format is removed
387
+ const angularProgram = new program_1.NgtscProgram(rootNames, compilerOptions, host);
388
+ const angularCompiler = angularProgram.compiler;
389
+ const pendingAnalysis = angularCompiler.analyzeAsync().then(() => {
390
+ for (const lazyRoute of angularCompiler.listLazyRoutes()) {
391
+ const [routeKey] = lazyRoute.route.split('#');
392
+ this.lazyRouteMap[routeKey] = lazyRoute.referencedModule.filePath;
393
+ }
394
+ return this.createFileEmitter(builder, transformers, () => []);
395
+ });
396
+ const analyzingFileEmitter = async (file) => {
397
+ const innerFileEmitter = await pendingAnalysis;
398
+ return innerFileEmitter(file);
399
+ };
400
+ return {
401
+ fileEmitter: analyzingFileEmitter,
402
+ builder,
403
+ internalFiles: undefined,
404
+ };
405
+ }
406
+ else {
407
+ // Update lazy route map for changed files using fast but less accurate method
408
+ for (const changedFile of changedFiles) {
409
+ if (!builder.getSourceFile(changedFile)) {
410
+ continue;
411
+ }
412
+ const routes = lazy_routes_1.findLazyRoutes(changedFile, host, builder.getProgram());
413
+ for (const [routeKey, filePath] of Object.entries(routes)) {
414
+ this.lazyRouteMap[routeKey] = filePath;
415
+ }
369
416
  }
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;
417
+ return {
418
+ fileEmitter: this.createFileEmitter(builder, transformers, () => []),
419
+ builder,
420
+ internalFiles: undefined,
421
+ };
378
422
  }
379
- return {
380
- fileEmitter: analyzingFileEmitter,
381
- builder,
382
- internalFiles: undefined,
383
- };
384
423
  }
385
424
  createFileEmitter(program, transformers = {}, getExtraDependencies, onAfterEmit) {
386
425
  return async (file) => {
@@ -410,7 +449,7 @@ class AngularWebpackPlugin {
410
449
  this.fileEmitHistory.set(filePath, { length: content.length, hash });
411
450
  }
412
451
  const dependencies = [
413
- ...program.getAllDependencies(sourceFile),
452
+ ...(this.fileDependencies.get(filePath) || []),
414
453
  ...getExtraDependencies(sourceFile),
415
454
  ].map(paths_1.externalizePath);
416
455
  return { content, map, dependencies, hash };
@@ -1,3 +1,17 @@
1
+ export interface ResourceLoader {
2
+ get(file: string): Promise<string>;
3
+ getModifiedResourceFiles(): Set<string>;
4
+ getResourceDependencies(file: string): Iterable<string>;
5
+ setAffectedResources(file: string, resources: Iterable<string>): void;
6
+ update(parentCompilation: import('webpack').compilation.Compilation, changedFiles?: Iterable<string>): void;
7
+ }
8
+ export declare class NoopResourceLoader implements ResourceLoader {
9
+ get(): Promise<string>;
10
+ getModifiedResourceFiles(): Set<string>;
11
+ getResourceDependencies(): Iterable<string>;
12
+ setAffectedResources(): void;
13
+ update(): void;
14
+ }
1
15
  export declare class WebpackResourceLoader {
2
16
  private _parentCompilation;
3
17
  private _fileDependencies;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WebpackResourceLoader = void 0;
3
+ exports.WebpackResourceLoader = exports.NoopResourceLoader = void 0;
4
4
  /**
5
5
  * @license
6
6
  * Copyright Google Inc. All Rights Reserved.
@@ -19,6 +19,20 @@ const NodeTemplatePlugin = require('webpack/lib/node/NodeTemplatePlugin');
19
19
  const NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');
20
20
  const LibraryTemplatePlugin = require('webpack/lib/LibraryTemplatePlugin');
21
21
  const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');
22
+ class NoopResourceLoader {
23
+ async get() {
24
+ return '';
25
+ }
26
+ getModifiedResourceFiles() {
27
+ return new Set();
28
+ }
29
+ getResourceDependencies() {
30
+ return [];
31
+ }
32
+ setAffectedResources() { }
33
+ update() { }
34
+ }
35
+ exports.NoopResourceLoader = NoopResourceLoader;
22
36
  class WebpackResourceLoader {
23
37
  constructor() {
24
38
  this._fileDependencies = new Map();