Files
ps5upload/scripts/build-console-strings.mjs
T
phantomptrandClaude Opus 5.5 99dda9acb8 console page: build, shell, controller navigation
The page the helper serves on the PS5 itself: client/src/console/ built by
vite.console.config.ts into one self-contained dist-console/index.html
(script and styles inlined, safari13, 253 KiB raw / 84.5 KiB gzip; the
build fails over 450 KiB raw or 150 KiB gzip). payload/Makefile gzips it
into build/webui_page.c and the helper serves it at GET /, with
GET /api/token for the per-start token.

- Shell: top bar (logo, console chip from /api/status), rail of the
  implemented sections (Home, Files, Settings placeholders), hint bar
  showing only verified bindings, reload banner on a 401.
- nav.ts: spatial focus (aligned neighbours first), the R11 default input
  table (keyboard + Gamepad "standard", all unverified), D-pad repeat.
- api.ts: token fetched once, X-PS5U-Token on every POST, 401 → expired.
- strings.ts: cw_* keys from all 21 locales via build-console-strings.mjs
  (vm-eval like the i18n gate), English fallback.
- CI: client npm ci before every make payload.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RxCGb8Asp1A1AQYiv1kbgS
2026-10-17 05:23:26 -07:00

77 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/*
* The console web page's translations: every `cw_*` key from all 21 locale
* files under client/src/i18n/locales/, written to
* client/src/console/strings.gen.json as { "<locale>": { "cw_…": "…" } }.
*
* The page (client/src/console/strings.ts) imports that JSON instead of the
* PC app's locale modules, so it ships only its own strings. `npm run
* build:console` runs this first; the file is committed so typecheck and
* tests work without a build, and strings.test.ts fails when it is stale.
*
* Locale files are read the way scripts/i18n-coverage.mjs reads them: the
* `const <id>: Translations = { … }` literal evaluated in an isolated vm
* context, never regex-parsed for values. Keep the matcher in sync with that
* script if the locale-file shape changes.
*
* Usage: node scripts/build-console-strings.mjs [--check]
* --check exit 1 when the committed file differs, write nothing.
*/
import fs from "node:fs";
import path from "node:path";
import vm from "node:vm";
import { fileURLToPath } from "node:url";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const LOCALES_DIR = path.join(repoRoot, "client/src/i18n/locales");
const OUT_PATH = path.join(repoRoot, "client/src/console/strings.gen.json");
const PREFIX = "cw_";
function loadLocale(fp) {
const src = fs.readFileSync(fp, "utf8");
const startMatch = src.match(/^const\s+[A-Za-z_][A-Za-z0-9_]*\s*:\s*Translations\s*=\s*/m);
if (!startMatch) throw new Error(`${fp}: could not find 'const <name>: Translations = ' declaration`);
const startIdx = startMatch.index + startMatch[0].length;
const endMatch = src.slice(startIdx).match(/\n};\s*\n\s*export default/);
if (!endMatch) throw new Error(`${fp}: could not find end of literal (};\\nexport default)`);
const literalSrc = src.slice(startIdx, startIdx + endMatch.index + "\n}".length);
return vm.runInNewContext(`(${literalSrc})`, Object.create(null), { timeout: 1000 });
}
/** { locale: { cw_key: value } }, locales and keys sorted, English required. */
export function extractConsoleStrings() {
const files = fs
.readdirSync(LOCALES_DIR)
.filter((f) => f.endsWith(".ts") && !f.endsWith(".d.ts"))
.sort();
const out = {};
for (const f of files) {
const dict = loadLocale(path.join(LOCALES_DIR, f));
const picked = {};
for (const key of Object.keys(dict).sort()) {
if (key.startsWith(PREFIX)) picked[key] = dict[key];
}
out[f.replace(/\.ts$/, "")] = picked;
}
if (!out.en) throw new Error(`${LOCALES_DIR} has no en.ts`);
return out;
}
function main() {
const text = `${JSON.stringify(extractConsoleStrings(), null, 2)}\n`;
const old = fs.existsSync(OUT_PATH) ? fs.readFileSync(OUT_PATH, "utf8") : null;
if (process.argv.includes("--check")) {
if (old !== text) {
console.error(`${path.relative(repoRoot, OUT_PATH)} is stale: run node scripts/build-console-strings.mjs`);
process.exit(1);
}
return;
}
// Rewrite only on a change, so the payload's make rule does not see a new mtime for nothing.
if (old !== text) fs.writeFileSync(OUT_PATH, text);
const n = Object.keys(JSON.parse(text).en).length;
console.log(`console strings: ${n} keys × ${Object.keys(JSON.parse(text)).length} locales`);
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main();