samengine 1.9.0 → 1.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 (87) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +203 -0
  3. package/dist/config/buildconfig.d.ts +146 -0
  4. package/dist/config/buildconfig.js +115 -0
  5. package/dist/config/index.d.ts +9 -0
  6. package/dist/config/index.js +1 -0
  7. package/dist/core.d.ts +17 -0
  8. package/dist/core.js +24 -0
  9. package/dist/html.d.ts +29 -0
  10. package/dist/html.js +20 -0
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.js +1 -2
  13. package/dist/input.d.ts +51 -0
  14. package/dist/input.js +44 -3
  15. package/dist/keys.d.ts +6 -0
  16. package/dist/keys.js +6 -2
  17. package/dist/logger.d.ts +8 -0
  18. package/dist/logger.js +8 -1
  19. package/dist/nonbrowser/getversion.d.ts +13 -0
  20. package/dist/nonbrowser/getversion.js +35 -0
  21. package/dist/nonbrowser/ghresolver.d.ts +1 -0
  22. package/dist/nonbrowser/ghresolver.js +7 -0
  23. package/dist/nonbrowser/index.d.ts +9 -0
  24. package/dist/nonbrowser/index.js +9 -0
  25. package/dist/nonbrowser/internal/buildhelper.d.ts +42 -0
  26. package/dist/nonbrowser/internal/buildhelper.js +144 -0
  27. package/dist/nonbrowser/internal/cli/argparser.d.ts +20 -0
  28. package/dist/nonbrowser/internal/cli/argparser.js +36 -0
  29. package/dist/nonbrowser/internal/cli/main.d.ts +13 -0
  30. package/dist/nonbrowser/internal/cli/main.js +262 -0
  31. package/dist/nonbrowser/internal/config.d.ts +9 -0
  32. package/dist/nonbrowser/internal/config.js +40 -0
  33. package/dist/nonbrowser/internal/exporthtml.d.ts +37 -0
  34. package/dist/nonbrowser/internal/exporthtml.js +622 -0
  35. package/dist/nonbrowser/internal/projcreator/downloadZip.d.ts +4 -0
  36. package/dist/nonbrowser/internal/projcreator/downloadZip.js +83 -0
  37. package/dist/nonbrowser/internal/projcreator/main.d.ts +1 -0
  38. package/dist/nonbrowser/internal/projcreator/main.js +81 -0
  39. package/dist/nonbrowser/utils.d.ts +8 -0
  40. package/dist/nonbrowser/utils.js +18 -0
  41. package/dist/physics/collision.d.ts +33 -0
  42. package/dist/physics/collision.js +27 -0
  43. package/dist/physics/physicsEngine.d.ts +18 -0
  44. package/dist/physics/physicsEngine.js +18 -0
  45. package/dist/physics/physicsObject.d.ts +20 -0
  46. package/dist/physics/physicsObject.js +20 -0
  47. package/dist/renderer.d.ts +85 -2
  48. package/dist/renderer.js +86 -7
  49. package/dist/samegui/index.d.ts +49 -0
  50. package/dist/samegui/index.js +137 -0
  51. package/dist/save.d.ts +30 -0
  52. package/dist/save.js +25 -0
  53. package/dist/sound/audioplayer.d.ts +39 -0
  54. package/dist/sound/audioplayer.js +39 -5
  55. package/dist/storage/index.d.ts +57 -0
  56. package/dist/storage/index.js +89 -0
  57. package/dist/text/index.d.ts +14 -0
  58. package/dist/text/index.js +58 -0
  59. package/dist/texture.d.ts +100 -0
  60. package/dist/texture.js +75 -41
  61. package/dist/types/button.d.ts +25 -0
  62. package/dist/types/button.js +22 -0
  63. package/dist/types/circle.d.ts +26 -0
  64. package/dist/types/circle.js +21 -7
  65. package/dist/types/color.d.ts +17 -0
  66. package/dist/types/color.js +11 -1
  67. package/dist/types/index.d.ts +1 -1
  68. package/dist/types/index.js +1 -1
  69. package/dist/types/rectangle.d.ts +29 -0
  70. package/dist/types/rectangle.js +23 -6
  71. package/dist/types/triangle.d.ts +23 -0
  72. package/dist/types/triangle.js +20 -6
  73. package/dist/types/vector2d.d.ts +42 -0
  74. package/dist/types/vector2d.js +39 -11
  75. package/dist/types/vector3d.d.ts +38 -0
  76. package/dist/types/vector3d.js +35 -11
  77. package/dist/utils/index.d.ts +13 -4
  78. package/dist/utils/index.js +26 -2
  79. package/dist/utils/logger/index.d.ts +24 -0
  80. package/dist/utils/logger/index.js +44 -0
  81. package/dist/utils/math.d.ts +18 -0
  82. package/dist/utils/math.js +18 -4
  83. package/package.json +40 -10
  84. package/dist/utils/jsonc-parser.d.ts +0 -4
  85. package/dist/utils/jsonc-parser.js +0 -166
  86. package/dist/utils/markdown.d.ts +0 -41
  87. package/dist/utils/markdown.js +0 -699
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Public package API for configuration files.
3
+ *
4
+ * Game projects import these types and factory functions from
5
+ * `samengine-build` when defining `samengine.config.ts`. The executable CLI is
6
+ * separate from this entry point and lives under `src/cli`.
7
+ */
8
+ export type { buildconfig, Paragraph, MarkdownStyle, SameGUI, HTMLMenuSettingOption, HTMLMenuSetting, HTMLMenu, HTMLMenuStyle, HTMLMenuText, } from "./buildconfig.js";
9
+ export { newSameGUI, new_buildconfig, svgfile, newHTMLMenu, newDevProfile, newReleaseProfile, newMarkdownStyle } from "./buildconfig.js";
@@ -0,0 +1 @@
1
+ export { newSameGUI, new_buildconfig, svgfile, newHTMLMenu, newDevProfile, newReleaseProfile, newMarkdownStyle } from "./buildconfig.js";
package/dist/core.d.ts CHANGED
@@ -1,3 +1,20 @@
1
1
  type GameLoop = (dt: number) => void;
