@ngtools/webpack 12.0.0-rc.2 → 12.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngtools/webpack",
3
- "version": "12.0.0-rc.2",
3
+ "version": "12.0.2",
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,7 +28,7 @@
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
  },
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;
package/src/ivy/plugin.js CHANGED
@@ -258,7 +258,7 @@ class AngularWebpackPlugin {
258
258
  this.requiredFilesToEmitCache.clear();
259
259
  }
260
260
  loadConfiguration(compilation) {
261
- const { options: compilerOptions, rootNames, errors } = compiler_cli_1.readConfiguration(this.pluginOptions.tsconfig, this.pluginOptions.compilerOptions);
261
+ const { options: compilerOptions, rootNames, errors, } = compiler_cli_1.readConfiguration(this.pluginOptions.tsconfig, this.pluginOptions.compilerOptions);
262
262
  compilerOptions.enableIvy = true;
263
263
  compilerOptions.noEmitOnError = false;
264
264
  compilerOptions.suppressOutputPathCheck = true;
@@ -287,39 +287,46 @@ class AngularWebpackPlugin {
287
287
  // The wrapped host inside NgtscProgram adds additional files that will not have versions.
288
288
  const typeScriptProgram = angularProgram.getTsProgram();
289
289
  host_1.augmentProgramWithVersioning(typeScriptProgram);
290
- const builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(typeScriptProgram, host, this.builder);
291
- // Save for next rebuild
290
+ let builder;
292
291
  if (this.watchMode) {
293
- this.builder = builder;
292
+ builder = this.builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(typeScriptProgram, host, this.builder);
294
293
  this.ngtscNextProgram = angularProgram;
295
294
  }
295
+ else {
296
+ // When not in watch mode, the startup cost of the incremental analysis can be avoided by
297
+ // using an abstract builder that only wraps a TypeScript program.
298
+ builder = ts.createAbstractBuilder(typeScriptProgram, host);
299
+ }
296
300
  // Update semantic diagnostics cache
297
301
  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);
302
+ // Analyze affected files when in watch mode for incremental type checking
303
+ if ('getSemanticDiagnosticsOfNextAffectedFile' in builder) {
304
+ // eslint-disable-next-line no-constant-condition
305
+ while (true) {
306
+ const result = builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
307
+ // If the affected file is a TTC shim, add the shim's original source file.
308
+ // This ensures that changes that affect TTC are typechecked even when the changes
309
+ // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes.
310
+ // For example, changing @Input property types of a directive used in another component's
311
+ // template.
312
+ if (ignoreForDiagnostics.has(sourceFile) &&
313
+ sourceFile.fileName.endsWith('.ngtypecheck.ts')) {
314
+ // This file name conversion relies on internal compiler logic and should be converted
315
+ // to an official method when available. 15 is length of `.ngtypecheck.ts`
316
+ const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts';
317
+ const originalSourceFile = builder.getSourceFile(originalFilename);
318
+ if (originalSourceFile) {
319
+ affectedFiles.add(originalSourceFile);
320
+ }
321
+ return true;
314
322
  }
315
- return true;
323
+ return false;
324
+ });
325
+ if (!result) {
326
+ break;
316
327
  }
317
- return false;
318
- });
319
- if (!result) {
320
- break;
328
+ affectedFiles.add(result.affected);
321
329
  }
322
- affectedFiles.add(result.affected);
323
330
  }
324
331
  // Collect non-semantic diagnostics
325
332
  const diagnostics = [
@@ -399,10 +406,14 @@ class AngularWebpackPlugin {
399
406
  };
400
407
  }
401
408
  updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter) {
402
- const builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, compilerOptions, host, this.builder);
403
- // Save for next rebuild
409
+ let builder;
404
410
  if (this.watchMode) {
405
- this.builder = builder;
411
+ builder = this.builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, compilerOptions, host, this.builder);
412
+ }
413
+ else {
414
+ // When not in watch mode, the startup cost of the incremental analysis can be avoided by
415
+ // using an abstract builder that only wraps a TypeScript program.
416
+ builder = ts.createAbstractBuilder(rootNames, compilerOptions, host);
406
417
  }
