mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(ngcc): make it easy to support more UMD wrapper function formats
Previously, ngcc could only handle UMD modules whose wrapper function was implemented as a `ts.ConditionalExpression` (i.e. using a ternary operator). This is the format emitted by popular bundlers, such as Rollup. However, this failed to account for a different format, using `if/else` statements, such as the one [emitted by Webpack][1]. This commit prepares ngcc for supporting different UMD wrapper function formats by decoupling the operation of parsing the wrapper function body to capture the various factory function calls and that of operating on the factory function calls (for example, to read or update their arguments). In a subsequent commit, this will be used to add support for the Webpack format. [1]: https://webpack.js.org/configuration/output/#type-umd
This commit is contained in:
parent
b31973c176
commit
2bc3522e16
4 changed files with 257 additions and 131 deletions
|
|
@ -292,9 +292,9 @@ export class UmdReflectionHost extends Esm5ReflectionHost {
|
|||
statement: WildcardReexportStatement, containingFile: ts.SourceFile): ExportDeclaration[] {
|
||||
const reexportArg = statement.expression.arguments[0];
|
||||
|
||||
const requireCall = isRequireCall(reexportArg) ?
|
||||
reexportArg :
|
||||
ts.isIdentifier(reexportArg) ? findRequireCallReference(reexportArg, this.checker) : null;
|
||||
const requireCall = isRequireCall(reexportArg) ? reexportArg :
|
||||
ts.isIdentifier(reexportArg) ? findRequireCallReference(reexportArg, this.checker) :
|
||||
null;
|
||||
|
||||
let importPath: string|null = null;
|
||||
|
||||
|
|
@ -521,7 +521,25 @@ export function parseStatementForUmdModule(statement: ts.Statement): UmdModule|n
|
|||
const factoryFn = stripParentheses(wrapper.call.arguments[factoryFnParamIndex]);
|
||||
if (!factoryFn || !ts.isFunctionExpression(factoryFn)) return null;
|
||||
|
||||
return {wrapperFn: wrapper.fn, factoryFn};
|
||||
let factoryCalls: UmdModule['factoryCalls']|null = null;
|
||||
|
||||
return {
|
||||
wrapperFn: wrapper.fn,
|
||||
factoryFn,
|
||||
// Compute `factoryCalls` lazily, because in some cases they might not be needed for the task at
|
||||
// hand.
|
||||
// For example, if we just want to determine if an entry-point is in CommonJS or UMD format,
|
||||
// trying to parse the wrapper function could potentially throw a (premature) error. By making
|
||||
// the computation of `factoryCalls` lazy, such an error would be thrown later (during an
|
||||
// operation where the format of the wrapper function does actually matter) or potentially not
|
||||
// at all (if we end up not having to process that entry-point).
|
||||
get factoryCalls() {
|
||||
if (factoryCalls === null) {
|
||||
factoryCalls = parseUmdWrapperFunction(this.wrapperFn);
|
||||
}
|
||||
return factoryCalls;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getUmdWrapper(statement: ts.Statement):
|
||||
|
|
@ -547,51 +565,216 @@ function getUmdWrapper(statement: ts.Statement):
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the wrapper function of a UMD module and extract info about the factory function calls for
|
||||
* the various formats (CommonJS, AMD, global).
|
||||
*
|
||||
* The supported format for the UMD wrapper function body is a single statement which is a
|
||||
* `ts.ConditionalExpression` (i.e. using a ternary operator). For example:
|
||||
*
|
||||
* ```js
|
||||
* // Using a conditional expression:
|
||||
* (function (global, factory) {
|
||||
* typeof exports === 'object' && typeof module !== 'undefined' ?
|
||||
* // CommonJS2 factory call.
|
||||
* factory(exports, require('foo'), require('bar')) :
|
||||
* typeof define === 'function' && define.amd ?
|
||||
* // AMD factory call.
|
||||
* define(['exports', 'foo', 'bar'], factory) :
|
||||
* // Global factory call.
|
||||
* (factory((global['my-lib'] = {}), global.foo, global.bar));
|
||||
* }(this, (function (exports, foo, bar) {
|
||||
* // ...
|
||||
* }));
|
||||
* ```
|
||||
*/
|
||||
function parseUmdWrapperFunction(wrapperFn: ts.FunctionExpression): UmdModule['factoryCalls'] {
|
||||
const stmt = wrapperFn.body.statements[0];
|
||||
let conditionalFactoryCalls: UmdConditionalFactoryCall[];
|
||||
|
||||
if (ts.isExpressionStatement(stmt) && ts.isConditionalExpression(stmt.expression)) {
|
||||
conditionalFactoryCalls = extractFactoryCallsFromConditionalExpression(stmt.expression);
|
||||
} else {
|
||||
throw new Error(
|
||||
'UMD wrapper body is not in a supported format (expected a conditional expression):\n' +
|
||||
wrapperFn.body.getText());
|
||||
}
|
||||
|
||||
const factoryCalls = {
|
||||
amdDefine: getAmdDefineCall(conditionalFactoryCalls),
|
||||
commonJs: getCommonJsFactoryCall(conditionalFactoryCalls),
|
||||
global: getGlobalFactoryCall(conditionalFactoryCalls),
|
||||
};
|
||||
|
||||
if (factoryCalls.commonJs === null) {
|
||||
throw new Error(
|
||||
'Unable to find a CommonJS factory call inside the UMD wrapper function:\n' +
|
||||
stmt.getText());
|
||||
}
|
||||
|
||||
return factoryCalls as (typeof factoryCalls&{commonJs: ts.CallExpression});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract `UmdConditionalFactoryCall`s from a `ts.ConditionalExpression` of the form:
|
||||
*
|
||||
* ```js
|
||||
* typeof exports === 'object' && typeof module !== 'undefined' ?
|
||||
* // CommonJS2 factory call.
|
||||
* factory(exports, require('foo'), require('bar')) :
|
||||
* typeof define === 'function' && define.amd ?
|
||||
* // AMD factory call.
|
||||
* define(['exports', 'foo', 'bar'], factory) :
|
||||
* // Global factory call.
|
||||
* (factory((global['my-lib'] = {}), global.foo, global.bar));
|
||||
* ```
|
||||
*/
|
||||
function extractFactoryCallsFromConditionalExpression(node: ts.ConditionalExpression):
|
||||
UmdConditionalFactoryCall[] {
|
||||
const factoryCalls: UmdConditionalFactoryCall[] = [];
|
||||
let currentNode: ts.Expression = node;
|
||||
|
||||
while (ts.isConditionalExpression(currentNode)) {
|
||||
if (!ts.isBinaryExpression(currentNode.condition)) {
|
||||
throw new Error(
|
||||
'Condition inside UMD wrapper is not a binary expression:\n' +
|
||||
currentNode.condition.getText());
|
||||
}
|
||||
|
||||
factoryCalls.push({
|
||||
condition: currentNode.condition,
|
||||
factoryCall: getFunctionCallFromExpression(currentNode.whenTrue),
|
||||
});
|
||||
|
||||
currentNode = currentNode.whenFalse;
|
||||
}
|
||||
|
||||
factoryCalls.push({
|
||||
condition: null,
|
||||
factoryCall: getFunctionCallFromExpression(currentNode),
|
||||
});
|
||||
|
||||
return factoryCalls;
|
||||
}
|
||||
|
||||
function getFunctionCallFromExpression(node: ts.Expression): ts.CallExpression {
|
||||
// Be resilient to `node` being inside parenthesis.
|
||||
if (ts.isParenthesizedExpression(node)) {
|
||||
// NOTE:
|
||||
// Since we are going further down the AST, there is no risk of infinite recursion.
|
||||
return getFunctionCallFromExpression(node.expression);
|
||||
}
|
||||
|
||||
// Be resilient to `node` being part of a comma expression.
|
||||
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.CommaToken) {
|
||||
// NOTE:
|
||||
// Since we are going further down the AST, there is no risk of infinite recursion.
|
||||
return getFunctionCallFromExpression(node.right);
|
||||
}
|
||||
|
||||
if (!ts.isCallExpression(node)) {
|
||||
throw new Error('Expression inside UMD wrapper is not a call expression:\n' + node.getText());
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `define` call for setting up the AMD dependencies in the UMD wrapper.
|
||||
*/
|
||||
function getAmdDefineCall(calls: UmdConditionalFactoryCall[]): ts.CallExpression|null {
|
||||
// The `define` call for AMD dependencies is the one that is guarded with a `&&` expression whose
|
||||
// one side is a `typeof define` condition.
|
||||
const amdConditionalCall = calls.find(
|
||||
call => call.condition?.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken &&
|
||||
oneOfBinaryConditions(call.condition, exp => isTypeOf(exp, 'define')) &&
|
||||
ts.isIdentifier(call.factoryCall.expression) &&
|
||||
call.factoryCall.expression.text === 'define');
|
||||
|
||||
return amdConditionalCall?.factoryCall ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the factory call for setting up the CommonJS dependencies in the UMD wrapper.
|
||||
*/
|
||||
function getCommonJsFactoryCall(calls: UmdConditionalFactoryCall[]): ts.CallExpression|null {
|
||||
// The factory call for CommonJS dependencies is the one that is guarded with a `&&` expression
|
||||
// whose one side is a `typeof exports` or `typeof module` condition.
|
||||
const cjsConditionalCall = calls.find(
|
||||
call => call.condition?.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken &&
|
||||
oneOfBinaryConditions(call.condition, exp => isTypeOf(exp, 'exports', 'module')) &&
|
||||
ts.isIdentifier(call.factoryCall.expression) &&
|
||||
call.factoryCall.expression.text === 'factory');
|
||||
|
||||
return cjsConditionalCall?.factoryCall ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the factory call for setting up the global dependencies in the UMD wrapper.
|
||||
*/
|
||||
function getGlobalFactoryCall(calls: UmdConditionalFactoryCall[]): ts.CallExpression|null {
|
||||
// The factory call for global dependencies is the one that is the final else-case (i.e. the one
|
||||
// that has `condition: null`).
|
||||
const globalConditionalCall = calls.find(call => call.condition === null);
|
||||
|
||||
return globalConditionalCall?.factoryCall ?? null;
|
||||
}
|
||||
|
||||
function oneOfBinaryConditions(
|
||||
node: ts.BinaryExpression, test: (expression: ts.Expression) => boolean) {
|
||||
return test(node.left) || test(node.right);
|
||||
}
|
||||
|
||||
function isTypeOf(node: ts.Expression, ...types: string[]): boolean {
|
||||
return ts.isBinaryExpression(node) && ts.isTypeOfExpression(node.left) &&
|
||||
ts.isIdentifier(node.left.expression) && types.includes(node.left.expression.text);
|
||||
}
|
||||
|
||||
export function getImportsOfUmdModule(umdModule: UmdModule):
|
||||
{parameter: ts.ParameterDeclaration, path: string}[] {
|
||||
const imports: {parameter: ts.ParameterDeclaration, path: string}[] = [];
|
||||
const cjsFactoryCall = umdModule.factoryCalls.commonJs;
|
||||
|
||||
for (let i = 1; i < umdModule.factoryFn.parameters.length; i++) {
|
||||
imports.push({
|
||||
parameter: umdModule.factoryFn.parameters[i],
|
||||
path: getRequiredModulePath(umdModule.wrapperFn, i)
|
||||
path: getRequiredModulePath(cjsFactoryCall, i),
|
||||
});
|
||||
}
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
interface UmdModule {
|
||||
wrapperFn: ts.FunctionExpression;
|
||||
factoryFn: ts.FunctionExpression;
|
||||
factoryCalls: {
|
||||
commonJs: ts.CallExpression; amdDefine: ts.CallExpression | null;
|
||||
global: ts.CallExpression | null;
|
||||
};
|
||||
}
|
||||
|
||||
function getRequiredModulePath(wrapperFn: ts.FunctionExpression, paramIndex: number): string {
|
||||
const statement = wrapperFn.body.statements[0];
|
||||
if (!ts.isExpressionStatement(statement)) {
|
||||
/**
|
||||
* Represents a factory call found inside the UMD wrapper function.
|
||||
*
|
||||
* Each factory call corresponds to a format (such as AMD, CommonJS, etc.) and is guarded by a
|
||||
* condition (except for the last factory call, which is reached when all other conditions fail).
|
||||
*/
|
||||
interface UmdConditionalFactoryCall {
|
||||
condition: ts.BinaryExpression|null;
|
||||
factoryCall: ts.CallExpression;
|
||||
}
|
||||
|
||||
function getRequiredModulePath(cjsFactoryCall: ts.CallExpression, paramIndex: number): string {
|
||||
const requireCall = cjsFactoryCall.arguments[paramIndex];
|
||||
|
||||
if (requireCall !== undefined && !isRequireCall(requireCall)) {
|
||||
throw new Error(
|
||||
'UMD wrapper body is not an expression statement:\n' + wrapperFn.body.getText());
|
||||
`Argument at index ${paramIndex} of UMD factory call is not a \`require\` call with a ` +
|
||||
'single string argument:\n' + cjsFactoryCall.getText());
|
||||
}
|
||||
const modulePaths: string[] = [];
|
||||
findModulePaths(statement.expression);
|
||||
|
||||
// Since we were only interested in the `require()` calls, we miss the `exports` argument, so we
|
||||
// need to subtract 1.
|
||||
// E.g. `function(exports, dep1, dep2)` maps to `function(exports, require('path/to/dep1'),
|
||||
// require('path/to/dep2'))`
|
||||
return modulePaths[paramIndex - 1];
|
||||
|
||||
// Search the statement for calls to `require('...')` and extract the string value of the first
|
||||
// argument
|
||||
function findModulePaths(node: ts.Node) {
|
||||
if (isRequireCall(node)) {
|
||||
const argument = node.arguments[0];
|
||||
if (ts.isStringLiteral(argument)) {
|
||||
modulePaths.push(argument.text);
|
||||
}
|
||||
} else {
|
||||
node.forEachChild(findModulePaths);
|
||||
}
|
||||
}
|
||||
return requireCall.arguments[0].text;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ import {UmdReflectionHost} from '../host/umd_host';
|
|||
import {Esm5RenderingFormatter} from './esm5_rendering_formatter';
|
||||
import {stripExtension} from './utils';
|
||||
|
||||
type CommonJsConditional = ts.ConditionalExpression&{whenTrue: ts.CallExpression};
|
||||
type AmdConditional = ts.ConditionalExpression&{whenTrue: ts.CallExpression};
|
||||
|
||||
/**
|
||||
* A RenderingFormatter that works with UMD files, instead of `import` and `export` statements
|
||||
* the module is an IIFE with a factory function call with dependencies, which are defined in a
|
||||
|
|
@ -61,12 +58,12 @@ export class UmdRenderingFormatter extends Esm5RenderingFormatter {
|
|||
return;
|
||||
}
|
||||
|
||||
const {wrapperFn, factoryFn} = umdModule;
|
||||
const {factoryFn, factoryCalls} = umdModule;
|
||||
|
||||
// We need to add new `require()` calls for each import in the CommonJS initializer
|
||||
renderCommonJsDependencies(output, wrapperFn, imports);
|
||||
renderAmdDependencies(output, wrapperFn, imports);
|
||||
renderGlobalDependencies(output, wrapperFn, imports);
|
||||
renderCommonJsDependencies(output, factoryCalls.commonJs, imports);
|
||||
renderAmdDependencies(output, factoryCalls.amdDefine, imports);
|
||||
renderGlobalDependencies(output, factoryCalls.global, imports);
|
||||
renderFactoryParameters(output, factoryFn, imports);
|
||||
}
|
||||
|
||||
|
|
@ -140,12 +137,11 @@ export class UmdRenderingFormatter extends Esm5RenderingFormatter {
|
|||
* Add dependencies to the CommonJS part of the UMD wrapper function.
|
||||
*/
|
||||
function renderCommonJsDependencies(
|
||||
output: MagicString, wrapperFunction: ts.FunctionExpression, imports: Import[]) {
|
||||
const conditional = find(wrapperFunction.body.statements[0], isCommonJSConditional);
|
||||
if (!conditional) {
|
||||
output: MagicString, factoryCall: ts.CallExpression|null, imports: Import[]) {
|
||||
if (factoryCall === null) {
|
||||
return;
|
||||
}
|
||||
const factoryCall = conditional.whenTrue;
|
||||
|
||||
const injectionPoint = factoryCall.arguments.length > 0 ?
|
||||
// Add extra dependencies before the first argument
|
||||
factoryCall.arguments[0].getFullStart() :
|
||||
|
|
@ -159,12 +155,11 @@ function renderCommonJsDependencies(
|
|||
* Add dependencies to the AMD part of the UMD wrapper function.
|
||||
*/
|
||||
function renderAmdDependencies(
|
||||
output: MagicString, wrapperFunction: ts.FunctionExpression, imports: Import[]) {
|
||||
const conditional = find(wrapperFunction.body.statements[0], isAmdConditional);
|
||||
if (!conditional) {
|
||||
output: MagicString, amdDefineCall: ts.CallExpression|null, imports: Import[]) {
|
||||
if (amdDefineCall === null) {
|
||||
return;
|
||||
}
|
||||
const amdDefineCall = conditional.whenTrue;
|
||||
|
||||
const importString = imports.map(i => `'${i.specifier}'`).join(',');
|
||||
// The dependency array (if it exists) is the second to last argument
|
||||
// `define(id?, dependencies?, factory);`
|
||||
|
|
@ -191,19 +186,18 @@ function renderAmdDependencies(
|
|||
* Add dependencies to the global part of the UMD wrapper function.
|
||||
*/
|
||||
function renderGlobalDependencies(
|
||||
output: MagicString, wrapperFunction: ts.FunctionExpression, imports: Import[]) {
|
||||
const globalFactoryCall = find(wrapperFunction.body.statements[0], isGlobalFactoryCall);
|
||||
if (!globalFactoryCall) {
|
||||
output: MagicString, factoryCall: ts.CallExpression|null, imports: Import[]) {
|
||||
if (factoryCall === null) {
|
||||
return;
|
||||
}
|
||||
const injectionPoint = globalFactoryCall.arguments.length > 0 ?
|
||||
|
||||
const injectionPoint = factoryCall.arguments.length > 0 ?
|
||||
// Add extra dependencies before the first argument
|
||||
globalFactoryCall.arguments[0].getFullStart() :
|
||||
factoryCall.arguments[0].getFullStart() :
|
||||
// Backup one char to account for the closing parenthesis on the call
|
||||
globalFactoryCall.getEnd() - 1;
|
||||
factoryCall.getEnd() - 1;
|
||||
const importString = imports.map(i => `global.${getGlobalIdentifier(i)}`).join(',');
|
||||
output.appendLeft(
|
||||
injectionPoint, importString + (globalFactoryCall.arguments.length > 0 ? ',' : ''));
|
||||
output.appendLeft(injectionPoint, importString + (factoryCall.arguments.length > 0 ? ',' : ''));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -226,66 +220,6 @@ function renderFactoryParameters(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this node the CommonJS conditional expression in the UMD wrapper?
|
||||
*/
|
||||
function isCommonJSConditional(value: ts.Node): value is CommonJsConditional {
|
||||
if (!ts.isConditionalExpression(value)) {
|
||||
return false;
|
||||
}
|
||||
if (!ts.isBinaryExpression(value.condition) ||
|
||||
value.condition.operatorToken.kind !== ts.SyntaxKind.AmpersandAmpersandToken) {
|
||||
return false;
|
||||
}
|
||||
if (!oneOfBinaryConditions(value.condition, (exp) => isTypeOf(exp, 'exports', 'module'))) {
|
||||
return false;
|
||||
}
|
||||
if (!ts.isCallExpression(value.whenTrue) || !ts.isIdentifier(value.whenTrue.expression)) {
|
||||
return false;
|
||||
}
|
||||
return value.whenTrue.expression.text === 'factory';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this node the AMD conditional expression in the UMD wrapper?
|
||||
*/
|
||||
function isAmdConditional(value: ts.Node): value is AmdConditional {
|
||||
if (!ts.isConditionalExpression(value)) {
|
||||
return false;
|
||||
}
|
||||
if (!ts.isBinaryExpression(value.condition) ||
|
||||
value.condition.operatorToken.kind !== ts.SyntaxKind.AmpersandAmpersandToken) {
|
||||
return false;
|
||||
}
|
||||
if (!oneOfBinaryConditions(value.condition, (exp) => isTypeOf(exp, 'define'))) {
|
||||
return false;
|
||||
}
|
||||
if (!ts.isCallExpression(value.whenTrue) || !ts.isIdentifier(value.whenTrue.expression)) {
|
||||
return false;
|
||||
}
|
||||
return value.whenTrue.expression.text === 'define';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this node the call to setup the global dependencies in the UMD wrapper?
|
||||
*/
|
||||
function isGlobalFactoryCall(value: ts.Node): value is ts.CallExpression {
|
||||
if (ts.isCallExpression(value) && !!value.parent) {
|
||||
// Be resilient to the value being part of a comma list
|
||||
value = isCommaExpression(value.parent) ? value.parent : value;
|
||||
// Be resilient to the value being inside parentheses
|
||||
value = ts.isParenthesizedExpression(value.parent) ? value.parent : value;
|
||||
return !!value.parent && ts.isConditionalExpression(value.parent) &&
|
||||
value.parent.whenFalse === value;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isCommaExpression(value: ts.Node): value is ts.BinaryExpression {
|
||||
return ts.isBinaryExpression(value) && value.operatorToken.kind === ts.SyntaxKind.CommaToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a global identifier for the given import (`i`).
|
||||
*
|
||||
|
|
@ -315,17 +249,3 @@ function getGlobalIdentifier(i: Import): string {
|
|||
.replace(/[-_]+(.?)/g, (_, c) => c.toUpperCase())
|
||||
.replace(/^./, c => c.toLowerCase());
|
||||
}
|
||||
|
||||
function find<T>(node: ts.Node, test: (node: ts.Node) => node is ts.Node & T): T|undefined {
|
||||
return test(node) ? node : node.forEachChild(child => find<T>(child, test));
|
||||
}
|
||||
|
||||
function oneOfBinaryConditions(
|
||||
node: ts.BinaryExpression, test: (expression: ts.Expression) => boolean) {
|
||||
return test(node.left) || test(node.right);
|
||||
}
|
||||
|
||||
function isTypeOf(node: ts.Expression, ...types: string[]): boolean {
|
||||
return ts.isBinaryExpression(node) && ts.isTypeOfExpression(node.left) &&
|
||||
ts.isIdentifier(node.left.expression) && types.indexOf(node.left.expression.text) !== -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -735,7 +735,8 @@ runInEachFileSystem(() => {
|
|||
name: _('/wildcard_reexports_with_require.js'),
|
||||
contents: `(function (global, factory) {\n` +
|
||||
` typeof exports === 'object' && typeof module !== 'undefined' ? factory(require, exports) :\n` +
|
||||
` typeof define === 'function' && define.amd ? define('wildcard_reexports_with_require', ['require', 'exports'], factory);\n` +
|
||||
` typeof define === 'function' && define.amd ? define('wildcard_reexports_with_require', ['require', 'exports'], factory) :\n` +
|
||||
` (factory(global.require, global.wildcard_reexports_with_require));\n` +
|
||||
`}(this, (function (require, exports) { 'use strict';\n` +
|
||||
` function __export(m) {\n` +
|
||||
` for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];\n` +
|
||||
|
|
@ -749,7 +750,8 @@ runInEachFileSystem(() => {
|
|||
name: _('/define_property_reexports.js'),
|
||||
contents: `(function (global, factory) {\n` +
|
||||
` typeof exports === 'object' && typeof module !== 'undefined' ? factory(require, exports) :\n` +
|
||||
` typeof define === 'function' && define.amd ? define('define_property_reexports', ['require', 'exports'], factory);\n` +
|
||||
` typeof define === 'function' && define.amd ? define('define_property_reexports', ['require', 'exports'], factory) :\n` +
|
||||
` (factory(global.require, global.define_property_reexports));\n` +
|
||||
`}(this, (function (require, exports) { 'use strict';\n` +
|
||||
`var moduleA = require("./a_module");\n` +
|
||||
`Object.defineProperty(exports, "newA", { enumerable: true, get: function () { return moduleA.a; } });\n` +
|
||||
|
|
@ -1769,7 +1771,7 @@ runInEachFileSystem(() => {
|
|||
case 'imported':
|
||||
fileHeaderWithUmd = `
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tslib'))) :
|
||||
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) { 'use strict';
|
||||
|
|
@ -1778,7 +1780,7 @@ runInEachFileSystem(() => {
|
|||
case 'inlined':
|
||||
fileHeaderWithUmd = `
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports)) :
|
||||
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';
|
||||
|
|
@ -1791,7 +1793,7 @@ runInEachFileSystem(() => {
|
|||
case 'inlined_with_suffix':
|
||||
fileHeaderWithUmd = `
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports)) :
|
||||
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';
|
||||
|
|
|
|||
|
|
@ -851,6 +851,27 @@ runInEachFileSystem(() => {
|
|||
entryPoint.packageJson.main = './bundles/valid_entry_point';
|
||||
expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd');
|
||||
});
|
||||
|
||||
it('should detect UMD even if the wrapper function is not of a supported format', () => {
|
||||
// In order to correctly process UMD, we require that at least one CommonJS variant is
|
||||
// present. However, for the purposes of detecting the module format, it should not matter
|
||||
// what the body of the wrapper function looks like.
|
||||
loadTestFiles([{
|
||||
name: _(
|
||||
'/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'),
|
||||
contents: `
|
||||
(function (global, factory) {
|
||||
// This is not a supported format for the wrapper function body, but it should still
|
||||
// be detected as UMD (since it is closer to UMD than any other of the supported
|
||||
// formats).
|
||||
typeof define === 'function' && define.amd ? define([], factory) : factory();
|
||||
})(this, function () { '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', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue