@ngtools/webpack 12.0.0 → 12.0.4

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": "12.0.0",
3
+ "version": "12.0.4",
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",
@@ -28,12 +28,12 @@
28
28
  "enhanced-resolve": "5.7.0"
29
29
  },
30
30
  "peerDependencies": {
31
- "@angular/compiler-cli": "^12.0.0-next",
31
+ "@angular/compiler-cli": "^12.0.0",
32
32
  "typescript": "~4.2.3",
33
33
  "webpack": "^5.30.0"
34
34
  },
35
35
  "engines": {
36
- "node": "^12.14.1 || ^14.0.0",
36
+ "node": "^12.14.1 || >=14.0.0",
37
37
  "npm": "^6.11.0 || ^7.5.6",
38
38
  "yarn": ">= 1.13.0"
39
39
  }
package/src/ivy/host.js CHANGED
@@ -42,7 +42,7 @@ function augmentHostWithResources(host, resourceLoader, options = {}) {
42
42
  return null;
43
43
  }
44
44
  if (options.inlineStyleMimeType) {
45
- const content = await resourceLoader.process(data, options.inlineStyleMimeType);
45
+ const content = await resourceLoader.process(data, options.inlineStyleMimeType, context.type, context.containingFile);
46
46
  return { content };
47
47
  }
48
48
  return null;
