diff --git a/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts b/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts index cb01e6a60f..ef1eaa614a 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts +++ b/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts @@ -12,6 +12,7 @@ import * as path from 'path'; import * as ts from 'typescript'; import {ShimGenerator} from '../../shims'; +import {relativePathBetween} from '../../util/src/path'; export class FlatIndexGenerator implements ShimGenerator { readonly flatIndexPath: string; @@ -27,10 +28,7 @@ export class FlatIndexGenerator implements ShimGenerator { recognize(fileName: string): boolean { return fileName === this.flatIndexPath; } generate(): ts.SourceFile { - const relativeEntryPoint = './' + - path.posix.relative(path.posix.dirname(this.flatIndexPath), this.entryPoint) - .replace(/\.tsx?$/, ''); - + const relativeEntryPoint = relativePathBetween(this.flatIndexPath, this.entryPoint); const contents = `/** * Generated bundle index. Do not edit. */ diff --git a/packages/compiler-cli/src/ngtsc/entry_point/src/logic.ts b/packages/compiler-cli/src/ngtsc/entry_point/src/logic.ts index de233eda36..5bfa55d56b 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/src/logic.ts +++ b/packages/compiler-cli/src/ngtsc/entry_point/src/logic.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {normalizeSeparators} from '../../util/src/path'; import {isNonDeclarationTsPath} from '../../util/src/typescript'; export function findFlatIndexEntryPoint(rootFiles: ReadonlyArray): string|null { @@ -13,22 +14,24 @@ export function findFlatIndexEntryPoint(rootFiles: ReadonlyArray): strin // 1) if it's the only file!!!!!! // 2) (deprecated) if it's named 'index.ts' and has the shortest path of all such files. const tsFiles = rootFiles.filter(file => isNonDeclarationTsPath(file)); + let resolvedEntryPoint: string|null = null; + if (tsFiles.length === 1) { // There's only one file - this is the flat module index. - return tsFiles[0]; + resolvedEntryPoint = tsFiles[0]; } else { // In the event there's more than one TS file, one of them can still be selected as the // flat module index if it's named 'index.ts'. If there's more than one 'index.ts', the one // with the shortest path wins. // // This behavior is DEPRECATED and only exists to support existing usages. - let indexFile: string|null = null; for (const tsFile of tsFiles) { if (tsFile.endsWith('/index.ts') && - (indexFile === null || tsFile.length <= indexFile.length)) { - indexFile = tsFile; + (resolvedEntryPoint === null || tsFile.length <= resolvedEntryPoint.length)) { + resolvedEntryPoint = tsFile; } } - return indexFile; } + + return resolvedEntryPoint ? normalizeSeparators(resolvedEntryPoint) : null; } diff --git a/packages/compiler-cli/src/ngtsc/entry_point/test/entry_point_spec.ts b/packages/compiler-cli/src/ngtsc/entry_point/test/entry_point_spec.ts new file mode 100644 index 0000000000..0c2931fd19 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/entry_point/test/entry_point_spec.ts @@ -0,0 +1,28 @@ + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {findFlatIndexEntryPoint} from '../src/logic'; + +describe('entry_point logic', () => { + + describe('findFlatIndexEntryPoint', () => { + + it('should use the only source file if only a single one is specified', + () => { expect(findFlatIndexEntryPoint(['/src/index.ts'])).toBe('/src/index.ts'); }); + + it('should use the shortest source file ending with "index.ts" for multiple files', () => { + expect(findFlatIndexEntryPoint([ + '/src/deep/index.ts', '/src/index.ts', '/index.ts' + ])).toBe('/index.ts'); + }); + + it('should normalize the path separators for the found entry point', + () => { expect(findFlatIndexEntryPoint(['\\src\\index.ts'])).toBe('/src/index.ts'); }); + }); +}); diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index 7cdb18d0cd..30926d17aa 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -24,6 +24,7 @@ import {FactoryGenerator, FactoryInfo, GeneratedShimsHostWrapper, ShimGenerator, import {ivySwitchTransform} from './switch'; import {IvyCompilation, ivyTransformFactory} from './transform'; import {TypeCheckContext, TypeCheckProgramHost} from './typecheck'; +import {normalizeSeparators} from './util/src/path'; import {isDtsPath} from './util/src/typescript'; export class NgtscProgram implements api.Program { @@ -107,8 +108,9 @@ export class NgtscProgram implements api.Program { }); } else { const flatModuleId = options.flatModuleId || null; + const flatModuleOutFile = normalizeSeparators(options.flatModuleOutFile); this.flatIndexGenerator = - new FlatIndexGenerator(entryPoint, options.flatModuleOutFile, flatModuleId); + new FlatIndexGenerator(entryPoint, flatModuleOutFile, flatModuleId); generators.push(this.flatIndexGenerator); rootFiles.push(this.flatIndexGenerator.flatIndexPath); } diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index befe6e253a..cff67d3790 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -1610,6 +1610,17 @@ describe('ngtsc behavioral tests', () => { expect(dtsContents).toContain('/// '); }); + it('should generate a proper flat module index file when nested', () => { + env.tsconfig({ + 'flatModuleOutFile': './public-api/index.js', + }); + + env.write('test.ts', `export const SOME_EXPORT = 'some-export'`); + env.driveMain(); + + expect(env.getContents('./public-api/index.js')).toContain(`export * from '../test';`); + }); + it('should report an error when a flat module index is requested but no entrypoint can be determined', () => { env.tsconfig({'flatModuleOutFile': 'flat.js'});