@ngtools/webpack 8.0.2 → 8.0.6

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": "8.0.2",
3
+ "version": "8.0.6",
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": "8.0.2",
28
+ "@angular-devkit/core": "8.0.6",
29
29
  "enhanced-resolve": "4.1.0",
30
30
  "rxjs": "6.4.0",
31
31
  "tree-kill": "1.2.1",
@@ -21,6 +21,7 @@ export declare class AngularCompilerPlugin {
21
21
  private _platform;
22
22
  private _JitMode;
23
23
  private _emitSkipped;
24
+ private _hadFullJitEmit;
24
25
  private _changedFileExtensions;
25
26
  private _firstRun;
26
27
  private _donePromise;
@@ -25,6 +25,7 @@ const paths_plugin_1 = require("./paths-plugin");
25
25
  const resource_loader_1 = require("./resource_loader");
26
26
  const transformers_1 = require("./transformers");
27
27
  const ast_helpers_1 = require("./transformers/ast_helpers");
28
+ const ctor_parameters_1 = require("./transformers/ctor-parameters");
28
29
  const type_checker_1 = require("./type_checker");
29
30
  const type_checker_messages_1 = require("./type_checker_messages");
30
31
  const utils_1 = require("./utils");
@@ -626,7 +627,6 @@ class AngularCompilerPlugin {
626
627
  }
627
628
  async _make(compilation) {
628
629
  benchmark_1.time('AngularCompilerPlugin._make');
629
- this._emitSkipped = true;
630
630
  // tslint:disable-next-line:no-any
631
631
  if (compilation._ngToolsWebpackPluginInstance) {
632
632
  throw new Error('An @ngtools/webpack plugin already exist for this compilation.');
@@ -681,6 +681,9 @@ class AngularCompilerPlugin {
681
681
  if (this._JitMode) {
682
682
  // Replace resources in JIT.
683
683
  this._transformers.push(transformers_1.replaceResources(isAppPath, getTypeChecker, this._options.directTemplateLoading));
684
+ // Downlevel constructor parameters for DI support
685
+ // This is required to support forwardRef in ES2015 due to TDZ issues
686
+ this._transformers.push(ctor_parameters_1.downlevelConstructorParameters(getTypeChecker));
684
687
  }
685
688
  else {
686
689
  // Remove unneeded angular decorators.
@@ -919,8 +922,9 @@ class AngularCompilerPlugin {
919
922
  }
920
923
  allDiagnostics.push(...gather_diagnostics_1.gatherDiagnostics(tsProgram, this._JitMode, 'AngularCompilerPlugin._emit.ts', diagMode));
921
924
  if (!gather_diagnostics_1.hasErrors(allDiagnostics)) {
922
- if (this._firstRun || changedTsFiles.size > 20 || this._emitSkipped) {
925
+ if (this._firstRun || changedTsFiles.size > 20 || !this._hadFullJitEmit) {
923
926
  emitResult = tsProgram.emit(undefined, undefined, undefined, undefined, { before: this._transformers });
927
+ this._hadFullJitEmit = !emitResult.emitSkipped;
924
928
  allDiagnostics.push(...emitResult.diagnostics);
925
929
  }
926
930
  else {
@@ -7,6 +7,7 @@
7
7
  */
8
8
  /// <reference types="node" />
9
9
  import { logging, virtualFs } from '@angular-devkit/core';
10
+ import { CompilerOptions } from '@angular/compiler-cli';
10
11
  import * as fs from 'fs';
11
12
  import * as ts from 'typescript';
12
13
  export declare enum PLATFORM {
@@ -48,7 +49,7 @@ export interface AngularCompilerPluginOptions {
48
49
  };
49
50
  additionalLazyModuleResources?: string[];
50
51
  contextElementDependencyConstructor?: ContextElementDependencyConstructor;
51
- compilerOptions?: ts.CompilerOptions;
52
+ compilerOptions?: CompilerOptions;
52
53
  host?: virtualFs.Host<fs.Stats>;
53
54
  platformTransformers?: ts.TransformerFactory<ts.SourceFile>[];
54
55
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google Inc. All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.io/license
7
+ */
8
+ import * as ts from 'typescript';
9
+ export declare function downlevelConstructorParameters(getTypeChecker: () => ts.TypeChecker): ts.TransformerFactory<ts.SourceFile>;
10
+ /**
11
+ * Transformer factory for the decorator downlevel transformer. See fileoverview for details.
12
+ */
13
+ export declare function decoratorDownlevelTransformer(typeChecker: ts.TypeChecker, diagnostics: ts.Diagnostic[]): (context: ts.TransformationContext) => ts.Transformer<ts.SourceFile>;
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * @license
5
+ * Copyright Google Inc. All Rights Reserved.
6
+ *
7
+ * Use of this source code is governed by an MIT-style license that can be
8
+ * found in the LICENSE file at https://angular.io/license
9
+ */
10
+ const ts = require("typescript");
11
+ function downlevelConstructorParameters(getTypeChecker) {
12
+ return (context) => {
13
+ const transformer = decoratorDownlevelTransformer(getTypeChecker(), []);
14
+ return transformer(context);
15
+ };
16
+ }
17
+ exports.downlevelConstructorParameters = downlevelConstructorParameters;
18
+ // The following is sourced from tsickle with local modifications
19
+ // Only the creation of `ctorParameters` is retained
20
+ // tslint:disable-next-line:max-line-length
21
+ // https://github.com/angular/tsickle/blob/0ceb7d6bc47f6945a6c4c09689f1388eb48f5c07/src/decorator_downlevel_transformer.ts
22
+ //
23
+ /**
24
+ * Extracts the type of the decorator (the function or expression invoked), as well as all the
25
+ * arguments passed to the decorator. Returns an AST with the form:
26
+ *
27
+ * // For @decorator(arg1, arg2)
28
+ * { type: decorator, args: [arg1, arg2] }
29
+ */
30
+ function extractMetadataFromSingleDecorator(decorator, diagnostics) {
31
+ const metadataProperties = [];
32
+ const expr = decorator.expression;
33
+ switch (expr.kind) {
34
+ case ts.SyntaxKind.Identifier:
35
+ // The decorator was a plain @Foo.
36
+ metadataProperties.push(ts.createPropertyAssignment('type', expr));
37
+ break;
38
+ case ts.SyntaxKind.CallExpression:
39
+ // The decorator was a call, like @Foo(bar).
40
+ const call = expr;
41
+ metadataProperties.push(ts.createPropertyAssignment('type', call.expression));
42
+ if (call.arguments.length) {
43
+ const args = [];
44
+ for (const arg of call.arguments) {
45
+ args.push(arg);
46
+ }
47
+ const argsArrayLiteral = ts.createArrayLiteral(args);
48
+ argsArrayLiteral.elements.hasTrailingComma = true;
49
+ metadataProperties.push(ts.createPropertyAssignment('args', argsArrayLiteral));
50
+ }
51
+ break;
52
+ default:
53
+ diagnostics.push({
54
+ file: decorator.getSourceFile(),
55
+ start: decorator.getStart(),
56
+ length: decorator.getEnd() - decorator.getStart(),
57
+ messageText: `${ts.SyntaxKind[decorator.kind]} not implemented in gathering decorator metadata`,
58
+ category: ts.DiagnosticCategory.Error,
59
+ code: 0,
60
+ });
61
+ break;
62
+ }
63
+ return ts.createObjectLiteral(metadataProperties);
64
+ }
65
+ /**
66
+ * createCtorParametersClassProperty creates a static 'ctorParameters' property containing
67
+ * downleveled decorator information.
68
+ *
69
+ * The property contains an arrow function that returns an array of object literals of the shape:
70
+ * static ctorParameters = () => [{
71
+ * type: SomeClass|undefined, // the type of the param that's decorated, if it's a value.
72
+ * decorators: [{
73
+ * type: DecoratorFn, // the type of the decorator that's invoked.
74
+ * args: [ARGS], // the arguments passed to the decorator.
75
+ * }]
76
+ * }];
77
+ */
78
+ function createCtorParametersClassProperty(diagnostics, entityNameToExpression, ctorParameters, typeChecker) {
79
+ const params = [];
80
+ for (const ctorParam of ctorParameters) {
81
+ if (!ctorParam.type && ctorParam.decorators.length === 0) {
82
+ params.push(ts.createNull());
83
+ continue;
84
+ }
85
+ const paramType = ctorParam.type
86
+ ? typeReferenceToExpression(entityNameToExpression, ctorParam.type, typeChecker)
87
+ : undefined;
88
+ const members = [
89
+ ts.createPropertyAssignment('type', paramType || ts.createIdentifier('undefined')),
90
+ ];
91
+ const decorators = [];
92
+ for (const deco of ctorParam.decorators) {
93
+ decorators.push(extractMetadataFromSingleDecorator(deco, diagnostics));
94
+ }
95
+ if (decorators.length) {
96
+ members.push(ts.createPropertyAssignment('decorators', ts.createArrayLiteral(decorators)));
97
+ }
98
+ params.push(ts.createObjectLiteral(members));
99
+ }
100
+ const initializer = ts.createArrowFunction(undefined, undefined, [], undefined, ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createArrayLiteral(params, true));
101
+ const ctorProp = ts.createProperty(undefined, [ts.createToken(ts.SyntaxKind.StaticKeyword)], 'ctorParameters', undefined, undefined, initializer);
102
+ return ctorProp;
103
+ }
104
+ /**
105
+ * Returns an expression representing the (potentially) value part for the given node.
106
+ *
107
+ * This is a partial re-implementation of TypeScript's serializeTypeReferenceNode. This is a
108
+ * workaround for https://github.com/Microsoft/TypeScript/issues/17516 (serializeTypeReferenceNode
109
+ * not being exposed). In practice this implementation is sufficient for Angular's use of type
110
+ * metadata.
111
+ */
112
+ function typeReferenceToExpression(entityNameToExpression, node, typeChecker) {
113
+ let kind = node.kind;
114
+ if (ts.isLiteralTypeNode(node)) {
115
+ // Treat literal types like their base type (boolean, string, number).
116
+ kind = node.literal.kind;
117
+ }
118
+ switch (kind) {
119
+ case ts.SyntaxKind.FunctionType:
120
+ case ts.SyntaxKind.ConstructorType:
121
+ return ts.createIdentifier('Function');
122
+ case ts.SyntaxKind.ArrayType:
123
+ case ts.SyntaxKind.TupleType:
124
+ return ts.createIdentifier('Array');
125
+ case ts.SyntaxKind.TypePredicate:
126
+ case ts.SyntaxKind.TrueKeyword:
127
+ case ts.SyntaxKind.FalseKeyword:
128
+ case ts.SyntaxKind.BooleanKeyword:
129
+ return ts.createIdentifier('Boolean');
130
+ case ts.SyntaxKind.StringLiteral:
131
+ case ts.SyntaxKind.StringKeyword:
132
+ return ts.createIdentifier('String');
133
+ case ts.SyntaxKind.ObjectKeyword:
134
+ return ts.createIdentifier('Object');
135
+ case ts.SyntaxKind.NumberKeyword:
136
+ case ts.SyntaxKind.NumericLiteral:
137
+ return ts.createIdentifier('Number');
138
+ case ts.SyntaxKind.TypeReference:
139
+ const typeRef = node;
140
+ let typeSymbol = typeChecker.getSymbolAtLocation(typeRef.typeName);
141
+ if (typeSymbol && typeSymbol.flags & ts.SymbolFlags.Alias) {
142
+ typeSymbol = typeChecker.getAliasedSymbol(typeSymbol);
143
+ }
144
+ if (!typeSymbol || !(typeSymbol.flags & ts.SymbolFlags.Value)) {
145
+ return undefined;
146
+ }
147
+ const type = typeChecker.getTypeOfSymbolAtLocation(typeSymbol, typeRef);
148
+ if (!type || typeChecker.getSignaturesOfType(type, ts.SignatureKind.Construct).length === 0) {
149
+ return undefined;
150
+ }
151
+ // Ignore any generic types, just return the base type.
152
+ return entityNameToExpression(typeRef.typeName);
153
+ default:
154
+ return undefined;
155
+ }
156
+ }
157
+ /**
158
+ * Transformer factory for the decorator downlevel transformer. See fileoverview for details.
159
+ */
160
+ function decoratorDownlevelTransformer(typeChecker, diagnostics) {
161
+ return (context) => {
162
+ const parameterTypeSymbols = new Set();
163
+ /**
164
+ * Converts an EntityName (from a type annotation) to an expression (accessing a value).
165
+ *
166
+ * For a given ts.EntityName, this walks depth first to find the leftmost ts.Identifier, then
167
+ * converts the path into property accesses.
168
+ *
169
+ */
170
+ function entityNameToExpression(name) {
171
+ if (ts.isIdentifier(name)) {
172
+ const typeSymbol = typeChecker.getSymbolAtLocation(name);
173
+ if (typeSymbol) {
174
+ parameterTypeSymbols.add(typeSymbol);
175
+ }
176
+ // Based on TS's strategy to allow the checker to reach this identifier
177
+ // tslint:disable-next-line:max-line-length
178
+ // https://github.com/microsoft/TypeScript/blob/7f47a08a5e9874f0f97a667bd81eebddec61247c/src/compiler/transformers/ts.ts#L2093
179
+ const exp = ts.getMutableClone(name);
180
+ exp.flags &= ~ts.NodeFlags.Synthesized;
181
+ exp.original = undefined;
182
+ exp.parent = ts.getParseTreeNode(name.getSourceFile());
183
+ return exp;
184
+ }
185
+ const ref = entityNameToExpression(name.left);
186
+ if (!ref) {
187
+ return undefined;
188
+ }
189
+ return ts.createPropertyAccess(ref, name.right);
190
+ }
191
+ function classMemberVisitor(node) {
192
+ if (!ts.isConstructorDeclaration(node) || !node.body) {
193
+ return visitor(node);
194
+ }
195
+ const parametersInfo = [];
196
+ for (const param of node.parameters) {
197
+ const paramInfo = { decorators: [], type: null };
198
+ for (const decorator of param.decorators || []) {
199
+ paramInfo.decorators.push(decorator);
200
+ }
201
+ if (param.type) {
202
+ // param has a type provided, e.g. "foo: Bar".
203
+ // The type will be emitted as a value expression in entityNameToExpression, which takes
204
+ // care not to emit anything for types that cannot be expressed as a value (e.g.
205
+ // interfaces).
206
+ paramInfo.type = param.type;
207
+ }
208
+ parametersInfo.push(paramInfo);
209
+ }
210
+ if (parametersInfo.length > 0) {
211
+ const ctorProperty = createCtorParametersClassProperty(diagnostics, entityNameToExpression, parametersInfo, typeChecker);
212
+ return [node, ctorProperty];
213
+ }
214
+ else {
215
+ return node;
216
+ }
217
+ }
218
+ function visitor(node) {
219
+ if (ts.isClassDeclaration(node)) {
220
+ return ts.updateClassDeclaration(node, node.decorators, node.modifiers, node.name, node.typeParameters, node.heritageClauses, ts.visitNodes(node.members, classMemberVisitor));
221
+ }
222
+ else {
223
+ return ts.visitEachChild(node, visitor, context);
224
+ }
225
+ }
226
+ return (sf) => {
227
+ parameterTypeSymbols.clear();
228
+ return ts.visitEachChild(visitor(sf), function visitImports(node) {
229
+ if ((ts.isImportSpecifier(node) || ts.isNamespaceImport(node) || ts.isImportClause(node)) &&
230
+ node.name) {
231
+ const importSymbol = typeChecker.getSymbolAtLocation(node.name);
232
+ if (importSymbol && parameterTypeSymbols.has(importSymbol)) {
233
+ // Using a clone prevents TS from removing the import specifier
234
+ return ts.getMutableClone(node);
235
+ }
236
+ }
237
+ return ts.visitEachChild(node, visitImports, context);
238
+ }, context);
239
+ };
240
+ };
241
+ }
242
+ exports.decoratorDownlevelTransformer = decoratorDownlevelTransformer;
@@ -10,50 +10,43 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  const ts = require("typescript");
11
11
  const elide_imports_1 = require("./elide_imports");
12
12
  const interfaces_1 = require("./interfaces");
13
- // Typescript below 2.7.0 needs a workaround.
14
- const tsVersionParts = ts.version.split('.').map(p => Number(p));
15
- const visitEachChild = tsVersionParts[0] <= 2 && tsVersionParts[1] < 7
16
- ? visitEachChildWorkaround
17
- : ts.visitEachChild;
18
13
  function makeTransform(standardTransform, getTypeChecker) {
19
14
  return (context) => {
20
15
  const transformer = (sf) => {
21
16
  const ops = standardTransform(sf);
22
- const removeOps = ops
23
- .filter((op) => op.kind === interfaces_1.OPERATION_KIND.Remove);
24
- const addOps = ops.filter((op) => op.kind === interfaces_1.OPERATION_KIND.Add);
25
- const replaceOps = ops
26
- .filter((op) => op.kind === interfaces_1.OPERATION_KIND.Replace);
17
+ const removeOps = ops.filter(op => op.kind === interfaces_1.OPERATION_KIND.Remove);
18
+ const addOps = ops.filter(op => op.kind === interfaces_1.OPERATION_KIND.Add);
19
+ const replaceOps = ops.filter(op => op.kind === interfaces_1.OPERATION_KIND.Replace);
27
20
  // If nodes are removed, elide the imports as well.
28
21
  // Mainly a workaround for https://github.com/Microsoft/TypeScript/issues/17552.
29
22
  // WARNING: this assumes that replaceOps DO NOT reuse any of the nodes they are replacing.
30
23
  // This is currently true for transforms that use replaceOps (replace_bootstrap and
31
24
  // replace_resources), but may not be true for new transforms.
32
25
  if (getTypeChecker && removeOps.length + replaceOps.length > 0) {
33
- const removedNodes = removeOps.concat(replaceOps).map((op) => op.target);
26
+ const removedNodes = removeOps.concat(replaceOps).map(op => op.target);
34
27
  removeOps.push(...elide_imports_1.elideImports(sf, removedNodes, getTypeChecker));
35
28
  }
36
- const visitor = (node) => {
29
+ const visitor = node => {
37
30
  let modified = false;
38
31
  let modifiedNodes = [node];
39
32
  // Check if node should be dropped.
40
- if (removeOps.find((op) => op.target === node)) {
33
+ if (removeOps.find(op => op.target === node)) {
41
34
  modifiedNodes = [];
42
35
  modified = true;
43
36
  }
44
37
  // Check if node should be replaced (only replaces with first op found).
45
- const replace = replaceOps.find((op) => op.target === node);
38
+ const replace = replaceOps.find(op => op.target === node);
46
39
  if (replace) {
47
40
  modifiedNodes = [replace.replacement];
48
41
  modified = true;
49
42
  }
50
43
  // Check if node should be added to.
51
- const add = addOps.filter((op) => op.target === node);
44
+ const add = addOps.filter(op => op.target === node);
52
45
  if (add.length > 0) {
53
46
  modifiedNodes = [
54
- ...add.filter((op) => op.before).map(((op) => op.before)),
47
+ ...add.filter(op => op.before).map(op => op.before),
55
48
  ...modifiedNodes,
56
- ...add.filter((op) => op.after).map(((op) => op.after)),
49
+ ...add.filter(op => op.after).map(op => op.after),
57
50
  ];
58
51
  modified = true;
59
52
  }
@@ -63,7 +56,7 @@ function makeTransform(standardTransform, getTypeChecker) {
63
56
  }
64
57
  else {
65
58
  // Otherwise return node as is and visit children.
66
- return visitEachChild(node, visitor, context);
59
+ return ts.visitEachChild(node, visitor, context);
67
60
  }
68
61
  };
69
62
  // Don't visit the sourcefile at all if we don't have ops for it.
@@ -72,7 +65,7 @@ function makeTransform(standardTransform, getTypeChecker) {
72
65
  }
73
66
  const result = ts.visitNode(sf, visitor);
74
67
  // If we removed any decorators, we need to clean up the decorator arrays.
75
- if (removeOps.some((op) => op.target.kind === ts.SyntaxKind.Decorator)) {
68
+ if (removeOps.some(op => op.target.kind === ts.SyntaxKind.Decorator)) {
76
69
  cleanupDecorators(result);
77
70
  }
78
71
  return result;
@@ -81,43 +74,11 @@ function makeTransform(standardTransform, getTypeChecker) {
81
74
  };
82
75
  }
83
76
  exports.makeTransform = makeTransform;
84
- /**
85
- * This is a version of `ts.visitEachChild` that works that calls our version
86
- * of `updateSourceFileNode`, so that typescript doesn't lose type information
87
- * for property decorators.
88
- * See https://github.com/Microsoft/TypeScript/issues/17384 (fixed by
89
- * https://github.com/Microsoft/TypeScript/pull/20314 and released in TS 2.7.0) and
90
- * https://github.com/Microsoft/TypeScript/issues/17551 (fixed by
91
- * https://github.com/Microsoft/TypeScript/pull/18051 and released on TS 2.5.0).
92
- */
93
- function visitEachChildWorkaround(node, visitor, context) {
94
- if (node.kind === ts.SyntaxKind.SourceFile) {
95
- const sf = node;
96
- const statements = ts.visitLexicalEnvironment(sf.statements, visitor, context);
97
- if (statements === sf.statements) {
98
- return sf;
99
- }
100
- // Note: Need to clone the original file (and not use `ts.updateSourceFileNode`)
101
- // as otherwise TS fails when resolving types for decorators.
102
- const sfClone = ts.getMutableClone(sf);
103
- sfClone.statements = statements;
104
- return sfClone;
105
- }
106
- return ts.visitEachChild(node, visitor, context);
107
- }
108
- // 1) If TS sees an empty decorator array, it will still emit a `__decorate` call.
77
+ // If TS sees an empty decorator array, it will still emit a `__decorate` call.
109
78
  // This seems to be a TS bug.
110
- // 2) Also ensure nodes with modified decorators have parents
111
- // built in TS transformers assume certain nodes have parents (fixed in TS 2.7+)
112
79
  function cleanupDecorators(node) {
113
- if (node.decorators) {
114
- if (node.decorators.length == 0) {
115
- node.decorators = undefined;
116
- }
117
- else if (node.parent == undefined) {
118
- const originalNode = ts.getParseTreeNode(node);
119
- node.parent = originalNode.parent;
120
- }
80
+ if (node.decorators && node.decorators.length === 0) {
81
+ node.decorators = undefined;
121
82
  }
122
83
  ts.forEachChild(node, node => cleanupDecorators(node));
123
84
  }