From adee3ab249ab451436cc7acdfb4e2f399832bc86 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sat, 3 Jan 2026 17:48:35 +0100 Subject: [PATCH 1/4] Rewatch: check if filename case for interface and implementation matches --- rewatch/src/build/packages.rs | 14 ++++++++++++++ tests/build_tests/case/input.js | 14 ++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/rewatch/src/build/packages.rs b/rewatch/src/build/packages.rs index da6e585ec1..f01fba1897 100644 --- a/rewatch/src/build/packages.rs +++ b/rewatch/src/build/packages.rs @@ -811,6 +811,20 @@ pub fn parse_packages(build_state: &mut BuildState) -> Result<()> { }; match source_files.get(&implementation_filename) { None => { + if let Some(implementation_path) = source_files.keys().find(|path| { + let extension = path.extension().and_then(|ext| ext.to_str()); + matches!(extension, Some(ext) if helpers::is_implementation_file(ext)) + && helpers::file_path_to_module_name(path, &namespace) == module_name + }) { + let implementation_display = + implementation_path.to_string_lossy().to_string(); + let interface_display = file.to_string_lossy().to_string(); + return Err(anyhow!( + "Implementation and interface have different path names or different cases: `{}` vs `{}`", + implementation_display, + interface_display + )); + } println!( "{} No implementation file found for interface file (skipping): {}", LINE_CLEAR, diff --git a/tests/build_tests/case/input.js b/tests/build_tests/case/input.js index 21f0c94547..e8bc3e0bb1 100644 --- a/tests/build_tests/case/input.js +++ b/tests/build_tests/case/input.js @@ -2,18 +2,16 @@ import * as assert from "node:assert"; import { setup } from "#dev/process"; import { normalizeNewlines } from "#dev/utils"; -const { execBuildLegacy } = setup(import.meta.dirname); +const { execBuild } = setup(import.meta.dirname); -const { stderr } = await execBuildLegacy(); +const { stdout } = await execBuild(); if ( ![ - "Error: Invalid rescript.json: implementation and interface have different path names or different cases src/demo vs src/Demo\n", + "Could not initialize build: Implementation and interface have different path names or different cases: `src/demo.res` vs `src/Demo.resi`\n", // Windows: path separator - "Error: Invalid rescript.json: implementation and interface have different path names or different cases src\\demo vs src\\Demo\n", - // Linux: files are parsed in different order - "Error: Invalid rescript.json: implementation and interface have different path names or different cases src/Demo vs src/demo\n", - ].includes(normalizeNewlines(stderr)) + "Could not initialize build: Implementation and interface have different path names or different cases: `src\\demo.res` vs `src\\Demo.resi`\n", + ].includes(normalizeNewlines(stdout)) ) { - assert.fail(stderr); + assert.fail(stdout); } From 832d37fd957e7882e06038bf371faf0503cccf4e Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sat, 3 Jan 2026 22:06:22 +0100 Subject: [PATCH 2/4] CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a598484ea..803ecdffe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ #### :nail_care: Polish - Rewatch: enable `--create-sourcedirs` by default (now deprecated when explicitly used). https://github.com/rescript-lang/rescript/pull/8092 +- Rewatch: check if filename case for interface and implementation matches. https://github.com/rescript-lang/rescript/pull/8144 #### :house: Internal From aed57c785cbb1317192b14c811b890430345d645 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sun, 4 Jan 2026 08:38:37 +0100 Subject: [PATCH 3/4] Another case test --- tests/build_tests/case2/input.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/build_tests/case2/input.js b/tests/build_tests/case2/input.js index 1a44c8a5ec..020e796e64 100644 --- a/tests/build_tests/case2/input.js +++ b/tests/build_tests/case2/input.js @@ -4,18 +4,16 @@ import * as assert from "node:assert"; import { setup } from "#dev/process"; import { normalizeNewlines } from "#dev/utils"; -const { execBuildLegacy } = setup(import.meta.dirname); +const { execBuild } = setup(import.meta.dirname); -const { stderr } = await execBuildLegacy(); +const { stdout } = await execBuild(); if ( ![ - "Error: Invalid rescript.json: implementation and interface have different path names or different cases src/X vs src/x\n", + "Could not initialize build: Implementation and interface have different path names or different cases: `src/X.res` vs `src/x.resi`\n", // Windows: path separator - "Error: Invalid rescript.json: implementation and interface have different path names or different cases src\\X vs src\\x\n", - // Linux: files are parsed in different order - "Error: Invalid rescript.json: implementation and interface have different path names or different cases src/x vs src/X\n", - ].includes(normalizeNewlines(stderr)) + "Could not initialize build: Implementation and interface have different path names or different cases: `src\\X.res` vs `src\\x.resi`\n", + ].includes(normalizeNewlines(stdout)) ) { - assert.fail(stderr); + assert.fail(stdout); } From 7b3a81d203e4c5992738653b0b75c509249893e8 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Tue, 6 Jan 2026 08:53:29 +0100 Subject: [PATCH 4/4] stderr after all --- tests/build_tests/case/input.js | 6 +++--- tests/build_tests/case2/input.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/build_tests/case/input.js b/tests/build_tests/case/input.js index e8bc3e0bb1..c1410ad416 100644 --- a/tests/build_tests/case/input.js +++ b/tests/build_tests/case/input.js @@ -4,14 +4,14 @@ import { normalizeNewlines } from "#dev/utils"; const { execBuild } = setup(import.meta.dirname); -const { stdout } = await execBuild(); +const { stderr } = await execBuild(); if ( ![ "Could not initialize build: Implementation and interface have different path names or different cases: `src/demo.res` vs `src/Demo.resi`\n", // Windows: path separator "Could not initialize build: Implementation and interface have different path names or different cases: `src\\demo.res` vs `src\\Demo.resi`\n", - ].includes(normalizeNewlines(stdout)) + ].includes(normalizeNewlines(stderr)) ) { - assert.fail(stdout); + assert.fail(stderr); } diff --git a/tests/build_tests/case2/input.js b/tests/build_tests/case2/input.js index 020e796e64..efafd16275 100644 --- a/tests/build_tests/case2/input.js +++ b/tests/build_tests/case2/input.js @@ -6,14 +6,14 @@ import { normalizeNewlines } from "#dev/utils"; const { execBuild } = setup(import.meta.dirname); -const { stdout } = await execBuild(); +const { stderr } = await execBuild(); if ( ![ "Could not initialize build: Implementation and interface have different path names or different cases: `src/X.res` vs `src/x.resi`\n", // Windows: path separator "Could not initialize build: Implementation and interface have different path names or different cases: `src\\X.res` vs `src\\x.resi`\n", - ].includes(normalizeNewlines(stdout)) + ].includes(normalizeNewlines(stderr)) ) { - assert.fail(stdout); + assert.fail(stderr); }