@moku-labs/game 0.8.0 → 0.10.0

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.
Files changed (60) hide show
  1. package/README.md +88 -26
  2. package/bin/moku-game.mjs +4 -0
  3. package/dist/app/page.d.mts +41 -0
  4. package/dist/app/page.mjs +376 -0
  5. package/dist/app/system.d.mts +129 -0
  6. package/dist/app/system.mjs +307 -0
  7. package/dist/app.d.mts +37 -0
  8. package/dist/app.mjs +235 -0
  9. package/dist/assets-BHwTLVta.mjs +7391 -0
  10. package/dist/assets.d.mts +97 -33
  11. package/dist/assets.mjs +1 -7150
  12. package/dist/{capture-BX5Gfoty.mjs → capture-fpd83Mi5.mjs} +384 -1
  13. package/dist/cli.d.mts +70 -0
  14. package/dist/cli.mjs +1650 -0
  15. package/dist/{commands-C1kIfOG5.d.mts → commands-C4QlqijJ.d.mts} +5 -6
  16. package/dist/config-CdARrrhU.mjs +81 -0
  17. package/dist/control.d.mts +11 -10
  18. package/dist/control.mjs +3 -3
  19. package/dist/{define-BPpZcpxW.d.mts → define-CCc0ozKe.d.mts} +2 -2
  20. package/dist/{dev-wKLgOw7c.mjs → dev-Cc50Dk7H.mjs} +1 -1
  21. package/dist/{memory-CvgdnsQO.mjs → drafts-BawpSaVp.mjs} +1 -104
  22. package/dist/fake-MpzwZhIZ.mjs +87 -0
  23. package/dist/{headless-CvamCUcR.mjs → headless-BsNdJs9W.mjs} +1 -1
  24. package/dist/{headless-q98SwADR.d.mts → headless-G4nuZ-xz.d.mts} +1 -1
  25. package/dist/hot.d.mts +8 -8
  26. package/dist/hot.mjs +15 -14
  27. package/dist/index-xxSxKJJW.d.mts +1611 -0
  28. package/dist/index.d.mts +6 -1597
  29. package/dist/index.mjs +4 -34343
  30. package/dist/inspect-la_KkjCS.mjs +1 -0
  31. package/dist/inspect.d.mts +7 -8
  32. package/dist/inspect.mjs +3 -2
  33. package/dist/jsx-dev-runtime.d.mts +1 -1
  34. package/dist/jsx-runtime.d.mts +1 -1
  35. package/dist/lint.d.mts +55 -4
  36. package/dist/lint.mjs +999 -42
  37. package/dist/memory-CWlqhS7l.mjs +136 -0
  38. package/dist/memory-DwDhfFWK.d.mts +50 -0
  39. package/dist/project.d.mts +29 -13
  40. package/dist/project.mjs +1120 -631
  41. package/dist/{run-CUPSFM85.mjs → run-wYty_Lwo.mjs} +4 -4
  42. package/dist/{runtime-CvDZZg1S.d.mts → runtime-DOBYM6g4.d.mts} +1 -1
  43. package/dist/{input-z_35Jwfn.mjs → session-DgukhR_7.mjs} +20 -20
  44. package/dist/{sources-BBUd15vQ.mjs → sources-Duzj6lhV.mjs} +3 -3
  45. package/dist/src-DSl1eNB1.mjs +34366 -0
  46. package/dist/testing.d.mts +115 -34
  47. package/dist/testing.mjs +175 -70
  48. package/dist/{types-DlgsfOmW.d.mts → types-BM4shh8K.d.mts} +300 -263
  49. package/dist/{types-DgdMLZnV.d.mts → types-BUwDHKXm.d.mts} +1372 -1373
  50. package/dist/{types-DF5kkW-C.d.mts → types-CefgYzFt.d.mts} +4 -5
  51. package/dist/{types-CDSKa0vl.d.mts → types-DAdwEJMp.d.mts} +2 -2
  52. package/dist/{types-BWzCSRM2.d.mts → types-ZQLYXo5X.d.mts} +2983 -20
  53. package/dist/types-hQgI_wrq.d.mts +544 -0
  54. package/dist/{visual-DUvvPPCt.mjs → visual-dMW8iqAE.mjs} +8 -4
  55. package/dist/visual.d.mts +10 -11
  56. package/dist/visual.mjs +5 -5
  57. package/llms.txt +170 -57
  58. package/package.json +30 -2
  59. package/dist/registry-DlpRCibU.mjs +0 -384
  60. package/dist/types-ERb2Y9U5.d.mts +0 -2967
