@silkweaver/build 1.1.0 → 1.3.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.
- package/dist/build.d.ts +10 -0
- package/dist/build.js +69 -12
- package/dist/object_format.d.ts +14 -5
- package/dist/object_format.js +93 -22
- package/dist/templates.js +9 -1
- package/package.json +1 -1
package/dist/build.d.ts
CHANGED
|
@@ -15,6 +15,16 @@ import type { project_file as project_data } from '@silkweaver/project';
|
|
|
15
15
|
export type { project_data };
|
|
16
16
|
/** Reads and parses a project's project.json. */
|
|
17
17
|
export declare function read_project(project_folder: string): Promise<project_data>;
|
|
18
|
+
/** Public: the engine version this toolchain ships — what "Update engine" would pin a project to. */
|
|
19
|
+
export declare function toolchain_engine_version(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Vendors the toolchain's current engine into a project as one self-contained bundle
|
|
22
|
+
* (`.engine/engine.mjs`, matter-js inlined) and records its version (`.engine/version.json` +
|
|
23
|
+
* project.json `engineVersion`). Called at project creation; safe to re-run to upgrade the pin.
|
|
24
|
+
* @param project_folder - Absolute path to the project folder
|
|
25
|
+
* @returns The vendored engine version.
|
|
26
|
+
*/
|
|
27
|
+
export declare function vendor_engine(project_folder: string): Promise<string>;
|
|
18
28
|
/**
|
|
19
29
|
* Builds the game to a single bundled game.js at out_path, for the in-IDE preview.
|
|
20
30
|
* @param out_path - Where to write game.js (the caller decides — e.g. exports/game.js)
|
package/dist/build.js
CHANGED
|
@@ -47,6 +47,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
47
47
|
})();
|
|
48
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
49
|
exports.read_project = read_project;
|
|
50
|
+
exports.toolchain_engine_version = toolchain_engine_version;
|
|
51
|
+
exports.vendor_engine = vendor_engine;
|
|
50
52
|
exports.build_preview = build_preview;
|
|
51
53
|
exports.export_html5 = export_html5;
|
|
52
54
|
exports.export_executable = export_executable;
|
|
@@ -65,18 +67,73 @@ async function read_project(project_folder) {
|
|
|
65
67
|
* entry can import the whole API (keeping it in sync with the IDE's autocomplete).
|
|
66
68
|
* esbuild tree-shakes whatever the game doesn't actually use.
|
|
67
69
|
*/
|
|
68
|
-
|
|
70
|
+
const _engine_names = new Map(); // engine path → its export names (per vendored engine)
|
|
69
71
|
async function engine_export_names(engine_path) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
const cached = _engine_names.get(engine_path);
|
|
73
|
+
if (cached)
|
|
74
|
+
return cached;
|
|
72
75
|
const mod = await import((0, node_url_1.pathToFileURL)(engine_path).href);
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
const names = Object.keys(mod).filter(n => n !== 'default' && /^[A-Za-z_$][\w$]*$/.test(n));
|
|
77
|
+
_engine_names.set(engine_path, names);
|
|
78
|
+
return names;
|
|
75
79
|
}
|
|
76
|
-
/** Absolute path to the engine
|
|
80
|
+
/** Absolute path to the toolchain's own engine entry (the vendoring source + pre-vendoring fallback). */
|
|
77
81
|
function engine_entry() {
|
|
78
82
|
return require.resolve('@silkweaver/engine');
|
|
79
83
|
}
|
|
84
|
+
/** The version of the toolchain's own engine (what a freshly-vendored project pins). */
|
|
85
|
+
function engine_version() {
|
|
86
|
+
try {
|
|
87
|
+
return JSON.parse(fs.readFileSync(path.join(path.dirname(engine_entry()), '..', 'package.json'), 'utf8')).version;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return '0.0.0';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Public: the engine version this toolchain ships — what "Update engine" would pin a project to. */
|
|
94
|
+
function toolchain_engine_version() {
|
|
95
|
+
return engine_version();
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Resolves the engine a project builds against: its vendored copy (`.engine/engine.mjs`) when
|
|
99
|
+
* present, else the toolchain's own engine (projects created before per-project vendoring). This is
|
|
100
|
+
* what lets a project keep building against the exact engine it was made with — IDE updates don't
|
|
101
|
+
* touch it.
|
|
102
|
+
*/
|
|
103
|
+
function resolve_engine(project_folder) {
|
|
104
|
+
const vendored = path.join(project_folder, '.engine', 'engine.mjs');
|
|
105
|
+
return fs.existsSync(vendored) ? vendored : engine_entry();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Vendors the toolchain's current engine into a project as one self-contained bundle
|
|
109
|
+
* (`.engine/engine.mjs`, matter-js inlined) and records its version (`.engine/version.json` +
|
|
110
|
+
* project.json `engineVersion`). Called at project creation; safe to re-run to upgrade the pin.
|
|
111
|
+
* @param project_folder - Absolute path to the project folder
|
|
112
|
+
* @returns The vendored engine version.
|
|
113
|
+
*/
|
|
114
|
+
async function vendor_engine(project_folder) {
|
|
115
|
+
const version = engine_version();
|
|
116
|
+
const dir = path.join(project_folder, '.engine');
|
|
117
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
118
|
+
const esbuild_api = require('esbuild');
|
|
119
|
+
await esbuild_api.build({
|
|
120
|
+
entryPoints: [engine_entry()],
|
|
121
|
+
bundle: true,
|
|
122
|
+
format: 'esm',
|
|
123
|
+
outfile: path.join(dir, 'engine.mjs'),
|
|
124
|
+
keepNames: true, // the engine reads constructor.name at runtime
|
|
125
|
+
});
|
|
126
|
+
await fs.promises.writeFile(path.join(dir, 'version.json'), JSON.stringify({ version, vendoredAt: new Date().toISOString() }, null, 2) + '\n', 'utf8');
|
|
127
|
+
// Pin the version in project.json so the IDE can display it / gate features on it.
|
|
128
|
+
try {
|
|
129
|
+
const proj_path = path.join(project_folder, 'project.json');
|
|
130
|
+
const proj = JSON.parse(await fs.promises.readFile(proj_path, 'utf8'));
|
|
131
|
+
proj.engineVersion = version;
|
|
132
|
+
await fs.promises.writeFile(proj_path, JSON.stringify(proj, null, 2) + '\n', 'utf8');
|
|
133
|
+
}
|
|
134
|
+
catch { /* no project.json yet — caller sets engineVersion */ }
|
|
135
|
+
return version;
|
|
136
|
+
}
|
|
80
137
|
/**
|
|
81
138
|
* Strips leading `import …` lines from a code snippet so it can be inlined inside a
|
|
82
139
|
* function body (timeline moments). Engine references auto-resolve via the inject shim,
|
|
@@ -101,7 +158,7 @@ function hex_to_bgr(hex) {
|
|
|
101
158
|
* Generates the bootstrapper (_entry.ts) source for a project.
|
|
102
159
|
* @param asset_mode - 'preview' (file:// into project) or 'export' (relative assets/)
|
|
103
160
|
*/
|
|
104
|
-
async function generate_entry_code(project_folder, proj, asset_mode) {
|
|
161
|
+
async function generate_entry_code(project_folder, proj, asset_mode, engine_path) {
|
|
105
162
|
// Parent-before-child so generated imports/registrations init objects in dependency order.
|
|
106
163
|
const object_names = await _objects_parent_first(project_folder, Object.keys(proj.resources.objects ?? {}));
|
|
107
164
|
const room_names = Object.keys(proj.resources.rooms ?? {});
|
|
@@ -110,8 +167,7 @@ async function generate_entry_code(project_folder, proj, asset_mode) {
|
|
|
110
167
|
const font_names = Object.keys(proj.resources.fonts ?? {});
|
|
111
168
|
const path_names = Object.keys(proj.resources.paths ?? {});
|
|
112
169
|
const timeline_names = Object.keys(proj.resources.timelines ?? {});
|
|
113
|
-
//
|
|
114
|
-
const engine_path = engine_entry();
|
|
170
|
+
// engine_path is whatever the project resolves to — its vendored copy, or the toolchain's.
|
|
115
171
|
// Each object is a single class file: objects/<name>.ts (a gm_object subclass).
|
|
116
172
|
// It is imported as-is; metadata/variables/events all live in the class.
|
|
117
173
|
const object_imports = [];
|
|
@@ -492,7 +548,8 @@ ${room_physics[start_room] ? ` physics_world_create(${room_physics[start_room
|
|
|
492
548
|
* ESM game.js at out_path via esbuild, then removes the temporary file.
|
|
493
549
|
*/
|
|
494
550
|
async function bundle_game(project_folder, proj, asset_mode, out_path, minify) {
|
|
495
|
-
const
|
|
551
|
+
const engine_path = resolve_engine(project_folder);
|
|
552
|
+
const entry_code = await generate_entry_code(project_folder, proj, asset_mode, engine_path);
|
|
496
553
|
const entry_path = path.join(project_folder, '_entry.ts');
|
|
497
554
|
const globals_path = path.join(project_folder, '_engine_globals.ts');
|
|
498
555
|
await fs.promises.writeFile(entry_path, entry_code, 'utf8');
|
|
@@ -500,7 +557,7 @@ async function bundle_game(project_folder, proj, asset_mode, out_path, minify) {
|
|
|
500
557
|
// `inject`, it makes any *bare* engine reference in an object/script file (e.g. `gm_object`,
|
|
501
558
|
// `draw_sprite`) resolve to an auto-injected import — tree-shaken — so users never write or
|
|
502
559
|
// manage `import … from '@silkweaver/engine'` themselves.
|
|
503
|
-
const engine_names = await engine_export_names(
|
|
560
|
+
const engine_names = await engine_export_names(engine_path);
|
|
504
561
|
// Also re-export the project's OBJECT classes by name, so they can be referenced bare —
|
|
505
562
|
// GMS-style, e.g. `place_meeting(x, y, obj_wall)` or `static parent = par_solid` — with no
|
|
506
563
|
// import (matching what the IDE editor already declares). The defining file won't self-import;
|
|
@@ -533,7 +590,7 @@ async function bundle_game(project_folder, proj, asset_mode, out_path, minify) {
|
|
|
533
590
|
// `this.constructor.name` (resource.name, room type checks, object_get_name),
|
|
534
591
|
// so identifier mangling would otherwise break exported (minified) games.
|
|
535
592
|
keepNames: true,
|
|
536
|
-
alias: { '@silkweaver/engine':
|
|
593
|
+
alias: { '@silkweaver/engine': engine_path },
|
|
537
594
|
inject: [globals_path],
|
|
538
595
|
});
|
|
539
596
|
}
|
package/dist/object_format.d.ts
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* Pure Node (typescript only) — unit-testable and reusable outside the IPC layer.
|
|
11
11
|
*/
|
|
12
12
|
/** Known static-metadata field names an object class may declare. */
|
|
13
|
-
export type meta_field = 'sprite' | 'solid' | 'visible' | 'persistent' | 'depth' | 'parent';
|
|
13
|
+
export type meta_field = 'sprite' | 'solid' | 'visible' | 'persistent' | 'depth' | 'parent' | 'physics' | 'physics_shape' | 'physics_density' | 'physics_restitution' | 'physics_friction' | 'physics_sensor';
|
|
14
|
+
/** Canonical order of the `static` metadata fields (keeps them tidy + consistent across files). */
|
|
15
|
+
export declare const META_ORDER: string[];
|
|
14
16
|
/** Canonical GMS-style order of `on_*` event methods (used to keep events ordered in code). */
|
|
15
17
|
export declare const EVENT_ORDER: string[];
|
|
16
18
|
export interface object_model {
|
|
@@ -26,6 +28,7 @@ export interface object_model {
|
|
|
26
28
|
value: string;
|
|
27
29
|
}[];
|
|
28
30
|
events: string[];
|
|
31
|
+
statics: Record<string, string>;
|
|
29
32
|
}
|
|
30
33
|
/** Extracts the object model from a class-file source. */
|
|
31
34
|
export declare function parse_object(src: string): object_model;
|
|
@@ -68,9 +71,15 @@ export declare function get_event_body(src: string, method: string): string | nu
|
|
|
68
71
|
*/
|
|
69
72
|
export declare function set_event_body(src: string, method: string, body: string): string;
|
|
70
73
|
/**
|
|
71
|
-
* Normalizes a class-per-object file for
|
|
72
|
-
*
|
|
74
|
+
* Normalizes a class-per-object file for the full code view: metadata → variables → events
|
|
75
|
+
* (execution order), a `;` on every field, then consistent indentation. The IDE only applies this
|
|
76
|
+
* when the user's "auto-organize objects" setting is on. Unchanged if there's no class.
|
|
73
77
|
*/
|
|
74
78
|
export declare function normalize_object(src: string): string;
|
|
75
|
-
/**
|
|
76
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Generates a class-file source for a new object. `kind` picks a starting template:
|
|
81
|
+
* - 'normal' → an empty object with an on_create stub;
|
|
82
|
+
* - 'physics' → a ready-to-run dynamic matter.js body (physics metadata + a body-sizing mask).
|
|
83
|
+
* Imports are auto-managed (none needed).
|
|
84
|
+
*/
|
|
85
|
+
export declare function scaffold_object(class_name: string, kind?: 'normal' | 'physics'): string;
|
package/dist/object_format.js
CHANGED
|
@@ -44,7 +44,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
44
44
|
};
|
|
45
45
|
})();
|
|
46
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
-
exports.EVENT_ORDER = void 0;
|
|
47
|
+
exports.EVENT_ORDER = exports.META_ORDER = void 0;
|
|
48
48
|
exports.parse_object = parse_object;
|
|
49
49
|
exports.this_members = this_members;
|
|
50
50
|
exports.set_static = set_static;
|
|
@@ -58,6 +58,11 @@ exports.set_event_body = set_event_body;
|
|
|
58
58
|
exports.normalize_object = normalize_object;
|
|
59
59
|
exports.scaffold_object = scaffold_object;
|
|
60
60
|
const ts = __importStar(require("typescript"));
|
|
61
|
+
/** Canonical order of the `static` metadata fields (keeps them tidy + consistent across files). */
|
|
62
|
+
exports.META_ORDER = [
|
|
63
|
+
'sprite', 'parent', 'solid', 'visible', 'persistent', 'depth',
|
|
64
|
+
'physics', 'physics_shape', 'physics_density', 'physics_restitution', 'physics_friction', 'physics_sensor',
|
|
65
|
+
];
|
|
61
66
|
/** Canonical GMS-style order of `on_*` event methods (used to keep events ordered in code). */
|
|
62
67
|
exports.EVENT_ORDER = [
|
|
63
68
|
'on_create', 'on_destroy',
|
|
@@ -94,7 +99,7 @@ function parse_object(src) {
|
|
|
94
99
|
class_name: cls?.name?.text ?? '',
|
|
95
100
|
sprite: null, parent: null,
|
|
96
101
|
solid: false, visible: true, persistent: false, depth: 0,
|
|
97
|
-
variables: [], events: [],
|
|
102
|
+
variables: [], events: [], statics: {},
|
|
98
103
|
};
|
|
99
104
|
if (!cls)
|
|
100
105
|
return model;
|
|
@@ -111,6 +116,7 @@ function parse_object(src) {
|
|
|
111
116
|
continue;
|
|
112
117
|
const init = m.initializer ? m.initializer.getText(sf) : '';
|
|
113
118
|
if (_is_static(m)) {
|
|
119
|
+
model.statics[n] = init; // generic: every static field, by name
|
|
114
120
|
switch (n) {
|
|
115
121
|
case 'sprite':
|
|
116
122
|
model.sprite = _string_literal(m.initializer);
|
|
@@ -225,9 +231,24 @@ function set_static(src, name, expr) {
|
|
|
225
231
|
}
|
|
226
232
|
if (existing) {
|
|
227
233
|
// declared without initializer — replace the whole member
|
|
228
|
-
return _apply(src, { start: existing.getStart(sf), end: existing.getEnd(), text: `static ${name} = ${expr}
|
|
234
|
+
return _apply(src, { start: existing.getStart(sf), end: existing.getEnd(), text: `static ${name} = ${expr};` });
|
|
229
235
|
}
|
|
230
|
-
|
|
236
|
+
// New static: metadata lives at the TOP, ordered by META_ORDER. Insert before the first static
|
|
237
|
+
// that sorts after it; else before the first non-static member (so statics stay grouped up top).
|
|
238
|
+
const stub = ` static ${name} = ${expr};\n`;
|
|
239
|
+
const order = exports.META_ORDER.indexOf(name);
|
|
240
|
+
if (order >= 0) {
|
|
241
|
+
const after = cls.members.find(m => {
|
|
242
|
+
const n = _name_of(m);
|
|
243
|
+
return ts.isPropertyDeclaration(m) && _is_static(m) && n != null && exports.META_ORDER.indexOf(n) > order;
|
|
244
|
+
});
|
|
245
|
+
if (after)
|
|
246
|
+
return _insert_before(src, after.getStart(sf), stub);
|
|
247
|
+
}
|
|
248
|
+
const first_non_static = cls.members.find(m => !(ts.isPropertyDeclaration(m) && _is_static(m)));
|
|
249
|
+
if (first_non_static)
|
|
250
|
+
return _insert_before(src, first_non_static.getStart(sf), stub);
|
|
251
|
+
return _insert_member(src, stub);
|
|
231
252
|
}
|
|
232
253
|
/** Removes a static metadata field if present (e.g. clearing the sprite). */
|
|
233
254
|
function remove_static(src, name) {
|
|
@@ -244,15 +265,19 @@ function set_field(src, name, expr) {
|
|
|
244
265
|
return _apply(src, { start: existing.initializer.getStart(sf), end: existing.initializer.getEnd(), text: expr });
|
|
245
266
|
}
|
|
246
267
|
if (existing) {
|
|
247
|
-
return _apply(src, { start: existing.getStart(sf), end: existing.getEnd(), text: `${name} = ${expr}
|
|
268
|
+
return _apply(src, { start: existing.getStart(sf), end: existing.getEnd(), text: `${name} = ${expr};` });
|
|
248
269
|
}
|
|
249
|
-
// New variable:
|
|
250
|
-
// every event below can reference them
|
|
251
|
-
//
|
|
252
|
-
const stub = ` ${name} = ${expr}
|
|
253
|
-
const
|
|
270
|
+
// New variable: variables sit between the static metadata and the events, in add-order (so a later
|
|
271
|
+
// one can read an earlier one, and every event below can reference them). Insert after the last
|
|
272
|
+
// variable; else after the last static; else before the first member.
|
|
273
|
+
const stub = ` ${name} = ${expr};\n`;
|
|
274
|
+
const props = cls.members.filter(m => ts.isPropertyDeclaration(m));
|
|
275
|
+
const vars = props.filter(m => !_is_static(m));
|
|
276
|
+
const statics = props.filter(m => _is_static(m));
|
|
254
277
|
if (vars.length)
|
|
255
278
|
return _insert_after(src, vars[vars.length - 1].getEnd(), stub);
|
|
279
|
+
if (statics.length)
|
|
280
|
+
return _insert_after(src, statics[statics.length - 1].getEnd(), stub);
|
|
256
281
|
if (cls.members.length)
|
|
257
282
|
return _insert_before(src, cls.members[0].getStart(sf), stub);
|
|
258
283
|
return _insert_member(src, stub);
|
|
@@ -463,12 +488,12 @@ function _reindent(code, unit = ' ') {
|
|
|
463
488
|
}
|
|
464
489
|
/**
|
|
465
490
|
* Reorders all class members into canonical groups, preserving each member's attached comments:
|
|
466
|
-
* 1.
|
|
491
|
+
* 1. `static` metadata (sprite/parent/solid/…) — at the TOP, in META_ORDER (non-meta statics after);
|
|
492
|
+
* 2. instance variables — in their existing relative order (field-init order can matter; one
|
|
467
493
|
* field's initializer may read another), so they're grouped but never shuffled among themselves;
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
* the order is already canonical.
|
|
494
|
+
* 3. `on_*` event methods — in canonical EVENT_ORDER;
|
|
495
|
+
* 4. everything else (helper methods) — in place, at the bottom.
|
|
496
|
+
* Metadata → variables → events (execution order) is the canonical shape. A no-op if already canonical.
|
|
472
497
|
*/
|
|
473
498
|
function _reorder_members(src) {
|
|
474
499
|
const sf = _source(src);
|
|
@@ -480,11 +505,19 @@ function _reorder_members(src) {
|
|
|
480
505
|
const n = _name_of(m);
|
|
481
506
|
return n && ts.isMethodDeclaration(m) ? exports.EVENT_ORDER.indexOf(n) : -1;
|
|
482
507
|
};
|
|
508
|
+
const is_static_prop = (m) => ts.isPropertyDeclaration(m) && _is_static(m);
|
|
483
509
|
const is_var = (m) => ts.isPropertyDeclaration(m) && !_is_static(m);
|
|
510
|
+
// Statics: META_ORDER fields first (sprite, parent, …); any other static keeps its relative place.
|
|
511
|
+
const static_key = (m) => {
|
|
512
|
+
const n = _name_of(m);
|
|
513
|
+
const i = n ? exports.META_ORDER.indexOf(n) : -1;
|
|
514
|
+
return i >= 0 ? i : exports.META_ORDER.length + members.indexOf(m);
|
|
515
|
+
};
|
|
516
|
+
const statics = members.filter(is_static_prop).sort((a, b) => static_key(a) - static_key(b));
|
|
484
517
|
const vars = members.filter(is_var);
|
|
485
518
|
const events = members.filter(m => event_idx(m) >= 0).sort((a, b) => event_idx(a) - event_idx(b));
|
|
486
|
-
const
|
|
487
|
-
const ordered = [...vars, ...events, ...
|
|
519
|
+
const rest = members.filter(m => !is_static_prop(m) && !is_var(m) && event_idx(m) < 0);
|
|
520
|
+
const ordered = [...statics, ...vars, ...events, ...rest];
|
|
488
521
|
if (ordered.every((m, i) => m === members[i]))
|
|
489
522
|
return src; // already canonical
|
|
490
523
|
// Rebuild the class body from each member's full text (getFullStart → getEnd includes the leading
|
|
@@ -495,18 +528,56 @@ function _reorder_members(src) {
|
|
|
495
528
|
const suffix = src.slice(members[members.length - 1].getEnd());
|
|
496
529
|
return prefix + ordered.map(chunk).join('') + suffix;
|
|
497
530
|
}
|
|
531
|
+
/** Ensures every field declaration (static + instance) ends with a `;`. Methods are left as-is. */
|
|
532
|
+
function _ensure_field_semicolons(src) {
|
|
533
|
+
const sf = _source(src);
|
|
534
|
+
const cls = _find_class(sf);
|
|
535
|
+
if (!cls)
|
|
536
|
+
return src;
|
|
537
|
+
const ends = [];
|
|
538
|
+
for (const m of cls.members) {
|
|
539
|
+
if (!ts.isPropertyDeclaration(m))
|
|
540
|
+
continue;
|
|
541
|
+
const end = m.getEnd();
|
|
542
|
+
if (src[end - 1] !== ';')
|
|
543
|
+
ends.push(end);
|
|
544
|
+
}
|
|
545
|
+
let out = src;
|
|
546
|
+
for (const pos of ends.sort((a, b) => b - a))
|
|
547
|
+
out = out.slice(0, pos) + ';' + out.slice(pos);
|
|
548
|
+
return out;
|
|
549
|
+
}
|
|
498
550
|
/**
|
|
499
|
-
* Normalizes a class-per-object file for
|
|
500
|
-
*
|
|
551
|
+
* Normalizes a class-per-object file for the full code view: metadata → variables → events
|
|
552
|
+
* (execution order), a `;` on every field, then consistent indentation. The IDE only applies this
|
|
553
|
+
* when the user's "auto-organize objects" setting is on. Unchanged if there's no class.
|
|
501
554
|
*/
|
|
502
555
|
function normalize_object(src) {
|
|
503
|
-
return _reindent(_reorder_members(src));
|
|
556
|
+
return _reindent(_ensure_field_semicolons(_reorder_members(src)));
|
|
504
557
|
}
|
|
505
558
|
// =========================================================================
|
|
506
559
|
// Scaffolding
|
|
507
560
|
// =========================================================================
|
|
508
|
-
/**
|
|
509
|
-
|
|
561
|
+
/**
|
|
562
|
+
* Generates a class-file source for a new object. `kind` picks a starting template:
|
|
563
|
+
* - 'normal' → an empty object with an on_create stub;
|
|
564
|
+
* - 'physics' → a ready-to-run dynamic matter.js body (physics metadata + a body-sizing mask).
|
|
565
|
+
* Imports are auto-managed (none needed).
|
|
566
|
+
*/
|
|
567
|
+
function scaffold_object(class_name, kind = 'normal') {
|
|
568
|
+
if (kind === 'physics') {
|
|
569
|
+
return `export class ${class_name} extends gm_object {
|
|
570
|
+
static physics = true;
|
|
571
|
+
static physics_shape = 'box';
|
|
572
|
+
static physics_density = 0.5;
|
|
573
|
+
static physics_restitution = 0.1;
|
|
574
|
+
static physics_friction = 0.2;
|
|
575
|
+
on_create(): void {
|
|
576
|
+
this.mask_set_size(32, 32);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
`;
|
|
580
|
+
}
|
|
510
581
|
return `export class ${class_name} extends gm_object {
|
|
511
582
|
on_create(): void {
|
|
512
583
|
|
package/dist/templates.js
CHANGED
|
@@ -45,6 +45,7 @@ exports.list_templates = list_templates;
|
|
|
45
45
|
exports.create_from_template = create_from_template;
|
|
46
46
|
const fs = __importStar(require("node:fs"));
|
|
47
47
|
const path = __importStar(require("node:path"));
|
|
48
|
+
const build_js_1 = require("./build.js");
|
|
48
49
|
/** Ordered registry of the bundled starter templates (folders live under ../templates/<id>). */
|
|
49
50
|
const TEMPLATE_REGISTRY = [
|
|
50
51
|
{ id: 'empty', label: 'Empty', description: 'A blank project with a single empty room.' },
|
|
@@ -56,7 +57,7 @@ function templates_dir() {
|
|
|
56
57
|
return path.join(__dirname, '..', 'templates');
|
|
57
58
|
}
|
|
58
59
|
/** Names never copied into a new project (build artifacts / VCS / deps), as a defensive guard. */
|
|
59
|
-
const COPY_DENYLIST = new Set(['_entry.ts', '_engine_globals.ts', 'node_modules', '.git', 'exports', 'game.js']);
|
|
60
|
+
const COPY_DENYLIST = new Set(['_entry.ts', '_engine_globals.ts', 'node_modules', '.git', 'exports', 'game.js', '.engine']);
|
|
60
61
|
/** Returns the available starter templates — only those whose folder is actually installed. */
|
|
61
62
|
function list_templates() {
|
|
62
63
|
const out = [];
|
|
@@ -103,4 +104,11 @@ async function create_from_template(template_id, dest_folder, name) {
|
|
|
103
104
|
}
|
|
104
105
|
catch { /* keep the template's bundled name */ }
|
|
105
106
|
}
|
|
107
|
+
// Vendor the current engine into the new project so it's pinned to this version (a later IDE
|
|
108
|
+
// update won't change it). Best-effort: if it fails, the project still builds against the
|
|
109
|
+
// toolchain engine via the resolve_engine fallback.
|
|
110
|
+
try {
|
|
111
|
+
await (0, build_js_1.vendor_engine)(dest_folder);
|
|
112
|
+
}
|
|
113
|
+
catch { /* falls back to the toolchain engine */ }
|
|
106
114
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silkweaver/build",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Silkweaver toolchain — compiles a project folder into a runnable game (HTML5 / desktop executable). Usable from a CLI or the IDE.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "GPL-3.0",
|