fix(ngcc): support alternate wrapper function layout for UMD (#43879)

Recently rollup, used by ng-packagr, changed the position of parentheses
around its generated UMD wrapper functions.

This commit ensures that ngcc can handle both.

Fixes #43870

PR Close #43879
This commit is contained in:
Pete Bacon Darwin 2021-10-18 17:24:21 +01:00 committed by Andrew Kushnir
parent 60f3b33b4b
commit ec8a565655
2 changed files with 44 additions and 15 deletions

View file

@ -511,30 +511,40 @@ export class UmdReflectionHost extends Esm5ReflectionHost {
}
export function parseStatementForUmdModule(statement: ts.Statement): UmdModule|null {
const wrapperCall = getUmdWrapperCall(statement);
if (!wrapperCall) return null;
const wrapper = getUmdWrapper(statement);
if (wrapper === null) return null;
const wrapperFn = wrapperCall.expression;
if (!ts.isFunctionExpression(wrapperFn)) return null;
const factoryFnParamIndex = wrapperFn.parameters.findIndex(
const factoryFnParamIndex = wrapper.fn.parameters.findIndex(
parameter => ts.isIdentifier(parameter.name) && parameter.name.text === 'factory');
if (factoryFnParamIndex === -1) return null;
const factoryFn = stripParentheses(wrapperCall.arguments[factoryFnParamIndex]);
const factoryFn = stripParentheses(wrapper.call.arguments[factoryFnParamIndex]);
if (!factoryFn || !ts.isFunctionExpression(factoryFn)) return null;
return {wrapperFn, factoryFn};
return {wrapperFn: wrapper.fn, factoryFn};
}
function getUmdWrapperCall(statement: ts.Statement): ts.CallExpression&
{expression: ts.FunctionExpression}|null {
if (!ts.isExpressionStatement(statement) || !ts.isParenthesizedExpression(statement.expression) ||
!ts.isCallExpression(statement.expression.expression) ||
!ts.isFunctionExpression(statement.expression.expression.expression)) {
return null;
function getUmdWrapper(statement: ts.Statement):
{call: ts.CallExpression, fn: ts.FunctionExpression}|null {
if (!ts.isExpressionStatement(statement)) return null;
if (ts.isParenthesizedExpression(statement.expression) &&
ts.isCallExpression(statement.expression.expression) &&
ts.isFunctionExpression(statement.expression.expression.expression)) {
// (function () { ... } (...) );
const call = statement.expression.expression;
const fn = statement.expression.expression.expression;
return {call, fn};
}
return statement.expression.expression as ts.CallExpression & {expression: ts.FunctionExpression};
if (ts.isCallExpression(statement.expression) &&
ts.isParenthesizedExpression(statement.expression.expression) &&
ts.isFunctionExpression(statement.expression.expression.expression)) {
// (function () { ... }) (...);
const call = statement.expression;
const fn = statement.expression.expression.expression;
return {call, fn};
}
return null;
}

View file

@ -747,6 +747,25 @@ runInEachFileSystem(() => {
entryPoint.packageJson.main = './bundles/valid_entry_point';
expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd');
});
it('should match alternate UMD format', () => {
// Note that in this format, instead of the whole wrapper being enclosed in parentheses,
// only the wrapper function is enclosed and the call is not inside the parentheses.
loadTestFiles([{
name: _(
'/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'),
contents: `
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) :
typeof define === 'function' && define.amd ? define('@angular/common', ['exports', '@angular/core'], factory) :
(global = global || self, factory((global.ng = global.ng || {}, global.ng.common = {}), global.ng.core));
})(this, function (exports, core) { 'use strict'; });
`
}]);
entryPoint.packageJson.main = './bundles/valid_entry_point';
expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd');
});
});
it('should return `undefined` if the `browser` property is not a string', () => {