@datamoc/mw_games 0.36.0 → 0.37.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 (114) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +6 -2
  3. package/api-surface.json +234 -0
  4. package/dist/assets/paths.d.ts.map +1 -1
  5. package/dist/assets/paths.js +3 -0
  6. package/dist/assets/paths.js.map +1 -1
  7. package/dist/audio/Playable.d.ts.map +1 -1
  8. package/dist/audio/Playable.js +14 -1
  9. package/dist/audio/Playable.js.map +1 -1
  10. package/dist/audio/Synth.d.ts +14 -0
  11. package/dist/audio/Synth.d.ts.map +1 -1
  12. package/dist/audio/Synth.js +16 -0
  13. package/dist/audio/Synth.js.map +1 -1
  14. package/dist/audio/index.d.ts +1 -1
  15. package/dist/audio/index.d.ts.map +1 -1
  16. package/dist/audio/index.js +1 -1
  17. package/dist/audio/index.js.map +1 -1
  18. package/dist/core/FixedStepper.d.ts +38 -0
  19. package/dist/core/FixedStepper.d.ts.map +1 -0
  20. package/dist/core/FixedStepper.js +56 -0
  21. package/dist/core/FixedStepper.js.map +1 -0
  22. package/dist/core/Input.d.ts +61 -0
  23. package/dist/core/Input.d.ts.map +1 -1
  24. package/dist/core/Input.js +101 -0
  25. package/dist/core/Input.js.map +1 -1
  26. package/dist/core/StoredValue.d.ts +11 -2
  27. package/dist/core/StoredValue.d.ts.map +1 -1
  28. package/dist/core/StoredValue.js +11 -2
  29. package/dist/core/StoredValue.js.map +1 -1
  30. package/dist/core/index.d.ts +3 -1
  31. package/dist/core/index.d.ts.map +1 -1
  32. package/dist/core/index.js +3 -1
  33. package/dist/core/index.js.map +1 -1
  34. package/dist/mw_games.global.js +121 -121
  35. package/dist/physics/PhysicsWorld.d.ts +123 -0
  36. package/dist/physics/PhysicsWorld.d.ts.map +1 -0
  37. package/dist/physics/PhysicsWorld.js +137 -0
  38. package/dist/physics/PhysicsWorld.js.map +1 -0
  39. package/dist/physics/XpbdWorld.d.ts +139 -0
  40. package/dist/physics/XpbdWorld.d.ts.map +1 -0
  41. package/dist/physics/XpbdWorld.js +207 -0
  42. package/dist/physics/XpbdWorld.js.map +1 -0
  43. package/dist/physics/index.d.ts +3 -0
  44. package/dist/physics/index.d.ts.map +1 -0
  45. package/dist/physics/index.js +2 -0
  46. package/dist/physics/index.js.map +1 -0
  47. package/dist/physics/xpbd.d.ts +3 -0
  48. package/dist/physics/xpbd.d.ts.map +1 -0
  49. package/dist/physics/xpbd.js +2 -0
  50. package/dist/physics/xpbd.js.map +1 -0
  51. package/dist/two-d/render/Camera.d.ts +6 -0
  52. package/dist/two-d/render/Camera.d.ts.map +1 -1
  53. package/dist/two-d/render/Camera.js +9 -0
  54. package/dist/two-d/render/Camera.js.map +1 -1
  55. package/dist/two-d/render/Particles.d.ts +10 -1
  56. package/dist/two-d/render/Particles.d.ts.map +1 -1
  57. package/dist/two-d/render/Particles.js +14 -7
  58. package/dist/two-d/render/Particles.js.map +1 -1
  59. package/dist/two-d/render/Shape2D.d.ts +7 -0
  60. package/dist/two-d/render/Shape2D.d.ts.map +1 -1
  61. package/dist/two-d/render/Shape2D.js +18 -0
  62. package/dist/two-d/render/Shape2D.js.map +1 -1
  63. package/dist/two-d/render/SpriteSheet.d.ts +5 -0
  64. package/dist/two-d/render/SpriteSheet.d.ts.map +1 -1
  65. package/dist/two-d/render/SpriteSheet.js +15 -0
  66. package/dist/two-d/render/SpriteSheet.js.map +1 -1
  67. package/dist/two-d/ui/Button.d.ts +7 -0
  68. package/dist/two-d/ui/Button.d.ts.map +1 -1
  69. package/dist/two-d/ui/Button.js +16 -2
  70. package/dist/two-d/ui/Button.js.map +1 -1
  71. package/dist/two-d/ui/Dropdown.d.ts +3 -0
  72. package/dist/two-d/ui/Dropdown.d.ts.map +1 -1
  73. package/dist/two-d/ui/Dropdown.js +3 -0
  74. package/dist/two-d/ui/Dropdown.js.map +1 -1
  75. package/dist/two-d/ui/DropdownView.d.ts +40 -0
  76. package/dist/two-d/ui/DropdownView.d.ts.map +1 -0
  77. package/dist/two-d/ui/DropdownView.js +75 -0
  78. package/dist/two-d/ui/DropdownView.js.map +1 -0
  79. package/dist/two-d/ui/Label.d.ts +2 -0
  80. package/dist/two-d/ui/Label.d.ts.map +1 -1
  81. package/dist/two-d/ui/Label.js +7 -0
  82. package/dist/two-d/ui/Label.js.map +1 -1
  83. package/dist/two-d/ui/Layout.d.ts +34 -0
  84. package/dist/two-d/ui/Layout.d.ts.map +1 -1
  85. package/dist/two-d/ui/Layout.js +47 -0
  86. package/dist/two-d/ui/Layout.js.map +1 -1
  87. package/dist/two-d/ui/Window.d.ts +7 -0
  88. package/dist/two-d/ui/Window.d.ts.map +1 -1
  89. package/dist/two-d/ui/Window.js +13 -0
  90. package/dist/two-d/ui/Window.js.map +1 -1
  91. package/dist/two-d/ui/index.d.ts +4 -2
  92. package/dist/two-d/ui/index.d.ts.map +1 -1
  93. package/dist/two-d/ui/index.js +2 -1
  94. package/dist/two-d/ui/index.js.map +1 -1
  95. package/dist/version.d.ts +1 -1
  96. package/dist/version.js +1 -1
  97. package/package.json +22 -3
  98. package/sbom.cdx.json +64 -12
  99. package/tools/browser-driver.d.mts +14 -0
  100. package/tools/browser-driver.mjs +86 -10
  101. package/tools/build-tablebases.mjs +6 -2
  102. package/tools/classic-html.mjs +9 -1
  103. package/tools/compile-openings.mjs +6 -2
  104. package/tools/compile-resources.d.mts +5 -0
  105. package/tools/compile-resources.mjs +45 -4
  106. package/tools/compress-dist.mjs +15 -3
  107. package/tools/emit-page.mjs +5 -1
  108. package/tools/init.d.mts +5 -0
  109. package/tools/init.mjs +145 -0
  110. package/tools/play-uci.mjs +6 -3
  111. package/tools/sbom.mjs +5 -1
  112. package/tools/single-file.mjs +9 -1
  113. package/tools/vite.mjs +5 -0
  114. package/tools/webp-convert.mjs +9 -1