2
+ /**
3
+ * Starts the central game loop of samengine.
4
+ *
5
+ * The `start` callback is executed once before the first frame. Use it for
6
+ * setup work such as creating objects, loading initial state, or configuring
7
+ * input. After that, `gameLoop` is called every animation frame with `dt`, the
8
+ * elapsed time in seconds since the previous frame.
9
+ *
10
+ * `dt` should be used for frame-rate independent movement:
11
+ *
12
+ * ```ts
13
+ * player.x += player.speed * dt;
14
+ * ```
15
+ *
16
+ * @param start Function that runs once before the loop starts.
17
+ * @param gameLoop Function that runs every frame. Receives delta time in seconds.
18
+ */
2
19
  export declare function startEngine(start: () => void, gameLoop: GameLoop): void;
3
20
  export {};
package/dist/core.js CHANGED
@@ -1,10 +1,34 @@
1
1
  let lastTime = 0;
2
2
  let loop;
3
+ /**
4
+ * Starts the central game loop of samengine.
5
+ *
6
+ * The `start` callback is executed once before the first frame. Use it for
7
+ * setup work such as creating objects, loading initial state, or configuring
8
+ * input. After that, `gameLoop` is called every animation frame with `dt`, the
9
+ * elapsed time in seconds since the previous frame.
10
+ *
11
+ * `dt` should be used for frame-rate independent movement:
12
+ *
13
+ * ```ts
14
+ * player.x += player.speed * dt;
15
+ * ```
16
+ *
17
+ * @param start Function that runs once before the loop starts.
18
+ * @param gameLoop Function that runs every frame. Receives delta time in seconds.
19
+ */
3
20
  export function startEngine(start, gameLoop) {
4
21
  loop = gameLoop;
5
22
  start();
6
23
  requestAnimationFrame(run);
7
24
  }
