@silkweaver/build 1.0.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/LICENSE +674 -0
- package/README.md +20 -0
- package/assets/icon.ico +0 -0
- package/dist/build.d.ts +42 -0
- package/dist/build.js +736 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +29 -0
- package/dist/object_format.d.ts +46 -0
- package/dist/object_format.js +235 -0
- package/package.json +46 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @silkweaver/build — the Silkweaver toolchain.
|
|
3
|
+
*
|
|
4
|
+
* Turns a project folder into a runnable game:
|
|
5
|
+
* - build_preview → exports/game.js (in-IDE preview)
|
|
6
|
+
* - export_html5 → a portable, self-contained HTML5 folder
|
|
7
|
+
* - export_executable → a packaged desktop application
|
|
8
|
+
*
|
|
9
|
+
* Also re-exports the class-file object read/write layer (object_format) used by the
|
|
10
|
+
* IDE's object editor. Pure Node — drives both the IDE (over IPC) and the CLI.
|
|
11
|
+
*/
|
|
12
|
+
export * from './build.js';
|
|
13
|
+
export * from './object_format.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @silkweaver/build — the Silkweaver toolchain.
|
|
4
|
+
*
|
|
5
|
+
* Turns a project folder into a runnable game:
|
|
6
|
+
* - build_preview → exports/game.js (in-IDE preview)
|
|
7
|
+
* - export_html5 → a portable, self-contained HTML5 folder
|
|
8
|
+
* - export_executable → a packaged desktop application
|
|
9
|
+
*
|
|
10
|
+
* Also re-exports the class-file object read/write layer (object_format) used by the
|
|
11
|
+
* IDE's object editor. Pure Node — drives both the IDE (over IPC) and the CLI.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
25
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
__exportStar(require("./build.js"), exports);
|
|
29
|
+
__exportStar(require("./object_format.js"), exports);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read/write layer for class-per-object files (`objects/<name>.ts`).
|
|
3
|
+
*
|
|
4
|
+
* The object editor's GUI is driven by this: `parse_object` extracts an object's
|
|
5
|
+
* metadata / variables / events from its class, and the `set_*` / `add_*` /
|
|
6
|
+
* `remove_*` helpers patch the source *surgically* (preserving the user's code and
|
|
7
|
+
* formatting) when the GUI changes something. Uses the real TypeScript compiler API
|
|
8
|
+
* for parsing, then string-splices at AST positions so unrelated code is untouched.
|
|
9
|
+
*
|
|
10
|
+
* Pure Node (typescript only) — unit-testable and reusable outside the IPC layer.
|
|
11
|
+
*/
|
|
12
|
+
/** Known static-metadata field names an object class may declare. */
|
|
13
|
+
export type meta_field = 'sprite' | 'solid' | 'visible' | 'persistent' | 'depth' | 'parent';
|
|
14
|
+
export interface object_model {
|
|
15
|
+
class_name: string;
|
|
16
|
+
sprite: string | null;
|
|
17
|
+
parent: string | null;
|
|
18
|
+
solid: boolean;
|
|
19
|
+
visible: boolean;
|
|
20
|
+
persistent: boolean;
|
|
21
|
+
depth: number;
|
|
22
|
+
variables: {
|
|
23
|
+
name: string;
|
|
24
|
+
value: string;
|
|
25
|
+
}[];
|
|
26
|
+
events: string[];
|
|
27
|
+
}
|
|
28
|
+
/** Extracts the object model from a class-file source. */
|
|
29
|
+
export declare function parse_object(src: string): object_model;
|
|
30
|
+
/**
|
|
31
|
+
* Sets (adds or updates) a static metadata field. `expr` is the raw initializer
|
|
32
|
+
* text (e.g. `'spr_player'`, `true`, `-5`, `obj_base`).
|
|
33
|
+
*/
|
|
34
|
+
export declare function set_static(src: string, name: meta_field, expr: string): string;
|
|
35
|
+
/** Removes a static metadata field if present (e.g. clearing the sprite). */
|
|
36
|
+
export declare function remove_static(src: string, name: meta_field): string;
|
|
37
|
+
/** Sets (adds or updates) an instance variable field. */
|
|
38
|
+
export declare function set_field(src: string, name: string, expr: string): string;
|
|
39
|
+
/** Removes an instance variable field. */
|
|
40
|
+
export declare function remove_field(src: string, name: string): string;
|
|
41
|
+
/** Adds an `on_*` event method stub if it is not already present. */
|
|
42
|
+
export declare function add_method(src: string, method: string, params?: string, body?: string): string;
|
|
43
|
+
/** Removes an event method by name. */
|
|
44
|
+
export declare function remove_method(src: string, method: string): string;
|
|
45
|
+
/** Generates a minimal class-file source for a new object. Imports are auto-managed (none needed). */
|
|
46
|
+
export declare function scaffold_object(class_name: string): string;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Read/write layer for class-per-object files (`objects/<name>.ts`).
|
|
4
|
+
*
|
|
5
|
+
* The object editor's GUI is driven by this: `parse_object` extracts an object's
|
|
6
|
+
* metadata / variables / events from its class, and the `set_*` / `add_*` /
|
|
7
|
+
* `remove_*` helpers patch the source *surgically* (preserving the user's code and
|
|
8
|
+
* formatting) when the GUI changes something. Uses the real TypeScript compiler API
|
|
9
|
+
* for parsing, then string-splices at AST positions so unrelated code is untouched.
|
|
10
|
+
*
|
|
11
|
+
* Pure Node (typescript only) — unit-testable and reusable outside the IPC layer.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.parse_object = parse_object;
|
|
48
|
+
exports.set_static = set_static;
|
|
49
|
+
exports.remove_static = remove_static;
|
|
50
|
+
exports.set_field = set_field;
|
|
51
|
+
exports.remove_field = remove_field;
|
|
52
|
+
exports.add_method = add_method;
|
|
53
|
+
exports.remove_method = remove_method;
|
|
54
|
+
exports.scaffold_object = scaffold_object;
|
|
55
|
+
const ts = __importStar(require("typescript"));
|
|
56
|
+
// =========================================================================
|
|
57
|
+
// Parsing
|
|
58
|
+
// =========================================================================
|
|
59
|
+
function _source(src) {
|
|
60
|
+
return ts.createSourceFile('object.ts', src, ts.ScriptTarget.Latest, /*setParentNodes*/ true);
|
|
61
|
+
}
|
|
62
|
+
function _find_class(sf) {
|
|
63
|
+
return sf.statements.find(ts.isClassDeclaration);
|
|
64
|
+
}
|
|
65
|
+
function _is_static(m) {
|
|
66
|
+
return !!ts.canHaveModifiers(m) && (ts.getModifiers(m) ?? []).some(mod => mod.kind === ts.SyntaxKind.StaticKeyword);
|
|
67
|
+
}
|
|
68
|
+
function _name_of(m) {
|
|
69
|
+
return m.name && ts.isIdentifier(m.name) ? m.name.text : null;
|
|
70
|
+
}
|
|
71
|
+
/** Extracts the object model from a class-file source. */
|
|
72
|
+
function parse_object(src) {
|
|
73
|
+
const sf = _source(src);
|
|
74
|
+
const cls = _find_class(sf);
|
|
75
|
+
const model = {
|
|
76
|
+
class_name: cls?.name?.text ?? '',
|
|
77
|
+
sprite: null, parent: null,
|
|
78
|
+
solid: false, visible: true, persistent: false, depth: 0,
|
|
79
|
+
variables: [], events: [],
|
|
80
|
+
};
|
|
81
|
+
if (!cls)
|
|
82
|
+
return model;
|
|
83
|
+
for (const m of cls.members) {
|
|
84
|
+
if (ts.isMethodDeclaration(m)) {
|
|
85
|
+
const n = _name_of(m);
|
|
86
|
+
if (n && n.startsWith('on_'))
|
|
87
|
+
model.events.push(n);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (ts.isPropertyDeclaration(m)) {
|
|
91
|
+
const n = _name_of(m);
|
|
92
|
+
if (!n)
|
|
93
|
+
continue;
|
|
94
|
+
const init = m.initializer ? m.initializer.getText(sf) : '';
|
|
95
|
+
if (_is_static(m)) {
|
|
96
|
+
switch (n) {
|
|
97
|
+
case 'sprite':
|
|
98
|
+
model.sprite = _string_literal(m.initializer);
|
|
99
|
+
break;
|
|
100
|
+
case 'parent':
|
|
101
|
+
model.parent = m.initializer ? init : null;
|
|
102
|
+
break;
|
|
103
|
+
case 'solid':
|
|
104
|
+
model.solid = init === 'true';
|
|
105
|
+
break;
|
|
106
|
+
case 'visible':
|
|
107
|
+
model.visible = init !== 'false';
|
|
108
|
+
break;
|
|
109
|
+
case 'persistent':
|
|
110
|
+
model.persistent = init === 'true';
|
|
111
|
+
break;
|
|
112
|
+
case 'depth':
|
|
113
|
+
model.depth = Number(init) || 0;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
model.variables.push({ name: n, value: init });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return model;
|
|
123
|
+
}
|
|
124
|
+
function _string_literal(node) {
|
|
125
|
+
if (node && ts.isStringLiteral(node))
|
|
126
|
+
return node.text;
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
function _apply(src, e) {
|
|
130
|
+
return src.slice(0, e.start) + e.text + src.slice(e.end);
|
|
131
|
+
}
|
|
132
|
+
/** Inserts a member declaration on its own line just before the class's closing brace. */
|
|
133
|
+
function _insert_member(src, text) {
|
|
134
|
+
const sf = _source(src);
|
|
135
|
+
const cls = _find_class(sf);
|
|
136
|
+
if (!cls)
|
|
137
|
+
return src;
|
|
138
|
+
const pos = cls.end - 1; // the `}` of the class body
|
|
139
|
+
const prefix = pos > 0 && src[pos - 1] !== '\n' ? '\n' : ''; // guard against mashing onto the prior line
|
|
140
|
+
return _apply(src, { start: pos, end: pos, text: prefix + text });
|
|
141
|
+
}
|
|
142
|
+
function _find_member(cls, pred) {
|
|
143
|
+
return cls.members.find(pred);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Sets (adds or updates) a static metadata field. `expr` is the raw initializer
|
|
147
|
+
* text (e.g. `'spr_player'`, `true`, `-5`, `obj_base`).
|
|
148
|
+
*/
|
|
149
|
+
function set_static(src, name, expr) {
|
|
150
|
+
const sf = _source(src);
|
|
151
|
+
const cls = _find_class(sf);
|
|
152
|
+
if (!cls)
|
|
153
|
+
return src;
|
|
154
|
+
const existing = _find_member(cls, m => ts.isPropertyDeclaration(m) && _is_static(m) && _name_of(m) === name);
|
|
155
|
+
if (existing?.initializer) {
|
|
156
|
+
return _apply(src, { start: existing.initializer.getStart(sf), end: existing.initializer.getEnd(), text: expr });
|
|
157
|
+
}
|
|
158
|
+
if (existing) {
|
|
159
|
+
// declared without initializer — replace the whole member
|
|
160
|
+
return _apply(src, { start: existing.getStart(sf), end: existing.getEnd(), text: `static ${name} = ${expr}` });
|
|
161
|
+
}
|
|
162
|
+
return _insert_member(src, ` static ${name} = ${expr}\n`);
|
|
163
|
+
}
|
|
164
|
+
/** Removes a static metadata field if present (e.g. clearing the sprite). */
|
|
165
|
+
function remove_static(src, name) {
|
|
166
|
+
return _remove_member(src, m => ts.isPropertyDeclaration(m) && _is_static(m) && _name_of(m) === name);
|
|
167
|
+
}
|
|
168
|
+
/** Sets (adds or updates) an instance variable field. */
|
|
169
|
+
function set_field(src, name, expr) {
|
|
170
|
+
const sf = _source(src);
|
|
171
|
+
const cls = _find_class(sf);
|
|
172
|
+
if (!cls)
|
|
173
|
+
return src;
|
|
174
|
+
const existing = _find_member(cls, m => ts.isPropertyDeclaration(m) && !_is_static(m) && _name_of(m) === name);
|
|
175
|
+
if (existing?.initializer) {
|
|
176
|
+
return _apply(src, { start: existing.initializer.getStart(sf), end: existing.initializer.getEnd(), text: expr });
|
|
177
|
+
}
|
|
178
|
+
if (existing) {
|
|
179
|
+
return _apply(src, { start: existing.getStart(sf), end: existing.getEnd(), text: `${name} = ${expr}` });
|
|
180
|
+
}
|
|
181
|
+
return _insert_member(src, ` ${name} = ${expr}\n`);
|
|
182
|
+
}
|
|
183
|
+
/** Removes an instance variable field. */
|
|
184
|
+
function remove_field(src, name) {
|
|
185
|
+
return _remove_member(src, m => ts.isPropertyDeclaration(m) && !_is_static(m) && _name_of(m) === name);
|
|
186
|
+
}
|
|
187
|
+
/** Adds an `on_*` event method stub if it is not already present. */
|
|
188
|
+
function add_method(src, method, params = '', body = '') {
|
|
189
|
+
const sf = _source(src);
|
|
190
|
+
const cls = _find_class(sf);
|
|
191
|
+
if (!cls)
|
|
192
|
+
return src;
|
|
193
|
+
if (_find_member(cls, m => ts.isMethodDeclaration(m) && _name_of(m) === method))
|
|
194
|
+
return src;
|
|
195
|
+
const stub = ` ${method}(${params}): void {\n${body ? ' ' + body + '\n' : ''} }\n`;
|
|
196
|
+
return _insert_member(src, stub);
|
|
197
|
+
}
|
|
198
|
+
/** Removes an event method by name. */
|
|
199
|
+
function remove_method(src, method) {
|
|
200
|
+
return _remove_member(src, m => ts.isMethodDeclaration(m) && _name_of(m) === method);
|
|
201
|
+
}
|
|
202
|
+
function _remove_member(src, pred) {
|
|
203
|
+
const sf = _source(src);
|
|
204
|
+
const cls = _find_class(sf);
|
|
205
|
+
if (!cls)
|
|
206
|
+
return src;
|
|
207
|
+
const m = _find_member(cls, pred);
|
|
208
|
+
if (!m)
|
|
209
|
+
return src;
|
|
210
|
+
// Remove the member's own line: back up over its leading indentation (but NOT the
|
|
211
|
+
// preceding newline — that belongs to the previous member), and consume the trailing newline.
|
|
212
|
+
let start = m.getStart(sf);
|
|
213
|
+
while (start > 0 && (src[start - 1] === ' ' || src[start - 1] === '\t'))
|
|
214
|
+
start--;
|
|
215
|
+
let end = m.getEnd();
|
|
216
|
+
while (end < src.length && (src[end] === ' ' || src[end] === '\t'))
|
|
217
|
+
end++;
|
|
218
|
+
if (src[end] === '\r')
|
|
219
|
+
end++;
|
|
220
|
+
if (src[end] === '\n')
|
|
221
|
+
end++;
|
|
222
|
+
return _apply(src, { start, end, text: '' });
|
|
223
|
+
}
|
|
224
|
+
// =========================================================================
|
|
225
|
+
// Scaffolding
|
|
226
|
+
// =========================================================================
|
|
227
|
+
/** Generates a minimal class-file source for a new object. Imports are auto-managed (none needed). */
|
|
228
|
+
function scaffold_object(class_name) {
|
|
229
|
+
return `export class ${class_name} extends gm_object {
|
|
230
|
+
on_create(): void {
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
`;
|
|
235
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@silkweaver/build",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Silkweaver toolchain — compiles a project folder into a runnable game (HTML5 / desktop executable). Usable from a CLI or the IDE.",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"license": "GPL-3.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/NicoNicoCip/Silkweaver",
|
|
10
|
+
"directory": "packages/build"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/NicoNicoCip/Silkweaver#readme",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"assets"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p ."
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@silkweaver/engine": "1.0.0",
|
|
33
|
+
"@silkweaver/project": "1.0.0",
|
|
34
|
+
"@electron/packager": "^19.0.5",
|
|
35
|
+
"esbuild": "^0.27.2",
|
|
36
|
+
"typescript": "^5.9.3"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"electron": "*"
|
|
40
|
+
},
|
|
41
|
+
"peerDependenciesMeta": {
|
|
42
|
+
"electron": {
|
|
43
|
+
"optional": true
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|