@silkweaver/build 1.2.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.
@@ -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 display in the full code view: variables hoisted above all
72
- * events, canonical event order, then consistent indentation. Unchanged if there's no class.
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
- /** Generates a minimal class-file source for a new object. Imports are auto-managed (none needed). */
76
- export declare function scaffold_object(class_name: string): string;
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;
@@ -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
- return _insert_member(src, ` static ${name} = ${expr}\n`);
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: keep all instance variables grouped at the TOP of the class, in add-order, so
250
- // every event below can reference them and a later variable's initializer can safely read an
251
- // earlier one. Insert after the last existing variable; if none, before the first member.
252
- const stub = ` ${name} = ${expr}\n`;
253
- const vars = cls.members.filter(m => ts.isPropertyDeclaration(m) && !_is_static(m));
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. instance variablesin their existing relative order (field-init order can matter; one
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
- * 2. `on_*` event methods — in canonical EVENT_ORDER;
469
- * 3. everything else (statics, helper methods) — in place.
470
- * Variables-before-events is the whole point: every event can reference every variable. A no-op if
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 others = members.filter(m => !is_var(m) && event_idx(m) < 0);
487
- const ordered = [...vars, ...events, ...others];
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 display in the full code view: variables hoisted above all
500
- * events, canonical event order, then consistent indentation. Unchanged if there's no class.
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
- /** Generates a minimal class-file source for a new object. Imports are auto-managed (none needed). */
509
- function scaffold_object(class_name) {
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silkweaver/build",
3
- "version": "1.2.0",
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",