samengine 1.9.1 → 1.10.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/LICENSE +201 -0
- package/README.md +168 -0
- package/dist/config/buildconfig.d.ts +146 -0
- package/dist/config/buildconfig.js +115 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.js +1 -0
- package/dist/core.d.ts +17 -0
- package/dist/core.js +24 -0
- package/dist/html.d.ts +29 -0
- package/dist/html.js +20 -0
- package/dist/input.d.ts +51 -0
- package/dist/input.js +44 -3
- package/dist/keys.d.ts +6 -0
- package/dist/keys.js +6 -2
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +8 -1
- package/dist/nonbrowser/getversion.d.ts +13 -0
- package/dist/nonbrowser/getversion.js +35 -0
- package/dist/nonbrowser/ghresolver.d.ts +1 -0
- package/dist/nonbrowser/ghresolver.js +7 -0
- package/dist/nonbrowser/index.d.ts +9 -0
- package/dist/nonbrowser/index.js +9 -0
- package/dist/nonbrowser/internal/buildhelper.d.ts +42 -0
- package/dist/nonbrowser/internal/buildhelper.js +144 -0
- package/dist/nonbrowser/internal/cli/argparser.d.ts +18 -0
- package/dist/nonbrowser/internal/cli/argparser.js +36 -0
- package/dist/nonbrowser/internal/cli/main.d.ts +13 -0
- package/dist/nonbrowser/internal/cli/main.js +265 -0
- package/dist/nonbrowser/internal/config.d.ts +9 -0
- package/dist/nonbrowser/internal/config.js +40 -0
- package/dist/nonbrowser/internal/exporthtml.d.ts +37 -0
- package/dist/nonbrowser/internal/exporthtml.js +622 -0
- package/dist/nonbrowser/utils.d.ts +8 -0
- package/dist/nonbrowser/utils.js +18 -0
- package/dist/physics/collision.d.ts +33 -0
- package/dist/physics/collision.js +27 -0
- package/dist/physics/physicsEngine.d.ts +18 -0
- package/dist/physics/physicsEngine.js +18 -0
- package/dist/physics/physicsObject.d.ts +20 -0
- package/dist/physics/physicsObject.js +20 -0
- package/dist/renderer.d.ts +78 -0
- package/dist/renderer.js +72 -9
- package/dist/samegui/index.d.ts +29 -0
- package/dist/samegui/index.js +26 -0
- package/dist/save.d.ts +12 -0
- package/dist/save.js +10 -0
- package/dist/sound/audioplayer.d.ts +39 -0
- package/dist/sound/audioplayer.js +39 -5
- package/dist/storage/index.d.ts +40 -2
- package/dist/storage/index.js +34 -3
- package/dist/text/index.d.ts +14 -0
- package/dist/text/index.js +58 -0
- package/dist/texture.d.ts +100 -0
- package/dist/texture.js +75 -41
- package/dist/types/button.d.ts +25 -0
- package/dist/types/button.js +22 -0
- package/dist/types/circle.d.ts +26 -0
- package/dist/types/circle.js +21 -7
- package/dist/types/color.d.ts +17 -0
- package/dist/types/color.js +11 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/rectangle.d.ts +29 -0
- package/dist/types/rectangle.js +23 -7
- package/dist/types/triangle.d.ts +23 -0
- package/dist/types/triangle.js +20 -6
- package/dist/types/vector2d.d.ts +42 -0
- package/dist/types/vector2d.js +39 -11
- package/dist/types/vector3d.d.ts +38 -0
- package/dist/types/vector3d.js +35 -11
- package/dist/utils/index.d.ts +11 -4
- package/dist/utils/index.js +11 -4
- package/dist/utils/logger/index.d.ts +24 -0
- package/dist/utils/logger/index.js +44 -0
- package/dist/utils/math.d.ts +18 -0
- package/dist/utils/math.js +18 -4
- package/package.json +29 -11
- package/dist/utils/csv/index.d.ts +0 -3
- package/dist/utils/csv/index.js +0 -2
- package/dist/utils/csv/parser.d.ts +0 -25
- package/dist/utils/csv/parser.js +0 -212
- package/dist/utils/csv/stringifier.d.ts +0 -30
- package/dist/utils/csv/stringifier.js +0 -130
- package/dist/utils/csv/types.d.ts +0 -63
- package/dist/utils/csv/types.js +0 -1
- package/dist/utils/jsonc-parser.d.ts +0 -4
- package/dist/utils/jsonc-parser.js +0 -166
- package/dist/utils/markdown.d.ts +0 -41
- package/dist/utils/markdown.js +0 -699
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/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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
package/dist/keys.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
|
|
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,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,18 @@
|
|
|
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
|
+
/** Show a Help Message */
|
|
10
|
+
help: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parses CLI arguments from `process.argv`.
|
|
14
|
+
*
|
|
15
|
+
* Unknown arguments are reported as warnings instead of crashing the process, so
|
|
16
|
+
* the CLI stays forgiving while still making mistakes visible.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseArgs(): CLIArgs;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses CLI arguments from `process.argv`.
|
|
3
|
+
*
|
|
4
|
+
* Unknown arguments are reported as warnings instead of crashing the process, so
|
|
5
|
+
* the CLI stays forgiving while still making mistakes visible.
|
|
6
|
+
*/
|
|
7
|
+
export function parseArgs() {
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
const options = { release: false, singlefile: false, newProject: false, help: false };
|
|
10
|
+
for (let i = 0; i < args.length; i++) {
|
|
11
|
+
const arg = args[i];
|
|
12
|
+
switch (arg) {
|
|
13
|
+
case "--release":
|
|
14
|
+
case "-r":
|
|
15
|
+
options.release = true;
|
|
16
|
+
break;
|
|
17
|
+
case "create":
|
|
18
|
+
case "new":
|
|
19
|
+
case "--new":
|
|
20
|
+
case "-n":
|
|
21
|
+
case "--new-empty":
|
|
22
|
+
options.newProject = true;
|
|
23
|
+
break;
|
|
24
|
+
case "help":
|
|
25
|
+
case "h":
|
|
26
|
+
case "-h":
|
|
27
|
+
case "--help":
|
|
28
|
+
options.help = true;
|
|
29
|
+
break;
|
|
30
|
+
default:
|
|
31
|
+
console.warn(`⚠️ Unknown Argument: ${arg}`);
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return options;
|
|
36
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for `samengine-build`.
|
|
4
|
+
*
|
|
5
|
+
* High-level flow:
|
|
6
|
+
* 1. Parse CLI arguments.
|
|
7
|
+
* 2. Optionally create a new project.
|
|
8
|
+
* 3. Load `samengine.config.ts`.
|
|
9
|
+
* 4. Bundle the game with esbuild.
|
|
10
|
+
* 5. Generate HTML and handle resources.
|
|
11
|
+
* 6. In development mode, start a local server and watch for changes.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|