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/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 slug3 = slash === -1 ? spec : spec.slice(0, slash);
113
- if (slugs.has(slug3)) {
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/${slug3}${sub}`;
115
+ return `/__lib/${slug2}${sub}`;
116
116
  }
117
- throw new Error(`Unknown library "${slug3}" — add it in Settings → Libraries, or use a relative path like "./${slug3}".`);
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 existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync } from "node:fs";
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 join9, relative as relative2 } from "node:path";
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 && existsSync7(override))
136
+ if (override && existsSync6(override))
137
137
  return override;
138
138
  const here = dirname5(fileURLToPath2(import.meta.url));
139
- const src = join9(here, "..", "inject.ts");
140
- if (existsSync7(src))
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 readdirSync4(dir)) {
167
- const p = join9(dir, name);
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("/")] = readFileSync6(p, "utf8");
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 (existsSync7(override))
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 && existsSync7(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 [slug3, libFiles] of Object.entries(libraries ?? {})) {
209
+ for (const [slug2, libFiles] of Object.entries(libraries ?? {})) {
210
210
  for (const [k, v] of Object.entries(libFiles)) {
211
- const path = normalize(`/__lib/${slug3}/${k}`);
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, true, minify, editor, libraries);
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 existsSync8, readdirSync as readdirSync5, readFileSync as readFileSync7 } from "node:fs";
381
- import { join as join10 } from "node:path";
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 && existsSync8(override) ? override : fallbackDir && existsSync8(fallbackDir) ? fallbackDir : null;
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 readdirSync5(dir)) {
388
+ for (const f of readdirSync4(dir)) {
389
389
  if (f.endsWith(".ts"))
390
- out[f] = readFileSync7(join10(dir, f), "utf8");
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.1",
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.1",
1494
- "lecodes-renderer": "^0.6.1",
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/create.ts
1998
- import { existsSync as existsSync6, mkdirSync as mkdirSync6, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "node:fs";
1999
- import { join as join7, resolve as resolve2 } from "node:path";
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
- setLoop((dt) => {
2006
- // your per-frame logic — dt is seconds since the previous frame
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 readdirSync3, readFileSync as readFileSync5 } from "node:fs";
2045
- import { join as join8, relative } from "node:path";
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 readdirSync3(dir, { withFileTypes: true })) {
2099
- const abs = join8(dir, entry.name);
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 existsSync9 } from "node:fs";
2166
+ import { existsSync as existsSync8 } from "node:fs";
2154
2167
  import { createRequire as createRequire3 } from "node:module";
2155
- import { dirname as dirname6, join as join11 } from "node:path";
2168
+ import { dirname as dirname6, join as join10 } from "node:path";
2156
2169
  init_compile();
2157
- var vendoredInject = join11(distRoot, "runtime", "sdk", "inject.ts");
2158
- if (existsSync9(vendoredInject))
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 = join11(dirname6(process.execPath), process.platform === "win32" ? "chisel.exe" : "chisel");
2169
- return existsSync9(local) ? local : null;
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 readFileSync8, writeFileSync as writeFileSync7 } from "node:fs";
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 readFileSync8(join12(registry, rel), "utf8");
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(readFileSync8(path, "utf8"));
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
- writeFileSync7(path, text);
2612
+ writeFileSync8(path, text);
2489
2613
  return true;
2490
2614
  };
2491
2615
  var safeRead = (path) => {
2492
2616
  try {
2493
- return readFileSync8(path, "utf8");
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
- writeFileSync7(abs, svg);
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(basename(file.path));
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: readFileSync9(file.absPath, "utf8") });
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 resolve3;
2757
+ let resolve4;
2634
2758
  let reject;
2635
2759
  const tokenPromise = new Promise((res, rej) => {
2636
- resolve3 = res;
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
- resolve3(token);
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 ?? basename2(root),
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(readFileSync10(join13(designDir, "meta.json"), "utf8"));
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(readFileSync10(path, "utf8"));
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
- writeFileSync8(path, JSON.stringify(config, null, 2) + `
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
- writeFileSync8(abs, content);
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) ? readFileSync10(ignorePath, "utf8") : "";
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
- writeFileSync8(join13(ctx.designDir, ".lecodes", "types", "design-macros.d.ts"), DESIGN_MACROS_DTS);
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(readFileSync10(join13(ctx.designDir, "screens", `${id}.ts`), "utf8"));
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
- writeFileSync8(join13(outDir, `${base}.json`), result.text);
3282
+ writeFileSync9(join13(outDir, `${base}.json`), result.text);
3159
3283
  log(`${c.green("✓")} ${tag} → ${join13(outDir, `${base}.json`)}`);
3160
3284
  if (pngResult) {
3161
- writeFileSync8(join13(outDir, `${base}.png`), pngResult.png);
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 readFileSync12, watch, writeFileSync as writeFileSync9 } from "node:fs";
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 basename4, join as join15 } from "node:path";
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((resolve3) => {
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", () => resolve3(body));
3643
- req.on("error", () => resolve3(body));
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((resolve3, reject) => {
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
- resolve3();
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(readFileSync12(path, "utf8")).token;
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
- writeFileSync9(path, JSON.stringify({ token }, null, 2) + `
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((resolve3) => qrcode.generate(url, { small: true }, (out) => {
3930
+ await new Promise((resolve4) => qrcode.generate(url, { small: true }, (out) => {
3828
3931
  log(out);
3829
- resolve3();
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 ?? basename4(root);
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 readFileSync13 } from "node:fs";
3940
- import { basename as basename5 } from "node:path";
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(basename5(path)));
3958
- entries.push({ path, status: "added", binary: !text, serverText: "", localText: text ? readFileSync13(local.absPath, "utf8") : undefined });
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 = readFileSync13(local.absPath, "utf8");
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(readFileSync13(local.absPath));
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 writeFileSync10 } from "node:fs";
4128
- import { basename as basename6, dirname as dirname8, join as join16, resolve as resolve3 } from "node:path";
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 preview -o app.js\`.
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 = resolve3(dirArg);
4167
- const name = basename6(root);
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
- writeFileSync10(abs, content);
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 preview -o app.js")} bundle it for LeCodes Desktop`);
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 preview'.${INSTALL_HELP}
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
- preview Compile the local project to JS (prints to stdout)
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";