From a874cab5f8d07f055dc362dc67998e6155a13af7 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sat, 20 Nov 2021 21:07:43 +0200 Subject: [PATCH] fix(ngcc): support the UMD wrapper function format emitted by Webpack 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. This commit adds support for a different format, that uses `if/else` statements, which is what is [emitted by Webpack][1]. [1]: https://webpack.js.org/configuration/output/#type-umd Fixes #44019 --- .../compiler-cli/ngcc/src/host/umd_host.ts | 146 +- packages/compiler-cli/ngcc/src/host/utils.ts | 23 + .../src/rendering/umd_rendering_formatter.ts | 3 +- .../ngcc/test/host/umd_host_spec.ts | 5445 ++++++++--------- 4 files changed, 2830 insertions(+), 2787 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/host/umd_host.ts b/packages/compiler-cli/ngcc/src/host/umd_host.ts index 4beacfbf6e4661..6e9ac956f8efc7 100644 --- a/packages/compiler-cli/ngcc/src/host/umd_host.ts +++ b/packages/compiler-cli/ngcc/src/host/umd_host.ts @@ -18,7 +18,7 @@ import {DefinePropertyReexportStatement, ExportDeclaration, ExportsStatement, ex import {getInnerClassDeclaration, getOuterNodeFromInnerDeclaration, isAssignment} from './esm2015_host'; import {Esm5ReflectionHost} from './esm5_host'; import {NgccClassSymbol} from './ngcc_host'; -import {stripParentheses} from './utils'; +import {RequireAtLeastOneNonNullable, stripParentheses} from './utils'; export class UmdReflectionHost extends Esm5ReflectionHost { protected umdModules = @@ -553,10 +553,15 @@ function getUmdWrapper(statement: ts.Statement): /** * Parse the wrapper function of a UMD module and extract info about the factory function calls for - * the various formats (CommonJS, AMD, global). + * the various formats (CommonJS, CommonJS2, 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: + * NOTE: + * For more info on the distinction between CommonJS and CommonJS2 see + * https://github.com/webpack/webpack/issues/1114. + * + * The supported format for the UMD wrapper function body is a single statement which is either a + * `ts.ConditionalExpression` (i.e. using a ternary operator) (typically emitted by Rollup) or a + * `ts.IfStatement` (typically emitted by Webpack). For example: * * ```js * // Using a conditional expression: @@ -573,6 +578,28 @@ function getUmdWrapper(statement: ts.Statement): * // ... * })); * ``` + * + * or + * + * ```js + * // Using an `if` statement: + * (function (root, factory) { + * if (typeof exports === 'object' && typeof module === 'object') + * // CommonJS2 factory call. + * module.exports = factory(require('foo'), require('bar')); + * else if (typeof define === 'function' && define.amd) + * // AMD factory call. + * define(['foo', 'bar'], factory); + * else if (typeof exports === 'object') + * // CommonJS factory call. + * exports['my-lib'] = factory(require('foo'), require('bar')); + * else + * // Global factory call. + * root['my-lib'] = factory(root['foo'], root['bar']); + * })(global, function (foo, bar) { + * // ... + * }); + * ``` */ function parseUmdWrapperFunction(wrapperFn: ts.FunctionExpression): UmdModule['factoryCalls'] { const stmt = wrapperFn.body.statements[0]; @@ -580,25 +607,28 @@ function parseUmdWrapperFunction(wrapperFn: ts.FunctionExpression): UmdModule['f if (ts.isExpressionStatement(stmt) && ts.isConditionalExpression(stmt.expression)) { conditionalFactoryCalls = extractFactoryCallsFromConditionalExpression(stmt.expression); + } else if (ts.isIfStatement(stmt)) { + conditionalFactoryCalls = extractFactoryCallsFromIfStatement(stmt); } else { throw new Error( - 'UMD wrapper body is not in a supported format (expected a conditional expression):\n' + - wrapperFn.body.getText()); + 'UMD wrapper body is not in a supported format (expected a conditional expression or if ' + + 'statement):\n' + wrapperFn.body.getText()); } const factoryCalls = { amdDefine: getAmdDefineCall(conditionalFactoryCalls), commonJs: getCommonJsFactoryCall(conditionalFactoryCalls), + commonJs2: getCommonJs2FactoryCall(conditionalFactoryCalls), global: getGlobalFactoryCall(conditionalFactoryCalls), }; - if (factoryCalls.commonJs === null) { + if (factoryCalls.commonJs === null && factoryCalls.commonJs2 === null) { throw new Error( - 'Unable to find a CommonJS factory call inside the UMD wrapper function:\n' + + 'Unable to find a CommonJS or CommonJS2 factory call inside the UMD wrapper function:\n' + stmt.getText()); } - return factoryCalls as (typeof factoryCalls&{commonJs: ts.CallExpression}); + return factoryCalls as RequireAtLeastOneNonNullable; } /** @@ -643,6 +673,64 @@ function extractFactoryCallsFromConditionalExpression(node: ts.ConditionalExpres return factoryCalls; } +/** + * Extract `UmdConditionalFactoryCall`s from a `ts.IfStatement` of the form: + * + * ```js + * if (typeof exports === 'object' && typeof module === 'object') + * // CommonJS2 factory call. + * module.exports = factory(require('foo'), require('bar')); + * else if (typeof define === 'function' && define.amd) + * // AMD factory call. + * define(['foo', 'bar'], factory); + * else if (typeof exports === 'object') + * // CommonJS factory call. + * exports['my-lib'] = factory(require('foo'), require('bar')); + * else + * // Global factory call. + * root['my-lib'] = factory(root['foo'], root['bar']); + * ``` + */ +function extractFactoryCallsFromIfStatement(node: ts.IfStatement): UmdConditionalFactoryCall[] { + const factoryCalls: UmdConditionalFactoryCall[] = []; + let currentNode: ts.Statement|undefined = node; + + while (currentNode && ts.isIfStatement(currentNode)) { + if (!ts.isBinaryExpression(currentNode.expression)) { + throw new Error( + 'Condition inside UMD wrapper is not a binary expression:\n' + + currentNode.expression.getText()); + } + if (!ts.isExpressionStatement(currentNode.thenStatement)) { + throw new Error( + 'Then-statement inside UMD wrapper is not an expression statement:\n' + + currentNode.thenStatement.getText()); + } + + factoryCalls.push({ + condition: currentNode.expression, + factoryCall: getFunctionCallFromExpression(currentNode.thenStatement.expression), + }); + + currentNode = currentNode.elseStatement; + } + + if (currentNode) { + if (!ts.isExpressionStatement(currentNode)) { + throw new Error( + 'Else-statement inside UMD wrapper is not an expression statement:\n' + + currentNode.getText()); + } + + factoryCalls.push({ + condition: null, + factoryCall: getFunctionCallFromExpression(currentNode.expression), + }); + } + + return factoryCalls; +} + function getFunctionCallFromExpression(node: ts.Expression): ts.CallExpression { // Be resilient to `node` being inside parenthesis. if (ts.isParenthesizedExpression(node)) { @@ -651,8 +739,9 @@ function getFunctionCallFromExpression(node: ts.Expression): ts.CallExpression { return getFunctionCallFromExpression(node.expression); } - // Be resilient to `node` being part of a comma expression. - if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.CommaToken) { + // Be resilient to `node` being part of an assignment or comma expression. + if (ts.isBinaryExpression(node) && + [ts.SyntaxKind.CommaToken, ts.SyntaxKind.EqualsToken].includes(node.operatorToken.kind)) { // NOTE: // Since we are going further down the AST, there is no risk of infinite recursion. return getFunctionCallFromExpression(node.right); @@ -684,15 +773,29 @@ function getAmdDefineCall(calls: UmdConditionalFactoryCall[]): ts.CallExpression * 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. + // The factory call for CommonJS dependencies is the one that is guarded with a `typeof exports` + // condition. const cjsConditionalCall = calls.find( + call => call.condition?.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken && + isTypeOf(call.condition, 'exports') && ts.isIdentifier(call.factoryCall.expression) && + call.factoryCall.expression.text === 'factory'); + + return cjsConditionalCall?.factoryCall ?? null; +} + +/** + * Get the factory call for setting up the CommonJS2 dependencies in the UMD wrapper. + */ +function getCommonJs2FactoryCall(calls: UmdConditionalFactoryCall[]): ts.CallExpression|null { + // The factory call for CommonJS2 dependencies is the one that is guarded with a `&&` expression + // whose one side is a `typeof exports` or `typeof module` condition. + const cjs2ConditionalCall = 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; + return cjs2ConditionalCall?.factoryCall ?? null; } /** @@ -719,9 +822,13 @@ function isTypeOf(node: ts.Expression, ...types: string[]): boolean { export function getImportsOfUmdModule(umdModule: UmdModule): {parameter: ts.ParameterDeclaration, path: string}[] { const imports: {parameter: ts.ParameterDeclaration, path: string}[] = []; - const cjsFactoryCall = umdModule.factoryCalls.commonJs; + const cjsFactoryCall = umdModule.factoryCalls.commonJs2 ?? umdModule.factoryCalls.commonJs!; + + // Some UMD formats pass `exports` as the first argument to the factory call, while others don't. + // Compute the index at which the dependencies start (i.e. the index of the first `require` call). + const depStartIndex = cjsFactoryCall.arguments.findIndex(arg => isRequireCall(arg)); - for (let i = 1; i < umdModule.factoryFn.parameters.length; i++) { + for (let i = depStartIndex; i < umdModule.factoryFn.parameters.length; i++) { imports.push({ parameter: umdModule.factoryFn.parameters[i], path: getRequiredModulePath(cjsFactoryCall, i), @@ -734,10 +841,9 @@ export function getImportsOfUmdModule(umdModule: UmdModule): interface UmdModule { wrapperFn: ts.FunctionExpression; factoryFn: ts.FunctionExpression; - factoryCalls: { - commonJs: ts.CallExpression; amdDefine: ts.CallExpression | null; - global: ts.CallExpression | null; - }; + factoryCalls: RequireAtLeastOneNonNullable< + Record<'amdDefine'|'commonJs'|'commonJs2'|'global', ts.CallExpression|null>, + 'commonJs'|'commonJs2'>; } /** diff --git a/packages/compiler-cli/ngcc/src/host/utils.ts b/packages/compiler-cli/ngcc/src/host/utils.ts index 5a8cf1e183adf7..dfb0b5a7429353 100644 --- a/packages/compiler-cli/ngcc/src/host/utils.ts +++ b/packages/compiler-cli/ngcc/src/host/utils.ts @@ -7,6 +7,29 @@ */ import ts from 'typescript'; +/** + * Require that at least one of the specified properties of a type are not null/undefined. + * + * For example, given a type `T` of the form `Record<'foo' | 'bar' | 'baz', number | null>`, you can + * specify that at least one of the properties `foo` and `bar` will be a number with + * `RequireAtLeastOneNonNullable`. This would be essentially equivalent with: + * + * ```ts + * { + * foo: number; + * bar: number | null; + * baz: number | null; + * } | { + * foo: number | null; + * bar: number; + * baz: number | null; + * } + * ``` + */ +export type RequireAtLeastOneNonNullable = { + [P in Props]: {[K in P]: NonNullable}&Omit; +}[Props]; + export function stripParentheses(node: ts.Node): ts.Node { return ts.isParenthesizedExpression(node) ? node.expression : node; } diff --git a/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts b/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts index 60544dc421a986..d2b88eeabef6cf 100644 --- a/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts @@ -62,6 +62,7 @@ export class UmdRenderingFormatter extends Esm5RenderingFormatter { // We need to add new `require()` calls for each import in the CommonJS initializer renderCommonJsDependencies(output, factoryCalls.commonJs, imports); + renderCommonJsDependencies(output, factoryCalls.commonJs2, imports); renderAmdDependencies(output, factoryCalls.amdDefine, imports); renderGlobalDependencies(output, factoryCalls.global, imports); renderFactoryParameters(output, factoryFn, imports); @@ -134,7 +135,7 @@ export class UmdRenderingFormatter extends Esm5RenderingFormatter { } /** - * Add dependencies to the CommonJS part of the UMD wrapper function. + * Add dependencies to the CommonJS/CommonJS2 part of the UMD wrapper function. */ function renderCommonJsDependencies( output: MagicString, factoryCall: ts.CallExpression|null, imports: Import[]) { 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 46f2d31b7b5842..5982b1226e9e05 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -24,99 +24,142 @@ import {getRootFiles, makeTestBundleProgram} from '../helpers/utils'; import {expectTypeValueReferencesForParameters} from './util'; runInEachFileSystem(() => { - for (const mode of ['inline exports', 'exported vars']) { - describe(`UmdReflectionHost [with ${mode}]`, () => { - let _: typeof absoluteFrom; - - let SOME_DIRECTIVE_FILE: TestFile; - let TOPLEVEL_DECORATORS_FILE: TestFile; - let CTOR_DECORATORS_ARRAY_FILE: TestFile; - let SIMPLE_ES2015_CLASS_FILE: TestFile; - let SIMPLE_CLASS_FILE: TestFile; - let FOO_FUNCTION_FILE: TestFile; - let INLINE_EXPORT_FILE: TestFile; - let EXPORTS_IDENTIFIERS_FILE: TestFile; - let INVALID_DECORATORS_FILE: TestFile; - let INVALID_DECORATOR_ARGS_FILE: TestFile; - let INVALID_PROP_DECORATORS_FILE: TestFile; - let INVALID_PROP_DECORATOR_ARGS_FILE: TestFile; - let INVALID_CTOR_DECORATORS_FILE: TestFile; - let INVALID_CTOR_DECORATOR_ARGS_FILE: TestFile; - let IMPORTS_FILES: TestFile[]; - let EXPORTS_FILES: TestFile[]; - let FUNCTION_BODY_FILE: TestFile; - let DECORATED_FILES: TestFile[]; - let TYPINGS_SRC_FILES: TestFile[]; - let TYPINGS_DTS_FILES: TestFile[]; - - // Helpers - const createHost = (bundle: BundleProgram, ngccHost: UmdReflectionHost) => { - const tsHost = new TypeScriptReflectionHost(bundle.program.getTypeChecker()); - return new DelegatingReflectionHost(tsHost, ngccHost); - }; - - // There are two different forms of UMD export declaration. - // - // - "exported vars" where there is a variable declaration that is then assigned to the - // `exports` object. For example: - // ``` - // var MyClass = <...>; - // exports.MyClass = MyClass; - // ``` - // - // - "inline exports" where there is no intemediate variable declaration. For example: - // ``` - // exports.MyClass = <...>; - // ``` - - // The following helpers allow us to setup code examples to use these two different - // approaches. - - /** - * Create an export declaration: e.g. - * - * ``` - * exports. = ; - * ``` - * - * or - * - * ``` - * var = ; - * ``` - */ - const expDecl = (name: string, noVar = false) => - mode === 'exported vars' ? `${noVar ? '' : 'var '}${name}` : `exports.${name}`; - - /** - * Export a variable that references a declaration (only for when the export was defined as a - * variable). For example: - * - * ``` - * exports. = ; - * ``` - */ - const varExp = (name: string) => mode === 'exported vars' ? `exports.${name} = ${name};` : ''; - - /** - * Select a predicate for matching an exported declaration based on whether the code contains - * "exported vars" or "inline exports". - */ - const isDesiredDeclaration: typeof hasNameIdentifier = - mode === 'exported vars' ? isNamedVariableDeclaration : isExportsDeclaration; - - beforeEach(() => { - _ = absoluteFrom; - - SOME_DIRECTIVE_FILE = { - name: _('/some_directive.umd.js'), - contents: ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : - (factory(global.some_directive,global.ng.core)); -}(this, (function (exports,core) { 'use strict'; + for (const wrapperFormat of ['rollup', 'webpack']) { + for (const mode of ['inline exports', 'exported vars']) { + describe(`UmdReflectionHost [with ${mode}][with wrapper format: ${wrapperFormat}]`, () => { + let _: typeof absoluteFrom; + + let SOME_DIRECTIVE_FILE: TestFile; + let TOPLEVEL_DECORATORS_FILE: TestFile; + let CTOR_DECORATORS_ARRAY_FILE: TestFile; + let SIMPLE_ES2015_CLASS_FILE: TestFile; + let SIMPLE_CLASS_FILE: TestFile; + let FOO_FUNCTION_FILE: TestFile; + let INLINE_EXPORT_FILE: TestFile; + let EXPORTS_IDENTIFIERS_FILE: TestFile; + let INVALID_DECORATORS_FILE: TestFile; + let INVALID_DECORATOR_ARGS_FILE: TestFile; + let INVALID_PROP_DECORATORS_FILE: TestFile; + let INVALID_PROP_DECORATOR_ARGS_FILE: TestFile; + let INVALID_CTOR_DECORATORS_FILE: TestFile; + let INVALID_CTOR_DECORATOR_ARGS_FILE: TestFile; + let IMPORTS_FILES: TestFile[]; + let EXPORTS_FILES: TestFile[]; + let FUNCTION_BODY_FILE: TestFile; + let DECORATED_FILES: TestFile[]; + let TYPINGS_SRC_FILES: TestFile[]; + let TYPINGS_DTS_FILES: TestFile[]; + // Helpers + const createHost = (bundle: BundleProgram, ngccHost: UmdReflectionHost) => { + const tsHost = new TypeScriptReflectionHost(bundle.program.getTypeChecker()); + return new DelegatingReflectionHost(tsHost, ngccHost); + }; + + // There are two different forms of UMD export declaration. + // + // - "exported vars" where there is a variable declaration that is then assigned to the + // `exports` object. For example: + // ``` + // var MyClass = <...>; + // exports.MyClass = MyClass; + // ``` + // + // - "inline exports" where there is no intemediate variable declaration. For example: + // ``` + // exports.MyClass = <...>; + // ``` + + // The following helpers allow us to setup code examples to use these two different + // approaches. + + /** + * Create an export declaration: e.g. + * + * ``` + * exports. = ; + * ``` + * + * or + * + * ``` + * var = ; + * ``` + */ + const expDecl = (name: string, noVar = false) => + mode === 'exported vars' ? `${noVar ? '' : 'var '}${name}` : `exports.${name}`; + + /** + * Export a variable that references a declaration (only for when the export was defined as + * a variable). For example: + * + * ``` + * exports. = ; + * ``` + */ + const varExp = (name: string) => + mode === 'exported vars' ? `exports.${name} = ${name};` : ''; + + /** + * Create the code for a UMD module depending on the current wrapper function format. + */ + const createUmdModule = (moduleName: string, deps: string[], factoryBody: string) => { + const depsIdentifiers = deps.map( + dep => dep.replace(/^@angular\//, 'ng.').replace(/^\.\//, '').replace(/-/g, '_')); + const depsParamNames = deps.map((dep, i) => depsIdentifiers[i].replace(/^.*\./, '')); + + switch (wrapperFormat) { + case 'rollup': + return ` + (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? + factory(${['exports', ...deps.map(d => `require('${d}')`)].join(', ')}) : + typeof define === 'function' && define.amd ? + define('${moduleName}', [${ + ['exports', ...deps].map(d => `'${d}'`).join(', ')}], factory) : + (factory(${ + [moduleName, ...depsIdentifiers].map(id => `global.${id}`).join(', ')})); + }(this, (function (${['exports', ...depsParamNames].join(', ')}) { + 'use strict'; + ${factoryBody} + }))); + `; + case 'webpack': + return ` + (function (root, factory) { + if (typeof exports === 'object' && typeof module === 'object') + module.exports = factory(${deps.map(d => `require('${d}')`).join(', ')}); + else if (typeof define === 'function' && define.amd) + define([${deps.map(d => `'${d}'`).join(', ')}], factory); + else if (typeof exports === 'object') + exports['${moduleName}'] = factory(${ + deps.map(d => `require('${d}')`).join(', ')}); + else + root['${moduleName}'] = factory(${ + depsIdentifiers.map(id => `root['${id}']`).join(', ')}); + }(global, function (${depsParamNames.join(', ')}) { + 'use strict'; + ${factoryBody} + })); + `; + default: + throw new Error(`Unsupported wrapper format: ${wrapperFormat}`); + } + }; + + /** + * Select a predicate for matching an exported declaration based on whether the code + * contains "exported vars" or "inline exports". + */ + const isDesiredDeclaration: typeof hasNameIdentifier = + mode === 'exported vars' ? isNamedVariableDeclaration : isExportsDeclaration; + + beforeEach(() => { + _ = absoluteFrom; + + SOME_DIRECTIVE_FILE = { + name: _('/some_directive.umd.js'), + contents: createUmdModule('some_directive', ['@angular/core'], ` var INJECTED_TOKEN = new InjectionToken('injected'); var ViewContainerRef = {}; var TemplateRef = {}; @@ -145,18 +188,12 @@ runInEachFileSystem(() => { return SomeDirective; }()); ${varExp('SomeDirective')} -})));`, - }; - - TOPLEVEL_DECORATORS_FILE = { - name: _('/toplevel_decorators.umd.js'), - contents: ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : - (factory(global.some_directive,global.ng.core)); -}(this, (function (exports,core) { 'use strict'; +`), + }; + TOPLEVEL_DECORATORS_FILE = { + name: _('/toplevel_decorators.umd.js'), + contents: createUmdModule('toplevel_decorators', ['@angular/core'], ` var INJECTED_TOKEN = new core.InjectionToken('injected'); var ViewContainerRef = {}; var TemplateRef = {}; @@ -178,17 +215,12 @@ runInEachFileSystem(() => { "input2": [{ type: core.Input },], }; ${varExp('SomeDirective')} -})));`, - }; +`), + }; - CTOR_DECORATORS_ARRAY_FILE = { - name: _('/ctor_decorated_as_array.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('ctor_decorated_as_array', ['exports', '@angular/core'], factory) : - (factory(global.ctor_decorated_as_array,global.ng.core)); - }(this, (function (exports,core) { 'use strict'; + CTOR_DECORATORS_ARRAY_FILE = { + name: _('/ctor_decorated_as_array.js'), + contents: createUmdModule('ctor_decorated_as_array', ['@angular/core'], ` ${expDecl('CtorDecoratedAsArray')} = (function() { function CtorDecoratedAsArray(arg1) { } @@ -196,24 +228,19 @@ runInEachFileSystem(() => { return CtorDecoratedAsArray; }()); ${varExp('CtorDecoratedAsArray')} - })));`, - }; + `), + }; - SIMPLE_ES2015_CLASS_FILE = { - name: _('/simple_es2015_class.d.ts'), - contents: ` + SIMPLE_ES2015_CLASS_FILE = { + name: _('/simple_es2015_class.d.ts'), + contents: ` export class EmptyClass {} `, - }; + }; - SIMPLE_CLASS_FILE = { - name: _('/simple_class.js'), - contents: ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define('simple_class', ['exports'], factory) : - (factory(global.simple_class)); -}(this, (function (exports) { 'use strict'; + SIMPLE_CLASS_FILE = { + name: _('/simple_class.js'), + contents: createUmdModule('simple_class', [], ` ${expDecl('EmptyClass')} = (function() { function EmptyClass() { } @@ -254,51 +281,35 @@ runInEachFileSystem(() => { }(SuperClass); ${varExp('EmptyClass')} ${varExp('NoDecoratorConstructorClass')} -})));`, - }; +`), + }; - FOO_FUNCTION_FILE = { - name: _('/foo_function.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.foo_function,global.ng.core)); -}(this, (function (exports,core) { 'use strict'; + FOO_FUNCTION_FILE = { + name: _('/foo_function.js'), + contents: createUmdModule('foo_function', ['@angular/core'], ` function foo() {} foo.decorators = [ { type: core.Directive, args: [{ selector: '[ignored]' },] } ]; ${varExp('foo')} -})));`, - }; +`), + }; - 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'; + INLINE_EXPORT_FILE = { + name: _('/inline_export.js'), + contents: createUmdModule('inline_export', ['@angular/core'], ` function foo() {} foo.decorators = [ { type: core.Directive, args: [{ selector: '[ignored]' },] } ]; exports.directives = [foo]; exports.Inline = (function() { function Inline() {} return Inline; })(); -}))); -`, - }; +`), + }; - EXPORTS_IDENTIFIERS_FILE = { - name: _('/exports_identifiers.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'; + EXPORTS_IDENTIFIERS_FILE = { + name: _('/exports_identifiers.js'), + contents: createUmdModule('inline_identifiers', ['@angular/core'], ` var x = exports; exports.foo = 42; @@ -314,18 +325,12 @@ runInEachFileSystem(() => { function exportsArg(exports) { exports.foo = 42; } -}))); -`, - }; +`), + }; - INVALID_DECORATORS_FILE = { - name: _('/invalid_decorators.js'), - contents: ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('invalid_decorators', ['exports', '@angular/core'], factory) : - (factory(global.invalid_decorators, global.ng.core)); -}(this, (function (exports,core) { 'use strict'; + INVALID_DECORATORS_FILE = { + name: _('/invalid_decorators.js'), + contents: createUmdModule('invalid_decorators', ['@angular/core'], ` ${expDecl('NotArrayLiteral')} = (function() { function NotArrayLiteral() { } @@ -364,17 +369,12 @@ runInEachFileSystem(() => { ]; return NotIdentifier; }()); -})));`, - }; +`), + }; - INVALID_DECORATOR_ARGS_FILE = { - name: _('/invalid_decorator_args.js'), - contents: ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('invalid_decorator_args', ['exports', '@angular/core'], factory) : - (factory(global.invalid_decorator_args, global.ng.core)); -}(this, (function (exports,core) { 'use strict'; + INVALID_DECORATOR_ARGS_FILE = { + name: _('/invalid_decorator_args.js'), + contents: createUmdModule('invalid_decorator_args', ['@angular/core'], ` ${expDecl('NoArgsProperty')} = (function() { function NoArgsProperty() { } @@ -402,17 +402,12 @@ runInEachFileSystem(() => { ]; return NotArrayLiteral; }()); -})));`, - }; +`), + }; - INVALID_PROP_DECORATORS_FILE = { - name: _('/invalid_prop_decorators.js'), - contents: ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('invalid_prop_decorators', ['exports', '@angular/core'], factory) : - (factory(global.invalid_prop_decorators, global.ng.core)); -}(this, (function (exports,core) { 'use strict'; + INVALID_PROP_DECORATORS_FILE = { + name: _('/invalid_prop_decorators.js'), + contents: createUmdModule('invalid_prop_decorators', ['@angular/core'], ` ${expDecl('NotObjectLiteral')} = (function() { function NotObjectLiteral() { } @@ -457,17 +452,12 @@ runInEachFileSystem(() => { }; return NotIdentifier; }()); -})));`, - }; +`), + }; - INVALID_PROP_DECORATOR_ARGS_FILE = { - name: _('/invalid_prop_decorator_args.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('invalid_prop_decorator_args', ['exports', '@angular/core'], factory) : - (factory(global.invalid_prop_decorator_args, global.ng.core)); - }(this, (function (exports,core) { 'use strict'; + INVALID_PROP_DECORATOR_ARGS_FILE = { + name: _('/invalid_prop_decorator_args.js'), + contents: createUmdModule('invalid_prop_decorator_args', ['@angular/core'], ` ${expDecl('NoArgsProperty')} = (function() { function NoArgsProperty() { } @@ -495,18 +485,13 @@ runInEachFileSystem(() => { }; return NotArrayLiteral; }()); -})));`, - }; +`), + }; - INVALID_CTOR_DECORATORS_FILE = { - name: _('/invalid_ctor_decorators.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('invalid_ctor_decorators', ['exports', '@angular/core'], factory) : - (factory(global.invalid_ctor_decorators,global.ng.core)); - }(this, (function (exports,core) { 'use strict'; - ${expDecl('NoParameters')} = (function() { + INVALID_CTOR_DECORATORS_FILE = { + name: _('/invalid_ctor_decorators.js'), + contents: createUmdModule('invalid_ctor_decorators', ['@angular/core'], ` + ${expDecl('NoParameters')} = (function() { function NoParameters() {} return NoParameters; }()); @@ -557,18 +542,13 @@ runInEachFileSystem(() => { ]; }; return NotIdentifier; }()); -})));`, - }; +`), + }; - INVALID_CTOR_DECORATOR_ARGS_FILE = { - name: _('/invalid_ctor_decorator_args.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('invalid_ctor_decorator_args', ['exports', '@angular/core'], factory) : - (factory(global.invalid_ctor_decorator_args,global.ng.core)); - }(this, (function (exports,core) { 'use strict'; - ${expDecl('NoArgsProperty')} = (function() { + INVALID_CTOR_DECORATOR_ARGS_FILE = { + name: _('/invalid_ctor_decorator_args.js'), + contents: createUmdModule('invalid_ctor_decorator_args', ['@angular/core'], ` + ${expDecl('NoArgsProperty')} = (function() { function NoArgsProperty(arg1) { } NoArgsProperty.ctorParameters = function() { return [ @@ -595,203 +575,153 @@ runInEachFileSystem(() => { ]; }; return NotArrayLiteral; }()); -})));`, - }; +`), + }; - IMPORTS_FILES = [ - { - name: _('/index.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./file_a'), require('./file_b'), require('./file_c')) : - typeof define === 'function' && define.amd ? define('index', ['exports', './file_a', './file_b', './file_c'], factory) : - (factory(global.index, global.file_a, global.file_b, global.file_c)); - }(this, (function (exports, file_a, file_b, file_c) { 'use strict'; - })));`, - }, - { - name: _('/file_a.js'), - contents: ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define('file_a', ['exports'], factory) : - (factory(global.file_a)); -}(this, (function (exports) { 'use strict'; + IMPORTS_FILES = [ + { + name: _('/index.js'), + contents: createUmdModule('index', ['./file_a', './file_b', './file_c'], ''), + }, + { + name: _('/file_a.js'), + contents: createUmdModule('file_a', [], ` var a = 'a'; ${varExp('a')} -})));`, - }, - { - name: _('/file_b.js'), - contents: ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./file_a')) : - typeof define === 'function' && define.amd ? define('file_b', ['exports', './file_a'], factory) : - (factory(global.file_b,global.file_a)); -}(this, (function (exports, file_a) { 'use strict'; +`), + }, + { + name: _('/file_b.js'), + contents: createUmdModule('file_b', ['./file_a'], ` var b = file_a.a; var c = 'c'; var d = c; -})));`, - }, - { - name: _('/file_c.js'), - contents: ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./file_a')) : - typeof define === 'function' && define.amd ? define('file_c', ['exports', 'file_a'], factory) : - (factory(global.file_c,global.file_a)); -}(this, function (exports, file_a) { 'use strict'; - var c = file_a.a; -}));`, - }, - ]; - - EXPORTS_FILES = [ - { - name: _('/index.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./a_module'), require('./b_module'), require('./wildcard_reexports'), require('./wildcard_reexports_imported_helpers'), require('./wildcard_reexports_with_require'), require('./define_property_reexports')) :\n` + - ` typeof define === 'function' && define.amd ? define('index', ['exports', './a_module', './b_module', './wildcard_reexports', './wildcard_reexports_imported_helpers', './wildcard_reexports_with_require', './define_property_reexports'], factory) :\n` + - ` (factory(global.index, global.a_module, global.b_module, global.wildcard_reexports, global.wildcard_reexports_imported_helpers, global.wildcard_reexports_with_require, global.define_property_reexports));\n` + - `}(this, (function (exports, a_module, b_module, wildcard_reexports, wildcard_reexports_imported_helpers, wildcard_reexports_with_require, define_property_reexports) { 'use strict';\n` + - `})));\n` - }, - { - name: _('/a_module.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n` + - ` typeof define === 'function' && define.amd ? define('a_module', ['exports'], factory) :\n` + - ` (factory(global.a_module));\n` + - `}(this, (function (exports) { 'use strict';\n` + - ` var a = 'a';\n` + - ` exports.a = a;\n` + - `})));\n`, - }, - { - name: _('/b_module.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('./a_module')) :\n` + - ` typeof define === 'function' && define.amd ? define('b_module', ['exports', '@angular/core', './a_module'], factory) :\n` + - ` (factory(global.b_module));\n` + - `}(this, (function (exports, core, a_module) { 'use strict';\n` + - ` var b = a_module.a;\n` + - ` var e = 'e';\n` + - ` var SomeClass = (function() {\n` + - ` function SomeClass() {}\n` + - ` return SomeClass;\n` + - ` }());\n` + - `\n` + - ` exports.Directive = core.Directive;\n` + - ` exports.a = a_module.a;\n` + - ` exports.b = b;\n` + - ` exports.c = a_module.a;\n` + - ` exports.d = b;\n` + - ` exports.e = e;\n` + - ` exports.DirectiveX = core.Directive;\n` + - ` var SomeClass_1;\n` + - ` exports.SomeClass = SomeClass_1 = SomeClass;\n` + - `})));\n`, - }, - { - name: _('/xtra_module.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n` + - ` typeof define === 'function' && define.amd ? define('xtra_module', ['exports'], factory) :\n` + - ` (factory(global.xtra_module));\n` + - `}(this, (function (exports) { 'use strict';\n` + - ` var xtra1 = 'xtra1';\n` + - ` var xtra2 = 'xtra2';\n` + - ` exports.xtra1 = xtra1;\n` + - ` exports.xtra2 = xtra2;\n` + - `})));\n`, - }, - { - name: _('/wildcard_reexports.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./b_module'), require('./xtra_module')) :\n` + - ` typeof define === 'function' && define.amd ? define('wildcard_reexports', ['exports', './b_module', './xtra_module'], factory) :\n` + - ` (factory(global.wildcard_reexports, b_module, xtra_module));\n` + - `}(this, (function (exports, b_module, xtra_module) { 'use strict';\n` + - ` function __export(m) {\n` + - ` for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];\n` + - ` }\n` + - ` __export(b_module);\n` + - ` __export(xtra_module);\n` + - `})));\n`, - }, - { - name: _('/wildcard_reexports_imported_helpers.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tslib'), require('./b_module'), require('./xtra_module')) :\n` + - ` typeof define === 'function' && define.amd ? define('wildcard_reexports', ['exports', 'tslib', './b_module', './xtra_module'], factory) :\n` + - ` (factory(global.wildcard_reexports_imported_helpers, tslib, b_module, xtra_module));\n` + - `}(this, (function (exports, tslib, b_module, xtra_module) { 'use strict';\n` + - ` tslib.__exportStar(b_module, exports);\n` + - ` tslib.__exportStar(xtra_module, exports);\n` + - `})));\n`, - }, - { - 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` + - ` (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` + - ` }\n` + - ` var b_module = require('./b_module');\n` + - ` __export(b_module);\n` + - ` __export(require('./xtra_module'));\n` + - `})));\n`, - }, - { - 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` + - ` (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` + - `})));`, - }, - { - name: _('/decorated_class.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n` + - ` typeof define === 'function' && define.amd ? define('decorated_class', ['exports'], factory) :\n` + - ` (factory(global.decorated_class));\n` + - `}(this, (function (exports) { 'use strict';\n` + - `var DecoratedClass_1;\n` + - `\n` + - `exports.DecoratedClass = DecoratedClass_1 = (function() {\n` + - ` function DecoratedClass() {\n` + - ` }\n` + - ` return DecoratedClass;\n` + - `}());\n` + - `\n` + - `exports.DecoratedClass = DecoratedClass_1 = __decorate([\n` + - ` SomeDecorator([ej2AngularBase.ComponentBase, ej2AngularBase.FormBase]),\n` + - ` __metadata("design:paramtypes", [core.ElementRef,\n` + - ` core.Renderer2,\n` + - ` core.ViewContainerRef,\n` + - ` core.Injector])\n` + - `], exports.DecoratedClass);\n` + - `})));\n`, - } - ]; - - FUNCTION_BODY_FILE = { - name: _('/function_body.js'), - contents: ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define('function_body', ['exports'], factory) : - (factory(global.function_body)); -}(this, (function (exports) { 'use strict'; - function foo(x) { +`), + }, + { + name: _('/file_c.js'), + contents: createUmdModule('file_c', ['./file_a'], 'var c = file_a.a;'), + }, + ]; + + EXPORTS_FILES = [ + { + name: _('/index.js'), + contents: createUmdModule( + 'index', + [ + './a_module', './b_module', './wildcard_reexports', + './wildcard_reexports_imported_helpers', './wildcard_reexports_with_require', + './define_property_reexports' + ], + ''), + }, + { + name: _('/a_module.js'), + contents: createUmdModule( + 'a_module', [], + ` var a = 'a';\n` + + ` exports.a = a;`), + }, + { + name: _('/b_module.js'), + contents: createUmdModule( + 'b_module', ['@angular/core', './a_module'], + ` var b = a_module.a;\n` + + ` var e = 'e';\n` + + ` var SomeClass = (function() {\n` + + ` function SomeClass() {}\n` + + ` return SomeClass;\n` + + ` }());\n` + + `\n` + + ` exports.Directive = core.Directive;\n` + + ` exports.a = a_module.a;\n` + + ` exports.b = b;\n` + + ` exports.c = a_module.a;\n` + + ` exports.d = b;\n` + + ` exports.e = e;\n` + + ` exports.DirectiveX = core.Directive;\n` + + ` var SomeClass_1;\n` + + ` exports.SomeClass = SomeClass_1 = SomeClass;`), + }, + { + name: _('/xtra_module.js'), + contents: createUmdModule( + 'xtra_module', [], + ` var xtra1 = 'xtra1';\n` + + ` var xtra2 = 'xtra2';\n` + + ` exports.xtra1 = xtra1;\n` + + ` exports.xtra2 = xtra2;`), + }, + { + name: _('/wildcard_reexports.js'), + contents: createUmdModule( + 'wildcard_reexports', ['./b_module', './xtra_module'], + ` function __export(m) {\n` + + ` for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];\n` + + ` }\n` + + ` __export(b_module);\n` + + ` __export(xtra_module);`), + }, + { + name: _('/wildcard_reexports_imported_helpers.js'), + contents: createUmdModule( + 'wildcard_reexports', ['tslib', './b_module', './xtra_module'], + ` tslib.__exportStar(b_module, exports);\n` + + ` tslib.__exportStar(xtra_module, exports);`), + }, + { + 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` + + ` (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` + + ` }\n` + + ` var b_module = require('./b_module');\n` + + ` __export(b_module);\n` + + ` __export(require('./xtra_module'));\n` + + `})));\n`, + }, + { + 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` + + ` (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` + + `})));`, + }, + { + name: _('/decorated_class.js'), + contents: createUmdModule( + 'decorated_class', [], + `var DecoratedClass_1;\n` + + `\n` + + `exports.DecoratedClass = DecoratedClass_1 = (function() {\n` + + ` function DecoratedClass() {\n` + + ` }\n` + + ` return DecoratedClass;\n` + + `}());\n` + + `\n` + + `exports.DecoratedClass = DecoratedClass_1 = __decorate([\n` + + ` SomeDecorator([ej2AngularBase.ComponentBase, ej2AngularBase.FormBase]),\n` + + ` __metadata("design:paramtypes", [core.ElementRef,\n` + + ` core.Renderer2,\n` + + ` core.ViewContainerRef,\n` + + ` core.Injector])\n` + + `], exports.DecoratedClass);`), + } + ]; + + FUNCTION_BODY_FILE = { + name: _('/function_body.js'), + contents: createUmdModule('function_body', [], ` + function foo(x) { return x; } function bar(x, y) { @@ -822,18 +752,13 @@ runInEachFileSystem(() => { if (x === void 0) { x = 42; } return x; } -})));` - }; +`), + }; - DECORATED_FILES = [ - { - name: _('/primary.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('./secondary')) : - typeof define === 'function' && define.amd ? define('primary', ['exports', '@angular/core', './secondary'], factory) : - (factory(global.primary,global.ng.core, global.secondary)); - }(this, (function (exports,core,secondary) { 'use strict'; + DECORATED_FILES = [ + { + name: _('/primary.js'), + contents: createUmdModule('primary', ['@angular/core', './secondary'], ` ${expDecl('A')} = (function() { function A() {} A.decorators = [ @@ -857,16 +782,11 @@ runInEachFileSystem(() => { ${varExp('A')} exports.x = x; ${varExp('C')} - })));` - }, - { - name: _('/secondary.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('primary', ['exports', '@angular/core'], factory) : - (factory(global.primary,global.ng.core)); - }(this, (function (exports,core) { 'use strict'; + `), + }, + { + name: _('/secondary.js'), + contents: createUmdModule('primary', ['@angular/core'], ` ${expDecl('D')} = (function() { function D() {} D.decorators = [ @@ -875,37 +795,31 @@ runInEachFileSystem(() => { return D; }()); ${varExp('D')} - }))); - ` - } - ]; + `), + } + ]; - TYPINGS_SRC_FILES = [ - { - name: _('/ep/src/index.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./internal'), require('./class1'), require('./class2'), require('./missing-class'), require('./flat-file'), require('./func1')) : - typeof define === 'function' && define.amd ? define('index', ['exports', './internal', './class1', './class2', './missing-class', './flat-file', './func1'], factory) : - (factory(global.index,global.internal,global.class1,global.class2,global.missing_class,global.flat_file,global.func1)); - }(this, (function (exports,internal,class1,class2,missing_class,flat_file,func1) { 'use strict'; + TYPINGS_SRC_FILES = [ + { + name: _('/ep/src/index.js'), + contents: createUmdModule( + 'index', + [ + './internal', './class1', './class2', './missing-class', './flat-file', + './func1' + ], + ` function __export(m) { for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; } var InternalClass = internal.InternalClass; __export(class1); __export(class2); - }))); - ` - }, - { - name: _('/ep/src/class1.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define('class1', ['exports'], factory) : - (factory(global.class1)); - }(this, (function (exports) { 'use strict'; + `), + }, + { + name: _('/ep/src/class1.js'), + contents: createUmdModule('class1', [], ` ${expDecl('Class1')} = (function() { function Class1() {} return Class1; @@ -916,46 +830,28 @@ runInEachFileSystem(() => { }()); ${varExp('Class1')} ${varExp('MissingClass1')} - }))); - ` - }, - { - name: _('/ep/src/class2.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define('class2', ['exports'], factory) : - (factory(global.class2)); - }(this, (function (exports) { 'use strict'; + `), + }, + { + name: _('/ep/src/class2.js'), + contents: createUmdModule('class2', [], ` ${expDecl('Class2')} = (function() { function Class2() {} return Class2; }()); ${varExp('Class2')} - }))); - ` - }, - { - name: _('/ep/src/func1.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define('internal', ['exports'], factory) : - (factory(global.internal)); - }(this, (function (exports) { 'use strict'; + `), + }, + { + name: _('/ep/src/func1.js'), + contents: createUmdModule('internal', [], ` function mooFn() {} exports.mooFn = mooFn; - }))); - ` - }, - { - name: _('/ep/src/internal.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define('internal', ['exports'], factory) : - (factory(global.internal)); - }(this, (function (exports) { 'use strict'; + `), + }, + { + name: _('/ep/src/internal.js'), + contents: createUmdModule('internal', [], ` ${expDecl('InternalClass')} = (function() { function InternalClass() {} return InternalClass; @@ -966,33 +862,21 @@ runInEachFileSystem(() => { }()); ${varExp('InternalClass')} ${varExp('Class2')} - }))); - ` - }, - { - name: _('/ep/src/missing-class.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define('missingClass', ['exports'], factory) : - (factory(global.missingClass)); - }(this, (function (exports) { 'use strict'; + `), + }, + { + name: _('/ep/src/missing-class.js'), + contents: createUmdModule('missingClass', [], ` ${expDecl('MissingClass2')} = (function() { function MissingClass2() {} return MissingClass2; }()); exports. MissingClass2 = MissingClass2; - }))); - ` - }, - { - name: _('/ep/src/flat-file.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define('missingClass', ['exports'], factory) : - (factory(global.missingClass)); - }(this, (function (exports) { 'use strict'; + `), + }, + { + name: _('/ep/src/flat-file.js'), + contents: createUmdModule('missingClass', [], ` ${expDecl('Class1')} = (function() { function Class1() {} return Class1; @@ -1013,959 +897,905 @@ runInEachFileSystem(() => { exports.AliasedClass = SourceClass; ${varExp('MissingClass1')} ${varExp('MissingClass2')} - }))); - ` - } - ]; + `), + } + ]; - TYPINGS_DTS_FILES = [ - { - name: _('/ep/typings/index.d.ts'), - contents: ` - import '../../an_external_lib/index'; - import {InternalClass} from './internal'; - import {mooFn} from './func1'; - export * from './class1'; - export * from './class2'; + TYPINGS_DTS_FILES = [ + { + name: _('/ep/typings/index.d.ts'), + contents: ` + import '../../an_external_lib/index'; + import {InternalClass} from './internal'; + import {mooFn} from './func1'; + export * from './class1'; + export * from './class2'; + ` + }, + { + name: _('/ep/typings/class1.d.ts'), + contents: `export declare class Class1 {}\nexport declare class OtherClass {}` + }, + { + name: _('/ep/typings/class2.d.ts'), + contents: ` + export declare class Class2 {} + export declare interface SomeInterface {} + export {TypingsClass as AliasedClass} from './typings-class'; ` - }, - { - name: _('/ep/typings/class1.d.ts'), - contents: `export declare class Class1 {}\nexport declare class OtherClass {}` - }, - { - name: _('/ep/typings/class2.d.ts'), - contents: ` - export declare class Class2 {} - export declare interface SomeInterface {} - export {TypingsClass as AliasedClass} from './typings-class'; - ` - }, - {name: _('/ep/typings/func1.d.ts'), contents: 'export declare function mooFn(): void;'}, - { - name: _('/ep/typings/internal.d.ts'), - contents: `export declare class InternalClass {}\nexport declare class Class2 {}` - }, - { - name: _('/ep/typings/typings-class.d.ts'), - contents: `export declare class TypingsClass {}` - }, - { - name: _('/ep/typings/shadow-class.d.ts'), - contents: `export declare class ShadowClass {}` - }, - {name: _('/an_external_lib/index.d.ts'), contents: 'export declare class ShadowClass {}'}, - ]; - }); - - describe('getDecoratorsOfDeclaration()', () => { - it('should find the decorators on a class', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; - - expect(decorators).toBeDefined(); - expect(decorators.length).toEqual(1); - - const decorator = decorators[0]; - expect(decorator.name).toEqual('Directive'); - expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args!.map(arg => arg.getText())).toEqual([ - '{ selector: \'[someDirective]\' }', - ]); - }); - - it('should find the decorators on a class at the top level', () => { - loadTestFiles([TOPLEVEL_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isDesiredDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; - - expect(decorators).toBeDefined(); - expect(decorators.length).toEqual(1); - - const decorator = decorators[0]; - expect(decorator.name).toEqual('Directive'); - expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args!.map(arg => arg.getText())).toEqual([ - '{ selector: \'[someDirective]\' }', - ]); - }); - - it('should return null if the symbol is not a class', () => { - loadTestFiles([FOO_FUNCTION_FILE]); - const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const functionNode = getDeclaration( - bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - const decorators = host.getDecoratorsOfDeclaration(functionNode); - expect(decorators).toBe(null); - }); - - it('should return null if there are no decorators', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode); - expect(decorators).toBe(null); - }); - - it('should ignore `decorators` if it is not an array literal', () => { - loadTestFiles([INVALID_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', - isDesiredDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode); - expect(decorators).toEqual([]); - }); - - it('should ignore decorator elements that are not object literals', () => { - loadTestFiles([INVALID_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', - isDesiredDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; - - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); + }, + {name: _('/ep/typings/func1.d.ts'), contents: 'export declare function mooFn(): void;'}, + { + name: _('/ep/typings/internal.d.ts'), + contents: `export declare class InternalClass {}\nexport declare class Class2 {}` + }, + { + name: _('/ep/typings/typings-class.d.ts'), + contents: `export declare class TypingsClass {}` + }, + { + name: _('/ep/typings/shadow-class.d.ts'), + contents: `export declare class ShadowClass {}` + }, + { + name: _('/an_external_lib/index.d.ts'), + contents: 'export declare class ShadowClass {}' + }, + ]; }); - it('should ignore decorator elements that have no `type` property', () => { - loadTestFiles([INVALID_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', isDesiredDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; - - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); - }); + describe('getDecoratorsOfDeclaration()', () => { + it('should find the decorators on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; - it('should ignore decorator elements whose `type` value is not an identifier', () => { - loadTestFiles([INVALID_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', isDesiredDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; + expect(decorators).toBeDefined(); + expect(decorators.length).toEqual(1); - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); - }); + const decorator = decorators[0]; + expect(decorator.name).toEqual('Directive'); + expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); + expect(decorator.args!.map(arg => arg.getText())).toEqual([ + '{ selector: \'[someDirective]\' }', + ]); + }); - it('should have import information on decorators', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + it('should find the decorators on a class at the top level', () => { + loadTestFiles([TOPLEVEL_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', + isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; + expect(decorators).toBeDefined(); + expect(decorators.length).toEqual(1); - expect(decorators.length).toEqual(1); - expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); - }); + const decorator = decorators[0]; + expect(decorator.name).toEqual('Directive'); + expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); + expect(decorator.args!.map(arg => arg.getText())).toEqual([ + '{ selector: \'[someDirective]\' }', + ]); + }); - it('should find decorated members on a class at the top level', () => { - loadTestFiles([TOPLEVEL_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); + it('should return null if the symbol is not a class', () => { + loadTestFiles([FOO_FUNCTION_FILE]); + const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const functionNode = getDeclaration( + bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); + const decorators = host.getDecoratorsOfDeclaration(functionNode); + expect(decorators).toBe(null); + }); - const input1 = members.find(member => member.name === 'input1')!; - expect(input1.kind).toEqual(ClassMemberKind.Property); - expect(input1.isStatic).toEqual(false); - expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + it('should return null if there are no decorators', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode); + expect(decorators).toBe(null); + }); - const input2 = members.find(member => member.name === 'input2')!; - expect(input2.kind).toEqual(ClassMemberKind.Property); - expect(input2.isStatic).toEqual(false); - expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - }); + it('should ignore `decorators` if it is not an array literal', () => { + loadTestFiles([INVALID_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', + isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode); + expect(decorators).toEqual([]); + }); - describe('(returned decorators `args`)', () => { - it('should be an empty array if decorator has no `args` property', () => { - loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + it('should ignore decorator elements that are not object literals', () => { + loadTestFiles([INVALID_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', + bundle.program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', isDesiredDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Directive'); - expect(decorators[0].args).toEqual([]); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); - it('should be an empty array if decorator\'s `args` has no property assignment', () => { - loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + it('should ignore decorator elements that have no `type` property', () => { + loadTestFiles([INVALID_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', + bundle.program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', isDesiredDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Directive'); - expect(decorators[0].args).toEqual([]); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); - it('should be an empty array if `args` property value is not an array literal', () => { - loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + it('should ignore decorator elements whose `type` value is not an identifier', () => { + loadTestFiles([INVALID_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', + bundle.program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', isDesiredDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Directive'); - expect(decorators[0].args).toEqual([]); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); - }); - }); - describe('getMembersOfClass()', () => { - it('should find decorated members on a class', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); + it('should have import information on decorators', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const input1 = members.find(member => member.name === 'input1')!; - expect(input1.kind).toEqual(ClassMemberKind.Property); - expect(input1.isStatic).toEqual(false); - expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; - const input2 = members.find(member => member.name === 'input2')!; - expect(input2.kind).toEqual(ClassMemberKind.Property); - expect(input2.isStatic).toEqual(false); - expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - }); + expect(decorators.length).toEqual(1); + expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); + }); - it('should find non decorated properties on a class', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); + it('should find decorated members on a class at the top level', () => { + loadTestFiles([TOPLEVEL_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', + isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); - const instanceProperty = members.find(member => member.name === 'instanceProperty')!; - expect(instanceProperty.kind).toEqual(ClassMemberKind.Property); - expect(instanceProperty.isStatic).toEqual(false); - expect(ts.isBinaryExpression(instanceProperty.implementation!)).toEqual(true); - expect(instanceProperty.value!.getText()).toEqual(`'instance'`); - }); + const input1 = members.find(member => member.name === 'input1')!; + expect(input1.kind).toEqual(ClassMemberKind.Property); + expect(input1.isStatic).toEqual(false); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - it('should find static methods on a class', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); + const input2 = members.find(member => member.name === 'input2')!; + expect(input2.kind).toEqual(ClassMemberKind.Property); + expect(input2.isStatic).toEqual(false); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + }); - const staticMethod = members.find(member => member.name === 'staticMethod')!; - expect(staticMethod.kind).toEqual(ClassMemberKind.Method); - expect(staticMethod.isStatic).toEqual(true); - expect(ts.isFunctionExpression(staticMethod.implementation!)).toEqual(true); - }); + describe('(returned decorators `args`)', () => { + it('should be an empty array if decorator has no `args` property', () => { + loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', + isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Directive'); + expect(decorators[0].args).toEqual([]); + }); - it('should find static properties on a class', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); + it('should be an empty array if decorator\'s `args` has no property assignment', () => { + loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', + isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Directive'); + expect(decorators[0].args).toEqual([]); + }); - const staticProperty = members.find(member => member.name === 'staticProperty')!; - expect(staticProperty.kind).toEqual(ClassMemberKind.Property); - expect(staticProperty.isStatic).toEqual(true); - expect(ts.isPropertyAccessExpression(staticProperty.implementation!)).toEqual(true); - expect(staticProperty.value!.getText()).toEqual(`'static'`); + it('should be an empty array if `args` property value is not an array literal', () => { + loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', + isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Directive'); + expect(decorators[0].args).toEqual([]); + }); + }); }); - it('should throw if the symbol is not a class', () => { - loadTestFiles([FOO_FUNCTION_FILE]); - const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const functionNode = getDeclaration( - bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - expect(() => { - host.getMembersOfClass(functionNode); - }).toThrowError(`Attempted to get members of a non-class: "function foo() {}"`); - }); + describe('getMembersOfClass()', () => { + it('should find decorated members on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); - it('should return an empty array if there are no prop decorators', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); + const input1 = members.find(member => member.name === 'input1')!; + expect(input1.kind).toEqual(ClassMemberKind.Property); + expect(input1.isStatic).toEqual(false); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - expect(members).toEqual([]); - }); + const input2 = members.find(member => member.name === 'input2')!; + expect(input2.kind).toEqual(ClassMemberKind.Property); + expect(input2.isStatic).toEqual(false); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + }); - it('should not process decorated properties in `propDecorators` if it is not an object literal', - () => { - loadTestFiles([INVALID_PROP_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', - isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); - - expect(members.map(member => member.name)).not.toContain('prop'); - }); - - it('should ignore prop decorator elements that are not object literals', () => { - loadTestFiles([INVALID_PROP_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp', - isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop')!; - const decorators = prop.decorators!; + it('should find non decorated properties on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); - }); + const instanceProperty = members.find(member => member.name === 'instanceProperty')!; + expect(instanceProperty.kind).toEqual(ClassMemberKind.Property); + expect(instanceProperty.isStatic).toEqual(false); + expect(ts.isBinaryExpression(instanceProperty.implementation!)).toEqual(true); + expect(instanceProperty.value!.getText()).toEqual(`'instance'`); + }); - it('should ignore prop decorator elements that have no `type` property', () => { - loadTestFiles([INVALID_PROP_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', - isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop')!; - const decorators = prop.decorators!; + it('should find static methods on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); - }); - }); + const staticMethod = members.find(member => member.name === 'staticMethod')!; + expect(staticMethod.kind).toEqual(ClassMemberKind.Method); + expect(staticMethod.isStatic).toEqual(true); + expect(ts.isFunctionExpression(staticMethod.implementation!)).toEqual(true); + }); - it('should ignore prop decorator elements whose `type` value is not an identifier', () => { - loadTestFiles([INVALID_PROP_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', - isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop')!; - const decorators = prop.decorators!; - - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); - }); + it('should find static properties on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); - it('should have import information on decorators', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const staticProperty = members.find(member => member.name === 'staticProperty')!; + expect(staticProperty.kind).toEqual(ClassMemberKind.Property); + expect(staticProperty.isStatic).toEqual(true); + expect(ts.isPropertyAccessExpression(staticProperty.implementation!)).toEqual(true); + expect(staticProperty.value!.getText()).toEqual(`'static'`); + }); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; + it('should throw if the symbol is not a class', () => { + loadTestFiles([FOO_FUNCTION_FILE]); + const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const functionNode = getDeclaration( + bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); + expect(() => { + host.getMembersOfClass(functionNode); + }).toThrowError(`Attempted to get members of a non-class: "function foo() {}"`); + }); - expect(decorators.length).toEqual(1); - expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); - }); + it('should return an empty array if there are no prop decorators', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); - describe('(returned prop decorators `args`)', () => { - it('should be an empty array if prop decorator has no `args` property', () => { - loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', - isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop')!; - const decorators = prop.decorators!; + expect(members).toEqual([]); + }); - expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Input'); - expect(decorators[0].args).toEqual([]); + it('should not process decorated properties in `propDecorators` if it is not an object literal', + () => { + loadTestFiles([INVALID_PROP_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', + isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + + expect(members.map(member => member.name)).not.toContain('prop'); + }); + + it('should ignore prop decorator elements that are not object literals', () => { + loadTestFiles([INVALID_PROP_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp', + isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); + }); + + it('should ignore prop decorator elements that have no `type` property', () => { + loadTestFiles([INVALID_PROP_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', + isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); + }); }); - it('should be an empty array if prop decorator\'s `args` has no property assignment', - () => { - loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', - isDesiredDeclaration); - const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop')!; - const decorators = prop.decorators!; - - expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Input'); - expect(decorators[0].args).toEqual([]); - }); - - it('should be an empty array if `args` property value is not an array literal', () => { - loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); + it('should ignore prop decorator elements whose `type` value is not an identifier', () => { + loadTestFiles([INVALID_PROP_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', + bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', isDesiredDeclaration); const members = host.getMembersOfClass(classNode); const prop = members.find(m => m.name === 'prop')!; const decorators = prop.decorators!; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Input'); - expect(decorators[0].args).toEqual([]); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); - }); - - describe('getConstructorParameters', () => { - it('should retain imported name for type value references for decorated constructor parameter types', - () => { - const files = [ - { - name: _('/node_modules/shared-lib/foo.d.ts'), - contents: ` - declare class Foo {} - export {Foo as Bar}; - `, - }, - { - name: _('/node_modules/shared-lib/index.d.ts'), - contents: ` - export {Bar as Baz} from './foo'; - `, - }, - { - name: _('/local.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define('local', ['exports'], factory) : - (factory(global.local)); - }(this, (function (exports) { 'use strict'; - var Internal = (function() { - function Internal() { - } - return Internal; - }()); - exports.External = Internal; - }))); - ` - }, - { - name: _('/main.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('shared-lib'), require('./local')) : - typeof define === 'function' && define.amd ? define('main', ['exports', 'shared-lib', './local'], factory) : - (factory(global.main, global.shared, global.local)); - }(this, (function (exports, shared, local) { 'use strict'; - ${expDecl('SameFile')} = (function() { - function SameFile() { - } - return SameFile; - }()); - ${varExp('SameFile')} - ${expDecl('SomeClass')} = (function() { - function SomeClass(arg1, arg2, arg3) {} - return SomeClass; - }()); - ${ - expDecl( - 'SomeClass', - true)}.ctorParameters = function() { return [{ type: shared.Baz }, { type: local.External }, { type: SameFile }]; }; - ${varExp('SomeClass')} - }))); - `, - }, - ]; - - loadTestFiles(files); - const bundle = makeTestBundleProgram(_('/main.js')); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, _('/main.js'), 'SomeClass', isNamedFunctionDeclaration); - - const parameters = host.getConstructorParameters(classNode)!; - - expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']); - expectTypeValueReferencesForParameters( - parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]); - }); - - it('should find the decorated constructor parameters', () => { + it('should have import information on decorators', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); - const parameters = host.getConstructorParameters(classNode); - - expect(parameters).toBeDefined(); - expect(parameters!.map(parameter => parameter.name)).toEqual([ - '_viewContainer', '_template', 'injected' - ]); - expectTypeValueReferencesForParameters(parameters!, [ - 'ViewContainerRef', - 'TemplateRef', - null, - ]); - }); - - it('should find the decorated constructor parameters at the top level', () => { - loadTestFiles([TOPLEVEL_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isDesiredDeclaration); - const parameters = host.getConstructorParameters(classNode); - - expect(parameters).toBeDefined(); - expect(parameters!.map(parameter => parameter.name)).toEqual([ - '_viewContainer', '_template', 'injected' - ]); - expectTypeValueReferencesForParameters(parameters!, [ - 'ViewContainerRef', - 'TemplateRef', - null, - ]); - }); - - it('should accept `ctorParameters` as an array', () => { - loadTestFiles([CTOR_DECORATORS_ARRAY_FILE]); - const bundle = makeTestBundleProgram(CTOR_DECORATORS_ARRAY_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, CTOR_DECORATORS_ARRAY_FILE.name, 'CtorDecoratedAsArray', - isDesiredDeclaration); - const parameters = host.getConstructorParameters(classNode)!; - - expect(parameters).toBeDefined(); - expect(parameters.map(parameter => parameter.name)).toEqual(['arg1']); - expectTypeValueReferencesForParameters(parameters, ['ParamType']); - }); - - it('should throw if the symbol is not a class', () => { - loadTestFiles([FOO_FUNCTION_FILE]); - const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const functionNode = getDeclaration( - bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - expect(() => { - host.getConstructorParameters(functionNode); - }) - .toThrowError( - 'Attempted to get constructor parameters of a non-class: "function foo() {}"'); - }); - - // In ES5 there is no such thing as a constructor-less class - // it('should return `null` if there is no constructor', () => { }); - - it('should return an array even if there are no decorators', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', - isDesiredDeclaration); - const parameters = host.getConstructorParameters(classNode); - - expect(parameters).toEqual(jasmine.any(Array)); - expect(parameters!.length).toEqual(1); - expect(parameters![0].name).toEqual('foo'); - expect(parameters![0].decorators).toBe(null); - }); - it('should return an empty array if there are no constructor parameters', () => { - loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', - isDesiredDeclaration); - const parameters = host.getConstructorParameters(classNode); - - expect(parameters).toEqual([]); - }); - - // In ES5 there are no arrow functions - // it('should ignore `ctorParameters` if it is an arrow function', () => { }); - - it('should ignore `ctorParameters` if it does not return an array literal', () => { - loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', - isDesiredDeclaration); - const parameters = host.getConstructorParameters(classNode); + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; - expect(parameters!.length).toBe(1); - expect(parameters![0]).toEqual(jasmine.objectContaining({ - name: 'arg1', - decorators: null, - })); + expect(decorators.length).toEqual(1); + expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); }); - describe('(returned parameters `decorators`)', () => { - it('should ignore param decorator elements that are not object literals', () => { - loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + describe('(returned prop decorators `args`)', () => { + it('should be an empty array if prop decorator has no `args` property', () => { + loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', + bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isDesiredDeclaration); - const parameters = host.getConstructorParameters(classNode); + const members = host.getMembersOfClass(classNode); + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; - expect(parameters!.length).toBe(2); - expect(parameters![0]).toEqual(jasmine.objectContaining({ - name: 'arg1', - decorators: null, - })); - expect(parameters![1]).toEqual(jasmine.objectContaining({ - name: 'arg2', - decorators: jasmine.any(Array) as any - })); + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Input'); + expect(decorators[0].args).toEqual([]); }); - it('should ignore param decorator elements that have no `type` property', () => { - loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + it('should be an empty array if prop decorator\'s `args` has no property assignment', + () => { + loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', + isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Input'); + expect(decorators[0].args).toEqual([]); + }); + + it('should be an empty array if `args` property value is not an array literal', () => { + loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', + bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isDesiredDeclaration); - const parameters = host.getConstructorParameters(classNode); - const decorators = parameters![0].decorators!; + const members = host.getMembersOfClass(classNode); + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); + expect(decorators[0].name).toBe('Input'); + expect(decorators[0].args).toEqual([]); }); + }); - it('should ignore param decorator elements whose `type` value is not an identifier', + describe('getConstructorParameters', () => { + it('should retain imported name for type value references for decorated constructor parameter types', () => { - loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + const files = [ + { + name: _('/node_modules/shared-lib/foo.d.ts'), + contents: ` + declare class Foo {} + export {Foo as Bar}; + `, + }, + { + name: _('/node_modules/shared-lib/index.d.ts'), + contents: ` + export {Bar as Baz} from './foo'; + `, + }, + { + name: _('/local.js'), + contents: ` + (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define('local', ['exports'], factory) : + (factory(global.local)); + }(this, (function (exports) { 'use strict'; + var Internal = (function() { + function Internal() { + } + return Internal; + }()); + exports.External = Internal; + }))); + ` + }, + { + name: _('/main.js'), + contents: ` + (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('shared-lib'), require('./local')) : + typeof define === 'function' && define.amd ? define('main', ['exports', 'shared-lib', './local'], factory) : + (factory(global.main, global.shared, global.local)); + }(this, (function (exports, shared, local) { 'use strict'; + ${expDecl('SameFile')} = (function() { + function SameFile() { + } + return SameFile; + }()); + ${varExp('SameFile')} + + ${expDecl('SomeClass')} = (function() { + function SomeClass(arg1, arg2, arg3) {} + return SomeClass; + }()); + ${ + expDecl( + 'SomeClass', + true)}.ctorParameters = function() { return [{ type: shared.Baz }, { type: local.External }, { type: SameFile }]; }; + ${varExp('SomeClass')} + }))); + `, + }, + ]; + + loadTestFiles(files); + const bundle = makeTestBundleProgram(_('/main.js')); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', - isDesiredDeclaration); - const parameters = host.getConstructorParameters(classNode); - const decorators = parameters![0].decorators!; + bundle.program, _('/main.js'), 'SomeClass', isNamedFunctionDeclaration); - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); + const parameters = host.getConstructorParameters(classNode)!; + + expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']); + expectTypeValueReferencesForParameters( + parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]); }); - it('should use `getImportOfIdentifier()` to retrieve import info', () => { + it('should find the decorated constructor parameters', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); - const mockImportInfo: Import = {from: '@angular/core', name: 'Directive'}; - const spy = spyOn(UmdReflectionHost.prototype, 'getImportOfIdentifier') - .and.returnValue(mockImportInfo); - const parameters = host.getConstructorParameters(classNode); - const decorators = parameters![2].decorators!; - expect(decorators.length).toEqual(1); - expect(decorators[0].import).toBe(mockImportInfo); + expect(parameters).toBeDefined(); + expect(parameters!.map(parameter => parameter.name)).toEqual([ + '_viewContainer', '_template', 'injected' + ]); + expectTypeValueReferencesForParameters(parameters!, [ + 'ViewContainerRef', + 'TemplateRef', + null, + ]); }); - }); - describe('(returned parameters `decorators.args`)', () => { - it('should be an empty array if param decorator has no `args` property', () => { - loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); + it('should find the decorated constructor parameters at the top level', () => { + loadTestFiles([TOPLEVEL_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', + bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isDesiredDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters!.length).toBe(1); - const decorators = parameters![0].decorators!; - expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Inject'); - expect(decorators[0].args).toEqual([]); + expect(parameters).toBeDefined(); + expect(parameters!.map(parameter => parameter.name)).toEqual([ + '_viewContainer', '_template', 'injected' + ]); + expectTypeValueReferencesForParameters(parameters!, [ + 'ViewContainerRef', + 'TemplateRef', + null, + ]); }); - it('should be an empty array if param decorator\'s `args` has no property assignment', - () => { - loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', - isDesiredDeclaration); - const parameters = host.getConstructorParameters(classNode); - const decorators = parameters![0].decorators!; - - expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Inject'); - expect(decorators[0].args).toEqual([]); - }); - - it('should be an empty array if `args` property value is not an array literal', () => { - loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); + it('should accept `ctorParameters` as an array', () => { + loadTestFiles([CTOR_DECORATORS_ARRAY_FILE]); + const bundle = makeTestBundleProgram(CTOR_DECORATORS_ARRAY_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', + bundle.program, CTOR_DECORATORS_ARRAY_FILE.name, 'CtorDecoratedAsArray', isDesiredDeclaration); - const parameters = host.getConstructorParameters(classNode); - const decorators = parameters![0].decorators!; + const parameters = host.getConstructorParameters(classNode)!; - expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Inject'); - expect(decorators[0].args).toEqual([]); + expect(parameters).toBeDefined(); + expect(parameters.map(parameter => parameter.name)).toEqual(['arg1']); + expectTypeValueReferencesForParameters(parameters, ['ParamType']); }); - }); - - function getConstructorParameters( - constructor: string, mode: 'inlined'|'inlined_with_suffix'|'imported' = 'imported') { - let fileHeaderWithUmd = ''; - switch (mode) { - case 'imported': - fileHeaderWithUmd = ` - (function (global, factory) { - 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'; - `; - break; - case 'inlined': - fileHeaderWithUmd = ` - (function (global, factory) { - 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'; - - var __spread = (this && this.__spread) || function (...args) { /* ... */ } - var __spreadArray = (this && this.__spreadArray) || function (...args) { /* ... */ } - var __read = (this && this.__read) || function (...args) { /* ... */ } - `; - break; - case 'inlined_with_suffix': - fileHeaderWithUmd = ` - (function (global, factory) { - 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'; + it('should throw if the symbol is not a class', () => { + loadTestFiles([FOO_FUNCTION_FILE]); + const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const functionNode = getDeclaration( + bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); + expect(() => { + host.getConstructorParameters(functionNode); + }) + .toThrowError( + 'Attempted to get constructor parameters of a non-class: "function foo() {}"'); + }); - var __spread$1 = (this && this.__spread$1) || function (...args) { /* ... */ } - var __spreadArray$1 = (this && this.__spreadArray$1) || function (...args) { /* ... */ } - var __read$2 = (this && this.__read$2) || function (...args) { /* ... */ } - `; - break; - } + // In ES5 there is no such thing as a constructor-less class + // it('should return `null` if there is no constructor', () => { }); - const file = { - name: _('/synthesized_constructors.js'), - contents: ` - ${fileHeaderWithUmd} - var TestClass = /** @class */ (function (_super) { - __extends(TestClass, _super); - ${constructor} - return TestClass; - }(null)); - - exports.TestClass = TestClass; - }))); - `, - }; + it('should return an array even if there are no decorators', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = - getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); - return host.getConstructorParameters(classNode); - } + expect(parameters).toEqual(jasmine.any(Array)); + expect(parameters!.length).toEqual(1); + expect(parameters![0].name).toEqual('foo'); + expect(parameters![0].decorators).toBe(null); + }); - describe('TS -> ES5: synthesized constructors', () => { - it('recognizes _this assignment from super call', () => { - const parameters = getConstructorParameters(` - function TestClass() { - var _this = _super !== null && _super.apply(this, arguments) || this; - _this.synthesizedProperty = null; - return _this; - } - `); + it('should return an empty array if there are no constructor parameters', () => { + loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); - expect(parameters).toBeNull(); + expect(parameters).toEqual([]); }); - it('recognizes super call as return statement', () => { - const parameters = getConstructorParameters(` - function TestClass() { - return _super !== null && _super.apply(this, arguments) || this; - } - `); + // In ES5 there are no arrow functions + // it('should ignore `ctorParameters` if it is an arrow function', () => { }); + + it('should ignore `ctorParameters` if it does not return an array literal', () => { + loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); - expect(parameters).toBeNull(); + expect(parameters!.length).toBe(1); + expect(parameters![0]).toEqual(jasmine.objectContaining({ + name: 'arg1', + decorators: null, + })); }); - it('handles the case where a unique name was generated for _super or _this', () => { - const parameters = getConstructorParameters(` - function TestClass() { - var _this_1 = _super_1 !== null && _super_1.apply(this, arguments) || this; - _this_1._this = null; - _this_1._super = null; - return _this_1; - } - `); + describe('(returned parameters `decorators`)', () => { + it('should ignore param decorator elements that are not object literals', () => { + loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + + expect(parameters!.length).toBe(2); + expect(parameters![0]).toEqual(jasmine.objectContaining({ + name: 'arg1', + decorators: null, + })); + expect(parameters![1]).toEqual(jasmine.objectContaining({ + name: 'arg2', + decorators: jasmine.any(Array) as any + })); + }); - expect(parameters).toBeNull(); + it('should ignore param decorator elements that have no `type` property', () => { + loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + const decorators = parameters![0].decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); + }); + + it('should ignore param decorator elements whose `type` value is not an identifier', + () => { + loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + const decorators = parameters![0].decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); + }); + + it('should use `getImportOfIdentifier()` to retrieve import info', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const mockImportInfo: Import = {from: '@angular/core', name: 'Directive'}; + const spy = spyOn(UmdReflectionHost.prototype, 'getImportOfIdentifier') + .and.returnValue(mockImportInfo); + + const parameters = host.getConstructorParameters(classNode); + const decorators = parameters![2].decorators!; + + expect(decorators.length).toEqual(1); + expect(decorators[0].import).toBe(mockImportInfo); + }); }); - it('does not consider constructors with parameters as synthesized', () => { - const parameters = getConstructorParameters(` - function TestClass(arg) { - return _super !== null && _super.apply(this, arguments) || this; - } - `); + describe('(returned parameters `decorators.args`)', () => { + it('should be an empty array if param decorator has no `args` property', () => { + loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + expect(parameters!.length).toBe(1); + const decorators = parameters![0].decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Inject'); + expect(decorators[0].args).toEqual([]); + }); - expect(parameters!.length).toBe(1); + it('should be an empty array if param decorator\'s `args` has no property assignment', + () => { + loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + const decorators = parameters![0].decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Inject'); + expect(decorators[0].args).toEqual([]); + }); + + it('should be an empty array if `args` property value is not an array literal', () => { + loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + const decorators = parameters![0].decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Inject'); + expect(decorators[0].args).toEqual([]); + }); }); - it('does not consider manual super calls as synthesized', () => { - const parameters = getConstructorParameters(` - function TestClass() { - return _super.call(this) || this; + function getConstructorParameters( + constructor: string, mode: 'inlined'|'inlined_with_suffix'|'imported' = 'imported') { + let fileHeaderWithUmd = ''; + + switch (mode) { + case 'imported': + fileHeaderWithUmd = ` + (function (global, factory) { + 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'; + `; + break; + case 'inlined': + fileHeaderWithUmd = ` + (function (global, factory) { + 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'; + + var __spread = (this && this.__spread) || function (...args) { /* ... */ } + var __spreadArray = (this && this.__spreadArray) || function (...args) { /* ... */ } + var __read = (this && this.__read) || function (...args) { /* ... */ } + `; + break; + case 'inlined_with_suffix': + fileHeaderWithUmd = ` + (function (global, factory) { + 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'; + + var __spread$1 = (this && this.__spread$1) || function (...args) { /* ... */ } + var __spreadArray$1 = (this && this.__spreadArray$1) || function (...args) { /* ... */ } + var __read$2 = (this && this.__read$2) || function (...args) { /* ... */ } + `; + break; } - `); - - expect(parameters!.length).toBe(0); - }); - it('does not consider empty constructors as synthesized', () => { - const parameters = getConstructorParameters(`function TestClass() {}`); - expect(parameters!.length).toBe(0); - }); - }); + const file = { + name: _('/synthesized_constructors.js'), + contents: ` + ${fileHeaderWithUmd} + var TestClass = /** @class */ (function (_super) { + __extends(TestClass, _super); + ${constructor} + return TestClass; + }(null)); + + exports.TestClass = TestClass; + }))); + `, + }; - // See: https://github.com/angular/angular/issues/38453. - describe('ES2015 -> ES5: synthesized constructors through TSC downleveling', () => { - it('recognizes delegate super call using inline spread helper', () => { - const parameters = getConstructorParameters( - ` - function TestClass() { - return _super.apply(this, __spread(arguments)) || this; - }`, - 'inlined'); + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = + getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); + return host.getConstructorParameters(classNode); + } - expect(parameters).toBeNull(); - }); + describe('TS -> ES5: synthesized constructors', () => { + it('recognizes _this assignment from super call', () => { + const parameters = getConstructorParameters(` + function TestClass() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.synthesizedProperty = null; + return _this; + } + `); - it('recognizes delegate super call using inline spreadArray helper', () => { - const parameters = getConstructorParameters( - ` - function TestClass() { - return _super.apply(this, __spreadArray([], __read(arguments))) || this; - }`, - 'inlined'); + expect(parameters).toBeNull(); + }); - expect(parameters).toBeNull(); - }); + it('recognizes super call as return statement', () => { + const parameters = getConstructorParameters(` + function TestClass() { + return _super !== null && _super.apply(this, arguments) || this; + } + `); - it('recognizes delegate super call using inline spread helper with suffix', () => { - const parameters = getConstructorParameters( - ` - function TestClass() { - return _super.apply(this, __spread$1(arguments)) || this; - }`, - 'inlined_with_suffix'); + expect(parameters).toBeNull(); + }); - expect(parameters).toBeNull(); - }); + it('handles the case where a unique name was generated for _super or _this', () => { + const parameters = getConstructorParameters(` + function TestClass() { + var _this_1 = _super_1 !== null && _super_1.apply(this, arguments) || this; + _this_1._this = null; + _this_1._super = null; + return _this_1; + } + `); - it('recognizes delegate super call using inline spreadArray helper with suffix', () => { - const parameters = getConstructorParameters( - ` - function TestClass() { - return _super.apply(this, __spreadArray$1([], __read$2(arguments))) || this; - }`, - 'inlined_with_suffix'); + expect(parameters).toBeNull(); + }); - expect(parameters).toBeNull(); - }); + it('does not consider constructors with parameters as synthesized', () => { + const parameters = getConstructorParameters(` + function TestClass(arg) { + return _super !== null && _super.apply(this, arguments) || this; + } + `); - it('recognizes delegate super call using imported spread helper', () => { - const parameters = getConstructorParameters( - ` - function TestClass() { - return _super.apply(this, tslib_1.__spread(arguments)) || this; - }`, - 'imported'); + expect(parameters!.length).toBe(1); + }); - expect(parameters).toBeNull(); - }); + it('does not consider manual super calls as synthesized', () => { + const parameters = getConstructorParameters(` + function TestClass() { + return _super.call(this) || this; + } + `); - it('recognizes delegate super call using imported spreadArray helper', () => { - const parameters = getConstructorParameters( - ` - function TestClass() { - return _super.apply(this, tslib_1.__spreadArray([], tslib.__read(arguments))) || this; - }`, - 'imported'); + expect(parameters!.length).toBe(0); + }); - expect(parameters).toBeNull(); + it('does not consider empty constructors as synthesized', () => { + const parameters = getConstructorParameters(`function TestClass() {}`); + expect(parameters!.length).toBe(0); + }); }); - describe('with class member assignment', () => { + // See: https://github.com/angular/angular/issues/38453. + describe('ES2015 -> ES5: synthesized constructors through TSC downleveling', () => { it('recognizes delegate super call using inline spread helper', () => { const parameters = getConstructorParameters( ` function TestClass() { - var _this = _super.apply(this, __spread(arguments)) || this; - _this.synthesizedProperty = null; - return _this; + return _super.apply(this, __spread(arguments)) || this; }`, 'inlined'); @@ -1976,9 +1806,7 @@ runInEachFileSystem(() => { const parameters = getConstructorParameters( ` function TestClass() { - var _this = _super.apply(this, __spreadArray([], __read(arguments))) || this; - _this.synthesizedProperty = null; - return _this; + return _super.apply(this, __spreadArray([], __read(arguments))) || this; }`, 'inlined'); @@ -1989,9 +1817,7 @@ runInEachFileSystem(() => { const parameters = getConstructorParameters( ` function TestClass() { - var _this = _super.apply(this, __spread$1(arguments)) || this; - _this.synthesizedProperty = null; - return _this; + return _super.apply(this, __spread$1(arguments)) || this; }`, 'inlined_with_suffix'); @@ -2002,9 +1828,7 @@ runInEachFileSystem(() => { const parameters = getConstructorParameters( ` function TestClass() { - var _this = _super.apply(this, __spreadArray$1([], __read$2(arguments))) || this; - _this.synthesizedProperty = null; - return _this; + return _super.apply(this, __spreadArray$1([], __read$2(arguments))) || this; }`, 'inlined_with_suffix'); @@ -2015,9 +1839,7 @@ runInEachFileSystem(() => { const parameters = getConstructorParameters( ` function TestClass() { - var _this = _super.apply(this, tslib_1.__spread(arguments)) || this; - _this.synthesizedProperty = null; - return _this; + return _super.apply(this, tslib_1.__spread(arguments)) || this; }`, 'imported'); @@ -2028,1590 +1850,1681 @@ runInEachFileSystem(() => { const parameters = getConstructorParameters( ` function TestClass() { - var _this = _super.apply(this, tslib_1.__spreadArray([], tslib.__read(arguments))) || this; - _this.synthesizedProperty = null; - return _this; + return _super.apply(this, tslib_1.__spreadArray([], tslib.__read(arguments))) || this; }`, 'imported'); expect(parameters).toBeNull(); }); - }); - it('handles the case where a unique name was generated for _super or _this', () => { - const parameters = getConstructorParameters( - ` - function TestClass() { - var _this_1 = _super_1.apply(this, __spread(arguments)) || this; - _this_1._this = null; - _this_1._super = null; - return _this_1; - }`, - 'inlined'); + describe('with class member assignment', () => { + it('recognizes delegate super call using inline spread helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, __spread(arguments)) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'inlined'); + + expect(parameters).toBeNull(); + }); + + it('recognizes delegate super call using inline spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, __spreadArray([], __read(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'inlined'); + + expect(parameters).toBeNull(); + }); + + it('recognizes delegate super call using inline spread helper with suffix', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, __spread$1(arguments)) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'inlined_with_suffix'); + + expect(parameters).toBeNull(); + }); + + it('recognizes delegate super call using inline spreadArray helper with suffix', + () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, __spreadArray$1([], __read$2(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'inlined_with_suffix'); + + expect(parameters).toBeNull(); + }); + + it('recognizes delegate super call using imported spread helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, tslib_1.__spread(arguments)) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'imported'); + + expect(parameters).toBeNull(); + }); + + it('recognizes delegate super call using imported spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, tslib_1.__spreadArray([], tslib.__read(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'imported'); + + expect(parameters).toBeNull(); + }); + }); - expect(parameters).toBeNull(); - }); + it('handles the case where a unique name was generated for _super or _this', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this_1 = _super_1.apply(this, __spread(arguments)) || this; + _this_1._this = null; + _this_1._super = null; + return _this_1; + }`, + 'inlined'); - it('does not consider constructors with parameters as synthesized', () => { - const parameters = getConstructorParameters( - ` - function TestClass(arg) { - return _super.apply(this, __spread(arguments)) || this; - }`, - 'inlined'); + expect(parameters).toBeNull(); + }); - expect(parameters!.length).toBe(1); + it('does not consider constructors with parameters as synthesized', () => { + const parameters = getConstructorParameters( + ` + function TestClass(arg) { + return _super.apply(this, __spread(arguments)) || this; + }`, + 'inlined'); + + expect(parameters!.length).toBe(1); + }); }); }); - }); - describe('getDefinitionOfFunction()', () => { - it('should return an object describing the function declaration passed as an argument', - () => { - loadTestFiles([FUNCTION_BODY_FILE]); - const bundle = makeTestBundleProgram(FUNCTION_BODY_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - - const fooNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration)!; - const fooDef = host.getDefinitionOfFunction(fooNode)!; - expect(fooDef.node).toBe(fooNode); - expect(fooDef.body!.length).toEqual(1); - expect(fooDef.body![0].getText()).toEqual(`return x;`); - expect(fooDef.parameters.length).toEqual(1); - expect(fooDef.parameters[0].name).toEqual('x'); - expect(fooDef.parameters[0].initializer).toBe(null); - - const barNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration)!; - const barDef = host.getDefinitionOfFunction(barNode)!; - expect(barDef.node).toBe(barNode); - expect(barDef.body!.length).toEqual(1); - expect(ts.isReturnStatement(barDef.body![0])).toBeTruthy(); - expect(barDef.body![0].getText()).toEqual(`return x + y;`); - expect(barDef.parameters.length).toEqual(2); - expect(barDef.parameters[0].name).toEqual('x'); - expect(fooDef.parameters[0].initializer).toBe(null); - expect(barDef.parameters[1].name).toEqual('y'); - expect(barDef.parameters[1].initializer!.getText()).toEqual('42'); - - const bazNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration)!; - const bazDef = host.getDefinitionOfFunction(bazNode)!; - expect(bazDef.node).toBe(bazNode); - expect(bazDef.body!.length).toEqual(3); - expect(bazDef.parameters.length).toEqual(1); - expect(bazDef.parameters[0].name).toEqual('x'); - expect(bazDef.parameters[0].initializer).toBe(null); - - const quxNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration)!; - const quxDef = host.getDefinitionOfFunction(quxNode)!; - expect(quxDef.node).toBe(quxNode); - expect(quxDef.body!.length).toEqual(2); - expect(quxDef.parameters.length).toEqual(1); - expect(quxDef.parameters[0].name).toEqual('x'); - expect(quxDef.parameters[0].initializer).toBe(null); - }); - }); + describe('getDefinitionOfFunction()', () => { + it('should return an object describing the function declaration passed as an argument', + () => { + loadTestFiles([FUNCTION_BODY_FILE]); + const bundle = makeTestBundleProgram(FUNCTION_BODY_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - describe('getImportOfIdentifier', () => { - it('should find the import of an identifier', () => { - loadTestFiles(IMPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const variableNode = - getDeclaration(bundle.program, _('/file_b.js'), 'b', isNamedVariableDeclaration); - const identifier = (variableNode.initializer && - ts.isPropertyAccessExpression(variableNode.initializer) && - ts.isIdentifier(variableNode.initializer.name)) ? - variableNode.initializer.name : - null; - - expect(identifier).not.toBe(null); - const importOfIdent = host.getImportOfIdentifier(identifier!); - expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); + const fooNode = getDeclaration( + bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration)!; + const fooDef = host.getDefinitionOfFunction(fooNode)!; + expect(fooDef.node).toBe(fooNode); + expect(fooDef.body!.length).toEqual(1); + expect(fooDef.body![0].getText()).toEqual(`return x;`); + expect(fooDef.parameters.length).toEqual(1); + expect(fooDef.parameters[0].name).toEqual('x'); + expect(fooDef.parameters[0].initializer).toBe(null); + + const barNode = getDeclaration( + bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration)!; + const barDef = host.getDefinitionOfFunction(barNode)!; + expect(barDef.node).toBe(barNode); + expect(barDef.body!.length).toEqual(1); + expect(ts.isReturnStatement(barDef.body![0])).toBeTruthy(); + expect(barDef.body![0].getText()).toEqual(`return x + y;`); + expect(barDef.parameters.length).toEqual(2); + expect(barDef.parameters[0].name).toEqual('x'); + expect(fooDef.parameters[0].initializer).toBe(null); + expect(barDef.parameters[1].name).toEqual('y'); + expect(barDef.parameters[1].initializer!.getText()).toEqual('42'); + + const bazNode = getDeclaration( + bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration)!; + const bazDef = host.getDefinitionOfFunction(bazNode)!; + expect(bazDef.node).toBe(bazNode); + expect(bazDef.body!.length).toEqual(3); + expect(bazDef.parameters.length).toEqual(1); + expect(bazDef.parameters[0].name).toEqual('x'); + expect(bazDef.parameters[0].initializer).toBe(null); + + const quxNode = getDeclaration( + bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration)!; + const quxDef = host.getDefinitionOfFunction(quxNode)!; + expect(quxDef.node).toBe(quxNode); + expect(quxDef.body!.length).toEqual(2); + expect(quxDef.parameters.length).toEqual(1); + expect(quxDef.parameters[0].name).toEqual('x'); + expect(quxDef.parameters[0].initializer).toBe(null); + }); }); - it('should return null if the identifier was not imported', () => { - loadTestFiles(IMPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const variableNode = - getDeclaration(bundle.program, _('/file_b.js'), 'd', isNamedVariableDeclaration); - const importOfIdent = - host.getImportOfIdentifier(variableNode.initializer as ts.Identifier); + describe('getImportOfIdentifier', () => { + it('should find the import of an identifier', () => { + loadTestFiles(IMPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const variableNode = + getDeclaration(bundle.program, _('/file_b.js'), 'b', isNamedVariableDeclaration); + const identifier = (variableNode.initializer && + ts.isPropertyAccessExpression(variableNode.initializer) && + ts.isIdentifier(variableNode.initializer.name)) ? + variableNode.initializer.name : + null; + + expect(identifier).not.toBe(null); + const importOfIdent = host.getImportOfIdentifier(identifier!); + expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); + }); - expect(importOfIdent).toBeNull(); - }); + it('should return null if the identifier was not imported', () => { + loadTestFiles(IMPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const variableNode = + getDeclaration(bundle.program, _('/file_b.js'), 'd', isNamedVariableDeclaration); + const importOfIdent = + host.getImportOfIdentifier(variableNode.initializer as ts.Identifier); - it('should handle factory functions not wrapped in parentheses', () => { - loadTestFiles(IMPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const variableNode = - getDeclaration(bundle.program, _('/file_c.js'), 'c', isNamedVariableDeclaration); - const identifier = (variableNode.initializer && - ts.isPropertyAccessExpression(variableNode.initializer) && - ts.isIdentifier(variableNode.initializer.name)) ? - variableNode.initializer.name : - null; - - expect(identifier).not.toBe(null); - const importOfIdent = host.getImportOfIdentifier(identifier!); - expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); + expect(importOfIdent).toBeNull(); + }); + + it('should handle factory functions not wrapped in parentheses', () => { + loadTestFiles(IMPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const variableNode = + getDeclaration(bundle.program, _('/file_c.js'), 'c', isNamedVariableDeclaration); + const identifier = (variableNode.initializer && + ts.isPropertyAccessExpression(variableNode.initializer) && + ts.isIdentifier(variableNode.initializer.name)) ? + variableNode.initializer.name : + null; + + expect(identifier).not.toBe(null); + const importOfIdent = host.getImportOfIdentifier(identifier!); + expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); + }); }); - }); - describe('getDeclarationOfIdentifier', () => { - // Helpers - const createTestForTsHelper = - (host: NgccReflectionHost, factoryFn: ts.FunctionExpression, - getHelperDeclaration: (factoryFn: ts.FunctionExpression, name: string) => - ts.Declaration) => - (varName: string, helperName: string, knownAs: KnownDeclaration, - viaModule: string|null = null) => { - const node = getVariableDeclaration(factoryFn, varName); - const helperIdentifier = getIdentifierFromCallExpression(node); - const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + describe('getDeclarationOfIdentifier', () => { + // Helpers + const createTestForTsHelper = + (host: NgccReflectionHost, factoryFn: ts.FunctionExpression, + getHelperDeclaration: (factoryFn: ts.FunctionExpression, name: string) => + ts.Declaration) => + (varName: string, helperName: string, knownAs: KnownDeclaration, + viaModule: string|null = null) => { + const node = getVariableDeclaration(factoryFn, varName); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + + expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Concrete, + known: knownAs, + node: getHelperDeclaration(factoryFn, helperName), + viaModule, + identity: null, + }); + }; + + const getFunctionDeclaration = (factoryFn: ts.FunctionExpression, name: string) => + factoryFn.body.statements.filter(ts.isFunctionDeclaration) + .find(decl => (decl.name !== undefined) && (decl.name.text === name))!; + + const getIdentifierFromCallExpression = (decl: ts.VariableDeclaration) => { + if (decl.initializer !== undefined && ts.isCallExpression(decl.initializer)) { + const expr = decl.initializer.expression; + if (ts.isIdentifier(expr)) return expr; + if (ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.name)) + return expr.name; + } + throw new Error(`Unable to extract identifier from declaration '${decl.getText()}'.`); + }; - expect(helperDeclaration).toEqual({ - kind: DeclarationKind.Concrete, - known: knownAs, - node: getHelperDeclaration(factoryFn, helperName), - viaModule, - identity: null, - }); - }; + const getVariableDeclaration = (factoryFn: ts.FunctionExpression, name: string) => + factoryFn.body.statements.filter(ts.isVariableStatement) + .map(stmt => stmt.declarationList.declarations[0]) + .find(decl => ts.isIdentifier(decl.name) && (decl.name.text === name))!; - const getFunctionDeclaration = (factoryFn: ts.FunctionExpression, name: string) => - factoryFn.body.statements.filter(ts.isFunctionDeclaration) - .find(decl => (decl.name !== undefined) && (decl.name.text === name))!; + it('should return the declaration of a locally defined identifier', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const ctrDecorators = host.getConstructorParameters(classNode)!; + const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference! as { + kind: TypeValueReferenceKind.LOCAL, + expression: ts.Identifier, + defaultImportStatement: null, + }).expression; + + const expectedDeclarationNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', + isNamedVariableDeclaration); + const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef); + expect(actualDeclaration).not.toBe(null); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe(null); + expect((actualDeclaration as ConcreteDeclaration).identity).toBe(null); + }); - const getIdentifierFromCallExpression = (decl: ts.VariableDeclaration) => { - if (decl.initializer !== undefined && ts.isCallExpression(decl.initializer)) { - const expr = decl.initializer.expression; - if (ts.isIdentifier(expr)) return expr; - if (ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.name)) return expr.name; - } - throw new Error(`Unable to extract identifier from declaration '${decl.getText()}'.`); - }; + it('should return the correct declaration for an outer alias identifier', () => { + const PROGRAM_FILE: TestFile = { + name: _('/test.js'), + contents: ` + (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : + (factory(global.test)); + }(this, (function (exports,module) { 'use strict'; + ${expDecl('AliasedClass')} = AliasedClass_1 = (function () { + function InnerClass() { + } + return InnerClass; + }()); + var AliasedClass_1; + }))); + `, + }; - const getVariableDeclaration = (factoryFn: ts.FunctionExpression, name: string) => - factoryFn.body.statements.filter(ts.isVariableStatement) - .map(stmt => stmt.declarationList.declarations[0]) - .find(decl => ts.isIdentifier(decl.name) && (decl.name.text === name))!; + loadTestFiles([PROGRAM_FILE]); + const bundle = makeTestBundleProgram(PROGRAM_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - it('should return the declaration of a locally defined identifier', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); - const ctrDecorators = host.getConstructorParameters(classNode)!; - const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference! as { - kind: TypeValueReferenceKind.LOCAL, - expression: ts.Identifier, - defaultImportStatement: null, - }).expression; - - const expectedDeclarationNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', - isNamedVariableDeclaration); - const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef); - expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration!.node).toBe(expectedDeclarationNode); - expect(actualDeclaration!.viaModule).toBe(null); - expect((actualDeclaration as ConcreteDeclaration).identity).toBe(null); - }); + const expectedDeclaration = getDeclaration( + bundle.program, PROGRAM_FILE.name, 'AliasedClass', isDesiredDeclaration); + // Grab the `AliasedClass_1` identifier (which is an alias for `AliasedClass`). + const aliasIdentifier = getDeclaration( + bundle.program, PROGRAM_FILE.name, 'AliasedClass_1', isNamedVariableDeclaration); + const actualDeclaration = host.getDeclarationOfIdentifier(aliasIdentifier.name); - it('should return the correct declaration for an outer alias identifier', () => { - const PROGRAM_FILE: TestFile = { - name: _('/test.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : - (factory(global.test)); - }(this, (function (exports,module) { 'use strict'; - ${expDecl('AliasedClass')} = AliasedClass_1 = (function () { - function InnerClass() { - } - return InnerClass; - }()); - var AliasedClass_1; - }))); - `, - }; + expect(aliasIdentifier.getText()).toBe('AliasedClass_1'); + expect(actualDeclaration).not.toBe(null); + expect(actualDeclaration!.node).toBe(expectedDeclaration); + }); - loadTestFiles([PROGRAM_FILE]); - const bundle = makeTestBundleProgram(PROGRAM_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + it('should return the source-file of an import namespace', () => { + loadFakeCore(getFileSystem()); + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; + const identifierOfDirective = + (((classDecorators[0].node as ts.ObjectLiteralExpression).properties[0] as + ts.PropertyAssignment) + .initializer as ts.PropertyAccessExpression) + .expression as ts.Identifier; + + const expectedDeclarationNode = + getSourceFileOrError(bundle.program, _('/node_modules/@angular/core/index.d.ts')); + const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective); + expect(actualDeclaration).not.toBe(null); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe('@angular/core'); + }); - const expectedDeclaration = getDeclaration( - bundle.program, PROGRAM_FILE.name, 'AliasedClass', isDesiredDeclaration); - // Grab the `AliasedClass_1` identifier (which is an alias for `AliasedClass`). - const aliasIdentifier = getDeclaration( - bundle.program, PROGRAM_FILE.name, 'AliasedClass_1', isNamedVariableDeclaration); - const actualDeclaration = host.getDeclarationOfIdentifier(aliasIdentifier.name); + it('should return the source file as the declaration of a standalone "exports" identifier', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const xVar = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'x', isNamedVariableDeclaration); + const exportsIdentifier = xVar.initializer; + if (exportsIdentifier === undefined || !ts.isIdentifier(exportsIdentifier)) { + throw new Error('Bad test - unable to find `var x = exports;` statement'); + } + const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportsDeclaration).not.toBeNull(); + expect(exportsDeclaration!.node).toBe(bundle.file); + }); - expect(aliasIdentifier.getText()).toBe('AliasedClass_1'); - expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration!.node).toBe(expectedDeclaration); - }); + it('should return the source file as the declaration of "exports" in the LHS of a property access', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const exportsStatement = walkForNode(bundle.file, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportsDeclaration).not.toBeNull(); + expect(exportsDeclaration!.node).toBe(bundle.file); + }); - it('should return the source-file of an import namespace', () => { - loadFakeCore(getFileSystem()); - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); - const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; - const identifierOfDirective = - (((classDecorators[0].node as ts.ObjectLiteralExpression).properties[0] as - ts.PropertyAssignment) - .initializer as ts.PropertyAccessExpression) - .expression as ts.Identifier; - - const expectedDeclarationNode = - getSourceFileOrError(bundle.program, _('/node_modules/@angular/core/index.d.ts')); - const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective); - expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration!.node).toBe(expectedDeclarationNode); - expect(actualDeclaration!.viaModule).toBe('@angular/core'); - }); + it('should return the source file as the declaration of "exports" in the LHS of a property access inside a local function', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const simpleFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'simpleFn', + ts.isFunctionDeclaration); + const exportsStatement = walkForNode(simpleFn, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportDeclaration).not.toBeNull(); + expect(exportDeclaration!.node).toBe(bundle.file); + }); - it('should return the source file as the declaration of a standalone "exports" identifier', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const xVar = getDeclaration( - bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'x', isNamedVariableDeclaration); - const exportsIdentifier = xVar.initializer; - if (exportsIdentifier === undefined || !ts.isIdentifier(exportsIdentifier)) { - throw new Error('Bad test - unable to find `var x = exports;` statement'); - } - const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportsDeclaration).not.toBeNull(); - expect(exportsDeclaration!.node).toBe(bundle.file); - }); - - it('should return the source file as the declaration of "exports" in the LHS of a property access', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const exportsStatement = walkForNode(bundle.file, isExportsStatement); - if (exportsStatement === undefined) { - throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); - } - const exportsIdentifier = exportsStatement.expression.left.expression; - const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportsDeclaration).not.toBeNull(); - expect(exportsDeclaration!.node).toBe(bundle.file); - }); - - it('should return the source file as the declaration of "exports" in the LHS of a property access inside a local function', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const simpleFn = getDeclaration( - bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'simpleFn', - ts.isFunctionDeclaration); - const exportsStatement = walkForNode(simpleFn, isExportsStatement); - if (exportsStatement === undefined) { - throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); - } - const exportsIdentifier = exportsStatement.expression.left.expression; - const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportDeclaration).not.toBeNull(); - expect(exportDeclaration!.node).toBe(bundle.file); - }); - - it('should return the variable declaration if a standalone "exports" is declared locally', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const localVarFn = getDeclaration( - bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'localVar', - ts.isFunctionDeclaration); - const exportsVar = walkForNode(localVarFn, isNamedVariableDeclaration); - if (exportsVar === undefined) { - throw new Error('Bad test - unable to find `var exports = {}` statement'); - } - const exportsIdentifier = exportsVar.name; - if (exportsIdentifier === undefined || !ts.isIdentifier(exportsIdentifier)) { - throw new Error('Bad test - unable to find `var exports = {};` statement'); - } - const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportsDeclaration).not.toBeNull(); - expect(exportsDeclaration!.node).toBe(exportsVar); - }); - - it('should return the variable declaration of "exports" in the LHS of a property access, if it is declared locally', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const localVarFn = getDeclaration( - bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'localVar', - ts.isFunctionDeclaration); - const exportsVar = walkForNode(localVarFn, isNamedVariableDeclaration); - const exportsStatement = walkForNode(localVarFn, isExportsStatement); - if (exportsStatement === undefined) { - throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); - } - const exportsIdentifier = exportsStatement.expression.left.expression; - const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportDeclaration).not.toBeNull(); - expect(exportDeclaration?.node).toBe(exportsVar); - }); - - it('should return the variable declaration of "exports" in the LHS of a property access, if it is a local function parameter', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const exportsArgFn = getDeclaration( - bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'exportsArg', - ts.isFunctionDeclaration); - const exportsVar = exportsArgFn.parameters[0]; - const exportsStatement = walkForNode(exportsArgFn, isExportsStatement); - if (exportsStatement === undefined) { - throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); - } - const exportsIdentifier = exportsStatement.expression.left.expression; - const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportDeclaration).not.toBeNull(); - expect(exportDeclaration?.node).toBe(exportsVar); - }); - - it('should recognize TypeScript helpers (as function declarations)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` - (function (global, factory) { - 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'; - function __assign(t, ...sources) { /* ... */ } - function __spread(...args) { /* ... */ } - function __spreadArrays(...args) { /* ... */ } - function __spreadArray(to, from) { /* ... */ } - function __read(o) { /* ... */ } - - var a = __assign({foo: 'bar'}, {baz: 'qux'}); - var b = __spread(['foo', 'bar'], ['baz', 'qux']); - var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); - var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); - var e = __read(['foo', 'bar']); - }))); - `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; + it('should return the variable declaration if a standalone "exports" is declared locally', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const localVarFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'localVar', + ts.isFunctionDeclaration); + const exportsVar = walkForNode(localVarFn, isNamedVariableDeclaration); + if (exportsVar === undefined) { + throw new Error('Bad test - unable to find `var exports = {}` statement'); + } + const exportsIdentifier = exportsVar.name; + if (exportsIdentifier === undefined || !ts.isIdentifier(exportsIdentifier)) { + throw new Error('Bad test - unable to find `var exports = {};` statement'); + } + const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportsDeclaration).not.toBeNull(); + expect(exportsDeclaration!.node).toBe(exportsVar); + }); - const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration); + it('should return the variable declaration of "exports" in the LHS of a property access, if it is declared locally', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const localVarFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'localVar', + ts.isFunctionDeclaration); + const exportsVar = walkForNode(localVarFn, isNamedVariableDeclaration); + const exportsStatement = walkForNode(localVarFn, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportDeclaration).not.toBeNull(); + expect(exportDeclaration?.node).toBe(exportsVar); + }); - testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); - testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); - testForHelper('e', '__read', KnownDeclaration.TsHelperRead); - }); + it('should return the variable declaration of "exports" in the LHS of a property access, if it is a local function parameter', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const exportsArgFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'exportsArg', + ts.isFunctionDeclaration); + const exportsVar = exportsArgFn.parameters[0]; + const exportsStatement = walkForNode(exportsArgFn, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportDeclaration).not.toBeNull(); + expect(exportDeclaration?.node).toBe(exportsVar); + }); - it('should recognize suffixed TypeScript helpers (as function declarations)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` - (function (global, factory) { - 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'; - function __assign$1(t, ...sources) { /* ... */ } - function __spread$2(...args) { /* ... */ } - function __spreadArrays$3(...args) { /* ... */ } - function __spreadArray$3(to, from) { /* ... */ } - function __read$3(o) { /* ... */ } - - var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); - var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); - var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); - var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); - var e = __read$3(['foo', 'bar']); - }))); - `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; + it('should recognize TypeScript helpers (as function declarations)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` + (function (global, factory) { + 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'; + function __assign(t, ...sources) { /* ... */ } + function __spread(...args) { /* ... */ } + function __spreadArrays(...args) { /* ... */ } + function __spreadArray(to, from) { /* ... */ } + function __read(o) { /* ... */ } + + var a = __assign({foo: 'bar'}, {baz: 'qux'}); + var b = __spread(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); + }))); + `, + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; - const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration); + const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration); - testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); - testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); - testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); - }); + testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); + }); - it('should recognize TypeScript helpers (as variable declarations)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` - (function (global, factory) { - 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'; - var __assign = (this && this.__assign) || function (t, ...sources) { /* ... */ } - var __spread = (this && this.__spread) || function (...args) { /* ... */ } - var __spreadArrays = (this && this.__spreadArrays) || function (...args) { /* ... */ } - var __spreadArray = (this && this.__spreadArray) || function (to, from) { /* ... */ } - var __read = (this && this.__read) || function (o) { /* ... */ } - - var a = __assign({foo: 'bar'}, {baz: 'qux'}); - var b = __spread(['foo', 'bar'], ['baz', 'qux']); - var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); - var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); - var e = __read(['foo', 'bar']); - }))); - `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; + it('should recognize suffixed TypeScript helpers (as function declarations)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` + (function (global, factory) { + 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'; + function __assign$1(t, ...sources) { /* ... */ } + function __spread$2(...args) { /* ... */ } + function __spreadArrays$3(...args) { /* ... */ } + function __spreadArray$3(to, from) { /* ... */ } + function __read$3(o) { /* ... */ } + + var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); + var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); + }))); + `, + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; - const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration); + const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration); - testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); - testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); - testForHelper('e', '__read', KnownDeclaration.TsHelperRead); - }); + testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); + }); - it('should recognize suffixed TypeScript helpers (as variable declarations)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` - (function (global, factory) { - 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'; - var __assign$1 = (this && this.__assign$1) || function (t, ...sources) { /* ... */ } - var __spread$2 = (this && this.__spread$2) || function (...args) { /* ... */ } - var __spreadArrays$3 = (this && this.__spreadArrays$3) || function (...args) { /* ... */ } - var __spreadArray$3 = (this && this.__spreadArray$3) || function (to, from) { /* ... */ } - var __read$3 = (this && this.__read$3) || function (o) { /* ... */ } - - var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); - var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); - var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); - var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); - var e = __read$3(['foo', 'bar']); - }))); - `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; + it('should recognize TypeScript helpers (as variable declarations)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` + (function (global, factory) { + 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'; + var __assign = (this && this.__assign) || function (t, ...sources) { /* ... */ } + var __spread = (this && this.__spread) || function (...args) { /* ... */ } + var __spreadArrays = (this && this.__spreadArrays) || function (...args) { /* ... */ } + var __spreadArray = (this && this.__spreadArray) || function (to, from) { /* ... */ } + var __read = (this && this.__read) || function (o) { /* ... */ } + + var a = __assign({foo: 'bar'}, {baz: 'qux'}); + var b = __spread(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); + }))); + `, + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; - const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration); + const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration); - testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); - testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); - testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); - }); + testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); + }); - it('should recognize imported TypeScript helpers', () => { - const files: TestFile[] = [ - { + it('should recognize suffixed TypeScript helpers (as variable declarations)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` + (function (global, factory) { + 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'; + var __assign$1 = (this && this.__assign$1) || function (t, ...sources) { /* ... */ } + var __spread$2 = (this && this.__spread$2) || function (...args) { /* ... */ } + var __spreadArrays$3 = (this && this.__spreadArrays$3) || function (...args) { /* ... */ } + var __spreadArray$3 = (this && this.__spreadArray$3) || function (to, from) { /* ... */ } + var __read$3 = (this && this.__read$3) || function (o) { /* ... */ } + + var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); + var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); + }))); + `, + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; + + const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration); + + testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); + }); + + it('should recognize imported TypeScript helpers', () => { + const files: TestFile[] = [ + { + name: _('/test.js'), + contents: ` + (function (global, factory) { + 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_1) { 'use strict'; + var a = tslib_1.__assign({foo: 'bar'}, {baz: 'qux'}); + var b = tslib_1.__spread(['foo', 'bar'], ['baz', 'qux']); + var c = tslib_1.__spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = tslib_1.__spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = tslib_1.__read(['foo', 'bar']); + }))); + `, + }, + { + name: _('/node_modules/tslib/index.d.ts'), + contents: ` + export declare function __assign(t: any, ...sources: any[]): any; + export declare function __spread(...args: any[][]): any[]; + export declare function __spreadArrays(...args: any[][]): any[]; + export declare function __spreadArray(to: any[], from: any[]): any[]; + export declare function __read(o: any, n?: number): any[]; + `, + }, + ]; + loadTestFiles(files); + + const [testFile, tslibFile] = files; + const bundle = makeTestBundleProgram(testFile.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, testFile.name).statements[0])!; + const testForHelper = createTestForTsHelper( + host, factoryFn, + (_fn, helperName) => getDeclaration( + bundle.program, tslibFile.name, helperName, ts.isFunctionDeclaration)); + + testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib'); + testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib'); + testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib'); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray, 'tslib'); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead, 'tslib'); + }); + + it('should recognize undeclared, unimported TypeScript helpers (by name)', () => { + const file: TestFile = { name: _('/test.js'), contents: ` (function (global, factory) { - 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_1) { 'use strict'; - var a = tslib_1.__assign({foo: 'bar'}, {baz: 'qux'}); - var b = tslib_1.__spread(['foo', 'bar'], ['baz', 'qux']); - var c = tslib_1.__spreadArrays(['foo', 'bar'], ['baz', 'qux']); - var d = tslib_1.__spreadArray(['foo', 'bar'], ['baz', 'qux']); - var e = tslib_1.__read(['foo', 'bar']); + 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'; + var a = __assign({foo: 'bar'}, {baz: 'qux'}); + var b = __spread(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); + }))); + `, + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; + + const testForHelper = + (varName: string, helperName: string, knownAs: KnownDeclaration) => { + const node = getVariableDeclaration(factoryFn, varName); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + + expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Inline, + known: knownAs, + node: helperIdentifier, + viaModule: null, + }); + }; + + testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); + }); + + it('should recognize suffixed, undeclared, unimported TypeScript helpers (by name)', + () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` + (function (global, factory) { + 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'; + var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); + var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); }))); `, - }, - { - name: _('/node_modules/tslib/index.d.ts'), - contents: ` - export declare function __assign(t: any, ...sources: any[]): any; - export declare function __spread(...args: any[][]): any[]; - export declare function __spreadArrays(...args: any[][]): any[]; - export declare function __spreadArray(to: any[], from: any[]): any[]; - export declare function __read(o: any, n?: number): any[]; - `, - }, - ]; - loadTestFiles(files); - - const [testFile, tslibFile] = files; - const bundle = makeTestBundleProgram(testFile.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, testFile.name).statements[0])!; - const testForHelper = createTestForTsHelper( - host, factoryFn, - (_fn, helperName) => getDeclaration( - bundle.program, tslibFile.name, helperName, ts.isFunctionDeclaration)); - - testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib'); - testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib'); - testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib'); - testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray, 'tslib'); - testForHelper('e', '__read', KnownDeclaration.TsHelperRead, 'tslib'); - }); + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; + + const testForHelper = + (varName: string, helperName: string, knownAs: KnownDeclaration) => { + const node = getVariableDeclaration(factoryFn, varName); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + + expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Inline, + known: knownAs, + node: helperIdentifier, + viaModule: null, + }); + }; + + testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); + }); - it('should recognize undeclared, unimported TypeScript helpers (by name)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` + it('should recognize enum declarations with string values', () => { + const testFile: TestFile = { + name: _('/node_modules/test-package/some/file.js'), + contents: ` (function (global, factory) { - 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'; - var a = __assign({foo: 'bar'}, {baz: 'qux'}); - var b = __spread(['foo', 'bar'], ['baz', 'qux']); - var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); - var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); - var e = __read(['foo', 'bar']); + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : + typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : + (factory(global.some_directive,global.ng.core)); + }(this, (function (exports,core) { 'use strict'; + var Enum; + (function (Enum) { + Enum["ValueA"] = "1"; + Enum["ValueB"] = "2"; + })(exports.Enum || (exports.Enum = {})); + + var value = Enum; }))); - `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; - - const testForHelper = - (varName: string, helperName: string, knownAs: KnownDeclaration) => { - const node = getVariableDeclaration(factoryFn, varName); - const helperIdentifier = getIdentifierFromCallExpression(node); - const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); - - expect(helperDeclaration).toEqual({ - kind: DeclarationKind.Inline, - known: knownAs, - node: helperIdentifier, - viaModule: null, - }); - }; - - testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); - testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); - testForHelper('e', '__read', KnownDeclaration.TsHelperRead); - }); + ` + }; + loadTestFiles([testFile]); + const bundle = makeTestBundleProgram(testFile.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, _('/node_modules/test-package/some/file.js')) + .statements[0])!; + const valueDecl = getVariableDeclaration(factoryFn, 'value'); + const declaration = host.getDeclarationOfIdentifier( + valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; + + const enumMembers = (declaration.identity as DownleveledEnum).enumMembers; + expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); + expect(enumMembers!.length).toBe(2); + expect(enumMembers![0].name.getText()).toBe('"ValueA"'); + expect(enumMembers![0].initializer!.getText()).toBe('"1"'); + expect(enumMembers![1].name.getText()).toBe('"ValueB"'); + expect(enumMembers![1].initializer!.getText()).toBe('"2"'); + }); - it('should recognize suffixed, undeclared, unimported TypeScript helpers (by name)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` + it('should recognize enum declarations with numeric values', () => { + const testFile: TestFile = { + name: _('/node_modules/test-package/some/file.js'), + contents: ` (function (global, factory) { - 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'; - var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); - var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); - var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); - var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); - var e = __read$3(['foo', 'bar']); + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : + typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : + (factory(global.some_directive,global.ng.core)); + }(this, (function (exports,core) { 'use strict'; + var Enum; + (function (Enum) { + Enum[Enum["ValueA"] = "1"] = "ValueA"; + Enum[Enum["ValueB"] = "2"] = "ValueB"; + })(exports.Enum || (exports.Enum = {})); + + var value = Enum; }))); - `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; - - const testForHelper = - (varName: string, helperName: string, knownAs: KnownDeclaration) => { - const node = getVariableDeclaration(factoryFn, varName); - const helperIdentifier = getIdentifierFromCallExpression(node); - const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); - - expect(helperDeclaration).toEqual({ - kind: DeclarationKind.Inline, - known: knownAs, - node: helperIdentifier, - viaModule: null, - }); - }; - - testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); - testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); - testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); - }); + ` + }; + loadTestFiles([testFile]); + const bundle = makeTestBundleProgram(testFile.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, _('/node_modules/test-package/some/file.js')) + .statements[0])!; + const valueDecl = getVariableDeclaration(factoryFn, 'value'); + const declaration = host.getDeclarationOfIdentifier( + valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; + + const enumMembers = (declaration.identity as DownleveledEnum).enumMembers; + expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); + expect(enumMembers!.length).toBe(2); + expect(enumMembers![0].name.getText()).toBe('"ValueA"'); + expect(enumMembers![0].initializer!.getText()).toBe('"1"'); + expect(enumMembers![1].name.getText()).toBe('"ValueB"'); + expect(enumMembers![1].initializer!.getText()).toBe('"2"'); + }); - it('should recognize enum declarations with string values', () => { - const testFile: TestFile = { - name: _('/node_modules/test-package/some/file.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : - (factory(global.some_directive,global.ng.core)); - }(this, (function (exports,core) { 'use strict'; - var Enum; - (function (Enum) { - Enum["ValueA"] = "1"; - Enum["ValueB"] = "2"; - })(exports.Enum || (exports.Enum = {})); - - var value = Enum; - }))); - ` - }; - loadTestFiles([testFile]); - const bundle = makeTestBundleProgram(testFile.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, _('/node_modules/test-package/some/file.js')) - .statements[0])!; - const valueDecl = getVariableDeclaration(factoryFn, 'value'); - const declaration = host.getDeclarationOfIdentifier( - valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; - - const enumMembers = (declaration.identity as DownleveledEnum).enumMembers; - expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); - expect(enumMembers!.length).toBe(2); - expect(enumMembers![0].name.getText()).toBe('"ValueA"'); - expect(enumMembers![0].initializer!.getText()).toBe('"1"'); - expect(enumMembers![1].name.getText()).toBe('"ValueB"'); - expect(enumMembers![1].initializer!.getText()).toBe('"2"'); - }); + it('should not consider IIFEs that do no assign members to the parameter as an enum declaration', + () => { + const testFile: TestFile = { + name: _('/node_modules/test-package/some/file.js'), + contents: ` + (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : + typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : + (factory(global.some_directive,global.ng.core)); + }(this, (function (exports,core) { 'use strict'; + var Enum; + (function (E) { + Enum["ValueA"] = "1"]; + Enum["ValueB"] = "2"]; + })(exports.Enum || (exports.Enum = {})); + + var value = Enum; + }))); + ` + }; + loadTestFiles([testFile]); + const bundle = makeTestBundleProgram(testFile.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const valueDecl = getDeclaration( + bundle.program, _('/node_modules/test-package/some/file.js'), 'value', + isNamedVariableDeclaration); + const declaration = + host.getDeclarationOfIdentifier(valueDecl.initializer as ts.Identifier) as + ConcreteDeclaration; + + expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); + expect(declaration.identity).toBe(null); + }); - it('should recognize enum declarations with numeric values', () => { - const testFile: TestFile = { - name: _('/node_modules/test-package/some/file.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : - (factory(global.some_directive,global.ng.core)); - }(this, (function (exports,core) { 'use strict'; - var Enum; - (function (Enum) { - Enum[Enum["ValueA"] = "1"] = "ValueA"; - Enum[Enum["ValueB"] = "2"] = "ValueB"; - })(exports.Enum || (exports.Enum = {})); - - var value = Enum; - }))); - ` - }; - loadTestFiles([testFile]); - const bundle = makeTestBundleProgram(testFile.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, _('/node_modules/test-package/some/file.js')) - .statements[0])!; - const valueDecl = getVariableDeclaration(factoryFn, 'value'); - const declaration = host.getDeclarationOfIdentifier( - valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; - - const enumMembers = (declaration.identity as DownleveledEnum).enumMembers; - expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); - expect(enumMembers!.length).toBe(2); - expect(enumMembers![0].name.getText()).toBe('"ValueA"'); - expect(enumMembers![0].initializer!.getText()).toBe('"1"'); - expect(enumMembers![1].name.getText()).toBe('"ValueB"'); - expect(enumMembers![1].initializer!.getText()).toBe('"2"'); + it('should not consider IIFEs without call argument as an enum declaration', () => { + const testFile: TestFile = { + name: _('/node_modules/test-package/some/file.js'), + contents: ` + (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : + typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : + (factory(global.some_directive,global.ng.core)); + }(this, (function (exports,core) { 'use strict'; + var Enum; + (function (Enum) { + Enum["ValueA"] = "1"]; + Enum["ValueB"] = "2"]; + })(); + + var value = Enum; + }))); + ` + }; + loadTestFiles([testFile]); + const bundle = makeTestBundleProgram(testFile.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const valueDecl = getDeclaration( + bundle.program, _('/node_modules/test-package/some/file.js'), 'value', + isNamedVariableDeclaration); + const declaration = host.getDeclarationOfIdentifier( + valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; + + expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); + expect(declaration.identity).toBe(null); + }); }); - it('should not consider IIFEs that do no assign members to the parameter as an enum declaration', - () => { - const testFile: TestFile = { - name: _('/node_modules/test-package/some/file.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : - (factory(global.some_directive,global.ng.core)); - }(this, (function (exports,core) { 'use strict'; - var Enum; - (function (E) { - Enum["ValueA"] = "1"]; - Enum["ValueB"] = "2"]; - })(exports.Enum || (exports.Enum = {})); - - var value = Enum; - }))); - ` - }; - loadTestFiles([testFile]); - const bundle = makeTestBundleProgram(testFile.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const valueDecl = getDeclaration( - bundle.program, _('/node_modules/test-package/some/file.js'), 'value', - isNamedVariableDeclaration); - const declaration = host.getDeclarationOfIdentifier( - valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; - - expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); - expect(declaration.identity).toBe(null); - }); - - it('should not consider IIFEs without call argument as an enum declaration', () => { - const testFile: TestFile = { - name: _('/node_modules/test-package/some/file.js'), - contents: ` - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : - typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : - (factory(global.some_directive,global.ng.core)); - }(this, (function (exports,core) { 'use strict'; - var Enum; - (function (Enum) { - Enum["ValueA"] = "1"]; - Enum["ValueB"] = "2"]; - })(); - - var value = Enum; - }))); - ` - }; - loadTestFiles([testFile]); - const bundle = makeTestBundleProgram(testFile.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const valueDecl = getDeclaration( - bundle.program, _('/node_modules/test-package/some/file.js'), 'value', - isNamedVariableDeclaration); - const declaration = host.getDeclarationOfIdentifier( - valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; - - expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); - expect(declaration.identity).toBe(null); - }); - }); + describe('getExportsOfModule()', () => { + it('should return a map of all the exports from a given module', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = getSourceFileOrError(bundle.program, _('/b_module.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>`, '@angular/core'], + ['a', `a = 'a'`, null], + ['b', `b = a_module.a`, null], + ['c', `a = 'a'`, null], + ['d', `b = a_module.a`, null], + ['e', `e = 'e'`, null], + ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, '@angular/core'], + [ + 'SomeClass', + 'SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())', + null + ], + ]); + }); - describe('getExportsOfModule()', () => { - it('should return a map of all the exports from a given module', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = getSourceFileOrError(bundle.program, _('/b_module.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>`, '@angular/core'], - ['a', `a = 'a'`, null], - ['b', `b = a_module.a`, null], - ['c', `a = 'a'`, null], - ['d', `b = a_module.a`, null], - ['e', `e = 'e'`, null], - ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, '@angular/core'], - [ - 'SomeClass', - 'SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())', - null - ], - ]); - }); + it('should return the correct declaration for a decorated class', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/decorated_class.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = getSourceFileOrError(bundle.program, _('/decorated_class.js')); + const exportDeclarations = host.getExportsOfModule(file); + expect(exportDeclarations).not.toBe(null); + expect(exportDeclarations!.size).toEqual(1); + const classDecl = exportDeclarations!.get('DecoratedClass') as InlineDeclaration; + expect(classDecl).toBeDefined(); + expect(classDecl.kind).toEqual(DeclarationKind.Inline); + expect(classDecl.known).toBe(null); + expect(classDecl.viaModule).toBe(null); + expect(classDecl.node.getText()).toEqual('exports.DecoratedClass'); + expect(classDecl.implementation!.getText()).toContain('function DecoratedClass() {'); + }); - it('should return the correct declaration for a decorated class', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/decorated_class.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = getSourceFileOrError(bundle.program, _('/decorated_class.js')); - const exportDeclarations = host.getExportsOfModule(file); - expect(exportDeclarations).not.toBe(null); - expect(exportDeclarations!.size).toEqual(1); - const classDecl = exportDeclarations!.get('DecoratedClass') as InlineDeclaration; - expect(classDecl).toBeDefined(); - expect(classDecl.kind).toEqual(DeclarationKind.Inline); - expect(classDecl.known).toBe(null); - expect(classDecl.viaModule).toBe(null); - expect(classDecl.node.getText()).toEqual('exports.DecoratedClass'); - expect(classDecl.implementation!.getText()).toContain('function DecoratedClass() {'); - }); + it('should handle wildcard re-exports of other modules (with emitted helpers)', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports.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 handle wildcard re-exports of other modules (with emitted helpers)', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports.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 handle wildcard re-exports of other modules (with imported helpers)', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = + getSourceFileOrError(bundle.program, _('/wildcard_reexports_imported_helpers.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 handle wildcard re-exports of other modules (with imported helpers)', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = - getSourceFileOrError(bundle.program, _('/wildcard_reexports_imported_helpers.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 handle inline exports', () => { + loadFakeCore(getFileSystem()); + loadTestFiles([INLINE_EXPORT_FILE]); + const bundle = makeTestBundleProgram(INLINE_EXPORT_FILE.name); + const host = createHost(bundle, 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 entries: [string, InlineDeclaration][] = + Array.from(exportDeclarations!.entries()) as any; + expect(entries.map( + ([name, decl]) => + [name, decl.node!.getText(), decl.implementation!.getText(), + decl.viaModule])) + .toEqual([ + ['directives', 'exports.directives', '[foo]', null], + ['Inline', 'exports.Inline', 'function Inline() {}', null], + ]); + }); - it('should handle inline exports', () => { - loadFakeCore(getFileSystem()); - loadTestFiles([INLINE_EXPORT_FILE]); - const bundle = makeTestBundleProgram(INLINE_EXPORT_FILE.name); - const host = createHost(bundle, 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 entries: [string, InlineDeclaration][] = - Array.from(exportDeclarations!.entries()) as any; - expect( - entries.map( - ([name, decl]) => - [name, decl.node!.getText(), decl.implementation!.getText(), decl.viaModule])) - .toEqual([ - ['directives', 'exports.directives', '[foo]', null], - ['Inline', 'exports.Inline', 'function Inline() {}', null], - ]); - }); + it('should recognize declarations of known TypeScript helpers', () => { + const tslib = { + name: _('/tslib.d.ts'), + contents: ` + export declare function __assign(t: any, ...sources: any[]): any; + export declare function __spread(...args: any[][]): any[]; + export declare function __spreadArrays(...args: any[][]): any[]; + export declare function __spreadArray(to: any[], from: any[]): any[]; + export declare function __read(o: any, n?: number): any[]; + export declare function __unknownHelper(...args: any[]): any; + `, + }; + loadTestFiles([tslib]); + const bundle = makeTestBundleProgram(tslib.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const sf = getSourceFileOrError(bundle.program, tslib.name); + const exportDeclarations = host.getExportsOfModule(sf)!; + + expect([...exportDeclarations].map(([exportName, {known}]) => [exportName, known])) + .toEqual([ + ['__assign', KnownDeclaration.TsHelperAssign], + ['__spread', KnownDeclaration.TsHelperSpread], + ['__spreadArrays', KnownDeclaration.TsHelperSpreadArrays], + ['__spreadArray', KnownDeclaration.TsHelperSpreadArray], + ['__read', KnownDeclaration.TsHelperRead], + ['__unknownHelper', null], + ]); + }); - it('should recognize declarations of known TypeScript helpers', () => { - const tslib = { - name: _('/tslib.d.ts'), - contents: ` - export declare function __assign(t: any, ...sources: any[]): any; - export declare function __spread(...args: any[][]): any[]; - export declare function __spreadArrays(...args: any[][]): any[]; - export declare function __spreadArray(to: any[], from: any[]): any[]; - export declare function __read(o: any, n?: number): any[]; - export declare function __unknownHelper(...args: any[]): any; - `, - }; - loadTestFiles([tslib]); - const bundle = makeTestBundleProgram(tslib.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const sf = getSourceFileOrError(bundle.program, tslib.name); - const exportDeclarations = host.getExportsOfModule(sf)!; - - expect([...exportDeclarations].map(([exportName, {known}]) => [exportName, known])) - .toEqual([ - ['__assign', KnownDeclaration.TsHelperAssign], - ['__spread', KnownDeclaration.TsHelperSpread], - ['__spreadArrays', KnownDeclaration.TsHelperSpreadArrays], - ['__spreadArray', KnownDeclaration.TsHelperSpreadArray], - ['__read', KnownDeclaration.TsHelperRead], - ['__unknownHelper', null], - ]); + it('should define property exports from a module', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = getSourceFileOrError(bundle.program, _('/define_property_reexports.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([ + ['newA', `a = 'a'`, null], + ]); + }); }); - it('should define property exports from a module', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = getSourceFileOrError(bundle.program, _('/define_property_reexports.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([ - ['newA', `a = 'a'`, null], - ]); - }); - }); + describe('getClassSymbol()', () => { + it('should return the class symbol for an ES2015 class', () => { + loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', + isNamedClassDeclaration); + const classSymbol = host.getClassSymbol(node); + + expect(classSymbol).toBeDefined(); + expect(classSymbol!.declaration.valueDeclaration).toBe(node); + expect(classSymbol!.implementation.valueDeclaration).toBe(node); + }); - describe('getClassSymbol()', () => { - it('should return the class symbol for an ES2015 class', () => { - loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); - const classSymbol = host.getClassSymbol(node); + it('should handle wildcard re-exports of other modules (with emitted helpers)', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports.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')], + ]); + }); - expect(classSymbol).toBeDefined(); - expect(classSymbol!.declaration.valueDeclaration).toBe(node); - expect(classSymbol!.implementation.valueDeclaration).toBe(node); - }); + it('should handle wildcard re-exports of other modules (with imported helpers)', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = + getSourceFileOrError(bundle.program, _('/wildcard_reexports_imported_helpers.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 handle wildcard re-exports of other modules (with emitted helpers)', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports.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 handle wildcard re-exports of other modules using `require()` calls', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, 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 handle wildcard re-exports of other modules (with imported helpers)', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = - getSourceFileOrError(bundle.program, _('/wildcard_reexports_imported_helpers.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)', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const outerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + const innerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedFunctionDeclaration); + const classSymbol = host.getClassSymbol(outerNode); + + expect(classSymbol).toBeDefined(); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); + }); - it('should handle wildcard re-exports of other modules using `require()` calls', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, 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 (inner function declaration)', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const outerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + const innerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedFunctionDeclaration); + const classSymbol = host.getClassSymbol(innerNode); + + expect(classSymbol).toBeDefined(); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); + }); - it('should return the class symbol for an ES5 class (outer variable declaration)', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const outerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); - const innerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedFunctionDeclaration); - const classSymbol = host.getClassSymbol(outerNode); - - expect(classSymbol).toBeDefined(); - expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); - expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); - }); + it('should return the same class symbol (of the outer declaration) for outer and inner declarations', + () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const outerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + const innerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', + isNamedFunctionDeclaration); + + const innerSymbol = host.getClassSymbol(innerNode)!; + const outerSymbol = host.getClassSymbol(outerNode)!; + expect(innerSymbol.declaration).toBe(outerSymbol.declaration); + expect(innerSymbol.implementation).toBe(outerSymbol.implementation); + }); - it('should return the class symbol for an ES5 class (inner function declaration)', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const outerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); - const innerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedFunctionDeclaration); - const classSymbol = host.getClassSymbol(innerNode); - - expect(classSymbol).toBeDefined(); - expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); - expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); - }); + it('should return the class symbol for an ES5 class whose IIFE is not wrapped in parens', + () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const outerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'NoParensClass', isDesiredDeclaration); + const innerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'NoParensClass', + isNamedFunctionDeclaration); + const classSymbol = host.getClassSymbol(outerNode); + + expect(classSymbol).toBeDefined(); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); + }); - it('should return the same class symbol (of the outer declaration) for outer and inner declarations', - () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const outerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); - const innerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedFunctionDeclaration); - - const innerSymbol = host.getClassSymbol(innerNode)!; - const outerSymbol = host.getClassSymbol(outerNode)!; - expect(innerSymbol.declaration).toBe(outerSymbol.declaration); - expect(innerSymbol.implementation).toBe(outerSymbol.implementation); - }); - - it('should return the class symbol for an ES5 class whose IIFE is not wrapped in parens', - () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const outerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'NoParensClass', isDesiredDeclaration); - const innerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'NoParensClass', - isNamedFunctionDeclaration); - const classSymbol = host.getClassSymbol(outerNode); - - expect(classSymbol).toBeDefined(); - expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); - expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); - }); - - it('should return the class symbol for an ES5 class whose IIFE is not wrapped with inner parens', - () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const outerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'InnerParensClass', isDesiredDeclaration); - const innerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'InnerParensClass', - isNamedFunctionDeclaration); - const classSymbol = host.getClassSymbol(outerNode); - - expect(classSymbol).toBeDefined(); - expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); - expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); - }); - - it('should return undefined if node is not an ES5 class', () => { - loadTestFiles([FOO_FUNCTION_FILE]); - const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - const classSymbol = host.getClassSymbol(node); + it('should return the class symbol for an ES5 class whose IIFE is not wrapped with inner parens', + () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const outerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'InnerParensClass', + isDesiredDeclaration); + const innerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'InnerParensClass', + isNamedFunctionDeclaration); + const classSymbol = host.getClassSymbol(outerNode); + + expect(classSymbol).toBeDefined(); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); + }); - expect(classSymbol).toBeUndefined(); - }); + it('should return undefined if node is not an ES5 class', () => { + loadTestFiles([FOO_FUNCTION_FILE]); + const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); + const classSymbol = host.getClassSymbol(node); - it('should return undefined if variable declaration is not initialized using an IIFE', - () => { - const testFile = { - name: _('/test.js'), - contents: `var MyClass = null;`, - }; - loadTestFiles([testFile]); - const bundle = makeTestBundleProgram(testFile.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, testFile.name, 'MyClass', isNamedVariableDeclaration); - const classSymbol = host.getClassSymbol(node); - - expect(classSymbol).toBeUndefined(); - }); - }); + expect(classSymbol).toBeUndefined(); + }); - describe('isClass()', () => { - it('should return true if a given node is a TS class declaration', () => { - loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); - expect(host.isClass(node)).toBe(true); - }); + it('should return undefined if variable declaration is not initialized using an IIFE', + () => { + const testFile = { + name: _('/test.js'), + contents: `var MyClass = null;`, + }; + loadTestFiles([testFile]); + const bundle = makeTestBundleProgram(testFile.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, testFile.name, 'MyClass', isNamedVariableDeclaration); + const classSymbol = host.getClassSymbol(node); - it('should return true if a given node is the outer variable declaration of a class', - () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); - expect(host.isClass(node)).toBe(true); - }); - - it('should return true if a given node is the inner variable declaration of a class', - () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedFunctionDeclaration); - expect(host.isClass(node)).toBe(true); - }); - - it('should return false if a given node is a function declaration', () => { - loadTestFiles([FOO_FUNCTION_FILE]); - const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - expect(host.isClass(node)).toBe(false); + expect(classSymbol).toBeUndefined(); + }); }); - }); - describe('hasBaseClass()', () => { - function hasBaseClass(source: string) { - const file = { - name: _('/synthesized_constructors.js'), - contents: source, - }; + describe('isClass()', () => { + it('should return true if a given node is a TS class declaration', () => { + loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', + isNamedClassDeclaration); + expect(host.isClass(node)).toBe(true); + }); - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = - getDeclaration(bundle.program, file.name, 'TestClass', isDesiredDeclaration); - return host.hasBaseClass(classNode); - } + it('should return true if a given node is the outer variable declaration of a class', + () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + expect(host.isClass(node)).toBe(true); + }); - it('should consider an IIFE with _super parameter as having a base class', () => { - const result = hasBaseClass(` - ${expDecl('TestClass')} = /** @class */ (function (_super) { - __extends(TestClass, _super); - function TestClass() {} - return TestClass; - }(null));`); - expect(result).toBe(true); - }); + it('should return true if a given node is the inner variable declaration of a class', + () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', + isNamedFunctionDeclaration); + expect(host.isClass(node)).toBe(true); + }); - it('should consider an IIFE with a unique name generated for the _super parameter as having a base class', - () => { - const result = hasBaseClass(` - ${expDecl('TestClass')} = /** @class */ (function (_super_1) { - __extends(TestClass, _super_1); - function TestClass() {} - return TestClass; - }(null));`); - expect(result).toBe(true); - }); - - it('should not consider an IIFE without parameter as having a base class', () => { - const result = hasBaseClass(` - ${expDecl('TestClass')} = /** @class */ (function () { - __extends(TestClass, _super); - function TestClass() {} - return TestClass; - }(null));`); - expect(result).toBe(false); + it('should return false if a given node is a function declaration', () => { + loadTestFiles([FOO_FUNCTION_FILE]); + const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); + expect(host.isClass(node)).toBe(false); + }); }); - }); - describe('getBaseClassExpression()', () => { - function getBaseClassIdentifier(source: string): ts.Identifier|null { - const file = { - name: _('/synthesized_constructors.js'), - contents: source, - }; + describe('hasBaseClass()', () => { + function hasBaseClass(source: string) { + const file = { + name: _('/synthesized_constructors.js'), + contents: source, + }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = - getDeclaration(bundle.program, file.name, 'TestClass', isDesiredDeclaration); - const expression = host.getBaseClassExpression(classNode); - if (expression !== null && !ts.isIdentifier(expression)) { - throw new Error( - 'Expected class to inherit via an identifier but got: ' + expression.getText()); + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = + getDeclaration(bundle.program, file.name, 'TestClass', isDesiredDeclaration); + return host.hasBaseClass(classNode); } - return expression; - } - it('should find the base class of an IIFE with _super parameter', () => { - const identifier = getBaseClassIdentifier(` - ${expDecl('BaseClass')} = /** @class */ (function () { - function BaseClass() {} - return BaseClass; - }()); + it('should consider an IIFE with _super parameter as having a base class', () => { + const result = hasBaseClass(` ${expDecl('TestClass')} = /** @class */ (function (_super) { __extends(TestClass, _super); function TestClass() {} return TestClass; - }(BaseClass));`); - expect(identifier!.text).toBe('BaseClass'); - }); + }(null));`); + expect(result).toBe(true); + }); - it('should find the base class of an IIFE with a unique name generated for the _super parameter', - () => { - const identifier = getBaseClassIdentifier(` - ${expDecl('BaseClass')} = /** @class */ (function () { - function BaseClass() {} - return BaseClass; - }()); + it('should consider an IIFE with a unique name generated for the _super parameter as having a base class', + () => { + const result = hasBaseClass(` ${expDecl('TestClass')} = /** @class */ (function (_super_1) { __extends(TestClass, _super_1); function TestClass() {} return TestClass; - }(BaseClass));`); - expect(identifier!.text).toBe('BaseClass'); - }); - - it('should not find a base class for an IIFE without parameter', () => { - const identifier = getBaseClassIdentifier(` - ${expDecl('BaseClass')} = /** @class */ (function () { - function BaseClass() {} - return BaseClass; - }()); + }(null));`); + expect(result).toBe(true); + }); + + it('should not consider an IIFE without parameter as having a base class', () => { + const result = hasBaseClass(` ${expDecl('TestClass')} = /** @class */ (function () { __extends(TestClass, _super); function TestClass() {} return TestClass; - }(BaseClass));`); - expect(identifier).toBe(null); + }(null));`); + expect(result).toBe(false); + }); }); - it('should find a dynamic base class expression of an IIFE', () => { - const file = { - name: _('/synthesized_constructors.js'), - contents: ` + describe('getBaseClassExpression()', () => { + function getBaseClassIdentifier(source: string): ts.Identifier|null { + const file = { + name: _('/synthesized_constructors.js'), + contents: source, + }; + + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = + getDeclaration(bundle.program, file.name, 'TestClass', isDesiredDeclaration); + const expression = host.getBaseClassExpression(classNode); + if (expression !== null && !ts.isIdentifier(expression)) { + throw new Error( + 'Expected class to inherit via an identifier but got: ' + expression.getText()); + } + return expression; + } + + it('should find the base class of an IIFE with _super parameter', () => { + const identifier = getBaseClassIdentifier(` ${expDecl('BaseClass')} = /** @class */ (function () { function BaseClass() {} return BaseClass; }()); - function foo() { return BaseClass; } ${expDecl('TestClass')} = /** @class */ (function (_super) { __extends(TestClass, _super); function TestClass() {} return TestClass; - }(foo()));`, - }; + }(BaseClass));`); + expect(identifier!.text).toBe('BaseClass'); + }); - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = - getDeclaration(bundle.program, file.name, 'TestClass', isDesiredDeclaration); - const expression = host.getBaseClassExpression(classNode)!; - expect(expression.getText()).toBe('foo()'); + it('should find the base class of an IIFE with a unique name generated for the _super parameter', + () => { + const identifier = getBaseClassIdentifier(` + ${expDecl('BaseClass')} = /** @class */ (function () { + function BaseClass() {} + return BaseClass; + }()); + ${expDecl('TestClass')} = /** @class */ (function (_super_1) { + __extends(TestClass, _super_1); + function TestClass() {} + return TestClass; + }(BaseClass));`); + expect(identifier!.text).toBe('BaseClass'); + }); + + it('should not find a base class for an IIFE without parameter', () => { + const identifier = getBaseClassIdentifier(` + ${expDecl('BaseClass')} = /** @class */ (function () { + function BaseClass() {} + return BaseClass; + }()); + ${expDecl('TestClass')} = /** @class */ (function () { + __extends(TestClass, _super); + function TestClass() {} + return TestClass; + }(BaseClass));`); + expect(identifier).toBe(null); + }); + + it('should find a dynamic base class expression of an IIFE', () => { + const file = { + name: _('/synthesized_constructors.js'), + contents: ` + ${expDecl('BaseClass')} = /** @class */ (function () { + function BaseClass() {} + return BaseClass; + }()); + function foo() { return BaseClass; } + ${expDecl('TestClass')} = /** @class */ (function (_super) { + __extends(TestClass, _super); + function TestClass() {} + return TestClass; + }(foo()));`, + }; + + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = + getDeclaration(bundle.program, file.name, 'TestClass', isDesiredDeclaration); + const expression = host.getBaseClassExpression(classNode)!; + expect(expression.getText()).toBe('foo()'); + }); }); - }); - describe('findClassSymbols()', () => { - it('should return an array of all classes in the given source file', () => { - loadTestFiles(DECORATED_FILES); - const bundle = makeTestBundleProgram(DECORATED_FILES[0].name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); - const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); + describe('findClassSymbols()', () => { + it('should return an array of all classes in the given source file', () => { + loadTestFiles(DECORATED_FILES); + const bundle = makeTestBundleProgram(DECORATED_FILES[0].name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); + const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); - const classSymbolsPrimary = host.findClassSymbols(primaryFile); - expect(classSymbolsPrimary.length).toEqual(2); - expect(classSymbolsPrimary.map(c => c.name)).toEqual(['A', 'B']); + const classSymbolsPrimary = host.findClassSymbols(primaryFile); + expect(classSymbolsPrimary.length).toEqual(2); + expect(classSymbolsPrimary.map(c => c.name)).toEqual(['A', 'B']); - const classSymbolsSecondary = host.findClassSymbols(secondaryFile); - expect(classSymbolsSecondary.length).toEqual(1); - expect(classSymbolsSecondary.map(c => c.name)).toEqual(['D']); + const classSymbolsSecondary = host.findClassSymbols(secondaryFile); + expect(classSymbolsSecondary.length).toEqual(1); + expect(classSymbolsSecondary.map(c => c.name)).toEqual(['D']); + }); }); - }); - describe('getDecoratorsOfSymbol()', () => { - it('should return decorators of class symbol', () => { - loadTestFiles(DECORATED_FILES); - const bundle = makeTestBundleProgram(DECORATED_FILES[0].name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); - const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); - - const classSymbolsPrimary = host.findClassSymbols(primaryFile); - const classDecoratorsPrimary = - classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s)); - expect(classDecoratorsPrimary.length).toEqual(2); - expect(classDecoratorsPrimary[0]!.map(d => d.name)).toEqual(['Directive']); - expect(classDecoratorsPrimary[1]!.map(d => d.name)).toEqual(['Directive']); - - const classSymbolsSecondary = host.findClassSymbols(secondaryFile); - const classDecoratorsSecondary = - classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s)); - expect(classDecoratorsSecondary.length).toEqual(1); - expect(classDecoratorsSecondary[0]!.map(d => d.name)).toEqual(['Directive']); + describe('getDecoratorsOfSymbol()', () => { + it('should return decorators of class symbol', () => { + loadTestFiles(DECORATED_FILES); + const bundle = makeTestBundleProgram(DECORATED_FILES[0].name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); + const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); + + const classSymbolsPrimary = host.findClassSymbols(primaryFile); + const classDecoratorsPrimary = + classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s)); + expect(classDecoratorsPrimary.length).toEqual(2); + expect(classDecoratorsPrimary[0]!.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsPrimary[1]!.map(d => d.name)).toEqual(['Directive']); + + const classSymbolsSecondary = host.findClassSymbols(secondaryFile); + const classDecoratorsSecondary = + classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s)); + expect(classDecoratorsSecondary.length).toEqual(1); + expect(classDecoratorsSecondary[0]!.map(d => d.name)).toEqual(['Directive']); + }); }); - }); - describe('getDtsDeclaration()', () => { - it('should find the dts declaration that has the same relative path to the source file', - () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - const class1 = getDeclaration( - bundle.program, _('/ep/src/class1.js'), 'Class1', isDesiredDeclaration); - - const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); - }); - - it('should find the dts declaration for exported functions', () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/ep/src/func1.js')); - const dts = makeTestBundleProgram(_('/ep/typings/func1.d.ts')); - const mooFn = - getDeclaration(bundle.program, _('/ep/src/func1.js'), 'mooFn', isExportsDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - const dtsDeclaration = host.getDtsDeclaration(mooFn); - expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); - }); + describe('getDtsDeclaration()', () => { + it('should find the dts declaration that has the same relative path to the source file', + () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + const class1 = getDeclaration( + bundle.program, _('/ep/src/class1.js'), 'Class1', isDesiredDeclaration); - it('should return null if there is no matching class in the matching dts file', () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const missingClass = getDeclaration( - bundle.program, _('/ep/src/class1.js'), 'MissingClass1', isDesiredDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - expect(host.getDtsDeclaration(missingClass)).toBe(null); - }); + const dtsDeclaration = host.getDtsDeclaration(class1); + expect(dtsDeclaration!.getSourceFile().fileName) + .toEqual(_('/ep/typings/class1.d.ts')); + }); - it('should return null if there is no matching dts file', () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const missingClass = getDeclaration( - bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', isDesiredDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - expect(host.getDtsDeclaration(missingClass)).toBe(null); - }); + it('should find the dts declaration for exported functions', () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(_('/ep/src/func1.js')); + const dts = makeTestBundleProgram(_('/ep/typings/func1.d.ts')); + const mooFn = getDeclaration( + bundle.program, _('/ep/src/func1.js'), 'mooFn', isExportsDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + + const dtsDeclaration = host.getDtsDeclaration(mooFn); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); + }); + it('should return null if there is no matching class in the matching dts file', () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const missingClass = getDeclaration( + bundle.program, _('/ep/src/class1.js'), 'MissingClass1', isDesiredDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + expect(host.getDtsDeclaration(missingClass)).toBe(null); + }); + it('should return null if there is no matching dts file', () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const missingClass = getDeclaration( + bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', + isDesiredDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - it('should find the dts file that contains a matching class declaration, even if the source files do not match', - () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const class1 = getDeclaration( - bundle.program, _('/ep/src/flat-file.js'), 'Class1', isDesiredDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); - }); - - it('should find aliased exports', () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const sourceClass = getDeclaration( - bundle.program, _('/ep/src/flat-file.js'), 'AliasedClass', isExportsDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - const dtsDeclaration = host.getDtsDeclaration(sourceClass); - if (dtsDeclaration === null) { - return fail('Expected dts class to be found'); - } - if (!isNamedClassDeclaration(dtsDeclaration)) { - return fail('Expected a named class to be found.'); - } - expect(dtsDeclaration.name.text).toEqual('TypingsClass'); - expect(_(dtsDeclaration.getSourceFile().fileName)) - .toEqual(_('/ep/typings/typings-class.d.ts')); - }); + expect(host.getDtsDeclaration(missingClass)).toBe(null); + }); - it('should match publicly and internal exported classes correctly, even if they have the same name', - () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - const class2 = getDeclaration( - bundle.program, _('/ep/src/class2.js'), 'Class2', isDesiredDeclaration); - const class2DtsDeclaration = host.getDtsDeclaration(class2); - expect(class2DtsDeclaration!.getSourceFile().fileName) - .toEqual(_('/ep/typings/class2.d.ts')); - - const internalClass2 = getDeclaration( - bundle.program, _('/ep/src/internal.js'), 'Class2', isDesiredDeclaration); - const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); - expect(internalClass2DtsDeclaration!.getSourceFile().fileName) - .toEqual(_('/ep/typings/internal.d.ts')); - }); - - it('should prefer the publicly exported class if there are multiple classes with the same name', - () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/ep/src/index.js')); - const dts = makeTestBundleProgram(_('/ep/typings/index.d.ts')); - const class2 = getDeclaration( - bundle.program, _('/ep/src/class2.js'), 'Class2', isDesiredDeclaration); - const internalClass2 = getDeclaration( - bundle.program, _('/ep/src/internal.js'), 'Class2', isDesiredDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - const class2DtsDeclaration = host.getDtsDeclaration(class2); - expect(class2DtsDeclaration!.getSourceFile().fileName) - .toEqual(_('/ep/typings/class2.d.ts')); - - const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); - expect(internalClass2DtsDeclaration!.getSourceFile().fileName) - .toEqual(_('/ep/typings/internal.d.ts')); - }); - }); - describe('getInternalNameOfClass()', () => { - it('should return the name of the inner class declaration', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const emptyClass = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); - expect(host.getInternalNameOfClass(emptyClass).text).toEqual('EmptyClass'); + it('should find the dts file that contains a matching class declaration, even if the source files do not match', + () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const class1 = getDeclaration( + bundle.program, _('/ep/src/flat-file.js'), 'Class1', isDesiredDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + + const dtsDeclaration = host.getDtsDeclaration(class1); + expect(dtsDeclaration!.getSourceFile().fileName) + .toEqual(_('/ep/typings/class1.d.ts')); + }); + + it('should find aliased exports', () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const sourceClass = getDeclaration( + bundle.program, _('/ep/src/flat-file.js'), 'AliasedClass', isExportsDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + + const dtsDeclaration = host.getDtsDeclaration(sourceClass); + if (dtsDeclaration === null) { + return fail('Expected dts class to be found'); + } + if (!isNamedClassDeclaration(dtsDeclaration)) { + return fail('Expected a named class to be found.'); + } + expect(dtsDeclaration.name.text).toEqual('TypingsClass'); + expect(_(dtsDeclaration.getSourceFile().fileName)) + .toEqual(_('/ep/typings/typings-class.d.ts')); + }); + + it('should match publicly and internal exported classes correctly, even if they have the same name', + () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + const class2 = getDeclaration( + bundle.program, _('/ep/src/class2.js'), 'Class2', isDesiredDeclaration); + const class2DtsDeclaration = host.getDtsDeclaration(class2); + expect(class2DtsDeclaration!.getSourceFile().fileName) + .toEqual(_('/ep/typings/class2.d.ts')); + + const internalClass2 = getDeclaration( + bundle.program, _('/ep/src/internal.js'), 'Class2', isDesiredDeclaration); + const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); + expect(internalClass2DtsDeclaration!.getSourceFile().fileName) + .toEqual(_('/ep/typings/internal.d.ts')); + }); + + it('should prefer the publicly exported class if there are multiple classes with the same name', + () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(_('/ep/src/index.js')); + const dts = makeTestBundleProgram(_('/ep/typings/index.d.ts')); + const class2 = getDeclaration( + bundle.program, _('/ep/src/class2.js'), 'Class2', isDesiredDeclaration); + const internalClass2 = getDeclaration( + bundle.program, _('/ep/src/internal.js'), 'Class2', isDesiredDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + + const class2DtsDeclaration = host.getDtsDeclaration(class2); + expect(class2DtsDeclaration!.getSourceFile().fileName) + .toEqual(_('/ep/typings/class2.d.ts')); + + const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); + expect(internalClass2DtsDeclaration!.getSourceFile().fileName) + .toEqual(_('/ep/typings/internal.d.ts')); + }); + }); + + describe('getInternalNameOfClass()', () => { + it('should return the name of the inner class declaration', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const class1 = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isDesiredDeclaration); - expect(host.getInternalNameOfClass(class1).text).toEqual('InnerClass1'); + const emptyClass = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + expect(host.getInternalNameOfClass(emptyClass).text).toEqual('EmptyClass'); - const class2 = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isDesiredDeclaration); - expect(host.getInternalNameOfClass(class2).text).toEqual('InnerClass2'); + const class1 = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isDesiredDeclaration); + expect(host.getInternalNameOfClass(class1).text).toEqual('InnerClass1'); - const childClass = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'ChildClass', isDesiredDeclaration); - expect(host.getInternalNameOfClass(childClass).text).toEqual('InnerChildClass'); + const class2 = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isDesiredDeclaration); + expect(host.getInternalNameOfClass(class2).text).toEqual('InnerClass2'); + + const childClass = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'ChildClass', isDesiredDeclaration); + expect(host.getInternalNameOfClass(childClass).text).toEqual('InnerChildClass'); + }); }); - }); - describe('getAdjacentNameOfClass()', () => { - it('should return the name of the inner class declaration', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + describe('getAdjacentNameOfClass()', () => { + it('should return the name of the inner class declaration', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const emptyClass = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); - expect(host.getAdjacentNameOfClass(emptyClass).text).toEqual('EmptyClass'); + const emptyClass = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + expect(host.getAdjacentNameOfClass(emptyClass).text).toEqual('EmptyClass'); - const class1 = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isDesiredDeclaration); - expect(host.getAdjacentNameOfClass(class1).text).toEqual('InnerClass1'); + const class1 = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isDesiredDeclaration); + expect(host.getAdjacentNameOfClass(class1).text).toEqual('InnerClass1'); - const class2 = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isDesiredDeclaration); - expect(host.getAdjacentNameOfClass(class2).text).toEqual('InnerClass2'); + const class2 = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isDesiredDeclaration); + expect(host.getAdjacentNameOfClass(class2).text).toEqual('InnerClass2'); - const childClass = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'ChildClass', isDesiredDeclaration); - expect(host.getAdjacentNameOfClass(childClass).text).toEqual('InnerChildClass'); + const childClass = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'ChildClass', isDesiredDeclaration); + expect(host.getAdjacentNameOfClass(childClass).text).toEqual('InnerChildClass'); + }); }); }); - }); + } } });