Skip to content

Commit 3dfc770

Browse files
petebacondarwinkara
authored andcommitted
fix(compiler-cli): ensure LogicalFileSystem maintains case in paths (angular#37008)
The work to support case-sensitivity in the `FileSystem` went too far with the `LogicalFileSystem`, which is used to compute import paths that will be added to files processed by ngtsc and ngcc. Previously all logical paths were canonicalised, which meant that on case-insensitive file-systems, the paths were all set to lower case. This resulted in incorrect imports being added to files. For example: ``` import { Apollo } from './Apollo'; import { SelectPipe } from './SelectPipe'; import * as ɵngcc0 from '@angular/core'; import * as ɵngcc1 from './selectpipe'; ``` The import from `./SelectPipe` is from the original file, while the import from `./selectpipe` is added by ngcc. This causes the TypeScript compiler to complain, or worse for paths not to be matched correctly. Now, when computing logical paths, the original absolute paths are matched against rootDirs in a canonical manner, but the actual logical path that is returned maintains it original casing. Fixes angular#36992, angular#36993, angular#37000 PR Close angular#37008
1 parent 2ca2c8f commit 3dfc770

File tree

2 files changed

+40
-7
lines changed

2 files changed

+40
-7
lines changed

packages/compiler-cli/src/ngtsc/file_system/src/logical.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ export class LogicalFileSystem {
4747
*/
4848
private rootDirs: AbsoluteFsPath[];
4949

50+
/**
51+
* The same root directories as `rootDirs` but with each one converted to its
52+
* canonical form for matching in case-insensitive file-systems.
53+
*/
54+
private canonicalRootDirs: AbsoluteFsPath[];
55+
5056
/**
5157
* A cache of file paths to project paths, because computation of these paths is slightly
5258
* expensive.
@@ -56,10 +62,9 @@ export class LogicalFileSystem {
5662
constructor(rootDirs: AbsoluteFsPath[], private compilerHost: ts.CompilerHost) {
5763
// Make a copy and sort it by length in reverse order (longest first). This speeds up lookups,
5864
// since there's no need to keep going through the array once a match is found.
59-
this.rootDirs =
60-
rootDirs.map(dir => this.compilerHost.getCanonicalFileName(dir) as AbsoluteFsPath)
61-
.concat([])
62-
.sort((a, b) => b.length - a.length);
65+
this.rootDirs = rootDirs.concat([]).sort((a, b) => b.length - a.length);
66+
this.canonicalRootDirs =
67+
this.rootDirs.map(dir => this.compilerHost.getCanonicalFileName(dir) as AbsoluteFsPath);
6368
}
6469

6570
/**
@@ -83,9 +88,13 @@ export class LogicalFileSystem {
8388
this.compilerHost.getCanonicalFileName(physicalFile) as AbsoluteFsPath;
8489
if (!this.cache.has(canonicalFilePath)) {
8590
let logicalFile: LogicalProjectPath|null = null;
86-
for (const rootDir of this.rootDirs) {
87-
if (isWithinBasePath(rootDir, canonicalFilePath)) {
88-
logicalFile = this.createLogicalProjectPath(canonicalFilePath, rootDir);
91+
for (let i = 0; i < this.rootDirs.length; i++) {
92+
const rootDir = this.rootDirs[i];
93+
const canonicalRootDir = this.canonicalRootDirs[i];
94+
if (isWithinBasePath(canonicalRootDir, canonicalFilePath)) {
95+
// Note that we match against canonical paths but then create the logical path from
96+
// original paths.
97+
logicalFile = this.createLogicalProjectPath(physicalFile, rootDir);
8998
// The logical project does not include any special "node_modules" nested directories.
9099
if (logicalFile.indexOf('/node_modules/') !== -1) {
91100
logicalFile = null;

packages/compiler-cli/src/ngtsc/file_system/test/logical_spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,24 @@ runInEachFileSystem(() => {
5252
expect(nonRootFs.logicalPathOfFile(_('/test/foo/foo.ts')))
5353
.toEqual('/foo/foo' as LogicalProjectPath);
5454
});
55+
56+
it('should maintain casing of logical paths', () => {
57+
const fs = new LogicalFileSystem([_('/Test')], host);
58+
expect(fs.logicalPathOfFile(_('/Test/foo/Foo.ts')))
59+
.toEqual('/foo/Foo' as LogicalProjectPath);
60+
expect(fs.logicalPathOfFile(_('/Test/bar/bAR.ts')))
61+
.toEqual('/bar/bAR' as LogicalProjectPath);
62+
});
63+
64+
it('should use case-sensitivity when matching rootDirs', () => {
65+
const fs = new LogicalFileSystem([_('/Test')], host);
66+
if (host.useCaseSensitiveFileNames()) {
67+
expect(fs.logicalPathOfFile(_('/test/car/CAR.ts'))).toBe(null);
68+
} else {
69+
expect(fs.logicalPathOfFile(_('/test/car/CAR.ts')))
70+
.toEqual('/car/CAR' as LogicalProjectPath);
71+
}
72+
});
5573
});
5674

5775
describe('utilities', () => {
@@ -66,6 +84,12 @@ runInEachFileSystem(() => {
6684
'/foo/index' as LogicalProjectPath, '/bar/index' as LogicalProjectPath);
6785
expect(res).toEqual('../bar/index');
6886
});
87+
88+
it('should maintain casing in relative path between logical files', () => {
89+
const res = LogicalProjectPath.relativePathBetween(
90+
'/fOO' as LogicalProjectPath, '/bAR' as LogicalProjectPath);
91+
expect(res).toEqual('./bAR');
92+
});
6993
});
7094
});
7195
});

0 commit comments

Comments
 (0)