package/tools/init.mjs ADDED
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from 'node:fs';
3
+ import { basename, dirname, join, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ /**
7
+ * `mwg-init <folder>`: the smallest game that builds into one double-clickable page.
8
+ *
9
+ * It writes a vite project that uses `mwgPage()`: one scene, a moving square, a vite config
10
+ * that loads without `"type": "module"` in the way `.mts` files do, and the scripts to run
11
+ * and build it. The point is that the nearest example to copy is already on disk, in the
12
+ * shape the published package supports, rather than a folder of this repository's own
13
+ * examples that import their sources by relative path.
14
+ */
15
+ const PACKAGE_VERSION = JSON.parse(
16
+ readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8'),
17
+ ).version;
18
+
19
+ /** the project's files as `{ path: content }`, named after `name` */
20
+ export function initFiles(name) {
21
+ return {
22
+ 'package.json':
23
+ JSON.stringify(
24
+ {
25
+ name,
26
+ private: true,
27
+ version: '0.0.0',
28
+ type: 'module',
29
+ scripts: { dev: 'vite', build: 'vite build', check: 'tsc --noEmit' },
30
+ dependencies: { '@datamoc/mw_games': `^${PACKAGE_VERSION}`, 'pixi.js': '^8.20.1' },
31
+ devDependencies: { typescript: '^7.0.2', vite: '^8.2.2' },
32
+ },
33
+ null,
34
+ 2,
35
+ ) + '\n',
36
+ 'vite.config.mts': [
37
+ "import { defineConfig } from 'vite';",
38
+ "import { mwgPage } from '@datamoc/mw_games/tools/vite';",
39
+ '',
40
+ '//`singleFile: true` also writes dist/standalone.html, the whole game in one file',
41
+ 'export default defineConfig({ plugins: [mwgPage({ singleFile: true })] });',
42
+ '',
43
+ ].join('\n'),
44
+ 'tsconfig.json':
45
+ JSON.stringify(
46
+ {
47
+ compilerOptions: {
48
+ target: 'ES2022',
49
+ module: 'ESNext',
50
+ moduleResolution: 'bundler',
51
+ lib: ['ES2022', 'DOM'],
52
+ strict: true,
53
+ noEmit: true,
54
+ skipLibCheck: true,
55
+ },
56
+ include: ['src'],
57
+ },
58
+ null,
59
+ 2,
60
+ ) + '\n',
61
+ 'index.html': [
62
+ '<!DOCTYPE html>',
63
+ '<html lang="en">',
64
+ '<head>',
65
+ '\t<meta charset="utf-8">',
66
+ '\t<meta name="viewport" content="width=device-width, initial-scale=1">',
67
+ `\t<title>${name}</title>`,
68
+ '\t<style>html, body { margin: 0; height: 100%; background: #101018; overflow: hidden; } #game { display: block; width: 100vw; height: 100vh; }</style>',
69
+ '</head>',
70
+ '<body>',
71
+ '\t<canvas id="game"></canvas>',
72
+ '\t<script type="module" src="./src/main.ts"></script>',
73
+ '</body>',
74
+ '</html>',
75
+ '',
76
+ ].join('\n'),
77
+ 'src/main.ts': [
78
+ "import { Game, Scene2D } from '@datamoc/mw_games/two-d';",
79
+ "import { Shape2D, Text2D } from '@datamoc/mw_games/two-d/render';",
80
+ '',
81
+ 'class MainScene extends Scene2D {',
82
+ '\tprivate square!: Shape2D;',
83
+ '\tprivate elapsed = 0;',
84
+ '',
85
+ '\toverride create(): void {',
86
+ "\t\tconst label = new Text2D({ text: 'Hello, mwg!', style: { fill: 0xd8dae6, fontFamily: 'monospace', fontSize: 16 } });",
87
+ '\t\tlabel.position.set(24, 24);',
88
+ '\t\tthis.stage.addChild(label);',
89
+ '\t\tthis.square = new Shape2D().rect(-20, -20, 40, 40).fill(0x6fb1ff);',
90
+ '\t\tthis.stage.addChild(this.square);',
91
+ '\t}',
92
+ '',
93
+ '\toverride update(dt: number): void {',
94
+ '\t\tthis.elapsed += dt;',
95
+ '\t\tthis.square.position.set(Game.current.width / 2 + Math.sin(this.elapsed) * 120, Game.current.height / 2);',
96
+ '\t}',
97
+ '}',
98
+ '',
99
+ "const game = new Game({ canvas: document.getElementById('game') as HTMLCanvasElement, background: 0x101018 });",
100
+ 'game.start(MainScene).catch((error) => console.error(error));',
101
+ '',
102
+ ].join('\n'),
103
+ 'assets/.gitkeep': '',
104
+ 'README.md': `# ${name}\n\n\`npm install\`, then \`npm run dev\` to play it while you edit, \`npm run build\` to write \`dist/\`.\nOpen \`dist/index.html\` (or \`dist/standalone.html\`) by double-clicking it: no server needed.\n\nPut images and sounds under \`assets/\`; the build compiles them into the page. An \`assets/credits.json\`\n(\`[{ "name": "...", "license": "...", "author": "...", "url": "..." }]\`) becomes \`dist/CREDITS.txt\`.\n`,
105
+ };
106
+ }
107
+
108
+ /** writes the project into `folder`; refuses a folder that already holds files, so nothing is overwritten */
109
+ export function initProject(folder) {
110
+ const target = resolve(folder);
111
+ if (existsSync(target) && readdirSync(target).length > 0) {
112
+ throw new Error(`${target} is not empty; run mwg-init in a new or empty folder`);
113
+ }
114
+ const written = [];
115
+ for (const [path, content] of Object.entries(
116
+ initFiles(
117
+ basename(target)
118
+ .toLowerCase()
119
+ .replace(/[^a-z0-9-]/g, '-'),
120
+ ),
121
+ )) {
122
+ const file = join(target, path);
123
+ mkdirSync(dirname(file), { recursive: true });
124
+ writeFileSync(file, content, 'utf8');
125
+ written.push(path);
126
+ }
127
+ return { folder: target, files: written };
128
+ }
129
+
130
+ //no top-level await: this module is also imported, and the real path covers an npm bin symlink
131
+ if (process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {
132
+ const folder = process.argv[2];
133
+ if (!folder) {
134
+ console.error('usage: mwg-init <new folder>');
135
+ process.exitCode = 2;
136
+ } else {
137
+ try {
138
+ const { folder: target, files } = initProject(folder);
139
+ console.log(`wrote ${files.length} files to ${target}\nnext: cd ${folder} && npm install && npm run dev`);
140
+ } catch (error) {
141
+ console.error(error.message);
142
+ process.exitCode = 1;
143
+ }
144
+ }
145
+ }
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync, readdirSync } from 'node:fs';
3
- import { join, basename, extname } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { readFileSync, readdirSync, realpathSync } from 'node:fs';
4
+ import { join, extname } from 'node:path';
4
5
  import { createInterface } from 'node:readline';
5
6
  import { applyMove, cloneChess, legalMoves, parseFen, squareName, startingChess } from '../src/board/chess.ts';
6
7
  import { parseUciMove, probeBook } from '../src/board/openings.ts';
@@ -164,7 +165,9 @@ export function createUciSession(options = {}) {
164
165
  };
165
166
  }
