fix(ivy): ngtsc fails building flat module out on windows (#27993)

`ngtsc` currently fails building a flat module out file on Windows because it generates an invalid flat module TypeScript source file. e.g:

```ts
5 export * from './C:\Users\Paul\Desktop\test\src\export';
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```

This is because `path.posix.relative` does not properly with non-posix paths, and only expects posix paths in order to work.

PR Close #27993
This commit is contained in:
Paul Gschwendtner 2019-01-12 19:00:39 +01:00 committed by Alex Rickabaugh
parent d336bff200
commit 070fca1591
5 changed files with 52 additions and 10 deletions

View File

@ -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.
*/

View File

@ -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>): string|null {
@ -13,22 +14,24 @@ export function findFlatIndexEntryPoint(rootFiles: ReadonlyArray<string>): 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;
}

View File

@ -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'); });
});
});

View File

@ -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);
}

View File

@ -1610,6 +1610,17 @@ describe('ngtsc behavioral tests', () => {
expect(dtsContents).toContain('/// <amd-module name="@mymodule" />');
});
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'});