@ngtools/webpack 8.0.0 → 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 +2 -2
- package/src/angular_compiler_plugin.d.ts +2 -0
- package/src/angular_compiler_plugin.js +9 -14
- package/src/compiler_host.js +5 -3
- package/src/interfaces.d.ts +2 -1
- package/src/ngcc_processor.d.ts +1 -2
- package/src/ngcc_processor.js +3 -3
- package/src/transformers/ctor-parameters.d.ts +13 -0
- package/src/transformers/ctor-parameters.js +231 -0
- package/src/transformers/make_transform.js +15 -54
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",
|
|
@@ -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;
|
|
@@ -41,6 +42,7 @@ export declare class AngularCompilerPlugin {
|
|
|
41
42
|
className: string;
|
|
42
43
|
} | null;
|
|
43
44
|
readonly typeChecker: ts.TypeChecker | null;
|
|
45
|
+
/** @deprecated From 8.0.2 */
|
|
44
46
|
static isSupported(): boolean;
|
|
45
47
|
private _setupOptions;
|
|
46
48
|
private _getTsProgram;
|
|
@@ -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");
|
|
@@ -68,8 +69,9 @@ class AngularCompilerPlugin {
|
|
|
68
69
|
const tsProgram = this._getTsProgram();
|
|
69
70
|
return tsProgram ? tsProgram.getTypeChecker() : null;
|
|
70
71
|
}
|
|
72
|
+
/** @deprecated From 8.0.2 */
|
|
71
73
|
static isSupported() {
|
|
72
|
-
return compiler_cli_1.VERSION && parseInt(compiler_cli_1.VERSION.major) >=
|
|
74
|
+
return compiler_cli_1.VERSION && parseInt(compiler_cli_1.VERSION.major) >= 8;
|
|
73
75
|
}
|
|
74
76
|
_setupOptions(options) {
|
|
75
77
|
benchmark_1.time('AngularCompilerPlugin._setupOptions');
|
|
@@ -487,17 +489,7 @@ class AngularCompilerPlugin {
|
|
|
487
489
|
}
|
|
488
490
|
let ngccProcessor;
|
|
489
491
|
if (this._compilerOptions.enableIvy) {
|
|
490
|
-
|
|
491
|
-
try {
|
|
492
|
-
// this is done for the sole reason that @ngtools/webpack
|
|
493
|
-
// support versions of Angular that don't have NGCC API
|
|
494
|
-
ngcc = require('@angular/compiler-cli/ngcc');
|
|
495
|
-
}
|
|
496
|
-
catch (_a) {
|
|
497
|
-
}
|
|
498
|
-
if (ngcc) {
|
|
499
|
-
ngccProcessor = new ngcc_processor_1.NgccProcessor(ngcc, this._mainFields, compilerWithFileSystems.inputFileSystem, this._warnings, this._errors, this._basePath);
|
|
500
|
-
}
|
|
492
|
+
ngccProcessor = new ngcc_processor_1.NgccProcessor(this._mainFields, compilerWithFileSystems.inputFileSystem, this._warnings, this._errors, this._basePath);
|
|
501
493
|
}
|
|
502
494
|
// Create the webpack compiler host.
|
|
503
495
|
const webpackCompilerHost = new compiler_host_1.WebpackCompilerHost(this._compilerOptions, this._basePath, host, true, this._options.directTemplateLoading, ngccProcessor);
|
|
@@ -635,7 +627,6 @@ class AngularCompilerPlugin {
|
|
|
635
627
|
}
|
|
636
628
|
async _make(compilation) {
|
|
637
629
|
benchmark_1.time('AngularCompilerPlugin._make');
|
|
638
|
-
this._emitSkipped = true;
|
|
639
630
|
// tslint:disable-next-line:no-any
|
|
640
631
|
if (compilation._ngToolsWebpackPluginInstance) {
|
|
641
632
|
throw new Error('An @ngtools/webpack plugin already exist for this compilation.');
|
|
@@ -690,6 +681,9 @@ class AngularCompilerPlugin {
|
|
|
690
681
|
if (this._JitMode) {
|
|
691
682
|
// Replace resources in JIT.
|
|
692
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));
|
|
693
687
|
}
|
|
694
688
|
else {
|
|
695
689
|
// Remove unneeded angular decorators.
|
|
@@ -928,8 +922,9 @@ class AngularCompilerPlugin {
|
|
|
928
922
|
}
|
|
929
923
|
allDiagnostics.push(...gather_diagnostics_1.gatherDiagnostics(tsProgram, this._JitMode, 'AngularCompilerPlugin._emit.ts', diagMode));
|
|
930
924
|
if (!gather_diagnostics_1.hasErrors(allDiagnostics)) {
|
|
931
|
-
if (this._firstRun || changedTsFiles.size > 20) {
|
|
925
|
+
if (this._firstRun || changedTsFiles.size > 20 || !this._hadFullJitEmit) {
|
|
932
926
|
emitResult = tsProgram.emit(undefined, undefined, undefined, undefined, { before: this._transformers });
|
|
927
|
+
this._hadFullJitEmit = !emitResult.emitSkipped;
|
|
933
928
|
allDiagnostics.push(...emitResult.diagnostics);
|
|
934
929
|
}
|
|
935
930
|
else {
|
package/src/compiler_host.js
CHANGED
|
@@ -106,13 +106,15 @@ class WebpackCompilerHost {
|
|
|
106
106
|
readFile(fileName) {
|
|
107
107
|
const filePath = this.resolve(fileName);
|
|
108
108
|
try {
|
|
109
|
+
let content;
|
|
109
110
|
if (this._memoryHost.isFile(filePath)) {
|
|
110
|
-
|
|
111
|
+
content = this._memoryHost.read(filePath);
|
|
111
112
|
}
|
|
112
113
|
else {
|
|
113
|
-
|
|
114
|
-
return core_1.virtualFs.fileBufferToString(content);
|
|
114
|
+
content = this._syncHost.read(filePath);
|
|
115
115
|
}
|
|
116
|
+
// strip BOM
|
|
117
|
+
return core_1.virtualFs.fileBufferToString(content).replace(/^\uFEFF/, '');
|
|
116
118
|
}
|
|
117
119
|
catch (_a) {
|
|
118
120
|
return undefined;
|
package/src/interfaces.d.ts
CHANGED
|
@@ -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?:
|
|
52
|
+
compilerOptions?: CompilerOptions;
|
|
52
53
|
host?: virtualFs.Host<fs.Stats>;
|
|
53
54
|
platformTransformers?: ts.TransformerFactory<ts.SourceFile>[];
|
|
54
55
|
}
|
package/src/ngcc_processor.d.ts
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
import * as ts from 'typescript';
|
|
9
9
|
import { InputFileSystem } from 'webpack';
|
|
10
10
|
export declare class NgccProcessor {
|
|
11
|
-
private readonly ngcc;
|
|
12
11
|
private readonly propertiesToConsider;
|
|
13
12
|
private readonly inputFileSystem;
|
|
14
13
|
private readonly compilationWarnings;
|
|
@@ -17,7 +16,7 @@ export declare class NgccProcessor {
|
|
|
17
16
|
private _processedModules;
|
|
18
17
|
private _logger;
|
|
19
18
|
private _nodeModulesDirectory;
|
|
20
|
-
constructor(
|
|
19
|
+
constructor(propertiesToConsider: string[], inputFileSystem: InputFileSystem, compilationWarnings: (Error | string)[], compilationErrors: (Error | string)[], basePath: string);
|
|
21
20
|
processModule(moduleName: string, resolvedModule: ts.ResolvedModule | ts.ResolvedTypeReferenceDirective): void;
|
|
22
21
|
/**
|
|
23
22
|
* Try resolve a package.json file from the resolved .d.ts file.
|
package/src/ngcc_processor.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* found in the LICENSE file at https://angular.io/license
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
const ngcc_1 = require("@angular/compiler-cli/ngcc");
|
|
10
11
|
const fs_1 = require("fs");
|
|
11
12
|
const path = require("path");
|
|
12
13
|
const benchmark_1 = require("./benchmark");
|
|
@@ -18,8 +19,7 @@ const benchmark_1 = require("./benchmark");
|
|
|
18
19
|
// but could not be resolved to an NgModule class
|
|
19
20
|
// We now transform a package and it's typings when NGTSC is resolving a module.
|
|
20
21
|
class NgccProcessor {
|
|
21
|
-
constructor(
|
|
22
|
-
this.ngcc = ngcc;
|
|
22
|
+
constructor(propertiesToConsider, inputFileSystem, compilationWarnings, compilationErrors, basePath) {
|
|
23
23
|
this.propertiesToConsider = propertiesToConsider;
|
|
24
24
|
this.inputFileSystem = inputFileSystem;
|
|
25
25
|
this.compilationWarnings = compilationWarnings;
|
|
@@ -45,7 +45,7 @@ class NgccProcessor {
|
|
|
45
45
|
}
|
|
46
46
|
const timeLabel = `NgccProcessor.processModule.ngcc.process+${moduleName}`;
|
|
47
47
|
benchmark_1.time(timeLabel);
|
|
48
|
-
|
|
48
|
+
ngcc_1.process({
|
|
49
49
|
basePath: this._nodeModulesDirectory,
|
|
50
50
|
targetEntryPointPath: path.dirname(packageJsonPath),
|
|
51
51
|
propertiesToConsider: this.propertiesToConsider,
|
|
@@ -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
|
}
|