diff --git a/packages/compiler-cli/ngcc/src/host/esm5_host.ts b/packages/compiler-cli/ngcc/src/host/esm5_host.ts index e8d6754588ee7..73cd5a36c58ef 100644 --- a/packages/compiler-cli/ngcc/src/host/esm5_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm5_host.ts @@ -627,10 +627,13 @@ function getTsHelperFn(node: ts.NamedDeclaration): TsHelperFn|null { stripDollarSuffix(node.name.text) : null; - if (name === '__spread') { - return TsHelperFn.Spread; - } else { - return null; + switch (name) { + case '__spread': + return TsHelperFn.Spread; + case '__spreadArrays': + return TsHelperFn.SpreadArrays; + default: + return null; } } diff --git a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts index 16f1b9b5c5736..aa3031ff6785a 100644 --- a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts @@ -1651,7 +1651,7 @@ runInEachFileSystem(() => { it('should recognize TypeScript __spread helper function declaration', () => { const file: TestFile = { name: _('/declaration.d.ts'), - contents: `export declare function __spread(...args: any[]): any[];`, + contents: `export declare function __spread(...args: any[][]): any[];`, }; loadTestFiles([file]); const {program} = makeTestBundleProgram(file.name); @@ -1711,6 +1711,78 @@ runInEachFileSystem(() => { 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 {program} = makeTestBundleProgram(file.name); + const host = new Esm5ReflectionHost(new MockLogger(), false, program.getTypeChecker()); + + const node = + getDeclaration(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 {program} = makeTestBundleProgram(file.name); + const host = new Esm5ReflectionHost(new MockLogger(), false, program.getTypeChecker()); + + const node = + getDeclaration(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 {program} = makeTestBundleProgram(file.name); + const host = new Esm5ReflectionHost(new MockLogger(), false, program.getTypeChecker()); + + const node = + getDeclaration(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); + }); }); describe('getImportOfIdentifier()', () => { diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/ts_helpers.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/ts_helpers.ts index 82e4ec805c806..055586b327778 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/ts_helpers.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/ts_helpers.ts @@ -15,10 +15,12 @@ import {ResolvedValue, ResolvedValueArray} from './result'; export function evaluateTsHelperInline( helper: TsHelperFn, node: ts.Node, args: ResolvedValueArray): ResolvedValue { - if (helper === TsHelperFn.Spread) { - return evaluateTsSpreadHelper(node, args); - } else { - throw new Error(`Cannot evaluate unknown helper ${helper} inline`); + switch (helper) { + case TsHelperFn.Spread: + case TsHelperFn.SpreadArrays: + return evaluateTsSpreadHelper(node, args); + default: + throw new Error(`Cannot evaluate unknown helper ${helper} inline`); } } diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts index a071ad349b6bd..d34755f792054 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts @@ -514,7 +514,28 @@ runInEachFileSystem(() => { { name: _('/node_modules/tslib/index.d.ts'), contents: ` - export declare function __spread(...args: any[]): any[]; + export declare function __spread(...args: any[][]): any[]; + ` + }, + ]); + const reflectionHost = new TsLibAwareReflectionHost(checker); + const evaluator = new PartialEvaluator(reflectionHost, checker); + const value = evaluator.evaluate(expression); + expect(value).toEqual([1, 2, 3]); + }); + + 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[]; ` }, ]); @@ -612,10 +633,13 @@ runInEachFileSystem(() => { function getTsHelperFn(node: ts.FunctionDeclaration): TsHelperFn|null { const name = node.name !== undefined && ts.isIdentifier(node.name) && node.name.text; - if (name === '__spread') { - return TsHelperFn.Spread; - } else { - return null; + switch (name) { + case '__spread': + return TsHelperFn.Spread; + case '__spreadArrays': + return TsHelperFn.SpreadArrays; + default: + return null; } } }); diff --git a/packages/compiler-cli/src/ngtsc/reflection/src/host.ts b/packages/compiler-cli/src/ngtsc/reflection/src/host.ts index 9c980a469841b..74c0a5e598b0f 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/src/host.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/src/host.ts @@ -336,6 +336,10 @@ export enum TsHelperFn { * Indicates the `__spread` function. */ Spread, + /** + * Indicates the `__spreadArrays` function. + */ + SpreadArrays, } /**