lecodes-cli 0.7.1 → 0.7.2
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 +3 -3
- package/dist/index.js +255 -243
- package/package.json +3 -3
- package/runtime/sdk/compile/bundler.ts +4 -2
- package/runtime/sdk/compile/compileProject.ts +6 -1
- package/runtime/sdk/inject.ts +1 -1
- package/runtime/sdk/ui/UINode.ts +13 -5
- package/runtime/sdk-types.json +1 -1
package/dist/index.js
CHANGED
|
@@ -109,12 +109,12 @@ var hasImportStatement = (src) => src.includes("import") || src.includes("from")
|
|
|
109
109
|
if (isRelativeOrAbsolute(spec))
|
|
110
110
|
return spec;
|
|
111
111
|
const slash = spec.indexOf("/");
|
|
112
|
-
const
|
|
113
|
-
if (slugs.has(
|
|
112
|
+
const slug2 = slash === -1 ? spec : spec.slice(0, slash);
|
|
113
|
+
if (slugs.has(slug2)) {
|
|
114
114
|
const sub = slash === -1 ? "/index" : spec.slice(slash);
|
|
115
|
-
return `/__lib/${
|
|
115
|
+
return `/__lib/${slug2}${sub}`;
|
|
116
116
|
}
|
|
117
|
-
throw new Error(`Unknown library "${
|
|
117
|
+
throw new Error(`Unknown library "${slug2}" — add it in Settings → Libraries, or use a relative path like "./${slug2}".`);
|
|
118
118
|
};
|
|
119
119
|
let out = src.replace(IMPORT_FROM_RE, (_m, boundary, head, quote, spec) => `${boundary}${head}${quote}${rewriteSpec(spec)}${quote}`);
|
|
120
120
|
out = out.replace(SIDE_EFFECT_RE, (m, boundary, head, quote, spec) => isRelativeOrAbsolute(spec) ? m : `${boundary}${head}${quote}${rewriteSpec(spec)}${quote}`);
|
|
@@ -126,18 +126,18 @@ var init_libraryImports = __esm(() => {
|
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
// ../sdk/src/compile/bundler.ts
|
|
129
|
-
import { existsSync as
|
|
129
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync } from "node:fs";
|
|
130
130
|
import { spawnSync } from "node:child_process";
|
|
131
131
|
import { createRequire as createRequire2 } from "node:module";
|
|
132
|
-
import { dirname as dirname5, join as
|
|
132
|
+
import { dirname as dirname5, join as join8, relative as relative2 } from "node:path";
|
|
133
133
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
134
134
|
var resolveInject = () => {
|
|
135
135
|
const override = process.env.LECODES_SDK_INJECT;
|
|
136
|
-
if (override &&
|
|
136
|
+
if (override && existsSync6(override))
|
|
137
137
|
return override;
|
|
138
138
|
const here = dirname5(fileURLToPath2(import.meta.url));
|
|
139
|
-
const src =
|
|
140
|
-
if (
|
|
139
|
+
const src = join8(here, "..", "inject.ts");
|
|
140
|
+
if (existsSync6(src))
|
|
141
141
|
return src;
|
|
142
142
|
throw new Error("sdk: could not locate the inject source (LECODES_SDK_INJECT / src/inject.ts)");
|
|
143
143
|
}, normalize = (path) => {
|
|
@@ -163,12 +163,12 @@ var resolveInject = () => {
|
|
|
163
163
|
return cached;
|
|
164
164
|
const out = {};
|
|
165
165
|
const walk = (dir) => {
|
|
166
|
-
for (const name of
|
|
167
|
-
const p =
|
|
166
|
+
for (const name of readdirSync3(dir)) {
|
|
167
|
+
const p = join8(dir, name);
|
|
168
168
|
if (statSync(p).isDirectory())
|
|
169
169
|
walk(p);
|
|
170
170
|
else if (isSdkSource(p))
|
|
171
|
-
out["/__sdk/" + relative2(sdkSrc, p).split("\\").join("/")] =
|
|
171
|
+
out["/__sdk/" + relative2(sdkSrc, p).split("\\").join("/")] = readFileSync8(p, "utf8");
|
|
172
172
|
}
|
|
173
173
|
};
|
|
174
174
|
walk(sdkSrc);
|
|
@@ -177,13 +177,13 @@ var resolveInject = () => {
|
|
|
177
177
|
}, resolveChiselBin = () => {
|
|
178
178
|
const override = process.env.LECODES_CHISEL_BIN;
|
|
179
179
|
if (override) {
|
|
180
|
-
if (
|
|
180
|
+
if (existsSync6(override))
|
|
181
181
|
return override;
|
|
182
182
|
throw new Error(`LECODES_CHISEL_BIN points at a missing file: ${override}`);
|
|
183
183
|
}
|
|
184
184
|
try {
|
|
185
185
|
const bin = createRequire2(import.meta.url)("@letary/chisel").binaryPath();
|
|
186
|
-
if (bin &&
|
|
186
|
+
if (bin && existsSync6(bin))
|
|
187
187
|
return bin;
|
|
188
188
|
} catch {}
|
|
189
189
|
throw new Error("chisel binary not found. Install @letary/chisel, or set LECODES_CHISEL_BIN.");
|
|
@@ -206,9 +206,9 @@ var resolveInject = () => {
|
|
|
206
206
|
}
|
|
207
207
|
if (!projFiles[entry])
|
|
208
208
|
throw new Error(`Entrypoint not found: ${entry}`);
|
|
209
|
-
for (const [
|
|
209
|
+
for (const [slug2, libFiles] of Object.entries(libraries ?? {})) {
|
|
210
210
|
for (const [k, v] of Object.entries(libFiles)) {
|
|
211
|
-
const path = normalize(`/__lib/${
|
|
211
|
+
const path = normalize(`/__lib/${slug2}/${k}`);
|
|
212
212
|
let src = v;
|
|
213
213
|
if (/\.[tj]sx?$/.test(path)) {
|
|
214
214
|
if (hasAssetMacro(src))
|
|
@@ -249,7 +249,7 @@ var resolveInject = () => {
|
|
|
249
249
|
if (parsed.code == null)
|
|
250
250
|
throw new Error("chisel produced no code");
|
|
251
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,
|
|
252
|
+
}, bundleProject = async (entrypoint, files, format = "esm") => (await bundleProjectChisel(entrypoint, files, format, false)).code, bundleProjectWithMap = async (entrypoint, files, format = "esm", minify, editor, libraries, sourcemap = true) => bundleProjectChisel(entrypoint, files, format, sourcemap, minify, editor, libraries);
|
|
253
253
|
var init_bundler = __esm(() => {
|
|
254
254
|
init_assetMacro();
|
|
255
255
|
init_aspectMacro();
|
|
@@ -360,7 +360,7 @@ var detectEntry = (entries) => {
|
|
|
360
360
|
files[e.path] = e.text ?? "";
|
|
361
361
|
}
|
|
362
362
|
}
|
|
363
|
-
const { code, map } = await bundleProjectWithMap(entry, files, opts.format ?? "esm", opts.minify, opts.editor, opts.libraries);
|
|
363
|
+
const { code, map } = await bundleProjectWithMap(entry, files, opts.format ?? "esm", opts.minify, opts.editor, opts.libraries, opts.sourcemap ?? true);
|
|
364
364
|
if (opts.header === false) {
|
|
365
365
|
return map ? `${code}
|
|
366
366
|
${inlineSourceMapComment(map)}` : code;
|
|
@@ -377,17 +377,17 @@ var init_compileProject = __esm(() => {
|
|
|
377
377
|
});
|
|
378
378
|
|
|
379
379
|
// ../sdk/src/compile/sceneEditor.ts
|
|
380
|
-
import { existsSync as
|
|
381
|
-
import { join as
|
|
380
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "node:fs";
|
|
381
|
+
import { join as join9 } from "node:path";
|
|
382
382
|
var SCENE_HARNESS_DIR = "./.scene-editor", SCENE_ENTRY_PATH = "./.scene-entry.ts", loadSceneHarness = (fallbackDir) => {
|
|
383
383
|
const override = process.env.LECODES_SCENE_HARNESS_DIR;
|
|
384
|
-
const dir = override &&
|
|
384
|
+
const dir = override && existsSync7(override) ? override : fallbackDir && existsSync7(fallbackDir) ? fallbackDir : null;
|
|
385
385
|
if (!dir)
|
|
386
386
|
throw new Error("scene-editor harness sources not found (set LECODES_SCENE_HARNESS_DIR)");
|
|
387
387
|
const out = {};
|
|
388
|
-
for (const f of
|
|
388
|
+
for (const f of readdirSync4(dir)) {
|
|
389
389
|
if (f.endsWith(".ts"))
|
|
390
|
-
out[f] =
|
|
390
|
+
out[f] = readFileSync9(join9(dir, f), "utf8");
|
|
391
391
|
}
|
|
392
392
|
return out;
|
|
393
393
|
}, sceneEditorEntries = (scenePath, harness, projectPaths = []) => {
|
|
@@ -1457,7 +1457,7 @@ var require_main = __commonJS((exports, module) => {
|
|
|
1457
1457
|
// package.json
|
|
1458
1458
|
var package_default = {
|
|
1459
1459
|
name: "lecodes-cli",
|
|
1460
|
-
version: "0.7.
|
|
1460
|
+
version: "0.7.2",
|
|
1461
1461
|
description: "Clone, edit and push LeCodes projects from your machine.",
|
|
1462
1462
|
type: "module",
|
|
1463
1463
|
license: "MIT",
|
|
@@ -1490,8 +1490,8 @@ var package_default = {
|
|
|
1490
1490
|
"@letary/chisel": "^0.4.1"
|
|
1491
1491
|
},
|
|
1492
1492
|
peerDependencies: {
|
|
1493
|
-
"lecodes-design": "^0.3.
|
|
1494
|
-
"lecodes-renderer": "^0.6.
|
|
1493
|
+
"lecodes-design": "^0.3.2",
|
|
1494
|
+
"lecodes-renderer": "^0.6.2",
|
|
1495
1495
|
"lecodes-3d-editor": "^0.1.0"
|
|
1496
1496
|
},
|
|
1497
1497
|
peerDependenciesMeta: {
|
|
@@ -1994,55 +1994,17 @@ var clone = async (args) => {
|
|
|
1994
1994
|
info(c.dim(` cd ${dir} — edit, then \`lecodes push -m "your message"\``));
|
|
1995
1995
|
};
|
|
1996
1996
|
|
|
1997
|
-
// src/commands/
|
|
1998
|
-
import {
|
|
1999
|
-
import {
|
|
2000
|
-
var slug2 = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
2001
|
-
var STARTER = `// Welcome to le.codes — no imports needed, the whole SDK is available as globals.
|
|
2002
|
-
const scene = new Scene2D({ background: '#10131a' })
|
|
2003
|
-
scene.open()
|
|
1997
|
+
// src/commands/compile.ts
|
|
1998
|
+
import { writeFileSync as writeFileSync6 } from "node:fs";
|
|
1999
|
+
import { basename as basename3, resolve as resolve2 } from "node:path";
|
|
2004
2000
|
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
})
|
|
2008
|
-
`;
|
|
2009
|
-
var create = async (args) => {
|
|
2010
|
-
const config = loadConfig();
|
|
2011
|
-
const apiUrl = requireApiUrl(config);
|
|
2012
|
-
const token = requireToken(config);
|
|
2013
|
-
const name = args._[0];
|
|
2014
|
-
if (!name)
|
|
2015
|
-
throw new CliError("Usage: lecodes create <name> [directory]");
|
|
2016
|
-
const dir = args._[1] ?? slug2(name);
|
|
2017
|
-
const root = resolve2(dir);
|
|
2018
|
-
if (existsSync6(root) && readdirSync2(root).length > 0) {
|
|
2019
|
-
throw new CliError(`Directory "${dir}" already exists and is not empty.`);
|
|
2020
|
-
}
|
|
2021
|
-
mkdirSync6(root, { recursive: true });
|
|
2022
|
-
info(c.dim(`Creating project ${c.bold(name)}…`));
|
|
2023
|
-
const { uuid } = await createProject(apiUrl, token, name, flagStr(args, "team"));
|
|
2024
|
-
info(c.dim("Fetching type definitions…"));
|
|
2025
|
-
await materializeTypes(root, apiUrl);
|
|
2026
|
-
writeTsconfig(root);
|
|
2027
|
-
writeDefaultIgnore(root);
|
|
2028
|
-
writeFileSync6(join7(root, "main.ts"), STARTER);
|
|
2029
|
-
const commits = await getCommits(apiUrl, token, uuid);
|
|
2030
|
-
writeManifest(root, { uuid, apiUrl, name, baseHeadId: commits.headId, files: {} });
|
|
2031
|
-
success(`Created ${c.bold(name)} in ${c.bold(dir)}/`);
|
|
2032
|
-
info(c.dim(` cd ${dir} — edit main.ts, then \`lecodes push -m "first commit"\` to save it`));
|
|
2033
|
-
};
|
|
2034
|
-
|
|
2035
|
-
// src/commands/design.ts
|
|
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";
|
|
2038
|
-
|
|
2039
|
-
// src/compile/designCompile.ts
|
|
2040
|
-
import { readFileSync as readFileSync9 } from "node:fs";
|
|
2001
|
+
// src/compile/collect.ts
|
|
2002
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
2041
2003
|
import { basename } from "node:path";
|
|
2042
2004
|
|
|
2043
2005
|
// src/localFiles.ts
|
|
2044
|
-
import { readdirSync as
|
|
2045
|
-
import { join as
|
|
2006
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "node:fs";
|
|
2007
|
+
import { join as join7, relative } from "node:path";
|
|
2046
2008
|
var SKIP_DIRS = new Set([".lecodes", ".git", "node_modules"]);
|
|
2047
2009
|
var SKIP_FILES = new Set(["tsconfig.json", "jsconfig.json", ".gitignore", ".lecodesignore", ".DS_Store"]);
|
|
2048
2010
|
var TEXT_EXTS = new Set([
|
|
@@ -2095,8 +2057,8 @@ var scanLocal = (root, opts = {}) => {
|
|
|
2095
2057
|
return false;
|
|
2096
2058
|
};
|
|
2097
2059
|
const walk = (dir, parentIgnored) => {
|
|
2098
|
-
for (const entry of
|
|
2099
|
-
const abs =
|
|
2060
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
2061
|
+
const abs = join7(dir, entry.name);
|
|
2100
2062
|
const key = toKey(relative(root, abs));
|
|
2101
2063
|
if (entry.isDirectory()) {
|
|
2102
2064
|
if (SKIP_DIRS.has(entry.name))
|
|
@@ -2149,13 +2111,64 @@ var diffLocal = (manifest, scan) => {
|
|
|
2149
2111
|
};
|
|
2150
2112
|
var hasChanges = (diff) => diff.added.length + diff.modified.length + diff.deleted.length > 0;
|
|
2151
2113
|
|
|
2114
|
+
// src/compile/collect.ts
|
|
2115
|
+
var collectEntries = (root, manifest, project) => {
|
|
2116
|
+
const pathById = buildPaths(project.assets);
|
|
2117
|
+
const serverByPath = new Map;
|
|
2118
|
+
for (const asset of project.assets) {
|
|
2119
|
+
if (asset.type === "folder")
|
|
2120
|
+
continue;
|
|
2121
|
+
serverByPath.set(pathById.get(asset.id), { fileSrc: asset.file?.src ?? undefined, shaders: asset.shaders ?? [] });
|
|
2122
|
+
}
|
|
2123
|
+
const entries = [];
|
|
2124
|
+
const warnings = [];
|
|
2125
|
+
for (const file of scanLocal(root).files) {
|
|
2126
|
+
const type = manifest.files[file.path]?.type ?? classifyKind(basename(file.path));
|
|
2127
|
+
if (type === "resource") {
|
|
2128
|
+
const src = serverByPath.get(file.path)?.fileSrc;
|
|
2129
|
+
if (!src)
|
|
2130
|
+
warnings.push(`No server URL for ${file.path} — push it so its URL resolves.`);
|
|
2131
|
+
entries.push({ path: file.path, type, fileSrc: src, absPath: file.absPath });
|
|
2132
|
+
} else if (type === "shader") {
|
|
2133
|
+
const shaders = serverByPath.get(file.path)?.shaders;
|
|
2134
|
+
if (!shaders?.length)
|
|
2135
|
+
warnings.push(`No compiled shader for ${file.path} — push it to compile the shader.`);
|
|
2136
|
+
entries.push({ path: file.path, type, shaders });
|
|
2137
|
+
} else {
|
|
2138
|
+
entries.push({ path: file.path, type, text: readFileSync6(file.absPath, "utf8") });
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
return { entries, warnings };
|
|
2142
|
+
};
|
|
2143
|
+
|
|
2144
|
+
// src/compile/collectLocal.ts
|
|
2145
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
2146
|
+
import { basename as basename2 } from "node:path";
|
|
2147
|
+
var collectLocalEntries = (root, manifest, assetBase) => {
|
|
2148
|
+
const entries = [];
|
|
2149
|
+
const warnings = [];
|
|
2150
|
+
const scan = scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest?.files ?? {})) });
|
|
2151
|
+
for (const file of scan.files) {
|
|
2152
|
+
const type = manifest?.files[file.path]?.type ?? classifyKind(basename2(file.path));
|
|
2153
|
+
if (type === "resource") {
|
|
2154
|
+
entries.push({ path: file.path, type, fileSrc: assetBase + encodeURI(file.path), absPath: file.absPath });
|
|
2155
|
+
} else if (type === "shader") {
|
|
2156
|
+
warnings.push(`${file.path}: shaders compile on the server — push the project once for shaders to work in dev mode.`);
|
|
2157
|
+
entries.push({ path: file.path, type, shaders: [] });
|
|
2158
|
+
} else {
|
|
2159
|
+
entries.push({ path: file.path, type, text: readFileSync7(file.absPath, "utf8") });
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
return { entries, warnings };
|
|
2163
|
+
};
|
|
2164
|
+
|
|
2152
2165
|
// src/compile/projectCompile.ts
|
|
2153
|
-
import { existsSync as
|
|
2166
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
2154
2167
|
import { createRequire as createRequire3 } from "node:module";
|
|
2155
|
-
import { dirname as dirname6, join as
|
|
2168
|
+
import { dirname as dirname6, join as join10 } from "node:path";
|
|
2156
2169
|
init_compile();
|
|
2157
|
-
var vendoredInject =
|
|
2158
|
-
if (
|
|
2170
|
+
var vendoredInject = join10(distRoot, "runtime", "sdk", "inject.ts");
|
|
2171
|
+
if (existsSync8(vendoredInject))
|
|
2159
2172
|
process.env.LECODES_SDK_INJECT = vendoredInject;
|
|
2160
2173
|
var resolveChiselBin2 = () => {
|
|
2161
2174
|
if (process.env.LECODES_CHISEL_BIN)
|
|
@@ -2165,8 +2178,8 @@ var resolveChiselBin2 = () => {
|
|
|
2165
2178
|
if (fromPkg)
|
|
2166
2179
|
return fromPkg;
|
|
2167
2180
|
} catch {}
|
|
2168
|
-
const local =
|
|
2169
|
-
return
|
|
2181
|
+
const local = join10(dirname6(process.execPath), process.platform === "win32" ? "chisel.exe" : "chisel");
|
|
2182
|
+
return existsSync8(local) ? local : null;
|
|
2170
2183
|
};
|
|
2171
2184
|
var chiselBin = resolveChiselBin2();
|
|
2172
2185
|
if (chiselBin)
|
|
@@ -2180,12 +2193,123 @@ var compileProject2 = (opts) => compileProject({
|
|
|
2180
2193
|
format: opts.format,
|
|
2181
2194
|
localAssets: opts.localAssets,
|
|
2182
2195
|
minify: opts.minify,
|
|
2196
|
+
sourcemap: opts.sourcemap,
|
|
2183
2197
|
editor: opts.editor
|
|
2184
2198
|
});
|
|
2185
2199
|
|
|
2200
|
+
// src/commands/compile.ts
|
|
2201
|
+
var compile2 = async (args) => {
|
|
2202
|
+
let root;
|
|
2203
|
+
let manifest;
|
|
2204
|
+
try {
|
|
2205
|
+
root = findProjectRoot(process.cwd());
|
|
2206
|
+
manifest = readManifest(root);
|
|
2207
|
+
} catch {
|
|
2208
|
+
root = process.cwd();
|
|
2209
|
+
manifest = null;
|
|
2210
|
+
}
|
|
2211
|
+
let entries;
|
|
2212
|
+
let warnings;
|
|
2213
|
+
let name;
|
|
2214
|
+
let publicUrl;
|
|
2215
|
+
let localAssets = false;
|
|
2216
|
+
if (manifest) {
|
|
2217
|
+
const config = loadConfig();
|
|
2218
|
+
const apiUrl = requireApiUrl(config);
|
|
2219
|
+
const token = requireToken(config);
|
|
2220
|
+
note("Resolving project assets…");
|
|
2221
|
+
const project = await getProject(apiUrl, token, manifest.uuid);
|
|
2222
|
+
({ entries, warnings } = collectEntries(root, manifest, project));
|
|
2223
|
+
name = manifest.name;
|
|
2224
|
+
publicUrl = flagStr(args, "public-url") ?? apiUrl;
|
|
2225
|
+
} else {
|
|
2226
|
+
const publicUrlFlag = flagStr(args, "public-url");
|
|
2227
|
+
localAssets = publicUrlFlag === undefined;
|
|
2228
|
+
publicUrl = publicUrlFlag ?? "";
|
|
2229
|
+
({ entries, warnings } = collectLocalEntries(root, null, publicUrl));
|
|
2230
|
+
name = basename3(root);
|
|
2231
|
+
}
|
|
2232
|
+
note("Compiling…");
|
|
2233
|
+
const js = await compileProject2({
|
|
2234
|
+
entries,
|
|
2235
|
+
name,
|
|
2236
|
+
publicUrl,
|
|
2237
|
+
entryOverride: flagStr(args, "entry"),
|
|
2238
|
+
header: !flagBool(args, "no-header"),
|
|
2239
|
+
localAssets,
|
|
2240
|
+
minify: flagBool(args, "no-minify") ? false : undefined,
|
|
2241
|
+
sourcemap: flagBool(args, "no-sourcemap", "no-source-map") ? false : undefined
|
|
2242
|
+
});
|
|
2243
|
+
for (const w of warnings)
|
|
2244
|
+
warnErr(w);
|
|
2245
|
+
const out = flagStr(args, "o", "out");
|
|
2246
|
+
if (out && isNullSink(out)) {
|
|
2247
|
+
note(`Discarded output (${out})`);
|
|
2248
|
+
} else if (out) {
|
|
2249
|
+
writeFileSync6(resolve2(out), js);
|
|
2250
|
+
note(`Wrote ${out} (${js.length} bytes)`);
|
|
2251
|
+
} else {
|
|
2252
|
+
process.stdout.write(js);
|
|
2253
|
+
if (!js.endsWith(`
|
|
2254
|
+
`))
|
|
2255
|
+
process.stdout.write(`
|
|
2256
|
+
`);
|
|
2257
|
+
}
|
|
2258
|
+
};
|
|
2259
|
+
var previewLegacy = async (args) => {
|
|
2260
|
+
warnErr("'lecodes preview' is now 'lecodes compile' — the old name still works.");
|
|
2261
|
+
await compile2(args);
|
|
2262
|
+
};
|
|
2263
|
+
|
|
2264
|
+
// src/commands/create.ts
|
|
2265
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync6, readdirSync as readdirSync5, writeFileSync as writeFileSync7 } from "node:fs";
|
|
2266
|
+
import { join as join11, resolve as resolve3 } from "node:path";
|
|
2267
|
+
var slug2 = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
2268
|
+
var STARTER = `// Welcome to le.codes — no imports needed, the whole SDK is available as globals.
|
|
2269
|
+
const scene = new Scene2D({ background: '#10131a' })
|
|
2270
|
+
scene.open()
|
|
2271
|
+
|
|
2272
|
+
setLoop((dt) => {
|
|
2273
|
+
// your per-frame logic — dt is seconds since the previous frame
|
|
2274
|
+
})
|
|
2275
|
+
`;
|
|
2276
|
+
var create = async (args) => {
|
|
2277
|
+
const config = loadConfig();
|
|
2278
|
+
const apiUrl = requireApiUrl(config);
|
|
2279
|
+
const token = requireToken(config);
|
|
2280
|
+
const name = args._[0];
|
|
2281
|
+
if (!name)
|
|
2282
|
+
throw new CliError("Usage: lecodes create <name> [directory]");
|
|
2283
|
+
const dir = args._[1] ?? slug2(name);
|
|
2284
|
+
const root = resolve3(dir);
|
|
2285
|
+
if (existsSync9(root) && readdirSync5(root).length > 0) {
|
|
2286
|
+
throw new CliError(`Directory "${dir}" already exists and is not empty.`);
|
|
2287
|
+
}
|
|
2288
|
+
mkdirSync6(root, { recursive: true });
|
|
2289
|
+
info(c.dim(`Creating project ${c.bold(name)}…`));
|
|
2290
|
+
const { uuid } = await createProject(apiUrl, token, name, flagStr(args, "team"));
|
|
2291
|
+
info(c.dim("Fetching type definitions…"));
|
|
2292
|
+
await materializeTypes(root, apiUrl);
|
|
2293
|
+
writeTsconfig(root);
|
|
2294
|
+
writeDefaultIgnore(root);
|
|
2295
|
+
writeFileSync7(join11(root, "main.ts"), STARTER);
|
|
2296
|
+
const commits = await getCommits(apiUrl, token, uuid);
|
|
2297
|
+
writeManifest(root, { uuid, apiUrl, name, baseHeadId: commits.headId, files: {} });
|
|
2298
|
+
success(`Created ${c.bold(name)} in ${c.bold(dir)}/`);
|
|
2299
|
+
info(c.dim(` cd ${dir} — edit main.ts, then \`lecodes push -m "first commit"\` to save it`));
|
|
2300
|
+
};
|
|
2301
|
+
|
|
2302
|
+
// src/commands/design.ts
|
|
2303
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync12, readdirSync as readdirSync6, writeFileSync as writeFileSync9, appendFileSync } from "node:fs";
|
|
2304
|
+
import { basename as basename5, join as join13 } from "node:path";
|
|
2305
|
+
|
|
2306
|
+
// src/compile/designCompile.ts
|
|
2307
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
2308
|
+
import { basename as basename4 } from "node:path";
|
|
2309
|
+
|
|
2186
2310
|
// src/compile/assetIcons.ts
|
|
2187
2311
|
import { createHash as createHash2 } from "node:crypto";
|
|
2188
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as
|
|
2312
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "node:fs";
|
|
2189
2313
|
import { dirname as dirname7, join as join12 } from "node:path";
|
|
2190
2314
|
var DEFAULT_REGISTRY = process.env.LECODES_ICON_REGISTRY?.trim() || "https://icon-registry.jt3.ru/v1";
|
|
2191
2315
|
var ICON_ID = /^([a-z0-9]+(?:-[a-z0-9]+)*):([a-z0-9]+(?:-[a-z0-9]+)*)$/;
|
|
@@ -2438,14 +2562,14 @@ var readText = async (registry, rel) => {
|
|
|
2438
2562
|
return res.ok ? await res.text() : null;
|
|
2439
2563
|
}
|
|
2440
2564
|
try {
|
|
2441
|
-
return
|
|
2565
|
+
return readFileSync10(join12(registry, rel), "utf8");
|
|
2442
2566
|
} catch {
|
|
2443
2567
|
return null;
|
|
2444
2568
|
}
|
|
2445
2569
|
};
|
|
2446
2570
|
var readLock = (path) => {
|
|
2447
2571
|
try {
|
|
2448
|
-
const lock = JSON.parse(
|
|
2572
|
+
const lock = JSON.parse(readFileSync10(path, "utf8"));
|
|
2449
2573
|
if (lock && typeof lock === "object" && lock.icons && lock.packs)
|
|
2450
2574
|
return lock;
|
|
2451
2575
|
} catch {}
|
|
@@ -2485,12 +2609,12 @@ var writeIfChanged = (path, text) => {
|
|
|
2485
2609
|
const current = existsSync10(path) ? safeRead(path) : null;
|
|
2486
2610
|
if (current === text)
|
|
2487
2611
|
return false;
|
|
2488
|
-
|
|
2612
|
+
writeFileSync8(path, text);
|
|
2489
2613
|
return true;
|
|
2490
2614
|
};
|
|
2491
2615
|
var safeRead = (path) => {
|
|
2492
2616
|
try {
|
|
2493
|
-
return
|
|
2617
|
+
return readFileSync10(path, "utf8");
|
|
2494
2618
|
} catch {
|
|
2495
2619
|
return null;
|
|
2496
2620
|
}
|
|
@@ -2526,7 +2650,7 @@ var vendorIcons = async (designDir, ids, opts = {}) => {
|
|
|
2526
2650
|
const rel = `assets/icons/${pack}/${name}.svg`;
|
|
2527
2651
|
const abs = join12(designDir, rel);
|
|
2528
2652
|
mkdirSync7(dirname7(abs), { recursive: true });
|
|
2529
|
-
|
|
2653
|
+
writeFileSync8(abs, svg);
|
|
2530
2654
|
lock.icons[id] = { pack, name, hash: sha12(svg), spdx: lic.spdx, path: rel };
|
|
2531
2655
|
lock.packs[pack] = { spdx: lic.spdx, title: lic.title, url: lic.url, attribution: lic.attribution, modifiedNotice: lic.modifiedNotice };
|
|
2532
2656
|
svgById.set(id, svg);
|
|
@@ -2559,7 +2683,7 @@ var compileDesignScreen = async (opts) => {
|
|
|
2559
2683
|
const entries = [];
|
|
2560
2684
|
const scan = scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest?.files ?? {})) });
|
|
2561
2685
|
for (const file of scan.files) {
|
|
2562
|
-
const type = manifest?.files[file.path]?.type ?? classifyKind(
|
|
2686
|
+
const type = manifest?.files[file.path]?.type ?? classifyKind(basename4(file.path));
|
|
2563
2687
|
if (type === "resource") {
|
|
2564
2688
|
entries.push({
|
|
2565
2689
|
path: file.path,
|
|
@@ -2570,7 +2694,7 @@ var compileDesignScreen = async (opts) => {
|
|
|
2570
2694
|
} else if (type === "shader") {
|
|
2571
2695
|
entries.push({ path: file.path, type, shaders: [] });
|
|
2572
2696
|
} else {
|
|
2573
|
-
entries.push({ path: file.path, type, text:
|
|
2697
|
+
entries.push({ path: file.path, type, text: readFileSync11(file.absPath, "utf8") });
|
|
2574
2698
|
}
|
|
2575
2699
|
}
|
|
2576
2700
|
const invoke = state !== undefined ? `screen(${JSON.stringify(state)})` : "screen()";
|
|
@@ -2630,10 +2754,10 @@ var openBrowser = (url) => {
|
|
|
2630
2754
|
};
|
|
2631
2755
|
var browserLogin = async (webBase, timeoutMs = 5 * 60 * 1000, open = openBrowser) => {
|
|
2632
2756
|
const state = randomBytes(16).toString("hex");
|
|
2633
|
-
let
|
|
2757
|
+
let resolve4;
|
|
2634
2758
|
let reject;
|
|
2635
2759
|
const tokenPromise = new Promise((res, rej) => {
|
|
2636
|
-
|
|
2760
|
+
resolve4 = res;
|
|
2637
2761
|
reject = rej;
|
|
2638
2762
|
});
|
|
2639
2763
|
const server = createServer((req, res) => {
|
|
@@ -2651,7 +2775,7 @@ var browserLogin = async (webBase, timeoutMs = 5 * 60 * 1000, open = openBrowser
|
|
|
2651
2775
|
}
|
|
2652
2776
|
res.writeHead(200, { "content-type": "text/html" });
|
|
2653
2777
|
res.end(SUCCESS_HTML);
|
|
2654
|
-
|
|
2778
|
+
resolve4(token);
|
|
2655
2779
|
});
|
|
2656
2780
|
await new Promise((res, rej) => {
|
|
2657
2781
|
server.once("error", rej);
|
|
@@ -2977,7 +3101,7 @@ var getContext = (args) => {
|
|
|
2977
3101
|
return {
|
|
2978
3102
|
root,
|
|
2979
3103
|
manifest,
|
|
2980
|
-
name: manifest?.name ??
|
|
3104
|
+
name: manifest?.name ?? basename5(root),
|
|
2981
3105
|
dirName,
|
|
2982
3106
|
designDir: join13(root, dirName),
|
|
2983
3107
|
designKey: "/" + dirName.replace(/\\/g, "/")
|
|
@@ -2991,7 +3115,7 @@ var listScreens = (designDir) => {
|
|
|
2991
3115
|
};
|
|
2992
3116
|
var readMetaSizes = (designDir) => {
|
|
2993
3117
|
try {
|
|
2994
|
-
const meta = JSON.parse(
|
|
3118
|
+
const meta = JSON.parse(readFileSync12(join13(designDir, "meta.json"), "utf8"));
|
|
2995
3119
|
return meta?.screens ?? {};
|
|
2996
3120
|
} catch {
|
|
2997
3121
|
return {};
|
|
@@ -3003,7 +3127,7 @@ var writeMcpConfig = (ctx, port) => {
|
|
|
3003
3127
|
let config = {};
|
|
3004
3128
|
if (existsSync11(path)) {
|
|
3005
3129
|
try {
|
|
3006
|
-
config = JSON.parse(
|
|
3130
|
+
config = JSON.parse(readFileSync12(path, "utf8"));
|
|
3007
3131
|
} catch {
|
|
3008
3132
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-design server by hand.");
|
|
3009
3133
|
return;
|
|
@@ -3016,7 +3140,7 @@ var writeMcpConfig = (ctx, port) => {
|
|
|
3016
3140
|
return;
|
|
3017
3141
|
}
|
|
3018
3142
|
config.mcpServers["lecodes-design"] = entry;
|
|
3019
|
-
|
|
3143
|
+
writeFileSync9(path, JSON.stringify(config, null, 2) + `
|
|
3020
3144
|
`);
|
|
3021
3145
|
log(`${c.green(existing ? "updated" : "created")} .mcp.json (lecodes-design MCP → ${entry.url})`);
|
|
3022
3146
|
};
|
|
@@ -3038,13 +3162,13 @@ var init = async (ctx, args) => {
|
|
|
3038
3162
|
note(`kept ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
3039
3163
|
continue;
|
|
3040
3164
|
}
|
|
3041
|
-
|
|
3165
|
+
writeFileSync9(abs, content);
|
|
3042
3166
|
log(`${c.green("created")} ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
3043
3167
|
created++;
|
|
3044
3168
|
}
|
|
3045
3169
|
const ignorePath = join13(ctx.root, ".lecodesignore");
|
|
3046
3170
|
const ignoreLine = `${ctx.dirName}/.snapshots/`;
|
|
3047
|
-
const current = existsSync11(ignorePath) ?
|
|
3171
|
+
const current = existsSync11(ignorePath) ? readFileSync12(ignorePath, "utf8") : "";
|
|
3048
3172
|
if (!current.split(/\r?\n/).includes(ignoreLine)) {
|
|
3049
3173
|
appendFileSync(ignorePath, (current && !current.endsWith(`
|
|
3050
3174
|
`) ? `
|
|
@@ -3056,7 +3180,7 @@ var init = async (ctx, args) => {
|
|
|
3056
3180
|
const wroteTypes = materializeTypesLocal(ctx.designDir);
|
|
3057
3181
|
writeTsconfig(ctx.designDir);
|
|
3058
3182
|
mkdirSync8(join13(ctx.designDir, ".lecodes", "types"), { recursive: true });
|
|
3059
|
-
|
|
3183
|
+
writeFileSync9(join13(ctx.designDir, ".lecodes", "types", "design-macros.d.ts"), DESIGN_MACROS_DTS);
|
|
3060
3184
|
log(`${c.green("created")} ${ctx.dirName}/tsconfig.json${wroteTypes ? ` + ${ctx.dirName}/.lecodes/types/` : ""} (IDE types)`);
|
|
3061
3185
|
if (!wroteTypes)
|
|
3062
3186
|
warnErr("Bundled SDK types not found — tsconfig written, but globals may show unresolved until the CLI is rebuilt with vendored types.");
|
|
@@ -3114,7 +3238,7 @@ var snapshot = async (ctx, args) => {
|
|
|
3114
3238
|
const [width, height] = sizes[id]?.size ?? [390, 844];
|
|
3115
3239
|
let plan = [{ state: undefined, bases: [id] }];
|
|
3116
3240
|
if (discover) {
|
|
3117
|
-
const { states, canonical } = discover(
|
|
3241
|
+
const { states, canonical } = discover(readFileSync12(join13(ctx.designDir, "screens", `${id}.ts`), "utf8"));
|
|
3118
3242
|
if (stateFlag !== undefined) {
|
|
3119
3243
|
if (!states.includes(stateFlag)) {
|
|
3120
3244
|
if (requested.length === 1)
|
|
@@ -3155,10 +3279,10 @@ var snapshot = async (ctx, args) => {
|
|
|
3155
3279
|
for (const w of pngResult.warnings)
|
|
3156
3280
|
warnErr(`${tag}: ${w}`);
|
|
3157
3281
|
for (const base of bases) {
|
|
3158
|
-
|
|
3282
|
+
writeFileSync9(join13(outDir, `${base}.json`), result.text);
|
|
3159
3283
|
log(`${c.green("✓")} ${tag} → ${join13(outDir, `${base}.json`)}`);
|
|
3160
3284
|
if (pngResult) {
|
|
3161
|
-
|
|
3285
|
+
writeFileSync9(join13(outDir, `${base}.png`), pngResult.png);
|
|
3162
3286
|
log(`${c.green("✓")} ${tag} → ${join13(outDir, `${base}.png`)}`);
|
|
3163
3287
|
}
|
|
3164
3288
|
}
|
|
@@ -3340,37 +3464,16 @@ var design = async (args) => {
|
|
|
3340
3464
|
};
|
|
3341
3465
|
|
|
3342
3466
|
// src/commands/dev.ts
|
|
3343
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as
|
|
3467
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync13, watch, writeFileSync as writeFileSync10 } from "node:fs";
|
|
3344
3468
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
3345
3469
|
import { networkInterfaces } from "node:os";
|
|
3346
|
-
import { basename as
|
|
3470
|
+
import { basename as basename6, join as join15 } from "node:path";
|
|
3347
3471
|
|
|
3348
3472
|
// src/dev/devServer.ts
|
|
3349
3473
|
import { createServer as createServer2 } from "node:http";
|
|
3350
3474
|
import { createReadStream, existsSync as existsSync12, statSync as statSync2 } from "node:fs";
|
|
3351
3475
|
import { join as join14 } from "node:path";
|
|
3352
3476
|
|
|
3353
|
-
// src/compile/collectLocal.ts
|
|
3354
|
-
import { readFileSync as readFileSync11 } from "node:fs";
|
|
3355
|
-
import { basename as basename3 } from "node:path";
|
|
3356
|
-
var collectLocalEntries = (root, manifest, assetBase) => {
|
|
3357
|
-
const entries = [];
|
|
3358
|
-
const warnings = [];
|
|
3359
|
-
const scan = scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest?.files ?? {})) });
|
|
3360
|
-
for (const file of scan.files) {
|
|
3361
|
-
const type = manifest?.files[file.path]?.type ?? classifyKind(basename3(file.path));
|
|
3362
|
-
if (type === "resource") {
|
|
3363
|
-
entries.push({ path: file.path, type, fileSrc: assetBase + encodeURI(file.path), absPath: file.absPath });
|
|
3364
|
-
} else if (type === "shader") {
|
|
3365
|
-
warnings.push(`${file.path}: shaders compile on the server — push the project once for shaders to work in dev mode.`);
|
|
3366
|
-
entries.push({ path: file.path, type, shaders: [] });
|
|
3367
|
-
} else {
|
|
3368
|
-
entries.push({ path: file.path, type, text: readFileSync11(file.absPath, "utf8") });
|
|
3369
|
-
}
|
|
3370
|
-
}
|
|
3371
|
-
return { entries, warnings };
|
|
3372
|
-
};
|
|
3373
|
-
|
|
3374
3477
|
// src/dev/clientTemplates.ts
|
|
3375
3478
|
var DEV_SCREEN_TS = `const title = "LeCodes Dev"
|
|
3376
3479
|
const project = __DEV_NAME__
|
|
@@ -3633,14 +3736,14 @@ var sendText = (res, status, type, body) => {
|
|
|
3633
3736
|
res.end(body);
|
|
3634
3737
|
};
|
|
3635
3738
|
var sendJson = (res, status, body) => sendText(res, status, "application/json", JSON.stringify(body));
|
|
3636
|
-
var readBody = (req) => new Promise((
|
|
3739
|
+
var readBody = (req) => new Promise((resolve4) => {
|
|
3637
3740
|
let body = "";
|
|
3638
3741
|
req.on("data", (c2) => {
|
|
3639
3742
|
if (body.length < 256 * 1024)
|
|
3640
3743
|
body += c2.toString("utf8");
|
|
3641
3744
|
});
|
|
3642
|
-
req.on("end", () =>
|
|
3643
|
-
req.on("error", () =>
|
|
3745
|
+
req.on("end", () => resolve4(body));
|
|
3746
|
+
req.on("error", () => resolve4(body));
|
|
3644
3747
|
});
|
|
3645
3748
|
var startDevServer = async (options) => {
|
|
3646
3749
|
const { root, manifest, name, origin, token, entryOverride } = options;
|
|
@@ -3769,11 +3872,11 @@ var startDevServer = async (options) => {
|
|
|
3769
3872
|
if (conn)
|
|
3770
3873
|
clients.add(conn);
|
|
3771
3874
|
});
|
|
3772
|
-
await new Promise((
|
|
3875
|
+
await new Promise((resolve4, reject) => {
|
|
3773
3876
|
server.once("error", reject);
|
|
3774
3877
|
server.listen(options.port, options.bind ?? "0.0.0.0", () => {
|
|
3775
3878
|
server.removeListener("error", reject);
|
|
3776
|
-
|
|
3879
|
+
resolve4();
|
|
3777
3880
|
});
|
|
3778
3881
|
});
|
|
3779
3882
|
const address = server.address();
|
|
@@ -3811,22 +3914,22 @@ var pickLanAddress = () => {
|
|
|
3811
3914
|
var loadDevToken = (root) => {
|
|
3812
3915
|
const path = join15(root, LECODES_DIR, "dev.json");
|
|
3813
3916
|
try {
|
|
3814
|
-
const token2 = JSON.parse(
|
|
3917
|
+
const token2 = JSON.parse(readFileSync13(path, "utf8")).token;
|
|
3815
3918
|
if (token2 && /^[a-z0-9]{8,}$/.test(token2))
|
|
3816
3919
|
return token2;
|
|
3817
3920
|
} catch {}
|
|
3818
3921
|
const token = randomBytes2(6).toString("hex");
|
|
3819
3922
|
mkdirSync9(join15(root, LECODES_DIR), { recursive: true });
|
|
3820
|
-
|
|
3923
|
+
writeFileSync10(path, JSON.stringify({ token }, null, 2) + `
|
|
3821
3924
|
`);
|
|
3822
3925
|
return token;
|
|
3823
3926
|
};
|
|
3824
3927
|
var printQr = async (url) => {
|
|
3825
3928
|
try {
|
|
3826
3929
|
const qrcode = (await Promise.resolve().then(() => __toESM(require_main(), 1))).default;
|
|
3827
|
-
await new Promise((
|
|
3930
|
+
await new Promise((resolve4) => qrcode.generate(url, { small: true }, (out) => {
|
|
3828
3931
|
log(out);
|
|
3829
|
-
|
|
3932
|
+
resolve4();
|
|
3830
3933
|
}));
|
|
3831
3934
|
} catch {
|
|
3832
3935
|
note("(couldn't render a QR code — open the URL below on the device)");
|
|
@@ -3842,7 +3945,7 @@ var dev = async (args) => {
|
|
|
3842
3945
|
root = process.cwd();
|
|
3843
3946
|
manifest = null;
|
|
3844
3947
|
}
|
|
3845
|
-
const name = manifest?.name ??
|
|
3948
|
+
const name = manifest?.name ?? basename6(root);
|
|
3846
3949
|
if (typeof globalThis.Bun !== "undefined") {
|
|
3847
3950
|
warnErr("Running under Bun: the websocket reload channel doesn't work here — run `lecodes dev` with Node (or use ?t=poll on the device URL).");
|
|
3848
3951
|
}
|
|
@@ -3936,8 +4039,8 @@ ${result.error}`);
|
|
|
3936
4039
|
};
|
|
3937
4040
|
|
|
3938
4041
|
// src/serverDiff.ts
|
|
3939
|
-
import { readFileSync as
|
|
3940
|
-
import { basename as
|
|
4042
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
4043
|
+
import { basename as basename7 } from "node:path";
|
|
3941
4044
|
var isTextAsset = (t) => t === "text" || t === "shader" || t === "scene";
|
|
3942
4045
|
var computeServerDiff = (project, scan) => {
|
|
3943
4046
|
const pathById = buildPaths(project.assets);
|
|
@@ -3954,8 +4057,8 @@ var computeServerDiff = (project, scan) => {
|
|
|
3954
4057
|
const server = serverByPath.get(path);
|
|
3955
4058
|
const local = localByPath.get(path);
|
|
3956
4059
|
if (local && !server) {
|
|
3957
|
-
const text = isTextKind(classifyKind(
|
|
3958
|
-
entries.push({ path, status: "added", binary: !text, serverText: "", localText: text ?
|
|
4060
|
+
const text = isTextKind(classifyKind(basename7(path)));
|
|
4061
|
+
entries.push({ path, status: "added", binary: !text, serverText: "", localText: text ? readFileSync14(local.absPath, "utf8") : undefined });
|
|
3959
4062
|
continue;
|
|
3960
4063
|
}
|
|
3961
4064
|
if (server && !local) {
|
|
@@ -3966,12 +4069,12 @@ var computeServerDiff = (project, scan) => {
|
|
|
3966
4069
|
if (server && local) {
|
|
3967
4070
|
if (isTextAsset(server.type)) {
|
|
3968
4071
|
const serverText = server.text ?? "";
|
|
3969
|
-
const localText =
|
|
4072
|
+
const localText = readFileSync14(local.absPath, "utf8");
|
|
3970
4073
|
if (serverText !== localText)
|
|
3971
4074
|
entries.push({ path, status: "modified", binary: false, serverText, localText });
|
|
3972
4075
|
} else {
|
|
3973
4076
|
const serverSize = server.file?.size;
|
|
3974
|
-
const localSize = Buffer.byteLength(
|
|
4077
|
+
const localSize = Buffer.byteLength(readFileSync14(local.absPath));
|
|
3975
4078
|
if (serverSize !== undefined && serverSize !== localSize) {
|
|
3976
4079
|
entries.push({ path, status: "modified", binary: true });
|
|
3977
4080
|
}
|
|
@@ -4124,10 +4227,10 @@ var diff = async (args) => {
|
|
|
4124
4227
|
var statusLabel = (e) => e.status === "added" ? c.green(" added ") : e.status === "removed" ? c.red(" removed ") : c.yellow(" modified ");
|
|
4125
4228
|
|
|
4126
4229
|
// src/commands/init.ts
|
|
4127
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync10, writeFileSync as
|
|
4128
|
-
import { basename as
|
|
4230
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync10, writeFileSync as writeFileSync11 } from "node:fs";
|
|
4231
|
+
import { basename as basename8, dirname as dirname8, join as join16, resolve as resolve4 } from "node:path";
|
|
4129
4232
|
var STARTER2 = `// Welcome to LeCodes — the whole SDK is available as globals, no imports needed.
|
|
4130
|
-
// Run it live on a device with \`lecodes dev\`, or bundle it with \`lecodes
|
|
4233
|
+
// Run it live on a device with \`lecodes dev\`, or bundle it with \`lecodes compile -o app.js\`.
|
|
4131
4234
|
|
|
4132
4235
|
let count = 0
|
|
4133
4236
|
|
|
@@ -4163,8 +4266,8 @@ screen.open()
|
|
|
4163
4266
|
`;
|
|
4164
4267
|
var init2 = async (args) => {
|
|
4165
4268
|
const dirArg = (args._[0] ?? ".").replace(/[/\\]+$/, "") || ".";
|
|
4166
|
-
const root =
|
|
4167
|
-
const name =
|
|
4269
|
+
const root = resolve4(dirArg);
|
|
4270
|
+
const name = basename8(root);
|
|
4168
4271
|
mkdirSync10(root, { recursive: true });
|
|
4169
4272
|
let created = 0;
|
|
4170
4273
|
const write = (rel, content) => {
|
|
@@ -4174,7 +4277,7 @@ var init2 = async (args) => {
|
|
|
4174
4277
|
return;
|
|
4175
4278
|
}
|
|
4176
4279
|
mkdirSync10(dirname8(abs), { recursive: true });
|
|
4177
|
-
|
|
4280
|
+
writeFileSync11(abs, content);
|
|
4178
4281
|
log(`${c.green("created")} ${rel}`);
|
|
4179
4282
|
created++;
|
|
4180
4283
|
};
|
|
@@ -4190,7 +4293,7 @@ var init2 = async (args) => {
|
|
|
4190
4293
|
success(created ? `Local project ${c.bold(name)} ready${dirArg === "." ? "" : ` in ${c.bold(dirArg)}/`}.` : `Project ${c.bold(name)} already initialized.`);
|
|
4191
4294
|
const cd = dirArg === "." ? "" : `cd ${dirArg} && `;
|
|
4192
4295
|
log(` ${c.bold(cd + "lecodes dev")} run it on a device with hot reload`);
|
|
4193
|
-
log(` ${c.bold(cd + "lecodes
|
|
4296
|
+
log(` ${c.bold(cd + "lecodes compile -o app.js")} bundle it for LeCodes Desktop`);
|
|
4194
4297
|
};
|
|
4195
4298
|
|
|
4196
4299
|
// src/commands/login.ts
|
|
@@ -4204,100 +4307,6 @@ var login = async (args) => {
|
|
|
4204
4307
|
success(`Logged in as ${c.bold(account.email)} on ${apiUrl}`);
|
|
4205
4308
|
};
|
|
4206
4309
|
|
|
4207
|
-
// src/commands/preview.ts
|
|
4208
|
-
import { writeFileSync as writeFileSync11 } from "node:fs";
|
|
4209
|
-
import { basename as basename8, resolve as resolve4 } from "node:path";
|
|
4210
|
-
|
|
4211
|
-
// src/compile/collect.ts
|
|
4212
|
-
import { readFileSync as readFileSync14 } from "node:fs";
|
|
4213
|
-
import { basename as basename7 } from "node:path";
|
|
4214
|
-
var collectEntries = (root, manifest, project) => {
|
|
4215
|
-
const pathById = buildPaths(project.assets);
|
|
4216
|
-
const serverByPath = new Map;
|
|
4217
|
-
for (const asset of project.assets) {
|
|
4218
|
-
if (asset.type === "folder")
|
|
4219
|
-
continue;
|
|
4220
|
-
serverByPath.set(pathById.get(asset.id), { fileSrc: asset.file?.src ?? undefined, shaders: asset.shaders ?? [] });
|
|
4221
|
-
}
|
|
4222
|
-
const entries = [];
|
|
4223
|
-
const warnings = [];
|
|
4224
|
-
for (const file of scanLocal(root).files) {
|
|
4225
|
-
const type = manifest.files[file.path]?.type ?? classifyKind(basename7(file.path));
|
|
4226
|
-
if (type === "resource") {
|
|
4227
|
-
const src = serverByPath.get(file.path)?.fileSrc;
|
|
4228
|
-
if (!src)
|
|
4229
|
-
warnings.push(`No server URL for ${file.path} — push it so its URL resolves.`);
|
|
4230
|
-
entries.push({ path: file.path, type, fileSrc: src, absPath: file.absPath });
|
|
4231
|
-
} else if (type === "shader") {
|
|
4232
|
-
const shaders = serverByPath.get(file.path)?.shaders;
|
|
4233
|
-
if (!shaders?.length)
|
|
4234
|
-
warnings.push(`No compiled shader for ${file.path} — push it to compile the shader.`);
|
|
4235
|
-
entries.push({ path: file.path, type, shaders });
|
|
4236
|
-
} else {
|
|
4237
|
-
entries.push({ path: file.path, type, text: readFileSync14(file.absPath, "utf8") });
|
|
4238
|
-
}
|
|
4239
|
-
}
|
|
4240
|
-
return { entries, warnings };
|
|
4241
|
-
};
|
|
4242
|
-
|
|
4243
|
-
// src/commands/preview.ts
|
|
4244
|
-
var preview = async (args) => {
|
|
4245
|
-
let root;
|
|
4246
|
-
let manifest;
|
|
4247
|
-
try {
|
|
4248
|
-
root = findProjectRoot(process.cwd());
|
|
4249
|
-
manifest = readManifest(root);
|
|
4250
|
-
} catch {
|
|
4251
|
-
root = process.cwd();
|
|
4252
|
-
manifest = null;
|
|
4253
|
-
}
|
|
4254
|
-
let entries;
|
|
4255
|
-
let warnings;
|
|
4256
|
-
let name;
|
|
4257
|
-
let publicUrl;
|
|
4258
|
-
let localAssets = false;
|
|
4259
|
-
if (manifest) {
|
|
4260
|
-
const config = loadConfig();
|
|
4261
|
-
const apiUrl = requireApiUrl(config);
|
|
4262
|
-
const token = requireToken(config);
|
|
4263
|
-
note("Resolving project assets…");
|
|
4264
|
-
const project = await getProject(apiUrl, token, manifest.uuid);
|
|
4265
|
-
({ entries, warnings } = collectEntries(root, manifest, project));
|
|
4266
|
-
name = manifest.name;
|
|
4267
|
-
publicUrl = flagStr(args, "public-url") ?? apiUrl;
|
|
4268
|
-
} else {
|
|
4269
|
-
const publicUrlFlag = flagStr(args, "public-url");
|
|
4270
|
-
localAssets = publicUrlFlag === undefined;
|
|
4271
|
-
publicUrl = publicUrlFlag ?? "";
|
|
4272
|
-
({ entries, warnings } = collectLocalEntries(root, null, publicUrl));
|
|
4273
|
-
name = basename8(root);
|
|
4274
|
-
}
|
|
4275
|
-
note("Compiling…");
|
|
4276
|
-
const js = await compileProject2({
|
|
4277
|
-
entries,
|
|
4278
|
-
name,
|
|
4279
|
-
publicUrl,
|
|
4280
|
-
entryOverride: flagStr(args, "entry"),
|
|
4281
|
-
header: !flagBool(args, "no-header"),
|
|
4282
|
-
localAssets
|
|
4283
|
-
});
|
|
4284
|
-
for (const w of warnings)
|
|
4285
|
-
warnErr(w);
|
|
4286
|
-
const out = flagStr(args, "o", "out");
|
|
4287
|
-
if (out && isNullSink(out)) {
|
|
4288
|
-
note(`Discarded output (${out})`);
|
|
4289
|
-
} else if (out) {
|
|
4290
|
-
writeFileSync11(resolve4(out), js);
|
|
4291
|
-
note(`Wrote ${out} (${js.length} bytes)`);
|
|
4292
|
-
} else {
|
|
4293
|
-
process.stdout.write(js);
|
|
4294
|
-
if (!js.endsWith(`
|
|
4295
|
-
`))
|
|
4296
|
-
process.stdout.write(`
|
|
4297
|
-
`);
|
|
4298
|
-
}
|
|
4299
|
-
};
|
|
4300
|
-
|
|
4301
4310
|
// src/commands/render.ts
|
|
4302
4311
|
import { writeFileSync as writeFileSync12 } from "node:fs";
|
|
4303
4312
|
import { resolve as resolve5 } from "node:path";
|
|
@@ -5004,7 +5013,7 @@ ${c.bold("Usage:")} lecodes <command> [options]
|
|
|
5004
5013
|
${c.bold("Commands:")}
|
|
5005
5014
|
init [dir] Scaffold a new LOCAL project (no login/server): a starter
|
|
5006
5015
|
main.ts, IDE types + tsconfig, and a .lecodesignore.
|
|
5007
|
-
Run it with 'lecodes dev' or 'lecodes
|
|
5016
|
+
Run it with 'lecodes dev' or 'lecodes compile'.${INSTALL_HELP}
|
|
5008
5017
|
login Authorize in the browser and store an access token
|
|
5009
5018
|
--token <pat> store a token from the web UI instead
|
|
5010
5019
|
--api <url> API origin (default https://le.codes)
|
|
@@ -5034,10 +5043,13 @@ ${c.bold("Commands:")}
|
|
|
5034
5043
|
--host <ip> LAN address to advertise (default: auto-detect)
|
|
5035
5044
|
--entry <path> override the entrypoint
|
|
5036
5045
|
--no-qr don't render the QR code
|
|
5037
|
-
|
|
5046
|
+
compile Compile the local project to JS (prints to stdout)
|
|
5047
|
+
(was 'lecodes preview' — the old name still works)
|
|
5038
5048
|
-o, --out <file> write to a file instead of stdout
|
|
5039
5049
|
--entry <path> override the entrypoint
|
|
5040
5050
|
--no-header omit the metadata header
|
|
5051
|
+
--no-minify keep the bundle readable (no compress/mangle)
|
|
5052
|
+
--no-sourcemap don't generate/inline the source map
|
|
5041
5053
|
--public-url <url> asset URL prefix (default: the API origin)
|
|
5042
5054
|
render Render the local UI project to a semantic JSON description
|
|
5043
5055
|
(needs the optional 'lecodes-renderer' package; UI projects only)
|
|
@@ -5070,7 +5082,7 @@ ${c.bold("Commands:")}
|
|
|
5070
5082
|
--no-mcp don't register the MCP server in .mcp.json
|
|
5071
5083
|
|
|
5072
5084
|
Config lives in ~/.lecodes/config.json (override with LECODES_API / LECODES_TOKEN).`;
|
|
5073
|
-
var commands = { init: init2, login, create, clone, status, diff, pull, push, preview, render, design, scene, dev };
|
|
5085
|
+
var commands = { init: init2, login, create, clone, status, diff, pull, push, compile: compile2, preview: previewLegacy, render, design, scene, dev };
|
|
5074
5086
|
if (STANDALONE)
|
|
5075
5087
|
commands.install = install;
|
|
5076
5088
|
var readVersion = () => package_default.version ?? "unknown";
|