diff --git a/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts index b7061d7d6d..e8498c8e8d 100644 --- a/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts @@ -58,33 +58,11 @@ export class PrivateDeclarationsAnalyzer { if (declaration.node.name.text === exportedName) { // This declaration is public so we can remove it from the list privateDeclarations.delete(declaration.node.name); - } else if (!this.host.getDtsDeclaration(declaration.node)) { + } else { // The referenced declaration is exported publicly but via an alias. // In some cases the original declaration is missing from the dts program, such as // when rolling up (flattening) the dts files. // This is because the original declaration gets renamed to the exported alias. - - // There is a constraint on this which we cannot handle. Consider the following - // code: - // - // /src/entry_point.js: - // export {MyComponent as aliasedMyComponent} from './a'; - // export {MyComponent} from './b';` - // - // /src/a.js: - // export class MyComponent {} - // - // /src/b.js: - // export class MyComponent {} - // - // //typings/entry_point.d.ts: - // export declare class aliasedMyComponent {} - // export declare class MyComponent {} - // - // In this case we would end up matching the `MyComponent` from `/src/a.js` to the - // `MyComponent` declared in `/typings/entry_point.d.ts` even though that - // declaration is actually for the `MyComponent` in `/src/b.js`. - exportAliasDeclarations.set(declaration.node.name, exportedName); } } diff --git a/packages/compiler-cli/ngcc/src/host/commonjs_host.ts b/packages/compiler-cli/ngcc/src/host/commonjs_host.ts index 18a0bed45d..817dac68d9 100644 --- a/packages/compiler-cli/ngcc/src/host/commonjs_host.ts +++ b/packages/compiler-cli/ngcc/src/host/commonjs_host.ts @@ -21,7 +21,7 @@ export class CommonJsReflectionHost extends Esm5ReflectionHost { protected topLevelHelperCalls = new Map>(); protected program: ts.Program; protected compilerHost: ts.CompilerHost; - constructor(logger: Logger, isCore: boolean, src: BundleProgram, dts?: BundleProgram|null) { + constructor(logger: Logger, isCore: boolean, src: BundleProgram, dts: BundleProgram|null = null) { super(logger, isCore, src, dts); this.program = src.program; this.compilerHost = src.host; diff --git a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts index 2bda8e8e8e..13b5d1e9af 100644 --- a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts @@ -8,7 +8,6 @@ import * as ts from 'typescript'; -import {AbsoluteFsPath} from '../../../src/ngtsc/file_system'; import {ClassDeclaration, ClassMember, ClassMemberKind, ConcreteDeclaration, CtorParameter, Declaration, Decorator, TypeScriptReflectionHost, TypeValueReference, isDecoratorIdentifier, reflectObjectLiteral} from '../../../src/ngtsc/reflection'; import {isWithinPackage} from '../analysis/util'; import {Logger} from '../logging/logger'; @@ -50,7 +49,22 @@ export const CONSTRUCTOR_PARAMS = 'ctorParameters' as ts.__String; * a static method called `ctorParameters`. */ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements NgccReflectionHost { - protected dtsDeclarationMap: Map|null; + /** + * A mapping from source declarations typings declarations, which are both publicly exported. + * + * There should be one entry for every public export visible from the root file of the source + * tree. Note that by definition the key and value declarations will not be in the same TS + * program. + */ + protected publicDtsDeclarationMap: Map|null = null; + /** + * A mapping from source declarations to typings declarations, which are not publicly exported. + * + * This mapping is a best guess between declarations that happen to be exported from their file by + * the same name in both the source and the dts file. Note that by definition the key and value + * declarations will not be in the same TS program. + */ + protected privateDtsDeclarationMap: Map|null = null; /** * The set of source files that have already been preprocessed. @@ -83,11 +97,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N protected decoratorCache = new Map(); constructor( - protected logger: Logger, protected isCore: boolean, src: BundleProgram, - dts?: BundleProgram|null) { + protected logger: Logger, protected isCore: boolean, protected src: BundleProgram, + protected dts: BundleProgram|null = null) { super(src.program.getTypeChecker()); - this.dtsDeclarationMap = - dts && this.computeDtsDeclarationMap(dts.path, dts.program, dts.package) || null; } /** @@ -484,16 +496,32 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * `ts.Program` as the input declaration. */ getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null { - if (!this.dtsDeclarationMap) { + if (this.dts === null) { return null; } if (!isNamedDeclaration(declaration)) { throw new Error( `Cannot get the dts file for a declaration that has no name: ${declaration.getText()} in ${declaration.getSourceFile().fileName}`); } - return this.dtsDeclarationMap.has(declaration.name.text) ? - this.dtsDeclarationMap.get(declaration.name.text) ! : - null; + + // Try to retrieve the dts declaration from the public map + if (this.publicDtsDeclarationMap === null) { + this.publicDtsDeclarationMap = this.computePublicDtsDeclarationMap(this.src, this.dts); + } + if (this.publicDtsDeclarationMap.has(declaration)) { + return this.publicDtsDeclarationMap.get(declaration) !; + } + + // No public export, try the private map + if (this.privateDtsDeclarationMap === null) { + this.privateDtsDeclarationMap = this.computePrivateDtsDeclarationMap(this.src, this.dts); + } + if (this.privateDtsDeclarationMap.has(declaration)) { + return this.privateDtsDeclarationMap.get(declaration) !; + } + + // No declaration found at all + return null; } /** @@ -1499,42 +1527,91 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N } /** - * Extract all the class declarations from the dtsTypings program, storing them in a map - * where the key is the declared name of the class and the value is the declaration itself. + * Create a mapping between the public exports in a src program and the public exports of a dts + * program. * - * It is possible for there to be multiple class declarations with the same local name. - * Only the first declaration with a given name is added to the map; subsequent classes will be - * ignored. - * - * We are most interested in classes that are publicly exported from the entry point, so these - * are added to the map first, to ensure that they are not ignored. - * - * @param dtsRootFileName The filename of the entry-point to the `dtsTypings` program. - * @param dtsProgram The program containing all the typings files. - * @returns a map of class names to class declarations. + * @param src the program bundle containing the source files. + * @param dts the program bundle containing the typings files. + * @returns a map of source declarations to typings declarations. */ - protected computeDtsDeclarationMap( - dtsRootFileName: AbsoluteFsPath, dtsProgram: ts.Program, - dtsPackage: AbsoluteFsPath): Map { + protected computePublicDtsDeclarationMap(src: BundleProgram, dts: BundleProgram): + Map { + const declarationMap = new Map(); const dtsDeclarationMap = new Map(); - const checker = dtsProgram.getTypeChecker(); + const rootDts = getRootFileOrFail(dts); + this.collectDtsExportedDeclarations(dtsDeclarationMap, rootDts, dts.program.getTypeChecker()); + const rootSrc = getRootFileOrFail(src); + this.collectSrcExportedDeclarations(declarationMap, dtsDeclarationMap, rootSrc); + return declarationMap; + } - // First add all the classes that are publicly exported from the entry-point - const rootFile = dtsProgram.getSourceFile(dtsRootFileName); - if (!rootFile) { - throw new Error(`The given file ${dtsRootFileName} is not part of the typings program.`); - } - collectExportedDeclarations(checker, dtsDeclarationMap, rootFile); - - // Now add any additional classes that are exported from individual dts files, - // but are not publicly exported from the entry-point. - dtsProgram.getSourceFiles().forEach(sourceFile => { - if (!isWithinPackage(dtsPackage, sourceFile)) { - return; + /** + * Create a mapping between the "private" exports in a src program and the "private" exports of a + * dts program. These exports may be exported from individual files in the src or dts programs, + * but not exported from the root file (i.e publicly from the entry-point). + * + * This mapping is a "best guess" since we cannot guarantee that two declarations that happen to + * be exported from a file with the same name are actually equivalent. But this is a reasonable + * estimate for the purposes of ngcc. + * + * @param src the program bundle containing the source files. + * @param dts the program bundle containing the typings files. + * @returns a map of source declarations to typings declarations. + */ + protected computePrivateDtsDeclarationMap(src: BundleProgram, dts: BundleProgram): + Map { + const declarationMap = new Map(); + const dtsDeclarationMap = new Map(); + const dtsFiles = getNonRootFiles(dts); + const typeChecker = dts.program.getTypeChecker(); + for (const dtsFile of dtsFiles) { + if (isWithinPackage(dts.package, dtsFile)) { + this.collectDtsExportedDeclarations(dtsDeclarationMap, dtsFile, typeChecker); } - collectExportedDeclarations(checker, dtsDeclarationMap, sourceFile); - }); - return dtsDeclarationMap; + } + const srcFiles = getNonRootFiles(src); + for (const srcFile of srcFiles) { + this.collectSrcExportedDeclarations(declarationMap, dtsDeclarationMap, srcFile); + } + return declarationMap; + } + + /** + * Collect mappings between names of exported declarations in a file and its actual declaration. + * + * Any new mappings are added to the `dtsDeclarationMap`. + */ + protected collectDtsExportedDeclarations( + dtsDeclarationMap: Map, srcFile: ts.SourceFile, + checker: ts.TypeChecker): void { + const srcModule = srcFile && checker.getSymbolAtLocation(srcFile); + const moduleExports = srcModule && checker.getExportsOfModule(srcModule); + if (moduleExports) { + moduleExports.forEach(exportedSymbol => { + const name = exportedSymbol.name; + if (exportedSymbol.flags & ts.SymbolFlags.Alias) { + exportedSymbol = checker.getAliasedSymbol(exportedSymbol); + } + const declaration = exportedSymbol.valueDeclaration; + if (declaration && !dtsDeclarationMap.has(name)) { + dtsDeclarationMap.set(name, declaration); + } + }); + } + } + + + protected collectSrcExportedDeclarations( + declarationMap: Map, + dtsDeclarationMap: Map, srcFile: ts.SourceFile): void { + const fileExports = this.getExportsOfModule(srcFile); + if (fileExports !== null) { + for (const [exportName, {node: declaration}] of fileExports) { + if (declaration !== null && dtsDeclarationMap.has(exportName)) { + declarationMap.set(declaration, dtsDeclarationMap.get(exportName) !); + } + } + } } /** @@ -1862,29 +1939,6 @@ function isClassMemberType(node: ts.Declaration): node is ts.ClassElement| !ts.isIndexSignatureDeclaration(node); } -/** - * Collect mappings between exported declarations in a source file and its associated - * declaration in the typings program. - */ -function collectExportedDeclarations( - checker: ts.TypeChecker, dtsDeclarationMap: Map, - srcFile: ts.SourceFile): void { - const srcModule = srcFile && checker.getSymbolAtLocation(srcFile); - const moduleExports = srcModule && checker.getExportsOfModule(srcModule); - if (moduleExports) { - moduleExports.forEach(exportedSymbol => { - if (exportedSymbol.flags & ts.SymbolFlags.Alias) { - exportedSymbol = checker.getAliasedSymbol(exportedSymbol); - } - const declaration = exportedSymbol.valueDeclaration; - const name = exportedSymbol.name; - if (declaration && !dtsDeclarationMap.has(name)) { - dtsDeclarationMap.set(name, declaration); - } - }); - } -} - /** * Attempt to resolve the variable declaration that the given declaration is assigned to. * For example, for the following code: @@ -1972,3 +2026,16 @@ function getContainingStatement(node: ts.Node): ts.ExpressionStatement|null { } return node || null; } + +function getRootFileOrFail(bundle: BundleProgram): ts.SourceFile { + const rootFile = bundle.program.getSourceFile(bundle.path); + if (rootFile === undefined) { + throw new Error(`The given rootPath ${rootFile} is not a file of the program.`); + } + return rootFile; +} + +function getNonRootFiles(bundle: BundleProgram): ts.SourceFile[] { + const rootFile = bundle.program.getSourceFile(bundle.path); + return bundle.program.getSourceFiles().filter(f => f !== rootFile); +} diff --git a/packages/compiler-cli/ngcc/src/host/umd_host.ts b/packages/compiler-cli/ngcc/src/host/umd_host.ts index 23c78ea31a..b39f28ade3 100644 --- a/packages/compiler-cli/ngcc/src/host/umd_host.ts +++ b/packages/compiler-cli/ngcc/src/host/umd_host.ts @@ -21,7 +21,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost { protected umdImportPaths = new Map(); protected program: ts.Program; protected compilerHost: ts.CompilerHost; - constructor(logger: Logger, isCore: boolean, src: BundleProgram, dts?: BundleProgram|null) { + constructor(logger: Logger, isCore: boolean, src: BundleProgram, dts: BundleProgram|null = null) { super(logger, isCore, src, dts); this.program = src.program; this.compilerHost = src.host; diff --git a/packages/compiler-cli/ngcc/test/analysis/private_declarations_analyzer_spec.ts b/packages/compiler-cli/ngcc/test/analysis/private_declarations_analyzer_spec.ts index 5e2bfbeba8..ffebe17e49 100644 --- a/packages/compiler-cli/ngcc/test/analysis/private_declarations_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/private_declarations_analyzer_spec.ts @@ -225,7 +225,7 @@ runInEachFileSystem(() => { expect(analyses).toEqual([{ identifier: 'ComponentOne', from: _('/node_modules/test-package/src/a.js'), - dtsFrom: null, + dtsFrom: _('/node_modules/test-package/typings/entry_point.d.ts'), alias: 'aliasedComponentOne', }]); }); diff --git a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts index cdcd450f09..721cab1565 100644 --- a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts @@ -650,7 +650,7 @@ exports.D = D; TYPINGS_SRC_FILES = [ { - name: _('/src/index.js'), + name: _('/ep/src/index.js'), contents: ` var internal = require('./internal'); var class1 = require('./class1'); @@ -667,7 +667,7 @@ __export(class2); ` }, { - name: _('/src/class1.js'), + name: _('/ep/src/class1.js'), contents: ` var Class1 = (function() { function Class1() {} @@ -682,7 +682,7 @@ exports.MissingClass1 = MissingClass1; ` }, { - name: _('/src/class2.js'), + name: _('/ep/src/class2.js'), contents: ` var Class2 = (function() { function Class2() {} @@ -691,8 +691,8 @@ var Class2 = (function() { exports.Class2 = Class2; ` }, - {name: _('/src/func1.js'), contents: 'function mooFn() {} export {mooFn}'}, { - name: _('/src/internal.js'), + {name: _('/ep/src/func1.js'), contents: 'function mooFn() {} export {mooFn}'}, { + name: _('/ep/src/internal.js'), contents: ` var InternalClass = (function() { function InternalClass() {} @@ -707,7 +707,7 @@ exports.Class2 = Class2; ` }, { - name: _('/src/missing-class.js'), + name: _('/ep/src/missing-class.js'), contents: ` var MissingClass2 = (function() { function MissingClass2() {} @@ -717,7 +717,7 @@ exports. MissingClass2 = MissingClass2; ` }, { - name: _('/src/flat-file.js'), + name: _('/ep/src/flat-file.js'), contents: ` var Class1 = (function() { function Class1() {} @@ -731,12 +731,12 @@ var MissingClass2 = (function() { function MissingClass2() {} return MissingClass2; }()); -var Class3 = (function() { - function Class3() {} - return Class3; +var SourceClass = (function() { + function SourceClass() {} + return SourceClass; }()); exports.Class1 = Class1; -exports.xClass3 = Class3; +exports.AliasedClass = SourceClass; exports.MissingClass1 = MissingClass1; exports.MissingClass2 = MissingClass2; ` @@ -745,29 +745,38 @@ exports.MissingClass2 = MissingClass2; TYPINGS_DTS_FILES = [ { - name: _('/typings/index.d.ts'), + name: _('/ep/typings/index.d.ts'), contents: ` - import {InternalClass} from './internal'; - import {mooFn} from './func1'; - export * from './class1'; - export * from './class2'; - ` + import '../../an_external_lib/index'; + import {InternalClass} from './internal'; + import {mooFn} from './func1'; + export * from './class1'; + export * from './class2'; + ` }, { - name: _('/typings/class1.d.ts'), + name: _('/ep/typings/class1.d.ts'), contents: `export declare class Class1 {}\nexport declare class OtherClass {}` }, { - name: _('/typings/class2.d.ts'), - contents: - `export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';` + name: _('/ep/typings/class2.d.ts'), + contents: ` + export declare class Class2 {} + export declare interface SomeInterface {} + export {TypingsClass as AliasedClass} from './typings-class'; + ` }, - {name: _('/typings/func1.d.ts'), contents: 'export declare function mooFn(): void;'}, + {name: _('/ep/typings/func1.d.ts'), contents: 'export declare function mooFn(): void;'}, { - name: _('/typings/internal.d.ts'), + name: _('/ep/typings/internal.d.ts'), contents: `export declare class InternalClass {}\nexport declare class Class2 {}` }, - {name: _('/typings/class3.d.ts'), contents: `export declare class Class3 {}`}, + { + 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 {}'}, ]; MODULE_WITH_PROVIDERS_PROGRAM = [ @@ -2140,35 +2149,36 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles(TYPINGS_SRC_FILES); loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/src/index.js')); - const dts = makeTestDtsBundleProgram(_('/typings/index.d.ts'), _('/')); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( - bundle.program, _('/src/class1.js'), 'Class1', ts.isVariableDeclaration); + bundle.program, _('/ep/src/class1.js'), 'Class1', ts.isVariableDeclaration); const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/class1.d.ts')); + 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(_('/src/index.js')); - const dts = makeTestDtsBundleProgram(_('/typings/index.d.ts'), _('/')); - const mooFn = - getDeclaration(bundle.program, _('/src/func1.js'), 'mooFn', ts.isFunctionDeclaration); + const bundle = makeTestBundleProgram(_('/ep/src/func1.js')); + const dts = makeTestDtsBundleProgram(_('/ep/typings/func1.d.ts'), _('/')); + const mooFn = getDeclaration( + bundle.program, _('/ep/src/func1.js'), 'mooFn', ts.isFunctionDeclaration); const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); const dtsDeclaration = host.getDtsDeclaration(mooFn); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/func1.d.ts')); + 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(_('/src/index.js')); - const dts = makeTestDtsBundleProgram(_('/typings/index.d.ts'), _('/')); + const bundle = makeTestBundleProgram(_('/ep/src/index.js')); + const dts = makeTestDtsBundleProgram(_('/ep/typings/index.d.ts'), _('/')); const missingClass = getDeclaration( - bundle.program, _('/src/class1.js'), 'MissingClass1', ts.isVariableDeclaration); + bundle.program, _('/ep/src/class1.js'), 'MissingClass1', ts.isVariableDeclaration); const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); expect(host.getDtsDeclaration(missingClass)).toBe(null); @@ -2177,10 +2187,10 @@ exports.ExternalModule = ExternalModule; it('should return null if there is no matching dts file', () => { loadTestFiles(TYPINGS_SRC_FILES); loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/src/index.js')); - const dts = makeTestDtsBundleProgram(_('/typings/index.d.ts'), _('/')); + const bundle = makeTestBundleProgram(_('/ep/src/index.js')); + const dts = makeTestDtsBundleProgram(_('/ep/typings/index.d.ts'), _('/')); const missingClass = getDeclaration( - bundle.program, _('/src/missing-class.js'), 'MissingClass2', + bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', ts.isVariableDeclaration); const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); @@ -2191,62 +2201,73 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles(TYPINGS_SRC_FILES); loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/src/index.js')); - const dts = makeTestDtsBundleProgram(_('/typings/index.d.ts'), _('/')); + const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( - bundle.program, _('/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); + bundle.program, _('/ep/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/class1.d.ts')); + 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(_('/src/index.js')); - const dts = makeTestDtsBundleProgram(_('/typings/index.d.ts'), _('/')); - const class3 = getDeclaration( - bundle.program, _('/src/flat-file.js'), 'Class3', ts.isVariableDeclaration); + 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'), 'SourceClass', ts.isVariableDeclaration); const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); - const dtsDeclaration = host.getDtsDeclaration(class3); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/class3.d.ts')); + 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 find the dts file that contains a matching class declaration, even if the class is not publicly exported', () => { loadTestFiles(TYPINGS_SRC_FILES); loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/src/index.js')); - const dts = makeTestDtsBundleProgram(_('/typings/index.d.ts'), _('/')); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const internalClass = getDeclaration( - bundle.program, _('/src/internal.js'), 'InternalClass', ts.isVariableDeclaration); + bundle.program, _('/ep/src/internal.js'), 'InternalClass', + ts.isVariableDeclaration); const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); const dtsDeclaration = host.getDtsDeclaration(internalClass); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/internal.d.ts')); + expect(dtsDeclaration !.getSourceFile().fileName) + .toEqual(_('/ep/typings/internal.d.ts')); }); - it('should prefer the publicly exported class if there are multiple classes with the same name', + 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(_('/src/index.js')); - const dts = makeTestDtsBundleProgram(_('/typings/index.d.ts'), _('/')); - const class2 = getDeclaration( - bundle.program, _('/src/class2.js'), 'Class2', ts.isVariableDeclaration); - const internalClass2 = getDeclaration( - bundle.program, _('/src/internal.js'), 'Class2', ts.isVariableDeclaration); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestDtsBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0], _('/ep')); const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); + const class2 = getDeclaration( + bundle.program, _('/ep/src/class2.js'), 'Class2', isNamedVariableDeclaration); const class2DtsDeclaration = host.getDtsDeclaration(class2); expect(class2DtsDeclaration !.getSourceFile().fileName) - .toEqual(_('/typings/class2.d.ts')); + .toEqual(_('/ep/typings/class2.d.ts')); + const internalClass2 = getDeclaration( + bundle.program, _('/ep/src/internal.js'), 'Class2', isNamedVariableDeclaration); const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); expect(internalClass2DtsDeclaration !.getSourceFile().fileName) - .toEqual(_('/typings/class2.d.ts')); + .toEqual(_('/ep/typings/internal.d.ts')); }); }); diff --git a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts index 9aec67f381..35b3c20cf3 100644 --- a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts @@ -10,12 +10,12 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; -import {ClassMemberKind, CtorParameter, Import, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; +import {ClassMemberKind, CtorParameter, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {MockLogger} from '../helpers/mock_logger'; -import {getRootFiles, makeTestBundleProgram} from '../helpers/utils'; +import {getRootFiles, makeTestBundleProgram, makeTestDtsBundleProgram} from '../helpers/utils'; import {expectTypeValueReferencesForParameters} from './util'; @@ -568,8 +568,12 @@ runInEachFileSystem(() => { {name: _('/ep/src/missing-class.js'), contents: 'export class MissingClass2 {}'}, { name: _('/ep/src/flat-file.js'), - contents: - 'export class Class1 {}\nexport class MissingClass1 {}\nexport class MissingClass2 {}\class Class3 {}\nexport {Class3 as xClass3};', + contents: ` + export class Class1 {} + export class MissingClass1 {} + export class MissingClass2 {} + class SourceClass {} + export {SourceClass as AliasedClass};`, }, { name: _('/ep/src/shadow-class.js'), @@ -594,20 +598,25 @@ runInEachFileSystem(() => { }, { name: _('/ep/typings/class2.d.ts'), - contents: - `export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';` + 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/class3.d.ts'), contents: `export declare class Class3 {}`}, + { + 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 {}'}, ]; - MODULE_WITH_PROVIDERS_PROGRAM = [ { name: _('/src/index.js'), @@ -1967,8 +1976,8 @@ runInEachFileSystem(() => { it('should find the dts declaration for exported functions', () => { 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 bundle = makeTestBundleProgram(_('/ep/src/func1.js')); + const dts = makeTestDtsBundleProgram(_('/ep/typings/func1.d.ts'), _('/')); const mooFn = getDeclaration( bundle.program, _('/ep/src/func1.js'), 'mooFn', isNamedFunctionDeclaration); const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); @@ -2023,7 +2032,7 @@ runInEachFileSystem(() => { () => { loadTestFiles(TYPINGS_SRC_FILES); loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + 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', isNamedClassDeclaration); @@ -2036,14 +2045,22 @@ runInEachFileSystem(() => { it('should find aliased exports', () => { loadTestFiles(TYPINGS_SRC_FILES); loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const class3 = getDeclaration( - bundle.program, _('/ep/src/flat-file.js'), 'Class3', isNamedClassDeclaration); + const sourceClass = getDeclaration( + bundle.program, _('/ep/src/flat-file.js'), 'SourceClass', isNamedClassDeclaration); const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); - const dtsDeclaration = host.getDtsDeclaration(class3); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/class3.d.ts')); + 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 find the dts file that contains a matching class declaration, even if the class is not publicly exported', @@ -2061,25 +2078,25 @@ runInEachFileSystem(() => { .toEqual(_('/ep/typings/internal.d.ts')); }); - it('should prefer the publicly exported class if there are multiple classes with the same name', + 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 class2 = getDeclaration( - bundle.program, _('/ep/src/class2.js'), 'Class2', isNamedClassDeclaration); - const internalClass2 = getDeclaration( - bundle.program, _('/ep/src/internal.js'), 'Class2', isNamedClassDeclaration); const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const class2 = getDeclaration( + bundle.program, _('/ep/src/class2.js'), 'Class2', isNamedClassDeclaration); 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', isNamedClassDeclaration); const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); expect(internalClass2DtsDeclaration !.getSourceFile().fileName) - .toEqual(_('/ep/typings/class2.d.ts')); + .toEqual(_('/ep/typings/internal.d.ts')); }); }); diff --git a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts index 1c71e24897..fa67d84d48 100644 --- a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts @@ -16,7 +16,7 @@ import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {Esm5ReflectionHost, getIifeBody} from '../../src/host/esm5_host'; import {MockLogger} from '../helpers/mock_logger'; -import {getRootFiles, makeTestBundleProgram} from '../helpers/utils'; +import {getRootFiles, makeTestBundleProgram, makeTestDtsBundleProgram} from '../helpers/utils'; import {expectTypeValueReferencesForParameters} from './util'; @@ -648,7 +648,7 @@ runInEachFileSystem(() => { TYPINGS_SRC_FILES = [ { - name: _('/src/index.js'), + name: _('/ep/src/index.js'), contents: ` import {InternalClass} from './internal'; import * as func1 from './func1'; @@ -659,7 +659,7 @@ runInEachFileSystem(() => { ` }, { - name: _('/src/class1.js'), + name: _('/ep/src/class1.js'), contents: ` var Class1 = (function() { function Class1() {} @@ -673,7 +673,7 @@ runInEachFileSystem(() => { ` }, { - name: _('/src/class2.js'), + name: _('/ep/src/class2.js'), contents: ` var Class2 = (function() { function Class2() {} @@ -682,8 +682,8 @@ runInEachFileSystem(() => { export {Class2}; ` }, - {name: _('/src/func1.js'), contents: 'function mooFn() {} export {mooFn}'}, { - name: _('/src/internal.js'), + {name: _('/ep/src/func1.js'), contents: 'function mooFn() {} export {mooFn}'}, { + name: _('/ep/src/internal.js'), contents: ` var InternalClass = (function() { function InternalClass() {} @@ -697,7 +697,7 @@ runInEachFileSystem(() => { ` }, { - name: _('/src/missing-class.js'), + name: _('/ep/src/missing-class.js'), contents: ` var MissingClass2 = (function() { function MissingClass2() {} @@ -707,7 +707,7 @@ runInEachFileSystem(() => { ` }, { - name: _('/src/flat-file.js'), + name: _('/ep/src/flat-file.js'), contents: ` var Class1 = (function() { function Class1() {} @@ -721,19 +721,20 @@ runInEachFileSystem(() => { function MissingClass2() {} return MissingClass2; }()); - var Class3 = (function() { - function Class3() {} - return Class3; + var SourceClass = (function() { + function SourceClass() {} + return SourceClass; }()); - export {Class1, Class3 as xClass3, MissingClass1, MissingClass2}; + export {Class1, SourceClass as AliasedClass, MissingClass1, MissingClass2}; ` } ]; TYPINGS_DTS_FILES = [ { - name: _('/typings/index.d.ts'), + name: _('/ep/typings/index.d.ts'), contents: ` + import '../../an_external_lib/index'; import {InternalClass} from './internal'; import {mooFn} from './func1'; export * from './class1'; @@ -741,20 +742,28 @@ runInEachFileSystem(() => { ` }, { - name: _('/typings/class1.d.ts'), + name: _('/ep/typings/class1.d.ts'), contents: `export declare class Class1 {}\nexport declare class OtherClass {}` }, { - name: _('/typings/class2.d.ts'), - contents: - `export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';` + name: _('/ep/typings/class2.d.ts'), + contents: ` + export declare class Class2 {} + export declare interface SomeInterface {} + export {TypingsClass as AliasedClass} from './typings-class'; + ` }, - {name: _('/typings/func1.d.ts'), contents: 'export declare function mooFn(): void;'}, + {name: _('/ep/typings/func1.d.ts'), contents: 'export declare function mooFn(): void;'}, { - name: _('/typings/internal.d.ts'), + name: _('/ep/typings/internal.d.ts'), contents: `export declare class InternalClass {}\nexport declare class Class2 {}` }, - {name: _('/typings/class3.d.ts'), contents: `export declare class Class3 {}`}, + { + 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 {}'}, ]; MODULE_WITH_PROVIDERS_PROGRAM = [ @@ -2369,24 +2378,24 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( - bundle.program, _('/src/class1.js'), 'Class1', ts.isVariableDeclaration); + bundle.program, _('/ep/src/class1.js'), 'Class1', ts.isVariableDeclaration); const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/class1.d.ts')); + 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(getRootFiles(TYPINGS_SRC_FILES)[0]); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const mooFn = - getDeclaration(bundle.program, _('/src/func1.js'), 'mooFn', ts.isFunctionDeclaration); + const bundle = makeTestBundleProgram(_('/ep/src/func1.js')); + const dts = makeTestDtsBundleProgram(_('/ep/typings/func1.d.ts'), _('/')); + const mooFn = getDeclaration( + bundle.program, _('/ep/src/func1.js'), 'mooFn', ts.isFunctionDeclaration); const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); const dtsDeclaration = host.getDtsDeclaration(mooFn); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/func1.d.ts')); + 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', () => { @@ -2395,7 +2404,7 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const missingClass = getDeclaration( - bundle.program, _('/src/class1.js'), 'MissingClass1', ts.isVariableDeclaration); + bundle.program, _('/ep/src/class1.js'), 'MissingClass1', ts.isVariableDeclaration); const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); expect(host.getDtsDeclaration(missingClass)).toBe(null); @@ -2407,7 +2416,8 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const missingClass = getDeclaration( - bundle.program, _('/src/missing-class.js'), 'MissingClass2', ts.isVariableDeclaration); + bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', + ts.isVariableDeclaration); const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); expect(host.getDtsDeclaration(missingClass)).toBe(null); @@ -2417,27 +2427,35 @@ runInEachFileSystem(() => { () => { loadTestFiles(TYPINGS_SRC_FILES); loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( - bundle.program, _('/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); + bundle.program, _('/ep/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/class1.d.ts')); + 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(getRootFiles(TYPINGS_SRC_FILES)[0]); + const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const class3 = getDeclaration( - bundle.program, _('/src/flat-file.js'), 'Class3', ts.isVariableDeclaration); + const sourceClass = getDeclaration( + bundle.program, _('/ep/src/flat-file.js'), 'SourceClass', ts.isVariableDeclaration); const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); - const dtsDeclaration = host.getDtsDeclaration(class3); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/class3.d.ts')); + 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 find the dts file that contains a matching class declaration, even if the class is not publicly exported', @@ -2447,32 +2465,33 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const internalClass = getDeclaration( - bundle.program, _('/src/internal.js'), 'InternalClass', ts.isVariableDeclaration); + bundle.program, _('/ep/src/internal.js'), 'InternalClass', ts.isVariableDeclaration); const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); const dtsDeclaration = host.getDtsDeclaration(internalClass); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/internal.d.ts')); + expect(dtsDeclaration !.getSourceFile().fileName) + .toEqual(_('/ep/typings/internal.d.ts')); }); - it('should prefer the publicly exported class if there are multiple classes with the same name', + 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 class2 = getDeclaration( - bundle.program, _('/src/class2.js'), 'Class2', ts.isVariableDeclaration); - const internalClass2 = getDeclaration( - bundle.program, _('/src/internal.js'), 'Class2', ts.isVariableDeclaration); const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); + const class2 = getDeclaration( + bundle.program, _('/ep/src/class2.js'), 'Class2', isNamedVariableDeclaration); const class2DtsDeclaration = host.getDtsDeclaration(class2); expect(class2DtsDeclaration !.getSourceFile().fileName) - .toEqual(_('/typings/class2.d.ts')); + .toEqual(_('/ep/typings/class2.d.ts')); + const internalClass2 = getDeclaration( + bundle.program, _('/ep/src/internal.js'), 'Class2', isNamedVariableDeclaration); const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); expect(internalClass2DtsDeclaration !.getSourceFile().fileName) - .toEqual(_('/typings/class2.d.ts')); + .toEqual(_('/ep/typings/internal.d.ts')); }); }); 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 21997fb6a4..c07aa72183 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -720,7 +720,7 @@ __export(xtra_module); TYPINGS_SRC_FILES = [ { - name: _('/src/index.js'), + 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')) : @@ -737,7 +737,7 @@ __export(xtra_module); ` }, { - name: _('/src/class1.js'), + name: _('/ep/src/class1.js'), contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : @@ -758,7 +758,7 @@ __export(xtra_module); ` }, { - name: _('/src/class2.js'), + name: _('/ep/src/class2.js'), contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : @@ -773,8 +773,8 @@ __export(xtra_module); }))); ` }, - {name: _('/src/func1.js'), contents: 'function mooFn() {} export {mooFn}'}, { - name: _('/src/internal.js'), + {name: _('/ep/src/func1.js'), contents: 'function mooFn() {} export {mooFn}'}, { + name: _('/ep/src/internal.js'), contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : @@ -795,7 +795,7 @@ __export(xtra_module); ` }, { - name: _('/src/missing-class.js'), + name: _('/ep/src/missing-class.js'), contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : @@ -811,7 +811,7 @@ __export(xtra_module); ` }, { - name: _('/src/flat-file.js'), + name: _('/ep/src/flat-file.js'), contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : @@ -830,12 +830,12 @@ __export(xtra_module); function MissingClass2() {} return MissingClass2; }()); - var Class3 = (function() { - function Class3() {} - return Class3; + var SourceClass = (function() { + function SourceClass() {} + return SourceClass; }()); - exports.Class1 = Class1; - exports.xClass3 = Class3; + exports.Class1 = Class1; + exports.AliasedClass = SourceClass; exports.MissingClass1 = MissingClass1; exports.MissingClass2 = MissingClass2; }))); @@ -845,29 +845,38 @@ __export(xtra_module); TYPINGS_DTS_FILES = [ { - name: _('/typings/index.d.ts'), + name: _('/ep/typings/index.d.ts'), contents: ` - import {InternalClass} from './internal'; - import {mooFn} from './func1'; - export * from './class1'; - export * from './class2'; - ` + import '../../an_external_lib/index'; + import {InternalClass} from './internal'; + import {mooFn} from './func1'; + export * from './class1'; + export * from './class2'; + ` }, { - name: _('/typings/class1.d.ts'), + name: _('/ep/typings/class1.d.ts'), contents: `export declare class Class1 {}\nexport declare class OtherClass {}` }, { - name: _('/typings/class2.d.ts'), - contents: - `export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';` + name: _('/ep/typings/class2.d.ts'), + contents: ` + export declare class Class2 {} + export declare interface SomeInterface {} + export {TypingsClass as AliasedClass} from './typings-class'; + ` }, - {name: _('/typings/func1.d.ts'), contents: 'export declare function mooFn(): void;'}, + {name: _('/ep/typings/func1.d.ts'), contents: 'export declare function mooFn(): void;'}, { - name: _('/typings/internal.d.ts'), + name: _('/ep/typings/internal.d.ts'), contents: `export declare class InternalClass {}\nexport declare class Class2 {}` }, - {name: _('/typings/class3.d.ts'), contents: `export declare class Class3 {}`}, + { + 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 {}'}, ]; MODULE_WITH_PROVIDERS_PROGRAM = [ @@ -2240,48 +2249,48 @@ __export(xtra_module); () => { loadTestFiles(TYPINGS_SRC_FILES); loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/src/index.js')); - const dts = makeTestBundleProgram(_('/typings/index.d.ts')); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( - bundle.program, _('/src/class1.js'), 'Class1', ts.isVariableDeclaration); + bundle.program, _('/ep/src/class1.js'), 'Class1', ts.isVariableDeclaration); const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/class1.d.ts')); + 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(_('/src/index.js')); - const dts = makeTestBundleProgram(_('/typings/index.d.ts')); - const mooFn = - getDeclaration(bundle.program, _('/src/func1.js'), 'mooFn', ts.isFunctionDeclaration); + 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', ts.isFunctionDeclaration); const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); const dtsDeclaration = host.getDtsDeclaration(mooFn); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/func1.d.ts')); + 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(_('/src/index.js')); - const dts = makeTestBundleProgram(_('/typings/index.d.ts')); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const missingClass = getDeclaration( - bundle.program, _('/src/class1.js'), 'MissingClass1', ts.isVariableDeclaration); + bundle.program, _('/ep/src/class1.js'), 'MissingClass1', ts.isVariableDeclaration); const host = 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(_('/src/index.js')); - const dts = makeTestBundleProgram(_('/typings/index.d.ts')); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const missingClass = getDeclaration( - bundle.program, _('/src/missing-class.js'), 'MissingClass2', ts.isVariableDeclaration); + bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', + ts.isVariableDeclaration); const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); expect(host.getDtsDeclaration(missingClass)).toBe(null); @@ -2291,62 +2300,91 @@ __export(xtra_module); () => { loadTestFiles(TYPINGS_SRC_FILES); loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/src/index.js')); - const dts = makeTestBundleProgram(_('/typings/index.d.ts')); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( - bundle.program, _('/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); + bundle.program, _('/ep/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/class1.d.ts')); + expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); + }); + + 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', ts.isVariableDeclaration); + const host = 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(_('/src/index.js')); - const dts = makeTestBundleProgram(_('/typings/index.d.ts')); - const class3 = getDeclaration( - bundle.program, _('/src/flat-file.js'), 'Class3', ts.isVariableDeclaration); + 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'), 'SourceClass', ts.isVariableDeclaration); const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); - const dtsDeclaration = host.getDtsDeclaration(class3); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/class3.d.ts')); + 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 find the dts file that contains a matching class declaration, even if the class is not publicly exported', + 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(_('/src/index.js')); - const dts = makeTestBundleProgram(_('/typings/index.d.ts')); - const internalClass = getDeclaration( - bundle.program, _('/src/internal.js'), 'InternalClass', ts.isVariableDeclaration); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); - const dtsDeclaration = host.getDtsDeclaration(internalClass); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/typings/internal.d.ts')); + const class2 = getDeclaration( + bundle.program, _('/ep/src/class2.js'), 'Class2', isNamedVariableDeclaration); + 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', isNamedVariableDeclaration); + 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(_('/src/index.js')); - const dts = makeTestBundleProgram(_('/typings/index.d.ts')); + const bundle = makeTestBundleProgram(_('/ep/src/index.js')); + const dts = makeTestBundleProgram(_('/ep/typings/index.d.ts')); const class2 = getDeclaration( - bundle.program, _('/src/class2.js'), 'Class2', ts.isVariableDeclaration); + bundle.program, _('/ep/src/class2.js'), 'Class2', ts.isVariableDeclaration); const internalClass2 = getDeclaration( - bundle.program, _('/src/internal.js'), 'Class2', ts.isVariableDeclaration); + bundle.program, _('/ep/src/internal.js'), 'Class2', ts.isVariableDeclaration); const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); const class2DtsDeclaration = host.getDtsDeclaration(class2); expect(class2DtsDeclaration !.getSourceFile().fileName) - .toEqual(_('/typings/class2.d.ts')); + .toEqual(_('/ep/typings/class2.d.ts')); const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); expect(internalClass2DtsDeclaration !.getSourceFile().fileName) - .toEqual(_('/typings/class2.d.ts')); + .toEqual(_('/ep/typings/internal.d.ts')); }); }); diff --git a/packages/compiler-cli/ngcc/test/rendering/dts_renderer_spec.ts b/packages/compiler-cli/ngcc/test/rendering/dts_renderer_spec.ts index 88616383a8..ac4294d51f 100644 --- a/packages/compiler-cli/ngcc/test/rendering/dts_renderer_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/dts_renderer_spec.ts @@ -114,7 +114,7 @@ runInEachFileSystem(() => { `import { Directive } from '@angular/core';\nexport class A {\n foo(x) {\n return x;\n }\n}\nA.decorators = [\n { type: Directive, args: [{ selector: '[a]' }] }\n];\n` }; INPUT_DTS_PROGRAM = { - name: _('/typings/file.d.ts'), + name: _('/node_modules/test-package/typings/file.d.ts'), contents: `export declare class A {\nfoo(x: number): number;\n}\n` }; }); @@ -126,7 +126,8 @@ runInEachFileSystem(() => { const result = renderer.renderProgram( decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses); - const typingsFile = result.find(f => f.path === _('/typings/file.d.ts')) !; + const typingsFile = + result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts')) !; expect(typingsFile.contents) .toContain( 'foo(x: number): number;\n static ɵfac: ɵngcc0.ɵɵFactoryDef;\n static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta'); @@ -139,7 +140,8 @@ runInEachFileSystem(() => { const result = renderer.renderProgram( decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses); - const typingsFile = result.find(f => f.path === _('/typings/file.d.ts')) !; + const typingsFile = + result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts')) !; expect(typingsFile.contents).toContain(`\n// ADD IMPORTS\n`); }); @@ -158,7 +160,8 @@ runInEachFileSystem(() => { const result = renderer.renderProgram( decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses); - const typingsFile = result.find(f => f.path === _('/typings/file.d.ts')) !; + const typingsFile = + result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts')) !; expect(typingsFile.contents).toContain(`\n// ADD EXPORTS\n`); }); @@ -170,7 +173,8 @@ runInEachFileSystem(() => { const result = renderer.renderProgram( decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses); - const typingsFile = result.find(f => f.path === _('/typings/file.d.ts')) !; + const typingsFile = + result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts')) !; expect(typingsFile.contents).toContain(`\n// ADD MODUlE WITH PROVIDERS PARAMS\n`); }); }); diff --git a/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts b/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts index 9396cef745..7ac27aca13 100644 --- a/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts @@ -483,7 +483,7 @@ export { D }; beforeEach(() => { MODULE_WITH_PROVIDERS_PROGRAM = [ { - name: _('/src/index.js'), + name: _('/node_modules/test-package/src/index.js'), contents: ` import {ExternalModule} from './module'; import {LibraryModule} from 'some-library'; @@ -510,7 +510,7 @@ export { D }; ` }, { - name: _('/src/module.js'), + name: _('/node_modules/test-package/src/module.js'), contents: ` export class ExternalModule { static withProviders1() { return {ngModule: ExternalModule}; } @@ -525,7 +525,7 @@ export { D }; MODULE_WITH_PROVIDERS_DTS_PROGRAM = [ { - name: _('/typings/index.d.ts'), + name: _('/node_modules/test-package/typings/index.d.ts'), contents: ` import {ModuleWithProviders} from '@angular/core'; export declare class SomeClass {} @@ -552,7 +552,7 @@ export { D }; ` }, { - name: _('/typings/module.d.ts'), + name: _('/node_modules/test-package/typings/module.d.ts'), contents: ` export interface ModuleWithProviders {} export declare class ExternalModule { @@ -575,7 +575,8 @@ export { D }; const moduleWithProvidersAnalyses = new ModuleWithProvidersAnalyzer(host, referencesRegistry, true) .analyzeProgram(bundle.src.program); - const typingsFile = getSourceFileOrError(bundle.dts !.program, _('/typings/index.d.ts')); + const typingsFile = getSourceFileOrError( + bundle.dts !.program, _('/node_modules/test-package/typings/index.d.ts')); const moduleWithProvidersInfo = moduleWithProvidersAnalyses.get(typingsFile) !; const output = new MagicString(MODULE_WITH_PROVIDERS_DTS_PROGRAM[0].contents); @@ -611,8 +612,8 @@ export { D }; const moduleWithProvidersAnalyses = new ModuleWithProvidersAnalyzer(host, referencesRegistry, true) .analyzeProgram(bundle.src.program); - const typingsFile = - getSourceFileOrError(bundle.dts !.program, _('/typings/module.d.ts')); + const typingsFile = getSourceFileOrError( + bundle.dts !.program, _('/node_modules/test-package/typings/module.d.ts')); const moduleWithProvidersInfo = moduleWithProvidersAnalyses.get(typingsFile) !; const output = new MagicString(MODULE_WITH_PROVIDERS_DTS_PROGRAM[1].contents);