diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts
index 39efe7eb0522e..3c65e4410bdbd 100644
--- a/src/compiler/moduleNameResolver.ts
+++ b/src/compiler/moduleNameResolver.ts
@@ -392,10 +392,12 @@ namespace ts {
if (resolved) {
const { fileName, packageId } = resolved;
const resolvedFileName = options.preserveSymlinks ? fileName : realPath(fileName, host, traceEnabled);
+ const pathsAreEqual = arePathsEqual(fileName, resolvedFileName, host);
resolvedTypeReferenceDirective = {
primary,
- resolvedFileName,
- originalPath: arePathsEqual(fileName, resolvedFileName, host) ? undefined : fileName,
+ // If the fileName and realpath are differing only in casing prefer fileName so that we can issue correct errors for casing under forceConsistentCasingInFileNames
+ resolvedFileName: pathsAreEqual ? fileName : resolvedFileName,
+ originalPath: pathsAreEqual ? undefined : fileName,
packageId,
isExternalLibraryImport: pathContainsNodeModules(fileName),
};
@@ -1406,8 +1408,10 @@ namespace ts {
let resolvedValue = resolved.value;
if (!compilerOptions.preserveSymlinks && resolvedValue && !resolvedValue.originalPath) {
const path = realPath(resolvedValue.path, host, traceEnabled);
- const originalPath = arePathsEqual(path, resolvedValue.path, host) ? undefined : resolvedValue.path;
- resolvedValue = { ...resolvedValue, path, originalPath };
+ const pathsAreEqual = arePathsEqual(path, resolvedValue.path, host);
+ const originalPath = pathsAreEqual ? undefined : resolvedValue.path;
+ // If the path and realpath are differing only in casing prefer path so that we can issue correct errors for casing under forceConsistentCasingInFileNames
+ resolvedValue = { ...resolvedValue, path: pathsAreEqual ? resolvedValue.path : path, originalPath };
}
// For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files.
return { value: resolvedValue && { resolved: resolvedValue, isExternalLibraryImport: true } };
diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json
index 1bd159038861d..02cd25cb7dadb 100644
--- a/src/testRunner/tsconfig.json
+++ b/src/testRunner/tsconfig.json
@@ -154,6 +154,7 @@
"unittests/tsc/cancellationToken.ts",
"unittests/tsc/composite.ts",
"unittests/tsc/declarationEmit.ts",
+ "unittests/tsc/forceConsistentCasingInFileNames.ts",
"unittests/tsc/incremental.ts",
"unittests/tsc/listFilesOnly.ts",
"unittests/tsc/projectReferences.ts",
diff --git a/src/testRunner/unittests/tsc/forceConsistentCasingInFileNames.ts b/src/testRunner/unittests/tsc/forceConsistentCasingInFileNames.ts
new file mode 100644
index 0000000000000..8718af088df8a
--- /dev/null
+++ b/src/testRunner/unittests/tsc/forceConsistentCasingInFileNames.ts
@@ -0,0 +1,18 @@
+namespace ts {
+ describe("unittests:: tsc:: forceConsistentCasingInFileNames::", () => {
+ verifyTsc({
+ scenario: "forceConsistentCasingInFileNames",
+ subScenario: "with relative and non relative file resolutions",
+ commandLineArgs: ["/src/project/src/struct.d.ts", "--forceConsistentCasingInFileNames", "--explainFiles"],
+ fs: () => loadProjectFromFiles({
+ "/src/project/src/struct.d.ts": Utils.dedent`
+ import * as xs1 from "fp-ts/lib/Struct";
+ import * as xs2 from "fp-ts/lib/struct";
+ import * as xs3 from "./Struct";
+ import * as xs4 from "./struct";
+ `,
+ "/src/project/node_modules/fp-ts/lib/struct.d.ts": `export function foo(): void`,
+ }),
+ });
+ });
+}
\ No newline at end of file
diff --git a/src/testRunner/unittests/tscWatch/forceConsistentCasingInFileNames.ts b/src/testRunner/unittests/tscWatch/forceConsistentCasingInFileNames.ts
index 60a989f2fa397..69ad855b626df 100644
--- a/src/testRunner/unittests/tscWatch/forceConsistentCasingInFileNames.ts
+++ b/src/testRunner/unittests/tscWatch/forceConsistentCasingInFileNames.ts
@@ -113,7 +113,7 @@ export const Fragment: unique symbol;
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({
compilerOptions: { jsx: "react-jsx", jsxImportSource: "react", forceConsistentCasingInFileNames: true },
- files: ["node_modules/react/jsx-Runtime/index.d.ts", "index.tsx"] // NB: casing does not match disk
+ files: ["node_modules/react/Jsx-Runtime/index.d.ts", "index.tsx"]
})
}
], { currentDirectory: projectRoot }),
diff --git a/tests/baselines/reference/tsc/forceConsistentCasingInFileNames/with-relative-and-non-relative-file-resolutions.js b/tests/baselines/reference/tsc/forceConsistentCasingInFileNames/with-relative-and-non-relative-file-resolutions.js
new file mode 100644
index 0000000000000..1d71a121bc001
--- /dev/null
+++ b/tests/baselines/reference/tsc/forceConsistentCasingInFileNames/with-relative-and-non-relative-file-resolutions.js
@@ -0,0 +1,72 @@
+Input::
+//// [/lib/lib.d.ts]
+///
+interface Boolean {}
+interface Function {}
+interface CallableFunction {}
+interface NewableFunction {}
+interface IArguments {}
+interface Number { toExponential: any; }
+interface Object {}
+interface RegExp {}
+interface String { charAt: any; }
+interface Array { length: number; [n: number]: T; }
+interface ReadonlyArray {}
+declare const console: { log(msg: any): void; };
+
+//// [/src/project/node_modules/fp-ts/lib/struct.d.ts]
+export function foo(): void
+
+//// [/src/project/src/struct.d.ts]
+import * as xs1 from "fp-ts/lib/Struct";
+import * as xs2 from "fp-ts/lib/struct";
+import * as xs3 from "./Struct";
+import * as xs4 from "./struct";
+
+
+
+
+Output::
+/lib/tsc /src/project/src/struct.d.ts --forceConsistentCasingInFileNames --explainFiles
+[96msrc/project/src/struct.d.ts[0m:[93m2[0m:[93m22[0m - [91merror[0m[90m TS1149: [0mFile name '/src/project/node_modules/fp-ts/lib/struct.d.ts' differs from already included file name '/src/project/node_modules/fp-ts/lib/Struct.d.ts' only in casing.
+ The file is in the program because:
+ Imported via "fp-ts/lib/Struct" from file '/src/project/src/struct.d.ts'
+ Imported via "fp-ts/lib/struct" from file '/src/project/src/struct.d.ts'
+
+[7m2[0m import * as xs2 from "fp-ts/lib/struct";
+[7m [0m [91m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/project/src/struct.d.ts[0m:[93m1[0m:[93m22[0m
+ [7m1[0m import * as xs1 from "fp-ts/lib/Struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+ File is included via import here.
+
+[96msrc/project/src/struct.d.ts[0m:[93m3[0m:[93m22[0m - [91merror[0m[90m TS1149: [0mFile name '/src/project/src/Struct.d.ts' differs from already included file name '/src/project/src/struct.d.ts' only in casing.
+ The file is in the program because:
+ Root file specified for compilation
+ Imported via "./Struct" from file '/src/project/src/struct.d.ts'
+ Imported via "./struct" from file '/src/project/src/struct.d.ts'
+
+[7m3[0m import * as xs3 from "./Struct";
+[7m [0m [91m ~~~~~~~~~~[0m
+
+ [96msrc/project/src/struct.d.ts[0m:[93m4[0m:[93m22[0m
+ [7m4[0m import * as xs4 from "./struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+ File is included via import here.
+
+lib/lib.d.ts
+ Default library for target 'es3'
+src/project/node_modules/fp-ts/lib/Struct.d.ts
+ Imported via "fp-ts/lib/Struct" from file 'src/project/src/struct.d.ts'
+ Imported via "fp-ts/lib/struct" from file 'src/project/src/struct.d.ts'
+src/project/src/struct.d.ts
+ Root file specified for compilation
+ Imported via "./Struct" from file 'src/project/src/struct.d.ts'
+ Imported via "./struct" from file 'src/project/src/struct.d.ts'
+
+Found 2 errors in the same file, starting at: src/project/src/struct.d.ts[90m:2[0m
+
+exitCode:: ExitStatus.DiagnosticsPresent_OutputsGenerated
+
+
diff --git a/tests/baselines/reference/tscWatch/forceConsistentCasingInFileNames/jsxImportSource-option-changed.js b/tests/baselines/reference/tscWatch/forceConsistentCasingInFileNames/jsxImportSource-option-changed.js
index 5fbffca425c7a..c820b4469c832 100644
--- a/tests/baselines/reference/tscWatch/forceConsistentCasingInFileNames/jsxImportSource-option-changed.js
+++ b/tests/baselines/reference/tscWatch/forceConsistentCasingInFileNames/jsxImportSource-option-changed.js
@@ -33,7 +33,7 @@ export const Fragment: unique symbol;
export const App = () => ;
//// [/user/username/projects/myproject/tsconfig.json]
-{"compilerOptions":{"jsx":"react-jsx","jsxImportSource":"react","forceConsistentCasingInFileNames":true},"files":["node_modules/react/jsx-Runtime/index.d.ts","index.tsx"]}
+{"compilerOptions":{"jsx":"react-jsx","jsxImportSource":"react","forceConsistentCasingInFileNames":true},"files":["node_modules/react/Jsx-Runtime/index.d.ts","index.tsx"]}
/a/lib/tsc.js --w --p . --explainFiles
@@ -41,19 +41,19 @@ Output::
>> Screen clear
[[90m12:00:31 AM[0m] Starting compilation in watch mode...
-[91merror[0m[90m TS1149: [0mFile name '/user/username/projects/myproject/node_modules/react/Jsx-runtime/index.d.ts' differs from already included file name '/user/username/projects/myproject/node_modules/react/jsx-Runtime/index.d.ts' only in casing.
+[91merror[0m[90m TS1149: [0mFile name '/user/username/projects/myproject/node_modules/react/jsx-runtime/index.d.ts' differs from already included file name '/user/username/projects/myproject/node_modules/react/Jsx-Runtime/index.d.ts' only in casing.
The file is in the program because:
Part of 'files' list in tsconfig.json
Imported via "react/jsx-runtime" from file '/user/username/projects/myproject/index.tsx' with packageId 'react/jsx-runtime/index.d.ts@0.0.1' to import 'jsx' and 'jsxs' factory functions
[96mtsconfig.json[0m:[93m1[0m:[93m115[0m
- [7m1[0m {"compilerOptions":{"jsx":"react-jsx","jsxImportSource":"react","forceConsistentCasingInFileNames":true},"files":["node_modules/react/jsx-Runtime/index.d.ts","index.tsx"]}
+ [7m1[0m {"compilerOptions":{"jsx":"react-jsx","jsxImportSource":"react","forceConsistentCasingInFileNames":true},"files":["node_modules/react/Jsx-Runtime/index.d.ts","index.tsx"]}
[7m [0m [96m ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[0m
File is matched by 'files' list specified here.
../../../../a/lib/lib.d.ts
Default library for target 'es3'
-node_modules/react/jsx-Runtime/index.d.ts
+node_modules/react/Jsx-Runtime/index.d.ts
Part of 'files' list in tsconfig.json
Imported via "react/jsx-runtime" from file 'index.tsx' with packageId 'react/jsx-runtime/index.d.ts@0.0.1' to import 'jsx' and 'jsxs' factory functions
index.tsx
@@ -62,12 +62,12 @@ index.tsx
-Program root files: ["/user/username/projects/myproject/node_modules/react/jsx-Runtime/index.d.ts","/user/username/projects/myproject/index.tsx"]
+Program root files: ["/user/username/projects/myproject/node_modules/react/Jsx-Runtime/index.d.ts","/user/username/projects/myproject/index.tsx"]
Program options: {"jsx":4,"jsxImportSource":"react","forceConsistentCasingInFileNames":true,"watch":true,"project":"/user/username/projects/myproject","explainFiles":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
Program structureReused: Not
Program files::
/a/lib/lib.d.ts
-/user/username/projects/myproject/node_modules/react/jsx-Runtime/index.d.ts
+/user/username/projects/myproject/node_modules/react/Jsx-Runtime/index.d.ts
/user/username/projects/myproject/index.tsx
No cached semantic diagnostics in the builder::
@@ -81,7 +81,7 @@ WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/node_modules/react/jsx-runtime/index.d.ts:
- {"fileName":"/user/username/projects/myproject/node_modules/react/jsx-Runtime/index.d.ts","pollingInterval":250}
+ {"fileName":"/user/username/projects/myproject/node_modules/react/Jsx-Runtime/index.d.ts","pollingInterval":250}
/user/username/projects/myproject/index.tsx:
{"fileName":"/user/username/projects/myproject/index.tsx","pollingInterval":250}
/a/lib/lib.d.ts: