Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changeset/small-rules-exist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
---

Internal generator fix so parallel CI typecheck/build cannot truncate `resources.generated.ts`.
35 changes: 33 additions & 2 deletions packages/ui/scripts/generate-i18n-resources.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* node scripts/generate-i18n-resources.mjs # one-shot (build / CI)
* node scripts/generate-i18n-resources.mjs --watch # watch locale dir in dev
*/
import { readdirSync, watch, writeFileSync } from 'node:fs';
import { readFileSync, readdirSync, renameSync, unlinkSync, watch, writeFileSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

Expand Down Expand Up @@ -70,10 +70,41 @@ ${resourceEntries}
export const supportedLngs = Object.keys(resources);
`;

writeFileSync(outputPath, content, 'utf8');
writeGeneratedFile(content);
console.log(`Generated ${outputPath} (${locales.length} locales: ${locales.join(', ')})`);
}

/**
* Replace resources.generated.ts without truncating it in place.
*
* CI runs UI typecheck and UI build in parallel; both call this generator.
* writeFileSync empties the destination first, so tsc can read a zero-length
* module and fail with TS2305 (no exported member 'resources' / 'supportedLngs').
* Skip the write when content is unchanged so parallel no-op runs do not race.
*/
function writeGeneratedFile(content) {
try {
if (readFileSync(outputPath, 'utf8') === content) {
return;
}
} catch {
Comment thread
Dustin-Kelley marked this conversation as resolved.
// Missing or unreadable — write a fresh copy.
}

const tempPath = `${outputPath}.${process.pid}.tmp`;
writeFileSync(tempPath, content, 'utf8');
try {
renameSync(tempPath, outputPath);
} catch (error) {
try {
unlinkSync(tempPath);
} catch {
// Best-effort cleanup; the rename error is the one to surface.
}
throw error;
}
}

function shouldRegenerate(filename) {
// macOS directory watches often omit filename; regenerate to stay safe.
if (filename == null) {
Expand Down
Loading