@@ -1,9 +1,8 @@
1
- import { N as Time, Z as Api, _ as Bookmark, b as FlowState } from "./types-ERb2Y9U5.mjs";
2
- import { C as Captured, T as DebugSwitches, k as Api$2, m as Api$1 } from "./types-BWzCSRM2.mjs";
3
- import { t as HeadlessApp } from "./headless-q98SwADR.mjs";
4
- import { n as Command, r as ControlApp } from "./types-CDSKa0vl.mjs";
5
- import { H as InputApi } from "./types-DgdMLZnV.mjs";
6
- import { b as AnimApi, p as ScenesApi, t as AudioApi, v as UiApi } from "./types-DlgsfOmW.mjs";
1
+ import { A as Api$2, E as DebugSwitches, Ot as Bookmark, h as Api$1, jt as FlowState, ln as Api, vn as Time, w as Captured } from "./types-ZQLYXo5X.mjs";
2
+ import { a as AudioApi, r as UiApi, v as ScenesApi, x as AnimApi } from "./types-BM4shh8K.mjs";
3
+ import { A as InputApi } from "./types-BUwDHKXm.mjs";
4
+ import { t as HeadlessApp } from "./headless-G4nuZ-xz.mjs";
5
+ import { n as Command, r as ControlApp } from "./types-DAdwEJMp.mjs";
7
6
 
8
7
  //#region src/plugins/flow/doors/commands.d.ts
9
8
  /**
@@ -0,0 +1,81 @@
1
+ //#region src/app/config.ts
2
+ /**
3
+ * The save kinds a config may name.
4
+ *
5
+ * @returns The set of save kinds.
6
+ * @example
7
+ * ```ts
8
+ * saveKinds().has("disk"); // false
9
+ * ```
10
+ */
11
+ function saveKinds() {
12
+ return /* @__PURE__ */ new Set([
13
+ "memory",
14
+ "local",
15
+ "store"
16
+ ]);
17
+ }
18
+ /**
19
+ * The system plugins the game shell wires, by their `@moku-labs/system` names.
20
+ *
21
+ * @returns The set of system names.
22
+ * @example
23
+ * ```ts
24
+ * systemNames().has("keepAwake"); // true
25
+ * systemNames().has("tray"); // false
26
+ * ```
27
+ */
28
+ function systemNames() {
29
+ return /* @__PURE__ */ new Set([
30
+ "lifecycle",
31
+ "back",
32
+ "haptics",
33
+ "keepAwake",
34
+ "store"
35
+ ]);
36
+ }
37
+ /**
38
+ * Throws for the first value of a config that TypeScript would have refused.
39
+ *
40
+ * @param config - The default export of `config.ts`.
41
+ * @throws {Error} When `save` is not a save kind, a `system` name is not wired, or the title is empty.
42
+ */
43
+ function checkConfig(config) {
44
+ if (config.save !== void 0 && !saveKinds().has(config.save)) throw new Error(`[game] config.save is "${config.save}".\n Use "memory", "local" or "store".`);
45
+ const wired = systemNames();
46
+ const unwired = config.system?.find((name) => !wired.has(name));
47
+ if (unwired !== void 0) throw new Error(`[game] config.system names "${unwired}", which the game shell does not wire.\n Use lifecycle, back, haptics, keepAwake or store.`);
48
+ if (config.page.title.trim() === "") throw new Error("[game] config.page.title is empty.\n Give the page a title in config.ts.");
49
+ }
50
+ /**
51
+ * Fills every default of a game's `config.ts`: `lang` `"en"`, `background` `"#000000"`,
52
+ * `orientation` `"portrait"`, no icons, no head tags, no system plugin, the memory save, no asset
53
+ * layers.
54
+ *
55
+ * @param config - The default export of `config.ts`.
56
+ * @returns The config with every field set.
57
+ * @throws {Error} When `save` is not a save kind, a `system` name is not wired, or the title is empty.
58
+ * @example
59
+ * ```ts
60
+ * resolveConfig({ page: { title: "T" } }).save; // "memory"
61
+ * ```
62
+ */
63
+ function resolveConfig(config) {
64
+ checkConfig(config);
65
+ return {
66
+ page: {
67
+ title: config.page.title,
68
+ lang: config.page.lang ?? "en",
69
+ background: config.page.background ?? "#000000",
70
+ orientation: config.page.orientation ?? "portrait",
71
+ icons: config.page.icons ?? {},
72
+ head: config.page.head ?? []
73
+ },
74
+ native: config.native,
75
+ system: config.system ?? [],
76
+ save: config.save ?? "memory",
77
+ assets: { layers: config.assets?.layers ?? {} }
78
+ };
79
+ }
80
+ //#endregion
81
+ export { resolveConfig as t };
@@ -1,13 +1,14 @@
1
- import { t as HeadlessApp } from "./headless-q98SwADR.mjs";
2
- import { i as InputArguments, n as Command, o as InputSchema, s as Ran } from "./types-CDSKa0vl.mjs";
3
- import { t as commands } from "./commands-C1kIfOG5.mjs";
4
- import { t as defineCommand } from "./define-BPpZcpxW.mjs";
1
+ import { t as HeadlessApp } from "./headless-G4nuZ-xz.mjs";
2
+ import { i as InputArguments, n as Command, o as InputSchema, s as Ran } from "./types-DAdwEJMp.mjs";
3
+ import { t as commands } from "./commands-C4QlqijJ.mjs";
4
+ import { t as defineCommand } from "./define-CCc0ozKe.mjs";
5
5
 