@@ -6,7 +6,7 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  import { CompilerOptions } from '@angular/compiler-cli';
9
- import { Compiler } from 'webpack';
9
+ import type { Compiler } from 'webpack';
10
10
  export interface AngularWebpackPluginOptions {
11
11
  tsconfig: string;
12
12
  compilerOptions?: CompilerOptions;
package/src/ivy/plugin.js CHANGED
@@ -12,7 +12,6 @@ const compiler_cli_1 = require("@angular/compiler-cli");
12
12
  const program_1 = require("@angular/compiler-cli/src/ngtsc/program");
13
13
  const crypto_1 = require("crypto");
14
14
  const ts = require("typescript");
15
- const webpack_1 = require("webpack");
16
15
  const ngcc_processor_1 = require("../ngcc_processor");
17
16
  const paths_plugin_1 = require("../paths-plugin");
18
17
  const resource_loader_1 = require("../resource_loader");
@@ -64,9 +63,10 @@ class AngularWebpackPlugin {
64
63
  return this.pluginOptions;
65
64
  }
66
65
  apply(compiler) {
66
+ const { NormalModuleReplacementPlugin, util } = compiler.webpack;
67
67
  // Setup file replacements with webpack
68
68
  for (const [key, value] of Object.entries(this.pluginOptions.fileReplacements)) {
69
- new webpack_1.NormalModuleReplacementPlugin(new RegExp('^' + key.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&') + '$'), value).apply(compiler);
69
+ new NormalModuleReplacementPlugin(new RegExp('^' + key.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&') + '$'), value).apply(compiler);
70
70
  }
71
71
  // Set resolver options
72
72
  const pathsPlugin = new paths_plugin_1.TypeScriptPathsPlugin();
@@ -84,7 +84,7 @@ class AngularWebpackPlugin {
84
84
  (_b = resolveOptions.plugins) !== null && _b !== void 0 ? _b : (resolveOptions.plugins = []);
85
85
  resolveOptions.plugins.push(pathsPlugin);
86
86
  // https://github.com/webpack/webpack/issues/11635#issuecomment-707016779
87
- return webpack_1.util.cleverMerge(resolveOptions, { mainFields: [...ivyMainFields, '...'] });
87
+ return util.cleverMerge(resolveOptions, { mainFields: [...ivyMainFields, '...'] });
88
88
  });
89
89
  });
90
90
  let ngccProcessor;
@@ -171,8 +171,9 @@ class AngularWebpackPlugin {
171
171
  if (internalFiles === null || internalFiles === void 0 ? void 0 : internalFiles.has(sourceFile)) {
172
172
  continue;
173
173
  }
174
- // Ensure all program files are considered part of the compilation and will be watched
175
- compilation.fileDependencies.add(sourceFile.fileName);
174
+ // Ensure all program files are considered part of the compilation and will be watched.
175
+ // Webpack does not normalize paths. Therefore, we need to normalize the path with FS seperators.
176
+ compilation.fileDependencies.add(paths_1.externalizePath(sourceFile.fileName));
176
177
  // Add all non-declaration files to the initial set of unused files. The set will be
177
178
  // analyzed and pruned after all Webpack modules are finished building.
178
179
  if (!sourceFile.isDeclarationFile) {
@@ -258,7 +259,7 @@ class AngularWebpackPlugin {
258
259
  this.requiredFilesToEmitCache.clear();
259
260
  }
260
261
  loadConfiguration(compilation) {
261
- const { options: compilerOptions, rootNames, errors } = compiler_cli_1.readConfiguration(this.pluginOptions.tsconfig, this.pluginOptions.compilerOptions);
262
+ const { options: compilerOptions, rootNames, errors, } = compiler_cli_1.readConfiguration(this.pluginOptions.tsconfig, this.pluginOptions.compilerOptions);
262
263
  compilerOptions.enableIvy = true;
263
264
  compilerOptions.noEmitOnError = false;
264
265
  compilerOptions.suppressOutputPathCheck = true;
@@ -287,39 +288,46 @@ class AngularWebpackPlugin {
287
288
  // The wrapped host inside NgtscProgram adds additional files that will not have versions.
288
289
  const typeScriptProgram = angularProgram.getTsProgram();
289
290
  host_1.augmentProgramWithVersioning(typeScriptProgram);
290
- const builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(typeScriptProgram, host, this.builder);
291
- // Save for next rebuild
291
+ let builder;
292
292
  if (this.watchMode) {
293
- this.builder = builder;
293
+ builder = this.builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(typeScriptProgram, host, this.builder);
294
294
  this.ngtscNextProgram = angularProgram;
295
295
  }
296
+ else {
297
+ // When not in watch mode, the startup cost of the incremental analysis can be avoided by
298
+ // using an abstract builder that only wraps a TypeScript program.
299
+ builder = ts.createAbstractBuilder(typeScriptProgram, host);
300
+ }
296
301
  // Update semantic diagnostics cache
297
302
  const affectedFiles = new Set();
298
- // eslint-disable-next-line no-constant-condition
299
- while (true) {
300
- const result = builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
301
- // If the affected file is a TTC shim, add the shim's original source file.
302
- // This ensures that changes that affect TTC are typechecked even when the changes
303
- // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes.
304
- // For example, changing @Input property types of a directive used in another component's
305
- // template.
306
- if (ignoreForDiagnostics.has(sourceFile) &&
307
- sourceFile.fileName.endsWith('.ngtypecheck.ts')) {
308
- // This file name conversion relies on internal compiler logic and should be converted
309
- // to an official method when available. 15 is length of `.ngtypecheck.ts`
310
- const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts';
311
- const originalSourceFile = builder.getSourceFile(originalFilename);
312
- if (originalSourceFile) {
313
- affectedFiles.add(originalSourceFile);
303
+ // Analyze affected files when in watch mode for incremental type checking
304
+ if ('getSemanticDiagnosticsOfNextAffectedFile' in builder) {
305
+ // eslint-disable-next-line no-constant-condition
306
+ while (true) {
307
+ const result = builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
308
+ // If the affected file is a TTC shim, add the shim's original source file.
309
+ // This ensures that changes that affect TTC are typechecked even when the changes
310
+ // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes.
311
+ // For example, changing @Input property types of a directive used in another component's
312
+ // template.
313
+ if (ignoreForDiagnostics.has(sourceFile) &&
314
+ sourceFile.fileName.endsWith('.ngtypecheck.ts')) {
315
+ // This file name conversion relies on internal compiler logic and should be converted
316
+ // to an official method when available. 15 is length of `.ngtypecheck.ts`
317
+ const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts';
318
+ const originalSourceFile = builder.getSourceFile(originalFilename);
319
+ if (originalSourceFile) {
320
+ affectedFiles.add(originalSourceFile);
321
+ }
322
+ return true;
314
323
  }
315
- return true;
324
+ return false;
325
+ });
326
+ if (!result) {
327
+ break;
316
328
  }
317
- return false;
318
- });
319
- if (!result) {
320
- break;
329
+ affectedFiles.add(result.affected);
321
330
  }
322
- affectedFiles.add(result.affected);
323
331
  }
324
332
  // Collect non-semantic diagnostics
325
333
  const diagnostics = [
@@ -399,10 +407,14 @@ class AngularWebpackPlugin {
399
407
  };
400
408
  }
401
409
  updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter) {
402
- const builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, compilerOptions, host, this.builder);
403
- // Save for next rebuild
410
+ let builder;
404
411
  if (this.watchMode) {
405
- this.builder = builder;
412
+ builder = this.builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, compilerOptions, host, this.builder);
413
+ }
414
+ else {
415
+ // When not in watch mode, the startup cost of the incremental analysis can be avoided by
416
+ // using an abstract builder that only wraps a TypeScript program.
417
+ builder = ts.createAbstractBuilder(rootNames, compilerOptions, host);
406
418
  }
407
419
  const diagnostics = [
408
420
  ...builder.getOptionsDiagnostics(),
@@ -5,13 +5,13 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
- import { Compilation } from 'webpack';
8
+ import type { Compilation } from 'webpack';
9
9
  export declare class WebpackResourceLoader {
10
10
  private _parentCompilation?;
11
11
  private _fileDependencies;
12
12
  private _reverseDependencies;
13
13
  private fileCache?;
14
- private inlineCache?;
14
+ private assetCache?;
15
15
  private modifiedResources;
16
16
  private outputPathCounter;
17
17
  constructor(shouldCache: boolean);
@@ -24,5 +24,5 @@ export declare class WebpackResourceLoader {
24
24
  private _compile;
25
25
  private _evaluate;
26
26
  get(filePath: string): Promise<string>;
27
- process(data: string, mimeType: string): Promise<string>;
27
+ process(data: string, mimeType: string, resourceType: 'template' | 'style', containingFile?: string): Promise<string>;
28
28
  }
@@ -11,7 +11,6 @@ exports.WebpackResourceLoader = void 0;
11
11
  const crypto_1 = require("crypto");
12
12
  const path = require("path");
13
13
  const vm = require("vm");
14
- const webpack_1 = require("webpack");
15
14
  const paths_1 = require("./ivy/paths");
16
15
  class WebpackResourceLoader {
17
16
  constructor(shouldCache) {
@@ -21,24 +20,37 @@ class WebpackResourceLoader {
21
20
  this.outputPathCounter = 1;
22
21
  if (shouldCache) {
23
22
  this.fileCache = new Map();
24
- this.inlineCache = new Map();
23
+ this.assetCache = new Map();
25
24
  }
26
25
  }
27
26
  update(parentCompilation, changedFiles) {
28
- var _a, _b;
27
+ var _a, _b, _c, _d, _e;
29
28
  this._parentCompilation = parentCompilation;
30
29
  // Update resource cache and modified resources
31
30
  this.modifiedResources.clear();
32
31
  if (changedFiles) {
33
32
  for (const changedFile of changedFiles) {
33
+ const changedFileNormalized = paths_1.normalizePath(changedFile);
34
+ (_a = this.assetCache) === null || _a === void 0 ? void 0 : _a.delete(changedFileNormalized);
34
35
  for (const affectedResource of this.getAffectedResources(changedFile)) {
35
- (_a = this.fileCache) === null || _a === void 0 ? void 0 : _a.delete(paths_1.normalizePath(affectedResource));
36
+ const affectedResourceNormalized = paths_1.normalizePath(affectedResource);
37
+ (_b = this.fileCache) === null || _b === void 0 ? void 0 : _b.delete(affectedResourceNormalized);
36
38
  this.modifiedResources.add(affectedResource);
39
+ for (const effectedDependencies of this.getResourceDependencies(affectedResourceNormalized)) {
40
+ (_c = this.assetCache) === null || _c === void 0 ? void 0 : _c.delete(paths_1.normalizePath(effectedDependencies));
41
+ }
37
42
  }
38
43
  }
39
44
  }
40
45
  else {
41
- (_b = this.fileCache) === null || _b === void 0 ? void 0 : _b.clear();
46
+ (_d = this.fileCache) === null || _d === void 0 ? void 0 : _d.clear();
47
+ (_e = this.assetCache) === null || _e === void 0 ? void 0 : _e.clear();
48
+ }
49
+ // Re-emit all assets for un-effected files
50
+ if (this.assetCache) {
51
+ for (const [, { name, source, info }] of this.assetCache) {
52
+ this._parentCompilation.emitAsset(name, source, info);
53
+ }
42
54
  }
43
55
  }
44
56
  clearParentCompilation() {
@@ -56,12 +68,14 @@ class WebpackResourceLoader {
56
68
  setAffectedResources(file, resources) {
57
69
  this._reverseDependencies.set(file, new Set(resources));
58
70
  }
59
- async _compile(filePath, data, mimeType) {
71
+ async _compile(filePath, data, mimeType, resourceType, containingFile) {
60
72
  if (!this._parentCompilation) {
61
73
  throw new Error('WebpackResourceLoader cannot be used without parentCompilation');
62
74
  }
63
75
  // Create a special URL for reading the resource from memory
64
- const entry = data ? 'angular-resource://' : filePath;
76
+ const entry = data
77
+ ? `angular-resource:${resourceType},${crypto_1.createHash('md5').update(data).digest('hex')}`
78
+ : filePath;
65
79
  if (!entry) {
66
80
  throw new Error(`"filePath" or "data" must be specified.`);
67
81
  }
@@ -69,7 +83,8 @@ class WebpackResourceLoader {
69
83
  if (filePath === null || filePath === void 0 ? void 0 : filePath.match(/\.[jt]s$/)) {
70
84
  throw new Error(`Cannot use a JavaScript or TypeScript file (${filePath}) in a component's styleUrls or templateUrl.`);
71
85
  }
72
- const outputFilePath = filePath || `angular-resource-output-${this.outputPathCounter++}.css`;
86
+ const outputFilePath = filePath ||
87
+ `${containingFile}-angular-inline--${this.outputPathCounter++}.${resourceType === 'template' ? 'html' : 'css'}`;
73
88
  const outputOptions = {
74
89
  filename: outputFilePath,
75
90
  library: {
@@ -77,12 +92,13 @@ class WebpackResourceLoader {
77
92
  name: 'resource',
78
93
  },
79
94
  };
80
- const context = this._parentCompilation.compiler.context;
95
+ const { context, webpack } = this._parentCompilation.compiler;
96
+ const { EntryPlugin, NormalModule, library, node, sources } = webpack;
81
97
  const childCompiler = this._parentCompilation.createChildCompiler('angular-compiler:resource', outputOptions, [
82
- new webpack_1.node.NodeTemplatePlugin(outputOptions),
83
- new webpack_1.node.NodeTargetPlugin(),
84
- new webpack_1.EntryPlugin(context, entry, { name: 'resource' }),
85
- new webpack_1.library.EnableLibraryPlugin('var'),
98
+ new node.NodeTemplatePlugin(outputOptions),
99
+ new node.NodeTargetPlugin(),
100
+ new EntryPlugin(context, entry, { name: 'resource' }),
101
+ new library.EnableLibraryPlugin('var'),
86
102
  ]);
87
103
  childCompiler.hooks.thisCompilation.tap('angular-compiler', (compilation, { normalModuleFactory }) => {
88
104
  // If no data is provided, the resource will be read from the filesystem
@@ -99,7 +115,7 @@ class WebpackResourceLoader {
99
115
  }
100
116
  return true;
101
117
  });
102
- webpack_1.NormalModule.getCompilationHooks(compilation)
118
+ NormalModule.getCompilationHooks(compilation)
103
119
  .readResourceForScheme.for('angular-resource')
104
120
  .tap('angular-compiler', () => data);
105
121
  }
@@ -111,7 +127,7 @@ class WebpackResourceLoader {
111
127
  try {
112
128
  const output = this._evaluate(outputFilePath, asset.source().toString());
113
129
  if (typeof output === 'string') {
114
- compilation.assets[outputFilePath] = new webpack_1.sources.RawSource(output);
130
+ compilation.assets[outputFilePath] = new sources.RawSource(output);
115
131
  }
116
132
  }
117
133
  catch (error) {
@@ -123,7 +139,7 @@ class WebpackResourceLoader {
123
139
  let finalContent;
124
140
  let finalMap;
125
141
  childCompiler.hooks.compilation.tap('angular-compiler', (childCompilation) => {
126
- childCompilation.hooks.processAssets.tap({ name: 'angular-compiler', stage: webpack_1.Compilation.PROCESS_ASSETS_STAGE_REPORT }, () => {
142
+ childCompilation.hooks.processAssets.tap({ name: 'angular-compiler', stage: webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT }, () => {
127
143
  var _a, _b;
128
144
  finalContent = (_a = childCompilation.assets[outputFilePath]) === null || _a === void 0 ? void 0 : _a.source().toString();
129
145
  finalMap = (_b = childCompilation.assets[outputFilePath + '.map']) === null || _b === void 0 ? void 0 : _b.source().toString();
@@ -133,7 +149,7 @@ class WebpackResourceLoader {
133
149
  });
134
150
  return new Promise((resolve, reject) => {
135
151
  childCompiler.runAsChild((error, _, childCompilation) => {
136
- var _a;
152
+ var _a, _b;
137
153
  if (error) {
138
154
  reject(error);
139
155
  return;
@@ -148,12 +164,26 @@ class WebpackResourceLoader {
148
164
  const parent = childCompiler.parentCompilation;
149
165
  if (parent) {
150
166
  parent.children = parent.children.filter((child) => child !== childCompilation);
151
- parent.fileDependencies.addAll(childCompilation.fileDependencies);
167
+ for (const fileDependency of childCompilation.fileDependencies) {
168
+ if (data && containingFile && fileDependency.endsWith(entry)) {
169
+ // use containing file if the resource was inline
170
+ parent.fileDependencies.add(containingFile);
171
+ }
172
+ else {
173
+ parent.fileDependencies.add(fileDependency);
174
+ }
175
+ }
152
176
  parent.contextDependencies.addAll(childCompilation.contextDependencies);
153
177
  parent.missingDependencies.addAll(childCompilation.missingDependencies);
154
178
  parent.buildDependencies.addAll(childCompilation.buildDependencies);
155
179
  parent.warnings.push(...childCompilation.warnings);
156
180
  parent.errors.push(...childCompilation.errors);
181
+ for (const { info, name, source } of childCompilation.getAssets()) {
182
+ if (info.sourceFilename === undefined) {
183
+ throw new Error(`'${name}' asset info 'sourceFilename' is 'undefined'.`);
184
+ }
185
+ (_a = this.assetCache) === null || _a === void 0 ? void 0 : _a.set(info.sourceFilename, { info, name, source });
186
+ }
157
187
  }
158
188
  // Save the dependencies for this resource.
159
189
  if (filePath) {
@@ -177,7 +207,7 @@ class WebpackResourceLoader {
177
207
  }
178
208
  resolve({
179
209
  content: finalContent !== null && finalContent !== void 0 ? finalContent : '',
180
- success: ((_a = childCompilation.errors) === null || _a === void 0 ? void 0 : _a.length) === 0,
210
+ success: ((_b = childCompilation.errors) === null || _b === void 0 ? void 0 : _b.length) === 0,
181
211
  });
182
212
  });
183
213
  });
@@ -215,19 +245,11 @@ class WebpackResourceLoader {
215
245
  }
216
246
  return compilationResult.content;
217
247
  }
218
- async process(data, mimeType) {
219
- var _a;
248
+ async process(data, mimeType, resourceType, containingFile) {
220
249
  if (data.trim().length === 0) {
221
250
  return '';
222
251
  }
223
- const cacheKey = crypto_1.createHash('md5').update(data).digest('hex');
224
- let compilationResult = (_a = this.inlineCache) === null || _a === void 0 ? void 0 : _a.get(cacheKey);
225
- if (compilationResult === undefined) {
226
- compilationResult = await this._compile(undefined, data, mimeType);
227
- if (this.inlineCache && compilationResult.success) {
228
- this.inlineCache.set(cacheKey, compilationResult);
229
- }
230
- }
252
+ const compilationResult = await this._compile(undefined, data, mimeType, resourceType, containingFile);
231
253
  return compilationResult.content;
232
254
  }
233
255
  }
@@ -5,6 +5,6 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
- import { Compilation } from 'webpack';
8
+ import type { Compilation } from 'webpack';
9
9
  export declare function addWarning(compilation: Compilation, message: string): void;
10
10
  export declare function addError(compilation: Compilation, message: string): void;
@@ -8,12 +8,11 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.addError = exports.addWarning = void 0;
11
- const webpack_1 = require("webpack");
12
11
  function addWarning(compilation, message) {
13
- compilation.warnings.push(new webpack_1.WebpackError(message));
12
+ compilation.warnings.push(new compilation.compiler.webpack.WebpackError(message));
14
13
  }
15
14
  exports.addWarning = addWarning;
16
15
  function addError(compilation, message) {
17
- compilation.errors.push(new webpack_1.WebpackError(message));
16
+ compilation.errors.push(new compilation.compiler.webpack.WebpackError(message));
18
17
  }
19
18
  exports.addError = addError;