From ec8a565655972db61efab2ade0b88617f91ef409 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 18 Oct 2021 17:24:21 +0100 Subject: [PATCH] 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 --- .../compiler-cli/ngcc/src/host/umd_host.ts | 40 ++++++++++++------- .../ngcc/test/packages/entry_point_spec.ts | 19 +++++++++ 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/host/umd_host.ts b/packages/compiler-cli/ngcc/src/host/umd_host.ts index 5ba952e7cff..2bf0f2f77a2 100644 --- a/packages/compiler-cli/ngcc/src/host/umd_host.ts +++ b/packages/compiler-cli/ngcc/src/host/umd_host.ts @@ -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; } diff --git a/packages/compiler-cli/ngcc/test/packages/entry_point_spec.ts b/packages/compiler-cli/ngcc/test/packages/entry_point_spec.ts index db9ec15c247..d8720dbd418 100644 --- a/packages/compiler-cli/ngcc/test/packages/entry_point_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/entry_point_spec.ts @@ -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', () => {