From 6654f82522bffba139e15fc2d63fed43cd647cb3 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Fri, 20 Dec 2019 14:19:08 +0200 Subject: [PATCH] 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 --- .../compiler-cli/ngcc/src/host/umd_host.ts | 24 +++++++------ .../ngcc/test/host/umd_host_spec.ts | 34 ++++++++++++++++++- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/host/umd_host.ts b/packages/compiler-cli/ngcc/src/host/umd_host.ts index b39f28ade3..e074f28b03 100644 --- a/packages/compiler-cli/ngcc/src/host/umd_host.ts +++ b/packages/compiler-cli/ngcc/src/host/umd_host.ts @@ -114,9 +114,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost { for (const statement of this.getModuleStatements(sourceFile)) { if (isUmdExportStatement(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)) { const reexports = this.extractUmdReexports(statement, sourceFile); for (const reexport of reexports) { @@ -127,16 +125,22 @@ export class UmdReflectionHost extends Esm5ReflectionHost { return moduleMap; } - private extractUmdExportDeclaration(statement: UmdExportStatement): UmdExportDeclaration|null { + private extractUmdExportDeclaration(statement: UmdExportStatement): UmdExportDeclaration { const exportExpression = statement.expression.right; - const name = statement.expression.left.name.text; - const declaration = this.getDeclarationOfExpression(exportExpression); - if (declaration === null) { - return null; + const name = statement.expression.left.name.text; + if (declaration !== null) { + return {name, declaration}; + } else { + return { + name, + declaration: { + node: null, + expression: exportExpression, + viaModule: null, + }, + }; } - - return {name, declaration}; } private extractUmdReexports(statement: ReexportStatement, containingFile: ts.SourceFile): diff --git a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts index c07aa72183..695503d298 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; 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 {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {getIifeBody} from '../../src/host/esm5_host'; @@ -30,6 +30,7 @@ runInEachFileSystem(() => { let SIMPLE_ES2015_CLASS_FILE: TestFile; let SIMPLE_CLASS_FILE: TestFile; let FOO_FUNCTION_FILE: TestFile; + let INLINE_EXPORT_FILE: TestFile; let INVALID_DECORATORS_FILE: TestFile; let INVALID_DECORATOR_ARGS_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 = { name: _('/invalid_decorators.js'), 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';` // because it gets compiled to something like: //