@moku-labs/game 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -134,7 +134,7 @@ app.model.store.snapshot().session; // { rolls: 2 }
134
134
 
135
135
  `bunx moku-game dev` serves it with hot reload on `http://localhost:3000/`. The full version, with a reset node and a vitest file, is in [docs/quick-start.md](./docs/quick-start.md). A bigger game, the merge game, lives in [moku-labs/demos](https://github.com/moku-labs/demos) with its tests.
136
136
 
137
- A game with a screen gets two more things from the package. The body font `fonts/font-body.*` (Pangolin, SIL OFL 1.1) is the default of the built-in text style `body` once copied into `features/ui/assets/`. `moku-game keys` writes the typed asset keys, the manifest and the strings. Both are in [docs/quick-start.md](./docs/quick-start.md#a-screen-the-body-font-and-the-asset-keys).
137
+ A game with a screen gets two more things from the package. The body font `fonts/font-body.*` (Pangolin, SIL OFL 1.1) is the default of the built-in text style `body` once copied into `features/ui/assets/`. `moku-game keys` writes the typed asset keys, the manifest and the strings, and `moku-game dev` writes them again when an asset or a strings file is saved. Both are in [docs/quick-start.md](./docs/quick-start.md#a-screen-the-body-font-and-the-asset-keys).
138
138
 
139
139
  ## Game shell
140
140
 
@@ -160,12 +160,12 @@ The engine bin `moku-game` serves, builds and packs a game folder. A game writes
160
160
 
161
161
  | Command | Does |
162
162
  |---|---|
163
- | `moku-game dev [--port 3000] [--packed]` | Serves the game with hot reload. Prints the bound URL, also for `--port 0` |
163
+ | `moku-game dev [--port 3000] [--packed]` | Serves the game with hot reload. Prints the bound URL, also for `--port 0`. On the raw files it runs the scan of `keys` at start and on a save of an asset or a strings file |
164
164
  | `moku-game build [--out dist/web]` | Packs the assets and builds the production page |
165
165
  | `moku-game native build <target> [--simulator]` | Builds the native app: `ios`, `macos`, `android` |
166
166
  | `moku-game native dev <target>` | Runs the native shell on the dev server |
167
167
  | `moku-game native doctor`, `native clean` | Checks or removes the native project |
168
- | `moku-game keys [--check]` | Writes `generated/assets.ts`, the strings and `manifest.json` |
168
+ | `moku-game keys [--check]` | Writes `generated/assets.ts`, the strings and `generated/manifest.json` |
169
169
  | `moku-game pack [--no-cache]` | Packs the assets into `dist/assets` |
170
170
  | `moku-game visual [--update] [--only <name>] [--no-pixels]` | Runs the visual tests of `tests/visual/index.ts` against `tests/visual/baselines/`. The pixel leg runs on a Mac, on a page served for the run. Exit 1 when a checkpoint differs |
171
171
 
@@ -245,7 +245,7 @@ createApp({ plugins: [...screen, effectsPlugin, audioPlugin, platformPlugin] });
245
245
  | `@moku-labs/game/app` | anywhere | `defineGameApp`, `startMoment` and the types of `index.ts` and `config.ts`. See [The game shell](./docs/shell.md) |
246
246
  | `@moku-labs/game/app/page` | the browser | `startPage`, the page the generated `.moku/main.ts` calls |
247
247
  | `@moku-labs/game/app/system` | the browser, the native shell | The system shell over the optional peer `@moku-labs/system` |
248
- | `@moku-labs/game/cli` | Node and Bun | `runCli`, the bin `moku-game`; `preparePage`, the dev page for the editor |
248
+ | `@moku-labs/game/cli` | Node and Bun | `runCli`, the bin `moku-game`; `preparePage`, the dev page for the editor; `watchKeys`, which keeps `generated/` fresh next to it |
249
249
  | `@moku-labs/game/testing` | anywhere | Headless and isolated games, the fake clock, the in-memory save |
250
250
  | `@moku-labs/game/visual` | Node and Bun | Visual tests: baselines of state, describe and pixels |
251
251
  | `@moku-labs/game/assets` | Node and Bun | Asset keys, the manifest, strings, the pack |
@@ -19,6 +19,7 @@ import { m as StartedPage, r as GameConfig, t as AnyGameApp, u as PageOptions }
19
19
  * ```ts
20
20
  * // Written by moku-game dev. Do not edit.
21
21
  * import "./dev.ts";
22
+ * import "./assets-stamp.ts";
22
23
  * import { startPage } from "@moku-labs/game/app/page";
23
24
  * import { systemShellOf } from "@moku-labs/game/app/system";
24
25
  * import game from "../index.ts";
package/dist/app/page.mjs CHANGED
@@ -321,6 +321,7 @@ async function startAgents(app, settings, options) {
321
321
  * ```ts
322
322
  * // Written by moku-game dev. Do not edit.
323
323
  * import "./dev.ts";
324
+ * import "./assets-stamp.ts";
324
325
  * import { startPage } from "@moku-labs/game/app/page";
325
326
  * import { systemShellOf } from "@moku-labs/game/app/system";
326
327
  * import game from "../index.ts";
package/dist/cli.d.mts CHANGED
@@ -1,3 +1,38 @@
1
+ //#region src/app/keys.d.ts
2
+ /** The running keys watch of one game folder, as `watchKeys` answers it. */
3
+ type KeysWatcher = {
4
+ /**
5
+ * Stops watching. A scan that is running finishes; no later one starts. A second call does
6
+ * nothing.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * // The editor closes the game it has open.
11
+ * const keys = await watchKeys("games/timber");
12
+ * keys.close(); // a later save of features/home/strings/en.json rewrites nothing in generated/
13
+ * ```
14
+ */
15
+ close(): void;
16
+ };
17
+ /** What `watchKeys` is started with. */
18
+ type WatchKeysOptions = {
19
+ /**
20
+ * Called with the scanner's message when a scan fails: bad JSON in a strings file, a key two
21
+ * files claim, a refused file. The watch goes on and the next save scans again. It is the only
22
+ * printer of a scan error. Default: a warning on the branded console, `[game] keys: <message>`.
23
+ *
24
+ * @param message - The message of the scanner; one line per problem when it found several.
25
+ * @example
26
+ * ```ts
27
+ * // The editor shows a failed scan in its own log, not in the terminal.
28
+ * const keys = await watchKeys("games/timber", { onError: message => log.warn(message) });
29
+ * // features/home/strings/en.json saved without its closing brace: under Bun log.warn gets
30
+ * // "[game] i18n: features/home/strings/en.json could not be read: JSON Parse error: Expected '}'.\n Fix the message or use a supported ICU feature."
31
+ * ```
32
+ */
33
+ onError?: (message: string) => void;
34
+ };
35
+ //#endregion
1
36
  //#region src/cli.d.ts
2
37
  /**
3
38
  * What the dev page of `preparePage` is written with besides the game.
@@ -50,8 +85,10 @@ declare function runCli(argv: readonly string[]): Promise<number>;
50
85
  /**
51
86
  * Writes a game's dev page into `<root>/.moku/` for a server the caller starts: `index.html`,
52
87
  * `dev.ts`, `main.ts` with the scenarios of `tests/scenarios/` (and, with agents, the agents and
53
- * the game's `.dev` modules), and `bunfig.toml` with the engine's hot plugin. It starts no server
54
- * and no watcher, and throws the `[game]` errors of `moku-game dev`.
88
+ * the game's `.dev` modules), and `bunfig.toml` with the engine's hot plugin. It also writes the
89
+ * first `assets-stamp.ts`, the empty one, when the game has none: `main.ts` imports it, and
90
+ * `watchKeys` rewrites it when an asset file changes. It starts no server and no watcher, and
91
+ * throws the `[game]` errors of `moku-game dev`.
55
92
  *
56
93
  * @param root - The game folder, relative to the cwd or absolute.
57
94
  * @param options - The agents, the preloads and the extra bundler plugins.
@@ -67,5 +104,32 @@ declare function runCli(argv: readonly string[]): Promise<number>;
67
104
  * ```
68
105
  */
69
106
  declare function preparePage(root: string, options?: PreparePageOptions): Promise<PreparedPage>;
107
+ /**
108
+ * Keeps `generated/` of a game fresh while its dev page is open, for a server the caller runs:
109
+ * the editor calls it next to `preparePage`, and raw `moku-game dev` runs the same watch itself.
110
+ * It watches the game folder, runs the scan of `moku-game keys` once and resolves after it. Then
111
+ * a save of an asset file (`.png`, `.webp`, `.fnt`, `.mp3`, `.m4a`) or of a
112
+ * `strings/<locale>.json` scans again, with the same flags, outputs and layers; an output that
113
+ * did not change is not rewritten. On the page a rewritten `generated/strings.<locale>.ts` hot
114
+ * swaps. An asset file that is new, gone or saved again rewrites `.moku/assets-stamp.ts`, which
115
+ * the page imports, so the page reloads and shows the new image or the new bytes of an old one. A
116
+ * failed scan goes to `onError` and the watch goes on, the first scan too. The layers of
117
+ * `config.ts` are read once: a changed layer needs a new `watchKeys`.
118
+ *
119
+ * @param root - The game folder, relative to the cwd or absolute.
120
+ * @param options - Where the message of a failed scan goes.
121
+ * @returns The running watch; `close()` stops it.
122
+ * @throws {Error} When the game has no `config.ts` or `index.ts`, or a config value is refused.
123
+ * @example
124
+ * ```ts
125
+ * // The editor bin, next to preparePage, started with --root games/timber:
126
+ * const { preparePage, watchKeys } = await import(Bun.resolveSync("@moku-labs/game/cli", root));
127
+ * await preparePage("games/timber", { agents: ["@moku-labs/editor/agent/page"] });
128
+ * const keys = await watchKeys("games/timber", { onError: message => log.warn(message) });
129
+ * // generated/ is fresh; a save of features/home/strings/en.json rewrites generated/strings.en.ts
130
+ * keys.close(); // when the editor closes the game
131
+ * ```
132
+ */
133
+ declare function watchKeys(root: string, options?: WatchKeysOptions): Promise<KeysWatcher>;
70
134
  //#endregion
71
- export { PreparePageOptions, PreparedPage, preparePage, runCli };
135
+ export { type KeysWatcher, PreparePageOptions, PreparedPage, type WatchKeysOptions, preparePage, runCli, watchKeys };