From 332db0be6b04f68fc269aa44fe3b5c0ecf295273 Mon Sep 17 00:00:00 2001 From: konstantin Date: Wed, 5 Aug 2026 01:53:40 +0200 Subject: [PATCH 1/2] fix(build): make messenger, ui and mdx dist resolvable by Node's ESM loader These three packages declare "type": "module" with only an exports.import condition, so Node reads their output as ESM, where relative specifiers need an explicit extension and a directory never resolves to its index.js. Their builds were missing tsc-alias's resolveFullPaths, which core and devtools already set, so dist shipped the shortened forms that only bundlers and CJS accept: import "./i18n"; // ERR_UNSUPPORTED_DIR_IMPORT import { logger } from "./logger"; // ERR_MODULE_NOT_FOUND ui/i18n had a second, independent blocker that resolveFullPaths does not fix: it imported its English strings from JSON, which Node refuses without a with { type: "json" } import attribute. That one failed silently rather than loudly - core reaches the entry through a dynamic import in Game.init wrapped in a try/catch that treats any failure as "the UI package isn't installed" - so UI strings degraded to raw translation keys under Node and SSR. The locale is now a TypeScript module, matching messenger/src/i18n/en.ts; the exported uiTranslations type is byte-identical to before. Verified by importing every emitted module of all five packages under plain Node: 209 modules, no resolver errors. Co-Authored-By: Claude Opus 5 (1M context) --- .changeset/esm-resolvable-dist.md | 22 +++++++++++++ packages/mdx/tsconfig.build.json | 9 +++++- packages/messenger/tsconfig.build.json | 9 +++++- packages/ui/src/i18n/index.ts | 2 +- packages/ui/src/i18n/locales/en/ui.json | 32 ------------------- packages/ui/src/i18n/locales/en/ui.ts | 42 +++++++++++++++++++++++++ packages/ui/tsconfig.build.json | 9 +++++- 7 files changed, 89 insertions(+), 36 deletions(-) create mode 100644 .changeset/esm-resolvable-dist.md delete mode 100644 packages/ui/src/i18n/locales/en/ui.json create mode 100644 packages/ui/src/i18n/locales/en/ui.ts diff --git a/.changeset/esm-resolvable-dist.md b/.changeset/esm-resolvable-dist.md new file mode 100644 index 000000000..c07d39435 --- /dev/null +++ b/.changeset/esm-resolvable-dist.md @@ -0,0 +1,22 @@ +--- +"@react-text-game/messenger": patch +"@react-text-game/ui": patch +"@react-text-game/mdx": patch +--- + +Fixed the published output being unloadable under Node's ESM resolver. + +These packages declare `"type": "module"` and only an `exports.import` condition, +so Node reads their files as ESM — where relative specifiers need an explicit +file extension and a directory never resolves to its `index.js`. `dist` shipped +the shortened forms, which bundlers accept but Node rejects with +`ERR_MODULE_NOT_FOUND` and `ERR_UNSUPPORTED_DIR_IMPORT`. The build now enables +`tsc-alias`'s `resolveFullPaths`, matching what `core` and `devtools` already did. + +`@react-text-game/ui/i18n` additionally imported its English strings from a JSON +file, which Node refuses without an `with { type: "json" }` import attribute +(`ERR_IMPORT_ATTRIBUTE_MISSING`). Because `core` reaches that entry through a +dynamic import inside `Game.init`, and that import is wrapped in a `try/catch` +that treats any failure as "the UI package isn't installed", UI strings silently +fell back to raw translation keys under Node and SSR. The locale is now a +TypeScript module; the exported `uiTranslations` type is unchanged. diff --git a/packages/mdx/tsconfig.build.json b/packages/mdx/tsconfig.build.json index a1c4cc507..0580c5e89 100644 --- a/packages/mdx/tsconfig.build.json +++ b/packages/mdx/tsconfig.build.json @@ -1,4 +1,11 @@ { "extends": "./tsconfig.json", - "exclude": ["dist", "node_modules", "src/tests"] + "exclude": ["dist", "node_modules", "src/tests"], + // Node's ESM resolver needs explicit file extensions and cannot resolve a + // directory to its index. Without this, `dist` ships specifiers like + // "./components" and Node fails with ERR_UNSUPPORTED_DIR_IMPORT. The plugin + // entry runs in a bundler config under Node, so this matters at build time. + "tsc-alias": { + "resolveFullPaths": true + } } diff --git a/packages/messenger/tsconfig.build.json b/packages/messenger/tsconfig.build.json index a1c4cc507..3f2ea0421 100644 --- a/packages/messenger/tsconfig.build.json +++ b/packages/messenger/tsconfig.build.json @@ -1,4 +1,11 @@ { "extends": "./tsconfig.json", - "exclude": ["dist", "node_modules", "src/tests"] + "exclude": ["dist", "node_modules", "src/tests"], + // Node's ESM resolver needs explicit file extensions and cannot resolve a + // directory to its index. Without this, `dist` ships specifiers like + // "./constants" and "./i18n" and Node fails with ERR_MODULE_NOT_FOUND and + // ERR_UNSUPPORTED_DIR_IMPORT. + "tsc-alias": { + "resolveFullPaths": true + } } diff --git a/packages/ui/src/i18n/index.ts b/packages/ui/src/i18n/index.ts index 6eb536aeb..ab49319ce 100644 --- a/packages/ui/src/i18n/index.ts +++ b/packages/ui/src/i18n/index.ts @@ -1,4 +1,4 @@ -import ui from "./locales/en/ui.json"; +import { ui } from "./locales/en/ui"; export const uiTranslations = { en: { ui }, diff --git a/packages/ui/src/i18n/locales/en/ui.json b/packages/ui/src/i18n/locales/en/ui.json deleted file mode 100644 index f01b0f9ac..000000000 --- a/packages/ui/src/i18n/locales/en/ui.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "mainMenu": { - "title": "Main Menu", - "newGame": "New Game", - "continue": "Continue", - "loadGame": "Load Game" - }, - "saves": { - "title": { - "save": "Save Game", - "load": "Load Game", - "saveLoad": "Save / Load Game" - }, - "slot": { - "label": "Slot {{number}}", - "empty": "Empty Slot", - "saveHere": "Save Here", - "overwrite": "Overwrite" - }, - "actions": { - "load": "Load", - "loading": "Loading...", - "save": "Save", - "saving": "Saving...", - "delete": "Delete", - "close": "Close" - }, - "errors": { - "actionFailed": "An error occurred. Please check the console for details." - } - } -} diff --git a/packages/ui/src/i18n/locales/en/ui.ts b/packages/ui/src/i18n/locales/en/ui.ts new file mode 100644 index 000000000..3606bdd48 --- /dev/null +++ b/packages/ui/src/i18n/locales/en/ui.ts @@ -0,0 +1,42 @@ +/** + * English defaults for the `ui` namespace. + * + * @remarks + * A TypeScript module rather than JSON on purpose: Node's ESM resolver requires + * an `with { type: "json" }` import attribute for JSON modules, so a bare JSON + * import makes this entry unloadable under plain Node (SSR, or `Game.init` + * reaching it through core's dynamic import). + */ +export const ui = { + mainMenu: { + title: "Main Menu", + newGame: "New Game", + continue: "Continue", + loadGame: "Load Game", + }, + saves: { + title: { + save: "Save Game", + load: "Load Game", + saveLoad: "Save / Load Game", + }, + slot: { + label: "Slot {{number}}", + empty: "Empty Slot", + saveHere: "Save Here", + overwrite: "Overwrite", + }, + actions: { + load: "Load", + loading: "Loading...", + save: "Save", + saving: "Saving...", + delete: "Delete", + close: "Close", + }, + errors: { + actionFailed: + "An error occurred. Please check the console for details.", + }, + }, +}; diff --git a/packages/ui/tsconfig.build.json b/packages/ui/tsconfig.build.json index a1c4cc507..1e3ba9f34 100644 --- a/packages/ui/tsconfig.build.json +++ b/packages/ui/tsconfig.build.json @@ -1,4 +1,11 @@ { "extends": "./tsconfig.json", - "exclude": ["dist", "node_modules", "src/tests"] + "exclude": ["dist", "node_modules", "src/tests"], + // Node's ESM resolver needs explicit file extensions and cannot resolve a + // directory to its index. Without this, `dist` ships specifiers like + // "./hooks" and Node fails with ERR_MODULE_NOT_FOUND and + // ERR_UNSUPPORTED_DIR_IMPORT. + "tsc-alias": { + "resolveFullPaths": true + } } From f00f55843429fa151f8a16b121cf323560f09169 Mon Sep 17 00:00:00 2001 From: konstantin Date: Wed, 5 Aug 2026 01:55:57 +0200 Subject: [PATCH 2/2] Version Packages Co-Authored-By: Claude Opus 5 (1M context) --- .changeset/esm-resolvable-dist.md | 22 ---------------------- apps/example-game/CHANGELOG.md | 8 ++++++++ apps/example-game/package.json | 2 +- apps/ui-test-app/CHANGELOG.md | 7 +++++++ apps/ui-test-app/package.json | 2 +- packages/mdx/CHANGELOG.md | 21 +++++++++++++++++++++ packages/mdx/package.json | 2 +- packages/messenger/CHANGELOG.md | 21 +++++++++++++++++++++ packages/messenger/package.json | 2 +- packages/ui/CHANGELOG.md | 21 +++++++++++++++++++++ packages/ui/package.json | 2 +- 11 files changed, 83 insertions(+), 27 deletions(-) delete mode 100644 .changeset/esm-resolvable-dist.md diff --git a/.changeset/esm-resolvable-dist.md b/.changeset/esm-resolvable-dist.md deleted file mode 100644 index c07d39435..000000000 --- a/.changeset/esm-resolvable-dist.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -"@react-text-game/messenger": patch -"@react-text-game/ui": patch -"@react-text-game/mdx": patch ---- - -Fixed the published output being unloadable under Node's ESM resolver. - -These packages declare `"type": "module"` and only an `exports.import` condition, -so Node reads their files as ESM — where relative specifiers need an explicit -file extension and a directory never resolves to its `index.js`. `dist` shipped -the shortened forms, which bundlers accept but Node rejects with -`ERR_MODULE_NOT_FOUND` and `ERR_UNSUPPORTED_DIR_IMPORT`. The build now enables -`tsc-alias`'s `resolveFullPaths`, matching what `core` and `devtools` already did. - -`@react-text-game/ui/i18n` additionally imported its English strings from a JSON -file, which Node refuses without an `with { type: "json" }` import attribute -(`ERR_IMPORT_ATTRIBUTE_MISSING`). Because `core` reaches that entry through a -dynamic import inside `Game.init`, and that import is wrapped in a `try/catch` -that treats any failure as "the UI package isn't installed", UI strings silently -fell back to raw translation keys under Node and SSR. The locale is now a -TypeScript module; the exported `uiTranslations` type is unchanged. diff --git a/apps/example-game/CHANGELOG.md b/apps/example-game/CHANGELOG.md index d3bb8f7dc..3f33a40cc 100644 --- a/apps/example-game/CHANGELOG.md +++ b/apps/example-game/CHANGELOG.md @@ -1,5 +1,13 @@ # example-game +## 0.0.7 + +### Patch Changes + +- Updated dependencies [332db0b] + - @react-text-game/ui@0.6.1 + - @react-text-game/mdx@0.3.1 + ## 0.0.6 ### Patch Changes diff --git a/apps/example-game/package.json b/apps/example-game/package.json index 7679185ae..fe4bf8d0e 100644 --- a/apps/example-game/package.json +++ b/apps/example-game/package.json @@ -1,7 +1,7 @@ { "name": "example-game", "private": true, - "version": "0.0.6", + "version": "0.0.7", "type": "module", "scripts": { "dev": "vite", diff --git a/apps/ui-test-app/CHANGELOG.md b/apps/ui-test-app/CHANGELOG.md index 274c1ccaf..866087b48 100644 --- a/apps/ui-test-app/CHANGELOG.md +++ b/apps/ui-test-app/CHANGELOG.md @@ -1,5 +1,12 @@ # ui-test-app +## 0.1.7 + +### Patch Changes + +- Updated dependencies [332db0b] + - @react-text-game/ui@0.6.1 + ## 0.1.6 ### Patch Changes diff --git a/apps/ui-test-app/package.json b/apps/ui-test-app/package.json index 9ad76d72c..5449f6a76 100644 --- a/apps/ui-test-app/package.json +++ b/apps/ui-test-app/package.json @@ -1,6 +1,6 @@ { "name": "ui-test-app", - "version": "0.1.6", + "version": "0.1.7", "private": true, "scripts": { "dev": "next dev --turbopack", diff --git a/packages/mdx/CHANGELOG.md b/packages/mdx/CHANGELOG.md index ec9292d7a..a3f798b48 100644 --- a/packages/mdx/CHANGELOG.md +++ b/packages/mdx/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 0.3.1 + +### Patch Changes + +- 332db0b: Fixed the published output being unloadable under Node's ESM resolver. + + These packages declare `"type": "module"` and only an `exports.import` condition, + so Node reads their files as ESM — where relative specifiers need an explicit + file extension and a directory never resolves to its `index.js`. `dist` shipped + the shortened forms, which bundlers accept but Node rejects with + `ERR_MODULE_NOT_FOUND` and `ERR_UNSUPPORTED_DIR_IMPORT`. The build now enables + `tsc-alias`'s `resolveFullPaths`, matching what `core` and `devtools` already did. + + `@react-text-game/ui/i18n` additionally imported its English strings from a JSON + file, which Node refuses without an `with { type: "json" }` import attribute + (`ERR_IMPORT_ATTRIBUTE_MISSING`). Because `core` reaches that entry through a + dynamic import inside `Game.init`, and that import is wrapped in a `try/catch` + that treats any failure as "the UI package isn't installed", UI strings silently + fell back to raw translation keys under Node and SSR. The locale is now a + TypeScript module; the exported `uiTranslations` type is unchanged. + ## 0.3.0 ### Minor Changes diff --git a/packages/mdx/package.json b/packages/mdx/package.json index ec9635dbf..24d402f93 100644 --- a/packages/mdx/package.json +++ b/packages/mdx/package.json @@ -1,6 +1,6 @@ { "name": "@react-text-game/mdx", - "version": "0.3.0", + "version": "0.3.1", "description": "MDX support for React Text Game", "main": "dist/index.js", "module": "dist/index.js", diff --git a/packages/messenger/CHANGELOG.md b/packages/messenger/CHANGELOG.md index 3a5a2d6c6..d886617a6 100644 --- a/packages/messenger/CHANGELOG.md +++ b/packages/messenger/CHANGELOG.md @@ -1,5 +1,26 @@ # @react-text-game/messenger +## 0.1.1 + +### Patch Changes + +- 332db0b: Fixed the published output being unloadable under Node's ESM resolver. + + These packages declare `"type": "module"` and only an `exports.import` condition, + so Node reads their files as ESM — where relative specifiers need an explicit + file extension and a directory never resolves to its `index.js`. `dist` shipped + the shortened forms, which bundlers accept but Node rejects with + `ERR_MODULE_NOT_FOUND` and `ERR_UNSUPPORTED_DIR_IMPORT`. The build now enables + `tsc-alias`'s `resolveFullPaths`, matching what `core` and `devtools` already did. + + `@react-text-game/ui/i18n` additionally imported its English strings from a JSON + file, which Node refuses without an `with { type: "json" }` import attribute + (`ERR_IMPORT_ATTRIBUTE_MISSING`). Because `core` reaches that entry through a + dynamic import inside `Game.init`, and that import is wrapped in a `try/catch` + that treats any failure as "the UI package isn't installed", UI strings silently + fell back to raw translation keys under Node and SSR. The locale is now a + TypeScript module; the exported `uiTranslations` type is unchanged. + ## 0.1.0 ### Minor Changes diff --git a/packages/messenger/package.json b/packages/messenger/package.json index 3d63cf310..76f2a1889 100644 --- a/packages/messenger/package.json +++ b/packages/messenger/package.json @@ -1,6 +1,6 @@ { "name": "@react-text-game/messenger", - "version": "0.1.0", + "version": "0.1.1", "description": "Headless messenger and visual-novel transcript engine for React Text Game: persistent message logs, seen tracking, group chats, media albums and scheduled delivery", "main": "dist/index.js", "module": "dist/index.js", diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 9794b5b8c..d4e6daeea 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 0.6.1 + +### Patch Changes + +- 332db0b: Fixed the published output being unloadable under Node's ESM resolver. + + These packages declare `"type": "module"` and only an `exports.import` condition, + so Node reads their files as ESM — where relative specifiers need an explicit + file extension and a directory never resolves to its `index.js`. `dist` shipped + the shortened forms, which bundlers accept but Node rejects with + `ERR_MODULE_NOT_FOUND` and `ERR_UNSUPPORTED_DIR_IMPORT`. The build now enables + `tsc-alias`'s `resolveFullPaths`, matching what `core` and `devtools` already did. + + `@react-text-game/ui/i18n` additionally imported its English strings from a JSON + file, which Node refuses without an `with { type: "json" }` import attribute + (`ERR_IMPORT_ATTRIBUTE_MISSING`). Because `core` reaches that entry through a + dynamic import inside `Game.init`, and that import is wrapped in a `try/catch` + that treats any failure as "the UI package isn't installed", UI strings silently + fell back to raw translation keys under Node and SSR. The locale is now a + TypeScript module; the exported `uiTranslations` type is unchanged. + ## 0.6.0 ### Minor Changes diff --git a/packages/ui/package.json b/packages/ui/package.json index 29affa891..cdc72c7b6 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@react-text-game/ui", - "version": "0.6.0", + "version": "0.6.1", "description": "Themeable React UI component library for text-based games with Tailwind CSS v4, featuring story passages, interactive maps, and customizable game interfaces", "private": false, "sideEffects": [