6
6
  //#region src/plugins/flow/doors/dev.d.ts
7
7
  /**
8
8
  * @file flow/doors — the dev flag and the errors a door throws. `__MOKU_GAME_DEV__` is a global
9
- * the engine never replaces: a game's dev build defines it `true` (bundler `define`) or sets
10
- * `globalThis.__MOKU_GAME_DEV__` before the engine runs; undefined means a production build.
9
+ * the engine never replaces: `moku-game dev` defines it `true` (bunfig `define`) and sets
10
+ * `globalThis.__MOKU_GAME_DEV__` in `.moku/dev.ts` before the engine runs; `moku-game build`
11
+ * defines it `false`; undefined means a production build.
11
12
  *
12
13
  * Bun does not inline `isDev()` across modules (checked on Bun 1.3.14), so a branch that must
13
14
  * vanish from a production bundle writes the guard inline:
@@ -16,12 +17,12 @@ import { t as defineCommand } from "./define-BPpZcpxW.mjs";
16
17
  */
17
18
  declare global {
18
19
  /**
19
- * The dev flag of a game. A game never re-declares it: it defines it in the bundler, or sets
20
- * `globalThis.__MOKU_GAME_DEV__ = true` in its dev entry.
20
+ * The dev flag of a game. A game never re-declares it: `moku-game dev` sets it, `moku-game
21
+ * build` defines it `false`, and a test stubs it.
21
22
  *
22
23
  * @example
23
24
  * ```ts
24
- * // web/dev.ts, imported first by the dev page: /control commands run from here on.
25
+ * // .moku/dev.ts, written by moku-game dev, the first import of the dev page: /control commands run from here on.
25
26
  * globalThis.__MOKU_GAME_DEV__ = true;
26
27
  * ```
27
28
  */
@@ -33,7 +34,7 @@ declare global {
33
34
  * @returns True only when `__MOKU_GAME_DEV__` is exactly `true`.
34
35
  * @example
35
36
  * ```ts
36
- * // The dev page set the flag in web/dev.ts before the engine started.
37
+ * // The dev page set the flag in .moku/dev.ts before the engine started.
37
38
  * globalThis.__MOKU_GAME_DEV__ = true;
38
39
  * isDev(); // true
39
40
  * ```
package/dist/control.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as controlRefused } from "./dev-wKLgOw7c.mjs";
2
- import { s as defineCommand } from "./input-z_35Jwfn.mjs";
3
- import { n as commands, t as run } from "./run-CUPSFM85.mjs";
1
+ import { t as controlRefused } from "./dev-Cc50Dk7H.mjs";
2
+ import { s as defineCommand } from "./session-DgukhR_7.mjs";
3
+ import { n as commands, t as run } from "./run-wYty_Lwo.mjs";
4
4
  export { commands, controlRefused, defineCommand, run };
@@ -1,5 +1,5 @@
1
- import { t as HeadlessApp } from "./headless-q98SwADR.mjs";
2
- import { c as Source, n as Command, o as InputSchema } from "./types-CDSKa0vl.mjs";
1
+ import { t as HeadlessApp } from "./headless-G4nuZ-xz.mjs";
2
+ import { c as Source, n as Command, o as InputSchema } from "./types-DAdwEJMp.mjs";
3
3
 
4
4
  //#region src/plugins/flow/doors/define.d.ts
5
5
  /**
@@ -5,7 +5,7 @@
5
5
  * @returns True only when `__MOKU_GAME_DEV__` is exactly `true`.
6
6
  * @example
7
7
  * ```ts
8
- * // The dev page set the flag in web/dev.ts before the engine started.
8
+ * // The dev page set the flag in .moku/dev.ts before the engine started.
9
9
  * globalThis.__MOKU_GAME_DEV__ = true;
10
10
  * isDev(); // true
11
11
  * ```
@@ -153,107 +153,4 @@ function dropDrafts(pair) {
153
153
  finishDraft(pair);
154
154
  }
155
155
  //#endregion
156
- //#region src/plugins/model/store/providers/memory.ts
157
- /** Seed of a save built by `saveOf` when the caller does not pin one. */
158
- const defaultSeed = 1;
159
- /** The base a patch applies to when the provider was never handed a document. */
160
- const noDocument = {};
161
- /**
162
- * Creates an in-memory provider that keeps what it was committed and records every call.
163
- * It is the default when a game configures no `playerProvider`, and the double every test uses:
164
- * `provider.calls` is the whole persistence protocol of one run, in order. The document lives as
165
- * long as the provider does, so a second app over the same instance reads what the first one
166
- * wrote, and nothing survives the process.
167
- *
168
- * `load()` reads the fixture, or everything committed since; without either, the player is new.
169
- * `commitDurable()` resolves at once: there is no disk behind it.
170
- *
171
- * @param fixture - The save `load()` starts from. Omitted: a new player.
172
- * @param fixture.state - The saved document.
173
- * @param fixture.version - Schema version of the saved document.
174
- * @returns A provider that keeps its document and records its calls.
175
- * @example
176
- * ```ts
177
- * // A new player is saved at once. One roll reaches the provider at the next rest node.
178
- * const provider = memory();
179
- * const game = await createHeadless(createGame(provider));
180
- *
181
- * await game.walk([{ at: "home", intent: "roll" }]);
182
- * provider.calls.map(call => call.method); // ["load", "commit", "commit"]
183
- * ```
184
- */
185
- function memory(fixture) {
186
- const calls = [];
187
- const held = {
188
- document: fixture?.state,
189
- version: fixture?.version ?? 0
190
- };
191
- /**
192
- * Applies the patches of one commit to the held document.
193
- *
194
- * @param patches - Doc patches since the last commit.
195
- * @param version - Schema version this build writes.
196
- * @throws {Error} When a patch names a path the held document does not have.
197
- */
198
- const hold = (patches, version) => {
199
- held.document = applyTo(held.document ?? noDocument, patches);
200
- held.version = version;
201
- };
202
- return {
203
- calls,
204
- load: async () => {
205
- calls.push({ method: "load" });
206
- const document = held.document;
207
- if (document === void 0) return null;
208
- return {
209
- state: document,
210
- version: held.version
211
- };
212
- },
213
- commit: (patches, version) => {
214
- calls.push({
215
- method: "commit",
216
- patches,
217
- version
218
- });
219
- hold(patches, version);
220
- },
221
- commitDurable: async (patches, txId, version) => {
222
- calls.push({
223
- method: "commitDurable",
224
- patches,
225
- txId,
226
- version
227
- });
228
- hold(patches, version);
229
- },
230
- flush: async () => {
231
- calls.push({ method: "flush" });
232
- }
233
- };
234
- }
235
- /**
236
- * Builds a save document from a player tree, with no rng streams drawn yet.
237
- * Short fixtures in tests read as `saveOf({ coins: 5 })` instead of spelling out the rng branch.
238
- *
239
- * @param player - Player tree of the save.
240
- * @param seed - Rng seed of the save.
241
- * @returns The save document.
242
- * @example
243
- * ```ts
244
- * // A returning player with 5 coins. The test starts from this save, not from `initialPlayer`.
245
- * saveOf({ coins: 5 }, 42); // { player: { coins: 5 }, rng: { seed: 42, streams: {} } }
246
- * const provider = memory({ state: saveOf({ coins: 5 }, 42), version: 1 });
247
- * ```
248
- */
249
- function saveOf(player, seed = defaultSeed) {
250
- return {
251
- player,
252
- rng: {
253
- seed,
254
- streams: {}
255
- }
256
- };
257
- }
258
- //#endregion
259
- export { enableDraftPatches as a, dropDrafts as i, saveOf as n, finishDrafts as o, deepFreeze as r, openDrafts as s, memory as t };
156
+ export { finishDrafts as a, enableDraftPatches as i, deepFreeze as n, openDrafts as o, dropDrafts as r, applyTo as t };
@@ -0,0 +1,87 @@
1
+ //#region src/plugins/clock/fake.ts
2
+ /**
3
+ * Removes and returns the timer that is due first, up to and including `until`.
4
+ * Timers due at the same moment come out in the order they were scheduled.
5
+ *
6
+ * @param state - Innards of the fake clock.
7
+ * @param until - Latest moment that counts as due.
8
+ * @returns The timer to fire, or `undefined` when nothing is due.
9
+ */
10
+ function takeDue(state, until) {
11
+ let earliest;
12
+ for (const timer of state.timers) {
13
+ if (timer.at > until) continue;
14
+ if (earliest === void 0 || timer.at < earliest.at) earliest = timer;
15
+ }
16
+ if (earliest === void 0) return void 0;
17
+ const due = earliest;
18
+ state.timers = state.timers.filter((timer) => timer !== due);
19
+ return due;
20
+ }
21
+ /**
22
+ * Moves the clock to `until`, firing every timer that falls due on the way, in due order.
23
+ * A timer scheduled inside a callback fires in the same run when it is due before `until`.
24
+ *
25
+ * @param state - Innards of the fake clock.
26
+ * @param until - Moment to stop at.
27
+ */
28
+ function runDue(state, until) {
29
+ let due = takeDue(state, until);
30
+ while (due !== void 0) {
31
+ state.now = Math.max(state.now, due.at);
32
+ due.callback();
33
+ due = takeDue(state, until);
34
+ }
35
+ state.now = Math.max(state.now, until);
36
+ }
37
+ /**
38
+ * Creates a fake source for tests. Timers fire synchronously inside `advance`, in due order. A
39
+ * negative delay counts as zero and an unknown handle is ignored.
40
+ *
41
+ * @param start - Starting moment in epoch milliseconds. Defaults to 0.
42
+ * @returns A clock source with the test-only controls `advance` and `set`.
43
+ * @example
44
+ * ```ts
45
+ * // An energy point refills 60 s after it was spent. The test moves the clock, not the minute.
46
+ * const clock = fakeClock(1000);
47
+ * const app = createApp({ pluginConfigs: { clock: { source: clock } } });
48
+ * const seen: number[] = [];
49
+ *
50
+ * app.clock.onElapsed(({ now }) => seen.push(now));
51
+ * app.clock.scheduleAt(61_000);
52
+ * clock.advance(60_000); // the timer fires inside advance, nothing waits
53
+ * seen; // [61000]
54
+ * ```
55
+ */
56
+ function fakeClock(start = 0) {
57
+ const state = {
58
+ now: start,
59
+ nextId: 1,
60
+ timers: []
61
+ };
62
+ return {
63
+ now: () => state.now,
64
+ setTimer: (callback, delayMs) => {
65
+ const id = state.nextId;
66
+ state.nextId += 1;
67
+ state.timers.push({
68
+ id,
69
+ at: state.now + Math.max(0, delayMs),
70
+ callback
71
+ });
72
+ return id;
73
+ },
74
+ clearTimer: (handle) => {
75
+ if (typeof handle !== "number") return;
76
+ state.timers = state.timers.filter((timer) => timer.id !== handle);
77
+ },
78
+ advance: (ms) => {
79
+ runDue(state, state.now + Math.max(0, ms));
80
+ },
81
+ set: (moment) => {
82
+ state.now = moment;
83
+ }
84
+ };
85
+ }
86
+ //#endregion
87
+ export { fakeClock as t };
@@ -1,4 +1,4 @@
1
- import { a as graphHash } from "./registry-DlpRCibU.mjs";
1
+ import { u as graphHash } from "./capture-fpd83Mi5.mjs";
2
2
  //#region src/plugins/flow/headless.ts
3
3
  const noPayload = null;
4
4
  /** Seed of a repro that pins no rng state, the seed `saveOf` writes. */
@@ -1,4 +1,4 @@
1
- import { A as Api, C as RouteStep, R as Json, X as RngState, b as FlowState, k as Answer, t as Api$1, x as JournalEntry } from "./types-ERb2Y9U5.mjs";
1
+ import { Bt as Answer, Mt as JournalEntry, Pt as RouteStep, Xt as Json, cn as RngState, dt as Api$1, hn as Api, jt as FlowState } from "./types-ZQLYXo5X.mjs";
2
2
 
3
3
  //#region src/plugins/flow/headless.d.ts
4
4
  /**
package/dist/hot.d.mts CHANGED
@@ -14,8 +14,10 @@
14
14
  type HotOptions = {
15
15
  /**
16
16
  * The files that get the footer. Default: `.tsx` files, `styles.ts`, `view.ts`,
17
- * `animations.ts`, `effects.ts` and `generated/strings.<locale>.ts`. Bun runs it as the load
18
- * filter on the absolute path, so it should not anchor at the start.
17
+ * `animations.ts`, `effects.ts`, any `.ts` directly in a `styles/`, `motion/`, `effects/` or
18
+ * `views/` folder, and in `world/projections/` or `world/layout/`, and
19
+ * `generated/strings.<locale>.ts`. Bun runs it as the load filter on the absolute path, so it
20
+ * should not anchor at the start.
19
21
  */
20
22
  include?: RegExp;
21
23
  /**
@@ -70,16 +72,14 @@ type HotPlugin = {
70
72
  * @returns The Bun plugin object.
71
73
  * @example
72
74
  * ```ts
73
- * // bunfig.toml of a game with the default layout:
74
- * // [serve.static]
75
- * // plugins = ["@moku-labs/game/hot"]
76
- * // A game that keeps its views in `look/` folders exports its own plugin from `hot.ts`
77
- * // and lists `plugins = ["./hot.ts"]` instead:
75
+ * // `moku-game dev` lists the default plugin in `.moku/bunfig.toml`: a game sets nothing.
76
+ * // A game with a server of its own and its views in `look/` folders exports its own plugin
77
+ * // from `hot.ts` and lists `plugins = ["./hot.ts"]` in its own bunfig.toml:
78
78
  * export default hot({ include: /\/look\/.*\.ts$/ });
79
79
  * ```
80
80
  */
81
81
  declare function hot(options?: HotOptions): HotPlugin;
82
- /** The Bun plugin with the default filters, for `plugins = ["@moku-labs/game/hot"]` in `bunfig.toml`. */
82
+ /** The Bun plugin with the default filters: the one `moku-game dev` lists, or `plugins = ["@moku-labs/game/hot"]` in a game's own `bunfig.toml`. */
83
83
  declare const _default: HotPlugin;
84
84
  //#endregion
85
85
  export { HotBuild, HotOptions, HotPlugin, _default as default, hot };
package/dist/hot.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { readFile } from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import { readFile } from "node:fs/promises";
3
3
  //#region src/hot.ts
4
4
  /**
5
5
  * @file Hot entry (subpath `./hot`): the Bun plugin of the dev server that hot swaps the views of
@@ -7,16 +7,19 @@ import path from "node:path";
7
7
  * update and hands the new exports to the running game through `globalThis.__moku_hot`. The `ui`
8
8
  * plugin installs that handler in dev builds only. What swaps: styles, components, projections,
9
9
  * animations, text styles, emitters and generated strings. A view module is a `.tsx` file, a
10
- * `styles.ts`, a `view.ts`, an `animations.ts`, an `effects.ts` or a generated
11
- * `generated/strings.<locale>.ts`; a logic module never gets the footer, so its save reaches the
12
- * root and the page reloads and restores its state. The plugin lives in `[serve.static]` of
13
- * `bunfig.toml`, the dev server only: `Bun.build` of a production build never loads it.
10
+ * `styles.ts`, a `view.ts`, an `animations.ts`, an `effects.ts`, any `.ts` directly in a
11
+ * `styles/`, `motion/`, `effects/` or `views/` folder, any `.ts` directly in `world/projections/`
12
+ * or `world/layout/`, or a generated `generated/strings.<locale>.ts`; a logic module never gets
13
+ * the footer (`world/components/` and `world/systems/` included: they are registered at start), so
14
+ * its save reaches the root and the page reloads and restores its state. `moku-game dev` lists the
15
+ * plugin first in `[serve.static]` of the bunfig it writes, `.moku/bunfig.toml`; a game without the
16
+ * shell lists it in its own `bunfig.toml`. The dev server only: `moku-game build` never loads it.
14
17
  */
15
18
  /**
16
- * The default view modules: `.tsx` files, `styles.ts`, `view.ts`, `animations.ts`, `effects.ts`
17
- * and the generated strings of a locale.
19
+ * The default view modules: the view files by name or by kind folder. Two literals joined, so
20
+ * each stays readable.
18
21
  */
19
- const DEFAULT_INCLUDE = /(\.tsx|\/styles\.ts|\/view\.ts|\/animations\.ts|\/effects\.ts|\/generated\/strings\.[\w-]+\.ts)$/;
22
+ const DEFAULT_INCLUDE = new RegExp(`(${/\.tsx|\/styles\.ts|\/view\.ts|\/animations\.ts|\/effects\.ts|\/generated\/strings\.[\w-]+\.ts/.source}|${/\/(?:styles|motion|effects|views)\/[^/]+\.ts|\/world\/(?:projections|layout)\/[^/]+\.ts/.source})$`);
20
23
  /** The generated strings of one locale, the one file under `generated/` that swaps. */
21
24
  const STRINGS = /\/generated\/strings\.[\w-]+\.ts$/;
22
25
  /** The default folders and test files that never hot swap. The outer group only scopes the `$`. */
@@ -71,11 +74,9 @@ function footerOf(file) {
71
74
  * @returns The Bun plugin object.
72
75
  * @example
73
76
  * ```ts
74
- * // bunfig.toml of a game with the default layout:
75
- * // [serve.static]
76
- * // plugins = ["@moku-labs/game/hot"]
77
- * // A game that keeps its views in `look/` folders exports its own plugin from `hot.ts`
78
- * // and lists `plugins = ["./hot.ts"]` instead:
77
+ * // `moku-game dev` lists the default plugin in `.moku/bunfig.toml`: a game sets nothing.
78
+ * // A game with a server of its own and its views in `look/` folders exports its own plugin
79
+ * // from `hot.ts` and lists `plugins = ["./hot.ts"]` in its own bunfig.toml:
79
80
  * export default hot({ include: /\/look\/.*\.ts$/ });
80
81
  * ```
81
82
  */
@@ -102,7 +103,7 @@ function hot(options = {}) {
102
103
  }
103
104
  };
104
105
  }
105
- /** The Bun plugin with the default filters, for `plugins = ["@moku-labs/game/hot"]` in `bunfig.toml`. */
106
+ /** The Bun plugin with the default filters: the one `moku-game dev` lists, or `plugins = ["@moku-labs/game/hot"]` in a game's own `bunfig.toml`. */
106
107
  var hot_default = hot();
107
108
  //#endregion
108
109
  export { hot_default as default, hot };