fix(ngcc): recognize re-exports with `require()` calls in UMD (#34512)
Previously, `UmdReflectionHost` would only recognize re-exports of the form `__export(someIdentifier)` and not `__export(require('...'))`. However, it is possible in some UMD variations to have the latter format as well. See discussion in https://github.com/angular/angular/pull/34254/files#r359515373 This commit adds support for re-export of the form `__export(require('...'))` in UMD. PR Close #34512
This commit is contained in:
parent
6654f82522
commit
d5fd742763
|
@ -145,11 +145,25 @@ export class UmdReflectionHost extends Esm5ReflectionHost {
|
||||||
|
|
||||||
private extractUmdReexports(statement: ReexportStatement, containingFile: ts.SourceFile):
|
private extractUmdReexports(statement: ReexportStatement, containingFile: ts.SourceFile):
|
||||||
UmdExportDeclaration[] {
|
UmdExportDeclaration[] {
|
||||||
const importParameter = this.findUmdImportParameter(statement.expression.arguments[0]);
|
const reexportArg = statement.expression.arguments[0];
|
||||||
const importPath = importParameter && this.getUmdImportPath(importParameter);
|
|
||||||
|
const requireCall = isRequireCall(reexportArg) ?
|
||||||
|
reexportArg :
|
||||||
|
ts.isIdentifier(reexportArg) ? this.findRequireCallReference(reexportArg) : null;
|
||||||
|
|
||||||
|
let importPath: string|null = null;
|
||||||
|
|
||||||
|
if (requireCall !== null) {
|
||||||
|
importPath = requireCall.arguments[0].text;
|
||||||
|
} else if (ts.isIdentifier(reexportArg)) {
|
||||||
|
const importParameter = this.findUmdImportParameter(reexportArg);
|
||||||
|
importPath = importParameter && this.getUmdImportPath(importParameter);
|
||||||
|
}
|
||||||
|
|
||||||
if (importPath === null) {
|
if (importPath === null) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const importedFile = this.resolveModuleName(importPath, containingFile);
|
const importedFile = this.resolveModuleName(importPath, containingFile);
|
||||||
if (importedFile === undefined) {
|
if (importedFile === undefined) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -182,6 +196,14 @@ export class UmdReflectionHost extends Esm5ReflectionHost {
|
||||||
return declaration && ts.isParameter(declaration) ? declaration : null;
|
return declaration && ts.isParameter(declaration) ? declaration : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private findRequireCallReference(id: ts.Identifier): RequireCall|null {
|
||||||
|
const symbol = id && this.checker.getSymbolAtLocation(id) || null;
|
||||||
|
const declaration = symbol && symbol.valueDeclaration;
|
||||||
|
const initializer =
|
||||||
|
declaration && ts.isVariableDeclaration(declaration) && declaration.initializer || null;
|
||||||
|
return initializer && isRequireCall(initializer) ? initializer : null;
|
||||||
|
}
|
||||||
|
|
||||||
private getUmdImportedDeclaration(id: ts.Identifier): Declaration|null {
|
private getUmdImportedDeclaration(id: ts.Identifier): Declaration|null {
|
||||||
const importInfo = this.getImportOfIdentifier(id);
|
const importInfo = this.getImportOfIdentifier(id);
|
||||||
if (importInfo === null) {
|
if (importInfo === null) {
|
||||||
|
@ -277,13 +299,12 @@ interface UmdExportDeclaration {
|
||||||
declaration: Declaration;
|
declaration: Declaration;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReexportStatement = ts.ExpressionStatement & {expression: {arguments: [ts.Identifier]}};
|
type ReexportStatement = ts.ExpressionStatement & {expression: ts.CallExpression};
|
||||||
function isReexportStatement(statement: ts.Statement): statement is ReexportStatement {
|
function isReexportStatement(statement: ts.Statement): statement is ReexportStatement {
|
||||||
return ts.isExpressionStatement(statement) && ts.isCallExpression(statement.expression) &&
|
return ts.isExpressionStatement(statement) && ts.isCallExpression(statement.expression) &&
|
||||||
ts.isIdentifier(statement.expression.expression) &&
|
ts.isIdentifier(statement.expression.expression) &&
|
||||||
statement.expression.expression.text === '__export' &&
|
statement.expression.expression.text === '__export' &&
|
||||||
statement.expression.arguments.length === 1 &&
|
statement.expression.arguments.length === 1;
|
||||||
ts.isIdentifier(statement.expression.arguments[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRequiredModulePath(wrapperFn: ts.FunctionExpression, paramIndex: number): string {
|
function getRequiredModulePath(wrapperFn: ts.FunctionExpression, paramIndex: number): string {
|
||||||
|
@ -315,7 +336,8 @@ function getRequiredModulePath(wrapperFn: ts.FunctionExpression, paramIndex: num
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRequireCall(node: ts.Node): node is ts.CallExpression {
|
type RequireCall = ts.CallExpression & {arguments: [ts.StringLiteral]};
|
||||||
|
function isRequireCall(node: ts.Node): node is RequireCall {
|
||||||
return ts.isCallExpression(node) && ts.isIdentifier(node.expression) &&
|
return ts.isCallExpression(node) && ts.isIdentifier(node.expression) &&
|
||||||
node.expression.text === 'require' && node.arguments.length === 1;
|
node.expression.text === 'require' && node.arguments.length === 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,10 +564,10 @@ runInEachFileSystem(() => {
|
||||||
name: _('/index.js'),
|
name: _('/index.js'),
|
||||||
contents: `
|
contents: `
|
||||||
(function (global, factory) {
|
(function (global, factory) {
|
||||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./a_module'), require('./b_module'), require('./wildcard_reexports')) :
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./a_module'), require('./b_module'), require('./wildcard_reexports'), require('./wildcard_reexports_with_require')) :
|
||||||
typeof define === 'function' && define.amd ? define('index', ['exports', './a_module', './b_module', './wildcard_reexports], factory) :
|
typeof define === 'function' && define.amd ? define('index', ['exports', './a_module', './b_module', './wildcard_reexports', './wildcard_reexports_with_require'], factory) :
|
||||||
(factory(global.index, global.a_module, global.b_module, global.wildcard_reexports));
|
(factory(global.index, global.a_module, global.b_module, global.wildcard_reexports, global.wildcard_reexports_with_require));
|
||||||
}(this, (function (exports, a_module, b_module, wildcard_reexports) { 'use strict';
|
}(this, (function (exports, a_module, b_module, wildcard_reexports, wildcard_reexports_with_require) { 'use strict';
|
||||||
})));
|
})));
|
||||||
`
|
`
|
||||||
},
|
},
|
||||||
|
@ -635,6 +635,21 @@ runInEachFileSystem(() => {
|
||||||
}
|
}
|
||||||
__export(b_module);
|
__export(b_module);
|
||||||
__export(xtra_module);
|
__export(xtra_module);
|
||||||
|
})));`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: _('/wildcard_reexports_with_require.js'),
|
||||||
|
contents: `
|
||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require, exports) :
|
||||||
|
typeof define === 'function' && define.amd ? define('wildcard_reexports_with_require', ['require', 'exports'], factory);
|
||||||
|
}(this, (function (require, exports) { 'use strict';
|
||||||
|
function __export(m) {
|
||||||
|
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
||||||
|
}
|
||||||
|
var b_module = require('./b_module');
|
||||||
|
__export(b_module);
|
||||||
|
__export(require('./xtra_module'));
|
||||||
})));`,
|
})));`,
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -1960,6 +1975,34 @@ __export(xtra_module);
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle wildcard re-exports of other modules using `require()` calls', () => {
|
||||||
|
loadFakeCore(getFileSystem());
|
||||||
|
loadTestFiles(EXPORTS_FILES);
|
||||||
|
const bundle = makeTestBundleProgram(_('/index.js'));
|
||||||
|
const host = new UmdReflectionHost(new MockLogger(), false, bundle);
|
||||||
|
const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports_with_require.js'));
|
||||||
|
const exportDeclarations = host.getExportsOfModule(file);
|
||||||
|
expect(exportDeclarations).not.toBe(null);
|
||||||
|
expect(Array.from(exportDeclarations !.entries())
|
||||||
|
.map(entry => [entry[0], entry[1].node !.getText(), entry[1].viaModule]))
|
||||||
|
.toEqual([
|
||||||
|
['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')],
|
||||||
|
['a', `a = 'a'`, _('/b_module')],
|
||||||
|
['b', `b = a_module.a`, _('/b_module')],
|
||||||
|
['c', `a = 'a'`, _('/b_module')],
|
||||||
|
['d', `b = a_module.a`, _('/b_module')],
|
||||||
|
['e', `e = 'e'`, _('/b_module')],
|
||||||
|
['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')],
|
||||||
|
[
|
||||||
|
'SomeClass',
|
||||||
|
`SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())`,
|
||||||
|
_('/b_module')
|
||||||
|
],
|
||||||
|
['xtra1', `xtra1 = 'xtra1'`, _('/xtra_module')],
|
||||||
|
['xtra2', `xtra2 = 'xtra2'`, _('/xtra_module')],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return the class symbol for an ES5 class (outer variable declaration)', () => {
|
it('should return the class symbol for an ES5 class (outer variable declaration)', () => {
|
||||||
loadTestFiles([SIMPLE_CLASS_FILE]);
|
loadTestFiles([SIMPLE_CLASS_FILE]);
|
||||||
const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name);
|
const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name);
|
||||||
|
|
Loading…
Reference in New Issue