fix(ngcc): correctly handle inline exports in UMD (#34512)

This fix was part of a broader `ngtsc`/`ngcc` fix in 02bab8cf9 (see
there for details). In 02bab8cf9, the fix was only applied to
`CommonJsReflectionHost`, but it is equally applicable to
`UmdReflectionHost`. Later in #34254, the fix was partially ported to
`UmdReflectionHost` by fixing the `extractUmdReexports()` method.

This commit fully fixes `ngcc`'s handling of inline exports for code in
UMD format.

PR Close #34512
This commit is contained in:
George Kalpakas 2019-12-20 14:19:08 +02:00 committed by Alex Rickabaugh
parent 02e4921ac9
commit 6654f82522
2 changed files with 47 additions and 11 deletions

View File

@ -114,9 +114,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost {
for (const statement of this.getModuleStatements(sourceFile)) { for (const statement of this.getModuleStatements(sourceFile)) {
if (isUmdExportStatement(statement)) { if (isUmdExportStatement(statement)) {
const declaration = this.extractUmdExportDeclaration(statement); const declaration = this.extractUmdExportDeclaration(statement);
if (declaration !== null) {
moduleMap.set(declaration.name, declaration.declaration); moduleMap.set(declaration.name, declaration.declaration);
}
} else if (isReexportStatement(statement)) { } else if (isReexportStatement(statement)) {
const reexports = this.extractUmdReexports(statement, sourceFile); const reexports = this.extractUmdReexports(statement, sourceFile);
for (const reexport of reexports) { for (const reexport of reexports) {
@ -127,16 +125,22 @@ export class UmdReflectionHost extends Esm5ReflectionHost {
return moduleMap; return moduleMap;
} }
private extractUmdExportDeclaration(statement: UmdExportStatement): UmdExportDeclaration|null { private extractUmdExportDeclaration(statement: UmdExportStatement): UmdExportDeclaration {
const exportExpression = statement.expression.right; const exportExpression = statement.expression.right;
const name = statement.expression.left.name.text;
const declaration = this.getDeclarationOfExpression(exportExpression); const declaration = this.getDeclarationOfExpression(exportExpression);
if (declaration === null) { const name = statement.expression.left.name.text;
return null; if (declaration !== null) {
}
return {name, declaration}; return {name, declaration};
} else {
return {
name,
declaration: {
node: null,
expression: exportExpression,
viaModule: null,
},
};
}
} }
private extractUmdReexports(statement: ReexportStatement, containingFile: ts.SourceFile): private extractUmdReexports(statement: ReexportStatement, containingFile: ts.SourceFile):

View File

@ -10,7 +10,7 @@ import * as ts from 'typescript';
import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system';
import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
import {ClassMemberKind, CtorParameter, Import, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; import {ClassMemberKind, CtorParameter, Import, InlineDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
import {getDeclaration} from '../../../src/ngtsc/testing'; import {getDeclaration} from '../../../src/ngtsc/testing';
import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers';
import {getIifeBody} from '../../src/host/esm5_host'; import {getIifeBody} from '../../src/host/esm5_host';
@ -30,6 +30,7 @@ runInEachFileSystem(() => {
let SIMPLE_ES2015_CLASS_FILE: TestFile; let SIMPLE_ES2015_CLASS_FILE: TestFile;
let SIMPLE_CLASS_FILE: TestFile; let SIMPLE_CLASS_FILE: TestFile;
let FOO_FUNCTION_FILE: TestFile; let FOO_FUNCTION_FILE: TestFile;
let INLINE_EXPORT_FILE: TestFile;
let INVALID_DECORATORS_FILE: TestFile; let INVALID_DECORATORS_FILE: TestFile;
let INVALID_DECORATOR_ARGS_FILE: TestFile; let INVALID_DECORATOR_ARGS_FILE: TestFile;
let INVALID_PROP_DECORATORS_FILE: TestFile; let INVALID_PROP_DECORATORS_FILE: TestFile;
@ -212,6 +213,23 @@ runInEachFileSystem(() => {
})));`, })));`,
}; };
INLINE_EXPORT_FILE = {
name: _('/inline_export.js'),
contents: `
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) :
typeof define === 'function' && define.amd ? define('foo_function', ['exports', '@angular/core'], factory) :
(factory(global.inline_export,global.ng.core));
}(this, (function (exports,core) { 'use strict';
function foo() {}
foo.decorators = [
{ type: core.Directive, args: [{ selector: '[ignored]' },] }
];
exports.directives = [foo];
})));
`,
};
INVALID_DECORATORS_FILE = { INVALID_DECORATORS_FILE = {
name: _('/invalid_decorators.js'), name: _('/invalid_decorators.js'),
contents: ` contents: `
@ -1874,6 +1892,20 @@ __export(xtra_module);
]); ]);
}); });
it('should handle inline exports', () => {
loadFakeCore(getFileSystem());
loadTestFiles([INLINE_EXPORT_FILE]);
const bundle = makeTestBundleProgram(INLINE_EXPORT_FILE.name);
const host = new UmdReflectionHost(new MockLogger(), false, bundle);
const file = getSourceFileOrError(bundle.program, INLINE_EXPORT_FILE.name);
const exportDeclarations = host.getExportsOfModule(file);
expect(exportDeclarations).not.toBe(null);
const decl = exportDeclarations !.get('directives') as InlineDeclaration;
expect(decl).not.toBeUndefined();
expect(decl.node).toBeNull();
expect(decl.expression).toBeDefined();
});
// Currently we do not support UMD versions of `export * from 'x';` // Currently we do not support UMD versions of `export * from 'x';`
// because it gets compiled to something like: // because it gets compiled to something like:
// //