lecodes-cli 0.6.3 → 0.7.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/README.md +7 -0
- package/dist/index.js +1035 -549
- package/package.json +9 -4
- package/runtime/scene-harness.json +1 -0
- package/runtime/sdk/compile/assetMacro.ts +4 -3
- package/runtime/sdk/compile/bundler.ts +35 -3
- package/runtime/sdk/compile/compileProject.ts +14 -2
- package/runtime/sdk/compile/index.ts +6 -0
- package/runtime/sdk/compile/libraryImports.ts +47 -0
- package/runtime/sdk/compile/sceneEditor.ts +78 -0
- package/runtime/sdk/core/Aspect.ts +37 -0
- package/runtime/sdk/core/InspectorUI.ts +212 -0
- package/runtime/sdk/core/fields.ts +120 -0
- package/runtime/sdk/gl/Material.ts +11 -1
- package/runtime/sdk/gl/Node.ts +4 -1
- package/runtime/sdk/gl/Scene.ts +6 -1
- package/runtime/sdk/gl/scenarios.ts +349 -0
- package/runtime/sdk/inject.ts +18 -0
- package/runtime/sdk/kit/UITabs.ts +105 -0
- package/runtime/sdk/runtime/datetime.ts +331 -0
- package/runtime/sdk/scene/defineScene.ts +903 -0
- package/runtime/sdk/scene/editorPlugins.ts +86 -0
- package/runtime/sdk/ui/UI.ts +1 -0
- package/runtime/sdk/ui/UIScreen.ts +23 -0
- package/runtime/sdk/ui/UIScreenHost.ts +212 -0
- package/runtime/sdk-types.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31,133 +31,538 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
31
31
|
return to;
|
|
32
32
|
};
|
|
33
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
var __returnValue = (v) => v;
|
|
35
|
+
function __exportSetter(name, newValue) {
|
|
36
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
+
}
|
|
38
|
+
var __export = (target, all) => {
|
|
39
|
+
for (var name in all)
|
|
40
|
+
__defProp(target, name, {
|
|
41
|
+
get: all[name],
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
set: __exportSetter.bind(all, name)
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
34
48
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
35
49
|
|
|
36
|
-
//
|
|
37
|
-
var
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
// ../sdk/src/compile/assetMacro.ts
|
|
51
|
+
var ASSET_RE, hasAssetMacro = (src) => src.includes("asset("), desugarAssetMacros = (src, filePath) => {
|
|
52
|
+
const byPath = new Map;
|
|
53
|
+
let i = 0;
|
|
54
|
+
const out = src.replace(ASSET_RE, (_match, _quote, path) => {
|
|
55
|
+
let name = byPath.get(path);
|
|
56
|
+
if (!name) {
|
|
57
|
+
name = `__asset_${i++}`;
|
|
58
|
+
byPath.set(path, name);
|
|
59
|
+
}
|
|
60
|
+
return name;
|
|
61
|
+
});
|
|
62
|
+
if (/(?<![.\w$])asset\(/.test(out)) {
|
|
63
|
+
throw new Error(`asset() expects a string literal, found a dynamic argument in: ${filePath}`);
|
|
64
|
+
}
|
|
65
|
+
if (byPath.size === 0)
|
|
66
|
+
return src;
|
|
67
|
+
const imports = [...byPath].map(([path, name]) => `import ${name} from ${JSON.stringify(path)};`).join(`
|
|
68
|
+
`);
|
|
69
|
+
return out + `
|
|
70
|
+
` + imports;
|
|
71
|
+
};
|
|
72
|
+
var init_assetMacro = __esm(() => {
|
|
73
|
+
ASSET_RE = /(?<![.\w$])asset\(\s*(["'])((?:[^"'\\]|\\.)*)\1\s*\)/g;
|
|
44
74
|
});
|
|
45
75
|
|
|
46
|
-
//
|
|
47
|
-
var
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
// ../sdk/src/compile/aspectMacro.ts
|
|
77
|
+
var ASPECT_RE, hasAspectExtends = (src) => /\bextends\s+Aspect\s*</.test(src), injectAspectNames = (src) => {
|
|
78
|
+
let out = "";
|
|
79
|
+
let last = 0;
|
|
80
|
+
ASPECT_RE.lastIndex = 0;
|
|
81
|
+
let m;
|
|
82
|
+
while ((m = ASPECT_RE.exec(src)) !== null) {
|
|
83
|
+
const braceEnd = m.index + m[0].length;
|
|
84
|
+
if (!classBodyDeclaresAspect(src, braceEnd)) {
|
|
85
|
+
out += src.slice(last, braceEnd) + ` static aspect = ${JSON.stringify(m[2])};`;
|
|
86
|
+
last = braceEnd;
|
|
87
|
+
}
|
|
52
88
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
89
|
+
return last === 0 ? src : out + src.slice(last);
|
|
90
|
+
}, classBodyDeclaresAspect = (src, from) => {
|
|
91
|
+
let depth = 1;
|
|
92
|
+
let i = from;
|
|
93
|
+
for (;i < src.length && depth > 0; i++) {
|
|
94
|
+
const c2 = src[i];
|
|
95
|
+
if (c2 === "{")
|
|
96
|
+
depth++;
|
|
97
|
+
else if (c2 === "}")
|
|
98
|
+
depth--;
|
|
99
|
+
}
|
|
100
|
+
return /\bstatic\b[^;{}()=]*\baspect\b/.test(src.slice(from, i));
|
|
101
|
+
};
|
|
102
|
+
var init_aspectMacro = __esm(() => {
|
|
103
|
+
ASPECT_RE = /\bclass\s+\w+\s+extends\s+Aspect\s*<\s*(["'])((?:[^"'\\]|\\.)*)\1[^{};]*>\s*\{/g;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ../sdk/src/compile/libraryImports.ts
|
|
107
|
+
var hasImportStatement = (src) => src.includes("import") || src.includes("from"), IMPORT_FROM_RE, SIDE_EFFECT_RE, isRelativeOrAbsolute = (spec) => spec.startsWith("/") || spec.startsWith("./") || spec.startsWith("../"), rewriteLibraryImports = (src, slugs) => {
|
|
108
|
+
const rewriteSpec = (spec) => {
|
|
109
|
+
if (isRelativeOrAbsolute(spec))
|
|
110
|
+
return spec;
|
|
111
|
+
const slash = spec.indexOf("/");
|
|
112
|
+
const slug3 = slash === -1 ? spec : spec.slice(0, slash);
|
|
113
|
+
if (slugs.has(slug3)) {
|
|
114
|
+
const sub = slash === -1 ? "/index" : spec.slice(slash);
|
|
115
|
+
return `/__lib/${slug3}${sub}`;
|
|
61
116
|
}
|
|
117
|
+
throw new Error(`Unknown library "${slug3}" — add it in Settings → Libraries, or use a relative path like "./${slug3}".`);
|
|
62
118
|
};
|
|
63
|
-
|
|
119
|
+
let out = src.replace(IMPORT_FROM_RE, (_m, boundary, head, quote, spec) => `${boundary}${head}${quote}${rewriteSpec(spec)}${quote}`);
|
|
120
|
+
out = out.replace(SIDE_EFFECT_RE, (m, boundary, head, quote, spec) => isRelativeOrAbsolute(spec) ? m : `${boundary}${head}${quote}${rewriteSpec(spec)}${quote}`);
|
|
121
|
+
return out;
|
|
122
|
+
};
|
|
123
|
+
var init_libraryImports = __esm(() => {
|
|
124
|
+
IMPORT_FROM_RE = /(^|[;\n}])(\s*(?:import|export)\b[^"'`;]*?\bfrom\s*)(["'])([^"'\n]+)\3/g;
|
|
125
|
+
SIDE_EFFECT_RE = /(^|[;\n}])(\s*import\s*)(["'])([^"'\n]+)\3/g;
|
|
64
126
|
});
|
|
65
127
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
var i;
|
|
91
|
-
for (i = 8;i < 256; i++) {
|
|
92
|
-
QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
|
|
128
|
+
// ../sdk/src/compile/bundler.ts
|
|
129
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync } from "node:fs";
|
|
130
|
+
import { spawnSync } from "node:child_process";
|
|
131
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
132
|
+
import { dirname as dirname5, join as join9, relative as relative2 } from "node:path";
|
|
133
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
134
|
+
var resolveInject = () => {
|
|
135
|
+
const override = process.env.LECODES_SDK_INJECT;
|
|
136
|
+
if (override && existsSync7(override))
|
|
137
|
+
return override;
|
|
138
|
+
const here = dirname5(fileURLToPath2(import.meta.url));
|
|
139
|
+
const src = join9(here, "..", "inject.ts");
|
|
140
|
+
if (existsSync7(src))
|
|
141
|
+
return src;
|
|
142
|
+
throw new Error("sdk: could not locate the inject source (LECODES_SDK_INJECT / src/inject.ts)");
|
|
143
|
+
}, normalize = (path) => {
|
|
144
|
+
const parts = [];
|
|
145
|
+
for (const part of path.split("/")) {
|
|
146
|
+
if (!part || part === ".")
|
|
147
|
+
continue;
|
|
148
|
+
if (part === "..")
|
|
149
|
+
parts.pop();
|
|
150
|
+
else
|
|
151
|
+
parts.push(part);
|
|
93
152
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
153
|
+
return "/" + parts.join("/");
|
|
154
|
+
}, CHISEL_DEFINE, CHISEL_KEEP, resolveSdkSrc = () => {
|
|
155
|
+
const inject = resolveInject();
|
|
156
|
+
if (!inject.endsWith("inject.ts")) {
|
|
157
|
+
throw new Error(`chisel needs the SDK source tree (src/inject.ts); got ${inject}. Set LECODES_SDK_INJECT to the source.`);
|
|
97
158
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
offset++;
|
|
159
|
+
return dirname5(inject);
|
|
160
|
+
}, isSdkSource = (path) => /\.[tj]s$/.test(path) && !path.endsWith(".d.ts") && !path.includes("/compile/") && !path.includes("/tests/") && !path.includes("/__tests__/"), sdkFilesCache, collectSdkFiles = (sdkSrc) => {
|
|
161
|
+
const cached = sdkFilesCache.get(sdkSrc);
|
|
162
|
+
if (cached)
|
|
163
|
+
return cached;
|
|
164
|
+
const out = {};
|
|
165
|
+
const walk = (dir) => {
|
|
166
|
+
for (const name of readdirSync4(dir)) {
|
|
167
|
+
const p = join9(dir, name);
|
|
168
|
+
if (statSync(p).isDirectory())
|
|
169
|
+
walk(p);
|
|
170
|
+
else if (isSdkSource(p))
|
|
171
|
+
out["/__sdk/" + relative2(sdkSrc, p).split("\\").join("/")] = readFileSync6(p, "utf8");
|
|
112
172
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
173
|
+
};
|
|
174
|
+
walk(sdkSrc);
|
|
175
|
+
sdkFilesCache.set(sdkSrc, out);
|
|
176
|
+
return out;
|
|
177
|
+
}, resolveChiselBin = () => {
|
|
178
|
+
const override = process.env.LECODES_CHISEL_BIN;
|
|
179
|
+
if (override) {
|
|
180
|
+
if (existsSync7(override))
|
|
181
|
+
return override;
|
|
182
|
+
throw new Error(`LECODES_CHISEL_BIN points at a missing file: ${override}`);
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
const bin = createRequire2(import.meta.url)("@letary/chisel").binaryPath();
|
|
186
|
+
if (bin && existsSync7(bin))
|
|
187
|
+
return bin;
|
|
188
|
+
} catch {}
|
|
189
|
+
throw new Error("chisel binary not found. Install @letary/chisel, or set LECODES_CHISEL_BIN.");
|
|
190
|
+
}, bundleProjectChisel = async (entrypoint, files, format, sourcemap, minifyOverride, editor, libraries) => {
|
|
191
|
+
const entry = normalize(entrypoint);
|
|
192
|
+
const slugs = new Set(Object.keys(libraries ?? {}));
|
|
193
|
+
const projFiles = {};
|
|
194
|
+
for (const [k, v] of Object.entries(files)) {
|
|
195
|
+
const path = normalize(k);
|
|
196
|
+
let src = v;
|
|
197
|
+
if (/\.[tj]sx?$/.test(path)) {
|
|
198
|
+
if (hasImportStatement(src))
|
|
199
|
+
src = rewriteLibraryImports(src, slugs);
|
|
200
|
+
if (hasAssetMacro(src))
|
|
201
|
+
src = desugarAssetMacros(src, path);
|
|
202
|
+
if (hasAspectExtends(src))
|
|
203
|
+
src = injectAspectNames(src);
|
|
116
204
|
}
|
|
205
|
+
projFiles[path] = src;
|
|
117
206
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return new QRPolynomial(num, 0);
|
|
133
|
-
},
|
|
134
|
-
mod: function(e) {
|
|
135
|
-
if (this.getLength() - e.getLength() < 0) {
|
|
136
|
-
return this;
|
|
137
|
-
}
|
|
138
|
-
var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));
|
|
139
|
-
var num = new Array(this.getLength());
|
|
140
|
-
for (var i = 0;i < this.getLength(); i++) {
|
|
141
|
-
num[i] = this.get(i);
|
|
142
|
-
}
|
|
143
|
-
for (var x = 0;x < e.getLength(); x++) {
|
|
144
|
-
num[x] ^= QRMath.gexp(QRMath.glog(e.get(x)) + ratio);
|
|
207
|
+
if (!projFiles[entry])
|
|
208
|
+
throw new Error(`Entrypoint not found: ${entry}`);
|
|
209
|
+
for (const [slug3, libFiles] of Object.entries(libraries ?? {})) {
|
|
210
|
+
for (const [k, v] of Object.entries(libFiles)) {
|
|
211
|
+
const path = normalize(`/__lib/${slug3}/${k}`);
|
|
212
|
+
let src = v;
|
|
213
|
+
if (/\.[tj]sx?$/.test(path)) {
|
|
214
|
+
if (hasAssetMacro(src))
|
|
215
|
+
src = desugarAssetMacros(src, path);
|
|
216
|
+
if (hasAspectExtends(src))
|
|
217
|
+
src = injectAspectNames(src);
|
|
145
218
|
}
|
|
146
|
-
|
|
219
|
+
projFiles[path] = src;
|
|
147
220
|
}
|
|
148
|
-
}
|
|
149
|
-
|
|
221
|
+
}
|
|
222
|
+
const sdkFiles = collectSdkFiles(resolveSdkSrc());
|
|
223
|
+
const minify = minifyOverride ?? process.env.LECODES_CHISEL_MINIFY !== "0";
|
|
224
|
+
const input = JSON.stringify({
|
|
225
|
+
files: { ...sdkFiles, ...projFiles },
|
|
226
|
+
entry,
|
|
227
|
+
inject: ["/__sdk/inject.ts"],
|
|
228
|
+
format,
|
|
229
|
+
minify,
|
|
230
|
+
fuse: process.env.LECODES_CHISEL_FUSE === "1",
|
|
231
|
+
define: { ...CHISEL_DEFINE, EDITOR: editor ? "true" : "false" },
|
|
232
|
+
keep: CHISEL_KEEP,
|
|
233
|
+
sourcemap,
|
|
234
|
+
reactive_ui: process.env.LECODES_CHISEL_REACTIVE_UI !== "0"
|
|
235
|
+
});
|
|
236
|
+
const bin = resolveChiselBin();
|
|
237
|
+
const r = spawnSync(bin, [], { input, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
|
|
238
|
+
if (r.status === 2 || r.error) {
|
|
239
|
+
throw new Error(`chisel failed: ${r.error?.message ?? r.stderr ?? "unknown error"}`);
|
|
240
|
+
}
|
|
241
|
+
let parsed;
|
|
242
|
+
try {
|
|
243
|
+
parsed = JSON.parse(r.stdout || "{}");
|
|
244
|
+
} catch {
|
|
245
|
+
throw new Error(`chisel produced invalid output: ${(r.stdout || r.stderr || "").slice(0, 200)}`);
|
|
246
|
+
}
|
|
247
|
+
if (parsed.error)
|
|
248
|
+
throw new Error(`chisel: ${parsed.error}`);
|
|
249
|
+
if (parsed.code == null)
|
|
250
|
+
throw new Error("chisel produced no code");
|
|
251
|
+
return { code: parsed.code, map: parsed.map ?? null };
|
|
252
|
+
}, bundleProject = async (entrypoint, files, format = "esm") => (await bundleProjectChisel(entrypoint, files, format, false)).code, bundleProjectWithMap = async (entrypoint, files, format = "esm", minify, editor, libraries) => bundleProjectChisel(entrypoint, files, format, true, minify, editor, libraries);
|
|
253
|
+
var init_bundler = __esm(() => {
|
|
254
|
+
init_assetMacro();
|
|
255
|
+
init_aspectMacro();
|
|
256
|
+
init_libraryImports();
|
|
257
|
+
CHISEL_DEFINE = { RAD2DEG: "57.29577951308232", DEG2RAD: "0.017453292519943295" };
|
|
258
|
+
CHISEL_KEEP = ["_*"];
|
|
259
|
+
sdkFilesCache = new Map;
|
|
150
260
|
});
|
|
151
261
|
|
|
152
|
-
//
|
|
153
|
-
var
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
262
|
+
// ../sdk/src/compile/sourcemap.ts
|
|
263
|
+
var offsetSourceMap = (mapJson, lines) => {
|
|
264
|
+
if (lines <= 0)
|
|
265
|
+
return mapJson;
|
|
266
|
+
const map = JSON.parse(mapJson);
|
|
267
|
+
map.mappings = ";".repeat(lines) + map.mappings;
|
|
268
|
+
return JSON.stringify(map);
|
|
269
|
+
}, inlineSourceMapComment = (mapJson) => {
|
|
270
|
+
const b64 = Buffer.from(mapJson, "utf8").toString("base64");
|
|
271
|
+
return `//# lecodesMap=data:application/json;charset=utf-8;base64,${b64}`;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// ../sdk/src/compile/header.ts
|
|
275
|
+
var buildHeader = (name, code, opts = {}) => {
|
|
276
|
+
let header = `// name: ${name.slice(0, 20)}
|
|
277
|
+
`;
|
|
278
|
+
if (opts.icon)
|
|
279
|
+
header += `// icon: ${opts.icon}
|
|
280
|
+
`;
|
|
281
|
+
header += `// last-updated: ${new Date().toISOString()}
|
|
282
|
+
`;
|
|
283
|
+
const localAssets = new Set;
|
|
284
|
+
for (const m of code.matchAll(/_creatorUtils\.fetchLocal\((.+?)\)/g)) {
|
|
285
|
+
localAssets.add(m[1].replaceAll(/['"]/g, "").trim());
|
|
286
|
+
}
|
|
287
|
+
if (/_creator\.createGlb/.test(code))
|
|
288
|
+
localAssets.add("uberarchive");
|
|
289
|
+
if (/_creator\.captureImage/.test(code))
|
|
290
|
+
localAssets.add("capture");
|
|
291
|
+
if (localAssets.size > 0)
|
|
292
|
+
header += `// preload: ${[...localAssets].join(";")}
|
|
293
|
+
`;
|
|
294
|
+
if (!code.includes("_creator.createScene") && !code.includes("_creator.createEntity"))
|
|
295
|
+
header += `// gl: disable
|
|
296
|
+
`;
|
|
297
|
+
else if (code.includes("_creator.createEngine"))
|
|
298
|
+
header += `// gl: defer
|
|
299
|
+
`;
|
|
300
|
+
if (code.includes("_creator2d."))
|
|
301
|
+
header += `// 2d: enable
|
|
302
|
+
`;
|
|
303
|
+
if (code.includes("_creator.createARController"))
|
|
304
|
+
header += "// ar: enable";
|
|
305
|
+
header += `
|
|
306
|
+
`;
|
|
307
|
+
return header;
|
|
308
|
+
}, detectProjectKind = (code) => {
|
|
309
|
+
if (code.includes("_creator.createARController"))
|
|
310
|
+
return "ar";
|
|
311
|
+
if (code.includes("_creator2d."))
|
|
312
|
+
return "2d";
|
|
313
|
+
if (!code.includes("_creator."))
|
|
314
|
+
return "ui";
|
|
315
|
+
return "3d";
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// ../sdk/src/compile/compileProject.ts
|
|
319
|
+
var detectEntry = (entries) => {
|
|
320
|
+
let entry = "";
|
|
321
|
+
let max = -1;
|
|
322
|
+
for (const e of entries) {
|
|
323
|
+
const ext2 = e.path.split(".").at(-1)?.toLowerCase();
|
|
324
|
+
if (e.path.endsWith(".editor.ts"))
|
|
325
|
+
continue;
|
|
326
|
+
if (e.type === "text" && e.text !== undefined && !e.text.includes("export") && (ext2 === "ts" || ext2 === "js")) {
|
|
327
|
+
if (e.text.length > max) {
|
|
328
|
+
entry = e.path;
|
|
329
|
+
max = e.text.length;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return entry;
|
|
334
|
+
}, compileProject = async (opts) => {
|
|
335
|
+
const entry = opts.entryOverride ?? detectEntry(opts.entries);
|
|
336
|
+
if (!entry) {
|
|
337
|
+
throw new Error("Couldn't find an entrypoint (a top-level .ts/.js file with no `export`). Pass entryOverride.");
|
|
338
|
+
}
|
|
339
|
+
const files = {};
|
|
340
|
+
for (const e of opts.entries) {
|
|
341
|
+
if (e.type === "resource") {
|
|
342
|
+
if (opts.localAssets) {
|
|
343
|
+
const name = e.path.split(/[\\/]/).pop() ?? e.path;
|
|
344
|
+
files[e.path] = `export default ("id:" + _creatorUtils.fetchLocal(${JSON.stringify(name)}))`;
|
|
345
|
+
} else {
|
|
346
|
+
files[e.path] = `export default "${opts.publicUrl}${e.fileSrc ?? ""}"`;
|
|
347
|
+
}
|
|
348
|
+
} else if (e.type === "shader") {
|
|
349
|
+
const shaders = e.shaders ?? [];
|
|
350
|
+
if (shaders.length > 2) {
|
|
351
|
+
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.subBackend ?? _creator.backend}")}\``;
|
|
352
|
+
} else if (shaders.length > 0) {
|
|
353
|
+
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.backend}")}\``;
|
|
354
|
+
} else {
|
|
355
|
+
files[e.path] = `export default ""`;
|
|
356
|
+
}
|
|
357
|
+
} else if (e.path.toLowerCase().endsWith(".svg")) {
|
|
358
|
+
files[e.path] = `export default SvgSource(\`${e.text ?? ""}\`)`;
|
|
359
|
+
} else {
|
|
360
|
+
files[e.path] = e.text ?? "";
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const { code, map } = await bundleProjectWithMap(entry, files, opts.format ?? "esm", opts.minify, opts.editor, opts.libraries);
|
|
364
|
+
if (opts.header === false) {
|
|
365
|
+
return map ? `${code}
|
|
366
|
+
${inlineSourceMapComment(map)}` : code;
|
|
367
|
+
}
|
|
368
|
+
const header = buildHeader(opts.name, code, { icon: opts.icon });
|
|
369
|
+
if (!map)
|
|
370
|
+
return header + code;
|
|
371
|
+
const headerLines = (header.match(/\n/g) ?? []).length;
|
|
372
|
+
return `${header}${code}
|
|
373
|
+
${inlineSourceMapComment(offsetSourceMap(map, headerLines))}`;
|
|
374
|
+
};
|
|
375
|
+
var init_compileProject = __esm(() => {
|
|
376
|
+
init_bundler();
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// ../sdk/src/compile/sceneEditor.ts
|
|
380
|
+
import { existsSync as existsSync8, readdirSync as readdirSync5, readFileSync as readFileSync7 } from "node:fs";
|
|
381
|
+
import { join as join10 } from "node:path";
|
|
382
|
+
var SCENE_HARNESS_DIR = "./.scene-editor", SCENE_ENTRY_PATH = "./.scene-entry.ts", loadSceneHarness = (fallbackDir) => {
|
|
383
|
+
const override = process.env.LECODES_SCENE_HARNESS_DIR;
|
|
384
|
+
const dir = override && existsSync8(override) ? override : fallbackDir && existsSync8(fallbackDir) ? fallbackDir : null;
|
|
385
|
+
if (!dir)
|
|
386
|
+
throw new Error("scene-editor harness sources not found (set LECODES_SCENE_HARNESS_DIR)");
|
|
387
|
+
const out = {};
|
|
388
|
+
for (const f of readdirSync5(dir)) {
|
|
389
|
+
if (f.endsWith(".ts"))
|
|
390
|
+
out[f] = readFileSync7(join10(dir, f), "utf8");
|
|
391
|
+
}
|
|
392
|
+
return out;
|
|
393
|
+
}, sceneEditorEntries = (scenePath, harness, projectPaths = []) => {
|
|
394
|
+
const entries = Object.entries(harness).map(([name, text]) => ({
|
|
395
|
+
path: `${SCENE_HARNESS_DIR}/${name}`,
|
|
396
|
+
type: "text",
|
|
397
|
+
text
|
|
398
|
+
}));
|
|
399
|
+
entries.push({
|
|
400
|
+
path: `${SCENE_HARNESS_DIR}/.edit-mode.ts`,
|
|
401
|
+
type: "text",
|
|
402
|
+
text: `;(globalThis as any).__lecodesSceneEdit = true
|
|
403
|
+
export {}
|
|
404
|
+
`
|
|
405
|
+
});
|
|
406
|
+
const editorImports = projectPaths.filter((p) => p.endsWith(".editor.ts")).map((p) => `import "${p}"
|
|
407
|
+
`).join("");
|
|
408
|
+
entries.push({
|
|
409
|
+
path: SCENE_ENTRY_PATH,
|
|
410
|
+
type: "text",
|
|
411
|
+
text: `import "${SCENE_HARNESS_DIR}/.edit-mode.ts"
|
|
412
|
+
` + `import "${scenePath}"
|
|
413
|
+
` + editorImports + `import "${SCENE_HARNESS_DIR}/main.ts"
|
|
414
|
+
`
|
|
415
|
+
});
|
|
416
|
+
return entries;
|
|
417
|
+
};
|
|
418
|
+
var init_sceneEditor = () => {};
|
|
419
|
+
|
|
420
|
+
// ../sdk/src/compile/index.ts
|
|
421
|
+
var exports_compile = {};
|
|
422
|
+
__export(exports_compile, {
|
|
423
|
+
sceneEditorEntries: () => sceneEditorEntries,
|
|
424
|
+
offsetSourceMap: () => offsetSourceMap,
|
|
425
|
+
loadSceneHarness: () => loadSceneHarness,
|
|
426
|
+
inlineSourceMapComment: () => inlineSourceMapComment,
|
|
427
|
+
detectProjectKind: () => detectProjectKind,
|
|
428
|
+
compileProject: () => compileProject,
|
|
429
|
+
bundleProjectWithMap: () => bundleProjectWithMap,
|
|
430
|
+
bundleProject: () => bundleProject,
|
|
431
|
+
buildHeader: () => buildHeader,
|
|
432
|
+
SCENE_HARNESS_DIR: () => SCENE_HARNESS_DIR,
|
|
433
|
+
SCENE_ENTRY_PATH: () => SCENE_ENTRY_PATH
|
|
434
|
+
});
|
|
435
|
+
var init_compile = __esm(() => {
|
|
436
|
+
init_bundler();
|
|
437
|
+
init_compileProject();
|
|
438
|
+
init_sceneEditor();
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
|
|
442
|
+
var require_QRMode = __commonJS((exports, module) => {
|
|
443
|
+
module.exports = {
|
|
444
|
+
MODE_NUMBER: 1 << 0,
|
|
445
|
+
MODE_ALPHA_NUM: 1 << 1,
|
|
446
|
+
MODE_8BIT_BYTE: 1 << 2,
|
|
447
|
+
MODE_KANJI: 1 << 3
|
|
448
|
+
};
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QR8bitByte.js
|
|
452
|
+
var require_QR8bitByte = __commonJS((exports, module) => {
|
|
453
|
+
var QRMode = require_QRMode();
|
|
454
|
+
function QR8bitByte(data) {
|
|
455
|
+
this.mode = QRMode.MODE_8BIT_BYTE;
|
|
456
|
+
this.data = data;
|
|
457
|
+
}
|
|
458
|
+
QR8bitByte.prototype = {
|
|
459
|
+
getLength: function() {
|
|
460
|
+
return this.data.length;
|
|
461
|
+
},
|
|
462
|
+
write: function(buffer) {
|
|
463
|
+
for (var i = 0;i < this.data.length; i++) {
|
|
464
|
+
buffer.put(this.data.charCodeAt(i), 8);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
module.exports = QR8bitByte;
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMath.js
|
|
472
|
+
var require_QRMath = __commonJS((exports, module) => {
|
|
473
|
+
var QRMath = {
|
|
474
|
+
glog: function(n) {
|
|
475
|
+
if (n < 1) {
|
|
476
|
+
throw new Error("glog(" + n + ")");
|
|
477
|
+
}
|
|
478
|
+
return QRMath.LOG_TABLE[n];
|
|
479
|
+
},
|
|
480
|
+
gexp: function(n) {
|
|
481
|
+
while (n < 0) {
|
|
482
|
+
n += 255;
|
|
483
|
+
}
|
|
484
|
+
while (n >= 256) {
|
|
485
|
+
n -= 255;
|
|
486
|
+
}
|
|
487
|
+
return QRMath.EXP_TABLE[n];
|
|
488
|
+
},
|
|
489
|
+
EXP_TABLE: new Array(256),
|
|
490
|
+
LOG_TABLE: new Array(256)
|
|
491
|
+
};
|
|
492
|
+
for (i = 0;i < 8; i++) {
|
|
493
|
+
QRMath.EXP_TABLE[i] = 1 << i;
|
|
494
|
+
}
|
|
495
|
+
var i;
|
|
496
|
+
for (i = 8;i < 256; i++) {
|
|
497
|
+
QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
|
|
498
|
+
}
|
|
499
|
+
var i;
|
|
500
|
+
for (i = 0;i < 255; i++) {
|
|
501
|
+
QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
|
|
502
|
+
}
|
|
503
|
+
var i;
|
|
504
|
+
module.exports = QRMath;
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRPolynomial.js
|
|
508
|
+
var require_QRPolynomial = __commonJS((exports, module) => {
|
|
509
|
+
var QRMath = require_QRMath();
|
|
510
|
+
function QRPolynomial(num, shift) {
|
|
511
|
+
if (num.length === undefined) {
|
|
512
|
+
throw new Error(num.length + "/" + shift);
|
|
513
|
+
}
|
|
514
|
+
var offset = 0;
|
|
515
|
+
while (offset < num.length && num[offset] === 0) {
|
|
516
|
+
offset++;
|
|
517
|
+
}
|
|
518
|
+
this.num = new Array(num.length - offset + shift);
|
|
519
|
+
for (var i = 0;i < num.length - offset; i++) {
|
|
520
|
+
this.num[i] = num[i + offset];
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
QRPolynomial.prototype = {
|
|
524
|
+
get: function(index) {
|
|
525
|
+
return this.num[index];
|
|
526
|
+
},
|
|
527
|
+
getLength: function() {
|
|
528
|
+
return this.num.length;
|
|
529
|
+
},
|
|
530
|
+
multiply: function(e) {
|
|
531
|
+
var num = new Array(this.getLength() + e.getLength() - 1);
|
|
532
|
+
for (var i = 0;i < this.getLength(); i++) {
|
|
533
|
+
for (var j = 0;j < e.getLength(); j++) {
|
|
534
|
+
num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return new QRPolynomial(num, 0);
|
|
538
|
+
},
|
|
539
|
+
mod: function(e) {
|
|
540
|
+
if (this.getLength() - e.getLength() < 0) {
|
|
541
|
+
return this;
|
|
542
|
+
}
|
|
543
|
+
var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));
|
|
544
|
+
var num = new Array(this.getLength());
|
|
545
|
+
for (var i = 0;i < this.getLength(); i++) {
|
|
546
|
+
num[i] = this.get(i);
|
|
547
|
+
}
|
|
548
|
+
for (var x = 0;x < e.getLength(); x++) {
|
|
549
|
+
num[x] ^= QRMath.gexp(QRMath.glog(e.get(x)) + ratio);
|
|
550
|
+
}
|
|
551
|
+
return new QRPolynomial(num, 0).mod(e);
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
module.exports = QRPolynomial;
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMaskPattern.js
|
|
558
|
+
var require_QRMaskPattern = __commonJS((exports, module) => {
|
|
559
|
+
module.exports = {
|
|
560
|
+
PATTERN000: 0,
|
|
561
|
+
PATTERN001: 1,
|
|
562
|
+
PATTERN010: 2,
|
|
563
|
+
PATTERN011: 3,
|
|
564
|
+
PATTERN100: 4,
|
|
565
|
+
PATTERN101: 5,
|
|
161
566
|
PATTERN110: 6,
|
|
162
567
|
PATTERN111: 7
|
|
163
568
|
};
|
|
@@ -1052,7 +1457,7 @@ var require_main = __commonJS((exports, module) => {
|
|
|
1052
1457
|
// package.json
|
|
1053
1458
|
var package_default = {
|
|
1054
1459
|
name: "lecodes-cli",
|
|
1055
|
-
version: "0.
|
|
1460
|
+
version: "0.7.0",
|
|
1056
1461
|
description: "Clone, edit and push LeCodes projects from your machine.",
|
|
1057
1462
|
type: "module",
|
|
1058
1463
|
license: "MIT",
|
|
@@ -1075,18 +1480,19 @@ var package_default = {
|
|
|
1075
1480
|
icon: "bun scripts/make-icon.ts",
|
|
1076
1481
|
"vendor-runtime": "bun scripts/vendor-runtime.ts",
|
|
1077
1482
|
"sync-peers": "bun scripts/sync-peer-versions.ts",
|
|
1078
|
-
build: 'bun scripts/vendor-runtime.ts && bun build src/index.ts --target=node --outfile dist/index.js --external @letary/chisel --external lecodes-renderer --external lecodes-design --banner="#!/usr/bin/env node"',
|
|
1483
|
+
build: 'bun scripts/vendor-runtime.ts && bun build src/index.ts --target=node --outfile dist/index.js --external @letary/chisel --external lecodes-renderer --external lecodes-design --external lecodes-3d-editor --banner="#!/usr/bin/env node"',
|
|
1079
1484
|
"build:bin": "bun scripts/build-bin.ts",
|
|
1080
1485
|
typecheck: "tsc --noEmit",
|
|
1081
1486
|
dev: "bun run src/index.ts",
|
|
1082
1487
|
prepublishOnly: "bun run sync-peers && bun run build"
|
|
1083
1488
|
},
|
|
1084
1489
|
dependencies: {
|
|
1085
|
-
"@letary/chisel": "^0.
|
|
1490
|
+
"@letary/chisel": "^0.4.0"
|
|
1086
1491
|
},
|
|
1087
1492
|
peerDependencies: {
|
|
1088
1493
|
"lecodes-design": "^0.3.1",
|
|
1089
|
-
"lecodes-renderer": "^0.
|
|
1494
|
+
"lecodes-renderer": "^0.6.0",
|
|
1495
|
+
"lecodes-3d-editor": "^0.1.0"
|
|
1090
1496
|
},
|
|
1091
1497
|
peerDependenciesMeta: {
|
|
1092
1498
|
"lecodes-design": {
|
|
@@ -1094,12 +1500,16 @@ var package_default = {
|
|
|
1094
1500
|
},
|
|
1095
1501
|
"lecodes-renderer": {
|
|
1096
1502
|
optional: true
|
|
1503
|
+
},
|
|
1504
|
+
"lecodes-3d-editor": {
|
|
1505
|
+
optional: true
|
|
1097
1506
|
}
|
|
1098
1507
|
},
|
|
1099
1508
|
devDependencies: {
|
|
1100
1509
|
"lecodes-design": "workspace:*",
|
|
1101
1510
|
"lecodes-renderer": "^0.4.1",
|
|
1102
1511
|
"qrcode-terminal": "^0.12.0",
|
|
1512
|
+
"lecodes-3d-editor": "workspace:*",
|
|
1103
1513
|
sdk: "workspace:*"
|
|
1104
1514
|
}
|
|
1105
1515
|
};
|
|
@@ -1623,11 +2033,11 @@ var create = async (args) => {
|
|
|
1623
2033
|
};
|
|
1624
2034
|
|
|
1625
2035
|
// src/commands/design.ts
|
|
1626
|
-
import { existsSync as
|
|
1627
|
-
import { basename as basename2, join as
|
|
2036
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync10, readdirSync as readdirSync6, writeFileSync as writeFileSync8, appendFileSync } from "node:fs";
|
|
2037
|
+
import { basename as basename2, join as join13 } from "node:path";
|
|
1628
2038
|
|
|
1629
2039
|
// src/compile/designCompile.ts
|
|
1630
|
-
import { readFileSync as
|
|
2040
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
1631
2041
|
import { basename } from "node:path";
|
|
1632
2042
|
|
|
1633
2043
|
// src/localFiles.ts
|
|
@@ -1660,374 +2070,92 @@ var ext = (name) => {
|
|
|
1660
2070
|
const i = name.lastIndexOf(".");
|
|
1661
2071
|
return i < 0 ? "" : name.slice(i + 1).toLowerCase();
|
|
1662
2072
|
};
|
|
1663
|
-
var classifyKind = (name) => {
|
|
1664
|
-
const e = ext(name);
|
|
1665
|
-
if (e === "mat")
|
|
1666
|
-
return "shader";
|
|
1667
|
-
if (e === "scene")
|
|
1668
|
-
return "scene";
|
|
1669
|
-
return TEXT_EXTS.has(e) ? "text" : "resource";
|
|
1670
|
-
};
|
|
1671
|
-
var isTextKind = (kind) => kind !== "resource";
|
|
1672
|
-
var scanLocal = (root, opts = {}) => {
|
|
1673
|
-
const { ignore, tracked } = opts;
|
|
1674
|
-
const files = [];
|
|
1675
|
-
const dirs = [];
|
|
1676
|
-
const ignored = [];
|
|
1677
|
-
const isTracked = (key) => tracked?.has(key) ?? false;
|
|
1678
|
-
const hasTrackedUnder = (key) => {
|
|
1679
|
-
if (!tracked)
|
|
1680
|
-
return false;
|
|
1681
|
-
const prefix = key + "/";
|
|
1682
|
-
for (const t of tracked)
|
|
1683
|
-
if (t.startsWith(prefix))
|
|
1684
|
-
return true;
|
|
1685
|
-
return false;
|
|
1686
|
-
};
|
|
1687
|
-
const walk = (dir, parentIgnored) => {
|
|
1688
|
-
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
1689
|
-
const abs = join8(dir, entry.name);
|
|
1690
|
-
const key = toKey(relative(root, abs));
|
|
1691
|
-
if (entry.isDirectory()) {
|
|
1692
|
-
if (SKIP_DIRS.has(entry.name))
|
|
1693
|
-
continue;
|
|
1694
|
-
const dirIgnored = parentIgnored || !!ignore && ignore(key, true);
|
|
1695
|
-
if (dirIgnored && !isTracked(key) && !hasTrackedUnder(key)) {
|
|
1696
|
-
ignored.push(key + "/");
|
|
1697
|
-
continue;
|
|
1698
|
-
}
|
|
1699
|
-
dirs.push(key);
|
|
1700
|
-
walk(abs, dirIgnored);
|
|
1701
|
-
} else if (entry.isFile()) {
|
|
1702
|
-
if (SKIP_FILES.has(entry.name))
|
|
1703
|
-
continue;
|
|
1704
|
-
const fileIgnored = parentIgnored || !!ignore && ignore(key, false);
|
|
1705
|
-
if (fileIgnored && !isTracked(key)) {
|
|
1706
|
-
ignored.push(key);
|
|
1707
|
-
continue;
|
|
1708
|
-
}
|
|
1709
|
-
files.push({ path: key, absPath: abs, sha: sha256(readFileSync5(abs)) });
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
};
|
|
1713
|
-
walk(root, false);
|
|
1714
|
-
return { files, dirs, ignored };
|
|
1715
|
-
};
|
|
1716
|
-
var scanProject = (root, manifest) => scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest.files)) });
|
|
1717
|
-
var diffLocal = (manifest, scan) => {
|
|
1718
|
-
const added = [];
|
|
1719
|
-
const modified = [];
|
|
1720
|
-
const localByPath = new Map(scan.files.map((f) => [f.path, f]));
|
|
1721
|
-
for (const file of scan.files) {
|
|
1722
|
-
const known = manifest.files[file.path];
|
|
1723
|
-
if (!known)
|
|
1724
|
-
added.push(file);
|
|
1725
|
-
else if (known.sha !== file.sha)
|
|
1726
|
-
modified.push(file);
|
|
1727
|
-
}
|
|
1728
|
-
const deleted = Object.keys(manifest.files).filter((path) => !localByPath.has(path));
|
|
1729
|
-
const moves = new Map;
|
|
1730
|
-
const claimed = new Set;
|
|
1731
|
-
for (const file of added) {
|
|
1732
|
-
const from = deleted.find((path) => !claimed.has(path) && manifest.files[path].sha === file.sha);
|
|
1733
|
-
if (from) {
|
|
1734
|
-
moves.set(file.path, from);
|
|
1735
|
-
claimed.add(from);
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
return { added, modified, deleted, moves };
|
|
1739
|
-
};
|
|
1740
|
-
var hasChanges = (diff) => diff.added.length + diff.modified.length + diff.deleted.length > 0;
|
|
1741
|
-
|
|
1742
|
-
// src/compile/projectCompile.ts
|
|
1743
|
-
import { existsSync as existsSync8 } from "node:fs";
|
|
1744
|
-
import { createRequire as createRequire3 } from "node:module";
|
|
1745
|
-
import { dirname as dirname6, join as join10 } from "node:path";
|
|
1746
|
-
// ../sdk/src/compile/bundler.ts
|
|
1747
|
-
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync } from "node:fs";
|
|
1748
|
-
import { spawnSync } from "node:child_process";
|
|
1749
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
1750
|
-
import { dirname as dirname5, join as join9, relative as relative2 } from "node:path";
|
|
1751
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1752
|
-
|
|
1753
|
-
// ../sdk/src/compile/assetMacro.ts
|
|
1754
|
-
var ASSET_RE = /\basset\(\s*(["'])((?:[^"'\\]|\\.)*)\1\s*\)/g;
|
|
1755
|
-
var hasAssetMacro = (src) => src.includes("asset(");
|
|
1756
|
-
var desugarAssetMacros = (src, filePath) => {
|
|
1757
|
-
const byPath = new Map;
|
|
1758
|
-
let i = 0;
|
|
1759
|
-
const out = src.replace(ASSET_RE, (_match, _quote, path) => {
|
|
1760
|
-
let name = byPath.get(path);
|
|
1761
|
-
if (!name) {
|
|
1762
|
-
name = `__asset_${i++}`;
|
|
1763
|
-
byPath.set(path, name);
|
|
1764
|
-
}
|
|
1765
|
-
return name;
|
|
1766
|
-
});
|
|
1767
|
-
if (/\basset\(/.test(out)) {
|
|
1768
|
-
throw new Error(`asset() expects a string literal, found a dynamic argument in: ${filePath}`);
|
|
1769
|
-
}
|
|
1770
|
-
if (byPath.size === 0)
|
|
1771
|
-
return src;
|
|
1772
|
-
const imports = [...byPath].map(([path, name]) => `import ${name} from ${JSON.stringify(path)};`).join(`
|
|
1773
|
-
`);
|
|
1774
|
-
return out + `
|
|
1775
|
-
` + imports;
|
|
1776
|
-
};
|
|
1777
|
-
|
|
1778
|
-
// ../sdk/src/compile/aspectMacro.ts
|
|
1779
|
-
var ASPECT_RE = /\bclass\s+\w+\s+extends\s+Aspect\s*<\s*(["'])((?:[^"'\\]|\\.)*)\1[^{};]*>\s*\{/g;
|
|
1780
|
-
var hasAspectExtends = (src) => /\bextends\s+Aspect\s*</.test(src);
|
|
1781
|
-
var injectAspectNames = (src) => {
|
|
1782
|
-
let out = "";
|
|
1783
|
-
let last = 0;
|
|
1784
|
-
ASPECT_RE.lastIndex = 0;
|
|
1785
|
-
let m;
|
|
1786
|
-
while ((m = ASPECT_RE.exec(src)) !== null) {
|
|
1787
|
-
const braceEnd = m.index + m[0].length;
|
|
1788
|
-
if (!classBodyDeclaresAspect(src, braceEnd)) {
|
|
1789
|
-
out += src.slice(last, braceEnd) + ` static aspect = ${JSON.stringify(m[2])};`;
|
|
1790
|
-
last = braceEnd;
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
return last === 0 ? src : out + src.slice(last);
|
|
1794
|
-
};
|
|
1795
|
-
var classBodyDeclaresAspect = (src, from) => {
|
|
1796
|
-
let depth = 1;
|
|
1797
|
-
let i = from;
|
|
1798
|
-
for (;i < src.length && depth > 0; i++) {
|
|
1799
|
-
const c2 = src[i];
|
|
1800
|
-
if (c2 === "{")
|
|
1801
|
-
depth++;
|
|
1802
|
-
else if (c2 === "}")
|
|
1803
|
-
depth--;
|
|
1804
|
-
}
|
|
1805
|
-
return /\bstatic\b[^;{}()=]*\baspect\b/.test(src.slice(from, i));
|
|
1806
|
-
};
|
|
1807
|
-
|
|
1808
|
-
// ../sdk/src/compile/bundler.ts
|
|
1809
|
-
var resolveInject = () => {
|
|
1810
|
-
const override = process.env.LECODES_SDK_INJECT;
|
|
1811
|
-
if (override && existsSync7(override))
|
|
1812
|
-
return override;
|
|
1813
|
-
const here = dirname5(fileURLToPath2(import.meta.url));
|
|
1814
|
-
const src = join9(here, "..", "inject.ts");
|
|
1815
|
-
if (existsSync7(src))
|
|
1816
|
-
return src;
|
|
1817
|
-
throw new Error("sdk: could not locate the inject source (LECODES_SDK_INJECT / src/inject.ts)");
|
|
1818
|
-
};
|
|
1819
|
-
var normalize = (path) => {
|
|
1820
|
-
const parts = [];
|
|
1821
|
-
for (const part of path.split("/")) {
|
|
1822
|
-
if (!part || part === ".")
|
|
1823
|
-
continue;
|
|
1824
|
-
if (part === "..")
|
|
1825
|
-
parts.pop();
|
|
1826
|
-
else
|
|
1827
|
-
parts.push(part);
|
|
1828
|
-
}
|
|
1829
|
-
return "/" + parts.join("/");
|
|
1830
|
-
};
|
|
1831
|
-
var CHISEL_DEFINE = { RAD2DEG: "57.29577951308232", DEG2RAD: "0.017453292519943295" };
|
|
1832
|
-
var CHISEL_KEEP = ["_*"];
|
|
1833
|
-
var resolveSdkSrc = () => {
|
|
1834
|
-
const inject = resolveInject();
|
|
1835
|
-
if (!inject.endsWith("inject.ts")) {
|
|
1836
|
-
throw new Error(`chisel needs the SDK source tree (src/inject.ts); got ${inject}. Set LECODES_SDK_INJECT to the source.`);
|
|
1837
|
-
}
|
|
1838
|
-
return dirname5(inject);
|
|
1839
|
-
};
|
|
1840
|
-
var isSdkSource = (path) => /\.[tj]s$/.test(path) && !path.endsWith(".d.ts") && !path.includes("/compile/") && !path.includes("/tests/") && !path.includes("/__tests__/");
|
|
1841
|
-
var sdkFilesCache = new Map;
|
|
1842
|
-
var collectSdkFiles = (sdkSrc) => {
|
|
1843
|
-
const cached = sdkFilesCache.get(sdkSrc);
|
|
1844
|
-
if (cached)
|
|
1845
|
-
return cached;
|
|
1846
|
-
const out = {};
|
|
1847
|
-
const walk = (dir) => {
|
|
1848
|
-
for (const name of readdirSync4(dir)) {
|
|
1849
|
-
const p = join9(dir, name);
|
|
1850
|
-
if (statSync(p).isDirectory())
|
|
1851
|
-
walk(p);
|
|
1852
|
-
else if (isSdkSource(p))
|
|
1853
|
-
out["/__sdk/" + relative2(sdkSrc, p).split("\\").join("/")] = readFileSync6(p, "utf8");
|
|
1854
|
-
}
|
|
1855
|
-
};
|
|
1856
|
-
walk(sdkSrc);
|
|
1857
|
-
sdkFilesCache.set(sdkSrc, out);
|
|
1858
|
-
return out;
|
|
1859
|
-
};
|
|
1860
|
-
var resolveChiselBin = () => {
|
|
1861
|
-
const override = process.env.LECODES_CHISEL_BIN;
|
|
1862
|
-
if (override) {
|
|
1863
|
-
if (existsSync7(override))
|
|
1864
|
-
return override;
|
|
1865
|
-
throw new Error(`LECODES_CHISEL_BIN points at a missing file: ${override}`);
|
|
1866
|
-
}
|
|
1867
|
-
try {
|
|
1868
|
-
const bin = createRequire2(import.meta.url)("@letary/chisel").binaryPath();
|
|
1869
|
-
if (bin && existsSync7(bin))
|
|
1870
|
-
return bin;
|
|
1871
|
-
} catch {}
|
|
1872
|
-
throw new Error("chisel binary not found. Install @letary/chisel, or set LECODES_CHISEL_BIN.");
|
|
1873
|
-
};
|
|
1874
|
-
var bundleProjectChisel = async (entrypoint, files, format, sourcemap) => {
|
|
1875
|
-
const entry = normalize(entrypoint);
|
|
1876
|
-
const projFiles = {};
|
|
1877
|
-
for (const [k, v] of Object.entries(files)) {
|
|
1878
|
-
const path = normalize(k);
|
|
1879
|
-
let src = v;
|
|
1880
|
-
if (/\.[tj]sx?$/.test(path)) {
|
|
1881
|
-
if (hasAssetMacro(src))
|
|
1882
|
-
src = desugarAssetMacros(src, path);
|
|
1883
|
-
if (hasAspectExtends(src))
|
|
1884
|
-
src = injectAspectNames(src);
|
|
1885
|
-
}
|
|
1886
|
-
projFiles[path] = src;
|
|
1887
|
-
}
|
|
1888
|
-
if (!projFiles[entry])
|
|
1889
|
-
throw new Error(`Entrypoint not found: ${entry}`);
|
|
1890
|
-
const sdkFiles = collectSdkFiles(resolveSdkSrc());
|
|
1891
|
-
const minify = process.env.LECODES_CHISEL_MINIFY !== "0";
|
|
1892
|
-
const input = JSON.stringify({
|
|
1893
|
-
files: { ...sdkFiles, ...projFiles },
|
|
1894
|
-
entry,
|
|
1895
|
-
inject: ["/__sdk/inject.ts"],
|
|
1896
|
-
format,
|
|
1897
|
-
minify,
|
|
1898
|
-
fuse: process.env.LECODES_CHISEL_FUSE === "1",
|
|
1899
|
-
define: CHISEL_DEFINE,
|
|
1900
|
-
keep: CHISEL_KEEP,
|
|
1901
|
-
sourcemap,
|
|
1902
|
-
reactive_ui: process.env.LECODES_CHISEL_REACTIVE_UI !== "0"
|
|
1903
|
-
});
|
|
1904
|
-
const bin = resolveChiselBin();
|
|
1905
|
-
const r = spawnSync(bin, [], { input, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
|
|
1906
|
-
if (r.status === 2 || r.error) {
|
|
1907
|
-
throw new Error(`chisel failed: ${r.error?.message ?? r.stderr ?? "unknown error"}`);
|
|
1908
|
-
}
|
|
1909
|
-
let parsed;
|
|
1910
|
-
try {
|
|
1911
|
-
parsed = JSON.parse(r.stdout || "{}");
|
|
1912
|
-
} catch {
|
|
1913
|
-
throw new Error(`chisel produced invalid output: ${(r.stdout || r.stderr || "").slice(0, 200)}`);
|
|
1914
|
-
}
|
|
1915
|
-
if (parsed.error)
|
|
1916
|
-
throw new Error(`chisel: ${parsed.error}`);
|
|
1917
|
-
if (parsed.code == null)
|
|
1918
|
-
throw new Error("chisel produced no code");
|
|
1919
|
-
return { code: parsed.code, map: parsed.map ?? null };
|
|
1920
|
-
};
|
|
1921
|
-
var bundleProjectWithMap = async (entrypoint, files, format = "esm") => bundleProjectChisel(entrypoint, files, format, true);
|
|
1922
|
-
|
|
1923
|
-
// ../sdk/src/compile/header.ts
|
|
1924
|
-
var buildHeader = (name, code, opts = {}) => {
|
|
1925
|
-
let header = `// name: ${name.slice(0, 20)}
|
|
1926
|
-
`;
|
|
1927
|
-
if (opts.icon)
|
|
1928
|
-
header += `// icon: ${opts.icon}
|
|
1929
|
-
`;
|
|
1930
|
-
header += `// last-updated: ${new Date().toISOString()}
|
|
1931
|
-
`;
|
|
1932
|
-
const localAssets = new Set;
|
|
1933
|
-
for (const m of code.matchAll(/_creatorUtils\.fetchLocal\((.+?)\)/g)) {
|
|
1934
|
-
localAssets.add(m[1].replaceAll(/['"]/g, "").trim());
|
|
1935
|
-
}
|
|
1936
|
-
if (/_creator\.createGlb/.test(code))
|
|
1937
|
-
localAssets.add("uberarchive");
|
|
1938
|
-
if (/_creator\.captureImage/.test(code))
|
|
1939
|
-
localAssets.add("capture");
|
|
1940
|
-
if (localAssets.size > 0)
|
|
1941
|
-
header += `// preload: ${[...localAssets].join(";")}
|
|
1942
|
-
`;
|
|
1943
|
-
if (!code.includes("_creator.createScene") && !code.includes("_creator.createEntity"))
|
|
1944
|
-
header += `// gl: disable
|
|
1945
|
-
`;
|
|
1946
|
-
else if (code.includes("_creator.createEngine"))
|
|
1947
|
-
header += `// gl: defer
|
|
1948
|
-
`;
|
|
1949
|
-
if (code.includes("_creator2d."))
|
|
1950
|
-
header += `// 2d: enable
|
|
1951
|
-
`;
|
|
1952
|
-
if (code.includes("_creator.createARController"))
|
|
1953
|
-
header += "// ar: enable";
|
|
1954
|
-
header += `
|
|
1955
|
-
`;
|
|
1956
|
-
return header;
|
|
1957
|
-
};
|
|
1958
|
-
|
|
1959
|
-
// ../sdk/src/compile/sourcemap.ts
|
|
1960
|
-
var offsetSourceMap = (mapJson, lines) => {
|
|
1961
|
-
if (lines <= 0)
|
|
1962
|
-
return mapJson;
|
|
1963
|
-
const map = JSON.parse(mapJson);
|
|
1964
|
-
map.mappings = ";".repeat(lines) + map.mappings;
|
|
1965
|
-
return JSON.stringify(map);
|
|
1966
|
-
};
|
|
1967
|
-
var inlineSourceMapComment = (mapJson) => {
|
|
1968
|
-
const b64 = Buffer.from(mapJson, "utf8").toString("base64");
|
|
1969
|
-
return `//# lecodesMap=data:application/json;charset=utf-8;base64,${b64}`;
|
|
2073
|
+
var classifyKind = (name) => {
|
|
2074
|
+
const e = ext(name);
|
|
2075
|
+
if (e === "mat")
|
|
2076
|
+
return "shader";
|
|
2077
|
+
if (e === "scene")
|
|
2078
|
+
return "scene";
|
|
2079
|
+
return TEXT_EXTS.has(e) ? "text" : "resource";
|
|
1970
2080
|
};
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
2081
|
+
var isTextKind = (kind) => kind !== "resource";
|
|
2082
|
+
var scanLocal = (root, opts = {}) => {
|
|
2083
|
+
const { ignore, tracked } = opts;
|
|
2084
|
+
const files = [];
|
|
2085
|
+
const dirs = [];
|
|
2086
|
+
const ignored = [];
|
|
2087
|
+
const isTracked = (key) => tracked?.has(key) ?? false;
|
|
2088
|
+
const hasTrackedUnder = (key) => {
|
|
2089
|
+
if (!tracked)
|
|
2090
|
+
return false;
|
|
2091
|
+
const prefix = key + "/";
|
|
2092
|
+
for (const t of tracked)
|
|
2093
|
+
if (t.startsWith(prefix))
|
|
2094
|
+
return true;
|
|
2095
|
+
return false;
|
|
2096
|
+
};
|
|
2097
|
+
const walk = (dir, parentIgnored) => {
|
|
2098
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
2099
|
+
const abs = join8(dir, entry.name);
|
|
2100
|
+
const key = toKey(relative(root, abs));
|
|
2101
|
+
if (entry.isDirectory()) {
|
|
2102
|
+
if (SKIP_DIRS.has(entry.name))
|
|
2103
|
+
continue;
|
|
2104
|
+
const dirIgnored = parentIgnored || !!ignore && ignore(key, true);
|
|
2105
|
+
if (dirIgnored && !isTracked(key) && !hasTrackedUnder(key)) {
|
|
2106
|
+
ignored.push(key + "/");
|
|
2107
|
+
continue;
|
|
2108
|
+
}
|
|
2109
|
+
dirs.push(key);
|
|
2110
|
+
walk(abs, dirIgnored);
|
|
2111
|
+
} else if (entry.isFile()) {
|
|
2112
|
+
if (SKIP_FILES.has(entry.name))
|
|
2113
|
+
continue;
|
|
2114
|
+
const fileIgnored = parentIgnored || !!ignore && ignore(key, false);
|
|
2115
|
+
if (fileIgnored && !isTracked(key)) {
|
|
2116
|
+
ignored.push(key);
|
|
2117
|
+
continue;
|
|
2118
|
+
}
|
|
2119
|
+
files.push({ path: key, absPath: abs, sha: sha256(readFileSync5(abs)) });
|
|
1982
2120
|
}
|
|
1983
2121
|
}
|
|
1984
|
-
}
|
|
1985
|
-
|
|
2122
|
+
};
|
|
2123
|
+
walk(root, false);
|
|
2124
|
+
return { files, dirs, ignored };
|
|
1986
2125
|
};
|
|
1987
|
-
var
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
2126
|
+
var scanProject = (root, manifest) => scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest.files)) });
|
|
2127
|
+
var diffLocal = (manifest, scan) => {
|
|
2128
|
+
const added = [];
|
|
2129
|
+
const modified = [];
|
|
2130
|
+
const localByPath = new Map(scan.files.map((f) => [f.path, f]));
|
|
2131
|
+
for (const file of scan.files) {
|
|
2132
|
+
const known = manifest.files[file.path];
|
|
2133
|
+
if (!known)
|
|
2134
|
+
added.push(file);
|
|
2135
|
+
else if (known.sha !== file.sha)
|
|
2136
|
+
modified.push(file);
|
|
1991
2137
|
}
|
|
1992
|
-
const
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
}
|
|
2001
|
-
} else if (e.type === "shader") {
|
|
2002
|
-
const shaders = e.shaders ?? [];
|
|
2003
|
-
if (shaders.length > 2) {
|
|
2004
|
-
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.subBackend ?? _creator.backend}")}\``;
|
|
2005
|
-
} else if (shaders.length > 0) {
|
|
2006
|
-
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.backend}")}\``;
|
|
2007
|
-
} else {
|
|
2008
|
-
files[e.path] = `export default ""`;
|
|
2009
|
-
}
|
|
2010
|
-
} else if (e.path.toLowerCase().endsWith(".svg")) {
|
|
2011
|
-
files[e.path] = `export default SvgSource(\`${e.text ?? ""}\`)`;
|
|
2012
|
-
} else {
|
|
2013
|
-
files[e.path] = e.text ?? "";
|
|
2138
|
+
const deleted = Object.keys(manifest.files).filter((path) => !localByPath.has(path));
|
|
2139
|
+
const moves = new Map;
|
|
2140
|
+
const claimed = new Set;
|
|
2141
|
+
for (const file of added) {
|
|
2142
|
+
const from = deleted.find((path) => !claimed.has(path) && manifest.files[path].sha === file.sha);
|
|
2143
|
+
if (from) {
|
|
2144
|
+
moves.set(file.path, from);
|
|
2145
|
+
claimed.add(from);
|
|
2014
2146
|
}
|
|
2015
2147
|
}
|
|
2016
|
-
|
|
2017
|
-
if (opts.header === false) {
|
|
2018
|
-
return map ? `${code}
|
|
2019
|
-
${inlineSourceMapComment(map)}` : code;
|
|
2020
|
-
}
|
|
2021
|
-
const header = buildHeader(opts.name, code, { icon: opts.icon });
|
|
2022
|
-
if (!map)
|
|
2023
|
-
return header + code;
|
|
2024
|
-
const headerLines = (header.match(/\n/g) ?? []).length;
|
|
2025
|
-
return `${header}${code}
|
|
2026
|
-
${inlineSourceMapComment(offsetSourceMap(map, headerLines))}`;
|
|
2148
|
+
return { added, modified, deleted, moves };
|
|
2027
2149
|
};
|
|
2150
|
+
var hasChanges = (diff) => diff.added.length + diff.modified.length + diff.deleted.length > 0;
|
|
2151
|
+
|
|
2028
2152
|
// src/compile/projectCompile.ts
|
|
2029
|
-
|
|
2030
|
-
|
|
2153
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
2154
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
2155
|
+
import { dirname as dirname6, join as join11 } from "node:path";
|
|
2156
|
+
init_compile();
|
|
2157
|
+
var vendoredInject = join11(distRoot, "runtime", "sdk", "inject.ts");
|
|
2158
|
+
if (existsSync9(vendoredInject))
|
|
2031
2159
|
process.env.LECODES_SDK_INJECT = vendoredInject;
|
|
2032
2160
|
var resolveChiselBin2 = () => {
|
|
2033
2161
|
if (process.env.LECODES_CHISEL_BIN)
|
|
@@ -2037,8 +2165,8 @@ var resolveChiselBin2 = () => {
|
|
|
2037
2165
|
if (fromPkg)
|
|
2038
2166
|
return fromPkg;
|
|
2039
2167
|
} catch {}
|
|
2040
|
-
const local =
|
|
2041
|
-
return
|
|
2168
|
+
const local = join11(dirname6(process.execPath), process.platform === "win32" ? "chisel.exe" : "chisel");
|
|
2169
|
+
return existsSync9(local) ? local : null;
|
|
2042
2170
|
};
|
|
2043
2171
|
var chiselBin = resolveChiselBin2();
|
|
2044
2172
|
if (chiselBin)
|
|
@@ -2050,13 +2178,15 @@ var compileProject2 = (opts) => compileProject({
|
|
|
2050
2178
|
entryOverride: opts.entryOverride,
|
|
2051
2179
|
header: opts.header,
|
|
2052
2180
|
format: opts.format,
|
|
2053
|
-
localAssets: opts.localAssets
|
|
2181
|
+
localAssets: opts.localAssets,
|
|
2182
|
+
minify: opts.minify,
|
|
2183
|
+
editor: opts.editor
|
|
2054
2184
|
});
|
|
2055
2185
|
|
|
2056
2186
|
// src/compile/assetIcons.ts
|
|
2057
2187
|
import { createHash as createHash2 } from "node:crypto";
|
|
2058
|
-
import { existsSync as
|
|
2059
|
-
import { dirname as dirname7, join as
|
|
2188
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "node:fs";
|
|
2189
|
+
import { dirname as dirname7, join as join12 } from "node:path";
|
|
2060
2190
|
var DEFAULT_REGISTRY = process.env.LECODES_ICON_REGISTRY?.trim() || "https://icon-registry.jt3.ru/v1";
|
|
2061
2191
|
var ICON_ID = /^([a-z0-9]+(?:-[a-z0-9]+)*):([a-z0-9]+(?:-[a-z0-9]+)*)$/;
|
|
2062
2192
|
var isIdentStart = (c2) => c2 >= "A" && c2 <= "Z" || c2 >= "a" && c2 <= "z" || c2 === "_" || c2 === "$";
|
|
@@ -2308,14 +2438,14 @@ var readText = async (registry, rel) => {
|
|
|
2308
2438
|
return res.ok ? await res.text() : null;
|
|
2309
2439
|
}
|
|
2310
2440
|
try {
|
|
2311
|
-
return
|
|
2441
|
+
return readFileSync8(join12(registry, rel), "utf8");
|
|
2312
2442
|
} catch {
|
|
2313
2443
|
return null;
|
|
2314
2444
|
}
|
|
2315
2445
|
};
|
|
2316
2446
|
var readLock = (path) => {
|
|
2317
2447
|
try {
|
|
2318
|
-
const lock = JSON.parse(
|
|
2448
|
+
const lock = JSON.parse(readFileSync8(path, "utf8"));
|
|
2319
2449
|
if (lock && typeof lock === "object" && lock.icons && lock.packs)
|
|
2320
2450
|
return lock;
|
|
2321
2451
|
} catch {}
|
|
@@ -2352,7 +2482,7 @@ var renderNotices = (lock) => {
|
|
|
2352
2482
|
`);
|
|
2353
2483
|
};
|
|
2354
2484
|
var writeIfChanged = (path, text) => {
|
|
2355
|
-
const current =
|
|
2485
|
+
const current = existsSync10(path) ? safeRead(path) : null;
|
|
2356
2486
|
if (current === text)
|
|
2357
2487
|
return false;
|
|
2358
2488
|
writeFileSync7(path, text);
|
|
@@ -2360,20 +2490,20 @@ var writeIfChanged = (path, text) => {
|
|
|
2360
2490
|
};
|
|
2361
2491
|
var safeRead = (path) => {
|
|
2362
2492
|
try {
|
|
2363
|
-
return
|
|
2493
|
+
return readFileSync8(path, "utf8");
|
|
2364
2494
|
} catch {
|
|
2365
2495
|
return null;
|
|
2366
2496
|
}
|
|
2367
2497
|
};
|
|
2368
2498
|
var vendorIcons = async (designDir, ids, opts = {}) => {
|
|
2369
2499
|
const registry = (opts.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
|
|
2370
|
-
const lockPath =
|
|
2500
|
+
const lockPath = join12(designDir, "icons.lock.json");
|
|
2371
2501
|
const lock = readLock(lockPath) ?? { registry, icons: {}, packs: {} };
|
|
2372
2502
|
const svgById = new Map;
|
|
2373
2503
|
const missing = [];
|
|
2374
2504
|
for (const id of ids) {
|
|
2375
2505
|
const li = lock.icons[id];
|
|
2376
|
-
const cached = li && safeRead(
|
|
2506
|
+
const cached = li && safeRead(join12(designDir, li.path));
|
|
2377
2507
|
if (cached)
|
|
2378
2508
|
svgById.set(id, cached);
|
|
2379
2509
|
else
|
|
@@ -2394,7 +2524,7 @@ var vendorIcons = async (designDir, ids, opts = {}) => {
|
|
|
2394
2524
|
if (svg === null)
|
|
2395
2525
|
throw new Error(`assetIcon: icon "${id}" not found in the registry. Check the name at ${registryHome(registry)}.`);
|
|
2396
2526
|
const rel = `assets/icons/${pack}/${name}.svg`;
|
|
2397
|
-
const abs =
|
|
2527
|
+
const abs = join12(designDir, rel);
|
|
2398
2528
|
mkdirSync7(dirname7(abs), { recursive: true });
|
|
2399
2529
|
writeFileSync7(abs, svg);
|
|
2400
2530
|
lock.icons[id] = { pack, name, hash: sha12(svg), spdx: lic.spdx, path: rel };
|
|
@@ -2405,7 +2535,7 @@ var vendorIcons = async (designDir, ids, opts = {}) => {
|
|
|
2405
2535
|
writeIfChanged(lockPath, serializeLock(lock));
|
|
2406
2536
|
}
|
|
2407
2537
|
if (Object.keys(lock.icons).length)
|
|
2408
|
-
writeIfChanged(
|
|
2538
|
+
writeIfChanged(join12(designDir, "ICON-NOTICES.md"), renderNotices(lock));
|
|
2409
2539
|
return svgById;
|
|
2410
2540
|
};
|
|
2411
2541
|
var applyAssetIcons = async (entries, designDir, opts = {}) => {
|
|
@@ -2440,7 +2570,7 @@ var compileDesignScreen = async (opts) => {
|
|
|
2440
2570
|
} else if (type === "shader") {
|
|
2441
2571
|
entries.push({ path: file.path, type, shaders: [] });
|
|
2442
2572
|
} else {
|
|
2443
|
-
entries.push({ path: file.path, type, text:
|
|
2573
|
+
entries.push({ path: file.path, type, text: readFileSync9(file.absPath, "utf8") });
|
|
2444
2574
|
}
|
|
2445
2575
|
}
|
|
2446
2576
|
const invoke = state !== undefined ? `screen(${JSON.stringify(state)})` : "screen()";
|
|
@@ -2849,31 +2979,31 @@ var getContext = (args) => {
|
|
|
2849
2979
|
manifest,
|
|
2850
2980
|
name: manifest?.name ?? basename2(root),
|
|
2851
2981
|
dirName,
|
|
2852
|
-
designDir:
|
|
2982
|
+
designDir: join13(root, dirName),
|
|
2853
2983
|
designKey: "/" + dirName.replace(/\\/g, "/")
|
|
2854
2984
|
};
|
|
2855
2985
|
};
|
|
2856
2986
|
var listScreens = (designDir) => {
|
|
2857
|
-
const dir =
|
|
2858
|
-
if (!
|
|
2987
|
+
const dir = join13(designDir, "screens");
|
|
2988
|
+
if (!existsSync11(dir))
|
|
2859
2989
|
return [];
|
|
2860
|
-
return
|
|
2990
|
+
return readdirSync6(dir).filter((f) => f.endsWith(".ts") && !f.startsWith(".")).map((f) => f.slice(0, -3)).sort();
|
|
2861
2991
|
};
|
|
2862
2992
|
var readMetaSizes = (designDir) => {
|
|
2863
2993
|
try {
|
|
2864
|
-
const meta = JSON.parse(
|
|
2994
|
+
const meta = JSON.parse(readFileSync10(join13(designDir, "meta.json"), "utf8"));
|
|
2865
2995
|
return meta?.screens ?? {};
|
|
2866
2996
|
} catch {
|
|
2867
2997
|
return {};
|
|
2868
2998
|
}
|
|
2869
2999
|
};
|
|
2870
3000
|
var writeMcpConfig = (ctx, port) => {
|
|
2871
|
-
const path =
|
|
3001
|
+
const path = join13(ctx.root, ".mcp.json");
|
|
2872
3002
|
const entry = { type: "http", url: `http://localhost:${port}/mcp` };
|
|
2873
3003
|
let config = {};
|
|
2874
|
-
if (
|
|
3004
|
+
if (existsSync11(path)) {
|
|
2875
3005
|
try {
|
|
2876
|
-
config = JSON.parse(
|
|
3006
|
+
config = JSON.parse(readFileSync10(path, "utf8"));
|
|
2877
3007
|
} catch {
|
|
2878
3008
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-design server by hand.");
|
|
2879
3009
|
return;
|
|
@@ -2896,15 +3026,15 @@ var init = async (ctx, args) => {
|
|
|
2896
3026
|
["spec.md", SPEC_MD],
|
|
2897
3027
|
["README.md", README_HINT],
|
|
2898
3028
|
["CLAUDE.md", CLAUDE_MD],
|
|
2899
|
-
[
|
|
2900
|
-
[
|
|
3029
|
+
[join13("shared", "tokens.ts"), TOKENS_TS],
|
|
3030
|
+
[join13("screens", "home.ts"), HOME_SCREEN_TS]
|
|
2901
3031
|
];
|
|
2902
|
-
mkdirSync8(
|
|
2903
|
-
mkdirSync8(
|
|
3032
|
+
mkdirSync8(join13(ctx.designDir, "screens"), { recursive: true });
|
|
3033
|
+
mkdirSync8(join13(ctx.designDir, "shared"), { recursive: true });
|
|
2904
3034
|
let created = 0;
|
|
2905
3035
|
for (const [rel, content] of files) {
|
|
2906
|
-
const abs =
|
|
2907
|
-
if (
|
|
3036
|
+
const abs = join13(ctx.designDir, rel);
|
|
3037
|
+
if (existsSync11(abs)) {
|
|
2908
3038
|
note(`kept ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
2909
3039
|
continue;
|
|
2910
3040
|
}
|
|
@@ -2912,9 +3042,9 @@ var init = async (ctx, args) => {
|
|
|
2912
3042
|
log(`${c.green("created")} ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
2913
3043
|
created++;
|
|
2914
3044
|
}
|
|
2915
|
-
const ignorePath =
|
|
3045
|
+
const ignorePath = join13(ctx.root, ".lecodesignore");
|
|
2916
3046
|
const ignoreLine = `${ctx.dirName}/.snapshots/`;
|
|
2917
|
-
const current =
|
|
3047
|
+
const current = existsSync11(ignorePath) ? readFileSync10(ignorePath, "utf8") : "";
|
|
2918
3048
|
if (!current.split(/\r?\n/).includes(ignoreLine)) {
|
|
2919
3049
|
appendFileSync(ignorePath, (current && !current.endsWith(`
|
|
2920
3050
|
`) ? `
|
|
@@ -2925,8 +3055,8 @@ var init = async (ctx, args) => {
|
|
|
2925
3055
|
writeMcpConfig(ctx, Number(flagStr(args, "port")) || DEFAULT_PORT);
|
|
2926
3056
|
const wroteTypes = materializeTypesLocal(ctx.designDir);
|
|
2927
3057
|
writeTsconfig(ctx.designDir);
|
|
2928
|
-
mkdirSync8(
|
|
2929
|
-
writeFileSync8(
|
|
3058
|
+
mkdirSync8(join13(ctx.designDir, ".lecodes", "types"), { recursive: true });
|
|
3059
|
+
writeFileSync8(join13(ctx.designDir, ".lecodes", "types", "design-macros.d.ts"), DESIGN_MACROS_DTS);
|
|
2930
3060
|
log(`${c.green("created")} ${ctx.dirName}/tsconfig.json${wroteTypes ? ` + ${ctx.dirName}/.lecodes/types/` : ""} (IDE types)`);
|
|
2931
3061
|
if (!wroteTypes)
|
|
2932
3062
|
warnErr("Bundled SDK types not found — tsconfig written, but globals may show unresolved until the CLI is rebuilt with vendored types.");
|
|
@@ -2975,7 +3105,7 @@ var snapshot = async (ctx, args) => {
|
|
|
2975
3105
|
killer.unref?.();
|
|
2976
3106
|
const png = flagBool(args, "png") || flagStr(args, "png") !== undefined;
|
|
2977
3107
|
const sizes = readMetaSizes(ctx.designDir);
|
|
2978
|
-
const outDir = flagStr(args, "out-dir") ??
|
|
3108
|
+
const outDir = flagStr(args, "out-dir") ?? join13(ctx.designDir, ".snapshots");
|
|
2979
3109
|
const single = targets.length === 1 && requested.length === 1 && !png && !allStates && !flagStr(args, "out-dir");
|
|
2980
3110
|
const onConsole = flagBool(args, "logs") ? (level, text) => logErr(`${c.dim(`[${level}]`)} ${text}`) : undefined;
|
|
2981
3111
|
if (!single)
|
|
@@ -2984,7 +3114,7 @@ var snapshot = async (ctx, args) => {
|
|
|
2984
3114
|
const [width, height] = sizes[id]?.size ?? [390, 844];
|
|
2985
3115
|
let plan = [{ state: undefined, bases: [id] }];
|
|
2986
3116
|
if (discover) {
|
|
2987
|
-
const { states, canonical } = discover(
|
|
3117
|
+
const { states, canonical } = discover(readFileSync10(join13(ctx.designDir, "screens", `${id}.ts`), "utf8"));
|
|
2988
3118
|
if (stateFlag !== undefined) {
|
|
2989
3119
|
if (!states.includes(stateFlag)) {
|
|
2990
3120
|
if (requested.length === 1)
|
|
@@ -3025,18 +3155,18 @@ var snapshot = async (ctx, args) => {
|
|
|
3025
3155
|
for (const w of pngResult.warnings)
|
|
3026
3156
|
warnErr(`${tag}: ${w}`);
|
|
3027
3157
|
for (const base of bases) {
|
|
3028
|
-
writeFileSync8(
|
|
3029
|
-
log(`${c.green("✓")} ${tag} → ${
|
|
3158
|
+
writeFileSync8(join13(outDir, `${base}.json`), result.text);
|
|
3159
|
+
log(`${c.green("✓")} ${tag} → ${join13(outDir, `${base}.json`)}`);
|
|
3030
3160
|
if (pngResult) {
|
|
3031
|
-
writeFileSync8(
|
|
3032
|
-
log(`${c.green("✓")} ${tag} → ${
|
|
3161
|
+
writeFileSync8(join13(outDir, `${base}.png`), pngResult.png);
|
|
3162
|
+
log(`${c.green("✓")} ${tag} → ${join13(outDir, `${base}.png`)}`);
|
|
3033
3163
|
}
|
|
3034
3164
|
}
|
|
3035
3165
|
}
|
|
3036
3166
|
}
|
|
3037
3167
|
};
|
|
3038
3168
|
var serve = async (ctx, args) => {
|
|
3039
|
-
if (!
|
|
3169
|
+
if (!existsSync11(ctx.designDir)) {
|
|
3040
3170
|
throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
|
|
3041
3171
|
}
|
|
3042
3172
|
let designServer;
|
|
@@ -3109,7 +3239,7 @@ var serve = async (ctx, args) => {
|
|
|
3109
3239
|
openBrowser(server.url);
|
|
3110
3240
|
};
|
|
3111
3241
|
var arrange = async (ctx, args) => {
|
|
3112
|
-
if (!
|
|
3242
|
+
if (!existsSync11(ctx.designDir)) {
|
|
3113
3243
|
throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
|
|
3114
3244
|
}
|
|
3115
3245
|
let designServer;
|
|
@@ -3145,7 +3275,7 @@ var share = async (ctx, args) => {
|
|
|
3145
3275
|
note("Turn it off with: lecodes design share --off");
|
|
3146
3276
|
};
|
|
3147
3277
|
var check = async (ctx, args) => {
|
|
3148
|
-
if (!
|
|
3278
|
+
if (!existsSync11(ctx.designDir)) {
|
|
3149
3279
|
throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
|
|
3150
3280
|
}
|
|
3151
3281
|
let designServer;
|
|
@@ -3210,18 +3340,18 @@ var design = async (args) => {
|
|
|
3210
3340
|
};
|
|
3211
3341
|
|
|
3212
3342
|
// src/commands/dev.ts
|
|
3213
|
-
import { existsSync as
|
|
3343
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync12, watch, writeFileSync as writeFileSync9 } from "node:fs";
|
|
3214
3344
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
3215
3345
|
import { networkInterfaces } from "node:os";
|
|
3216
|
-
import { basename as basename4, join as
|
|
3346
|
+
import { basename as basename4, join as join15 } from "node:path";
|
|
3217
3347
|
|
|
3218
3348
|
// src/dev/devServer.ts
|
|
3219
3349
|
import { createServer as createServer2 } from "node:http";
|
|
3220
|
-
import { createReadStream, existsSync as
|
|
3221
|
-
import { join as
|
|
3350
|
+
import { createReadStream, existsSync as existsSync12, statSync as statSync2 } from "node:fs";
|
|
3351
|
+
import { join as join14 } from "node:path";
|
|
3222
3352
|
|
|
3223
3353
|
// src/compile/collectLocal.ts
|
|
3224
|
-
import { readFileSync as
|
|
3354
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
3225
3355
|
import { basename as basename3 } from "node:path";
|
|
3226
3356
|
var collectLocalEntries = (root, manifest, assetBase) => {
|
|
3227
3357
|
const entries = [];
|
|
@@ -3235,7 +3365,7 @@ var collectLocalEntries = (root, manifest, assetBase) => {
|
|
|
3235
3365
|
warnings.push(`${file.path}: shaders compile on the server — push the project once for shaders to work in dev mode.`);
|
|
3236
3366
|
entries.push({ path: file.path, type, shaders: [] });
|
|
3237
3367
|
} else {
|
|
3238
|
-
entries.push({ path: file.path, type, text:
|
|
3368
|
+
entries.push({ path: file.path, type, text: readFileSync11(file.absPath, "utf8") });
|
|
3239
3369
|
}
|
|
3240
3370
|
}
|
|
3241
3371
|
return { entries, warnings };
|
|
@@ -3595,8 +3725,8 @@ var startDevServer = async (options) => {
|
|
|
3595
3725
|
} catch {
|
|
3596
3726
|
return sendText(res, 400, "text/plain", "Bad path");
|
|
3597
3727
|
}
|
|
3598
|
-
const abs =
|
|
3599
|
-
if (!
|
|
3728
|
+
const abs = join14(root, key.slice(1));
|
|
3729
|
+
if (!existsSync12(abs) || !statSync2(abs).isFile())
|
|
3600
3730
|
return sendText(res, 404, "text/plain", "Not found");
|
|
3601
3731
|
res.writeHead(200, { "content-type": contentType(key), "cache-control": "no-store" });
|
|
3602
3732
|
createReadStream(abs).pipe(res);
|
|
@@ -3679,14 +3809,14 @@ var pickLanAddress = () => {
|
|
|
3679
3809
|
return null;
|
|
3680
3810
|
};
|
|
3681
3811
|
var loadDevToken = (root) => {
|
|
3682
|
-
const path =
|
|
3812
|
+
const path = join15(root, LECODES_DIR, "dev.json");
|
|
3683
3813
|
try {
|
|
3684
|
-
const token2 = JSON.parse(
|
|
3814
|
+
const token2 = JSON.parse(readFileSync12(path, "utf8")).token;
|
|
3685
3815
|
if (token2 && /^[a-z0-9]{8,}$/.test(token2))
|
|
3686
3816
|
return token2;
|
|
3687
3817
|
} catch {}
|
|
3688
3818
|
const token = randomBytes2(6).toString("hex");
|
|
3689
|
-
mkdirSync9(
|
|
3819
|
+
mkdirSync9(join15(root, LECODES_DIR), { recursive: true });
|
|
3690
3820
|
writeFileSync9(path, JSON.stringify({ token }, null, 2) + `
|
|
3691
3821
|
`);
|
|
3692
3822
|
return token;
|
|
@@ -3799,14 +3929,14 @@ ${result.error}`);
|
|
|
3799
3929
|
await printQr(server.url);
|
|
3800
3930
|
log(`Scan the QR with the LeCodes app, or open: ${c.bold(server.url)}`);
|
|
3801
3931
|
note(`Older desktop builds (no websocket bridge): ${server.url}?t=poll`);
|
|
3802
|
-
if (
|
|
3932
|
+
if (existsSync13(join15(root, LECODES_DIR, "manifest.json")))
|
|
3803
3933
|
note(`Token stored in ${LECODES_DIR}/dev.json (delete it to rotate the URL).`);
|
|
3804
3934
|
log("");
|
|
3805
3935
|
log(`Watching ${root} — save a file to hot-reload. Ctrl+C to stop.`);
|
|
3806
3936
|
};
|
|
3807
3937
|
|
|
3808
3938
|
// src/serverDiff.ts
|
|
3809
|
-
import { readFileSync as
|
|
3939
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
3810
3940
|
import { basename as basename5 } from "node:path";
|
|
3811
3941
|
var isTextAsset = (t) => t === "text" || t === "shader" || t === "scene";
|
|
3812
3942
|
var computeServerDiff = (project, scan) => {
|
|
@@ -3825,7 +3955,7 @@ var computeServerDiff = (project, scan) => {
|
|
|
3825
3955
|
const local = localByPath.get(path);
|
|
3826
3956
|
if (local && !server) {
|
|
3827
3957
|
const text = isTextKind(classifyKind(basename5(path)));
|
|
3828
|
-
entries.push({ path, status: "added", binary: !text, serverText: "", localText: text ?
|
|
3958
|
+
entries.push({ path, status: "added", binary: !text, serverText: "", localText: text ? readFileSync13(local.absPath, "utf8") : undefined });
|
|
3829
3959
|
continue;
|
|
3830
3960
|
}
|
|
3831
3961
|
if (server && !local) {
|
|
@@ -3836,12 +3966,12 @@ var computeServerDiff = (project, scan) => {
|
|
|
3836
3966
|
if (server && local) {
|
|
3837
3967
|
if (isTextAsset(server.type)) {
|
|
3838
3968
|
const serverText = server.text ?? "";
|
|
3839
|
-
const localText =
|
|
3969
|
+
const localText = readFileSync13(local.absPath, "utf8");
|
|
3840
3970
|
if (serverText !== localText)
|
|
3841
3971
|
entries.push({ path, status: "modified", binary: false, serverText, localText });
|
|
3842
3972
|
} else {
|
|
3843
3973
|
const serverSize = server.file?.size;
|
|
3844
|
-
const localSize = Buffer.byteLength(
|
|
3974
|
+
const localSize = Buffer.byteLength(readFileSync13(local.absPath));
|
|
3845
3975
|
if (serverSize !== undefined && serverSize !== localSize) {
|
|
3846
3976
|
entries.push({ path, status: "modified", binary: true });
|
|
3847
3977
|
}
|
|
@@ -3994,8 +4124,8 @@ var diff = async (args) => {
|
|
|
3994
4124
|
var statusLabel = (e) => e.status === "added" ? c.green(" added ") : e.status === "removed" ? c.red(" removed ") : c.yellow(" modified ");
|
|
3995
4125
|
|
|
3996
4126
|
// src/commands/init.ts
|
|
3997
|
-
import { existsSync as
|
|
3998
|
-
import { basename as basename6, dirname as dirname8, join as
|
|
4127
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "node:fs";
|
|
4128
|
+
import { basename as basename6, dirname as dirname8, join as join16, resolve as resolve3 } from "node:path";
|
|
3999
4129
|
var STARTER2 = `// Welcome to LeCodes — the whole SDK is available as globals, no imports needed.
|
|
4000
4130
|
// Run it live on a device with \`lecodes dev\`, or bundle it with \`lecodes preview -o app.js\`.
|
|
4001
4131
|
|
|
@@ -4038,8 +4168,8 @@ var init2 = async (args) => {
|
|
|
4038
4168
|
mkdirSync10(root, { recursive: true });
|
|
4039
4169
|
let created = 0;
|
|
4040
4170
|
const write = (rel, content) => {
|
|
4041
|
-
const abs =
|
|
4042
|
-
if (
|
|
4171
|
+
const abs = join16(root, rel);
|
|
4172
|
+
if (existsSync14(abs)) {
|
|
4043
4173
|
note(`kept ${rel}`);
|
|
4044
4174
|
return;
|
|
4045
4175
|
}
|
|
@@ -4079,7 +4209,7 @@ import { writeFileSync as writeFileSync11 } from "node:fs";
|
|
|
4079
4209
|
import { basename as basename8, resolve as resolve4 } from "node:path";
|
|
4080
4210
|
|
|
4081
4211
|
// src/compile/collect.ts
|
|
4082
|
-
import { readFileSync as
|
|
4212
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
4083
4213
|
import { basename as basename7 } from "node:path";
|
|
4084
4214
|
var collectEntries = (root, manifest, project) => {
|
|
4085
4215
|
const pathById = buildPaths(project.assets);
|
|
@@ -4104,7 +4234,7 @@ var collectEntries = (root, manifest, project) => {
|
|
|
4104
4234
|
warnings.push(`No compiled shader for ${file.path} — push it to compile the shader.`);
|
|
4105
4235
|
entries.push({ path: file.path, type, shaders });
|
|
4106
4236
|
} else {
|
|
4107
|
-
entries.push({ path: file.path, type, text:
|
|
4237
|
+
entries.push({ path: file.path, type, text: readFileSync14(file.absPath, "utf8") });
|
|
4108
4238
|
}
|
|
4109
4239
|
}
|
|
4110
4240
|
return { entries, warnings };
|
|
@@ -4223,7 +4353,7 @@ var render = async (args) => {
|
|
|
4223
4353
|
format: "iife"
|
|
4224
4354
|
});
|
|
4225
4355
|
if (js.includes("_creator.createScene") || js.includes("_creator.createEntity")) {
|
|
4226
|
-
warnings.push("
|
|
4356
|
+
warnings.push("3D project: the scene graph is captured as `scene3d` in the JSON output; 3D isn't rasterized into PNGs.");
|
|
4227
4357
|
}
|
|
4228
4358
|
if (js.includes("_creator2d") && !useLocalAssets && !flagBool(args, "wait-network")) {
|
|
4229
4359
|
warnings.push("2D scene detected: sprite textures load over the network — add --wait-network to include them (else sprites render as flat placeholders).");
|
|
@@ -4292,8 +4422,8 @@ var render = async (args) => {
|
|
|
4292
4422
|
};
|
|
4293
4423
|
|
|
4294
4424
|
// src/commands/pull.ts
|
|
4295
|
-
import { existsSync as
|
|
4296
|
-
import { dirname as dirname9, join as
|
|
4425
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync11, rmSync, writeFileSync as writeFileSync13 } from "node:fs";
|
|
4426
|
+
import { dirname as dirname9, join as join17 } from "node:path";
|
|
4297
4427
|
var pull = async (args) => {
|
|
4298
4428
|
const root = findProjectRoot(process.cwd());
|
|
4299
4429
|
const manifest = readManifest(root);
|
|
@@ -4313,8 +4443,8 @@ var pull = async (args) => {
|
|
|
4313
4443
|
const remotePaths = new Set([...buildPaths(project.assets).entries()].filter(([id]) => project.assets.find((a) => a.id === id)?.type !== "folder").map(([, path]) => path));
|
|
4314
4444
|
for (const path of Object.keys(manifest.files)) {
|
|
4315
4445
|
if (!remotePaths.has(path)) {
|
|
4316
|
-
const abs =
|
|
4317
|
-
if (
|
|
4446
|
+
const abs = join17(root, ...path.replace(/^\//, "").split("/"));
|
|
4447
|
+
if (existsSync15(abs))
|
|
4318
4448
|
rmSync(abs);
|
|
4319
4449
|
}
|
|
4320
4450
|
}
|
|
@@ -4352,7 +4482,7 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
4352
4482
|
const files = { ...manifest.files };
|
|
4353
4483
|
for (const asset of targets) {
|
|
4354
4484
|
const key = pathById.get(asset.id);
|
|
4355
|
-
const abs =
|
|
4485
|
+
const abs = join17(root, ...key.replace(/^\//, "").split("/"));
|
|
4356
4486
|
mkdirSync11(dirname9(abs), { recursive: true });
|
|
4357
4487
|
let sha;
|
|
4358
4488
|
if (asset.type === "file") {
|
|
@@ -4376,7 +4506,7 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
4376
4506
|
|
|
4377
4507
|
// src/commands/push.ts
|
|
4378
4508
|
import { basename as basename9 } from "node:path";
|
|
4379
|
-
import { readFileSync as
|
|
4509
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
4380
4510
|
var push = async (args) => {
|
|
4381
4511
|
const root = findProjectRoot(process.cwd());
|
|
4382
4512
|
const manifest = readManifest(root);
|
|
@@ -4435,9 +4565,9 @@ var push = async (args) => {
|
|
|
4435
4565
|
if (source)
|
|
4436
4566
|
entry.assetId = source.id;
|
|
4437
4567
|
if (isTextKind(kind)) {
|
|
4438
|
-
entry.text =
|
|
4568
|
+
entry.text = readFileSync15(file.absPath, "utf8");
|
|
4439
4569
|
} else if (!source || source.sha !== file.sha) {
|
|
4440
|
-
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename9(file.path),
|
|
4570
|
+
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename9(file.path), readFileSync15(file.absPath));
|
|
4441
4571
|
entry.blobFileId = blob.id;
|
|
4442
4572
|
uploaded++;
|
|
4443
4573
|
}
|
|
@@ -4489,6 +4619,154 @@ var reportPublish = (res) => {
|
|
|
4489
4619
|
warn(`Push landed, but publish failed: ${res.publishError ?? "unknown error"}`);
|
|
4490
4620
|
};
|
|
4491
4621
|
|
|
4622
|
+
// src/commands/scene.ts
|
|
4623
|
+
import { existsSync as existsSync17, readFileSync as readFileSync17, readdirSync as readdirSync7, statSync as statSync3, writeFileSync as writeFileSync14 } from "node:fs";
|
|
4624
|
+
import { basename as basename10, dirname as dirname10, join as join19, resolve as resolve6 } from "node:path";
|
|
4625
|
+
|
|
4626
|
+
// src/compile/sceneCompile.ts
|
|
4627
|
+
import { existsSync as existsSync16, readFileSync as readFileSync16 } from "node:fs";
|
|
4628
|
+
import { join as join18 } from "node:path";
|
|
4629
|
+
var TEXT_EXT = /\.(ts|js|json|svg|txt|md)$/i;
|
|
4630
|
+
var rel = (key) => key.replace(/^\//, "");
|
|
4631
|
+
var resolveHarness = (loadSceneHarness2) => {
|
|
4632
|
+
const inRepo = join18(distRoot, "..", "projects", "src", "scene-editor");
|
|
4633
|
+
try {
|
|
4634
|
+
return loadSceneHarness2(existsSync16(inRepo) ? inRepo : undefined);
|
|
4635
|
+
} catch {
|
|
4636
|
+
const vendored = join18(distRoot, "runtime", "scene-harness.json");
|
|
4637
|
+
if (existsSync16(vendored))
|
|
4638
|
+
return JSON.parse(readFileSync16(vendored, "utf8"));
|
|
4639
|
+
throw new CliError("scene-editor harness sources not found — reinstall the CLI or set LECODES_SCENE_HARNESS_DIR.");
|
|
4640
|
+
}
|
|
4641
|
+
};
|
|
4642
|
+
var compileSceneEditor = async (opts) => {
|
|
4643
|
+
const { root, scenePath } = opts;
|
|
4644
|
+
const { loadSceneHarness: loadSceneHarness2, sceneEditorEntries: sceneEditorEntries2, SCENE_ENTRY_PATH: SCENE_ENTRY_PATH2 } = await Promise.resolve().then(() => (init_compile(), exports_compile));
|
|
4645
|
+
const entries = [];
|
|
4646
|
+
const scan = scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(opts.manifest?.files ?? {})) });
|
|
4647
|
+
for (const file of scan.files) {
|
|
4648
|
+
const path = "./" + rel(file.path);
|
|
4649
|
+
if (TEXT_EXT.test(path)) {
|
|
4650
|
+
entries.push({ path, type: "text", text: readFileSync16(file.absPath, "utf8") });
|
|
4651
|
+
} else {
|
|
4652
|
+
entries.push({ path, type: "resource", fileSrc: "/project/" + rel(file.path) });
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
const scene = scenePath.startsWith("./") ? scenePath : "./" + rel(scenePath);
|
|
4656
|
+
if (!entries.some((e) => e.path === scene && e.type === "text")) {
|
|
4657
|
+
return { js: null, error: `Scene file not found: ${scene}` };
|
|
4658
|
+
}
|
|
4659
|
+
entries.push(...sceneEditorEntries2(scene, resolveHarness(loadSceneHarness2), entries.map((e) => e.path)));
|
|
4660
|
+
try {
|
|
4661
|
+
const js = await compileProject2({
|
|
4662
|
+
entries,
|
|
4663
|
+
name: opts.name,
|
|
4664
|
+
publicUrl: "",
|
|
4665
|
+
entryOverride: SCENE_ENTRY_PATH2,
|
|
4666
|
+
header: false,
|
|
4667
|
+
format: "iife",
|
|
4668
|
+
minify: false,
|
|
4669
|
+
editor: true
|
|
4670
|
+
});
|
|
4671
|
+
return { js };
|
|
4672
|
+
} catch (e) {
|
|
4673
|
+
return { js: null, error: e?.message?.replace(/mem:/g, "") ?? String(e) };
|
|
4674
|
+
}
|
|
4675
|
+
};
|
|
4676
|
+
|
|
4677
|
+
// src/commands/scene.ts
|
|
4678
|
+
var DEFAULT_PORT3 = 4488;
|
|
4679
|
+
var getContext2 = (args) => {
|
|
4680
|
+
let root;
|
|
4681
|
+
let manifest;
|
|
4682
|
+
try {
|
|
4683
|
+
root = findProjectRoot(process.cwd());
|
|
4684
|
+
manifest = readManifest(root);
|
|
4685
|
+
} catch {
|
|
4686
|
+
root = process.cwd();
|
|
4687
|
+
manifest = null;
|
|
4688
|
+
const pathArg = args._[0];
|
|
4689
|
+
if (pathArg) {
|
|
4690
|
+
const abs = resolve6(process.cwd(), pathArg);
|
|
4691
|
+
if (existsSync17(abs) && statSync3(abs).isDirectory())
|
|
4692
|
+
root = abs;
|
|
4693
|
+
else if (abs.endsWith(".scene.ts") && existsSync17(abs))
|
|
4694
|
+
root = dirname10(abs);
|
|
4695
|
+
else
|
|
4696
|
+
throw new CliError(`No such folder or .scene.ts: ${pathArg}`);
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
return { root, manifest, name: manifest?.name ?? basename10(root) };
|
|
4700
|
+
};
|
|
4701
|
+
var writeMcpConfig2 = (root, port) => {
|
|
4702
|
+
const path = join19(root, ".mcp.json");
|
|
4703
|
+
const entry = { type: "http", url: `http://localhost:${port}/mcp` };
|
|
4704
|
+
let config = {};
|
|
4705
|
+
if (existsSync17(path)) {
|
|
4706
|
+
try {
|
|
4707
|
+
config = JSON.parse(readFileSync17(path, "utf8"));
|
|
4708
|
+
} catch {
|
|
4709
|
+
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-scene server by hand.");
|
|
4710
|
+
return;
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
config.mcpServers ??= {};
|
|
4714
|
+
const existing = config.mcpServers["lecodes-scene"];
|
|
4715
|
+
if (existing?.url === entry.url) {
|
|
4716
|
+
note("kept .mcp.json (lecodes-scene MCP server)");
|
|
4717
|
+
return;
|
|
4718
|
+
}
|
|
4719
|
+
config.mcpServers["lecodes-scene"] = entry;
|
|
4720
|
+
writeFileSync14(path, JSON.stringify(config, null, 2) + `
|
|
4721
|
+
`);
|
|
4722
|
+
note(`${existing ? "updated" : "created"} .mcp.json (lecodes-scene MCP → ${entry.url})`);
|
|
4723
|
+
};
|
|
4724
|
+
var countScenes = (dir) => {
|
|
4725
|
+
let n = 0;
|
|
4726
|
+
for (const name of readdirSync7(dir)) {
|
|
4727
|
+
if (name === "node_modules" || name.startsWith("."))
|
|
4728
|
+
continue;
|
|
4729
|
+
const full = join19(dir, name);
|
|
4730
|
+
if (statSync3(full).isDirectory())
|
|
4731
|
+
n += countScenes(full);
|
|
4732
|
+
else if (name.endsWith(".scene.ts"))
|
|
4733
|
+
n++;
|
|
4734
|
+
}
|
|
4735
|
+
return n;
|
|
4736
|
+
};
|
|
4737
|
+
var scene = async (args) => {
|
|
4738
|
+
const ctx = getContext2(args);
|
|
4739
|
+
let sceneServer;
|
|
4740
|
+
try {
|
|
4741
|
+
sceneServer = await import("lecodes-3d-editor/server");
|
|
4742
|
+
} catch {
|
|
4743
|
+
throw new CliError("The scene editor isn't installed. Run: npm i -D lecodes-3d-editor");
|
|
4744
|
+
}
|
|
4745
|
+
const port = Number(flagStr(args, "port")) || DEFAULT_PORT3;
|
|
4746
|
+
if (countScenes(ctx.root) === 0) {
|
|
4747
|
+
warnErr(`No .scene.ts file under ${ctx.root} yet — add one and the editor will pick it up.`);
|
|
4748
|
+
}
|
|
4749
|
+
const server = await sceneServer.startSceneServer({
|
|
4750
|
+
root: ctx.root,
|
|
4751
|
+
port,
|
|
4752
|
+
log: note,
|
|
4753
|
+
compile: (scenePath) => compileSceneEditor({
|
|
4754
|
+
root: ctx.root,
|
|
4755
|
+
manifest: ctx.manifest,
|
|
4756
|
+
name: ctx.name,
|
|
4757
|
+
scenePath
|
|
4758
|
+
})
|
|
4759
|
+
});
|
|
4760
|
+
if (!flagBool(args, "no-mcp"))
|
|
4761
|
+
writeMcpConfig2(ctx.root, port);
|
|
4762
|
+
log(`${c.bold("LeCodes Scene Editor")} — ${ctx.name}`);
|
|
4763
|
+
log(`Editor: ${c.bold(server.url)}`);
|
|
4764
|
+
log(`MCP: ${c.bold(server.url + "/mcp")}`);
|
|
4765
|
+
log(`Watching: ${ctx.root} (scenes hot-reload on save; Ctrl+C to stop)`);
|
|
4766
|
+
if (!flagBool(args, "no-open"))
|
|
4767
|
+
openBrowser(server.url);
|
|
4768
|
+
};
|
|
4769
|
+
|
|
4492
4770
|
// src/commands/status.ts
|
|
4493
4771
|
var status = async (_args) => {
|
|
4494
4772
|
const root = findProjectRoot(process.cwd());
|
|
@@ -4519,6 +4797,206 @@ var status = async (_args) => {
|
|
|
4519
4797
|
};
|
|
4520
4798
|
|
|
4521
4799
|
// src/index.ts
|
|
4800
|
+
import { existsSync as existsSync19, realpathSync as realpathSync2 } from "node:fs";
|
|
4801
|
+
import { delimiter as delimiter2, dirname as dirname12, join as join21 } from "node:path";
|
|
4802
|
+
|
|
4803
|
+
// src/commands/install.ts
|
|
4804
|
+
import { execFileSync } from "node:child_process";
|
|
4805
|
+
import { appendFileSync as appendFileSync2, chmodSync, cpSync, existsSync as existsSync18, mkdirSync as mkdirSync12, readFileSync as readFileSync18, realpathSync, rmSync as rmSync2, symlinkSync } from "node:fs";
|
|
4806
|
+
import { homedir as homedir2 } from "node:os";
|
|
4807
|
+
import { basename as basename11, delimiter, dirname as dirname11, join as join20, resolve as resolve7, sep } from "node:path";
|
|
4808
|
+
var isWin = process.platform === "win32";
|
|
4809
|
+
var HOME = homedir2();
|
|
4810
|
+
var safeRealpath = (p) => {
|
|
4811
|
+
try {
|
|
4812
|
+
return realpathSync(p);
|
|
4813
|
+
} catch {
|
|
4814
|
+
return resolve7(p);
|
|
4815
|
+
}
|
|
4816
|
+
};
|
|
4817
|
+
var onPath = (dir) => {
|
|
4818
|
+
const want = safeRealpath(dir);
|
|
4819
|
+
return (process.env.PATH ?? "").split(delimiter).some((p) => {
|
|
4820
|
+
if (!p)
|
|
4821
|
+
return false;
|
|
4822
|
+
try {
|
|
4823
|
+
return safeRealpath(p) === want;
|
|
4824
|
+
} catch {
|
|
4825
|
+
return false;
|
|
4826
|
+
}
|
|
4827
|
+
});
|
|
4828
|
+
};
|
|
4829
|
+
var install = async (args) => {
|
|
4830
|
+
const bundle = dirname11(process.execPath);
|
|
4831
|
+
const exeName = basename11(process.execPath);
|
|
4832
|
+
const chiselName = isWin ? "chisel.exe" : "chisel";
|
|
4833
|
+
if (!existsSync18(join20(bundle, "runtime")) || !existsSync18(join20(bundle, chiselName))) {
|
|
4834
|
+
throw new CliError("`lecodes install` only works from the packaged release build (the archive containing lecodes + " + "chisel + runtime/). You appear to be running from source or an npm install — install globally " + "with your package manager there instead.");
|
|
4835
|
+
}
|
|
4836
|
+
const companions = [exeName, chiselName, "runtime", "README.txt"].filter((f) => existsSync18(join20(bundle, f)));
|
|
4837
|
+
if (isWin)
|
|
4838
|
+
return installWindows(args, bundle, companions);
|
|
4839
|
+
return installUnix(args, bundle, companions);
|
|
4840
|
+
};
|
|
4841
|
+
var copyBundle = (bundle, target, companions) => {
|
|
4842
|
+
mkdirSync12(target, { recursive: true });
|
|
4843
|
+
if (safeRealpath(target) === safeRealpath(bundle))
|
|
4844
|
+
return;
|
|
4845
|
+
for (const f of companions) {
|
|
4846
|
+
const dest = join20(target, f);
|
|
4847
|
+
rmSync2(dest, { recursive: true, force: true });
|
|
4848
|
+
cpSync(join20(bundle, f), dest, { recursive: true });
|
|
4849
|
+
}
|
|
4850
|
+
};
|
|
4851
|
+
var permError = (e, dir, system) => {
|
|
4852
|
+
const code = e?.code;
|
|
4853
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
4854
|
+
return new CliError(`Permission denied writing to ${dir}.` + (system ? " Re-run with sudo: sudo lecodes install --system" : " Try without --system, or pick a writable --dir."));
|
|
4855
|
+
}
|
|
4856
|
+
return new CliError(`Install failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
4857
|
+
};
|
|
4858
|
+
var installUnix = (args, bundle, companions) => {
|
|
4859
|
+
const system = flagBool(args, "system");
|
|
4860
|
+
const libDir = resolve7(flagStr(args, "dir") ?? (system ? "/usr/local/lib/lecodes-cli" : join20(HOME, ".local", "lib", "lecodes-cli")));
|
|
4861
|
+
const binDir = resolve7(flagStr(args, "bin") ?? (system ? "/usr/local/bin" : join20(HOME, ".local", "bin")));
|
|
4862
|
+
const link = join20(binDir, "lecodes");
|
|
4863
|
+
try {
|
|
4864
|
+
copyBundle(bundle, libDir, companions);
|
|
4865
|
+
chmodSync(join20(libDir, "lecodes"), 493);
|
|
4866
|
+
chmodSync(join20(libDir, "chisel"), 493);
|
|
4867
|
+
} catch (e) {
|
|
4868
|
+
throw permError(e, libDir, system);
|
|
4869
|
+
}
|
|
4870
|
+
try {
|
|
4871
|
+
mkdirSync12(binDir, { recursive: true });
|
|
4872
|
+
rmSync2(link, { force: true });
|
|
4873
|
+
symlinkSync(join20(libDir, "lecodes"), link);
|
|
4874
|
+
} catch (e) {
|
|
4875
|
+
throw permError(e, binDir, system);
|
|
4876
|
+
}
|
|
4877
|
+
success(`Installed to ${c.bold(libDir)}`);
|
|
4878
|
+
log(` ${c.dim(link + " → " + join20(libDir, "lecodes"))}`);
|
|
4879
|
+
finishPathUnix(args, binDir);
|
|
4880
|
+
};
|
|
4881
|
+
var shellRc = () => {
|
|
4882
|
+
const shell = basename11(process.env.SHELL ?? "");
|
|
4883
|
+
if (shell === "zsh")
|
|
4884
|
+
return { file: join20(HOME, ".zshrc"), name: "~/.zshrc" };
|
|
4885
|
+
if (shell === "bash")
|
|
4886
|
+
return { file: join20(HOME, ".bashrc"), name: "~/.bashrc" };
|
|
4887
|
+
return { file: join20(HOME, ".profile"), name: "~/.profile" };
|
|
4888
|
+
};
|
|
4889
|
+
var PATH_MARKER = "# Added by `lecodes install`";
|
|
4890
|
+
var appendPathToShellRc = (binDir) => {
|
|
4891
|
+
const { file, name } = shellRc();
|
|
4892
|
+
const line = `export PATH="${binDir}:$PATH"`;
|
|
4893
|
+
try {
|
|
4894
|
+
const existing = existsSync18(file) ? readFileSync18(file, "utf8") : "";
|
|
4895
|
+
if (existing.includes(line))
|
|
4896
|
+
return name;
|
|
4897
|
+
appendFileSync2(file, `${existing && !existing.endsWith(`
|
|
4898
|
+
`) ? `
|
|
4899
|
+
` : ""}
|
|
4900
|
+
${PATH_MARKER}
|
|
4901
|
+
${line}
|
|
4902
|
+
`);
|
|
4903
|
+
return name;
|
|
4904
|
+
} catch {
|
|
4905
|
+
return null;
|
|
4906
|
+
}
|
|
4907
|
+
};
|
|
4908
|
+
var finishPathUnix = (args, binDir) => {
|
|
4909
|
+
if (onPath(binDir)) {
|
|
4910
|
+
log(` Run ${c.bold("lecodes --version")} in a new shell to verify.`);
|
|
4911
|
+
return;
|
|
4912
|
+
}
|
|
4913
|
+
if (flagBool(args, "modify-path")) {
|
|
4914
|
+
const rc = appendPathToShellRc(binDir);
|
|
4915
|
+
if (rc) {
|
|
4916
|
+
success(`Added ${c.bold(binDir)} to PATH in ${rc}.`);
|
|
4917
|
+
log(` Run ${c.bold(`source ${rc}`)} (or open a new terminal), then ${c.bold("lecodes --version")}.`);
|
|
4918
|
+
} else {
|
|
4919
|
+
warn("Couldn't edit your shell startup file. Add this line to it manually:");
|
|
4920
|
+
log(` ${c.bold(`export PATH="${binDir}:$PATH"`)}`);
|
|
4921
|
+
}
|
|
4922
|
+
return;
|
|
4923
|
+
}
|
|
4924
|
+
const { name } = shellRc();
|
|
4925
|
+
warn(`${binDir} is not on your PATH yet. To finish, either:`);
|
|
4926
|
+
log(` • this shell now: ${c.bold(`export PATH="${binDir}:$PATH"`)}`);
|
|
4927
|
+
log(` • all new shells: ${c.bold(`echo 'export PATH="${binDir}:$PATH"' >> ${name}`)} ${c.dim("(then reopen it)")}`);
|
|
4928
|
+
log(` • let install do it: re-run with ${c.bold("--modify-path")}`);
|
|
4929
|
+
log(` • or install system-wide (no PATH edit): ${c.bold("sudo lecodes install --system")}`);
|
|
4930
|
+
};
|
|
4931
|
+
var installWindows = (args, bundle, companions) => {
|
|
4932
|
+
const target = resolve7(flagStr(args, "dir") ?? join20(process.env.LOCALAPPDATA ?? join20(HOME, "AppData", "Local"), "Programs", "lecodes-cli"));
|
|
4933
|
+
try {
|
|
4934
|
+
copyBundle(bundle, target, companions);
|
|
4935
|
+
} catch (e) {
|
|
4936
|
+
throw new CliError(`Install failed writing to ${target}: ${e instanceof Error ? e.message : String(e)}`);
|
|
4937
|
+
}
|
|
4938
|
+
success(`Installed to ${c.bold(target)}`);
|
|
4939
|
+
if (onPath(target)) {
|
|
4940
|
+
log(` Already on PATH — run ${c.bold("lecodes --version")} in a new terminal.`);
|
|
4941
|
+
return;
|
|
4942
|
+
}
|
|
4943
|
+
if (flagBool(args, "modify-path")) {
|
|
4944
|
+
if (addToUserPathWindows(target)) {
|
|
4945
|
+
success(`Added ${c.bold(target)} to your user PATH.`);
|
|
4946
|
+
log(` ${c.yellow("Open a NEW terminal")} for it to take effect, then run ${c.bold("lecodes --version")}.`);
|
|
4947
|
+
} else {
|
|
4948
|
+
warn("Couldn't edit PATH automatically. Add this folder to your PATH manually:");
|
|
4949
|
+
log(` ${c.bold(target)}`);
|
|
4950
|
+
}
|
|
4951
|
+
return;
|
|
4952
|
+
}
|
|
4953
|
+
warn(`${target} is not on your PATH yet. To finish, either:`);
|
|
4954
|
+
log(` • let install do it: re-run with ${c.bold("--modify-path")} (adds it to your user PATH)`);
|
|
4955
|
+
log(` • or add the folder manually via System → Environment Variables.`);
|
|
4956
|
+
};
|
|
4957
|
+
var addToUserPathWindows = (dir) => {
|
|
4958
|
+
const script = "$d = $env:LECODES_INSTALL_DIR;" + "$p = [Environment]::GetEnvironmentVariable('Path','User'); if (-not $p) { $p = '' };" + "$parts = @($p.Split(';') | Where-Object { $_ -ne '' });" + "if ($parts -notcontains $d) { [Environment]::SetEnvironmentVariable('Path', (@($parts + $d) -join ';'), 'User') }";
|
|
4959
|
+
try {
|
|
4960
|
+
execFileSync("powershell", ["-NoProfile", "-NonInteractive", "-Command", script], { stdio: "ignore", env: { ...process.env, LECODES_INSTALL_DIR: dir } });
|
|
4961
|
+
return true;
|
|
4962
|
+
} catch {
|
|
4963
|
+
return false;
|
|
4964
|
+
}
|
|
4965
|
+
};
|
|
4966
|
+
|
|
4967
|
+
// src/index.ts
|
|
4968
|
+
var STANDALONE = (() => {
|
|
4969
|
+
const dir = dirname12(process.execPath);
|
|
4970
|
+
const chisel = process.platform === "win32" ? "chisel.exe" : "chisel";
|
|
4971
|
+
return existsSync19(join21(dir, "runtime")) && existsSync19(join21(dir, chisel));
|
|
4972
|
+
})();
|
|
4973
|
+
var INSTALLED = STANDALONE && (() => {
|
|
4974
|
+
const exe = process.platform === "win32" ? "lecodes.exe" : "lecodes";
|
|
4975
|
+
const self = (() => {
|
|
4976
|
+
try {
|
|
4977
|
+
return realpathSync2(process.execPath);
|
|
4978
|
+
} catch {
|
|
4979
|
+
return process.execPath;
|
|
4980
|
+
}
|
|
4981
|
+
})();
|
|
4982
|
+
return (process.env.PATH ?? "").split(delimiter2).some((d) => {
|
|
4983
|
+
if (!d)
|
|
4984
|
+
return false;
|
|
4985
|
+
try {
|
|
4986
|
+
const cand = join21(d, exe);
|
|
4987
|
+
return existsSync19(cand) && realpathSync2(cand) === self;
|
|
4988
|
+
} catch {
|
|
4989
|
+
return false;
|
|
4990
|
+
}
|
|
4991
|
+
});
|
|
4992
|
+
})();
|
|
4993
|
+
var INSTALL_HELP = STANDALONE && !INSTALLED ? `
|
|
4994
|
+
install Install this standalone build so 'lecodes' is on your PATH
|
|
4995
|
+
(copies the bundle to a library folder + a symlink/PATH entry).
|
|
4996
|
+
--modify-path also add it to PATH (edits your shell rc / user PATH)
|
|
4997
|
+
--system install for all users (Linux/macOS; needs sudo)
|
|
4998
|
+
--dir <path> override the install/library folder
|
|
4999
|
+
--bin <path> override the symlink folder (Linux/macOS)` : "";
|
|
4522
5000
|
var HELP = `${c.bold("lecodes")} — clone, edit and push LeCodes projects from your machine.
|
|
4523
5001
|
|
|
4524
5002
|
${c.bold("Usage:")} lecodes <command> [options]
|
|
@@ -4526,7 +5004,7 @@ ${c.bold("Usage:")} lecodes <command> [options]
|
|
|
4526
5004
|
${c.bold("Commands:")}
|
|
4527
5005
|
init [dir] Scaffold a new LOCAL project (no login/server): a starter
|
|
4528
5006
|
main.ts, IDE types + tsconfig, and a .lecodesignore.
|
|
4529
|
-
Run it with 'lecodes dev' or 'lecodes preview'
|
|
5007
|
+
Run it with 'lecodes dev' or 'lecodes preview'.${INSTALL_HELP}
|
|
4530
5008
|
login Authorize in the browser and store an access token
|
|
4531
5009
|
--token <pat> store a token from the web UI instead
|
|
4532
5010
|
--api <url> API origin (default https://le.codes)
|
|
@@ -4584,9 +5062,17 @@ ${c.bold("Commands:")}
|
|
|
4584
5062
|
--png also rasterize PNGs (needs @napi-rs/canvas)
|
|
4585
5063
|
--out-dir <dir> output folder override
|
|
4586
5064
|
--logs forward the screens' console output (to stderr)
|
|
5065
|
+
scene [path] LeCodes Scene Editor — visually edit a .scene.ts (tree, inspector,
|
|
5066
|
+
gizmo) on local files (needs the optional 'lecodes-3d-editor' package;
|
|
5067
|
+
works inside a cloned project or standalone in any folder)
|
|
5068
|
+
--port <n> editor-server port (default 4488)
|
|
5069
|
+
--no-open don't open the browser
|
|
5070
|
+
--no-mcp don't register the MCP server in .mcp.json
|
|
4587
5071
|
|
|
4588
5072
|
Config lives in ~/.lecodes/config.json (override with LECODES_API / LECODES_TOKEN).`;
|
|
4589
|
-
var commands = { init: init2, login, create, clone, status, diff, pull, push, preview, render, design, dev };
|
|
5073
|
+
var commands = { init: init2, login, create, clone, status, diff, pull, push, preview, render, design, scene, dev };
|
|
5074
|
+
if (STANDALONE)
|
|
5075
|
+
commands.install = install;
|
|
4590
5076
|
var readVersion = () => package_default.version ?? "unknown";
|
|
4591
5077
|
var main = async () => {
|
|
4592
5078
|
const argv = process.argv.slice(2);
|