Skip to content

Commit

Permalink
fix(ngcc): handle new __spreadArrays tslib helper (#33617)
Browse files Browse the repository at this point in the history
We already have special cases for the `__spread` helper function and with this change we handle the new tslib helper introduced in version 1.10 `__spreadArrays`.

For more context see: https://github.com/microsoft/tslib/releases/tag/1.10.0

Fixes: #33614

PR Close #33617
  • Loading branch information
alan-agius4 authored and atscott committed Nov 6, 2019
1 parent 2a4061a commit d749dd3
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 14 deletions.
11 changes: 7 additions & 4 deletions packages/compiler-cli/ngcc/src/host/esm5_host.ts
Expand Up @@ -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;
}
}

Expand Down
74 changes: 73 additions & 1 deletion packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts
Expand Up @@ -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);
Expand Down Expand Up @@ -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()', () => {
Expand Down
Expand Up @@ -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`);
}
}

Expand Down
Expand Up @@ -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[];
`
},
]);
Expand Down Expand Up @@ -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;
}
}
});
4 changes: 4 additions & 0 deletions packages/compiler-cli/src/ngtsc/reflection/src/host.ts
Expand Up @@ -336,6 +336,10 @@ export enum TsHelperFn {
* Indicates the `__spread` function.
*/
Spread,
/**
* Indicates the `__spreadArrays` function.
*/
SpreadArrays,
}

/**
Expand Down

0 comments on commit d749dd3

Please sign in to comment.