lecodes-cli 0.1.5 → 0.1.7
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 +300 -149
- package/package.json +5 -3
- package/runtime/sdk/animate/animate.ts +238 -0
- package/runtime/sdk/animate/bezier.ts +138 -0
- package/runtime/sdk/animate/easings.ts +126 -0
- package/runtime/sdk/core/color.ts +66 -0
- package/runtime/sdk/core/events.ts +38 -0
- package/runtime/sdk/core/registry.ts +23 -0
- package/runtime/sdk/g2/Camera2D.ts +73 -0
- package/runtime/sdk/g2/Node2D.ts +104 -0
- package/runtime/sdk/g2/Scene2D.ts +73 -0
- package/runtime/sdk/g2/Sprite.ts +164 -0
- package/runtime/sdk/g2/Texture2D.ts +37 -0
- package/runtime/sdk/g2/Tilemap.ts +41 -0
- package/runtime/sdk/g2/loop.ts +22 -0
- package/runtime/sdk/gl/Body.ts +101 -0
- package/runtime/sdk/gl/Camera.ts +40 -0
- package/runtime/sdk/gl/Geometry.ts +218 -0
- package/runtime/sdk/gl/Light.ts +37 -0
- package/runtime/sdk/gl/Material.ts +124 -0
- package/runtime/sdk/gl/Mesh.ts +123 -0
- package/runtime/sdk/gl/Node.ts +307 -0
- package/runtime/sdk/gl/Noise.ts +30 -0
- package/runtime/sdk/gl/Particles.ts +185 -0
- package/runtime/sdk/gl/Plane.ts +53 -0
- package/runtime/sdk/gl/Ray.ts +16 -0
- package/runtime/sdk/gl/Scene.ts +179 -0
- package/runtime/sdk/gl/Texture.ts +35 -0
- package/runtime/sdk/gl/controls.ts +95 -0
- package/runtime/sdk/gl/state.ts +6 -0
- package/runtime/sdk/gl/touch.ts +56 -0
- package/runtime/sdk/inject.ts +72 -0
- package/runtime/sdk/math/Mathf.ts +118 -0
- package/runtime/sdk/math/mat4.ts +278 -0
- package/runtime/sdk/math/quat.ts +232 -0
- package/runtime/sdk/math/vec.ts +255 -0
- package/runtime/sdk/plugins/qr.ts +36 -0
- package/runtime/sdk/runtime/device.ts +33 -0
- package/runtime/sdk/runtime/fetch.ts +71 -0
- package/runtime/sdk/runtime/files.ts +23 -0
- package/runtime/sdk/runtime/input.ts +12 -0
- package/runtime/sdk/runtime/media.ts +111 -0
- package/runtime/sdk/runtime/misc.ts +12 -0
- package/runtime/sdk/runtime/net.ts +36 -0
- package/runtime/sdk/runtime/storage.ts +13 -0
- package/runtime/sdk/runtime/touch.ts +44 -0
- package/runtime/sdk/ui/UI.ts +19 -0
- package/runtime/sdk/ui/UIButton.ts +84 -0
- package/runtime/sdk/ui/UIContainer.ts +100 -0
- package/runtime/sdk/ui/UIImage.ts +68 -0
- package/runtime/sdk/ui/UIInput.ts +78 -0
- package/runtime/sdk/ui/UINode.ts +342 -0
- package/runtime/sdk/ui/UIScreen.ts +122 -0
- package/runtime/sdk/ui/UIScrollable.ts +70 -0
- package/runtime/sdk/ui/UISpacer.ts +14 -0
- package/runtime/sdk/ui/UIText.ts +40 -0
- package/runtime/sdk/ui/UIVideo.ts +38 -0
- package/runtime/sdk/ui/UIVirtualizedList.ts +222 -0
- package/runtime/sdk/ui/UIWidget.ts +97 -0
- package/runtime/sdk/ui/fonts.ts +10 -0
- package/runtime/sdk/ui/router.ts +73 -0
- package/runtime/gl-matrix/common.js +0 -54
- package/runtime/gl-matrix/header.js +0 -12
- package/runtime/gl-matrix/index.js +0 -11
- package/runtime/gl-matrix/mat2.js +0 -425
- package/runtime/gl-matrix/mat2d.js +0 -476
- package/runtime/gl-matrix/mat3.js +0 -765
- package/runtime/gl-matrix/mat4.js +0 -1947
- package/runtime/gl-matrix/quat.js +0 -737
- package/runtime/gl-matrix/quat2.js +0 -822
- package/runtime/gl-matrix/vec2.js +0 -620
- package/runtime/gl-matrix/vec3.js +0 -791
- package/runtime/gl-matrix/vec4.js +0 -652
- package/runtime/worker/animate.js +0 -304
- package/runtime/worker/chunk-4ZN32GYS.js +0 -34
- package/runtime/worker/chunk-G5ZA3LKB.js +0 -52
- package/runtime/worker/chunk-MI3UXE5H.js +0 -1395
- package/runtime/worker/creator.js +0 -930
- package/runtime/worker/globals.js +0 -208
- package/runtime/worker/ui.js +0 -763
- package/runtime/worker/utils.js +0 -16
package/dist/index.js
CHANGED
|
@@ -3,9 +3,9 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
|
-
import { readFileSync as
|
|
7
|
-
import { dirname as
|
|
8
|
-
import { fileURLToPath as
|
|
6
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
7
|
+
import { dirname as dirname7, join as join11 } from "node:path";
|
|
8
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
9
9
|
|
|
10
10
|
// src/commands/clone.ts
|
|
11
11
|
import { existsSync as existsSync3, mkdirSync as mkdirSync5, readdirSync } from "node:fs";
|
|
@@ -160,6 +160,7 @@ var apiRequest = async (apiUrl, path, opts = {}) => {
|
|
|
160
160
|
};
|
|
161
161
|
var getAccount = (apiUrl, token) => apiRequest(apiUrl, "/account", { token });
|
|
162
162
|
var getProject = (apiUrl, token, uuid) => apiRequest(apiUrl, `/projects/${uuid}`, { token });
|
|
163
|
+
var createProject = (apiUrl, token, name, teamUuid) => apiRequest(apiUrl, "/projects", { token, body: { name, teamUuid } });
|
|
163
164
|
var getCommits = (apiUrl, token, uuid) => apiRequest(apiUrl, `/projects/${uuid}/commits`, { token });
|
|
164
165
|
var getTypes = (apiUrl) => apiRequest(apiUrl, "/types");
|
|
165
166
|
var uploadBlob = (apiUrl, token, uuid, name, bytes) => {
|
|
@@ -282,38 +283,8 @@ var materializeAssets = async (root, project, apiUrl, token, onProgress) => {
|
|
|
282
283
|
};
|
|
283
284
|
|
|
284
285
|
// src/types.ts
|
|
285
|
-
import { join as join4 } from "node:path";
|
|
286
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
286
287
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
287
|
-
var wrapGlobal = (text) => {
|
|
288
|
-
const marker = text.indexOf("declare global");
|
|
289
|
-
if (marker < 0)
|
|
290
|
-
return text;
|
|
291
|
-
const importLines = [];
|
|
292
|
-
const bodyLines = [];
|
|
293
|
-
for (const line of text.split(`
|
|
294
|
-
`)) {
|
|
295
|
-
if (/^\s*import\s.*\sfrom\s/.test(line))
|
|
296
|
-
importLines.push(line);
|
|
297
|
-
else
|
|
298
|
-
bodyLines.push(line);
|
|
299
|
-
}
|
|
300
|
-
const rest = bodyLines.join(`
|
|
301
|
-
`);
|
|
302
|
-
const open = rest.indexOf("{", rest.indexOf("declare global"));
|
|
303
|
-
const close = rest.lastIndexOf("}");
|
|
304
|
-
const preamble = rest.slice(0, rest.indexOf("declare global"));
|
|
305
|
-
const inner = rest.slice(open + 1, close);
|
|
306
|
-
const stripDeclare = (s) => s.replace(/(^|\n)([ \t]*)declare\s+/g, "$1$2");
|
|
307
|
-
const merged = stripDeclare(preamble) + `
|
|
308
|
-
` + stripDeclare(inner);
|
|
309
|
-
return `${importLines.join(`
|
|
310
|
-
`)}
|
|
311
|
-
|
|
312
|
-
declare global {
|
|
313
|
-
${merged}
|
|
314
|
-
}
|
|
315
|
-
`;
|
|
316
|
-
};
|
|
317
288
|
var TSCONFIG = {
|
|
318
289
|
compilerOptions: {
|
|
319
290
|
target: "ES2020",
|
|
@@ -333,10 +304,10 @@ var TSCONFIG = {
|
|
|
333
304
|
var materializeTypes = async (root, apiUrl) => {
|
|
334
305
|
const bundle = await getTypes(apiUrl);
|
|
335
306
|
const typesDir = join4(root, LECODES_DIR, "types");
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
writeFileSync4(
|
|
307
|
+
for (const [path, text] of Object.entries(bundle)) {
|
|
308
|
+
const dest = join4(typesDir, path);
|
|
309
|
+
mkdirSync4(dirname3(dest), { recursive: true });
|
|
310
|
+
writeFileSync4(dest, text);
|
|
340
311
|
}
|
|
341
312
|
};
|
|
342
313
|
var writeTsconfig = (root) => {
|
|
@@ -351,10 +322,13 @@ var DEFAULT_IGNORE_FILE = `# Files matched here are NOT pushed to LeCodes.
|
|
|
351
322
|
# Only untracked files are affected — assets already in the project stay tracked
|
|
352
323
|
# even if they match a pattern. Built-in defaults (node_modules, _*, *.log) always apply.
|
|
353
324
|
|
|
354
|
-
# Render / preview outputs
|
|
325
|
+
# Render / preview outputs (the default \`lecodes render --png\` target)
|
|
355
326
|
screenshot.png
|
|
356
327
|
shots/
|
|
357
|
-
|
|
328
|
+
|
|
329
|
+
# Image & other binary assets ARE pushed by default — they're real project resources.
|
|
330
|
+
# Uncomment a pattern below only if you want to keep some untracked ones out of pushes.
|
|
331
|
+
# *.png
|
|
358
332
|
|
|
359
333
|
# Local notes & dumps (uncomment to ignore)
|
|
360
334
|
# *.json
|
|
@@ -471,9 +445,47 @@ var clone = async (args) => {
|
|
|
471
445
|
info(c.dim(` cd ${dir} — edit, then \`lecodes push -m "your message"\``));
|
|
472
446
|
};
|
|
473
447
|
|
|
448
|
+
// src/commands/create.ts
|
|
449
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync6, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
450
|
+
import { join as join6, resolve as resolve2 } from "node:path";
|
|
451
|
+
var slug2 = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
452
|
+
var STARTER = `// Welcome to le.codes — no imports needed, the whole SDK is available as globals.
|
|
453
|
+
const scene = new Scene2D({ background: '#10131a' })
|
|
454
|
+
scene.open()
|
|
455
|
+
|
|
456
|
+
setLoop((dt) => {
|
|
457
|
+
// your per-frame logic — dt is seconds since the previous frame
|
|
458
|
+
})
|
|
459
|
+
`;
|
|
460
|
+
var create = async (args) => {
|
|
461
|
+
const config = loadConfig();
|
|
462
|
+
const apiUrl = requireApiUrl(config);
|
|
463
|
+
const token = requireToken(config);
|
|
464
|
+
const name = args._[0];
|
|
465
|
+
if (!name)
|
|
466
|
+
throw new CliError("Usage: lecodes create <name> [directory]");
|
|
467
|
+
const dir = args._[1] ?? slug2(name);
|
|
468
|
+
const root = resolve2(dir);
|
|
469
|
+
if (existsSync4(root) && readdirSync2(root).length > 0) {
|
|
470
|
+
throw new CliError(`Directory "${dir}" already exists and is not empty.`);
|
|
471
|
+
}
|
|
472
|
+
mkdirSync6(root, { recursive: true });
|
|
473
|
+
info(c.dim(`Creating project ${c.bold(name)}…`));
|
|
474
|
+
const { uuid } = await createProject(apiUrl, token, name, flagStr(args, "team"));
|
|
475
|
+
info(c.dim("Fetching type definitions…"));
|
|
476
|
+
await materializeTypes(root, apiUrl);
|
|
477
|
+
writeTsconfig(root);
|
|
478
|
+
writeDefaultIgnore(root);
|
|
479
|
+
writeFileSync6(join6(root, "main.ts"), STARTER);
|
|
480
|
+
const commits = await getCommits(apiUrl, token, uuid);
|
|
481
|
+
writeManifest(root, { uuid, apiUrl, name, baseHeadId: commits.headId, files: {} });
|
|
482
|
+
success(`Created ${c.bold(name)} in ${c.bold(dir)}/`);
|
|
483
|
+
info(c.dim(` cd ${dir} — edit main.ts, then \`lecodes push -m "first commit"\` to save it`));
|
|
484
|
+
};
|
|
485
|
+
|
|
474
486
|
// src/localFiles.ts
|
|
475
|
-
import { readdirSync as
|
|
476
|
-
import { join as
|
|
487
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
488
|
+
import { join as join7, relative } from "node:path";
|
|
477
489
|
var SKIP_DIRS = new Set([".lecodes", ".git", "node_modules"]);
|
|
478
490
|
var SKIP_FILES = new Set(["tsconfig.json", "jsconfig.json", ".gitignore", ".lecodesignore", ".DS_Store"]);
|
|
479
491
|
var TEXT_EXTS = new Set([
|
|
@@ -526,8 +538,8 @@ var scanLocal = (root, opts = {}) => {
|
|
|
526
538
|
return false;
|
|
527
539
|
};
|
|
528
540
|
const walk = (dir, parentIgnored) => {
|
|
529
|
-
for (const entry of
|
|
530
|
-
const abs =
|
|
541
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
542
|
+
const abs = join7(dir, entry.name);
|
|
531
543
|
const key = toKey(relative(root, abs));
|
|
532
544
|
if (entry.isDirectory()) {
|
|
533
545
|
if (SKIP_DIRS.has(entry.name))
|
|
@@ -804,10 +816,10 @@ var openBrowser = (url) => {
|
|
|
804
816
|
};
|
|
805
817
|
var browserLogin = async (webBase, timeoutMs = 5 * 60 * 1000, open = openBrowser) => {
|
|
806
818
|
const state = randomBytes(16).toString("hex");
|
|
807
|
-
let
|
|
819
|
+
let resolve3;
|
|
808
820
|
let reject;
|
|
809
821
|
const tokenPromise = new Promise((res, rej) => {
|
|
810
|
-
|
|
822
|
+
resolve3 = res;
|
|
811
823
|
reject = rej;
|
|
812
824
|
});
|
|
813
825
|
const server = createServer((req, res) => {
|
|
@@ -825,7 +837,7 @@ var browserLogin = async (webBase, timeoutMs = 5 * 60 * 1000, open = openBrowser
|
|
|
825
837
|
}
|
|
826
838
|
res.writeHead(200, { "content-type": "text/html" });
|
|
827
839
|
res.end(SUCCESS_HTML);
|
|
828
|
-
|
|
840
|
+
resolve3(token);
|
|
829
841
|
});
|
|
830
842
|
await new Promise((res, rej) => {
|
|
831
843
|
server.once("error", rej);
|
|
@@ -858,8 +870,8 @@ var login = async (args) => {
|
|
|
858
870
|
};
|
|
859
871
|
|
|
860
872
|
// src/commands/preview.ts
|
|
861
|
-
import { writeFileSync as
|
|
862
|
-
import { resolve as
|
|
873
|
+
import { writeFileSync as writeFileSync7 } from "node:fs";
|
|
874
|
+
import { resolve as resolve3 } from "node:path";
|
|
863
875
|
|
|
864
876
|
// src/compile/collect.ts
|
|
865
877
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
@@ -893,13 +905,19 @@ var collectEntries = (root, manifest, project) => {
|
|
|
893
905
|
return { entries, warnings };
|
|
894
906
|
};
|
|
895
907
|
|
|
896
|
-
// src/compile/
|
|
908
|
+
// src/compile/projectCompile.ts
|
|
909
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
910
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
911
|
+
import { dirname as dirname5, join as join9 } from "node:path";
|
|
912
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
913
|
+
// ../sdk/src/compile/bundler.ts
|
|
897
914
|
import * as esbuild from "esbuild";
|
|
898
|
-
import { existsSync as
|
|
899
|
-
import {
|
|
915
|
+
import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync7, statSync } from "node:fs";
|
|
916
|
+
import { spawnSync } from "node:child_process";
|
|
917
|
+
import { dirname as dirname4, join as join8, relative as relative2 } from "node:path";
|
|
900
918
|
import { fileURLToPath } from "node:url";
|
|
901
919
|
|
|
902
|
-
// src/compile/assetMacro.ts
|
|
920
|
+
// ../sdk/src/compile/assetMacro.ts
|
|
903
921
|
var ASSET_RE = /\basset\(\s*(["'])((?:[^"'\\]|\\.)*)\1\s*\)/g;
|
|
904
922
|
var hasAssetMacro = (src) => src.includes("asset(");
|
|
905
923
|
var desugarAssetMacros = (src, filePath) => {
|
|
@@ -924,52 +942,21 @@ var desugarAssetMacros = (src, filePath) => {
|
|
|
924
942
|
` + out;
|
|
925
943
|
};
|
|
926
944
|
|
|
927
|
-
// src/compile/bundler.ts
|
|
928
|
-
var
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
const vendored = join7(here, "..", "runtime");
|
|
943
|
-
if (existsSync4(join7(vendored, "worker", "creator.js"))) {
|
|
944
|
-
const w = join7(vendored, "worker");
|
|
945
|
-
return {
|
|
946
|
-
alias: {
|
|
947
|
-
creator: join7(w, "creator.js"),
|
|
948
|
-
ui: join7(w, "ui.js"),
|
|
949
|
-
animate: join7(w, "animate.js"),
|
|
950
|
-
utils: join7(w, "utils.js"),
|
|
951
|
-
"gl-matrix": join7(vendored, "gl-matrix")
|
|
952
|
-
},
|
|
953
|
-
inject: [join7(w, "globals.js")]
|
|
954
|
-
};
|
|
955
|
-
}
|
|
956
|
-
const repoRoot = join7(here, "..", "..", "..", "..");
|
|
957
|
-
const ws = join7(repoRoot, "packages", "worker", "src");
|
|
958
|
-
if (existsSync4(join7(ws, "index.ts"))) {
|
|
959
|
-
return {
|
|
960
|
-
alias: {
|
|
961
|
-
creator: join7(ws, "index.ts"),
|
|
962
|
-
ui: join7(ws, "ui.ts"),
|
|
963
|
-
animate: join7(ws, "animate.ts"),
|
|
964
|
-
utils: join7(ws, "utils.ts"),
|
|
965
|
-
"gl-matrix": join7(repoRoot, "packages", "backend", "src", "assets", "dependencies", "gl-matrix")
|
|
966
|
-
},
|
|
967
|
-
inject: [join7(ws, "globals.ts")]
|
|
968
|
-
};
|
|
969
|
-
}
|
|
970
|
-
throw new CliError("Could not locate the compile runtime — try reinstalling lecodes-cli.");
|
|
971
|
-
};
|
|
972
|
-
function normalize(path) {
|
|
945
|
+
// ../sdk/src/compile/bundler.ts
|
|
946
|
+
var resolveInject = () => {
|
|
947
|
+
const override = process.env.LECODES_SDK_INJECT;
|
|
948
|
+
if (override && existsSync5(override))
|
|
949
|
+
return override;
|
|
950
|
+
const here = dirname4(fileURLToPath(import.meta.url));
|
|
951
|
+
const src = join8(here, "..", "inject.ts");
|
|
952
|
+
if (existsSync5(src))
|
|
953
|
+
return src;
|
|
954
|
+
const dist = join8(here, "..", "..", "dist", "inject.js");
|
|
955
|
+
if (existsSync5(dist))
|
|
956
|
+
return dist;
|
|
957
|
+
throw new Error("sdk: could not locate the inject entry (LECODES_SDK_INJECT / src/inject.ts / dist/inject.js)");
|
|
958
|
+
};
|
|
959
|
+
var normalize = (path) => {
|
|
973
960
|
const parts = [];
|
|
974
961
|
for (const part of path.split("/")) {
|
|
975
962
|
if (!part || part === ".")
|
|
@@ -980,20 +967,20 @@ function normalize(path) {
|
|
|
980
967
|
parts.push(part);
|
|
981
968
|
}
|
|
982
969
|
return "/" + parts.join("/");
|
|
983
|
-
}
|
|
984
|
-
|
|
970
|
+
};
|
|
971
|
+
var dirOf = (path) => {
|
|
985
972
|
const i = path.lastIndexOf("/");
|
|
986
973
|
return i <= 0 ? "/" : path.slice(0, i);
|
|
987
|
-
}
|
|
974
|
+
};
|
|
988
975
|
var hasExtension = (path) => /\.[a-zA-Z0-9]+$/.test(path);
|
|
989
|
-
|
|
976
|
+
var resolveBase = (importer, specifier) => {
|
|
990
977
|
if (specifier.startsWith("/"))
|
|
991
978
|
return normalize(specifier);
|
|
992
979
|
if (specifier.startsWith("./") || specifier.startsWith("../"))
|
|
993
980
|
return normalize(dirOf(importer) + "/" + specifier);
|
|
994
|
-
throw new Error(`Bare
|
|
995
|
-
}
|
|
996
|
-
|
|
981
|
+
throw new Error(`Bare imports are not allowed — the whole SDK is available as globals (no imports): ${specifier}`);
|
|
982
|
+
};
|
|
983
|
+
var resolveWithExtensions = (files, importer, specifier) => {
|
|
997
984
|
const base = resolveBase(importer, specifier);
|
|
998
985
|
if (hasExtension(base)) {
|
|
999
986
|
if (files.has(base))
|
|
@@ -1005,31 +992,26 @@ function resolveWithExtensions(files, importer, specifier) {
|
|
|
1005
992
|
return candidate;
|
|
1006
993
|
}
|
|
1007
994
|
throw new Error(`Module not found: ${specifier} from ${importer}`);
|
|
1008
|
-
}
|
|
995
|
+
};
|
|
1009
996
|
var getLoader = (path) => path.endsWith(".ts") ? "ts" : "js";
|
|
1010
|
-
async
|
|
1011
|
-
const
|
|
1012
|
-
const files = new Map(Object.entries(inputFiles).map(([k, v]) => [normalize(k), v]));
|
|
997
|
+
var bundleProjectEsbuild = async (entrypoint, files, format = "esm") => {
|
|
998
|
+
const fileMap = new Map(Object.entries(files).map(([k, v]) => [normalize(k), v]));
|
|
1013
999
|
const entry = normalize(entrypoint);
|
|
1014
|
-
if (!
|
|
1015
|
-
throw new
|
|
1000
|
+
if (!fileMap.has(entry))
|
|
1001
|
+
throw new Error(`Entrypoint not found: ${entry}`);
|
|
1016
1002
|
const plugin = {
|
|
1017
1003
|
name: "memfs",
|
|
1018
1004
|
setup(build2) {
|
|
1019
1005
|
build2.onResolve({ filter: /^entry$/ }, () => ({ path: entry, namespace: "mem" }));
|
|
1020
1006
|
build2.onResolve({ filter: /.*/, namespace: "mem" }, (args) => {
|
|
1021
|
-
const isRelative = args.path.startsWith("./") || args.path.startsWith("../");
|
|
1022
|
-
const isAbsolute = args.path.startsWith("/");
|
|
1023
|
-
if (!isRelative && !isAbsolute)
|
|
1024
|
-
return null;
|
|
1025
1007
|
try {
|
|
1026
|
-
return { path: resolveWithExtensions(
|
|
1008
|
+
return { path: resolveWithExtensions(fileMap, args.importer || entry, args.path), namespace: "mem" };
|
|
1027
1009
|
} catch (error) {
|
|
1028
1010
|
return { errors: [{ text: error instanceof Error ? error.message : String(error) }] };
|
|
1029
1011
|
}
|
|
1030
1012
|
});
|
|
1031
1013
|
build2.onLoad({ filter: /.*/, namespace: "mem" }, (args) => {
|
|
1032
|
-
const contents =
|
|
1014
|
+
const contents = fileMap.get(args.path);
|
|
1033
1015
|
if (contents == null)
|
|
1034
1016
|
return { errors: [{ text: `Module not found: ${args.path}` }] };
|
|
1035
1017
|
const finalContents = /\.[tj]sx?$/.test(args.path) && hasAssetMacro(contents) ? desugarAssetMacros(contents, args.path) : contents;
|
|
@@ -1039,9 +1021,8 @@ async function buildInMemory(entrypoint, inputFiles, format = "esm") {
|
|
|
1039
1021
|
};
|
|
1040
1022
|
const result = await esbuild.build({
|
|
1041
1023
|
stdin: { contents: `import "entry";`, loader: "js", sourcefile: "bootstrap.js" },
|
|
1042
|
-
|
|
1024
|
+
inject: [resolveInject()],
|
|
1043
1025
|
define: { RAD2DEG: "57.29577951308232", DEG2RAD: "0.017453292519943295" },
|
|
1044
|
-
inject: runtime.inject,
|
|
1045
1026
|
bundle: true,
|
|
1046
1027
|
write: false,
|
|
1047
1028
|
format,
|
|
@@ -1051,13 +1032,105 @@ async function buildInMemory(entrypoint, inputFiles, format = "esm") {
|
|
|
1051
1032
|
});
|
|
1052
1033
|
const output = result.outputFiles?.[0];
|
|
1053
1034
|
if (!output)
|
|
1054
|
-
throw new
|
|
1035
|
+
throw new Error("Compilation produced no output");
|
|
1055
1036
|
return output.text;
|
|
1056
|
-
}
|
|
1037
|
+
};
|
|
1038
|
+
var CHISEL_DEFINE = { RAD2DEG: "57.29577951308232", DEG2RAD: "0.017453292519943295" };
|
|
1039
|
+
var CHISEL_KEEP = ["_*"];
|
|
1040
|
+
var resolveSdkSrc = () => {
|
|
1041
|
+
const inject = resolveInject();
|
|
1042
|
+
if (!inject.endsWith("inject.ts")) {
|
|
1043
|
+
throw new Error(`chisel needs the SDK source tree (src/inject.ts); got a prebuilt inject at ${inject}. ` + `Set LECODES_SDK_INJECT to the source, or use esbuild (unset LECODES_BUNDLER).`);
|
|
1044
|
+
}
|
|
1045
|
+
return dirname4(inject);
|
|
1046
|
+
};
|
|
1047
|
+
var isSdkSource = (path) => /\.[tj]s$/.test(path) && !path.endsWith(".d.ts") && !path.includes("/compile/") && !path.includes("/tests/") && !path.includes("/__tests__/");
|
|
1048
|
+
var sdkFilesCache = new Map;
|
|
1049
|
+
var collectSdkFiles = (sdkSrc) => {
|
|
1050
|
+
const cached = sdkFilesCache.get(sdkSrc);
|
|
1051
|
+
if (cached)
|
|
1052
|
+
return cached;
|
|
1053
|
+
const out = {};
|
|
1054
|
+
const walk = (dir) => {
|
|
1055
|
+
for (const name of readdirSync4(dir)) {
|
|
1056
|
+
const p = join8(dir, name);
|
|
1057
|
+
if (statSync(p).isDirectory())
|
|
1058
|
+
walk(p);
|
|
1059
|
+
else if (isSdkSource(p))
|
|
1060
|
+
out["/__sdk/" + relative2(sdkSrc, p)] = readFileSync7(p, "utf8");
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
walk(sdkSrc);
|
|
1064
|
+
sdkFilesCache.set(sdkSrc, out);
|
|
1065
|
+
return out;
|
|
1066
|
+
};
|
|
1067
|
+
var resolveChiselBin = () => {
|
|
1068
|
+
const override = process.env.LECODES_CHISEL_BIN;
|
|
1069
|
+
if (override) {
|
|
1070
|
+
if (existsSync5(override))
|
|
1071
|
+
return override;
|
|
1072
|
+
throw new Error(`LECODES_CHISEL_BIN points at a missing file: ${override}`);
|
|
1073
|
+
}
|
|
1074
|
+
const here = dirname4(fileURLToPath(import.meta.url));
|
|
1075
|
+
for (const rel of ["release/chisel", "debug/chisel"]) {
|
|
1076
|
+
const p = join8(here, "..", "..", "..", "..", "..", "chisel", "target", rel);
|
|
1077
|
+
if (existsSync5(p))
|
|
1078
|
+
return p;
|
|
1079
|
+
}
|
|
1080
|
+
throw new Error("chisel binary not found. Build it (`cargo build --release` in the chisel repo) or set LECODES_CHISEL_BIN.");
|
|
1081
|
+
};
|
|
1082
|
+
var bundleProjectChisel = async (entrypoint, files, format, sourcemap) => {
|
|
1083
|
+
const entry = normalize(entrypoint);
|
|
1084
|
+
const projFiles = {};
|
|
1085
|
+
for (const [k, v] of Object.entries(files)) {
|
|
1086
|
+
const path = normalize(k);
|
|
1087
|
+
projFiles[path] = /\.[tj]sx?$/.test(path) && hasAssetMacro(v) ? desugarAssetMacros(v, path) : v;
|
|
1088
|
+
}
|
|
1089
|
+
if (!projFiles[entry])
|
|
1090
|
+
throw new Error(`Entrypoint not found: ${entry}`);
|
|
1091
|
+
const sdkFiles = collectSdkFiles(resolveSdkSrc());
|
|
1092
|
+
const minify = process.env.LECODES_CHISEL_MINIFY !== "0";
|
|
1093
|
+
const input = JSON.stringify({
|
|
1094
|
+
files: { ...sdkFiles, ...projFiles },
|
|
1095
|
+
entry,
|
|
1096
|
+
inject: ["/__sdk/inject.ts"],
|
|
1097
|
+
format,
|
|
1098
|
+
minify,
|
|
1099
|
+
fuse: process.env.LECODES_CHISEL_FUSE === "1",
|
|
1100
|
+
define: CHISEL_DEFINE,
|
|
1101
|
+
keep: CHISEL_KEEP,
|
|
1102
|
+
sourcemap
|
|
1103
|
+
});
|
|
1104
|
+
const bin = resolveChiselBin();
|
|
1105
|
+
const r = spawnSync(bin, [], { input, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
|
|
1106
|
+
if (r.status === 2 || r.error) {
|
|
1107
|
+
throw new Error(`chisel failed: ${r.error?.message ?? r.stderr ?? "unknown error"}`);
|
|
1108
|
+
}
|
|
1109
|
+
let parsed;
|
|
1110
|
+
try {
|
|
1111
|
+
parsed = JSON.parse(r.stdout || "{}");
|
|
1112
|
+
} catch {
|
|
1113
|
+
throw new Error(`chisel produced invalid output: ${(r.stdout || r.stderr || "").slice(0, 200)}`);
|
|
1114
|
+
}
|
|
1115
|
+
if (parsed.error)
|
|
1116
|
+
throw new Error(`chisel: ${parsed.error}`);
|
|
1117
|
+
if (parsed.code == null)
|
|
1118
|
+
throw new Error("chisel produced no code");
|
|
1119
|
+
return { code: parsed.code, map: parsed.map ?? null };
|
|
1120
|
+
};
|
|
1121
|
+
var bundleProjectWithMap = async (entrypoint, files, format = "esm") => {
|
|
1122
|
+
if (process.env.LECODES_BUNDLER === "chisel") {
|
|
1123
|
+
return bundleProjectChisel(entrypoint, files, format, true);
|
|
1124
|
+
}
|
|
1125
|
+
return { code: await bundleProjectEsbuild(entrypoint, files, format), map: null };
|
|
1126
|
+
};
|
|
1057
1127
|
|
|
1058
|
-
// src/compile/
|
|
1059
|
-
var buildHeader = (name, code) => {
|
|
1128
|
+
// ../sdk/src/compile/header.ts
|
|
1129
|
+
var buildHeader = (name, code, opts = {}) => {
|
|
1060
1130
|
let header = `// name: ${name.slice(0, 20)}
|
|
1131
|
+
`;
|
|
1132
|
+
if (opts.icon)
|
|
1133
|
+
header += `// icon: ${opts.icon}
|
|
1061
1134
|
`;
|
|
1062
1135
|
header += `// last-updated: ${new Date().toISOString()}
|
|
1063
1136
|
`;
|
|
@@ -1077,6 +1150,9 @@ var buildHeader = (name, code) => {
|
|
|
1077
1150
|
`;
|
|
1078
1151
|
else if (code.includes("_creator.createEngine"))
|
|
1079
1152
|
header += `// gl: defer
|
|
1153
|
+
`;
|
|
1154
|
+
if (code.includes("_creator2d."))
|
|
1155
|
+
header += `// 2d: enable
|
|
1080
1156
|
`;
|
|
1081
1157
|
if (code.includes("_creator.createARController"))
|
|
1082
1158
|
header += "// ar: enable";
|
|
@@ -1084,6 +1160,21 @@ var buildHeader = (name, code) => {
|
|
|
1084
1160
|
`;
|
|
1085
1161
|
return header;
|
|
1086
1162
|
};
|
|
1163
|
+
|
|
1164
|
+
// ../sdk/src/compile/sourcemap.ts
|
|
1165
|
+
var offsetSourceMap = (mapJson, lines) => {
|
|
1166
|
+
if (lines <= 0)
|
|
1167
|
+
return mapJson;
|
|
1168
|
+
const map = JSON.parse(mapJson);
|
|
1169
|
+
map.mappings = ";".repeat(lines) + map.mappings;
|
|
1170
|
+
return JSON.stringify(map);
|
|
1171
|
+
};
|
|
1172
|
+
var inlineSourceMapComment = (mapJson) => {
|
|
1173
|
+
const b64 = Buffer.from(mapJson, "utf8").toString("base64");
|
|
1174
|
+
return `//# lecodesMap=data:application/json;charset=utf-8;base64,${b64}`;
|
|
1175
|
+
};
|
|
1176
|
+
|
|
1177
|
+
// ../sdk/src/compile/compileProject.ts
|
|
1087
1178
|
var detectEntry = (entries) => {
|
|
1088
1179
|
let entry = "";
|
|
1089
1180
|
let max = -1;
|
|
@@ -1101,7 +1192,7 @@ var detectEntry = (entries) => {
|
|
|
1101
1192
|
var compileProject = async (opts) => {
|
|
1102
1193
|
const entry = opts.entryOverride ?? detectEntry(opts.entries);
|
|
1103
1194
|
if (!entry) {
|
|
1104
|
-
throw new
|
|
1195
|
+
throw new Error("Couldn't find an entrypoint (a top-level .ts/.js file with no `export`). Pass entryOverride.");
|
|
1105
1196
|
}
|
|
1106
1197
|
const files = {};
|
|
1107
1198
|
for (const e of opts.entries) {
|
|
@@ -1119,12 +1210,50 @@ var compileProject = async (opts) => {
|
|
|
1119
1210
|
} else if (e.path.toLowerCase().endsWith(".svg")) {
|
|
1120
1211
|
files[e.path] = `export default SvgSource(\`${e.text ?? ""}\`)`;
|
|
1121
1212
|
} else {
|
|
1122
|
-
files[e.path] =
|
|
1213
|
+
files[e.path] = e.text ?? "";
|
|
1123
1214
|
}
|
|
1124
1215
|
}
|
|
1125
|
-
const code = await
|
|
1126
|
-
|
|
1216
|
+
const { code, map } = await bundleProjectWithMap(entry, files, opts.format ?? "esm");
|
|
1217
|
+
if (opts.header === false) {
|
|
1218
|
+
return map ? `${code}
|
|
1219
|
+
${inlineSourceMapComment(map)}` : code;
|
|
1220
|
+
}
|
|
1221
|
+
const header = buildHeader(opts.name, code, { icon: opts.icon });
|
|
1222
|
+
if (!map)
|
|
1223
|
+
return header + code;
|
|
1224
|
+
const headerLines = (header.match(/\n/g) ?? []).length;
|
|
1225
|
+
return `${header}${code}
|
|
1226
|
+
${inlineSourceMapComment(offsetSourceMap(map, headerLines))}`;
|
|
1227
|
+
};
|
|
1228
|
+
// src/compile/projectCompile.ts
|
|
1229
|
+
var here = dirname5(fileURLToPath2(import.meta.url));
|
|
1230
|
+
var vendoredInject = join9(here, "..", "runtime", "sdk", "inject.ts");
|
|
1231
|
+
if (existsSync6(vendoredInject))
|
|
1232
|
+
process.env.LECODES_SDK_INJECT = vendoredInject;
|
|
1233
|
+
var resolveChiselBin2 = () => {
|
|
1234
|
+
if (process.env.LECODES_CHISEL_BIN)
|
|
1235
|
+
return process.env.LECODES_CHISEL_BIN;
|
|
1236
|
+
try {
|
|
1237
|
+
return createRequire2(import.meta.url)("@letary/chisel").binaryPath() ?? null;
|
|
1238
|
+
} catch {
|
|
1239
|
+
return null;
|
|
1240
|
+
}
|
|
1127
1241
|
};
|
|
1242
|
+
if (!process.env.LECODES_BUNDLER) {
|
|
1243
|
+
const chiselBin = resolveChiselBin2();
|
|
1244
|
+
if (chiselBin) {
|
|
1245
|
+
process.env.LECODES_CHISEL_BIN = chiselBin;
|
|
1246
|
+
process.env.LECODES_BUNDLER = "chisel";
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
var compileProject2 = (opts) => compileProject({
|
|
1250
|
+
entries: opts.entries.map((e) => ({ path: e.path, type: e.type, text: e.text, fileSrc: e.fileSrc, shaders: e.shaders })),
|
|
1251
|
+
name: opts.name,
|
|
1252
|
+
publicUrl: opts.publicUrl,
|
|
1253
|
+
entryOverride: opts.entryOverride,
|
|
1254
|
+
header: opts.header,
|
|
1255
|
+
format: opts.format
|
|
1256
|
+
});
|
|
1128
1257
|
|
|
1129
1258
|
// src/commands/preview.ts
|
|
1130
1259
|
var preview = async (args) => {
|
|
@@ -1137,7 +1266,7 @@ var preview = async (args) => {
|
|
|
1137
1266
|
const project = await getProject(apiUrl, token, manifest.uuid);
|
|
1138
1267
|
const { entries, warnings } = collectEntries(root, manifest, project);
|
|
1139
1268
|
note("Compiling…");
|
|
1140
|
-
const js = await
|
|
1269
|
+
const js = await compileProject2({
|
|
1141
1270
|
entries,
|
|
1142
1271
|
name: manifest.name,
|
|
1143
1272
|
publicUrl: flagStr(args, "public-url") ?? apiUrl,
|
|
@@ -1150,7 +1279,7 @@ var preview = async (args) => {
|
|
|
1150
1279
|
if (out && isNullSink(out)) {
|
|
1151
1280
|
note(`Discarded output (${out})`);
|
|
1152
1281
|
} else if (out) {
|
|
1153
|
-
|
|
1282
|
+
writeFileSync7(resolve3(out), js);
|
|
1154
1283
|
note(`Wrote ${out} (${js.length} bytes)`);
|
|
1155
1284
|
} else {
|
|
1156
1285
|
process.stdout.write(js);
|
|
@@ -1162,8 +1291,8 @@ var preview = async (args) => {
|
|
|
1162
1291
|
};
|
|
1163
1292
|
|
|
1164
1293
|
// src/commands/render.ts
|
|
1165
|
-
import { writeFileSync as
|
|
1166
|
-
import { resolve as
|
|
1294
|
+
import { writeFileSync as writeFileSync8 } from "node:fs";
|
|
1295
|
+
import { resolve as resolve4 } from "node:path";
|
|
1167
1296
|
var DEVICE_SAFE_AREA = {
|
|
1168
1297
|
none: [0, 0, 0, 0],
|
|
1169
1298
|
iphone: [47, 0, 34, 0],
|
|
@@ -1202,7 +1331,7 @@ var render = async (args) => {
|
|
|
1202
1331
|
const project = await getProject(apiUrl, token, manifest.uuid);
|
|
1203
1332
|
const { entries, warnings } = collectEntries(root, manifest, project);
|
|
1204
1333
|
note("Compiling…");
|
|
1205
|
-
const js = await
|
|
1334
|
+
const js = await compileProject2({
|
|
1206
1335
|
entries,
|
|
1207
1336
|
name: manifest.name,
|
|
1208
1337
|
publicUrl: flagStr(args, "public-url") ?? apiUrl,
|
|
@@ -1250,7 +1379,7 @@ var render = async (args) => {
|
|
|
1250
1379
|
if (isNullSink(pngFlag)) {
|
|
1251
1380
|
note(`Discarded PNG (${pngFlag})`);
|
|
1252
1381
|
} else {
|
|
1253
|
-
|
|
1382
|
+
writeFileSync8(resolve4(pngFlag), png);
|
|
1254
1383
|
note(`Wrote ${pngFlag} (${png.length} bytes, ${width}x${height})`);
|
|
1255
1384
|
}
|
|
1256
1385
|
return done((cb) => cb());
|
|
@@ -1265,7 +1394,7 @@ var render = async (args) => {
|
|
|
1265
1394
|
return done((cb) => cb());
|
|
1266
1395
|
}
|
|
1267
1396
|
if (out) {
|
|
1268
|
-
|
|
1397
|
+
writeFileSync8(resolve4(out), result.text);
|
|
1269
1398
|
note(`Wrote ${out} (${result.text.length} bytes)`);
|
|
1270
1399
|
return done((cb) => cb());
|
|
1271
1400
|
}
|
|
@@ -1276,8 +1405,8 @@ var render = async (args) => {
|
|
|
1276
1405
|
};
|
|
1277
1406
|
|
|
1278
1407
|
// src/commands/pull.ts
|
|
1279
|
-
import { existsSync as
|
|
1280
|
-
import { dirname as
|
|
1408
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync7, rmSync, writeFileSync as writeFileSync9 } from "node:fs";
|
|
1409
|
+
import { dirname as dirname6, join as join10 } from "node:path";
|
|
1281
1410
|
var pull = async (args) => {
|
|
1282
1411
|
const root = findProjectRoot(process.cwd());
|
|
1283
1412
|
const manifest = readManifest(root);
|
|
@@ -1297,8 +1426,8 @@ var pull = async (args) => {
|
|
|
1297
1426
|
const remotePaths = new Set([...buildPaths(project.assets).entries()].filter(([id]) => project.assets.find((a) => a.id === id)?.type !== "folder").map(([, path]) => path));
|
|
1298
1427
|
for (const path of Object.keys(manifest.files)) {
|
|
1299
1428
|
if (!remotePaths.has(path)) {
|
|
1300
|
-
const abs =
|
|
1301
|
-
if (
|
|
1429
|
+
const abs = join10(root, ...path.replace(/^\//, "").split("/"));
|
|
1430
|
+
if (existsSync7(abs))
|
|
1302
1431
|
rmSync(abs);
|
|
1303
1432
|
}
|
|
1304
1433
|
}
|
|
@@ -1336,18 +1465,18 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
1336
1465
|
const files = { ...manifest.files };
|
|
1337
1466
|
for (const asset of targets) {
|
|
1338
1467
|
const key = pathById.get(asset.id);
|
|
1339
|
-
const abs =
|
|
1340
|
-
|
|
1468
|
+
const abs = join10(root, ...key.replace(/^\//, "").split("/"));
|
|
1469
|
+
mkdirSync7(dirname6(abs), { recursive: true });
|
|
1341
1470
|
let sha;
|
|
1342
1471
|
if (asset.type === "file") {
|
|
1343
1472
|
if (!asset.file?.src)
|
|
1344
1473
|
continue;
|
|
1345
1474
|
const bytes = await downloadBlob(apiUrl, asset.file.src);
|
|
1346
|
-
|
|
1475
|
+
writeFileSync9(abs, bytes);
|
|
1347
1476
|
sha = sha256(bytes);
|
|
1348
1477
|
} else {
|
|
1349
1478
|
const text = asset.text ?? "";
|
|
1350
|
-
|
|
1479
|
+
writeFileSync9(abs, text);
|
|
1351
1480
|
sha = sha256(text);
|
|
1352
1481
|
}
|
|
1353
1482
|
files[key] = { id: asset.id, type: kindOf2(asset.type), sha };
|
|
@@ -1360,7 +1489,7 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
1360
1489
|
|
|
1361
1490
|
// src/commands/push.ts
|
|
1362
1491
|
import { basename as basename3 } from "node:path";
|
|
1363
|
-
import { readFileSync as
|
|
1492
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
1364
1493
|
var push = async (args) => {
|
|
1365
1494
|
const root = findProjectRoot(process.cwd());
|
|
1366
1495
|
const manifest = readManifest(root);
|
|
@@ -1385,6 +1514,12 @@ var push = async (args) => {
|
|
|
1385
1514
|
if (commits.headId !== manifest.baseHeadId) {
|
|
1386
1515
|
throw new CliError("The project changed on the server since your last sync. Run `lecodes diff` to compare, `lecodes pull` to take the server's version, or `lecodes push --force` to overwrite.");
|
|
1387
1516
|
}
|
|
1517
|
+
if (commits.dirty) {
|
|
1518
|
+
const drift = serverPathDrift(await getProject(apiUrl, token, manifest.uuid), manifest);
|
|
1519
|
+
if (drift) {
|
|
1520
|
+
throw new CliError(`The project has uncommitted changes in the web editor your checkout doesn't have (${drift}). Run \`lecodes pull\` to take them, or \`lecodes push --force\` to overwrite the server.`);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1388
1523
|
}
|
|
1389
1524
|
if (force) {
|
|
1390
1525
|
const project = await getProject(apiUrl, token, manifest.uuid);
|
|
@@ -1413,9 +1548,9 @@ var push = async (args) => {
|
|
|
1413
1548
|
if (source)
|
|
1414
1549
|
entry.assetId = source.id;
|
|
1415
1550
|
if (isTextKind(kind)) {
|
|
1416
|
-
entry.text =
|
|
1551
|
+
entry.text = readFileSync8(file.absPath, "utf8");
|
|
1417
1552
|
} else if (!source || source.sha !== file.sha) {
|
|
1418
|
-
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename3(file.path),
|
|
1553
|
+
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename3(file.path), readFileSync8(file.absPath));
|
|
1419
1554
|
entry.blobFileId = blob.id;
|
|
1420
1555
|
uploaded++;
|
|
1421
1556
|
}
|
|
@@ -1447,6 +1582,19 @@ var push = async (args) => {
|
|
|
1447
1582
|
if (publish)
|
|
1448
1583
|
reportPublish(res);
|
|
1449
1584
|
};
|
|
1585
|
+
var serverPathDrift = (project, manifest) => {
|
|
1586
|
+
const pathById = buildPaths(project.assets);
|
|
1587
|
+
const serverPaths = new Set(project.assets.filter((a) => a.type !== "folder").map((a) => pathById.get(a.id)));
|
|
1588
|
+
const manifestPaths = new Set(Object.keys(manifest.files));
|
|
1589
|
+
const added = [...serverPaths].filter((p) => !manifestPaths.has(p));
|
|
1590
|
+
const removed = [...manifestPaths].filter((p) => !serverPaths.has(p));
|
|
1591
|
+
const describe = (label, paths) => `${paths.length} ${label} on the server: ${paths.slice(0, 3).join(", ")}${paths.length > 3 ? ", …" : ""}`;
|
|
1592
|
+
const parts = [
|
|
1593
|
+
added.length ? describe("added", added) : "",
|
|
1594
|
+
removed.length ? describe("removed", removed) : ""
|
|
1595
|
+
].filter(Boolean);
|
|
1596
|
+
return parts.length ? parts.join("; ") : null;
|
|
1597
|
+
};
|
|
1450
1598
|
var reportPublish = (res) => {
|
|
1451
1599
|
if (res.published)
|
|
1452
1600
|
success("Published — the project is now live.");
|
|
@@ -1493,6 +1641,9 @@ ${c.bold("Commands:")}
|
|
|
1493
1641
|
--token <pat> store a token from the web UI instead
|
|
1494
1642
|
--api <url> API origin (default https://le.codes)
|
|
1495
1643
|
--web <url> app origin for the browser flow
|
|
1644
|
+
create <name> [dir] Create a new project on the server and scaffold it locally
|
|
1645
|
+
(types from the server + tsconfig + a starter main.ts)
|
|
1646
|
+
--team <uuid> create it inside a team
|
|
1496
1647
|
clone <uuid|url> [dir] Download a project's files + types into a new folder
|
|
1497
1648
|
--no-types skip the .d.ts / tsconfig generation
|
|
1498
1649
|
status Show local changes vs the last sync
|
|
@@ -1525,11 +1676,11 @@ ${c.bold("Commands:")}
|
|
|
1525
1676
|
--logs forward the app's console output (to stderr)
|
|
1526
1677
|
|
|
1527
1678
|
Config lives in ~/.lecodes/config.json (override with LECODES_API / LECODES_TOKEN).`;
|
|
1528
|
-
var commands = { login, clone, status, diff, pull, push, preview, render };
|
|
1679
|
+
var commands = { login, create, clone, status, diff, pull, push, preview, render };
|
|
1529
1680
|
var readVersion = () => {
|
|
1530
1681
|
try {
|
|
1531
|
-
const
|
|
1532
|
-
return JSON.parse(
|
|
1682
|
+
const here2 = dirname7(fileURLToPath3(import.meta.url));
|
|
1683
|
+
return JSON.parse(readFileSync9(join11(here2, "..", "package.json"), "utf8")).version ?? "unknown";
|
|
1533
1684
|
} catch {
|
|
1534
1685
|
return "unknown";
|
|
1535
1686
|
}
|