import { strict as assert } from 'node:assert'; import { test } from 'node:test'; import { readdirSync } from 'node:fs'; import { join } from 'node:path'; /** * Guard against same-directory filenames that differ only by case (e.g. * `threadSummary.ts` vs `ThreadSummary.tsx`). On case-insensitive filesystems * (the Windows release runner) an extensionless import of one can resolve to * the OTHER file — rolldown tries `.ts` before `.tsx` — producing * MISSING_EXPORT failures that never reproduce on the Linux/macOS machines the * project is developed and web-deployed on. This broke the desktop release * build twice before being diagnosed; this test makes the collision a local, * immediate failure instead. */ const findCaseCollisions = (dir: string, collisions: string[]): void => { const entries = readdirSync(dir, { withFileTypes: true }); const seen = new Map(); entries.forEach((entry) => { // Compare basenames without extension: `Foo.tsx` collides with `foo.ts` // because module resolution is extensionless. const stem = entry.isDirectory() ? entry.name : entry.name.replace(/\.[^.]+$/, ''); const key = stem.toLowerCase(); const existing = seen.get(key); if (existing !== undefined && existing !== stem) { collisions.push(`${dir}: "${existing}" vs "${stem}"`); } if (existing === undefined) seen.set(key, stem); if (entry.isDirectory()) { findCaseCollisions(join(dir, entry.name), collisions); } }); }; test('no same-directory filenames differing only by case under src/', () => { const collisions: string[] = []; findCaseCollisions('src', collisions); assert.deepEqual( collisions, [], `Case-colliding names break Windows builds:\n${collisions.join('\n')}`, ); });