25
+ /**
26
+ * Internal `requestAnimationFrame` callback.
27
+ *
28
+ * Converts the browser timestamp from milliseconds to a seconds-based delta and
29
+ * schedules the next frame. This function is intentionally not exported because
30
+ * consumers should control the engine through `startEngine`.
31
+ */
8
32
  function run(time) {
9
33
  const dt = (time - lastTime) / 1000;
10
34
  lastTime = time;
package/dist/html.d.ts CHANGED
@@ -1,11 +1,27 @@
1
1
  export type CanvasConfig = {
2
+ /** Fixed canvas width when `fullscreen` is false. Defaults to 800. */
2
3
  width?: number;
4
+ /** Fixed canvas height when `fullscreen` is false. Defaults to 800. */
3
5
  height?: number;
6
+ /** If true, the canvas is resized to the browser window. */
4
7
  fullscreen?: boolean;
8
+ /**
9
+ * Scaling mode. `"fit"` keeps a virtual resolution and letterboxes it into
10
+ * the real canvas. `"none"` draws directly in canvas pixels.
11
+ */
5
12
  scaling?: "none" | "fit";
13
+ /** Logical game width used by `"fit"` scaling. Defaults to 800. */
6
14
  virtualWidth?: number;
15
+ /** Logical game height used by `"fit"` scaling. Defaults to 800. */
7
16
  virtualHeight?: number;
8
17
  };
18
+ /**
19
+ * Creates a canvas, appends it to `document.body`, and returns its 2D context.
20
+ *
21
+ * When `scaling` is `"fit"`, call `applyScaling()` at the beginning of each
22
+ * frame before drawing game objects. This sets the canvas transform so your game
23
+ * can render in virtual coordinates independent of the real browser size.
24
+ */
9
25
  export declare function createCanvas(config?: CanvasConfig): {
10
26
  canvas: HTMLCanvasElement;
11
27
  ctx: CanvasRenderingContext2D;
@@ -13,6 +29,19 @@ export declare function createCanvas(config?: CanvasConfig): {
13
29
  virtualWidth: number;
14
30
  virtualHeight: number;
15
31
  };
32
+ /**
33
+ * Resizes an existing canvas to the browser window size.
34
+ */
16
35
  export declare function resizeCanvas(canvas: HTMLCanvasElement): void;
36
+ /**
37
+ * Adds a keyboard shortcut for fullscreen mode.
38
+ *
39
+ * Pressing the `f` key toggles fullscreen for the provided canvas.
40
+ */
17
41
  export declare function enableFullscreen(canvas: HTMLCanvasElement): void;
42
+ /**
43
+ * Connects an existing DOM element with id `fullscreenBtn` to fullscreen mode.
44
+ *
45
+ * If no element with that id exists, the function does nothing.
46
+ */
18
47
  export declare function setupFullscreenButton(canvas: HTMLCanvasElement): void;
package/dist/html.js CHANGED
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Creates a canvas, appends it to `document.body`, and returns its 2D context.
3
+ *
4
+ * When `scaling` is `"fit"`, call `applyScaling()` at the beginning of each
5
+ * frame before drawing game objects. This sets the canvas transform so your game
6
+ * can render in virtual coordinates independent of the real browser size.
7
+ */
1
8
  export function createCanvas(config = {}) {
2
9
  const canvas = document.createElement("canvas");
3
10
  const ctx = canvas.getContext("2d");
@@ -35,10 +42,18 @@ export function createCanvas(config = {}) {
35
42
  virtualHeight,
36
43
  };
37
44
  }
45
+ /**
46
+ * Resizes an existing canvas to the browser window size.
47
+ */
38
48
  export function resizeCanvas(canvas) {
39
49
  canvas.width = window.innerWidth;
40
50
  canvas.height = window.innerHeight;
41
51
  }
52
+ /**
53
+ * Adds a keyboard shortcut for fullscreen mode.
54
+ *
55
+ * Pressing the `f` key toggles fullscreen for the provided canvas.
56
+ */
42
57
  export function enableFullscreen(canvas) {
43
58
  window.addEventListener("keydown", (e) => {
44
59
  if (e.key === "f") {
@@ -51,6 +66,11 @@ export function enableFullscreen(canvas) {
51
66
  }
52
67
  });
53
68
  }
69
+ /**
70
+ * Connects an existing DOM element with id `fullscreenBtn` to fullscreen mode.
71
+ *
72
+ * If no element with that id exists, the function does nothing.
73
+ */
54
74
  export function setupFullscreenButton(canvas) {
55
75
  const btn = document.getElementById("fullscreenBtn");
56
76
  if (!btn)
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { startEngine } from "./core.js";
2
- export { renderText, renderBitmapText, drawRect, drawRectOutline, drawCircle, drawCircleOutline, drawTriangle, drawTriangleOutline, } from "./renderer.js";
2
+ export type { CharMap, ParallaxLayer } from "./renderer.js";
3
+ export { renderText, renderBitmapText, drawRect, drawRectOutline, drawCircle, drawCircleOutline, drawTriangle, drawTriangleOutline, renderParallaxBackground, renderParallaxLayers, } from "./renderer.js";
3
4
  export type { Mouse } from "./input.js";
4
5
  export { setupInput, isKeyPressed, isKeyJustPressed, isKeyJustReleased, resetInput, getMouse } from "./input.js";
5
6
  export { dlog } from "./logger.js";
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  // Core Engine Exports
2
2
  export { startEngine } from "./core.js";
3
- // Rendering
4
- export { renderText, renderBitmapText, drawRect, drawRectOutline, drawCircle, drawCircleOutline, drawTriangle, drawTriangleOutline, } from "./renderer.js";
3
+ export { renderText, renderBitmapText, drawRect, drawRectOutline, drawCircle, drawCircleOutline, drawTriangle, drawTriangleOutline, renderParallaxBackground, renderParallaxLayers, } from "./renderer.js";
5
4
  export { setupInput, isKeyPressed, isKeyJustPressed, isKeyJustReleased, resetInput, getMouse } from "./input.js";
6
5
  // Logging
7
6
  export { dlog } from "./logger.js";
package/dist/input.d.ts CHANGED
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Snapshot of the current mouse state in virtual canvas coordinates.
3
+ *
4
+ * The left mouse button uses `pressed`, `justPressed`, and `justReleased`.
5
+ * The right mouse button uses the matching `right...` fields. `wheelDelta`
6
+ * stores the most recent wheel event delta and is reset by `resetInput`.
7
+ */
1
8
  export type Mouse = {
2
9
  x: number;
3
10
  y: number;
@@ -9,9 +16,53 @@ export type Mouse = {
9
16
  rightjustReleased: boolean;
10
17
  wheelDelta: number;
11
18
  };
19
+ /**
20
+ * Installs keyboard and mouse listeners for the engine.
21
+ *
22
+ * Mouse coordinates are converted into the virtual coordinate system used by
23
+ * `createCanvas({ scaling: "fit" })`. Pass the same virtual width and height
24
+ * here that you use for canvas scaling, otherwise mouse hit tests will not line
25
+ * up with rendered objects.
26
+ *
27
+ * Call this once during game setup.
28
+ *
29
+ * @param canvas Canvas that should receive mouse events.
30
+ * @param vWidth Width of the virtual game area. Defaults to 800.
31
+ * @param vHeight Height of the virtual game area. Defaults to 800.
32
+ */
12
33
  export declare function setupInput(canvas: HTMLCanvasElement, vWidth?: number, vHeight?: number): void;
34
+ /**
35
+ * Returns whether the given keyboard code is currently held down.
36
+ *
37
+ * Use values from the `Key` enum for autocomplete-friendly input names.
38
+ */
13
39
  export declare function isKeyPressed(code: string): boolean;
40
+ /**
41
+ * Returns true only during the frame in which a key became pressed.
42
+ *
43
+ * Call `resetInput()` once at the end of each game loop frame so this one-frame
44
+ * flag can be cleared.
45
+ */
14
46
  export declare function isKeyJustPressed(code: string): boolean;
47
+ /**
48
+ * Returns true only during the frame in which a key was released.
49
+ *
50
+ * Call `resetInput()` once at the end of each game loop frame so this one-frame
51
+ * flag can be cleared.
52
+ */
15
53
  export declare function isKeyJustReleased(code: string): boolean;
54
+ /**
55
+ * Returns a read-only copy of the current mouse state.
56
+ *
57
+ * A copy is returned so game code cannot accidentally mutate the internal input
58
+ * state. Use this object for hit tests such as `isMouseInRect` or
59
+ * `isRectClicked`.
60
+ */
16
61
  export declare function getMouse(): Readonly<Mouse>;
62
+ /**
63
+ * Clears all one-frame input flags.
64
+ *
65
+ * This should usually be called once at the end of each frame, after your game
66
+ * logic has consumed `justPressed`, `justReleased`, and `wheelDelta`.
67
+ */
17
68
  export declare function resetInput(): void;
package/dist/input.js CHANGED
@@ -17,6 +17,20 @@ let canvasRef;
17
17
  // 👉 optional: für scaling (kannst du später aus deiner engine holen)
18
18
  let virtualWidth = 800;
19
19
  let virtualHeight = 800;
20
+ /**
21
+ * Installs keyboard and mouse listeners for the engine.
22
+ *
23
+ * Mouse coordinates are converted into the virtual coordinate system used by
24
+ * `createCanvas({ scaling: "fit" })`. Pass the same virtual width and height
25
+ * here that you use for canvas scaling, otherwise mouse hit tests will not line
26
+ * up with rendered objects.
27
+ *
28
+ * Call this once during game setup.
29
+ *
30
+ * @param canvas Canvas that should receive mouse events.
31
+ * @param vWidth Width of the virtual game area. Defaults to 800.
32
+ * @param vHeight Height of the virtual game area. Defaults to 800.
33
+ */
20
34
  export function setupInput(canvas, vWidth = 800, vHeight = 800) {
21
35
  canvasRef = canvas;
22
36
  virtualWidth = vWidth;
@@ -83,21 +97,48 @@ export function setupInput(canvas, vWidth = 800, vHeight = 800) {
83
97
  mouse.rightPressed = false;
84
98
  });
85
99
  }
86
- // ===== KEY HELPERS =====
100
+ /**
101
+ * Returns whether the given keyboard code is currently held down.
102
+ *
103
+ * Use values from the `Key` enum for autocomplete-friendly input names.
104
+ */
87
105
  export function isKeyPressed(code) {
88
106
  return keys[code]?.pressed || false;
89
107
  }
108
+ /**
109
+ * Returns true only during the frame in which a key became pressed.
110
+ *
111
+ * Call `resetInput()` once at the end of each game loop frame so this one-frame
112
+ * flag can be cleared.
113
+ */
90
114
  export function isKeyJustPressed(code) {
91
115
  return keys[code]?.justPressed || false;
92
116
  }
117
+ /**
118
+ * Returns true only during the frame in which a key was released.
119
+ *
120
+ * Call `resetInput()` once at the end of each game loop frame so this one-frame
121
+ * flag can be cleared.
122
+ */
93
123
  export function isKeyJustReleased(code) {
94
124
  return keys[code]?.justReleased || false;
95
125
  }
96
- // ===== MOUSE =====
126
+ /**
127
+ * Returns a read-only copy of the current mouse state.
128
+ *
129
+ * A copy is returned so game code cannot accidentally mutate the internal input
130
+ * state. Use this object for hit tests such as `isMouseInRect` or
131
+ * `isRectClicked`.
132
+ */
97
133
  export function getMouse() {
98
134
  return { ...mouse };
99
135
  }
100
- // ===== RESET =====
136
+ /**
137
+ * Clears all one-frame input flags.
138
+ *
139
+ * This should usually be called once at the end of each frame, after your game
140
+ * logic has consumed `justPressed`, `justReleased`, and `wheelDelta`.
141
+ */
101
142
  export function resetInput() {
102
143
  for (const k in keys) {
103
144
  keys[k].justPressed = false;
package/dist/keys.d.ts CHANGED
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Common `KeyboardEvent.code` values for autocomplete-friendly input checks.
3
+ *
4
+ * Use these values with `isKeyPressed`, `isKeyJustPressed`, and
5
+ * `isKeyJustReleased`.
6
+ */
1
7
  export declare enum Key {
2
8
  ArrowUp = "ArrowUp",
3
9
  ArrowDown = "ArrowDown",
package/dist/keys.js CHANGED
@@ -1,5 +1,9 @@
1
- // keys.ts
2
- // to view the Keys easily with the Autocomplete Feature
1
+ /**
2
+ * Common `KeyboardEvent.code` values for autocomplete-friendly input checks.
3
+ *
4
+ * Use these values with `isKeyPressed`, `isKeyJustPressed`, and
5
+ * `isKeyJustReleased`.
6
+ */
3
7
  export var Key;
4
8
  (function (Key) {
5
9
  Key["ArrowUp"] = "ArrowUp";
package/dist/logger.d.ts CHANGED
@@ -1 +1,9 @@
1
+ /**
2
+ * Debug logger used by samengine internals.
3
+ *
4
+ * In the current release build this is intentionally a no-op, so calls can stay
5
+ * in code without producing console output. The commented implementation above
6
+ * shows the development logger that can be re-enabled when needed.
7
+ * @deprecated Use samengine/utils/logger instead!
8
+ */
1
9
  export declare const dlog: (...args: any[]) => void;
package/dist/logger.js CHANGED
@@ -1,4 +1,11 @@
1
- // Debug Log Function
1
+ /**
2
+ * Debug logger used by samengine internals.
3
+ *
4
+ * In the current release build this is intentionally a no-op, so calls can stay
5
+ * in code without producing console output. The commented implementation above
6
+ * shows the development logger that can be re-enabled when needed.
7
+ * @deprecated Use samengine/utils/logger instead!
8
+ */
2
9
  export const dlog =
3
10
  // (import.meta.env?.DEV ?? true) // Default true, falls undefined
4
11
  // ? (...args: any[]) => {
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Reads the installed version of an npm package from its `package.json`.
3
+ *
4
+ * The CLI uses this to write the installed samengine version into generated
5
+ * HTML/JS metadata and into `window.__samengine__`. The optional `projectRoot`
6
+ * parameter makes the lookup reusable for tests or tools that need to inspect a
7
+ * different project directory.
8
+ *
9
+ * @throws If the package cannot be found in `node_modules`.
10
+ * @throws If the package manifest cannot be parsed as JSON.
11
+ * @throws If the package manifest does not contain a `version` field.
12
+ */
13
+ export declare function getPackageVersion(packageName: string, projectRoot?: string): string;
@@ -0,0 +1,35 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ /**
4
+ * Reads the installed version of an npm package from its `package.json`.
5
+ *
6
+ * The CLI uses this to write the installed samengine version into generated
7
+ * HTML/JS metadata and into `window.__samengine__`. The optional `projectRoot`
8
+ * parameter makes the lookup reusable for tests or tools that need to inspect a
9
+ * different project directory.
10
+ *
11
+ * @throws If the package cannot be found in `node_modules`.
12
+ * @throws If the package manifest cannot be parsed as JSON.
13
+ * @throws If the package manifest does not contain a `version` field.
14
+ */
15
+ export function getPackageVersion(packageName, projectRoot = process.cwd()) {
16
+ const packageJsonPath = path.join(projectRoot, "node_modules", packageName, "package.json");
17
+ let raw;
18
+ try {
19
+ raw = fs.readFileSync(packageJsonPath, "utf-8");
20
+ }
21
+ catch (err) {
22
+ throw new Error(`Package '${packageName}' was not found in node_modules`);
23
+ }
24
+ let packageJson;
25
+ try {
26
+ packageJson = JSON.parse(raw);
27
+ }
28
+ catch (err) {
29
+ throw new Error(`Invalid package.json for '${packageName}'`);
30
+ }
31
+ if (!packageJson.version) {
32
+ throw new Error(`No version field found in package.json of '${packageName}'`);
33
+ }
34
+ return packageJson.version;
35
+ }
@@ -0,0 +1 @@
1
+ export declare function getTemplateZipUrl(version: string, username: string, reponame: string): string;
@@ -0,0 +1,7 @@
1
+ export function getTemplateZipUrl(version, username, reponame) {
2
+ const base = `https://codeload.github.com/${username}/${reponame}/zip`;
3
+ if (version) {
4
+ return `${base}/refs/tags/${version}`;
5
+ }
6
+ return `${base}/refs/heads/main`;
7
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Public utility entry point.
3
+ *
4
+ * This file exists so consumers can import helper functions from
5
+ * `sanegine/nonbrowser` without depending on internal source paths.
6
+ */
7
+ export { compressHTML } from "./utils.js";
8
+ export { getPackageVersion } from "./getversion.js";
9
+ export { getTemplateZipUrl } from "./ghresolver.js";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Public utility entry point.
3
+ *
4
+ * This file exists so consumers can import helper functions from
5
+ * `sanegine/nonbrowser` without depending on internal source paths.
6
+ */
7
+ export { compressHTML } from "./utils.js";
8
+ export { getPackageVersion } from "./getversion.js";
9
+ export { getTemplateZipUrl } from "./ghresolver.js";
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Logs CLI messages with a timestamp down to milliseconds.
3
+ *
4
+ * The build tool can receive several file-system events in quick succession
5
+ * while watching a project. Timestamped logs make it easier to understand which
6
+ * rebuild, copy operation, or server action happened first.
7
+ */
8
+ export declare const flog: (...args: any[]) => void;
9
+ /**
10
+ * Recursively copies a folder from `src` to `dest`.
11
+ *
12
+ * The multi-file build uses this to copy `resources` into the output directory.
13
+ * Files are copied as raw buffers so binary assets such as images and audio are
14
+ * preserved exactly.
15
+ */
16
+ export declare function copyFolder(src: string, dest: string): Promise<void>;
17
+ /**
18
+ * Scans a resource folder and converts every file into a Base64 Data URI.
19
+ *
20
+ * The returned object uses paths relative to `resourceDir`, for example
21
+ * `sprites/player.png`. Single-file builds embed this map into the generated
22
+ * HTML so the game can load assets without separate files.
23
+ */
24
+ export declare function scanResourcesAsDataURIs(resourceDir: string): Promise<Record<string, string>>;
25
+ /** Returns the Content-Type used by the local development server. */
26
+ export declare function getContentType(path: string): string;
27
+ /**
28
+ * Returns the MIME type used when embedding a resource as a Data URI.
29
+ *
30
+ * Unknown file extensions fall back to `application/octet-stream`, which keeps
31
+ * the resource loadable even when the type is not explicitly listed here.
32
+ */
33
+ export declare function getMimeType(filename: string): string;
34
+ /**
35
+ * Removes resources that do not appear to be referenced by the bundled code.
36
+ *
37
+ * The check intentionally stays simple: it looks for the full relative resource
38
+ * path or just the filename inside the JavaScript bundle. This keeps
39
+ * single-file exports smaller, but it can only detect assets whose names remain
40
+ * visible as strings in the bundle.
41
+ */
42
+ export declare function filterResourcesByUsage(bundledJsContent: string, resourcesMap: Record<string, string>): Record<string, string>;
@@ -0,0 +1,144 @@
1
+ import { readdirSync, mkdirSync, promises as fsPromises, existsSync } from "fs";
2
+ import { join } from "path";
3
+ /**
4
+ * Logs CLI messages with a timestamp down to milliseconds.
5
+ *
6
+ * The build tool can receive several file-system events in quick succession
7
+ * while watching a project. Timestamped logs make it easier to understand which
8
+ * rebuild, copy operation, or server action happened first.
9
+ */
10
+ export const flog = (...args) => {
11
+ const now = new Date();
12
+ const time = `[${now.getHours().toString().padStart(2, "0")}:` +
13
+ `${now.getMinutes().toString().padStart(2, "0")}:` +
14
+ `${now.getSeconds().toString().padStart(2, "0")}.` +
15
+ `${now.getMilliseconds().toString().padStart(3, "0")}]`;
16
+ console.log(time, ...args);
17
+ };
18
+ /**
19
+ * Recursively copies a folder from `src` to `dest`.
20
+ *
21
+ * The multi-file build uses this to copy `resources` into the output directory.
22
+ * Files are copied as raw buffers so binary assets such as images and audio are
23
+ * preserved exactly.
24
+ */
25
+ export async function copyFolder(src, dest) {
26
+ // Create the target folder before copying nested entries into it.
27
+ mkdirSync(dest, { recursive: true });
28
+ const entries = readdirSync(src, { withFileTypes: true });
29
+ for (const entry of entries) {
30
+ const srcPath = join(src, entry.name);
31
+ const destPath = join(dest, entry.name);
32
+ if (entry.isDirectory()) {
33
+ await copyFolder(srcPath, destPath);
34
+ }
35
+ else if (entry.isFile()) {
36
+ // Datei mit Node.js schreiben
37
+ const data = await fsPromises.readFile(srcPath);
38
+ await fsPromises.writeFile(destPath, data);
39
+ }
40
+ }
41
+ }
42
+ /**
43
+ * Scans a resource folder and converts every file into a Base64 Data URI.
44
+ *
45
+ * The returned object uses paths relative to `resourceDir`, for example
46
+ * `sprites/player.png`. Single-file builds embed this map into the generated
47
+ * HTML so the game can load assets without separate files.
48
+ */
49
+ export async function scanResourcesAsDataURIs(resourceDir) {
50
+ const resourceMap = {};
51
+ if (!existsSync(resourceDir)) {
52
+ return resourceMap;
53
+ }
54
+ async function scanDir(dir, basePath = "") {
55
+ const entries = readdirSync(dir, { withFileTypes: true });
56
+ for (const entry of entries) {
57
+ const fullPath = join(dir, entry.name);
58
+ const resourcePath = basePath ? `${basePath}/${entry.name}` : entry.name;
59
+ if (entry.isDirectory()) {
60
+ await scanDir(fullPath, resourcePath);
61
+ }
62
+ else if (entry.isFile()) {
63
+ const fileData = await fsPromises.readFile(fullPath);
64
+ const base64Data = fileData.toString("base64");
65
+ const mimeType = getMimeType(entry.name);
66
+ resourceMap[resourcePath] = `data:${mimeType};base64,${base64Data}`;
67
+ }
68
+ }
69
+ }
70
+ await scanDir(resourceDir);
71
+ return resourceMap;
72
+ }
73
+ // === Helper ===
74
+ /** Returns the Content-Type used by the local development server. */
75
+ export function getContentType(path) {
76
+ if (path.endsWith(".js"))
77
+ return "application/javascript";
78
+ if (path.endsWith(".ts"))
79
+ return "application/typescript";
80
+ if (path.endsWith(".html"))
81
+ return "text/html";
82
+ if (path.endsWith(".css"))
83
+ return "text/css";
84
+ if (path.endsWith(".png"))
85
+ return "image/png";
86
+ if (path.endsWith(".svg"))
87
+ return "image/svg+xml";
88
+ return "text/plain";
89
+ }
90
+ /**
91
+ * Returns the MIME type used when embedding a resource as a Data URI.
92
+ *
93
+ * Unknown file extensions fall back to `application/octet-stream`, which keeps
94
+ * the resource loadable even when the type is not explicitly listed here.
95
+ */
96
+ export function getMimeType(filename) {
97
+ const ext = filename.toLowerCase().split(".").pop() || "";
98
+ const mimeTypes = {
99
+ "png": "image/png",
100
+ "jpg": "image/jpeg",
101
+ "jpeg": "image/jpeg",
102
+ "gif": "image/gif",
103
+ "svg": "image/svg+xml",
104
+ "webp": "image/webp",
105
+ "mp3": "audio/mpeg",
106
+ "wav": "audio/wav",
107
+ "ogg": "audio/ogg",
108
+ "m4a": "audio/mp4",
109
+ "json": "application/json",
110
+ "txt": "text/plain",
111
+ "css": "text/css",
112
+ };
113
+ return mimeTypes[ext] || "application/octet-stream";
114
+ }
115
+ /**
116
+ * Removes resources that do not appear to be referenced by the bundled code.
117
+ *
118
+ * The check intentionally stays simple: it looks for the full relative resource
119
+ * path or just the filename inside the JavaScript bundle. This keeps
120
+ * single-file exports smaller, but it can only detect assets whose names remain
121
+ * visible as strings in the bundle.
122
+ */
123
+ export function filterResourcesByUsage(bundledJsContent, resourcesMap) {
124
+ const filteredResources = {};
125
+ const unusedResources = [];
126
+ for (const resourcePath in resourcesMap) {
127
+ // Extract just the filename (last part after /)
128
+ const filename = resourcePath.split("/").pop() || resourcePath;
129
+ // Check if the resource is referenced in the bundled code
130
+ // Look for string literals containing the resource path or filename
131
+ if (bundledJsContent.includes(filename) || bundledJsContent.includes(resourcePath)) {
132
+ filteredResources[resourcePath] = resourcesMap[resourcePath];
133
+ }
134
+ else {
135
+ unusedResources.push(resourcePath);
136
+ }
137
+ }
138
+ // Log unused resources
139
+ if (unusedResources.length > 0) {
140
+ flog(`⚠️ ${unusedResources.length} unused resource(s) excluded from single-file build:`);
141
+ unusedResources.forEach(res => flog(` - ${res}`));
142
+ }
143
+ return filteredResources;
144
+ }
@@ -0,0 +1,20 @@
1
+ /** Parsed command-line options for the `samengine-build` executable. */
2
+ export interface CLIArgs {
3
+ /** True when the user passed `--release` or `-r`. */
4
+ release: boolean;
5
+ /** Reserved for a CLI-level single-file flag. The config currently controls this. */
6
+ singlefile: boolean;
7
+ /** Project name passed to `--new` or `--new-empty`; otherwise `null`. */
8
+ newProject: boolean;
9
+ /** True when `--new-empty` should create an empty starter instead of the example game. */
10
+ empty: boolean;
11
+ /** Show a Help Message */
12
+ help: boolean;
13
+ }
14
+ /**
15
+ * Parses CLI arguments from `process.argv`.
16
+ *
17
+ * Unknown arguments are reported as warnings instead of crashing the process, so
18
+ * the CLI stays forgiving while still making mistakes visible.
19
+ */
20
+ export declare function parseArgs(): CLIArgs;