166
167
 
167
- const isMain = process.argv[1] && import.meta.url.endsWith(basename(process.argv[1]));
168
+ //the real path, since an npm `bin` shim reaches this file through a symlink; a suffix test on the
169
+ //name is not enough (`compress-dist.mjs` ends with `t.mjs`)
170
+ const isMain = process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
168
171
 
169
172
  if (isMain) {
170
173
  const args = process.argv.slice(2);
package/tools/sbom.mjs CHANGED
@@ -328,5 +328,9 @@ async function main(argv) {
328
328
  }
329
329
 
330
330
  //realpath, since an npm `bin` shim reaches this file through a symlink
331
+ //no top-level await: a vite config loaded as CommonJS has to be able to require() this module
331
332
  if (process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url))
332
- await main(process.argv.slice(2));
333
+ main(process.argv.slice(2)).catch((error) => {
334
+ console.error(error);
335
+ process.exitCode = 1;
336
+ });
@@ -200,7 +200,7 @@ function renderBootstrap(payloads, algorithm) {
200
200
 
201
201
  const isMain = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
202
202
 
203
- if (isMain) {
203
+ async function runCli() {
204
204
  const args = process.argv.slice(2);
205
205
  const dist = args[0];
206
206
  if (!dist || dist.startsWith('--')) {
@@ -229,3 +229,11 @@ if (isMain) {
229
229
  );
230
230
  console.log(' one file, no server, no sibling script - open it directly');
231
231
  }
232
+
233
+ //no top-level await: a vite config loaded as CommonJS has to be able to require() this module
234
+ if (isMain) {
235
+ runCli().catch((error) => {
236
+ console.error(error);
237
+ process.exitCode = 1;
238
+ });
239
+ }
package/tools/vite.mjs CHANGED
@@ -50,6 +50,11 @@ export function mwgPage(options = {}) {
50
50
  reportCompressedSize: false,
51
51
  rollupOptions: {
52
52
  output: { format: 'iife', entryFileNames: 'game.js', assetFileNames: '[name][extname]' },
53
+ //an iife has no import.meta, and the bundler already swaps it for {} (vite's own
54
+ //preload helper reads it); the warning on every build is noise, not information
55
+ onLog(level, log, defaultHandler) {
56
+ if (log.code !== 'EMPTY_IMPORT_META') defaultHandler(level, log);
57
+ },
53
58
  },
54
59
  },
55
60
  };
@@ -58,7 +58,7 @@ export const WEBP_CONVERTIBLE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg']);
58
58
 
59
59
  const isMain = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
60
60
 
61
- if (isMain) {
61
+ async function runCli() {
62
62
  const args = process.argv.slice(2);
63
63
  const input = args.find((a) => !a.startsWith('--'));
64
64
  if (!input) {
@@ -88,3 +88,11 @@ if (isMain) {
88
88
  (lossless ? ' [lossless]' : ` [lossy, quality ${quality}]`),
89
89
  );
90
90
  }
91
+
92
+ //no top-level await: a vite config loaded as CommonJS has to be able to require() this module
93
+ if (isMain) {
94
+ runCli().catch((error) => {
95
+ console.error(error);
96
+ process.exitCode = 1;
97
+ });
98
+ }