fix(ngcc): correctly detect emitted TS helpers in ES5 (#35191)
In ES5 code, TypeScript requires certain helpers (such as `__spreadArrays()`) to be able to support ES2015+ features. These helpers can be either imported from `tslib` (by setting the `importHelpers` TS compiler option to `true`) or emitted inline (by setting the `importHelpers` and `noEmitHelpers` TS compiler options to `false`, which is the default value for both). Ngtsc's `StaticInterpreter` (which is also used during ngcc processing) is able to statically evaluate some of these helpers (currently `__assign()`, `__spread()` and `__spreadArrays()`), as long as `ReflectionHost#getDefinitionOfFunction()` correctly detects the declaration of the helper. For this to happen, the left-hand side of the corresponding call expression (i.e. `__spread(...)` or `tslib.__spread(...)`) must be evaluated as a function declaration for `getDefinitionOfFunction()` to be called with. In the case of imported helpers, the `tslib.__someHelper` expression was resolved to a function declaration of the form `export declare function __someHelper(...args: any[][]): any[];`, which allows `getDefinitionOfFunction()` to correctly map it to a TS helper. In contrast, in the case of emitted helpers (and regardless of the module format: `CommonJS`, `ESNext`, `UMD`, etc.)), the `__someHelper` identifier was resolved to a variable declaration of the form `var __someHelper = (this && this.__someHelper) || function () { ... }`, which upon further evaluation was categorized as a `DynamicValue` (prohibiting further evaluation by the `getDefinitionOfFunction()`). As a result of the above, emitted TypeScript helpers were not evaluated in ES5 code. --- This commit changes the detection of TS helpers to leverage the existing `KnownFn` feature (previously only used for built-in functions). `Esm5ReflectionHost` is changed to always return `KnownDeclaration`s for TS helpers, both imported (`getExportsOfModule()`) as well as emitted (`getDeclarationOfIdentifier()`). Similar changes are made to `CommonJsReflectionHost` and `UmdReflectionHost`. The `KnownDeclaration`s are then mapped to `KnownFn`s in `StaticInterpreter`, allowing it to statically evaluate call expressions involving any kind of TS helpers. Jira issue: https://angular-team.atlassian.net/browse/FW-1689 PR Close #35191
This commit is contained in:
parent
14744f27c5
commit
bd6a39c364
|
@ -11,7 +11,7 @@ import {absoluteFrom} from '../../../src/ngtsc/file_system';
|
|||
import {Declaration, Import} from '../../../src/ngtsc/reflection';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {BundleProgram} from '../packages/bundle_program';
|
||||
import {FactoryMap, isDefined, stripExtension} from '../utils';
|
||||
import {FactoryMap, getTsHelperFnFromIdentifier, isDefined, stripExtension} from '../utils';
|
||||
|
||||
import {ExportDeclaration, ExportStatement, ReexportStatement, RequireCall, findNamespaceOfIdentifier, findRequireCallReference, isExportStatement, isReexportStatement, isRequireCall} from './commonjs_umd_utils';
|
||||
import {Esm5ReflectionHost} from './esm5_host';
|
||||
|
@ -189,7 +189,7 @@ export class CommonJsReflectionHost extends Esm5ReflectionHost {
|
|||
}
|
||||
|
||||
const viaModule = !importInfo.from.startsWith('.') ? importInfo.from : null;
|
||||
return {node: importedFile, known: null, viaModule};
|
||||
return {node: importedFile, known: getTsHelperFnFromIdentifier(id), viaModule};
|
||||
}
|
||||
|
||||
private resolveModuleName(moduleName: string, containingFile: ts.SourceFile): ts.SourceFile
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, Parameter, TsHelperFn, isNamedVariableDeclaration, reflectObjectLiteral} from '../../../src/ngtsc/reflection';
|
||||
import {getNameText, hasNameIdentifier, stripDollarSuffix} from '../utils';
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, Parameter, isNamedVariableDeclaration, reflectObjectLiteral} from '../../../src/ngtsc/reflection';
|
||||
import {getNameText, getTsHelperFnFromDeclaration, hasNameIdentifier} from '../utils';
|
||||
|
||||
import {Esm2015ReflectionHost, ParamInfo, getPropertyValueFromSymbol, isAssignment, isAssignmentStatement} from './esm2015_host';
|
||||
import {NgccClassSymbol} from './ngcc_host';
|
||||
|
@ -118,6 +118,7 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
|||
// Return the statement before the IIFE return statement
|
||||
return iifeBody.statements[returnStatementIndex - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* In ES5, the implementation of a class is a function expression that is hidden inside an IIFE,
|
||||
* whose value is assigned to a variable (which represents the class to the rest of the program).
|
||||
|
@ -245,23 +246,7 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
|||
*/
|
||||
getDefinitionOfFunction(node: ts.Node): FunctionDefinition|null {
|
||||
if (!ts.isFunctionDeclaration(node) && !ts.isMethodDeclaration(node) &&
|
||||
!ts.isFunctionExpression(node) && !ts.isVariableDeclaration(node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tsHelperFn = getTsHelperFn(node);
|
||||
if (tsHelperFn !== null) {
|
||||
return {
|
||||
node,
|
||||
body: null,
|
||||
helper: tsHelperFn,
|
||||
parameters: [],
|
||||
};
|
||||
}
|
||||
|
||||
// If the node was not identified to be a TypeScript helper, a variable declaration at this
|
||||
// point cannot be resolved as a function.
|
||||
if (ts.isVariableDeclaration(node)) {
|
||||
!ts.isFunctionExpression(node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -276,11 +261,26 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
|||
return !lookingForParamInitializers;
|
||||
});
|
||||
|
||||
return {node, body: statements || null, helper: null, parameters};
|
||||
return {node, body: statements || null, parameters};
|
||||
}
|
||||
|
||||
|
||||
///////////// Protected Helpers /////////////
|
||||
/**
|
||||
* Resolve a `ts.Symbol` to its declaration and detect whether it corresponds with a known
|
||||
* TypeScript helper function.
|
||||
*/
|
||||
protected getDeclarationOfSymbol(symbol: ts.Symbol, originalId: ts.Identifier|null): Declaration
|
||||
|null {
|
||||
const superDeclaration = super.getDeclarationOfSymbol(symbol, originalId);
|
||||
|
||||
if (superDeclaration !== null && superDeclaration.node !== null &&
|
||||
superDeclaration.known === null) {
|
||||
superDeclaration.known = getTsHelperFnFromDeclaration(superDeclaration.node);
|
||||
}
|
||||
|
||||
return superDeclaration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the inner function declaration of an ES5-style class.
|
||||
|
@ -651,28 +651,6 @@ function reflectArrayElement(element: ts.Expression) {
|
|||
return ts.isObjectLiteralExpression(element) ? reflectObjectLiteral(element) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects a function declaration to determine if it corresponds with a TypeScript helper function,
|
||||
* returning its kind if so or null if the declaration does not seem to correspond with such a
|
||||
* helper.
|
||||
*/
|
||||
function getTsHelperFn(node: ts.NamedDeclaration): TsHelperFn|null {
|
||||
const name = node.name !== undefined && ts.isIdentifier(node.name) ?
|
||||
stripDollarSuffix(node.name.text) :
|
||||
null;
|
||||
|
||||
switch (name) {
|
||||
case '__assign':
|
||||
return TsHelperFn.Assign;
|
||||
case '__spread':
|
||||
return TsHelperFn.Spread;
|
||||
case '__spreadArrays':
|
||||
return TsHelperFn.SpreadArrays;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructor function may have been "synthesized" by TypeScript during JavaScript emit,
|
||||
* in the case no user-defined constructor exists and e.g. property initializers are used.
|
||||
|
|
|
@ -12,7 +12,7 @@ import {absoluteFrom} from '../../../src/ngtsc/file_system';
|
|||
import {Declaration, Import} from '../../../src/ngtsc/reflection';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {BundleProgram} from '../packages/bundle_program';
|
||||
import {FactoryMap, stripExtension} from '../utils';
|
||||
import {FactoryMap, getTsHelperFnFromIdentifier, stripExtension} from '../utils';
|
||||
import {ExportDeclaration, ExportStatement, ReexportStatement, findNamespaceOfIdentifier, findRequireCallReference, isExportStatement, isReexportStatement, isRequireCall} from './commonjs_umd_utils';
|
||||
import {Esm5ReflectionHost, stripParentheses} from './esm5_host';
|
||||
|
||||
|
@ -215,7 +215,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost {
|
|||
|
||||
// We need to add the `viaModule` because the `getExportsOfModule()` call
|
||||
// did not know that we were importing the declaration.
|
||||
return {node: importedFile, known: null, viaModule: importInfo.from};
|
||||
return {node: importedFile, known: getTsHelperFnFromIdentifier(id), viaModule: importInfo.from};
|
||||
}
|
||||
|
||||
private resolveModuleName(moduleName: string, containingFile: ts.SourceFile): ts.SourceFile
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
import * as ts from 'typescript';
|
||||
import {AbsoluteFsPath, FileSystem, absoluteFrom} from '../../src/ngtsc/file_system';
|
||||
import {KnownDeclaration} from '../../src/ngtsc/reflection';
|
||||
|
||||
/**
|
||||
* A list (`Array`) of partially ordered `T` items.
|
||||
|
@ -132,6 +133,40 @@ export function resolveFileWithPostfixes(
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a function declaration corresponds with a TypeScript helper function, returning
|
||||
* its kind if so or null if the declaration does not seem to correspond with such a helper.
|
||||
*/
|
||||
export function getTsHelperFnFromDeclaration(decl: ts.Declaration): KnownDeclaration|null {
|
||||
if (!ts.isFunctionDeclaration(decl) && !ts.isVariableDeclaration(decl)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (decl.name === undefined || !ts.isIdentifier(decl.name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getTsHelperFnFromIdentifier(decl.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an identifier corresponds with a TypeScript helper function (based on its
|
||||
* name), returning its kind if so or null if the identifier does not seem to correspond with such a
|
||||
* helper.
|
||||
*/
|
||||
export function getTsHelperFnFromIdentifier(id: ts.Identifier): KnownDeclaration|null {
|
||||
switch (stripDollarSuffix(id.text)) {
|
||||
case '__assign':
|
||||
return KnownDeclaration.TsHelperAssign;
|
||||
case '__spread':
|
||||
return KnownDeclaration.TsHelperSpread;
|
||||
case '__spreadArrays':
|
||||
return KnownDeclaration.TsHelperSpreadArrays;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An identifier may become repeated when bundling multiple source files into a single bundle, so
|
||||
* bundlers have a strategy of suffixing non-unique identifiers with a suffix like $2. This function
|
||||
|
|
|
@ -9,7 +9,7 @@ import * as ts from 'typescript';
|
|||
|
||||
import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system';
|
||||
import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {ClassMemberKind, CtorParameter, InlineDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {ClassMemberKind, CtorParameter, InlineDeclaration, KnownDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {getDeclaration} from '../../../src/ngtsc/testing';
|
||||
import {loadFakeCore, loadTestFiles} from '../../../test/helpers';
|
||||
import {CommonJsReflectionHost} from '../../src/host/commonjs_host';
|
||||
|
@ -1660,6 +1660,32 @@ exports.ExternalModule = ExternalModule;
|
|||
});
|
||||
|
||||
describe('getDeclarationOfIdentifier', () => {
|
||||
// Helpers
|
||||
const createTestForTsHelper =
|
||||
(program: ts.Program, host: CommonJsReflectionHost, srcFile: TestFile,
|
||||
getHelperDeclaration: (name: string) => ts.Declaration) =>
|
||||
(varName: string, helperName: string, knownAs: KnownDeclaration,
|
||||
viaModule: string | null = null) => {
|
||||
const node =
|
||||
getDeclaration(program, srcFile.name, varName, ts.isVariableDeclaration);
|
||||
const helperIdentifier = getIdentifierFromCallExpression(node);
|
||||
const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier);
|
||||
|
||||
expect(helperDeclaration).toEqual({
|
||||
known: knownAs,
|
||||
node: getHelperDeclaration(helperName), viaModule,
|
||||
});
|
||||
};
|
||||
|
||||
const getIdentifierFromCallExpression = (decl: ts.VariableDeclaration) => {
|
||||
if (decl.initializer !== undefined && ts.isCallExpression(decl.initializer)) {
|
||||
const expr = decl.initializer.expression;
|
||||
if (ts.isIdentifier(expr)) return expr;
|
||||
if (ts.isPropertyAccessExpression(expr)) return expr.name;
|
||||
}
|
||||
throw new Error(`Unable to extract identifier from declaration '${decl.getText()}'.`);
|
||||
};
|
||||
|
||||
it('should return the declaration of a locally defined identifier', () => {
|
||||
loadTestFiles([SOME_DIRECTIVE_FILE]);
|
||||
const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name);
|
||||
|
@ -1819,6 +1845,150 @@ exports.ExternalModule = ExternalModule;
|
|||
expect(decl.viaModule).toEqual('sub_module');
|
||||
expect(decl.node).toBe(expectedDeclaration);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript helpers (as function declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
function __assign(t, ...sources) { /* ... */ }
|
||||
function __spread(...args) { /* ... */ }
|
||||
function __spreadArrays(...args) { /* ... */ }
|
||||
|
||||
var a = __assign({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']);
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new CommonJsReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const testForHelper = createTestForTsHelper(
|
||||
bundle.program, host, file,
|
||||
helperName =>
|
||||
getDeclaration(bundle.program, file.name, helperName, ts.isFunctionDeclaration));
|
||||
|
||||
testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize suffixed TypeScript helpers (as function declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
function __assign$1(t, ...sources) { /* ... */ }
|
||||
function __spread$2(...args) { /* ... */ }
|
||||
function __spreadArrays$3(...args) { /* ... */ }
|
||||
|
||||
var a = __assign$1({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread$2(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']);
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new CommonJsReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const testForHelper = createTestForTsHelper(
|
||||
bundle.program, host, file,
|
||||
helperName =>
|
||||
getDeclaration(bundle.program, file.name, helperName, ts.isFunctionDeclaration));
|
||||
|
||||
testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript helpers (as variable declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
var __assign = (this && this.__assign) || function (t, ...sources) { /* ... */ }
|
||||
var __spread = (this && this.__spread) || function (...args) { /* ... */ }
|
||||
var __spreadArrays = (this && this.__spreadArrays) || function (...args) { /* ... */ }
|
||||
|
||||
var a = __assign({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']);
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new CommonJsReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const testForHelper = createTestForTsHelper(
|
||||
bundle.program, host, file,
|
||||
helperName =>
|
||||
getDeclaration(bundle.program, file.name, helperName, ts.isVariableDeclaration));
|
||||
|
||||
testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize suffixed TypeScript helpers (as variable declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
var __assign$1 = (this && this.__assign$1) || function (t, ...sources) { /* ... */ }
|
||||
var __spread$2 = (this && this.__spread$2) || function (...args) { /* ... */ }
|
||||
var __spreadArrays$3 = (this && this.__spreadArrays$3) || function (...args) { /* ... */ }
|
||||
|
||||
var a = __assign$1({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread$2(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']);
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new CommonJsReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const testForHelper = createTestForTsHelper(
|
||||
bundle.program, host, file,
|
||||
helperName =>
|
||||
getDeclaration(bundle.program, file.name, helperName, ts.isVariableDeclaration));
|
||||
|
||||
testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize imported TypeScript helpers', () => {
|
||||
const files: TestFile[] = [
|
||||
{
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
var tslib_1 = require('tslib');
|
||||
|
||||
var a = tslib_1.__assign({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = tslib_1.__spread(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = tslib_1.__spreadArrays(['foo', 'bar'], ['baz', 'qux']);
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/tslib/index.d.ts'),
|
||||
contents: `
|
||||
export declare function __assign(t: any, ...sources: any[]): any;
|
||||
export declare function __spread(...args: any[][]): any[];
|
||||
export declare function __spreadArrays(...args: any[][]): any[];
|
||||
`,
|
||||
},
|
||||
];
|
||||
loadTestFiles(files);
|
||||
|
||||
const [testFile, tslibFile] = files;
|
||||
const bundle = makeTestBundleProgram(testFile.name);
|
||||
const host = new CommonJsReflectionHost(new MockLogger(), false, bundle);
|
||||
const tslibSourceFile = getSourceFileOrError(bundle.program, tslibFile.name);
|
||||
|
||||
const testForHelper =
|
||||
createTestForTsHelper(bundle.program, host, testFile, () => tslibSourceFile);
|
||||
|
||||
testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib');
|
||||
testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib');
|
||||
testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getExportsOfModule()', () => {
|
||||
|
@ -1918,6 +2088,31 @@ exports.ExternalModule = ExternalModule;
|
|||
expect(decl.node).toBeNull();
|
||||
expect(decl.expression).toBeDefined();
|
||||
});
|
||||
|
||||
it('should recognize declarations of known TypeScript helpers', () => {
|
||||
const tslib = {
|
||||
name: _('/tslib.d.ts'),
|
||||
contents: `
|
||||
export declare function __assign(t: any, ...sources: any[]): any;
|
||||
export declare function __spread(...args: any[][]): any[];
|
||||
export declare function __spreadArrays(...args: any[][]): any[];
|
||||
export declare function __unknownHelper(...args: any[]): any;
|
||||
`,
|
||||
};
|
||||
loadTestFiles([tslib]);
|
||||
const bundle = makeTestBundleProgram(tslib.name);
|
||||
const host = new CommonJsReflectionHost(new MockLogger(), false, bundle);
|
||||
const sf = getSourceFileOrError(bundle.program, tslib.name);
|
||||
const exportDeclarations = host.getExportsOfModule(sf) !;
|
||||
|
||||
expect([...exportDeclarations].map(([exportName, {known}]) => [exportName, known]))
|
||||
.toEqual([
|
||||
['__assign', KnownDeclaration.TsHelperAssign],
|
||||
['__spread', KnownDeclaration.TsHelperSpread],
|
||||
['__spreadArrays', KnownDeclaration.TsHelperSpreadArrays],
|
||||
['__unknownHelper', null],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getClassSymbol()', () => {
|
||||
|
|
|
@ -10,7 +10,7 @@ import * as ts from 'typescript';
|
|||
|
||||
import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system';
|
||||
import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {ClassMemberKind, CtorParameter, Decorator, Import, TsHelperFn, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {ClassMemberKind, CtorParameter, Decorator, KnownDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {getDeclaration} from '../../../src/ngtsc/testing';
|
||||
import {loadFakeCore, loadTestFiles} from '../../../test/helpers';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||
|
@ -1689,224 +1689,6 @@ runInEachFileSystem(() => {
|
|||
expect(quxDef.parameters[0].name).toEqual('x');
|
||||
expect(quxDef.parameters[0].initializer).toBe(null);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript __spread helper function declaration', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/declaration.d.ts'),
|
||||
contents: `export declare function __spread(...args: any[][]): any[];`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const node =
|
||||
getDeclaration(bundle.program, file.name, '__spread', isNamedFunctionDeclaration) !;
|
||||
|
||||
const definition = host.getDefinitionOfFunction(node) !;
|
||||
expect(definition.node).toBe(node);
|
||||
expect(definition.body).toBeNull();
|
||||
expect(definition.helper).toBe(TsHelperFn.Spread);
|
||||
expect(definition.parameters.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript __spread helper function implementation', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/implementation.js'),
|
||||
contents: `
|
||||
var __spread = (this && this.__spread) || function () {
|
||||
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
|
||||
return ar;
|
||||
};`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const node =
|
||||
getDeclaration(bundle.program, file.name, '__spread', ts.isVariableDeclaration) !;
|
||||
|
||||
const definition = host.getDefinitionOfFunction(node) !;
|
||||
expect(definition.node).toBe(node);
|
||||
expect(definition.body).toBeNull();
|
||||
expect(definition.helper).toBe(TsHelperFn.Spread);
|
||||
expect(definition.parameters.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript __spread helper function implementation when suffixed',
|
||||
() => {
|
||||
const file: TestFile = {
|
||||
name: _('/implementation.js'),
|
||||
contents: `
|
||||
var __spread$2 = (this && this.__spread$2) || function () {
|
||||
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
|
||||
return ar;
|
||||
};`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const node =
|
||||
getDeclaration(bundle.program, file.name, '__spread$2', ts.isVariableDeclaration) !;
|
||||
|
||||
const definition = host.getDefinitionOfFunction(node) !;
|
||||
expect(definition.node).toBe(node);
|
||||
expect(definition.body).toBeNull();
|
||||
expect(definition.helper).toBe(TsHelperFn.Spread);
|
||||
expect(definition.parameters.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript __spreadArrays helper function declaration', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/declaration.d.ts'),
|
||||
contents: `export declare function __spreadArrays(...args: any[][]): any[];`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const node = getDeclaration(
|
||||
bundle.program, file.name, '__spreadArrays', isNamedFunctionDeclaration) !;
|
||||
|
||||
const definition = host.getDefinitionOfFunction(node) !;
|
||||
expect(definition.node).toBe(node);
|
||||
expect(definition.body).toBeNull();
|
||||
expect(definition.helper).toBe(TsHelperFn.SpreadArrays);
|
||||
expect(definition.parameters.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript __spreadArrays helper function implementation', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/implementation.js'),
|
||||
contents: `
|
||||
var __spreadArrays = (this && this.__spreadArrays) || function () {
|
||||
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
|
||||
for (var r = Array(s), k = 0, i = 0; i < il; i++)
|
||||
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
|
||||
r[k] = a[j];
|
||||
return r;
|
||||
};`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const node =
|
||||
getDeclaration(bundle.program, file.name, '__spreadArrays', ts.isVariableDeclaration) !;
|
||||
|
||||
const definition = host.getDefinitionOfFunction(node) !;
|
||||
expect(definition.node).toBe(node);
|
||||
expect(definition.body).toBeNull();
|
||||
expect(definition.helper).toBe(TsHelperFn.SpreadArrays);
|
||||
expect(definition.parameters.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript __spreadArrays helper function implementation when suffixed',
|
||||
() => {
|
||||
const file: TestFile = {
|
||||
name: _('/implementation.js'),
|
||||
contents: `
|
||||
var __spreadArrays$2 = (this && this.__spreadArrays$2) || function () {
|
||||
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
|
||||
for (var r = Array(s), k = 0, i = 0; i < il; i++)
|
||||
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
|
||||
r[k] = a[j];
|
||||
return r;
|
||||
};`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const node = getDeclaration(
|
||||
bundle.program, file.name, '__spreadArrays$2', ts.isVariableDeclaration) !;
|
||||
|
||||
const definition = host.getDefinitionOfFunction(node) !;
|
||||
expect(definition.node).toBe(node);
|
||||
expect(definition.body).toBeNull();
|
||||
expect(definition.helper).toBe(TsHelperFn.SpreadArrays);
|
||||
expect(definition.parameters.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript __assign helper function declaration', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/declaration.d.ts'),
|
||||
contents: `export declare function __assign(...args: object[]): object;`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const node =
|
||||
getDeclaration(bundle.program, file.name, '__assign', isNamedFunctionDeclaration) !;
|
||||
|
||||
const definition = host.getDefinitionOfFunction(node) !;
|
||||
expect(definition.node).toBe(node);
|
||||
expect(definition.body).toBeNull();
|
||||
expect(definition.helper).toBe(TsHelperFn.Assign);
|
||||
expect(definition.parameters.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript __assign helper function implementation', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/implementation.js'),
|
||||
contents: `
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const node =
|
||||
getDeclaration(bundle.program, file.name, '__assign', ts.isVariableDeclaration) !;
|
||||
|
||||
const definition = host.getDefinitionOfFunction(node) !;
|
||||
expect(definition.node).toBe(node);
|
||||
expect(definition.body).toBeNull();
|
||||
expect(definition.helper).toBe(TsHelperFn.Assign);
|
||||
expect(definition.parameters.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript __assign helper function implementation when suffixed',
|
||||
() => {
|
||||
const file: TestFile = {
|
||||
name: _('/implementation.js'),
|
||||
contents: `
|
||||
var __assign$2 = (this && this.__assign$2) || function () {
|
||||
__assign$2 = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign$2.apply(this, arguments);
|
||||
};`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const node =
|
||||
getDeclaration(bundle.program, file.name, '__assign$2', ts.isVariableDeclaration) !;
|
||||
|
||||
const definition = host.getDefinitionOfFunction(node) !;
|
||||
expect(definition.node).toBe(node);
|
||||
expect(definition.body).toBeNull();
|
||||
expect(definition.helper).toBe(TsHelperFn.Assign);
|
||||
expect(definition.parameters.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getImportOfIdentifier()', () => {
|
||||
|
@ -1945,6 +1727,32 @@ runInEachFileSystem(() => {
|
|||
});
|
||||
|
||||
describe('getDeclarationOfIdentifier()', () => {
|
||||
// Helpers
|
||||
const createTestForTsHelper =
|
||||
(program: ts.Program, host: Esm5ReflectionHost, srcFile: TestFile,
|
||||
getHelperDeclaration: (name: string) => ts.Declaration) =>
|
||||
(varName: string, helperName: string, knownAs: KnownDeclaration,
|
||||
viaModule: string | null = null) => {
|
||||
const node =
|
||||
getDeclaration(program, srcFile.name, varName, ts.isVariableDeclaration);
|
||||
const helperIdentifier = getIdentifierFromCallExpression(node);
|
||||
const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier);
|
||||
|
||||
expect(helperDeclaration).toEqual({
|
||||
known: knownAs,
|
||||
node: getHelperDeclaration(helperName), viaModule,
|
||||
});
|
||||
};
|
||||
|
||||
const getIdentifierFromCallExpression = (decl: ts.VariableDeclaration) => {
|
||||
if (decl.initializer !== undefined && ts.isCallExpression(decl.initializer)) {
|
||||
const expr = decl.initializer.expression;
|
||||
if (ts.isIdentifier(expr)) return expr;
|
||||
if (ts.isPropertyAccessExpression(expr)) return expr.name;
|
||||
}
|
||||
throw new Error(`Unable to extract identifier from declaration '${decl.getText()}'.`);
|
||||
};
|
||||
|
||||
it('should return the declaration of a locally defined identifier', () => {
|
||||
loadTestFiles([SOME_DIRECTIVE_FILE]);
|
||||
const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name);
|
||||
|
@ -2117,6 +1925,188 @@ runInEachFileSystem(() => {
|
|||
const actualDeclaration = host.getDeclarationOfIdentifier(identifier) !;
|
||||
expect(actualDeclaration.node !.getText()).toBe(expectedDeclaration.getText());
|
||||
});
|
||||
|
||||
it('should recognize TypeScript helpers (as function declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
function __assign(t, ...sources) { /* ... */ }
|
||||
function __spread(...args) { /* ... */ }
|
||||
function __spreadArrays(...args) { /* ... */ }
|
||||
|
||||
var a = __assign({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']);
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const testForHelper = createTestForTsHelper(
|
||||
bundle.program, host, file,
|
||||
helperName =>
|
||||
getDeclaration(bundle.program, file.name, helperName, ts.isFunctionDeclaration));
|
||||
|
||||
testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize suffixed TypeScript helpers (as function declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
function __assign$1(t, ...sources) { /* ... */ }
|
||||
function __spread$2(...args) { /* ... */ }
|
||||
function __spreadArrays$3(...args) { /* ... */ }
|
||||
|
||||
var a = __assign$1({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread$2(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']);
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const testForHelper = createTestForTsHelper(
|
||||
bundle.program, host, file,
|
||||
helperName =>
|
||||
getDeclaration(bundle.program, file.name, helperName, ts.isFunctionDeclaration));
|
||||
|
||||
testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript helpers (as variable declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
var __assign = (this && this.__assign) || function (t, ...sources) { /* ... */ }
|
||||
var __spread = (this && this.__spread) || function (...args) { /* ... */ }
|
||||
var __spreadArrays = (this && this.__spreadArrays) || function (...args) { /* ... */ }
|
||||
|
||||
var a = __assign({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']);
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const testForHelper = createTestForTsHelper(
|
||||
bundle.program, host, file,
|
||||
helperName =>
|
||||
getDeclaration(bundle.program, file.name, helperName, ts.isVariableDeclaration));
|
||||
|
||||
testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize suffixed TypeScript helpers (as variable declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
var __assign$1 = (this && this.__assign$1) || function (t, ...sources) { /* ... */ }
|
||||
var __spread$2 = (this && this.__spread$2) || function (...args) { /* ... */ }
|
||||
var __spreadArrays$3 = (this && this.__spreadArrays$3) || function (...args) { /* ... */ }
|
||||
|
||||
var a = __assign$1({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread$2(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']);
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const testForHelper = createTestForTsHelper(
|
||||
bundle.program, host, file,
|
||||
helperName =>
|
||||
getDeclaration(bundle.program, file.name, helperName, ts.isVariableDeclaration));
|
||||
|
||||
testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize imported TypeScript helpers (named imports)', () => {
|
||||
const files: TestFile[] = [
|
||||
{
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
import {__assign, __spread, __spreadArrays} from 'tslib';
|
||||
|
||||
var a = __assign({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']);
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/tslib/index.d.ts'),
|
||||
contents: `
|
||||
export declare function __assign(t: any, ...sources: any[]): any;
|
||||
export declare function __spread(...args: any[][]): any[];
|
||||
export declare function __spreadArrays(...args: any[][]): any[];
|
||||
`,
|
||||
},
|
||||
];
|
||||
loadTestFiles(files);
|
||||
|
||||
const [testFile, tslibFile] = files;
|
||||
const bundle = makeTestBundleProgram(testFile.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const testForHelper = createTestForTsHelper(
|
||||
bundle.program, host, testFile,
|
||||
helperName => getDeclaration(
|
||||
bundle.program, tslibFile.name, helperName, ts.isFunctionDeclaration));
|
||||
|
||||
testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib');
|
||||
testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib');
|
||||
testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib');
|
||||
});
|
||||
|
||||
it('should recognize imported TypeScript helpers (star import)', () => {
|
||||
const files: TestFile[] = [
|
||||
{
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
import * as tslib_1 from 'tslib';
|
||||
|
||||
var a = tslib_1.__assign({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = tslib_1.__spread(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = tslib_1.__spreadArrays(['foo', 'bar'], ['baz', 'qux']);
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/tslib/index.d.ts'),
|
||||
contents: `
|
||||
export declare function __assign(t: any, ...sources: any[]): any;
|
||||
export declare function __spread(...args: any[][]): any[];
|
||||
export declare function __spreadArrays(...args: any[][]): any[];
|
||||
`,
|
||||
},
|
||||
];
|
||||
loadTestFiles(files);
|
||||
|
||||
const [testFile, tslibFile] = files;
|
||||
const bundle = makeTestBundleProgram(testFile.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
|
||||
const testForHelper = createTestForTsHelper(
|
||||
bundle.program, host, testFile,
|
||||
helperName => getDeclaration(
|
||||
bundle.program, tslibFile.name, helperName, ts.isFunctionDeclaration));
|
||||
|
||||
testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib');
|
||||
testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib');
|
||||
testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getExportsOfModule()', () => {
|
||||
|
@ -2159,6 +2149,31 @@ runInEachFileSystem(() => {
|
|||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should recognize declarations of known TypeScript helpers', () => {
|
||||
const tslib = {
|
||||
name: _('/tslib.d.ts'),
|
||||
contents: `
|
||||
export declare function __assign(t: any, ...sources: any[]): any;
|
||||
export declare function __spread(...args: any[][]): any[];
|
||||
export declare function __spreadArrays(...args: any[][]): any[];
|
||||
export declare function __unknownHelper(...args: any[]): any;
|
||||
`,
|
||||
};
|
||||
loadTestFiles([tslib]);
|
||||
const bundle = makeTestBundleProgram(tslib.name);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, bundle);
|
||||
const sf = getSourceFileOrError(bundle.program, tslib.name);
|
||||
const exportDeclarations = host.getExportsOfModule(sf) !;
|
||||
|
||||
expect([...exportDeclarations].map(([exportName, {known}]) => [exportName, known]))
|
||||
.toEqual([
|
||||
['__assign', KnownDeclaration.TsHelperAssign],
|
||||
['__spread', KnownDeclaration.TsHelperSpread],
|
||||
['__spreadArrays', KnownDeclaration.TsHelperSpreadArrays],
|
||||
['__unknownHelper', null],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getClassSymbol()', () => {
|
||||
|
|
|
@ -10,11 +10,11 @@ import * as ts from 'typescript';
|
|||
|
||||
import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system';
|
||||
import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {ClassMemberKind, CtorParameter, Import, InlineDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {ClassMemberKind, CtorParameter, Import, InlineDeclaration, KnownDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {getDeclaration} from '../../../src/ngtsc/testing';
|
||||
import {loadFakeCore, loadTestFiles} from '../../../test/helpers';
|
||||
import {getIifeBody} from '../../src/host/esm5_host';
|
||||
import {UmdReflectionHost} from '../../src/host/umd_host';
|
||||
import {UmdReflectionHost, parseStatementForUmdModule} from '../../src/host/umd_host';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
import {getRootFiles, makeTestBundleProgram} from '../helpers/utils';
|
||||
|
||||
|
@ -1827,6 +1827,41 @@ runInEachFileSystem(() => {
|
|||
});
|
||||
|
||||
describe('getDeclarationOfIdentifier', () => {
|
||||
// Helpers
|
||||
const createTestForTsHelper =
|
||||
(host: UmdReflectionHost, factoryFn: ts.FunctionExpression,
|
||||
getHelperDeclaration: (factoryFn: ts.FunctionExpression, name: string) =>
|
||||
ts.Declaration) =>
|
||||
(varName: string, helperName: string, knownAs: KnownDeclaration,
|
||||
viaModule: string | null = null) => {
|
||||
const node = getVariableDeclaration(factoryFn, varName);
|
||||
const helperIdentifier = getIdentifierFromCallExpression(node);
|
||||
const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier);
|
||||
|
||||
expect(helperDeclaration).toEqual({
|
||||
known: knownAs,
|
||||
node: getHelperDeclaration(factoryFn, helperName), viaModule,
|
||||
});
|
||||
};
|
||||
|
||||
const getFunctionDeclaration = (factoryFn: ts.FunctionExpression, name: string) =>
|
||||
factoryFn.body.statements.filter(ts.isFunctionDeclaration)
|
||||
.find(decl => (decl.name !== undefined) && (decl.name.text === name)) !;
|
||||
|
||||
const getIdentifierFromCallExpression = (decl: ts.VariableDeclaration) => {
|
||||
if (decl.initializer !== undefined && ts.isCallExpression(decl.initializer)) {
|
||||
const expr = decl.initializer.expression;
|
||||
if (ts.isIdentifier(expr)) return expr;
|
||||
if (ts.isPropertyAccessExpression(expr)) return expr.name;
|
||||
}
|
||||
throw new Error(`Unable to extract identifier from declaration '${decl.getText()}'.`);
|
||||
};
|
||||
|
||||
const getVariableDeclaration = (factoryFn: ts.FunctionExpression, name: string) =>
|
||||
factoryFn.body.statements.filter(ts.isVariableStatement)
|
||||
.map(stmt => stmt.declarationList.declarations[0])
|
||||
.find(decl => ts.isIdentifier(decl.name) && (decl.name.text === name)) !;
|
||||
|
||||
it('should return the declaration of a locally defined identifier', () => {
|
||||
loadTestFiles([SOME_DIRECTIVE_FILE]);
|
||||
const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name);
|
||||
|
@ -1937,6 +1972,175 @@ runInEachFileSystem(() => {
|
|||
expect(decl.viaModule).toEqual('sub_module');
|
||||
expect(decl.node).toBe(expectedDeclaration);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript helpers (as function declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define('test', ['exports'], factory) :
|
||||
(factory(global.test));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
function __assign(t, ...sources) { /* ... */ }
|
||||
function __spread(...args) { /* ... */ }
|
||||
function __spreadArrays(...args) { /* ... */ }
|
||||
|
||||
var a = __assign({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']);
|
||||
})));
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new UmdReflectionHost(new MockLogger(), false, bundle);
|
||||
const {factoryFn} = parseStatementForUmdModule(
|
||||
getSourceFileOrError(bundle.program, file.name).statements[0]) !;
|
||||
|
||||
const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration);
|
||||
|
||||
testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize suffixed TypeScript helpers (as function declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define('test', ['exports'], factory) :
|
||||
(factory(global.test));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
function __assign$1(t, ...sources) { /* ... */ }
|
||||
function __spread$2(...args) { /* ... */ }
|
||||
function __spreadArrays$3(...args) { /* ... */ }
|
||||
|
||||
var a = __assign$1({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread$2(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']);
|
||||
})));
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new UmdReflectionHost(new MockLogger(), false, bundle);
|
||||
const {factoryFn} = parseStatementForUmdModule(
|
||||
getSourceFileOrError(bundle.program, file.name).statements[0]) !;
|
||||
|
||||
const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration);
|
||||
|
||||
testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize TypeScript helpers (as variable declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define('test', ['exports'], factory) :
|
||||
(factory(global.test));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
var __assign = (this && this.__assign) || function (t, ...sources) { /* ... */ }
|
||||
var __spread = (this && this.__spread) || function (...args) { /* ... */ }
|
||||
var __spreadArrays = (this && this.__spreadArrays) || function (...args) { /* ... */ }
|
||||
|
||||
var a = __assign({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']);
|
||||
})));
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new UmdReflectionHost(new MockLogger(), false, bundle);
|
||||
const {factoryFn} = parseStatementForUmdModule(
|
||||
getSourceFileOrError(bundle.program, file.name).statements[0]) !;
|
||||
|
||||
const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration);
|
||||
|
||||
testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize suffixed TypeScript helpers (as variable declarations)', () => {
|
||||
const file: TestFile = {
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define('test', ['exports'], factory) :
|
||||
(factory(global.test));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
var __assign$1 = (this && this.__assign$1) || function (t, ...sources) { /* ... */ }
|
||||
var __spread$2 = (this && this.__spread$2) || function (...args) { /* ... */ }
|
||||
var __spreadArrays$3 = (this && this.__spreadArrays$3) || function (...args) { /* ... */ }
|
||||
|
||||
var a = __assign$1({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = __spread$2(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']);
|
||||
})));
|
||||
`,
|
||||
};
|
||||
loadTestFiles([file]);
|
||||
const bundle = makeTestBundleProgram(file.name);
|
||||
const host = new UmdReflectionHost(new MockLogger(), false, bundle);
|
||||
const {factoryFn} = parseStatementForUmdModule(
|
||||
getSourceFileOrError(bundle.program, file.name).statements[0]) !;
|
||||
|
||||
const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration);
|
||||
|
||||
testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign);
|
||||
testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread);
|
||||
testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize imported TypeScript helpers', () => {
|
||||
const files: TestFile[] = [
|
||||
{
|
||||
name: _('/test.js'),
|
||||
contents: `
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tslib')) :
|
||||
typeof define === 'function' && define.amd ? define('test', ['exports', 'tslib'], factory) :
|
||||
(factory(global.test, global.tslib));
|
||||
}(this, (function (exports, tslib_1) { 'use strict';
|
||||
var a = tslib_1.__assign({foo: 'bar'}, {baz: 'qux'});
|
||||
var b = tslib_1.__spread(['foo', 'bar'], ['baz', 'qux']);
|
||||
var c = tslib_1.__spreadArrays(['foo', 'bar'], ['baz', 'qux']);
|
||||
})));
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/tslib/index.d.ts'),
|
||||
contents: `
|
||||
export declare function __assign(t: any, ...sources: any[]): any;
|
||||
export declare function __spread(...args: any[][]): any[];
|
||||
export declare function __spreadArrays(...args: any[][]): any[];
|
||||
`,
|
||||
},
|
||||
];
|
||||
loadTestFiles(files);
|
||||
|
||||
const [testFile, tslibFile] = files;
|
||||
const bundle = makeTestBundleProgram(testFile.name);
|
||||
const host = new UmdReflectionHost(new MockLogger(), false, bundle);
|
||||
const {factoryFn} = parseStatementForUmdModule(
|
||||
getSourceFileOrError(bundle.program, testFile.name).statements[0]) !;
|
||||
const tslibSourceFile = getSourceFileOrError(bundle.program, tslibFile.name);
|
||||
|
||||
const testForHelper = createTestForTsHelper(host, factoryFn, () => tslibSourceFile);
|
||||
|
||||
testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib');
|
||||
testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib');
|
||||
testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getExportsOfModule()', () => {
|
||||
|
@ -2038,6 +2242,31 @@ runInEachFileSystem(() => {
|
|||
expect(decl.node).toBeNull();
|
||||
expect(decl.expression).toBeDefined();
|
||||
});
|
||||
|
||||
it('should recognize declarations of known TypeScript helpers', () => {
|
||||
const tslib = {
|
||||
name: _('/tslib.d.ts'),
|
||||
contents: `
|
||||
export declare function __assign(t: any, ...sources: any[]): any;
|
||||
export declare function __spread(...args: any[][]): any[];
|
||||
export declare function __spreadArrays(...args: any[][]): any[];
|
||||
export declare function __unknownHelper(...args: any[]): any;
|
||||
`,
|
||||
};
|
||||
loadTestFiles([tslib]);
|
||||
const bundle = makeTestBundleProgram(tslib.name);
|
||||
const host = new UmdReflectionHost(new MockLogger(), false, bundle);
|
||||
const sf = getSourceFileOrError(bundle.program, tslib.name);
|
||||
const exportDeclarations = host.getExportsOfModule(sf) !;
|
||||
|
||||
expect([...exportDeclarations].map(([exportName, {known}]) => [exportName, known]))
|
||||
.toEqual([
|
||||
['__assign', KnownDeclaration.TsHelperAssign],
|
||||
['__spread', KnownDeclaration.TsHelperSpread],
|
||||
['__spreadArrays', KnownDeclaration.TsHelperSpreadArrays],
|
||||
['__unknownHelper', null],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getClassSymbol()', () => {
|
||||
|
|
|
@ -147,46 +147,79 @@ runInEachFileSystem(() => {
|
|||
});
|
||||
|
||||
['esm5', 'esm2015'].forEach(target => {
|
||||
it(`should be able to process spread operator inside objects for ${target} format`, () => {
|
||||
compileIntoApf(
|
||||
'test-package', {
|
||||
'/index.ts': `
|
||||
import {Directive, Input, NgModule} from '@angular/core';
|
||||
it(`should be able to process spread operator inside objects for ${target} format (imported helpers)`,
|
||||
() => {
|
||||
compileIntoApf(
|
||||
'test-package', {
|
||||
'/index.ts': `
|
||||
import {Directive, Input, NgModule} from '@angular/core';
|
||||
|
||||
const a = { '[class.a]': 'true' };
|
||||
const b = { '[class.b]': 'true' };
|
||||
const a = { '[class.a]': 'true' };
|
||||
const b = { '[class.b]': 'true' };
|
||||
|
||||
@Directive({
|
||||
selector: '[foo]',
|
||||
host: {...a, ...b, '[class.c]': 'false'}
|
||||
})
|
||||
export class FooDirective {}
|
||||
@Directive({
|
||||
selector: '[foo]',
|
||||
host: {...a, ...b, '[class.c]': 'false'}
|
||||
})
|
||||
export class FooDirective {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [FooDirective],
|
||||
})
|
||||
export class FooModule {}
|
||||
`,
|
||||
},
|
||||
{importHelpers: true});
|
||||
@NgModule({
|
||||
declarations: [FooDirective],
|
||||
})
|
||||
export class FooModule {}
|
||||
`,
|
||||
},
|
||||
{importHelpers: true, noEmitHelpers: true});
|
||||
|
||||
// TODO: add test with import helpers disabled. This currently won't work because
|
||||
// inlined TS helper functions are not detected. For more details, see PR:
|
||||
// https://github.com/angular/angular/pull/34169
|
||||
fs.writeFile(
|
||||
_('/node_modules/tslib/index.d.ts'),
|
||||
`export declare function __assign(...args: object[]): object;`);
|
||||
fs.writeFile(
|
||||
_('/node_modules/tslib/index.d.ts'),
|
||||
`export declare function __assign(...args: object[]): object;`);
|
||||
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
targetEntryPointPath: 'test-package',
|
||||
propertiesToConsider: [target],
|
||||
});
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
targetEntryPointPath: 'test-package',
|
||||
propertiesToConsider: [target],
|
||||
});
|
||||
|
||||
const jsContents = fs.readFile(_(`/node_modules/test-package/${target}/src/index.js`))
|
||||
.replace(/\s+/g, ' ');
|
||||
expect(jsContents).toContain('ngcc0.ɵɵclassProp("a", true)("b", true)("c", false)');
|
||||
});
|
||||
const jsContents = fs.readFile(_(`/node_modules/test-package/${target}/src/index.js`))
|
||||
.replace(/\s+/g, ' ');
|
||||
expect(jsContents).toContain('ngcc0.ɵɵclassProp("a", true)("b", true)("c", false)');
|
||||
});
|
||||
|
||||
it(`should be able to process emitted spread operator inside objects for ${target} format (emitted helpers)`,
|
||||
() => {
|
||||
compileIntoApf(
|
||||
'test-package', {
|
||||
'/index.ts': `
|
||||
import {Directive, Input, NgModule} from '@angular/core';
|
||||
|
||||
const a = { '[class.a]': 'true' };
|
||||
const b = { '[class.b]': 'true' };
|
||||
|
||||
@Directive({
|
||||
selector: '[foo]',
|
||||
host: {...a, ...b, '[class.c]': 'false'}
|
||||
})
|
||||
export class FooDirective {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [FooDirective],
|
||||
})
|
||||
export class FooModule {}
|
||||
`,
|
||||
},
|
||||
{importHelpers: false, noEmitHelpers: false});
|
||||
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
targetEntryPointPath: 'test-package',
|
||||
propertiesToConsider: [target],
|
||||
});
|
||||
|
||||
const jsContents = fs.readFile(_(`/node_modules/test-package/${target}/src/index.js`))
|
||||
.replace(/\s+/g, ' ');
|
||||
expect(jsContents).toContain('ngcc0.ɵɵclassProp("a", true)("b", true)("c", false)');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not add `const` in ES5 generated code', () => {
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {FactoryMap, isRelativePath, stripExtension} from '../src/utils';
|
||||
import * as ts from 'typescript';
|
||||
import {KnownDeclaration} from '../../src/ngtsc/reflection';
|
||||
import {FactoryMap, getTsHelperFnFromDeclaration, getTsHelperFnFromIdentifier, isRelativePath, stripExtension} from '../src/utils';
|
||||
|
||||
describe('FactoryMap', () => {
|
||||
it('should return an existing value', () => {
|
||||
|
@ -50,6 +52,121 @@ describe('FactoryMap', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getTsHelperFnFromDeclaration()', () => {
|
||||
const createFunctionDeclaration = (fnName?: string) => ts.createFunctionDeclaration(
|
||||
undefined, undefined, undefined, fnName, undefined, [], undefined, undefined);
|
||||
const createVariableDeclaration = (varName: string) =>
|
||||
ts.createVariableDeclaration(varName, undefined, undefined);
|
||||
|
||||
it('should recognize the `__assign` helper as function declaration', () => {
|
||||
const decl1 = createFunctionDeclaration('__assign');
|
||||
const decl2 = createFunctionDeclaration('__assign$42');
|
||||
|
||||
expect(getTsHelperFnFromDeclaration(decl1)).toBe(KnownDeclaration.TsHelperAssign);
|
||||
expect(getTsHelperFnFromDeclaration(decl2)).toBe(KnownDeclaration.TsHelperAssign);
|
||||
});
|
||||
|
||||
it('should recognize the `__assign` helper as variable declaration', () => {
|
||||
const decl1 = createVariableDeclaration('__assign');
|
||||
const decl2 = createVariableDeclaration('__assign$42');
|
||||
|
||||
expect(getTsHelperFnFromDeclaration(decl1)).toBe(KnownDeclaration.TsHelperAssign);
|
||||
expect(getTsHelperFnFromDeclaration(decl2)).toBe(KnownDeclaration.TsHelperAssign);
|
||||
});
|
||||
|
||||
it('should recognize the `__spread` helper as function declaration', () => {
|
||||
const decl1 = createFunctionDeclaration('__spread');
|
||||
const decl2 = createFunctionDeclaration('__spread$42');
|
||||
|
||||
expect(getTsHelperFnFromDeclaration(decl1)).toBe(KnownDeclaration.TsHelperSpread);
|
||||
expect(getTsHelperFnFromDeclaration(decl2)).toBe(KnownDeclaration.TsHelperSpread);
|
||||
});
|
||||
|
||||
it('should recognize the `__spread` helper as variable declaration', () => {
|
||||
const decl1 = createVariableDeclaration('__spread');
|
||||
const decl2 = createVariableDeclaration('__spread$42');
|
||||
|
||||
expect(getTsHelperFnFromDeclaration(decl1)).toBe(KnownDeclaration.TsHelperSpread);
|
||||
expect(getTsHelperFnFromDeclaration(decl2)).toBe(KnownDeclaration.TsHelperSpread);
|
||||
});
|
||||
|
||||
it('should recognize the `__spreadArrays` helper as function declaration', () => {
|
||||
const decl1 = createFunctionDeclaration('__spreadArrays');
|
||||
const decl2 = createFunctionDeclaration('__spreadArrays$42');
|
||||
|
||||
expect(getTsHelperFnFromDeclaration(decl1)).toBe(KnownDeclaration.TsHelperSpreadArrays);
|
||||
expect(getTsHelperFnFromDeclaration(decl2)).toBe(KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should recognize the `__spreadArrays` helper as variable declaration', () => {
|
||||
const decl1 = createVariableDeclaration('__spreadArrays');
|
||||
const decl2 = createVariableDeclaration('__spreadArrays$42');
|
||||
|
||||
expect(getTsHelperFnFromDeclaration(decl1)).toBe(KnownDeclaration.TsHelperSpreadArrays);
|
||||
expect(getTsHelperFnFromDeclaration(decl2)).toBe(KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should return null for unrecognized helpers', () => {
|
||||
const decl1 = createFunctionDeclaration('__foo');
|
||||
const decl2 = createVariableDeclaration('spread');
|
||||
const decl3 = createFunctionDeclaration('spread$42');
|
||||
|
||||
expect(getTsHelperFnFromDeclaration(decl1)).toBe(null);
|
||||
expect(getTsHelperFnFromDeclaration(decl2)).toBe(null);
|
||||
expect(getTsHelperFnFromDeclaration(decl3)).toBe(null);
|
||||
});
|
||||
|
||||
it('should return null for unnamed declarations', () => {
|
||||
const unnamledDecl = createFunctionDeclaration(undefined);
|
||||
|
||||
expect(getTsHelperFnFromDeclaration(unnamledDecl)).toBe(null);
|
||||
});
|
||||
|
||||
it('should return null for non-function/variable declarations', () => {
|
||||
const classDecl =
|
||||
ts.createClassDeclaration(undefined, undefined, '__assign', undefined, undefined, []);
|
||||
|
||||
expect(classDecl.name !.text).toBe('__assign');
|
||||
expect(getTsHelperFnFromDeclaration(classDecl)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTsHelperFnFromIdentifier()', () => {
|
||||
it('should recognize the `__assign` helper', () => {
|
||||
const id1 = ts.createIdentifier('__assign');
|
||||
const id2 = ts.createIdentifier('__assign$42');
|
||||
|
||||
expect(getTsHelperFnFromIdentifier(id1)).toBe(KnownDeclaration.TsHelperAssign);
|
||||
expect(getTsHelperFnFromIdentifier(id2)).toBe(KnownDeclaration.TsHelperAssign);
|
||||
});
|
||||
|
||||
it('should recognize the `__spread` helper', () => {
|
||||
const id1 = ts.createIdentifier('__spread');
|
||||
const id2 = ts.createIdentifier('__spread$42');
|
||||
|
||||
expect(getTsHelperFnFromIdentifier(id1)).toBe(KnownDeclaration.TsHelperSpread);
|
||||
expect(getTsHelperFnFromIdentifier(id2)).toBe(KnownDeclaration.TsHelperSpread);
|
||||
});
|
||||
|
||||
it('should recognize the `__spreadArrays` helper', () => {
|
||||
const id1 = ts.createIdentifier('__spreadArrays');
|
||||
const id2 = ts.createIdentifier('__spreadArrays$42');
|
||||
|
||||
expect(getTsHelperFnFromIdentifier(id1)).toBe(KnownDeclaration.TsHelperSpreadArrays);
|
||||
expect(getTsHelperFnFromIdentifier(id2)).toBe(KnownDeclaration.TsHelperSpreadArrays);
|
||||
});
|
||||
|
||||
it('should return null for unrecognized helpers', () => {
|
||||
const id1 = ts.createIdentifier('__foo');
|
||||
const id2 = ts.createIdentifier('spread');
|
||||
const id3 = ts.createIdentifier('spread$42');
|
||||
|
||||
expect(getTsHelperFnFromIdentifier(id1)).toBe(null);
|
||||
expect(getTsHelperFnFromIdentifier(id2)).toBe(null);
|
||||
expect(getTsHelperFnFromIdentifier(id3)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isRelativePath()', () => {
|
||||
it('should return true for relative paths', () => {
|
||||
expect(isRelativePath('.')).toBe(true);
|
||||
|
|
|
@ -19,7 +19,6 @@ import {DynamicValue} from './dynamic';
|
|||
import {ForeignFunctionResolver} from './interface';
|
||||
import {resolveKnownDeclaration} from './known_declaration';
|
||||
import {EnumValue, KnownFn, ResolvedModule, ResolvedValue, ResolvedValueArray, ResolvedValueMap} from './result';
|
||||
import {evaluateTsHelperInline} from './ts_helpers';
|
||||
|
||||
|
||||
|
||||
|
@ -333,6 +332,10 @@ export class StaticInterpreter {
|
|||
}
|
||||
|
||||
return new ResolvedModule(declarations, decl => {
|
||||
if (decl.known !== null) {
|
||||
return resolveKnownDeclaration(decl.known);
|
||||
}
|
||||
|
||||
const declContext = {
|
||||
...context, ...joinModuleContext(context, node, decl),
|
||||
};
|
||||
|
@ -417,12 +420,6 @@ export class StaticInterpreter {
|
|||
return DynamicValue.fromInvalidExpressionType(node.expression, lhs);
|
||||
}
|
||||
|
||||
// If the function corresponds with a tslib helper function, evaluate it with custom logic.
|
||||
if (fn.helper !== null) {
|
||||
const args = this.evaluateFunctionArguments(node, context);
|
||||
return evaluateTsHelperInline(fn.helper, node, args);
|
||||
}
|
||||
|
||||
if (!isFunctionOrMethodReference(lhs)) {
|
||||
return DynamicValue.fromInvalidExpressionType(node.expression, lhs);
|
||||
}
|
||||
|
|
|
@ -10,19 +10,31 @@ import {KnownDeclaration} from '../../reflection/src/host';
|
|||
|
||||
import {ObjectAssignBuiltinFn} from './builtin';
|
||||
import {ResolvedValue} from './result';
|
||||
import {AssignHelperFn, SpreadHelperFn} from './ts_helpers';
|
||||
|
||||
/** Resolved value for the JavaScript global `Object` declaration .*/
|
||||
/** Resolved value for the JavaScript global `Object` declaration. */
|
||||
export const jsGlobalObjectValue = new Map([['assign', new ObjectAssignBuiltinFn()]]);
|
||||
|
||||
/** Resolved value for the `__assign()` TypeScript helper declaration. */
|
||||
const assignTsHelperFn = new AssignHelperFn();
|
||||
|
||||
/** Resolved value for the `__spread()` and `__spreadArrays()` TypeScript helper declarations. */
|
||||
const spreadTsHelperFn = new SpreadHelperFn();
|
||||
|
||||
/**
|
||||
* Resolves the specified known declaration to a resolved value. For example,
|
||||
* the known JavaScript global `Object` will resolve to a `Map` that provides the
|
||||
* `assign` method with a builtin function. This enables evaluation of `Object.assign`.
|
||||
* `assign` method with a built-in function. This enables evaluation of `Object.assign`.
|
||||
*/
|
||||
export function resolveKnownDeclaration(decl: KnownDeclaration): ResolvedValue {
|
||||
switch (decl) {
|
||||
case KnownDeclaration.JsGlobalObject:
|
||||
return jsGlobalObjectValue;
|
||||
case KnownDeclaration.TsHelperAssign:
|
||||
return assignTsHelperFn;
|
||||
case KnownDeclaration.TsHelperSpread:
|
||||
case KnownDeclaration.TsHelperSpreadArrays:
|
||||
return spreadTsHelperFn;
|
||||
default:
|
||||
throw new Error(`Cannot resolve known declaration. Received: ${KnownDeclaration[decl]}.`);
|
||||
}
|
||||
|
|
|
@ -8,44 +8,30 @@
|
|||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {TsHelperFn} from '../../reflection';
|
||||
|
||||
import {ObjectAssignBuiltinFn} from './builtin';
|
||||
import {DynamicValue} from './dynamic';
|
||||
import {ResolvedValue, ResolvedValueArray} from './result';
|
||||
import {KnownFn, ResolvedValueArray} from './result';
|
||||
|
||||
|
||||
/**
|
||||
* Instance of the known `Object.assign` built-in function. Used for evaluating
|
||||
* the `__assign` TypeScript helper.
|
||||
*/
|
||||
const objectAssignBuiltinFn = new ObjectAssignBuiltinFn();
|
||||
// Use the same implementation we use for `Object.assign()`. Semantically these functions are the
|
||||
// same, so they can also share the same evaluation code.
|
||||
export class AssignHelperFn extends ObjectAssignBuiltinFn {}
|
||||
|
||||
export function evaluateTsHelperInline(
|
||||
helper: TsHelperFn, node: ts.CallExpression, args: ResolvedValueArray): ResolvedValue {
|
||||
switch (helper) {
|
||||
case TsHelperFn.Assign:
|
||||
// Use the same implementation we use for `Object.assign`. Semantically these
|
||||
// functions are the same, so they can also share the same evaluation code.
|
||||
return objectAssignBuiltinFn.evaluate(node, args);
|
||||
case TsHelperFn.Spread:
|
||||
case TsHelperFn.SpreadArrays:
|
||||
return evaluateTsSpreadHelper(node, args);
|
||||
default:
|
||||
throw new Error(`Cannot evaluate TypeScript helper function: ${TsHelperFn[helper]}`);
|
||||
}
|
||||
}
|
||||
// Used for both `__spread()` and `__spreadArrays()` TypeScript helper functions.
|
||||
export class SpreadHelperFn extends KnownFn {
|
||||
evaluate(node: ts.Node, args: ResolvedValueArray): ResolvedValueArray {
|
||||
const result: ResolvedValueArray = [];
|
||||
|
||||
function evaluateTsSpreadHelper(node: ts.Node, args: ResolvedValueArray): ResolvedValueArray {
|
||||
const result: ResolvedValueArray = [];
|
||||
for (const arg of args) {
|
||||
if (arg instanceof DynamicValue) {
|
||||
result.push(DynamicValue.fromDynamicInput(node, arg));
|
||||
} else if (Array.isArray(arg)) {
|
||||
result.push(...arg);
|
||||
} else {
|
||||
result.push(arg);
|
||||
for (const arg of args) {
|
||||
if (arg instanceof DynamicValue) {
|
||||
result.push(DynamicValue.fromDynamicInput(node, arg));
|
||||
} else if (Array.isArray(arg)) {
|
||||
result.push(...arg);
|
||||
} else {
|
||||
result.push(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@ import {absoluteFrom, getSourceFileOrError} from '../../file_system';
|
|||
import {runInEachFileSystem} from '../../file_system/testing';
|
||||
import {Reference} from '../../imports';
|
||||
import {DependencyTracker} from '../../incremental/api';
|
||||
import {FunctionDefinition, TsHelperFn, TypeScriptReflectionHost} from '../../reflection';
|
||||
import {Declaration, KnownDeclaration, TypeScriptReflectionHost} from '../../reflection';
|
||||
import {getDeclaration, makeProgram} from '../../testing';
|
||||
import {DynamicValue} from '../src/dynamic';
|
||||
import {PartialEvaluator} from '../src/interface';
|
||||
import {EnumValue} from '../src/result';
|
||||
import {EnumValue, ResolvedValue} from '../src/result';
|
||||
|
||||
import {evaluate, firstArgFfr, makeEvaluator, makeExpression, owningModuleOf} from './utils';
|
||||
|
||||
|
@ -538,69 +538,209 @@ runInEachFileSystem(() => {
|
|||
expect((value.node as ts.CallExpression).expression.getText()).toBe('foo');
|
||||
});
|
||||
|
||||
it('should evaluate TypeScript __spread helper', () => {
|
||||
const {checker, expression} = makeExpression(
|
||||
`
|
||||
import * as tslib from 'tslib';
|
||||
const a = [1];
|
||||
const b = [2, 3];
|
||||
`,
|
||||
'tslib.__spread(a, b)', [
|
||||
{
|
||||
name: _('/node_modules/tslib/index.d.ts'),
|
||||
contents: `
|
||||
export declare function __spread(...args: any[][]): any[];
|
||||
`
|
||||
},
|
||||
]);
|
||||
const reflectionHost = new TsLibAwareReflectionHost(checker);
|
||||
const evaluator = new PartialEvaluator(reflectionHost, checker, null);
|
||||
const value = evaluator.evaluate(expression);
|
||||
expect(value).toEqual([1, 2, 3]);
|
||||
describe('(with imported TypeScript helpers)', () => {
|
||||
// Helpers
|
||||
const evaluateExpression = <T extends ResolvedValue>(code: string, expr: string) => {
|
||||
const {checker, expression} = makeExpression(code, expr, [
|
||||
{
|
||||
name: _('/node_modules/tslib/index.d.ts'),
|
||||
contents: `
|
||||
export declare function __assign(t: any, ...sources: any[]): any;
|
||||
export declare function __spread(...args: any[][]): any[];
|
||||
export declare function __spreadArrays(...args: any[][]): any[];
|
||||
`,
|
||||
},
|
||||
]);
|
||||
|
||||
const reflectionHost = new TsLibAwareReflectionHost(checker);
|
||||
const evaluator = new PartialEvaluator(reflectionHost, checker, null);
|
||||
|
||||
return evaluator.evaluate(expression) as T;
|
||||
};
|
||||
|
||||
it('should evaluate `__assign()` (named import)', () => {
|
||||
const map: Map<string, boolean> = evaluateExpression(
|
||||
`
|
||||
import {__assign} from 'tslib';
|
||||
const a = {a: true};
|
||||
const b = {b: true};
|
||||
`,
|
||||
'__assign(a, b)');
|
||||
|
||||
expect([...map]).toEqual([
|
||||
['a', true],
|
||||
['b', true],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should evaluate `__assign()` (star import)', () => {
|
||||
const map: Map<string, boolean> = evaluateExpression(
|
||||
`
|
||||
import * as tslib from 'tslib';
|
||||
const a = {a: true};
|
||||
const b = {b: true};
|
||||
`,
|
||||
'tslib.__assign(a, b)');
|
||||
|
||||
expect([...map]).toEqual([
|
||||
['a', true],
|
||||
['b', true],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should evaluate `__spread()` (named import)', () => {
|
||||
const arr: number[] = evaluateExpression(
|
||||
`
|
||||
import {__spread} from 'tslib';
|
||||
const a = [1];
|
||||
const b = [2, 3];
|
||||
`,
|
||||
'__spread(a, b)');
|
||||
|
||||
expect(arr).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should evaluate `__spread()` (star import)', () => {
|
||||
const arr: number[] = evaluateExpression(
|
||||
`
|
||||
import * as tslib from 'tslib';
|
||||
const a = [1];
|
||||
const b = [2, 3];
|
||||
`,
|
||||
'tslib.__spread(a, b)');
|
||||
|
||||
expect(arr).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should evaluate `__spreadArrays()` (named import)', () => {
|
||||
const arr: number[] = evaluateExpression(
|
||||
`
|
||||
import {__spreadArrays} from 'tslib';
|
||||
const a = [4];
|
||||
const b = [5, 6];
|
||||
`,
|
||||
'__spreadArrays(a, b)');
|
||||
|
||||
expect(arr).toEqual([4, 5, 6]);
|
||||
});
|
||||
|
||||
it('should evaluate `__spreadArrays()` (star import)', () => {
|
||||
const arr: number[] = evaluateExpression(
|
||||
`
|
||||
import * as tslib from 'tslib';
|
||||
const a = [4];
|
||||
const b = [5, 6];
|
||||
`,
|
||||
'tslib.__spreadArrays(a, b)');
|
||||
|
||||
expect(arr).toEqual([4, 5, 6]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should evaluate TypeScript __spreadArrays helper', () => {
|
||||
const {checker, expression} = makeExpression(
|
||||
`
|
||||
import * as tslib from 'tslib';
|
||||
const a = [1];
|
||||
const b = [2, 3];
|
||||
`,
|
||||
'tslib.__spreadArrays(a, b)', [
|
||||
{
|
||||
name: _('/node_modules/tslib/index.d.ts'),
|
||||
contents: `
|
||||
export declare function __spreadArrays(...args: any[][]): any[];
|
||||
`
|
||||
},
|
||||
]);
|
||||
const reflectionHost = new TsLibAwareReflectionHost(checker);
|
||||
const evaluator = new PartialEvaluator(reflectionHost, checker, null);
|
||||
const value = evaluator.evaluate(expression);
|
||||
expect(value).toEqual([1, 2, 3]);
|
||||
describe('(with emitted TypeScript helpers as functions)', () => {
|
||||
// Helpers
|
||||
const evaluateExpression = <T extends ResolvedValue>(code: string, expr: string) => {
|
||||
const helpers = `
|
||||
function __assign(t, ...sources) { /* ... */ }
|
||||
function __spread(...args) { /* ... */ }
|
||||
function __spreadArrays(...args) { /* ... */ }
|
||||
`;
|
||||
const {checker, expression} = makeExpression(helpers + code, expr);
|
||||
|
||||
const reflectionHost = new TsLibAwareReflectionHost(checker);
|
||||
const evaluator = new PartialEvaluator(reflectionHost, checker, null);
|
||||
|
||||
return evaluator.evaluate(expression) as T;
|
||||
};
|
||||
|
||||
it('should evaluate `__assign()`', () => {
|
||||
const map: Map<string, boolean> = evaluateExpression(
|
||||
`
|
||||
const a = {a: true};
|
||||
const b = {b: true};
|
||||
`,
|
||||
'__assign(a, b)');
|
||||
|
||||
expect([...map]).toEqual([
|
||||
['a', true],
|
||||
['b', true],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should evaluate `__spread()`', () => {
|
||||
const arr: number[] = evaluateExpression(
|
||||
`
|
||||
const a = [1];
|
||||
const b = [2, 3];
|
||||
`,
|
||||
'__spread(a, b)');
|
||||
|
||||
expect(arr).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should evaluate `__spreadArrays()`', () => {
|
||||
const arr: number[] = evaluateExpression(
|
||||
`
|
||||
const a = [4];
|
||||
const b = [5, 6];
|
||||
`,
|
||||
'__spreadArrays(a, b)');
|
||||
|
||||
expect(arr).toEqual([4, 5, 6]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should evaluate TypeScript __assign helper', () => {
|
||||
const {checker, expression} = makeExpression(
|
||||
`
|
||||
import * as tslib from 'tslib';
|
||||
const a = {a: true};
|
||||
const b = {b: true};
|
||||
`,
|
||||
'tslib.__assign(a, b)', [
|
||||
{
|
||||
name: _('/node_modules/tslib/index.d.ts'),
|
||||
contents: `
|
||||
export declare function __assign(...args: object[]): object;
|
||||
`
|
||||
},
|
||||
]);
|
||||
const reflectionHost = new TsLibAwareReflectionHost(checker);
|
||||
const evaluator = new PartialEvaluator(reflectionHost, checker, null);
|
||||
const map = evaluator.evaluate(expression) as Map<string, boolean>;
|
||||
const obj: {[key: string]: boolean} = {};
|
||||
map.forEach((value, key) => obj[key] = value);
|
||||
expect(obj).toEqual({a: true, b: true});
|
||||
describe('(with emitted TypeScript helpers as variables)', () => {
|
||||
// Helpers
|
||||
const evaluateExpression = <T extends ResolvedValue>(code: string, expr: string) => {
|
||||
const helpers = `
|
||||
var __assign = (this && this.__assign) || function (t, ...sources) { /* ... */ }
|
||||
var __spread = (this && this.__spread) || function (...args) { /* ... */ }
|
||||
var __spreadArrays = (this && this.__spreadArrays) || function (...args) { /* ... */ }
|
||||
`;
|
||||
const {checker, expression} = makeExpression(helpers + code, expr);
|
||||
|
||||
const reflectionHost = new TsLibAwareReflectionHost(checker);
|
||||
const evaluator = new PartialEvaluator(reflectionHost, checker, null);
|
||||
|
||||
return evaluator.evaluate(expression) as T;
|
||||
};
|
||||
|
||||
it('should evaluate `__assign()`', () => {
|
||||
const map: Map<string, boolean> = evaluateExpression(
|
||||
`
|
||||
const a = {a: true};
|
||||
const b = {b: true};
|
||||
`,
|
||||
'__assign(a, b)');
|
||||
|
||||
expect([...map]).toEqual([
|
||||
['a', true],
|
||||
['b', true],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should evaluate `__spread()`', () => {
|
||||
const arr: number[] = evaluateExpression(
|
||||
`
|
||||
const a = [1];
|
||||
const b = [2, 3];
|
||||
`,
|
||||
'__spread(a, b)');
|
||||
|
||||
expect(arr).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should evaluate `__spreadArrays()`', () => {
|
||||
const arr: number[] = evaluateExpression(
|
||||
`
|
||||
const a = [4];
|
||||
const b = [5, 6];
|
||||
`,
|
||||
'__spreadArrays(a, b)');
|
||||
|
||||
expect(arr).toEqual([4, 5, 6]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('(visited file tracking)', () => {
|
||||
|
@ -665,36 +805,52 @@ runInEachFileSystem(() => {
|
|||
});
|
||||
|
||||
/**
|
||||
* Customizes the resolution of functions to recognize functions from tslib. Such functions are
|
||||
* not handled specially in the default TypeScript host, as only ngcc's ES5 host will have special
|
||||
* powers to recognize functions from tslib.
|
||||
* Customizes the resolution of module exports and identifier declarations to recognize known
|
||||
* helper functions from `tslib`. Such functions are not handled specially in the default
|
||||
* TypeScript host, as only ngcc's ES5 hosts will have special powers to recognize such functions.
|
||||
*/
|
||||
class TsLibAwareReflectionHost extends TypeScriptReflectionHost {
|
||||
getDefinitionOfFunction(node: ts.Node): FunctionDefinition|null {
|
||||
if (ts.isFunctionDeclaration(node)) {
|
||||
const helper = getTsHelperFn(node);
|
||||
if (helper !== null) {
|
||||
return {
|
||||
node,
|
||||
body: null, helper,
|
||||
parameters: [],
|
||||
};
|
||||
}
|
||||
getExportsOfModule(node: ts.Node): Map<string, Declaration>|null {
|
||||
const map = super.getExportsOfModule(node);
|
||||
|
||||
if (map !== null) {
|
||||
map.forEach(decl => decl.known = decl.known || (decl.node && getTsHelperFn(decl.node)));
|
||||
}
|
||||
return super.getDefinitionOfFunction(node);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null {
|
||||
const superDeclaration = super.getDeclarationOfIdentifier(id);
|
||||
|
||||
if (superDeclaration === null || superDeclaration.node === null) {
|
||||
return superDeclaration;
|
||||
}
|
||||
|
||||
const tsHelperFn = getTsHelperFn(superDeclaration.node);
|
||||
if (tsHelperFn !== null) {
|
||||
return {
|
||||
known: tsHelperFn,
|
||||
node: id,
|
||||
viaModule: null,
|
||||
};
|
||||
}
|
||||
|
||||
return superDeclaration;
|
||||
}
|
||||
}
|
||||
|
||||
function getTsHelperFn(node: ts.FunctionDeclaration): TsHelperFn|null {
|
||||
const name = node.name !== undefined && ts.isIdentifier(node.name) && node.name.text;
|
||||
function getTsHelperFn(node: ts.Declaration): KnownDeclaration|null {
|
||||
const id = (node as ts.Declaration & {name?: ts.Identifier}).name || null;
|
||||
const name = id && id.text;
|
||||
|
||||
switch (name) {
|
||||
case '__assign':
|
||||
return TsHelperFn.Assign;
|
||||
return KnownDeclaration.TsHelperAssign;
|
||||
case '__spread':
|
||||
return TsHelperFn.Spread;
|
||||
return KnownDeclaration.TsHelperSpread;
|
||||
case '__spreadArrays':
|
||||
return TsHelperFn.SpreadArrays;
|
||||
return KnownDeclaration.TsHelperSpreadArrays;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -316,12 +316,6 @@ export interface FunctionDefinition {
|
|||
*/
|
||||
body: ts.Statement[]|null;
|
||||
|
||||
/**
|
||||
* The type of tslib helper function, if the function is determined to represent a tslib helper
|
||||
* function. Otherwise, this will be null.
|
||||
*/
|
||||
helper: TsHelperFn|null;
|
||||
|
||||
/**
|
||||
* Metadata regarding the function's parameters, including possible default value expressions.
|
||||
*/
|
||||
|
@ -329,31 +323,28 @@ export interface FunctionDefinition {
|
|||
}
|
||||
|
||||
/**
|
||||
* Possible functions from TypeScript's helper library.
|
||||
*/
|
||||
export enum TsHelperFn {
|
||||
/**
|
||||
* Indicates the `__assign` function.
|
||||
*/
|
||||
Assign,
|
||||
/**
|
||||
* Indicates the `__spread` function.
|
||||
*/
|
||||
Spread,
|
||||
/**
|
||||
* Indicates the `__spreadArrays` function.
|
||||
*/
|
||||
SpreadArrays,
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible declarations which are known.
|
||||
* Possible declarations of known values, such as built-in objects/functions or TypeScript helpers.
|
||||
*/
|
||||
export enum KnownDeclaration {
|
||||
/**
|
||||
* Indicates the JavaScript global `Object` class.
|
||||
*/
|
||||
JsGlobalObject,
|
||||
|
||||
/**
|
||||
* Indicates the `__assign` TypeScript helper function.
|
||||
*/
|
||||
TsHelperAssign,
|
||||
|
||||
/**
|
||||
* Indicates the `__spread` TypeScript helper function.
|
||||
*/
|
||||
TsHelperSpread,
|
||||
|
||||
/**
|
||||
* Indicates the `__spreadArrays` TypeScript helper function.
|
||||
*/
|
||||
TsHelperSpreadArrays,
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -164,7 +164,6 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
|||
return {
|
||||
node,
|
||||
body: node.body !== undefined ? Array.from(node.body.statements) : null,
|
||||
helper: null,
|
||||
parameters: node.parameters.map(param => {
|
||||
const name = parameterName(param.name);
|
||||
const initializer = param.initializer || null;
|
||||
|
@ -266,10 +265,8 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
|||
|
||||
/**
|
||||
* Resolve a `ts.Symbol` to its declaration, keeping track of the `viaModule` along the way.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
private getDeclarationOfSymbol(symbol: ts.Symbol, originalId: ts.Identifier|null): Declaration
|
||||
protected getDeclarationOfSymbol(symbol: ts.Symbol, originalId: ts.Identifier|null): Declaration
|
||||
|null {
|
||||
// If the symbol points to a ShorthandPropertyAssignment, resolve it.
|
||||
let valueDeclaration: ts.Declaration|undefined = undefined;
|
||||
|
|
Loading…
Reference in New Issue