fix(ngcc): handle CommonJS re-exports by reference (#34254)

In TS we can re-export imports using statements of the form:

```
export * from 'some-import';
```

This can be downleveled in CommonJS to either:

```
__export(require('some-import'));
```

or

```
var someImport = require('some-import');
__export(someImport);
```

Previously we only supported the first downleveled version.
This commit adds support for the second version.

PR Close #34254
This commit is contained in:
Pete Bacon Darwin 2019-12-18 14:03:04 +00:00 committed by Kara Erickson
parent 0b837e2f0d
commit 47666f548c
2 changed files with 37 additions and 23 deletions

View File

@ -137,24 +137,35 @@ export class CommonJsReflectionHost extends Esm5ReflectionHost {
private extractCommonJsReexports(statement: ReexportStatement, containingFile: ts.SourceFile):
CommonJsExportDeclaration[] {
const reexports: CommonJsExportDeclaration[] = [];
const requireCall = statement.expression.arguments[0];
const reexportArg = statement.expression.arguments[0];
const requireCall = isRequireCall(reexportArg) ?
reexportArg :
ts.isIdentifier(reexportArg) ? this.findRequireCallReference(reexportArg) : null;
if (requireCall === null) {
return [];
}
const importPath = requireCall.arguments[0].text;
const importedFile = this.resolveModuleName(importPath, containingFile);
if (importedFile !== undefined) {
const viaModule = stripExtension(importedFile.fileName);
const importedExports = this.getExportsOfModule(importedFile);
if (importedExports !== null) {
importedExports.forEach((decl, name) => {
if (decl.node !== null) {
reexports.push({name, declaration: {node: decl.node, viaModule}});
} else {
reexports.push(
{name, declaration: {node: null, expression: decl.expression, viaModule}});
}
});
}
if (importedFile === undefined) {
return [];
}
const viaModule = stripExtension(importedFile.fileName);
const importedExports = this.getExportsOfModule(importedFile);
if (importedExports === null) {
return [];
}
const reexports: CommonJsExportDeclaration[] = [];
importedExports.forEach((decl, name) => {
if (decl.node !== null) {
reexports.push({name, declaration: {node: decl.node, viaModule}});
} else {
reexports.push({name, declaration: {node: null, expression: decl.expression, viaModule}});
}
});
return reexports;
}
@ -162,11 +173,14 @@ export class CommonJsReflectionHost extends Esm5ReflectionHost {
// Is `id` a namespaced property access, e.g. `Directive` in `core.Directive`?
// If so capture the symbol of the namespace, e.g. `core`.
const nsIdentifier = findNamespaceOfIdentifier(id);
const nsSymbol = nsIdentifier && this.checker.getSymbolAtLocation(nsIdentifier) || null;
const nsDeclaration = nsSymbol && nsSymbol.valueDeclaration;
return nsIdentifier && this.findRequireCallReference(nsIdentifier);
}
private findRequireCallReference(id: ts.Identifier): RequireCall|null {
const symbol = id && this.checker.getSymbolAtLocation(id) || null;
const declaration = symbol && symbol.valueDeclaration;
const initializer =
nsDeclaration && ts.isVariableDeclaration(nsDeclaration) && nsDeclaration.initializer ||
null;
declaration && ts.isVariableDeclaration(declaration) && declaration.initializer || null;
return initializer && isRequireCall(initializer) ? initializer : null;
}
@ -237,13 +251,12 @@ function findNamespaceOfIdentifier(id: ts.Identifier): ts.Identifier|null {
null;
}
type ReexportStatement = ts.ExpressionStatement & {expression: {arguments: [RequireCall]}};
type ReexportStatement = ts.ExpressionStatement & {expression: ts.CallExpression};
function isReexportStatement(statement: ts.Statement): statement is ReexportStatement {
return ts.isExpressionStatement(statement) && ts.isCallExpression(statement.expression) &&
ts.isIdentifier(statement.expression.expression) &&
statement.expression.expression.text === '__export' &&
statement.expression.arguments.length === 1 &&
isRequireCall(statement.expression.arguments[0]);
statement.expression.arguments.length === 1;
}
function stripExtension(fileName: string): string {

View File

@ -557,7 +557,8 @@ exports.xtra2 = xtra2;
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(require("./b_module"));
var b_module = require("./b_module");
__export(b_module);
__export(require("./xtra_module"));
`
},