@silkweaver/build 1.2.0 → 1.3.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/dist/build.js +8 -0
- package/dist/object_format.d.ts +14 -5
- package/dist/object_format.js +113 -26
- package/package.json +1 -1
package/dist/build.js
CHANGED
|
@@ -410,6 +410,14 @@ async function _load_sprite(name: string, meta_url: string, img_base: string): P
|
|
|
410
410
|
spr.mask_right = (meta.mask_x || 0) + meta.mask_w
|
|
411
411
|
spr.mask_bottom = (meta.mask_y || 0) + meta.mask_h
|
|
412
412
|
}
|
|
413
|
+
// Scale mode (how the sprite fills a scaled area) + 9-slice border insets (sprite editor).
|
|
414
|
+
if (meta.scale_mode === 'tile' || meta.scale_mode === 'nineslice' || meta.scale_mode === 'stretch') {
|
|
415
|
+
spr.scale_mode = meta.scale_mode
|
|
416
|
+
}
|
|
417
|
+
spr.slice_left = meta.slice_left || 0
|
|
418
|
+
spr.slice_top = meta.slice_top || 0
|
|
419
|
+
spr.slice_right = meta.slice_right || 0
|
|
420
|
+
spr.slice_bottom = meta.slice_bottom || 0
|
|
413
421
|
|
|
414
422
|
// Load all frames specified in meta.json
|
|
415
423
|
const frames = meta.frames || []
|
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,24 @@ 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
|
+
];
|
|
66
|
+
/**
|
|
67
|
+
* Explicit type annotations for static fields whose base-class type is narrower than what TypeScript
|
|
68
|
+
* would infer from the literal initializer. Without this, `static physics_shape = 'box'` widens to
|
|
69
|
+
* `string`, which doesn't satisfy the base's `'box' | 'circle'` and the subclass fails to compile.
|
|
70
|
+
*/
|
|
71
|
+
const META_TYPES = {
|
|
72
|
+
physics_shape: `'box' | 'circle'`,
|
|
73
|
+
};
|
|
74
|
+
/** Renders a `static <name>[: <type>] = <expr>;` declaration, adding an annotation where one is needed. */
|
|
75
|
+
function _static_decl(name, expr) {
|
|
76
|
+
const ann = META_TYPES[name];
|
|
77
|
+
return `static ${name}${ann ? `: ${ann}` : ''} = ${expr};`;
|
|
78
|
+
}
|
|
61
79
|
/** Canonical GMS-style order of `on_*` event methods (used to keep events ordered in code). */
|
|
62
80
|
exports.EVENT_ORDER = [
|
|
63
81
|
'on_create', 'on_destroy',
|
|
@@ -94,7 +112,7 @@ function parse_object(src) {
|
|
|
94
112
|
class_name: cls?.name?.text ?? '',
|
|
95
113
|
sprite: null, parent: null,
|
|
96
114
|
solid: false, visible: true, persistent: false, depth: 0,
|
|
97
|
-
variables: [], events: [],
|
|
115
|
+
variables: [], events: [], statics: {},
|
|
98
116
|
};
|
|
99
117
|
if (!cls)
|
|
100
118
|
return model;
|
|
@@ -111,6 +129,7 @@ function parse_object(src) {
|
|
|
111
129
|
continue;
|
|
112
130
|
const init = m.initializer ? m.initializer.getText(sf) : '';
|
|
113
131
|
if (_is_static(m)) {
|
|
132
|
+
model.statics[n] = init; // generic: every static field, by name
|
|
114
133
|
switch (n) {
|
|
115
134
|
case 'sprite':
|
|
116
135
|
model.sprite = _string_literal(m.initializer);
|
|
@@ -220,14 +239,32 @@ function set_static(src, name, expr) {
|
|
|
220
239
|
if (!cls)
|
|
221
240
|
return src;
|
|
222
241
|
const existing = _find_member(cls, m => ts.isPropertyDeclaration(m) && _is_static(m) && _name_of(m) === name);
|
|
223
|
-
if (existing?.initializer) {
|
|
224
|
-
return _apply(src, { start: existing.initializer.getStart(sf), end: existing.initializer.getEnd(), text: expr });
|
|
225
|
-
}
|
|
226
242
|
if (existing) {
|
|
227
|
-
//
|
|
228
|
-
|
|
243
|
+
// For annotated fields (e.g. physics_shape) replace the whole member so the type annotation is
|
|
244
|
+
// always present/correct — updating just the initializer could leave it un-annotated (→ widens
|
|
245
|
+
// to string and fails to compile). For plain fields, the cheaper initializer-only edit is fine.
|
|
246
|
+
if (META_TYPES[name])
|
|
247
|
+
return _apply(src, { start: existing.getStart(sf), end: existing.getEnd(), text: _static_decl(name, expr) });
|
|
248
|
+
if (existing.initializer)
|
|
249
|
+
return _apply(src, { start: existing.initializer.getStart(sf), end: existing.initializer.getEnd(), text: expr });
|
|
250
|
+
return _apply(src, { start: existing.getStart(sf), end: existing.getEnd(), text: _static_decl(name, expr) });
|
|
251
|
+
}
|
|
252
|
+
// New static: metadata lives at the TOP, ordered by META_ORDER. Insert before the first static
|
|
253
|
+
// that sorts after it; else before the first non-static member (so statics stay grouped up top).
|
|
254
|
+
const stub = ` ${_static_decl(name, expr)}\n`;
|
|
255
|
+
const order = exports.META_ORDER.indexOf(name);
|
|
256
|
+
if (order >= 0) {
|
|
257
|
+
const after = cls.members.find(m => {
|
|
258
|
+
const n = _name_of(m);
|
|
259
|
+
return ts.isPropertyDeclaration(m) && _is_static(m) && n != null && exports.META_ORDER.indexOf(n) > order;
|
|
260
|
+
});
|
|
261
|
+
if (after)
|
|
262
|
+
return _insert_before(src, after.getStart(sf), stub);
|
|
229
263
|
}
|
|
230
|
-
|
|
264
|
+
const first_non_static = cls.members.find(m => !(ts.isPropertyDeclaration(m) && _is_static(m)));
|
|
265
|
+
if (first_non_static)
|
|
266
|
+
return _insert_before(src, first_non_static.getStart(sf), stub);
|
|
267
|
+
return _insert_member(src, stub);
|
|
231
268
|
}
|
|
232
269
|
/** Removes a static metadata field if present (e.g. clearing the sprite). */
|
|
233
270
|
function remove_static(src, name) {
|
|
@@ -244,15 +281,19 @@ function set_field(src, name, expr) {
|
|
|
244
281
|
return _apply(src, { start: existing.initializer.getStart(sf), end: existing.initializer.getEnd(), text: expr });
|
|
245
282
|
}
|
|
246
283
|
if (existing) {
|
|
247
|
-
return _apply(src, { start: existing.getStart(sf), end: existing.getEnd(), text: `${name} = ${expr}
|
|
284
|
+
return _apply(src, { start: existing.getStart(sf), end: existing.getEnd(), text: `${name} = ${expr};` });
|
|
248
285
|
}
|
|
249
|
-
// New variable:
|
|
250
|
-
// every event below can reference them
|
|
251
|
-
//
|
|
252
|
-
const stub = ` ${name} = ${expr}
|
|
253
|
-
const
|
|
286
|
+
// New variable: variables sit between the static metadata and the events, in add-order (so a later
|
|
287
|
+
// one can read an earlier one, and every event below can reference them). Insert after the last
|
|
288
|
+
// variable; else after the last static; else before the first member.
|
|
289
|
+
const stub = ` ${name} = ${expr};\n`;
|
|
290
|
+
const props = cls.members.filter(m => ts.isPropertyDeclaration(m));
|
|
291
|
+
const vars = props.filter(m => !_is_static(m));
|
|
292
|
+
const statics = props.filter(m => _is_static(m));
|
|
254
293
|
if (vars.length)
|
|
255
294
|
return _insert_after(src, vars[vars.length - 1].getEnd(), stub);
|
|
295
|
+
if (statics.length)
|
|
296
|
+
return _insert_after(src, statics[statics.length - 1].getEnd(), stub);
|
|
256
297
|
if (cls.members.length)
|
|
257
298
|
return _insert_before(src, cls.members[0].getStart(sf), stub);
|
|
258
299
|
return _insert_member(src, stub);
|
|
@@ -463,12 +504,12 @@ function _reindent(code, unit = ' ') {
|
|
|
463
504
|
}
|
|
464
505
|
/**
|
|
465
506
|
* Reorders all class members into canonical groups, preserving each member's attached comments:
|
|
466
|
-
* 1.
|
|
507
|
+
* 1. `static` metadata (sprite/parent/solid/…) — at the TOP, in META_ORDER (non-meta statics after);
|
|
508
|
+
* 2. instance variables — in their existing relative order (field-init order can matter; one
|
|
467
509
|
* field's initializer may read another), so they're grouped but never shuffled among themselves;
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
* the order is already canonical.
|
|
510
|
+
* 3. `on_*` event methods — in canonical EVENT_ORDER;
|
|
511
|
+
* 4. everything else (helper methods) — in place, at the bottom.
|
|
512
|
+
* Metadata → variables → events (execution order) is the canonical shape. A no-op if already canonical.
|
|
472
513
|
*/
|
|
473
514
|
function _reorder_members(src) {
|
|
474
515
|
const sf = _source(src);
|
|
@@ -480,11 +521,19 @@ function _reorder_members(src) {
|
|
|
480
521
|
const n = _name_of(m);
|
|
481
522
|
return n && ts.isMethodDeclaration(m) ? exports.EVENT_ORDER.indexOf(n) : -1;
|
|
482
523
|
};
|
|
524
|
+
const is_static_prop = (m) => ts.isPropertyDeclaration(m) && _is_static(m);
|
|
483
525
|
const is_var = (m) => ts.isPropertyDeclaration(m) && !_is_static(m);
|
|
526
|
+
// Statics: META_ORDER fields first (sprite, parent, …); any other static keeps its relative place.
|
|
527
|
+
const static_key = (m) => {
|
|
528
|
+
const n = _name_of(m);
|
|
529
|
+
const i = n ? exports.META_ORDER.indexOf(n) : -1;
|
|
530
|
+
return i >= 0 ? i : exports.META_ORDER.length + members.indexOf(m);
|
|
531
|
+
};
|
|
532
|
+
const statics = members.filter(is_static_prop).sort((a, b) => static_key(a) - static_key(b));
|
|
484
533
|
const vars = members.filter(is_var);
|
|
485
534
|
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, ...
|
|
535
|
+
const rest = members.filter(m => !is_static_prop(m) && !is_var(m) && event_idx(m) < 0);
|
|
536
|
+
const ordered = [...statics, ...vars, ...events, ...rest];
|
|
488
537
|
if (ordered.every((m, i) => m === members[i]))
|
|
489
538
|
return src; // already canonical
|
|
490
539
|
// Rebuild the class body from each member's full text (getFullStart → getEnd includes the leading
|
|
@@ -495,18 +544,56 @@ function _reorder_members(src) {
|
|
|
495
544
|
const suffix = src.slice(members[members.length - 1].getEnd());
|
|
496
545
|
return prefix + ordered.map(chunk).join('') + suffix;
|
|
497
546
|
}
|
|
547
|
+
/** Ensures every field declaration (static + instance) ends with a `;`. Methods are left as-is. */
|
|
548
|
+
function _ensure_field_semicolons(src) {
|
|
549
|
+
const sf = _source(src);
|
|
550
|
+
const cls = _find_class(sf);
|
|
551
|
+
if (!cls)
|
|
552
|
+
return src;
|
|
553
|
+
const ends = [];
|
|
554
|
+
for (const m of cls.members) {
|
|
555
|
+
if (!ts.isPropertyDeclaration(m))
|
|
556
|
+
continue;
|
|
557
|
+
const end = m.getEnd();
|
|
558
|
+
if (src[end - 1] !== ';')
|
|
559
|
+
ends.push(end);
|
|
560
|
+
}
|
|
561
|
+
let out = src;
|
|
562
|
+
for (const pos of ends.sort((a, b) => b - a))
|
|
563
|
+
out = out.slice(0, pos) + ';' + out.slice(pos);
|
|
564
|
+
return out;
|
|
565
|
+
}
|
|
498
566
|
/**
|
|
499
|
-
* Normalizes a class-per-object file for
|
|
500
|
-
*
|
|
567
|
+
* Normalizes a class-per-object file for the full code view: metadata → variables → events
|
|
568
|
+
* (execution order), a `;` on every field, then consistent indentation. The IDE only applies this
|
|
569
|
+
* when the user's "auto-organize objects" setting is on. Unchanged if there's no class.
|
|
501
570
|
*/
|
|
502
571
|
function normalize_object(src) {
|
|
503
|
-
return _reindent(_reorder_members(src));
|
|
572
|
+
return _reindent(_ensure_field_semicolons(_reorder_members(src)));
|
|
504
573
|
}
|
|
505
574
|
// =========================================================================
|
|
506
575
|
// Scaffolding
|
|
507
576
|
// =========================================================================
|
|
508
|
-
/**
|
|
509
|
-
|
|
577
|
+
/**
|
|
578
|
+
* Generates a class-file source for a new object. `kind` picks a starting template:
|
|
579
|
+
* - 'normal' → an empty object with an on_create stub;
|
|
580
|
+
* - 'physics' → a ready-to-run dynamic matter.js body (physics metadata + a body-sizing mask).
|
|
581
|
+
* Imports are auto-managed (none needed).
|
|
582
|
+
*/
|
|
583
|
+
function scaffold_object(class_name, kind = 'normal') {
|
|
584
|
+
if (kind === 'physics') {
|
|
585
|
+
return `export class ${class_name} extends gm_object {
|
|
586
|
+
static physics = true;
|
|
587
|
+
static physics_shape: 'box' | 'circle' = 'box';
|
|
588
|
+
static physics_density = 0.5;
|
|
589
|
+
static physics_restitution = 0.1;
|
|
590
|
+
static physics_friction = 0.2;
|
|
591
|
+
on_create(): void {
|
|
592
|
+
this.mask_set_size(32, 32);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
`;
|
|
596
|
+
}
|
|
510
597
|
return `export class ${class_name} extends gm_object {
|
|
511
598
|
on_create(): void {
|
|
512
599
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silkweaver/build",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
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",
|