407
418
  const diagnostics = [
408
419
  ...builder.getOptionsDiagnostics(),
@@ -12,6 +12,7 @@ export declare class WebpackResourceLoader {
12
12
  private _reverseDependencies;
13
13
  private fileCache?;
14
14
  private inlineCache?;
15
+ private assetCache?;
15
16
  private modifiedResources;
16
17
  private outputPathCounter;
17
18
  constructor(shouldCache: boolean);
@@ -24,5 +25,5 @@ export declare class WebpackResourceLoader {
24
25
  private _compile;
25
26
  private _evaluate;
26
27
  get(filePath: string): Promise<string>;
27
- process(data: string, mimeType: string): Promise<string>;
28
+ process(data: string, mimeType: string, resourceType: 'template' | 'style', containingFile?: string): Promise<string>;
28
29
  }
@@ -22,23 +22,37 @@ class WebpackResourceLoader {
22
22
  if (shouldCache) {
23
23
  this.fileCache = new Map();
24
24
  this.inlineCache = new Map();
25
+ this.assetCache = new Map();
25
26
  }
26
27
  }
27
28
  update(parentCompilation, changedFiles) {
28
- var _a, _b;
29
+ var _a, _b, _c, _d, _e;
29
30
  this._parentCompilation = parentCompilation;
30
31
  // Update resource cache and modified resources
31
32
  this.modifiedResources.clear();
32
33
  if (changedFiles) {
33
34
  for (const changedFile of changedFiles) {
35
+ const changedFileNormalized = paths_1.normalizePath(changedFile);
36
+ (_a = this.assetCache) === null || _a === void 0 ? void 0 : _a.delete(changedFileNormalized);
34
37
  for (const affectedResource of this.getAffectedResources(changedFile)) {
35
- (_a = this.fileCache) === null || _a === void 0 ? void 0 : _a.delete(paths_1.normalizePath(affectedResource));
38
+ const affectedResourceNormalized = paths_1.normalizePath(affectedResource);
39
+ (_b = this.fileCache) === null || _b === void 0 ? void 0 : _b.delete(affectedResourceNormalized);
36
40
  this.modifiedResources.add(affectedResource);
41
+ for (const effectedDependencies of this.getResourceDependencies(affectedResourceNormalized)) {
42
+ (_c = this.assetCache) === null || _c === void 0 ? void 0 : _c.delete(paths_1.normalizePath(effectedDependencies));
43
+ }
37
44
  }
38
45
  }
39
46
  }
40
47
  else {
41
- (_b = this.fileCache) === null || _b === void 0 ? void 0 : _b.clear();
48
+ (_d = this.fileCache) === null || _d === void 0 ? void 0 : _d.clear();
49
+ (_e = this.assetCache) === null || _e === void 0 ? void 0 : _e.clear();
50
+ }
51
+ // Re-emit all assets for un-effected files
52
+ if (this.assetCache) {
53
+ for (const [, { name, source, info }] of this.assetCache) {
54
+ this._parentCompilation.emitAsset(name, source, info);
55
+ }
42
56
  }
43
57
  }
44
58
  clearParentCompilation() {
@@ -56,12 +70,12 @@ class WebpackResourceLoader {
56
70
  setAffectedResources(file, resources) {
57
71
  this._reverseDependencies.set(file, new Set(resources));
58
72
  }
59
- async _compile(filePath, data, mimeType) {
73
+ async _compile(filePath, data, mimeType, resourceType, hash, containingFile) {
60
74
  if (!this._parentCompilation) {
61
75
  throw new Error('WebpackResourceLoader cannot be used without parentCompilation');
62
76
  }
63
77
  // Create a special URL for reading the resource from memory
64
- const entry = data ? 'angular-resource://' : filePath;
78
+ const entry = data ? `angular-resource:${resourceType},${hash}` : 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: {
@@ -133,7 +148,7 @@ class WebpackResourceLoader {
133
148
  });
134
149
  return new Promise((resolve, reject) => {
135
150
  childCompiler.runAsChild((error, _, childCompilation) => {
136
- var _a;
151
+ var _a, _b;
137
152
  if (error) {
138
153
  reject(error);
139
154
  return;
@@ -148,12 +163,26 @@ class WebpackResourceLoader {
148
163
  const parent = childCompiler.parentCompilation;
149
164
  if (parent) {
150
165
  parent.children = parent.children.filter((child) => child !== childCompilation);
151
- parent.fileDependencies.addAll(childCompilation.fileDependencies);
166
+ for (const fileDependency of childCompilation.fileDependencies) {
167
+ if (data && containingFile && fileDependency.endsWith(entry)) {
168
+ // use containing file if the resource was inline
169
+ parent.fileDependencies.add(containingFile);
170
+ }
171
+ else {
172
+ parent.fileDependencies.add(fileDependency);
173
+ }
174
+ }
152
175
  parent.contextDependencies.addAll(childCompilation.contextDependencies);
153
176
  parent.missingDependencies.addAll(childCompilation.missingDependencies);
154
177
  parent.buildDependencies.addAll(childCompilation.buildDependencies);
155
178
  parent.warnings.push(...childCompilation.warnings);
156
179
  parent.errors.push(...childCompilation.errors);
180
+ for (const { info, name, source } of childCompilation.getAssets()) {
181
+ if (info.sourceFilename === undefined) {
182
+ throw new Error(`'${name}' asset info 'sourceFilename' is 'undefined'.`);
183
+ }
184
+ (_a = this.assetCache) === null || _a === void 0 ? void 0 : _a.set(info.sourceFilename, { info, name, source });
185
+ }
157
186
  }
158
187
  // Save the dependencies for this resource.
159
188
  if (filePath) {
@@ -177,7 +206,7 @@ class WebpackResourceLoader {
177
206
  }
178
207
  resolve({
179
208
  content: finalContent !== null && finalContent !== void 0 ? finalContent : '',
180
- success: ((_a = childCompilation.errors) === null || _a === void 0 ? void 0 : _a.length) === 0,
209
+ success: ((_b = childCompilation.errors) === null || _b === void 0 ? void 0 : _b.length) === 0,
181
210
  });
182
211
  });
183
212
  });
@@ -215,7 +244,7 @@ class WebpackResourceLoader {
215
244
  }
216
245
  return compilationResult.content;
217
246
  }
218
- async process(data, mimeType) {
247
+ async process(data, mimeType, resourceType, containingFile) {
219
248
  var _a;
220
249
  if (data.trim().length === 0) {
221
250
  return '';
@@ -223,7 +252,7 @@ class WebpackResourceLoader {
223
252
  const cacheKey = crypto_1.createHash('md5').update(data).digest('hex');
224
253
  let compilationResult = (_a = this.inlineCache) === null || _a === void 0 ? void 0 : _a.get(cacheKey);
225
254
  if (compilationResult === undefined) {
226
- compilationResult = await this._compile(undefined, data, mimeType);
255
+ compilationResult = await this._compile(undefined, data, mimeType, resourceType, cacheKey, containingFile);
227
256
  if (this.inlineCache && compilationResult.success) {
228
257
  this.inlineCache.set(cacheKey, compilationResult);
229
258
  }