Files
cinny/src/app/utils/caseCollision.test.ts
T

44 lines
1.7 KiB
TypeScript
Raw Normal View History

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<string, string>();
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')}`,
);
});