Compare commits

...

2 Commits

Author SHA1 Message Date
jared 7f960b026b fix(build): complete the threadSummary rename — remove the old casing
CI / Build & Quality Checks (push) Successful in 10m44s
CI / Trigger Desktop Build (push) Successful in 7s
The deletions from the git-mv in 992d2b83 were unstaged by a concurrent
worktree operation before commit, so the pushed tree contained BOTH
threadSummary.ts and threadSummaryData.ts (and the Windows case-collision
persisted). This commit removes the stale originals; caseCollision.test.ts
would have failed CI on the incomplete state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 23:44:59 -04:00
jared 992d2b83b3 fix(build): rename threadSummary.ts — case-collision broke the Windows release
CI / Build & Quality Checks (push) Failing after 5m22s
CI / Trigger Desktop Build (push) Has been skipped
threadSummary.ts (pure helpers) and ThreadSummary.tsx (chip component) lived in
the same directory differing only by case. On the case-insensitive Windows
release runner, RoomTimeline's extensionless import of ./thread/ThreadSummary
resolved .ts BEFORE .tsx and matched the helper module → rolldown
MISSING_EXPORT "ThreadSummary" — invisible on every Linux/macOS build (and the
cause of the earlier masked pdf.worker failure). Helper module renamed to
threadSummaryData.ts (+ test), 3 importers updated.

Prevention: new caseCollision.test.ts walks src/ and fails on any same-directory
names differing only by case (extensionless compare, so Foo.tsx vs foo.ts is
caught) — verified it fails on the pre-rename tree. Runs in the hard CI gate.

Gates: tsc clean, eslint/prettier clean, build OK, 658/659 tests (1 IDB skip).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 23:43:20 -04:00
5 changed files with 46 additions and 3 deletions
@@ -1,7 +1,7 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { EventStatus, MatrixEvent, RelationType } from 'matrix-js-sdk';
import { getThreadSummary, isPendingThreadReply } from './threadSummary';
import { getThreadSummary, isPendingThreadReply } from './threadSummaryData';
// getThreadSummary reads either the live Thread (preferred) or the
// server-aggregated `m.thread` bundle. We stub only the members it touches and
+1 -1
View File
@@ -12,7 +12,7 @@ import {
ThreadEvent,
} from 'matrix-js-sdk';
import { getLinkedTimelines } from '../RoomTimeline';
import { isPendingThreadReply } from './threadSummary';
import { isPendingThreadReply } from './threadSummaryData';
/**
* Resolve (or bootstrap) the live {@link Thread} for a root event.
+1 -1
View File
@@ -8,7 +8,7 @@ import {
RoomEventHandlerMap,
ThreadEvent,
} from 'matrix-js-sdk';
import { getThreadSummary, ThreadSummaryData } from '../features/room/thread/threadSummary';
import { getThreadSummary, ThreadSummaryData } from '../features/room/thread/threadSummaryData';
import { threadNotificationsAtom } from '../state/threadNotifications';
import { getThreadNotificationMode, ThreadNotificationMode } from '../utils/threadNotifications';
+43
View File
@@ -0,0 +1,43 @@
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')}`,
);
});