@ngtools/webpack 8.0.3 → 8.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": "8.0.
|
|
3
|
+
"version": "8.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",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://github.com/angular/angular-cli",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@angular-devkit/core": "8.0.
|
|
28
|
+
"@angular-devkit/core": "8.0.4",
|
|
29
29
|
"enhanced-resolve": "4.1.0",
|
|
30
30
|
"rxjs": "6.4.0",
|
|
31
31
|
"tree-kill": "1.2.1",
|
|
@@ -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");
|
|
@@ -680,6 +681,9 @@ class AngularCompilerPlugin {
|
|
|
680
681
|
if (this._JitMode) {
|
|
681
682
|
// Replace resources in JIT.
|
|
682
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));
|
|
683
687
|
}
|
|
684
688
|
else {
|
|
685
689
|
// Remove unneeded angular decorators.
|
|
@@ -918,8 +922,9 @@ class AngularCompilerPlugin {
|
|
|
918
922
|
}
|
|
919
923
|
allDiagnostics.push(...gather_diagnostics_1.gatherDiagnostics(tsProgram, this._JitMode, 'AngularCompilerPlugin._emit.ts', diagMode));
|
|
920
924
|
if (!gather_diagnostics_1.hasErrors(allDiagnostics)) {
|
|
921
|
-
if (this._firstRun || changedTsFiles.size > 20 || this.
|
|
925
|
+
if (this._firstRun || changedTsFiles.size > 20 || !this._hadFullJitEmit) {
|
|
922
926
|
emitResult = tsProgram.emit(undefined, undefined, undefined, undefined, { before: this._transformers });
|
|
927
|
+
this._hadFullJitEmit = !emitResult.emitSkipped;
|
|
923
928
|
allDiagnostics.push(...emitResult.diagnostics);
|
|
924
929
|
}
|
|
925
930
|
else {
|
|
@@ -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,231 @@
|
|
|
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) {
|
|
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)
|
|
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) {
|
|
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
|
+
// Ignore any generic types, just return the base type.
|
|
141
|
+
return entityNameToExpression(typeRef.typeName);
|
|
142
|
+
default:
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Transformer factory for the decorator downlevel transformer. See fileoverview for details.
|
|
148
|
+
*/
|
|
149
|
+
function decoratorDownlevelTransformer(typeChecker, diagnostics) {
|
|
150
|
+
return (context) => {
|
|
151
|
+
const parameterTypeSymbols = new Set();
|
|
152
|
+
/**
|
|
153
|
+
* Converts an EntityName (from a type annotation) to an expression (accessing a value).
|
|
154
|
+
*
|
|
155
|
+
* For a given ts.EntityName, this walks depth first to find the leftmost ts.Identifier, then
|
|
156
|
+
* converts the path into property accesses.
|
|
157
|
+
*
|
|
158
|
+
*/
|
|
159
|
+
function entityNameToExpression(name) {
|
|
160
|
+
if (ts.isIdentifier(name)) {
|
|
161
|
+
const typeSymbol = typeChecker.getSymbolAtLocation(name);
|
|
162
|
+
if (typeSymbol) {
|
|
163
|
+
parameterTypeSymbols.add(typeSymbol);
|
|
164
|
+
}
|
|
165
|
+
// Based on TS's strategy to allow the checker to reach this identifier
|
|
166
|
+
// tslint:disable-next-line:max-line-length
|
|
167
|
+
// https://github.com/microsoft/TypeScript/blob/7f47a08a5e9874f0f97a667bd81eebddec61247c/src/compiler/transformers/ts.ts#L2093
|
|
168
|
+
const exp = ts.getMutableClone(name);
|
|
169
|
+
exp.flags &= ~ts.NodeFlags.Synthesized;
|
|
170
|
+
exp.original = undefined;
|
|
171
|
+
exp.parent = ts.getParseTreeNode(name.getSourceFile());
|
|
172
|
+
return exp;
|
|
173
|
+
}
|
|
174
|
+
const ref = entityNameToExpression(name.left);
|
|
175
|
+
if (!ref) {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
return ts.createPropertyAccess(ref, name.right);
|
|
179
|
+
}
|
|
180
|
+
function classMemberVisitor(node) {
|
|
181
|
+
if (!ts.isConstructorDeclaration(node) || !node.body) {
|
|
182
|
+
return visitor(node);
|
|
183
|
+
}
|
|
184
|
+
const parametersInfo = [];
|
|
185
|
+
for (const param of node.parameters) {
|
|
186
|
+
const paramInfo = { decorators: [], type: null };
|
|
187
|
+
for (const decorator of param.decorators || []) {
|
|
188
|
+
paramInfo.decorators.push(decorator);
|
|
189
|
+
}
|
|
190
|
+
if (param.type) {
|
|
191
|
+
// param has a type provided, e.g. "foo: Bar".
|
|
192
|
+
// The type will be emitted as a value expression in entityNameToExpression, which takes
|
|
193
|
+
// care not to emit anything for types that cannot be expressed as a value (e.g.
|
|
194
|
+
// interfaces).
|
|
195
|
+
paramInfo.type = param.type;
|
|
196
|
+
}
|
|
197
|
+
parametersInfo.push(paramInfo);
|
|
198
|
+
}
|
|
199
|
+
if (parametersInfo.length > 0) {
|
|
200
|
+
const ctorProperty = createCtorParametersClassProperty(diagnostics, entityNameToExpression, parametersInfo);
|
|
201
|
+
return [node, ctorProperty];
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
return node;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function visitor(node) {
|
|
208
|
+
if (ts.isClassDeclaration(node)) {
|
|
209
|
+
return ts.updateClassDeclaration(node, node.decorators, node.modifiers, node.name, node.typeParameters, node.heritageClauses, ts.visitNodes(node.members, classMemberVisitor));
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
return ts.visitEachChild(node, visitor, context);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return (sf) => {
|
|
216
|
+
parameterTypeSymbols.clear();
|
|
217
|
+
return ts.visitEachChild(visitor(sf), function visitImports(node) {
|
|
218
|
+
if ((ts.isImportSpecifier(node) || ts.isNamespaceImport(node) || ts.isImportClause(node)) &&
|
|
219
|
+
node.name) {
|
|
220
|
+
const importSymbol = typeChecker.getSymbolAtLocation(node.name);
|
|
221
|
+
if (importSymbol && parameterTypeSymbols.has(importSymbol)) {
|
|
222
|
+
// Using a clone prevents TS from removing the import specifier
|
|
223
|
+
return ts.getMutableClone(node);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return ts.visitEachChild(node, visitImports, context);
|
|
227
|
+
}, context);
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
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
|
-
|
|
24
|
-
const
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
44
|
+
const add = addOps.filter(op => op.target === node);
|
|
52
45
|
if (add.length > 0) {
|
|
53
46
|
modifiedNodes = [
|
|
54
|
-
...add.filter(
|
|
47
|
+
...add.filter(op => op.before).map(op => op.before),
|
|
55
48
|
...modifiedNodes,
|
|
56
|
-
...add.filter(
|
|
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(
|
|
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
|
-
|
|
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
|
}
|