diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts b/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts index 2c1be6ba8b..ebaaec59a5 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts @@ -47,6 +47,12 @@ export class LogicalFileSystem { */ private rootDirs: AbsoluteFsPath[]; + /** + * The same root directories as `rootDirs` but with each one converted to its + * canonical form for matching in case-insensitive file-systems. + */ + private canonicalRootDirs: AbsoluteFsPath[]; + /** * A cache of file paths to project paths, because computation of these paths is slightly * expensive. @@ -56,10 +62,9 @@ export class LogicalFileSystem { constructor(rootDirs: AbsoluteFsPath[], private compilerHost: ts.CompilerHost) { // Make a copy and sort it by length in reverse order (longest first). This speeds up lookups, // since there's no need to keep going through the array once a match is found. - this.rootDirs = - rootDirs.map(dir => this.compilerHost.getCanonicalFileName(dir) as AbsoluteFsPath) - .concat([]) - .sort((a, b) => b.length - a.length); + this.rootDirs = rootDirs.concat([]).sort((a, b) => b.length - a.length); + this.canonicalRootDirs = + this.rootDirs.map(dir => this.compilerHost.getCanonicalFileName(dir) as AbsoluteFsPath); } /** @@ -83,9 +88,13 @@ export class LogicalFileSystem { this.compilerHost.getCanonicalFileName(physicalFile) as AbsoluteFsPath; if (!this.cache.has(canonicalFilePath)) { let logicalFile: LogicalProjectPath|null = null; - for (const rootDir of this.rootDirs) { - if (isWithinBasePath(rootDir, canonicalFilePath)) { - logicalFile = this.createLogicalProjectPath(canonicalFilePath, rootDir); + for (let i = 0; i < this.rootDirs.length; i++) { + const rootDir = this.rootDirs[i]; + const canonicalRootDir = this.canonicalRootDirs[i]; + if (isWithinBasePath(canonicalRootDir, canonicalFilePath)) { + // Note that we match against canonical paths but then create the logical path from + // original paths. + logicalFile = this.createLogicalProjectPath(physicalFile, rootDir); // The logical project does not include any special "node_modules" nested directories. if (logicalFile.indexOf('/node_modules/') !== -1) { logicalFile = null; diff --git a/packages/compiler-cli/src/ngtsc/file_system/test/logical_spec.ts b/packages/compiler-cli/src/ngtsc/file_system/test/logical_spec.ts index 1bf4fc26b2..20fce32a16 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/test/logical_spec.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/test/logical_spec.ts @@ -52,6 +52,24 @@ runInEachFileSystem(() => { expect(nonRootFs.logicalPathOfFile(_('/test/foo/foo.ts'))) .toEqual('/foo/foo' as LogicalProjectPath); }); + + it('should maintain casing of logical paths', () => { + const fs = new LogicalFileSystem([_('/Test')], host); + expect(fs.logicalPathOfFile(_('/Test/foo/Foo.ts'))) + .toEqual('/foo/Foo' as LogicalProjectPath); + expect(fs.logicalPathOfFile(_('/Test/bar/bAR.ts'))) + .toEqual('/bar/bAR' as LogicalProjectPath); + }); + + it('should use case-sensitivity when matching rootDirs', () => { + const fs = new LogicalFileSystem([_('/Test')], host); + if (host.useCaseSensitiveFileNames()) { + expect(fs.logicalPathOfFile(_('/test/car/CAR.ts'))).toBe(null); + } else { + expect(fs.logicalPathOfFile(_('/test/car/CAR.ts'))) + .toEqual('/car/CAR' as LogicalProjectPath); + } + }); }); describe('utilities', () => { @@ -66,6 +84,12 @@ runInEachFileSystem(() => { '/foo/index' as LogicalProjectPath, '/bar/index' as LogicalProjectPath); expect(res).toEqual('../bar/index'); }); + + it('should maintain casing in relative path between logical files', () => { + const res = LogicalProjectPath.relativePathBetween( + '/fOO' as LogicalProjectPath, '/bAR' as LogicalProjectPath); + expect(res).toEqual('./bAR'); + }); }); }); });