lecodes-cli 0.17.1 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1340 -648
- package/package.json +3 -3
- package/runtime/scene-harness.json +1 -1
- package/runtime/sdk/compile/bundler.ts +1 -1
- package/runtime/sdk/compile/compileProject.ts +16 -1
- package/runtime/sdk/compile/header.ts +6 -1
- package/runtime/sdk/compile/index.ts +16 -0
- package/runtime/sdk/compile/liteMaterial.ts +247 -0
- package/runtime/sdk/compile/shaderTargets.ts +42 -0
- package/runtime/sdk/core/Aspect.ts +255 -244
- package/runtime/sdk/g2/CharacterController2D.ts +2 -2
- package/runtime/sdk/gl/Camera.ts +41 -0
- package/runtime/sdk/gl/CameraPlace.ts +51 -0
- package/runtime/sdk/gl/CharacterController.ts +6 -2
- package/runtime/sdk/gl/IK.ts +193 -174
- package/runtime/sdk/gl/Material.ts +23 -0
- package/runtime/sdk/gl/Model.ts +7 -6
- package/runtime/sdk/gl/Node.ts +270 -285
- package/runtime/sdk/gl/Particles.ts +676 -625
- package/runtime/sdk/gl/Physics.ts +53 -1
- package/runtime/sdk/gl/Scene.ts +323 -291
- package/runtime/sdk/gl/Shape.ts +248 -8
- package/runtime/sdk/gl/Trigger.ts +50 -46
- package/runtime/sdk/gl/Vehicle.ts +519 -0
- package/runtime/sdk/gl/{AnimationClip.ts → animation/AnimationClip.ts} +37 -7
- package/runtime/sdk/gl/animation/Animator.ts +87 -0
- package/runtime/sdk/gl/animation/Layer.ts +29 -0
- package/runtime/sdk/gl/animation/Loop.ts +25 -0
- package/runtime/sdk/gl/animation/Playback.ts +43 -0
- package/runtime/sdk/gl/animation/core.ts +294 -0
- package/runtime/sdk/gl/scenarios.ts +26 -58
- package/runtime/sdk/inject.ts +18 -9
- package/runtime/sdk/runtime/app.ts +13 -0
- package/runtime/sdk/runtime/input.ts +169 -6
- package/runtime/sdk/scene/defineScene.ts +182 -26
- package/runtime/sdk/scene/gizmos.ts +128 -0
- package/runtime/sdk-types.json +1 -1
- package/runtime/sdk/gl/Animator.ts +0 -642
- package/runtime/sdk/gl/ModelAnimation.ts +0 -95
package/dist/index.js
CHANGED
|
@@ -171459,6 +171459,264 @@ var init_detectEntry = __esm(() => {
|
|
|
171459
171459
|
EXPORT_RE = /(?:^|[\n;])\s*export\b/;
|
|
171460
171460
|
});
|
|
171461
171461
|
|
|
171462
|
+
// ../sdk/src/compile/shaderTargets.ts
|
|
171463
|
+
var externalSamplerReg, PLATFORM_BUILD, matcPlatformsFor = (text) => {
|
|
171464
|
+
externalSamplerReg.lastIndex = 0;
|
|
171465
|
+
return externalSamplerReg.test(text) ? ["opengl", "metal", "webgl", "desktop"] : ["opengl", "metal", "desktop"];
|
|
171466
|
+
}, matcArgs = (platform, outPath, inPath) => {
|
|
171467
|
+
const build = PLATFORM_BUILD[platform];
|
|
171468
|
+
return ["-p", build.profile, "--no-essl1", "-a", build.api, "-o", outPath, inPath];
|
|
171469
|
+
};
|
|
171470
|
+
var init_shaderTargets = __esm(() => {
|
|
171471
|
+
externalSamplerReg = /type\s*:\s*samplerExternal/g;
|
|
171472
|
+
PLATFORM_BUILD = {
|
|
171473
|
+
opengl: { profile: "mobile", api: "opengl", replaceExternal: false },
|
|
171474
|
+
metal: { profile: "mobile", api: "metal", replaceExternal: false },
|
|
171475
|
+
webgl: { profile: "mobile", api: "opengl", replaceExternal: true },
|
|
171476
|
+
desktop: { profile: "desktop", api: "opengl", replaceExternal: true }
|
|
171477
|
+
};
|
|
171478
|
+
});
|
|
171479
|
+
|
|
171480
|
+
// ../sdk/src/compile/liteMaterial.ts
|
|
171481
|
+
var extractMatBlocks = (text) => {
|
|
171482
|
+
const blocks = {};
|
|
171483
|
+
let i = 0;
|
|
171484
|
+
const skipNoise = () => {
|
|
171485
|
+
for (;; ) {
|
|
171486
|
+
while (i < text.length && /\s/.test(text[i]))
|
|
171487
|
+
i++;
|
|
171488
|
+
if (text[i] === "/" && text[i + 1] === "/") {
|
|
171489
|
+
while (i < text.length && text[i] !== `
|
|
171490
|
+
`)
|
|
171491
|
+
i++;
|
|
171492
|
+
} else if (text[i] === "/" && text[i + 1] === "*") {
|
|
171493
|
+
i += 2;
|
|
171494
|
+
while (i < text.length && !(text[i] === "*" && text[i + 1] === "/"))
|
|
171495
|
+
i++;
|
|
171496
|
+
i += 2;
|
|
171497
|
+
} else
|
|
171498
|
+
return;
|
|
171499
|
+
}
|
|
171500
|
+
};
|
|
171501
|
+
while (i < text.length) {
|
|
171502
|
+
skipNoise();
|
|
171503
|
+
const m = /^[A-Za-z_][A-Za-z0-9_]*/.exec(text.slice(i));
|
|
171504
|
+
if (!m) {
|
|
171505
|
+
i++;
|
|
171506
|
+
continue;
|
|
171507
|
+
}
|
|
171508
|
+
i += m[0].length;
|
|
171509
|
+
skipNoise();
|
|
171510
|
+
if (text[i] !== "{")
|
|
171511
|
+
continue;
|
|
171512
|
+
let depth = 0;
|
|
171513
|
+
const start = i;
|
|
171514
|
+
while (i < text.length) {
|
|
171515
|
+
const c2 = text[i];
|
|
171516
|
+
if (c2 === "/" && text[i + 1] === "/") {
|
|
171517
|
+
while (i < text.length && text[i] !== `
|
|
171518
|
+
`)
|
|
171519
|
+
i++;
|
|
171520
|
+
} else if (c2 === "/" && text[i + 1] === "*") {
|
|
171521
|
+
i += 2;
|
|
171522
|
+
while (i < text.length && !(text[i] === "*" && text[i + 1] === "/"))
|
|
171523
|
+
i++;
|
|
171524
|
+
i++;
|
|
171525
|
+
} else if (c2 === "{")
|
|
171526
|
+
depth++;
|
|
171527
|
+
else if (c2 === "}") {
|
|
171528
|
+
depth--;
|
|
171529
|
+
if (depth === 0)
|
|
171530
|
+
break;
|
|
171531
|
+
}
|
|
171532
|
+
i++;
|
|
171533
|
+
}
|
|
171534
|
+
blocks[m[0]] = text.slice(start + 1, i);
|
|
171535
|
+
i++;
|
|
171536
|
+
}
|
|
171537
|
+
return blocks;
|
|
171538
|
+
}, parseJsonish = (src) => {
|
|
171539
|
+
let i = 0;
|
|
171540
|
+
const fail = (msg) => {
|
|
171541
|
+
throw new Error(`material block: ${msg} at offset ${i}`);
|
|
171542
|
+
};
|
|
171543
|
+
const skip = () => {
|
|
171544
|
+
for (;; ) {
|
|
171545
|
+
while (i < src.length && /[\s,]/.test(src[i]))
|
|
171546
|
+
i++;
|
|
171547
|
+
if (src[i] === "/" && src[i + 1] === "/") {
|
|
171548
|
+
while (i < src.length && src[i] !== `
|
|
171549
|
+
`)
|
|
171550
|
+
i++;
|
|
171551
|
+
} else if (src[i] === "/" && src[i + 1] === "*") {
|
|
171552
|
+
i += 2;
|
|
171553
|
+
while (i < src.length && !(src[i] === "*" && src[i + 1] === "/"))
|
|
171554
|
+
i++;
|
|
171555
|
+
i += 2;
|
|
171556
|
+
} else
|
|
171557
|
+
return;
|
|
171558
|
+
}
|
|
171559
|
+
};
|
|
171560
|
+
const quoted = (q) => {
|
|
171561
|
+
i++;
|
|
171562
|
+
const start = i;
|
|
171563
|
+
while (i < src.length && src[i] !== q)
|
|
171564
|
+
i++;
|
|
171565
|
+
return src.slice(start, i++);
|
|
171566
|
+
};
|
|
171567
|
+
const word = () => {
|
|
171568
|
+
const m = /^[^\s{}[\]:,="']+/.exec(src.slice(i));
|
|
171569
|
+
if (!m)
|
|
171570
|
+
fail("expected a value");
|
|
171571
|
+
i += m[0].length;
|
|
171572
|
+
return m[0];
|
|
171573
|
+
};
|
|
171574
|
+
const value = () => {
|
|
171575
|
+
skip();
|
|
171576
|
+
const c2 = src[i];
|
|
171577
|
+
if (c2 === "{")
|
|
171578
|
+
return object();
|
|
171579
|
+
if (c2 === "[")
|
|
171580
|
+
return array();
|
|
171581
|
+
if (c2 === '"' || c2 === "'")
|
|
171582
|
+
return quoted(c2);
|
|
171583
|
+
const w = word();
|
|
171584
|
+
if (w === "true")
|
|
171585
|
+
return true;
|
|
171586
|
+
if (w === "false")
|
|
171587
|
+
return false;
|
|
171588
|
+
const n = Number(w);
|
|
171589
|
+
return Number.isNaN(n) ? w : n;
|
|
171590
|
+
};
|
|
171591
|
+
const array = () => {
|
|
171592
|
+
i++;
|
|
171593
|
+
const out = [];
|
|
171594
|
+
for (;; ) {
|
|
171595
|
+
skip();
|
|
171596
|
+
if (i >= src.length)
|
|
171597
|
+
fail("unterminated array");
|
|
171598
|
+
if (src[i] === "]") {
|
|
171599
|
+
i++;
|
|
171600
|
+
return out;
|
|
171601
|
+
}
|
|
171602
|
+
out.push(value());
|
|
171603
|
+
}
|
|
171604
|
+
};
|
|
171605
|
+
const object = () => {
|
|
171606
|
+
i++;
|
|
171607
|
+
const out = {};
|
|
171608
|
+
for (;; ) {
|
|
171609
|
+
skip();
|
|
171610
|
+
if (i >= src.length)
|
|
171611
|
+
fail("unterminated object");
|
|
171612
|
+
if (src[i] === "}") {
|
|
171613
|
+
i++;
|
|
171614
|
+
return out;
|
|
171615
|
+
}
|
|
171616
|
+
const key = src[i] === '"' || src[i] === "'" ? quoted(src[i]) : word();
|
|
171617
|
+
skip();
|
|
171618
|
+
if (src[i] !== ":" && src[i] !== "=")
|
|
171619
|
+
fail(`expected ':' after key "${key}"`);
|
|
171620
|
+
i++;
|
|
171621
|
+
out[key] = value();
|
|
171622
|
+
}
|
|
171623
|
+
};
|
|
171624
|
+
skip();
|
|
171625
|
+
if (src[i] !== "{")
|
|
171626
|
+
fail("expected '{'");
|
|
171627
|
+
return object();
|
|
171628
|
+
}, PARAM_TYPES, SAMPLER_TYPES, unsupported = (reason) => ({
|
|
171629
|
+
lecodesLiteMaterial: 1,
|
|
171630
|
+
unsupported: reason,
|
|
171631
|
+
shadingModel: "unlit",
|
|
171632
|
+
blending: "opaque",
|
|
171633
|
+
maskThreshold: 0.4,
|
|
171634
|
+
doubleSided: false,
|
|
171635
|
+
culling: "back",
|
|
171636
|
+
depthWrite: true,
|
|
171637
|
+
shadowMultiplier: false,
|
|
171638
|
+
params: [],
|
|
171639
|
+
samplers: [],
|
|
171640
|
+
fragmentCode: "",
|
|
171641
|
+
writesNormal: false,
|
|
171642
|
+
hasVertexBlock: false
|
|
171643
|
+
}), buildLiteMaterialArtifact = (text) => {
|
|
171644
|
+
try {
|
|
171645
|
+
const blocks = extractMatBlocks(text);
|
|
171646
|
+
if (blocks.material === undefined)
|
|
171647
|
+
return unsupported("no material block");
|
|
171648
|
+
if (blocks.fragment === undefined)
|
|
171649
|
+
return unsupported("no fragment block");
|
|
171650
|
+
const def = parseJsonish("{" + blocks.material + "}");
|
|
171651
|
+
const params = [];
|
|
171652
|
+
const samplers = [];
|
|
171653
|
+
const rawParams = def.parameters === undefined ? [] : Array.isArray(def.parameters) ? def.parameters : [def.parameters];
|
|
171654
|
+
for (const raw of rawParams) {
|
|
171655
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw))
|
|
171656
|
+
return unsupported("malformed parameter entry");
|
|
171657
|
+
const name = String(raw.name ?? "");
|
|
171658
|
+
const typeRaw = String(raw.type ?? "");
|
|
171659
|
+
const typeMatch = /^(\w+)(?:\[(\d+)\])?$/.exec(typeRaw);
|
|
171660
|
+
if (!name || !typeMatch)
|
|
171661
|
+
return unsupported(`malformed parameter "${name || typeRaw}"`);
|
|
171662
|
+
const [, type, size] = typeMatch;
|
|
171663
|
+
if (SAMPLER_TYPES.has(type)) {
|
|
171664
|
+
if (size)
|
|
171665
|
+
return unsupported(`sampler arrays are not supported ("${name}")`);
|
|
171666
|
+
samplers.push({ name });
|
|
171667
|
+
} else if (PARAM_TYPES.has(type)) {
|
|
171668
|
+
params.push(size ? { name, type, size: Number(size) } : { name, type });
|
|
171669
|
+
} else {
|
|
171670
|
+
return unsupported(`parameter type "${typeRaw}" ("${name}")`);
|
|
171671
|
+
}
|
|
171672
|
+
}
|
|
171673
|
+
const shadingModel = def.shadingModel === "unlit" ? "unlit" : "lit";
|
|
171674
|
+
const blendingRaw = String(def.blending ?? "opaque");
|
|
171675
|
+
const blending = blendingRaw === "transparent" || blendingRaw === "fade" || blendingRaw === "add" || blendingRaw === "masked" ? blendingRaw : blendingRaw === "multiply" || blendingRaw === "screen" ? "transparent" : "opaque";
|
|
171676
|
+
const culling = def.culling === "none" ? "none" : def.culling === "front" ? "front" : "back";
|
|
171677
|
+
const depthWrite = typeof def.depthWrite === "boolean" ? def.depthWrite : blending !== "transparent" && blending !== "fade" && blending !== "add";
|
|
171678
|
+
const fragmentCode = blocks.fragment.replace(/\bmaterialParams\./g, "materialParams_");
|
|
171679
|
+
if (!/\bvoid\s+material\s*\(/.test(fragmentCode))
|
|
171680
|
+
return unsupported("fragment block has no material() function");
|
|
171681
|
+
return {
|
|
171682
|
+
lecodesLiteMaterial: 1,
|
|
171683
|
+
shadingModel,
|
|
171684
|
+
blending,
|
|
171685
|
+
maskThreshold: typeof def.maskThreshold === "number" ? def.maskThreshold : 0.4,
|
|
171686
|
+
doubleSided: def.doubleSided === true,
|
|
171687
|
+
culling,
|
|
171688
|
+
depthWrite,
|
|
171689
|
+
shadowMultiplier: def.shadowMultiplier === true,
|
|
171690
|
+
params,
|
|
171691
|
+
samplers,
|
|
171692
|
+
fragmentCode,
|
|
171693
|
+
writesNormal: /\.normal\s*=/.test(fragmentCode),
|
|
171694
|
+
hasVertexBlock: blocks.vertex !== undefined
|
|
171695
|
+
};
|
|
171696
|
+
} catch (e) {
|
|
171697
|
+
return unsupported(e instanceof Error ? e.message : String(e));
|
|
171698
|
+
}
|
|
171699
|
+
};
|
|
171700
|
+
var init_liteMaterial = __esm(() => {
|
|
171701
|
+
PARAM_TYPES = new Set([
|
|
171702
|
+
"float",
|
|
171703
|
+
"float2",
|
|
171704
|
+
"float3",
|
|
171705
|
+
"float4",
|
|
171706
|
+
"int",
|
|
171707
|
+
"int2",
|
|
171708
|
+
"int3",
|
|
171709
|
+
"int4",
|
|
171710
|
+
"bool",
|
|
171711
|
+
"bool2",
|
|
171712
|
+
"bool3",
|
|
171713
|
+
"bool4",
|
|
171714
|
+
"float3x3",
|
|
171715
|
+
"float4x4"
|
|
171716
|
+
]);
|
|
171717
|
+
SAMPLER_TYPES = new Set(["sampler2d", "samplerExternal"]);
|
|
171718
|
+
});
|
|
171719
|
+
|
|
171462
171720
|
// ../sdk/src/compile/sourcemap.ts
|
|
171463
171721
|
var offsetSourceMap = (mapJson, lines) => {
|
|
171464
171722
|
if (lines <= 0)
|
|
@@ -171494,7 +171752,10 @@ var buildHeader = (name, code, opts = {}) => {
|
|
|
171494
171752
|
`;
|
|
171495
171753
|
const localAssets = new Set;
|
|
171496
171754
|
for (const m of code.matchAll(/_creatorUtils\.fetchLocal\((.+?)\)/g)) {
|
|
171497
|
-
|
|
171755
|
+
const arg = m[1].trim();
|
|
171756
|
+
if (!/^(['"])[^'"]*\1$/.test(arg))
|
|
171757
|
+
continue;
|
|
171758
|
+
localAssets.add(arg.slice(1, -1).trim());
|
|
171498
171759
|
}
|
|
171499
171760
|
if (/_creator\.createGlb/.test(code))
|
|
171500
171761
|
localAssets.add("uberarchive");
|
|
@@ -171540,7 +171801,11 @@ var compileProject = async (opts) => {
|
|
|
171540
171801
|
}
|
|
171541
171802
|
} else if (e.type === "shader") {
|
|
171542
171803
|
const shaders = e.shaders ?? [];
|
|
171543
|
-
if (shaders.length >
|
|
171804
|
+
if (e.shadersLocal && shaders.length > 0) {
|
|
171805
|
+
const name = shaders[0].src.split(/[\\/]/).pop() ?? "";
|
|
171806
|
+
const [prefix, suffix = ".filamat"] = name.split("_opengl");
|
|
171807
|
+
files[e.path] = `export default ("id:" + _creatorUtils.fetchLocal(${JSON.stringify(prefix + "_")} + _creator.backend + ${JSON.stringify(suffix)}))`;
|
|
171808
|
+
} else if (shaders.length > 2) {
|
|
171544
171809
|
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.subBackend ?? _creator.backend}")}\``;
|
|
171545
171810
|
} else if (shaders.length > 0) {
|
|
171546
171811
|
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.backend}")}\``;
|
|
@@ -172479,7 +172744,10 @@ __export(exports_compile, {
|
|
|
172479
172744
|
sanitizeAssetName: () => sanitizeAssetName,
|
|
172480
172745
|
rewriteAssetIcons: () => rewriteAssetIcons,
|
|
172481
172746
|
parseSfnt: () => parseSfnt,
|
|
172747
|
+
parseJsonish: () => parseJsonish,
|
|
172482
172748
|
offsetSourceMap: () => offsetSourceMap,
|
|
172749
|
+
matcPlatformsFor: () => matcPlatformsFor,
|
|
172750
|
+
matcArgs: () => matcArgs,
|
|
172483
172751
|
loadSceneHarness: () => loadSceneHarness,
|
|
172484
172752
|
isServerPath: () => isServerPath,
|
|
172485
172753
|
isCanonicalAssetName: () => isCanonicalAssetName,
|
|
@@ -172487,6 +172755,8 @@ __export(exports_compile, {
|
|
|
172487
172755
|
iconNotFoundError: () => iconNotFoundError,
|
|
172488
172756
|
fontRegistryBase: () => fontRegistryBase,
|
|
172489
172757
|
faceFileName: () => faceFileName,
|
|
172758
|
+
extractMatBlocks: () => extractMatBlocks,
|
|
172759
|
+
externalSamplerReg: () => externalSamplerReg,
|
|
172490
172760
|
detectProjectKind: () => detectProjectKind,
|
|
172491
172761
|
detectEntry: () => detectEntry,
|
|
172492
172762
|
deriveEndpointParams: () => deriveEndpointParams,
|
|
@@ -172495,6 +172765,7 @@ __export(exports_compile, {
|
|
|
172495
172765
|
collectVendoredIcons: () => collectVendoredIcons,
|
|
172496
172766
|
bundleProjectWithMap: () => bundleProjectWithMap,
|
|
172497
172767
|
bundleProject: () => bundleProject,
|
|
172768
|
+
buildLiteMaterialArtifact: () => buildLiteMaterialArtifact,
|
|
172498
172769
|
buildIconsHeaderValue: () => buildIconsHeaderValue,
|
|
172499
172770
|
buildHeader: () => buildHeader,
|
|
172500
172771
|
buildFontsHeaderValue: () => buildFontsHeaderValue,
|
|
@@ -172504,6 +172775,7 @@ __export(exports_compile, {
|
|
|
172504
172775
|
SERVER_ENTRY_PATH: () => SERVER_ENTRY_PATH,
|
|
172505
172776
|
SCENE_HARNESS_DIR: () => SCENE_HARNESS_DIR,
|
|
172506
172777
|
SCENE_ENTRY_PATH: () => SCENE_ENTRY_PATH,
|
|
172778
|
+
PLATFORM_BUILD: () => PLATFORM_BUILD,
|
|
172507
172779
|
ICON_ID: () => ICON_ID,
|
|
172508
172780
|
FONT_REGISTRY: () => FONT_REGISTRY,
|
|
172509
172781
|
EndpointTypeError: () => EndpointTypeError,
|
|
@@ -172517,6 +172789,8 @@ var init_compile = __esm(() => {
|
|
|
172517
172789
|
init_serverTypes();
|
|
172518
172790
|
init_detectEntry();
|
|
172519
172791
|
init_assetName();
|
|
172792
|
+
init_shaderTargets();
|
|
172793
|
+
init_liteMaterial();
|
|
172520
172794
|
init_compileProject();
|
|
172521
172795
|
init_sceneEditor();
|
|
172522
172796
|
init_fontMacro();
|
|
@@ -172544,7 +172818,7 @@ var vendoredInject, resolveChiselBin2 = () => {
|
|
|
172544
172818
|
return null;
|
|
172545
172819
|
return `Ambiguous entrypoint: nothing imports ${roots.join(", ")} — picked ${entry}. Rename your entry to main.ts (or pass --entry).`;
|
|
172546
172820
|
}, compileProject2 = (opts) => compileProject({
|
|
172547
|
-
entries: opts.entries.map((e) => ({ path: e.path, type: e.type, text: e.text, fileSrc: e.fileSrc, remote: e.remote, shaders: e.shaders })),
|
|
172821
|
+
entries: opts.entries.map((e) => ({ path: e.path, type: e.type, text: e.text, fileSrc: e.fileSrc, remote: e.remote, shaders: e.shaders, shadersLocal: e.shadersLocal })),
|
|
172548
172822
|
name: opts.name,
|
|
172549
172823
|
publicUrl: opts.publicUrl,
|
|
172550
172824
|
entryOverride: opts.entryOverride,
|
|
@@ -172682,11 +172956,11 @@ var init_fonts = __esm(() => {
|
|
|
172682
172956
|
});
|
|
172683
172957
|
|
|
172684
172958
|
// src/compile/assetIcons.ts
|
|
172685
|
-
import { createHash as
|
|
172686
|
-
import { existsSync as
|
|
172687
|
-
import { homedir as
|
|
172688
|
-
import { dirname as dirname7, join as
|
|
172689
|
-
var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) =>
|
|
172959
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
172960
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "node:fs";
|
|
172961
|
+
import { homedir as homedir3 } from "node:os";
|
|
172962
|
+
import { dirname as dirname7, join as join14 } from "node:path";
|
|
172963
|
+
var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => createHash5("sha256").update(s).digest("hex").slice(0, 12), readText = async (registry, rel) => {
|
|
172690
172964
|
if (isHttp2(registry)) {
|
|
172691
172965
|
try {
|
|
172692
172966
|
const res = await fetch(`${registry}/${rel}`);
|
|
@@ -172696,21 +172970,21 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
172696
172970
|
}
|
|
172697
172971
|
}
|
|
172698
172972
|
try {
|
|
172699
|
-
return
|
|
172973
|
+
return readFileSync11(join14(registry, rel), "utf8");
|
|
172700
172974
|
} catch {
|
|
172701
172975
|
return null;
|
|
172702
172976
|
}
|
|
172703
172977
|
}, safeRead = (path) => {
|
|
172704
172978
|
try {
|
|
172705
|
-
return
|
|
172979
|
+
return readFileSync11(path, "utf8");
|
|
172706
172980
|
} catch {
|
|
172707
172981
|
return null;
|
|
172708
172982
|
}
|
|
172709
172983
|
}, writeIfChanged2 = (path, text) => {
|
|
172710
|
-
const current =
|
|
172984
|
+
const current = existsSync12(path) ? safeRead(path) : null;
|
|
172711
172985
|
if (current === text)
|
|
172712
172986
|
return false;
|
|
172713
|
-
|
|
172987
|
+
writeFileSync8(path, text);
|
|
172714
172988
|
return true;
|
|
172715
172989
|
}, fetchIcon = async (registry, pack, name) => {
|
|
172716
172990
|
const svg = await readText(registry, `${pack}/${name}.svg`);
|
|
@@ -172724,7 +172998,7 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
172724
172998
|
return JSON.parse(raw);
|
|
172725
172999
|
}, readLock2 = (path) => {
|
|
172726
173000
|
try {
|
|
172727
|
-
const lock = JSON.parse(
|
|
173001
|
+
const lock = JSON.parse(readFileSync11(path, "utf8"));
|
|
172728
173002
|
if (lock && typeof lock === "object" && lock.icons && lock.packs)
|
|
172729
173003
|
return lock;
|
|
172730
173004
|
} catch {}
|
|
@@ -172759,13 +173033,13 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
172759
173033
|
`);
|
|
172760
173034
|
}, vendorIcons = async (designDir, ids, opts = {}) => {
|
|
172761
173035
|
const registry = (opts.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
|
|
172762
|
-
const lockPath =
|
|
173036
|
+
const lockPath = join14(designDir, "icons.lock.json");
|
|
172763
173037
|
const lock = readLock2(lockPath) ?? { registry, icons: {}, packs: {} };
|
|
172764
173038
|
const svgById = new Map;
|
|
172765
173039
|
const missing = [];
|
|
172766
173040
|
for (const id of ids) {
|
|
172767
173041
|
const li = lock.icons[id];
|
|
172768
|
-
const cached = li && safeRead(
|
|
173042
|
+
const cached = li && safeRead(join14(designDir, li.path));
|
|
172769
173043
|
if (cached)
|
|
172770
173044
|
svgById.set(id, cached);
|
|
172771
173045
|
else
|
|
@@ -172781,9 +173055,9 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
172781
173055
|
throw new Error(`assetIcon: unknown icon pack "${pack}" (in "${id}"). Available packs: ${Object.keys(licenses).sort().join(", ")}.`);
|
|
172782
173056
|
const svg = await fetchIcon(registry, pack, name);
|
|
172783
173057
|
const rel = `assets/icons/${pack}/${name}.svg`;
|
|
172784
|
-
const abs =
|
|
172785
|
-
|
|
172786
|
-
|
|
173058
|
+
const abs = join14(designDir, rel);
|
|
173059
|
+
mkdirSync7(dirname7(abs), { recursive: true });
|
|
173060
|
+
writeFileSync8(abs, svg);
|
|
172787
173061
|
lock.icons[id] = { pack, name, hash: sha122(svg), spdx: lic.spdx, path: rel };
|
|
172788
173062
|
lock.packs[pack] = { spdx: lic.spdx, title: lic.title, url: lic.url, attribution: lic.attribution, modifiedNotice: lic.modifiedNotice };
|
|
172789
173063
|
svgById.set(id, svg);
|
|
@@ -172792,25 +173066,25 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
172792
173066
|
writeIfChanged2(lockPath, serializeLock2(lock));
|
|
172793
173067
|
}
|
|
172794
173068
|
if (Object.keys(lock.icons).length)
|
|
172795
|
-
writeIfChanged2(
|
|
173069
|
+
writeIfChanged2(join14(designDir, "ICON-NOTICES.md"), renderNotices2(lock));
|
|
172796
173070
|
return svgById;
|
|
172797
173071
|
}, applyAssetIcons = async (entries, designDir, opts = {}) => {
|
|
172798
173072
|
await applyAssetIconMacro(entries, (ids) => vendorIcons(designDir, ids, opts));
|
|
172799
|
-
}, defaultCacheDir = () =>
|
|
173073
|
+
}, defaultCacheDir = () => join14(homedir3(), ".lecodes", "cache", "icons"), cacheIcons = async (ids, opts = {}) => {
|
|
172800
173074
|
const registry = (opts.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
|
|
172801
173075
|
const cacheDir = opts.cacheDir ?? defaultCacheDir();
|
|
172802
173076
|
const svgById = new Map;
|
|
172803
173077
|
for (const id of ids) {
|
|
172804
173078
|
const [, pack, name] = ICON_ID.exec(id);
|
|
172805
|
-
const cachePath =
|
|
173079
|
+
const cachePath = join14(cacheDir, pack, `${name}.svg`);
|
|
172806
173080
|
const cached = safeRead(cachePath);
|
|
172807
173081
|
if (cached) {
|
|
172808
173082
|
svgById.set(id, cached);
|
|
172809
173083
|
continue;
|
|
172810
173084
|
}
|
|
172811
173085
|
const svg = await fetchIcon(registry, pack, name);
|
|
172812
|
-
|
|
172813
|
-
|
|
173086
|
+
mkdirSync7(dirname7(cachePath), { recursive: true });
|
|
173087
|
+
writeFileSync8(cachePath, svg);
|
|
172814
173088
|
svgById.set(id, svg);
|
|
172815
173089
|
}
|
|
172816
173090
|
return svgById;
|
|
@@ -177889,7 +178163,7 @@ var require_image_q = __commonJS((exports, module) => {
|
|
|
177889
178163
|
colors,
|
|
177890
178164
|
onProgress
|
|
177891
178165
|
} = {}) {
|
|
177892
|
-
return new Promise((
|
|
178166
|
+
return new Promise((resolve4, reject) => {
|
|
177893
178167
|
const distanceCalculator = colorDistanceFormulaToColorDistance(colorDistanceFormula);
|
|
177894
178168
|
const paletteQuantizer = paletteQuantizationToPaletteQuantizer(distanceCalculator, paletteQuantization, colors);
|
|
177895
178169
|
images.forEach((image2) => paletteQuantizer.sample(image2));
|
|
@@ -177899,7 +178173,7 @@ var require_image_q = __commonJS((exports, module) => {
|
|
|
177899
178173
|
try {
|
|
177900
178174
|
const result = iterator.next();
|
|
177901
178175
|
if (result.done) {
|
|
177902
|
-
|
|
178176
|
+
resolve4(palette2);
|
|
177903
178177
|
} else {
|
|
177904
178178
|
if (result.value.palette)
|
|
177905
178179
|
palette2 = result.value.palette;
|
|
@@ -177924,7 +178198,7 @@ var require_image_q = __commonJS((exports, module) => {
|
|
|
177924
178198
|
imageQuantization,
|
|
177925
178199
|
onProgress
|
|
177926
178200
|
} = {}) {
|
|
177927
|
-
return new Promise((
|
|
178201
|
+
return new Promise((resolve4, reject) => {
|
|
177928
178202
|
const distanceCalculator = colorDistanceFormulaToColorDistance(colorDistanceFormula);
|
|
177929
178203
|
const imageQuantizer = imageQuantizationToImageQuantizer(distanceCalculator, imageQuantization);
|
|
177930
178204
|
let outPointContainer;
|
|
@@ -177933,7 +178207,7 @@ var require_image_q = __commonJS((exports, module) => {
|
|
|
177933
178207
|
try {
|
|
177934
178208
|
const result = iterator.next();
|
|
177935
178209
|
if (result.done) {
|
|
177936
|
-
|
|
178210
|
+
resolve4(outPointContainer);
|
|
177937
178211
|
} else {
|
|
177938
178212
|
if (result.value.pointContainer) {
|
|
177939
178213
|
outPointContainer = result.value.pointContainer;
|
|
@@ -178267,22 +178541,22 @@ var require_gifutil = __commonJS((exports) => {
|
|
|
178267
178541
|
}
|
|
178268
178542
|
}
|
|
178269
178543
|
function _readBinary(path) {
|
|
178270
|
-
return new Promise((
|
|
178544
|
+
return new Promise((resolve4, reject) => {
|
|
178271
178545
|
fs.readFile(path, (err, buffer) => {
|
|
178272
178546
|
if (err) {
|
|
178273
178547
|
return reject(err);
|
|
178274
178548
|
}
|
|
178275
|
-
return
|
|
178549
|
+
return resolve4(buffer);
|
|
178276
178550
|
});
|
|
178277
178551
|
});
|
|
178278
178552
|
}
|
|
178279
178553
|
function _writeBinary(path, buffer) {
|
|
178280
|
-
return new Promise((
|
|
178554
|
+
return new Promise((resolve4, reject) => {
|
|
178281
178555
|
fs.writeFile(path, buffer, (err) => {
|
|
178282
178556
|
if (err) {
|
|
178283
178557
|
return reject(err);
|
|
178284
178558
|
}
|
|
178285
|
-
return
|
|
178559
|
+
return resolve4();
|
|
178286
178560
|
});
|
|
178287
178561
|
});
|
|
178288
178562
|
}
|
|
@@ -195848,7 +196122,7 @@ var require_await_to_js_umd = __commonJS((exports, module) => {
|
|
|
195848
196122
|
|
|
195849
196123
|
// ../../node_modules/.bun/@jimp+file-ops@1.6.1/node_modules/@jimp/file-ops/dist/esm/index.js
|
|
195850
196124
|
import { promises as fs } from "fs";
|
|
195851
|
-
import { existsSync as
|
|
196125
|
+
import { existsSync as existsSync14 } from "fs";
|
|
195852
196126
|
var readFile, writeFile;
|
|
195853
196127
|
var init_esm13 = __esm(() => {
|
|
195854
196128
|
readFile = fs.readFile;
|
|
@@ -197515,9 +197789,9 @@ class Deferred {
|
|
|
197515
197789
|
constructor() {
|
|
197516
197790
|
this.resolve = () => null;
|
|
197517
197791
|
this.reject = () => null;
|
|
197518
|
-
this.promise = new Promise((
|
|
197792
|
+
this.promise = new Promise((resolve4, reject) => {
|
|
197519
197793
|
this.reject = reject;
|
|
197520
|
-
this.resolve =
|
|
197794
|
+
this.resolve = resolve4;
|
|
197521
197795
|
});
|
|
197522
197796
|
}
|
|
197523
197797
|
}
|
|
@@ -200209,7 +200483,7 @@ function readByobReaderWithSignal(reader, buffer, signal) {
|
|
|
200209
200483
|
return reader.read(buffer);
|
|
200210
200484
|
}
|
|
200211
200485
|
signal.throwIfAborted();
|
|
200212
|
-
return new Promise((
|
|
200486
|
+
return new Promise((resolve4, reject) => {
|
|
200213
200487
|
const cleanup = () => {
|
|
200214
200488
|
signal.removeEventListener("abort", onAbort);
|
|
200215
200489
|
};
|
|
@@ -200228,7 +200502,7 @@ function readByobReaderWithSignal(reader, buffer, signal) {
|
|
|
200228
200502
|
try {
|
|
200229
200503
|
const result = await reader.read(buffer);
|
|
200230
200504
|
cleanup();
|
|
200231
|
-
|
|
200505
|
+
resolve4(result);
|
|
200232
200506
|
} catch (error) {
|
|
200233
200507
|
cleanup();
|
|
200234
200508
|
reject(error);
|
|
@@ -202120,7 +202394,7 @@ var init_file_type = __esm(() => {
|
|
|
202120
202394
|
const { signal } = this.options;
|
|
202121
202395
|
const normalizedSampleSize = normalizeSampleSize(sampleSize);
|
|
202122
202396
|
signal?.throwIfAborted();
|
|
202123
|
-
return new Promise((
|
|
202397
|
+
return new Promise((resolve4, reject) => {
|
|
202124
202398
|
let isSettled = false;
|
|
202125
202399
|
const cleanup = () => {
|
|
202126
202400
|
readableStream.off("error", onError);
|
|
@@ -202159,7 +202433,7 @@ var init_file_type = __esm(() => {
|
|
|
202159
202433
|
settle(reject, error);
|
|
202160
202434
|
}
|
|
202161
202435
|
}
|
|
202162
|
-
settle(
|
|
202436
|
+
settle(resolve4, outputStream);
|
|
202163
202437
|
} catch (error) {
|
|
202164
202438
|
settle(reject, error);
|
|
202165
202439
|
}
|
|
@@ -202228,7 +202502,7 @@ function createJimp({ plugins: pluginsArg, formats: formatsArg } = {}) {
|
|
|
202228
202502
|
if (Buffer.isBuffer(url) || url instanceof ArrayBuffer) {
|
|
202229
202503
|
return this.fromBuffer(url);
|
|
202230
202504
|
}
|
|
202231
|
-
if (
|
|
202505
|
+
if (existsSync14(url)) {
|
|
202232
202506
|
return this.fromBuffer(await readFile(url));
|
|
202233
202507
|
}
|
|
202234
202508
|
const [fetchErr, response] = await import_await_to_js.to(fetch(url));
|
|
@@ -212491,12 +212765,12 @@ var require_parser3 = __commonJS((exports) => {
|
|
|
212491
212765
|
};
|
|
212492
212766
|
Parser.prototype.parseStringPromise = function(str) {
|
|
212493
212767
|
return new Promise(function(_this) {
|
|
212494
|
-
return function(
|
|
212768
|
+
return function(resolve4, reject) {
|
|
212495
212769
|
return _this.parseString(str, function(err, value) {
|
|
212496
212770
|
if (err) {
|
|
212497
212771
|
return reject(err);
|
|
212498
212772
|
} else {
|
|
212499
|
-
return
|
|
212773
|
+
return resolve4(value);
|
|
212500
212774
|
}
|
|
212501
212775
|
});
|
|
212502
212776
|
};
|
|
@@ -213129,8 +213403,8 @@ var init_load_bitmap_font = __esm(() => {
|
|
|
213129
213403
|
async function loadFont(file) {
|
|
213130
213404
|
let fileOrBuffer = file;
|
|
213131
213405
|
if (typeof window === "undefined" && !isWebWorker) {
|
|
213132
|
-
const { existsSync:
|
|
213133
|
-
if (
|
|
213406
|
+
const { existsSync: existsSync15, promises: fs3 } = await import("fs");
|
|
213407
|
+
if (existsSync15(file)) {
|
|
213134
213408
|
fileOrBuffer = await fs3.readFile(file);
|
|
213135
213409
|
}
|
|
213136
213410
|
}
|
|
@@ -213453,65 +213727,65 @@ var init_esm30 = __esm(() => {
|
|
|
213453
213727
|
});
|
|
213454
213728
|
|
|
213455
213729
|
// src/types.ts
|
|
213456
|
-
import { dirname as dirname13, join as
|
|
213457
|
-
import { existsSync as
|
|
213730
|
+
import { dirname as dirname13, join as join21, relative as relative3, sep } from "node:path";
|
|
213731
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync14, readdirSync as readdirSync9, readFileSync as readFileSync17, rmSync as rmSync7, statSync as statSync7, writeFileSync as writeFileSync16 } from "node:fs";
|
|
213458
213732
|
var TSCONFIG, listBundleFiles = (dir, base = dir) => {
|
|
213459
|
-
if (!
|
|
213733
|
+
if (!existsSync20(dir))
|
|
213460
213734
|
return [];
|
|
213461
213735
|
const out = [];
|
|
213462
|
-
for (const name of
|
|
213463
|
-
const path2 =
|
|
213464
|
-
if (
|
|
213736
|
+
for (const name of readdirSync9(dir)) {
|
|
213737
|
+
const path2 = join21(dir, name);
|
|
213738
|
+
if (statSync7(path2).isDirectory())
|
|
213465
213739
|
out.push(...listBundleFiles(path2, base));
|
|
213466
213740
|
else
|
|
213467
213741
|
out.push(relative3(base, path2).split(sep).join("/"));
|
|
213468
213742
|
}
|
|
213469
213743
|
return out;
|
|
213470
213744
|
}, pruneEmptyDirs = (dir, root) => {
|
|
213471
|
-
if (!
|
|
213745
|
+
if (!existsSync20(dir) || !statSync7(dir).isDirectory())
|
|
213472
213746
|
return;
|
|
213473
|
-
for (const name of
|
|
213474
|
-
pruneEmptyDirs(
|
|
213475
|
-
if (dir !== root &&
|
|
213476
|
-
|
|
213747
|
+
for (const name of readdirSync9(dir))
|
|
213748
|
+
pruneEmptyDirs(join21(dir, name), root);
|
|
213749
|
+
if (dir !== root && readdirSync9(dir).length === 0)
|
|
213750
|
+
rmSync7(dir, { recursive: true });
|
|
213477
213751
|
}, writeTypeBundle = (root, bundle) => {
|
|
213478
|
-
const typesDir =
|
|
213752
|
+
const typesDir = join21(root, LECODES_DIR, "types");
|
|
213479
213753
|
const before = new Set(listBundleFiles(typesDir));
|
|
213480
213754
|
const stats = { added: 0, updated: 0, removed: 0, total: Object.keys(bundle).length };
|
|
213481
213755
|
for (const [path2, text] of Object.entries(bundle)) {
|
|
213482
|
-
const dest =
|
|
213756
|
+
const dest = join21(typesDir, path2);
|
|
213483
213757
|
if (!before.has(path2))
|
|
213484
213758
|
stats.added++;
|
|
213485
|
-
else if (
|
|
213759
|
+
else if (readFileSync17(dest, "utf8") !== text)
|
|
213486
213760
|
stats.updated++;
|
|
213487
213761
|
else
|
|
213488
213762
|
continue;
|
|
213489
|
-
|
|
213490
|
-
|
|
213763
|
+
mkdirSync14(dirname13(dest), { recursive: true });
|
|
213764
|
+
writeFileSync16(dest, text);
|
|
213491
213765
|
}
|
|
213492
213766
|
for (const path2 of before) {
|
|
213493
213767
|
if (path2 in bundle)
|
|
213494
213768
|
continue;
|
|
213495
|
-
|
|
213769
|
+
rmSync7(join21(typesDir, path2));
|
|
213496
213770
|
stats.removed++;
|
|
213497
213771
|
}
|
|
213498
213772
|
pruneEmptyDirs(typesDir, typesDir);
|
|
213499
213773
|
return stats;
|
|
213500
213774
|
}, bundleHash = (bundle) => sha256(Object.keys(bundle).sort().map((k) => `${k}\x00${bundle[k]}`).join("\x00")), readProjectTypeBundle = (root) => {
|
|
213501
|
-
const typesDir =
|
|
213775
|
+
const typesDir = join21(root, LECODES_DIR, "types");
|
|
213502
213776
|
const bundle = {};
|
|
213503
213777
|
for (const path2 of listBundleFiles(typesDir))
|
|
213504
|
-
bundle[path2] =
|
|
213778
|
+
bundle[path2] = readFileSync17(join21(typesDir, path2), "utf8");
|
|
213505
213779
|
return bundle;
|
|
213506
213780
|
}, materializeTypes = async (root, apiUrl) => writeTypeBundle(root, await getTypes(apiUrl)), readLocalTypeBundle = () => {
|
|
213507
213781
|
const candidates = [
|
|
213508
|
-
|
|
213509
|
-
|
|
213782
|
+
join21(distRoot, "runtime", "sdk-types.json"),
|
|
213783
|
+
join21(distRoot, "..", "sdk", "dist", "types.json")
|
|
213510
213784
|
];
|
|
213511
213785
|
for (const path2 of candidates) {
|
|
213512
|
-
if (
|
|
213786
|
+
if (existsSync20(path2)) {
|
|
213513
213787
|
try {
|
|
213514
|
-
return JSON.parse(
|
|
213788
|
+
return JSON.parse(readFileSync17(path2, "utf8"));
|
|
213515
213789
|
} catch {}
|
|
213516
213790
|
}
|
|
213517
213791
|
}
|
|
@@ -213523,7 +213797,7 @@ var TSCONFIG, listBundleFiles = (dir, base = dir) => {
|
|
|
213523
213797
|
writeTypeBundle(root, bundle);
|
|
213524
213798
|
return true;
|
|
213525
213799
|
}, writeTsconfig = (root) => {
|
|
213526
|
-
|
|
213800
|
+
writeFileSync16(join21(root, "tsconfig.json"), JSON.stringify(TSCONFIG, null, 2));
|
|
213527
213801
|
};
|
|
213528
213802
|
var init_types4 = __esm(() => {
|
|
213529
213803
|
init_api();
|
|
@@ -213549,14 +213823,14 @@ var init_types4 = __esm(() => {
|
|
|
213549
213823
|
});
|
|
213550
213824
|
|
|
213551
213825
|
// src/compile/designCompile.ts
|
|
213552
|
-
import { readFileSync as
|
|
213553
|
-
import { basename as
|
|
213826
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
213827
|
+
import { basename as basename8 } from "node:path";
|
|
213554
213828
|
var LOCAL_ASSET_SCHEME = "lecodesfile://", compileDesignScreen = async (opts) => {
|
|
213555
213829
|
const { root, manifest, designDir, designKey, screenId, state } = opts;
|
|
213556
213830
|
const entries = [];
|
|
213557
213831
|
const scan2 = scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest?.files ?? {})) });
|
|
213558
213832
|
for (const file of scan2.files) {
|
|
213559
|
-
const type = manifest?.files[file.path]?.type ?? classifyKind(
|
|
213833
|
+
const type = manifest?.files[file.path]?.type ?? classifyKind(basename8(file.path));
|
|
213560
213834
|
if (type === "resource") {
|
|
213561
213835
|
entries.push({
|
|
213562
213836
|
path: file.path,
|
|
@@ -213567,7 +213841,7 @@ var LOCAL_ASSET_SCHEME = "lecodesfile://", compileDesignScreen = async (opts) =>
|
|
|
213567
213841
|
} else if (type === "shader") {
|
|
213568
213842
|
entries.push({ path: file.path, type, shaders: [] });
|
|
213569
213843
|
} else {
|
|
213570
|
-
entries.push({ path: file.path, type, text:
|
|
213844
|
+
entries.push({ path: file.path, type, text: readFileSync18(file.absPath, "utf8") });
|
|
213571
213845
|
}
|
|
213572
213846
|
}
|
|
213573
213847
|
const invoke = state !== undefined ? `screen(${JSON.stringify(state)})` : "screen()";
|
|
@@ -213613,7 +213887,7 @@ var init_designCompile = __esm(() => {
|
|
|
213613
213887
|
// src/browserAuth.ts
|
|
213614
213888
|
import { createServer } from "node:http";
|
|
213615
213889
|
import { spawn as spawn4 } from "node:child_process";
|
|
213616
|
-
import { randomBytes } from "node:crypto";
|
|
213890
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
213617
213891
|
import { hostname } from "node:os";
|
|
213618
213892
|
var SUCCESS_HTML = `<!doctype html><meta charset="utf-8"><title>lecodes</title>
|
|
213619
213893
|
<body style="font-family:system-ui,sans-serif;background:#0f1115;color:#e6e6e6;display:grid;place-items:center;height:100vh;margin:0">
|
|
@@ -213642,11 +213916,11 @@ var SUCCESS_HTML = `<!doctype html><meta charset="utf-8"><title>lecodes</title>
|
|
|
213642
213916
|
child.unref();
|
|
213643
213917
|
} catch {}
|
|
213644
213918
|
}, browserLogin = async (webBase, timeoutMs = 5 * 60 * 1000, open = openBrowser) => {
|
|
213645
|
-
const state =
|
|
213646
|
-
let
|
|
213919
|
+
const state = randomBytes2(16).toString("hex");
|
|
213920
|
+
let resolve10;
|
|
213647
213921
|
let reject;
|
|
213648
213922
|
const tokenPromise = new Promise((res, rej) => {
|
|
213649
|
-
|
|
213923
|
+
resolve10 = res;
|
|
213650
213924
|
reject = rej;
|
|
213651
213925
|
});
|
|
213652
213926
|
const server2 = createServer((req, res) => {
|
|
@@ -213664,7 +213938,7 @@ var SUCCESS_HTML = `<!doctype html><meta charset="utf-8"><title>lecodes</title>
|
|
|
213664
213938
|
}
|
|
213665
213939
|
res.writeHead(200, { "content-type": "text/html" });
|
|
213666
213940
|
res.end(SUCCESS_HTML);
|
|
213667
|
-
|
|
213941
|
+
resolve10(token);
|
|
213668
213942
|
});
|
|
213669
213943
|
await new Promise((res, rej) => {
|
|
213670
213944
|
server2.once("error", rej);
|
|
@@ -214165,8 +214439,8 @@ __export(exports_design, {
|
|
|
214165
214439
|
flushDesignComments: () => flushDesignComments,
|
|
214166
214440
|
design: () => design
|
|
214167
214441
|
});
|
|
214168
|
-
import { existsSync as
|
|
214169
|
-
import { basename as
|
|
214442
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync17, readFileSync as readFileSync19, readdirSync as readdirSync12, writeFileSync as writeFileSync18, appendFileSync } from "node:fs";
|
|
214443
|
+
import { basename as basename9, join as join23 } from "node:path";
|
|
214170
214444
|
var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
214171
214445
|
let root;
|
|
214172
214446
|
let manifest;
|
|
@@ -214181,13 +214455,13 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214181
214455
|
return {
|
|
214182
214456
|
root,
|
|
214183
214457
|
manifest,
|
|
214184
|
-
name: manifest?.name ??
|
|
214458
|
+
name: manifest?.name ?? basename9(root),
|
|
214185
214459
|
dirName,
|
|
214186
|
-
designDir:
|
|
214460
|
+
designDir: join23(root, dirName),
|
|
214187
214461
|
designKey: "/" + dirName.replace(/\\/g, "/")
|
|
214188
214462
|
};
|
|
214189
214463
|
}, loadDesignServer = async (ctx, needs = []) => {
|
|
214190
|
-
if (!
|
|
214464
|
+
if (!existsSync23(ctx.designDir)) {
|
|
214191
214465
|
throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
|
|
214192
214466
|
}
|
|
214193
214467
|
let mod;
|
|
@@ -214219,13 +214493,13 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214219
214493
|
`).find((l2) => l2.trim())?.trim() ?? "";
|
|
214220
214494
|
return line.length > max ? line.slice(0, max - 1) + "…" : line;
|
|
214221
214495
|
}, listScreens = (designDir) => {
|
|
214222
|
-
const dir =
|
|
214223
|
-
if (!
|
|
214496
|
+
const dir = join23(designDir, "screens");
|
|
214497
|
+
if (!existsSync23(dir))
|
|
214224
214498
|
return [];
|
|
214225
|
-
return
|
|
214499
|
+
return readdirSync12(dir).filter((f3) => f3.endsWith(".ts") && !f3.startsWith(".")).map((f3) => f3.slice(0, -3)).sort();
|
|
214226
214500
|
}, PHONE_SIZE, readMetaFrames = (designDir) => {
|
|
214227
214501
|
try {
|
|
214228
|
-
const meta = JSON.parse(
|
|
214502
|
+
const meta = JSON.parse(readFileSync19(join23(designDir, "meta.json"), "utf8"));
|
|
214229
214503
|
const d = meta?.defaultSize;
|
|
214230
214504
|
const valid = Array.isArray(d) && d.length === 2 && d.every((n2) => typeof n2 === "number" && Number.isFinite(n2) && n2 >= 64 && n2 <= 8192);
|
|
214231
214505
|
return {
|
|
@@ -214236,12 +214510,12 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214236
214510
|
return { screens: {}, defaultSize: PHONE_SIZE };
|
|
214237
214511
|
}
|
|
214238
214512
|
}, writeMcpConfig = (ctx, port) => {
|
|
214239
|
-
const path2 =
|
|
214513
|
+
const path2 = join23(ctx.root, ".mcp.json");
|
|
214240
214514
|
const entry = { type: "http", url: `http://localhost:${port}/mcp` };
|
|
214241
214515
|
let config = {};
|
|
214242
|
-
if (
|
|
214516
|
+
if (existsSync23(path2)) {
|
|
214243
214517
|
try {
|
|
214244
|
-
config = JSON.parse(
|
|
214518
|
+
config = JSON.parse(readFileSync19(path2, "utf8"));
|
|
214245
214519
|
} catch {
|
|
214246
214520
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-design server by hand.");
|
|
214247
214521
|
return;
|
|
@@ -214254,7 +214528,7 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214254
214528
|
return;
|
|
214255
214529
|
}
|
|
214256
214530
|
config.mcpServers["lecodes-design"] = entry;
|
|
214257
|
-
|
|
214531
|
+
writeFileSync18(path2, JSON.stringify(config, null, 2) + `
|
|
214258
214532
|
`);
|
|
214259
214533
|
log(`${c.green(existing ? "updated" : "created")} .mcp.json (lecodes-design MCP → ${entry.url})`);
|
|
214260
214534
|
}, init = async (ctx, args) => {
|
|
@@ -214263,26 +214537,26 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214263
214537
|
["spec.md", SPEC_MD],
|
|
214264
214538
|
["README.md", README_HINT],
|
|
214265
214539
|
["CLAUDE.md", CLAUDE_MD],
|
|
214266
|
-
[
|
|
214267
|
-
[
|
|
214268
|
-
[
|
|
214540
|
+
[join23("shared", "tokens.ts"), TOKENS_TS],
|
|
214541
|
+
[join23("shared", "tabs.ts"), TABS_TS],
|
|
214542
|
+
[join23("screens", "home.ts"), HOME_SCREEN_TS]
|
|
214269
214543
|
];
|
|
214270
|
-
|
|
214271
|
-
|
|
214544
|
+
mkdirSync17(join23(ctx.designDir, "screens"), { recursive: true });
|
|
214545
|
+
mkdirSync17(join23(ctx.designDir, "shared"), { recursive: true });
|
|
214272
214546
|
let created = 0;
|
|
214273
214547
|
for (const [rel, content] of files) {
|
|
214274
|
-
const abs =
|
|
214275
|
-
if (
|
|
214548
|
+
const abs = join23(ctx.designDir, rel);
|
|
214549
|
+
if (existsSync23(abs)) {
|
|
214276
214550
|
note(`kept ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
214277
214551
|
continue;
|
|
214278
214552
|
}
|
|
214279
|
-
|
|
214553
|
+
writeFileSync18(abs, content);
|
|
214280
214554
|
log(`${c.green("created")} ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
214281
214555
|
created++;
|
|
214282
214556
|
}
|
|
214283
|
-
const ignorePath =
|
|
214557
|
+
const ignorePath = join23(ctx.root, ".lecodesignore");
|
|
214284
214558
|
const ignoreLines = [`${ctx.dirName}/.snapshots/`, `${ctx.dirName}/comments/`];
|
|
214285
|
-
const current =
|
|
214559
|
+
const current = existsSync23(ignorePath) ? readFileSync19(ignorePath, "utf8") : "";
|
|
214286
214560
|
const present = new Set(current.split(/\r?\n/));
|
|
214287
214561
|
const missing = ignoreLines.filter((line) => !present.has(line));
|
|
214288
214562
|
if (missing.length) {
|
|
@@ -214297,8 +214571,8 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214297
214571
|
writeMcpConfig(ctx, Number(flagStr(args, "port")) || DEFAULT_PORT);
|
|
214298
214572
|
const wroteTypes = materializeTypesLocal(ctx.designDir);
|
|
214299
214573
|
writeTsconfig(ctx.designDir);
|
|
214300
|
-
|
|
214301
|
-
|
|
214574
|
+
mkdirSync17(join23(ctx.designDir, ".lecodes", "types"), { recursive: true });
|
|
214575
|
+
writeFileSync18(join23(ctx.designDir, ".lecodes", "types", "design-macros.d.ts"), DESIGN_MACROS_DTS);
|
|
214302
214576
|
log(`${c.green("created")} ${ctx.dirName}/tsconfig.json${wroteTypes ? ` + ${ctx.dirName}/.lecodes/types/` : ""} (IDE types)`);
|
|
214303
214577
|
if (!wroteTypes)
|
|
214304
214578
|
warnErr("Bundled SDK types not found — tsconfig written, but globals may show unresolved until the CLI is rebuilt with vendored types.");
|
|
@@ -214346,16 +214620,16 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214346
214620
|
killer.unref?.();
|
|
214347
214621
|
const png2 = flagBool(args, "png") || flagStr(args, "png") !== undefined;
|
|
214348
214622
|
const frames = readMetaFrames(ctx.designDir);
|
|
214349
|
-
const outDir = flagStr(args, "out-dir") ??
|
|
214623
|
+
const outDir = flagStr(args, "out-dir") ?? join23(ctx.designDir, ".snapshots");
|
|
214350
214624
|
const single = targets.length === 1 && requested.length === 1 && !png2 && !allStates && !flagStr(args, "out-dir");
|
|
214351
214625
|
const onConsole = flagBool(args, "logs") ? (level, text) => logErr(`${c.dim(`[${level}]`)} ${text}`) : undefined;
|
|
214352
214626
|
if (!single)
|
|
214353
|
-
|
|
214627
|
+
mkdirSync17(outDir, { recursive: true });
|
|
214354
214628
|
for (const id of targets) {
|
|
214355
214629
|
const [width, height] = frames.screens[id]?.size ?? frames.defaultSize;
|
|
214356
214630
|
let plan = [{ state: undefined, bases: [id] }];
|
|
214357
214631
|
if (discover) {
|
|
214358
|
-
const { states, canonical } = discover(
|
|
214632
|
+
const { states, canonical } = discover(readFileSync19(join23(ctx.designDir, "screens", `${id}.ts`), "utf8"));
|
|
214359
214633
|
if (stateFlag !== undefined) {
|
|
214360
214634
|
if (!states.includes(stateFlag)) {
|
|
214361
214635
|
if (requested.length === 1)
|
|
@@ -214396,11 +214670,11 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214396
214670
|
for (const w of pngResult.warnings)
|
|
214397
214671
|
warnErr(`${tag}: ${w}`);
|
|
214398
214672
|
for (const base of bases) {
|
|
214399
|
-
|
|
214400
|
-
log(`${c.green("✓")} ${tag} → ${
|
|
214673
|
+
writeFileSync18(join23(outDir, `${base}.json`), result.text);
|
|
214674
|
+
log(`${c.green("✓")} ${tag} → ${join23(outDir, `${base}.json`)}`);
|
|
214401
214675
|
if (pngResult) {
|
|
214402
|
-
|
|
214403
|
-
log(`${c.green("✓")} ${tag} → ${
|
|
214676
|
+
writeFileSync18(join23(outDir, `${base}.png`), pngResult.png);
|
|
214677
|
+
log(`${c.green("✓")} ${tag} → ${join23(outDir, `${base}.png`)}`);
|
|
214404
214678
|
}
|
|
214405
214679
|
}
|
|
214406
214680
|
}
|
|
@@ -214702,7 +214976,7 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214702
214976
|
note("Read them with: lecodes design comments");
|
|
214703
214977
|
}, flushDesignComments = async (args) => {
|
|
214704
214978
|
const ctx = getContext(args);
|
|
214705
|
-
if (!
|
|
214979
|
+
if (!existsSync23(ctx.designDir))
|
|
214706
214980
|
return null;
|
|
214707
214981
|
const platform = optionalPlatform(ctx);
|
|
214708
214982
|
if (!platform)
|
|
@@ -215844,7 +216118,7 @@ var require_main = __commonJS((exports, module) => {
|
|
|
215844
216118
|
// package.json
|
|
215845
216119
|
var package_default = {
|
|
215846
216120
|
name: "lecodes-cli",
|
|
215847
|
-
version: "0.
|
|
216121
|
+
version: "0.18.0",
|
|
215848
216122
|
dependencies: {
|
|
215849
216123
|
"@letary/chisel": "^0.8.0",
|
|
215850
216124
|
jimp: "^1.6.1"
|
|
@@ -215860,8 +216134,8 @@ var package_default = {
|
|
|
215860
216134
|
},
|
|
215861
216135
|
peerDependencies: {
|
|
215862
216136
|
"lecodes-design": "^0.8.1",
|
|
215863
|
-
"lecodes-renderer": "^0.8.
|
|
215864
|
-
"lecodes-3d-editor": "^0.
|
|
216137
|
+
"lecodes-renderer": "^0.8.8",
|
|
216138
|
+
"lecodes-3d-editor": "^0.4.0",
|
|
215865
216139
|
"lecodes-assets": "^0.1.0"
|
|
215866
216140
|
},
|
|
215867
216141
|
bin: {
|
|
@@ -215912,16 +216186,16 @@ var package_default = {
|
|
|
215912
216186
|
init_manifest();
|
|
215913
216187
|
init_config();
|
|
215914
216188
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
215915
|
-
import { cpSync as cpSync3, existsSync as
|
|
215916
|
-
import { homedir as
|
|
215917
|
-
import { basename as
|
|
216189
|
+
import { cpSync as cpSync3, existsSync as existsSync19, mkdirSync as mkdirSync13, readFileSync as readFileSync16, readdirSync as readdirSync8, rmSync as rmSync6, statSync as statSync6, writeFileSync as writeFileSync15 } from "node:fs";
|
|
216190
|
+
import { homedir as homedir7 } from "node:os";
|
|
216191
|
+
import { basename as basename7, dirname as dirname12, join as join20, resolve as resolve7 } from "node:path";
|
|
215918
216192
|
|
|
215919
216193
|
// src/commands/compile.ts
|
|
215920
216194
|
init_api();
|
|
215921
216195
|
init_config();
|
|
215922
216196
|
init_manifest();
|
|
215923
|
-
import { writeFileSync as
|
|
215924
|
-
import { basename as
|
|
216197
|
+
import { writeFileSync as writeFileSync10 } from "node:fs";
|
|
216198
|
+
import { basename as basename4, resolve as resolve3 } from "node:path";
|
|
215925
216199
|
|
|
215926
216200
|
// src/compile/collect.ts
|
|
215927
216201
|
init_localFiles();
|
|
@@ -216011,7 +216285,7 @@ init_localFiles();
|
|
|
216011
216285
|
init_ignore();
|
|
216012
216286
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
216013
216287
|
import { basename as basename2 } from "node:path";
|
|
216014
|
-
var collectLocalEntries = (root, manifest, assetBase) => {
|
|
216288
|
+
var collectLocalEntries = (root, manifest, assetBase, opts = {}) => {
|
|
216015
216289
|
const entries = [];
|
|
216016
216290
|
const warnings = [];
|
|
216017
216291
|
const scan = scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest?.files ?? {})) });
|
|
@@ -216020,8 +216294,10 @@ var collectLocalEntries = (root, manifest, assetBase) => {
|
|
|
216020
216294
|
if (type === "resource") {
|
|
216021
216295
|
entries.push({ path: file.path, type, fileSrc: assetBase + encodeURI(file.path), absPath: file.absPath });
|
|
216022
216296
|
} else if (type === "shader") {
|
|
216023
|
-
|
|
216024
|
-
|
|
216297
|
+
if (!opts.localShaders) {
|
|
216298
|
+
warnings.push(`${file.path}: shaders compile on the server — push the project once for shaders to work in dev mode.`);
|
|
216299
|
+
}
|
|
216300
|
+
entries.push({ path: file.path, type, shaders: [], absPath: file.absPath });
|
|
216025
216301
|
} else {
|
|
216026
216302
|
entries.push({ path: file.path, type, text: readFileSync6(file.absPath, "utf8") });
|
|
216027
216303
|
}
|
|
@@ -216032,6 +216308,266 @@ var collectLocalEntries = (root, manifest, assetBase) => {
|
|
|
216032
216308
|
// src/commands/compile.ts
|
|
216033
216309
|
init_projectCompile();
|
|
216034
216310
|
init_fonts();
|
|
216311
|
+
|
|
216312
|
+
// src/compile/shaders.ts
|
|
216313
|
+
init_compile();
|
|
216314
|
+
import { execFile } from "node:child_process";
|
|
216315
|
+
import { createHash as createHash4, randomBytes } from "node:crypto";
|
|
216316
|
+
import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync10, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
216317
|
+
import { tmpdir } from "node:os";
|
|
216318
|
+
import { basename as basename3, join as join13 } from "node:path";
|
|
216319
|
+
|
|
216320
|
+
// src/matcTool.ts
|
|
216321
|
+
import { existsSync as existsSync10, readdirSync as readdirSync4 } from "node:fs";
|
|
216322
|
+
import { homedir as homedir2 } from "node:os";
|
|
216323
|
+
import { join as join12, resolve } from "node:path";
|
|
216324
|
+
|
|
216325
|
+
// src/releases.ts
|
|
216326
|
+
init_util();
|
|
216327
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
216328
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, mkdtempSync, renameSync, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
216329
|
+
import { join as join11 } from "node:path";
|
|
216330
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
216331
|
+
var MANIFEST_URL = "https://storage.yandexcloud.net/le-codes/releases/versions.json";
|
|
216332
|
+
var semverNewer = (a, b) => {
|
|
216333
|
+
const pa = a.split(/[.-]/).map(Number), pb = b.split(/[.-]/).map(Number);
|
|
216334
|
+
for (let i = 0;i < 3; i++)
|
|
216335
|
+
if ((pb[i] ?? 0) !== (pa[i] ?? 0))
|
|
216336
|
+
return (pb[i] ?? 0) - (pa[i] ?? 0);
|
|
216337
|
+
return 0;
|
|
216338
|
+
};
|
|
216339
|
+
var fetchReleaseProduct = async (product, label) => {
|
|
216340
|
+
let manifest;
|
|
216341
|
+
try {
|
|
216342
|
+
const res = await fetch(MANIFEST_URL);
|
|
216343
|
+
if (!res.ok)
|
|
216344
|
+
throw new Error(`HTTP ${res.status}`);
|
|
216345
|
+
manifest = await res.json();
|
|
216346
|
+
} catch (e) {
|
|
216347
|
+
throw new CliError(`Can't fetch the release manifest (${MANIFEST_URL}): ${e instanceof Error ? e.message : String(e)}`);
|
|
216348
|
+
}
|
|
216349
|
+
const entry = manifest.products?.[product];
|
|
216350
|
+
if (!entry)
|
|
216351
|
+
throw new CliError(`The release manifest has no ${label} product.`);
|
|
216352
|
+
return entry;
|
|
216353
|
+
};
|
|
216354
|
+
var downloadReleaseArchive = async (file, opts) => {
|
|
216355
|
+
let bytes;
|
|
216356
|
+
try {
|
|
216357
|
+
const res = await fetch(file.url);
|
|
216358
|
+
if (!res.ok)
|
|
216359
|
+
throw new Error(`HTTP ${res.status}`);
|
|
216360
|
+
bytes = Buffer.from(await res.arrayBuffer());
|
|
216361
|
+
} catch (e) {
|
|
216362
|
+
throw new CliError(`Download failed (${file.url}): ${e instanceof Error ? e.message : String(e)}`);
|
|
216363
|
+
}
|
|
216364
|
+
const sha = createHash3("sha256").update(bytes).digest("hex");
|
|
216365
|
+
if (sha !== file.sha256)
|
|
216366
|
+
throw new CliError(`Downloaded archive is corrupt (sha256 mismatch) — try again.`);
|
|
216367
|
+
mkdirSync5(opts.cacheRoot, { recursive: true });
|
|
216368
|
+
const stage = mkdtempSync(join11(opts.cacheRoot, "dl-"));
|
|
216369
|
+
try {
|
|
216370
|
+
const archiveName = file.url.split("/").pop() ?? "archive";
|
|
216371
|
+
const archive = join11(stage, archiveName);
|
|
216372
|
+
writeFileSync6(archive, bytes);
|
|
216373
|
+
const winTar = join11(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe");
|
|
216374
|
+
const tarExe = process.platform === "win32" && existsSync9(winTar) ? winTar : "tar";
|
|
216375
|
+
const tar = spawnSync2(tarExe, ["-xf", archiveName], { cwd: stage, stdio: ["ignore", "ignore", "pipe"] });
|
|
216376
|
+
if (tar.status !== 0)
|
|
216377
|
+
throw new CliError(`Couldn't extract the archive: ${tar.stderr?.toString() ?? "tar failed"}`);
|
|
216378
|
+
rmSync(archive);
|
|
216379
|
+
const bad = opts.validate(stage);
|
|
216380
|
+
if (bad)
|
|
216381
|
+
throw new CliError(bad);
|
|
216382
|
+
try {
|
|
216383
|
+
renameSync(stage, opts.dest);
|
|
216384
|
+
} catch {
|
|
216385
|
+
let destBad;
|
|
216386
|
+
try {
|
|
216387
|
+
destBad = opts.validate(opts.dest);
|
|
216388
|
+
} catch {
|
|
216389
|
+
destBad = "missing";
|
|
216390
|
+
}
|
|
216391
|
+
if (destBad)
|
|
216392
|
+
throw new CliError(`Couldn't move the download into ${opts.dest}.`);
|
|
216393
|
+
}
|
|
216394
|
+
} finally {
|
|
216395
|
+
rmSync(stage, { recursive: true, force: true });
|
|
216396
|
+
}
|
|
216397
|
+
};
|
|
216398
|
+
|
|
216399
|
+
// src/matcTool.ts
|
|
216400
|
+
init_util();
|
|
216401
|
+
var matcName = () => process.platform === "win32" ? "matc.exe" : "matc";
|
|
216402
|
+
var platformKey = () => {
|
|
216403
|
+
if (process.platform === "win32")
|
|
216404
|
+
return "windows-x64";
|
|
216405
|
+
if (process.platform === "linux")
|
|
216406
|
+
return "linux-x64";
|
|
216407
|
+
throw new CliError(process.platform === "darwin" ? "matc isn't published for macOS yet — push the project once so shaders compile on the server." : `matc isn't published for ${process.platform}.`);
|
|
216408
|
+
};
|
|
216409
|
+
var cacheRoot = () => join12(homedir2(), ".lecodes", "matc");
|
|
216410
|
+
var findMatc = (dir) => {
|
|
216411
|
+
const name = matcName();
|
|
216412
|
+
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
216413
|
+
const p = join12(dir, entry.name);
|
|
216414
|
+
if (entry.isFile() && entry.name === name)
|
|
216415
|
+
return p;
|
|
216416
|
+
if (entry.isDirectory()) {
|
|
216417
|
+
const found = findMatc(p);
|
|
216418
|
+
if (found)
|
|
216419
|
+
return found;
|
|
216420
|
+
}
|
|
216421
|
+
}
|
|
216422
|
+
return null;
|
|
216423
|
+
};
|
|
216424
|
+
var installedMatcVersions = () => {
|
|
216425
|
+
try {
|
|
216426
|
+
return readdirSync4(cacheRoot(), { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("matc-")).map((e) => e.name.slice("matc-".length)).filter((v) => findMatc(join12(cacheRoot(), `matc-${v}`)) !== null).sort(semverNewer);
|
|
216427
|
+
} catch {
|
|
216428
|
+
return [];
|
|
216429
|
+
}
|
|
216430
|
+
};
|
|
216431
|
+
var fetchMatcManifest = () => fetchReleaseProduct("matc", "matc (shader compiler)");
|
|
216432
|
+
var downloadMatc = async (version, versions, dest) => {
|
|
216433
|
+
const key = platformKey();
|
|
216434
|
+
const file = versions.find((v) => v.version === version)?.files[key];
|
|
216435
|
+
if (!file) {
|
|
216436
|
+
throw new CliError(`matc ${version} [${key}] isn't published — push the project once so shaders compile on the ` + `server, or set LECODES_MATC to a locally built matc.`);
|
|
216437
|
+
}
|
|
216438
|
+
note(`Downloading the shader compiler (matc ${version}, ${(file.size / 1024 / 1024).toFixed(1)} MB)…`);
|
|
216439
|
+
await downloadReleaseArchive(file, {
|
|
216440
|
+
cacheRoot: cacheRoot(),
|
|
216441
|
+
dest,
|
|
216442
|
+
validate: (dir) => findMatc(dir) ? null : "The downloaded archive doesn't contain the matc binary."
|
|
216443
|
+
});
|
|
216444
|
+
};
|
|
216445
|
+
var resolveMatcExeCached = () => {
|
|
216446
|
+
const override = process.env.LECODES_MATC;
|
|
216447
|
+
if (override) {
|
|
216448
|
+
if (!existsSync10(override))
|
|
216449
|
+
throw new CliError(`LECODES_MATC points at "${override}" which doesn't exist.`);
|
|
216450
|
+
return resolve(override);
|
|
216451
|
+
}
|
|
216452
|
+
const cached = installedMatcVersions()[0];
|
|
216453
|
+
return cached ? findMatc(join12(cacheRoot(), `matc-${cached}`)) : null;
|
|
216454
|
+
};
|
|
216455
|
+
var resolveMatcExe = async () => {
|
|
216456
|
+
const cached = resolveMatcExeCached();
|
|
216457
|
+
if (cached)
|
|
216458
|
+
return cached;
|
|
216459
|
+
const { latest, versions } = await fetchMatcManifest();
|
|
216460
|
+
const dir = join12(cacheRoot(), `matc-${latest}`);
|
|
216461
|
+
await downloadMatc(latest, versions, dir);
|
|
216462
|
+
const exe = findMatc(dir);
|
|
216463
|
+
if (!exe)
|
|
216464
|
+
throw new CliError(`matc cache at ${dir} is broken — delete the folder and retry.`);
|
|
216465
|
+
return exe;
|
|
216466
|
+
};
|
|
216467
|
+
var updateMatc = async () => {
|
|
216468
|
+
const { latest, versions } = await fetchMatcManifest();
|
|
216469
|
+
const before = installedMatcVersions()[0];
|
|
216470
|
+
const dir = join12(cacheRoot(), `matc-${latest}`);
|
|
216471
|
+
if (existsSync10(dir) && findMatc(dir) !== null) {
|
|
216472
|
+
note(`Already up to date: matc ${latest}.`);
|
|
216473
|
+
return;
|
|
216474
|
+
}
|
|
216475
|
+
await downloadMatc(latest, versions, dir);
|
|
216476
|
+
note(before ? `matc updated to ${latest} (was ${before}).` : `matc ${latest} installed.`);
|
|
216477
|
+
};
|
|
216478
|
+
|
|
216479
|
+
// src/compile/shaders.ts
|
|
216480
|
+
init_manifest();
|
|
216481
|
+
init_util();
|
|
216482
|
+
var SHADERS_DIR = "shaders";
|
|
216483
|
+
var shadersCacheDir = (root) => join13(root, LECODES_DIR, SHADERS_DIR);
|
|
216484
|
+
var isShaderArtifactName = (name) => /^[0-9a-f]{16}_[a-z-]+\.filamat$/.test(name);
|
|
216485
|
+
var matcIdentity = (exe) => {
|
|
216486
|
+
const stat = statSync2(exe);
|
|
216487
|
+
return `${stat.size}:${Math.trunc(stat.mtimeMs)}`;
|
|
216488
|
+
};
|
|
216489
|
+
var shaderHash = (text, matcId) => createHash4("sha256").update(matcId + "\x00" + text).digest("hex").slice(0, 16);
|
|
216490
|
+
var artifactFamily = (text) => {
|
|
216491
|
+
const matcPlatforms = matcPlatformsFor(text);
|
|
216492
|
+
return [...matcPlatforms, ...matcPlatforms.includes("webgl") ? [] : ["webgl"], "webgl-lite"];
|
|
216493
|
+
};
|
|
216494
|
+
var shaderIsCompiled = (root, text, matcId) => {
|
|
216495
|
+
const hash = shaderHash(text, matcId);
|
|
216496
|
+
return artifactFamily(text).every((p) => existsSync11(join13(shadersCacheDir(root), `${hash}_${p}.filamat`)));
|
|
216497
|
+
};
|
|
216498
|
+
var runMatc = (exe, args) => new Promise((resolve2) => {
|
|
216499
|
+
execFile(exe, args, { windowsHide: true }, (error, _stdout, stderr) => {
|
|
216500
|
+
const text = String(stderr ?? "") || (error ? error.message : "");
|
|
216501
|
+
resolve2({ ok: !error, stderr: text });
|
|
216502
|
+
});
|
|
216503
|
+
});
|
|
216504
|
+
var cleanMatcOutput = (err, tempName, realName) => {
|
|
216505
|
+
const output = err.replaceAll("\r", "").replaceAll(tempName, realName);
|
|
216506
|
+
const cut = output.indexOf(`
|
|
216507
|
+
|
|
216508
|
+
`);
|
|
216509
|
+
return (cut >= 0 ? output.slice(0, cut) : output).trim();
|
|
216510
|
+
};
|
|
216511
|
+
var NATIVE_PLATFORMS = ["opengl", "metal", "desktop"];
|
|
216512
|
+
var applyLocalShaders = async (entries, opts) => {
|
|
216513
|
+
const shaderEntries = entries.filter((e) => e.type === "shader" && e.absPath && (opts.local || !e.shaders?.length));
|
|
216514
|
+
if (shaderEntries.length === 0)
|
|
216515
|
+
return { warnings: [], artifacts: [] };
|
|
216516
|
+
let matcExe;
|
|
216517
|
+
try {
|
|
216518
|
+
matcExe = await resolveMatcExe();
|
|
216519
|
+
} catch (e) {
|
|
216520
|
+
const reason = e instanceof CliError ? e.message : String(e);
|
|
216521
|
+
return { warnings: [`Local shader compile unavailable (${reason})
|
|
216522
|
+
Shaders fall back to the server — push the project once for them to work.`], artifacts: [] };
|
|
216523
|
+
}
|
|
216524
|
+
const matcId = matcIdentity(matcExe);
|
|
216525
|
+
const cacheDir = shadersCacheDir(opts.root);
|
|
216526
|
+
mkdirSync6(cacheDir, { recursive: true });
|
|
216527
|
+
const artifacts = [];
|
|
216528
|
+
for (const entry of shaderEntries) {
|
|
216529
|
+
const text = readFileSync10(entry.absPath, "utf8");
|
|
216530
|
+
const hash = shaderHash(text, matcId);
|
|
216531
|
+
const matcPlatforms = matcPlatformsFor(text);
|
|
216532
|
+
const artifact = (platform) => `${hash}_${platform}.filamat`;
|
|
216533
|
+
if (!artifactFamily(text).every((p) => existsSync11(join13(cacheDir, artifact(p))))) {
|
|
216534
|
+
const tempName = `lecodes-matc-${randomBytes(6).toString("hex")}.mat`;
|
|
216535
|
+
const tempFile = join13(tmpdir(), tempName);
|
|
216536
|
+
try {
|
|
216537
|
+
for (const platform of matcPlatforms) {
|
|
216538
|
+
const build = PLATFORM_BUILD[platform];
|
|
216539
|
+
writeFileSync7(tempFile, build.replaceExternal ? text.replace(externalSamplerReg, "type:sampler2d") : text);
|
|
216540
|
+
const out = join13(cacheDir, artifact(platform));
|
|
216541
|
+
const { ok, stderr } = await runMatc(matcExe, matcArgs(platform, out, tempFile));
|
|
216542
|
+
if (!ok) {
|
|
216543
|
+
rmSync2(out, { force: true });
|
|
216544
|
+
throw new CliError(`${entry.path}: shader compile failed [${platform}]
|
|
216545
|
+
${cleanMatcOutput(stderr, tempName, basename3(entry.path))}`);
|
|
216546
|
+
}
|
|
216547
|
+
}
|
|
216548
|
+
} finally {
|
|
216549
|
+
rmSync2(tempFile, { force: true });
|
|
216550
|
+
}
|
|
216551
|
+
if (!matcPlatforms.includes("webgl")) {
|
|
216552
|
+
copyFileSync(join13(cacheDir, artifact("opengl")), join13(cacheDir, artifact("webgl")));
|
|
216553
|
+
}
|
|
216554
|
+
writeFileSync7(join13(cacheDir, artifact("webgl-lite")), JSON.stringify(buildLiteMaterialArtifact(text)));
|
|
216555
|
+
}
|
|
216556
|
+
if (opts.local) {
|
|
216557
|
+
entry.shaders = NATIVE_PLATFORMS.map((platform) => ({ platform, src: artifact(platform) }));
|
|
216558
|
+
entry.shadersLocal = true;
|
|
216559
|
+
for (const platform of NATIVE_PLATFORMS) {
|
|
216560
|
+
artifacts.push({ name: artifact(platform), absPath: join13(cacheDir, artifact(platform)) });
|
|
216561
|
+
}
|
|
216562
|
+
} else {
|
|
216563
|
+
const order = ["opengl", "metal", "desktop", "webgl", "webgl-lite"];
|
|
216564
|
+
entry.shaders = order.map((platform) => ({ platform, src: `${opts.srcPrefix}/${artifact(platform)}` }));
|
|
216565
|
+
}
|
|
216566
|
+
}
|
|
216567
|
+
return { warnings: [], artifacts };
|
|
216568
|
+
};
|
|
216569
|
+
|
|
216570
|
+
// src/commands/compile.ts
|
|
216035
216571
|
init_assetIcons();
|
|
216036
216572
|
init_ignore();
|
|
216037
216573
|
|
|
@@ -216040,16 +216576,16 @@ init_manifest();
|
|
|
216040
216576
|
init_ignore();
|
|
216041
216577
|
init_util();
|
|
216042
216578
|
import { execFileSync } from "node:child_process";
|
|
216043
|
-
import { existsSync as
|
|
216044
|
-
import { homedir as
|
|
216045
|
-
import { dirname as dirname8, join as
|
|
216579
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
|
|
216580
|
+
import { homedir as homedir4 } from "node:os";
|
|
216581
|
+
import { dirname as dirname8, join as join15, resolve as resolve2 } from "node:path";
|
|
216046
216582
|
var FIRST_PARTY_PLUGINS = {
|
|
216047
216583
|
camera: "letary/lecodes-plugins",
|
|
216048
216584
|
"qr-scanner": "letary/lecodes-plugins",
|
|
216049
216585
|
geolocation: "letary/lecodes-plugins",
|
|
216050
216586
|
push: "letary/lecodes-plugins"
|
|
216051
216587
|
};
|
|
216052
|
-
var readAppConfigAt = (root) => readJson(
|
|
216588
|
+
var readAppConfigAt = (root) => readJson(join15(root, "app.json"));
|
|
216053
216589
|
var DESKTOP_DEFAULT_WINDOW = { width: 1280, height: 720 };
|
|
216054
216590
|
var desktopDefaultViewport = (root) => {
|
|
216055
216591
|
const cfg = readAppConfigAt(root);
|
|
@@ -216061,15 +216597,15 @@ var desktopDefaultViewport = (root) => {
|
|
|
216061
216597
|
};
|
|
216062
216598
|
};
|
|
216063
216599
|
var readJson = (path) => {
|
|
216064
|
-
if (!
|
|
216600
|
+
if (!existsSync13(path))
|
|
216065
216601
|
return null;
|
|
216066
216602
|
try {
|
|
216067
|
-
return JSON.parse(
|
|
216603
|
+
return JSON.parse(readFileSync12(path, "utf8").replace(/^\uFEFF/, ""));
|
|
216068
216604
|
} catch {
|
|
216069
216605
|
throw new CliError(`Invalid JSON in ${path}`);
|
|
216070
216606
|
}
|
|
216071
216607
|
};
|
|
216072
|
-
var writeJson = (path, value) =>
|
|
216608
|
+
var writeJson = (path, value) => writeFileSync9(path, JSON.stringify(value, null, 2) + `
|
|
216073
216609
|
`);
|
|
216074
216610
|
var projectRoot = () => {
|
|
216075
216611
|
try {
|
|
@@ -216094,12 +216630,12 @@ ${tail}`);
|
|
|
216094
216630
|
}
|
|
216095
216631
|
};
|
|
216096
216632
|
var ensureShellIgnored = (root, dir) => {
|
|
216097
|
-
const ignorePath =
|
|
216098
|
-
const ignore =
|
|
216633
|
+
const ignorePath = join15(root, ".lecodesignore");
|
|
216634
|
+
const ignore = existsSync13(ignorePath) ? readFileSync12(ignorePath, "utf8") : DEFAULT_IGNORE_FILE;
|
|
216099
216635
|
if (new RegExp(`^${dir}\\/$`, "m").test(ignore))
|
|
216100
216636
|
return;
|
|
216101
216637
|
const comment = ignore.includes("# Native app shells") ? "" : "# Native app shells — generated by `lecodes app init`, never pushed to LeCodes.\n";
|
|
216102
|
-
|
|
216638
|
+
writeFileSync9(ignorePath, ignore.replace(/\n*$/, `
|
|
216103
216639
|
`) + `
|
|
216104
216640
|
` + comment + `${dir}/
|
|
216105
216641
|
`);
|
|
@@ -216175,12 +216711,12 @@ var ensureRepo = (repo, wantTag, lock) => {
|
|
|
216175
216711
|
const override = process.env.LECODES_PLUGINS_DIR;
|
|
216176
216712
|
if (override) {
|
|
216177
216713
|
note(`Using local plugin checkout ${override} (LECODES_PLUGINS_DIR)`);
|
|
216178
|
-
return { dir:
|
|
216714
|
+
return { dir: resolve2(override), tag: "local", commit: "local" };
|
|
216179
216715
|
}
|
|
216180
|
-
const cache =
|
|
216716
|
+
const cache = join15(homedir4(), ".lecodes", "plugins", repo.replace("/", "__"));
|
|
216181
216717
|
const url = `https://github.com/${repo}.git`;
|
|
216182
|
-
if (!
|
|
216183
|
-
|
|
216718
|
+
if (!existsSync13(join15(cache, ".git"))) {
|
|
216719
|
+
mkdirSync8(dirname8(cache), { recursive: true });
|
|
216184
216720
|
note(`Cloning ${url}…`);
|
|
216185
216721
|
git(dirname8(cache), "clone", "--quiet", url, cache);
|
|
216186
216722
|
}
|
|
@@ -216222,8 +216758,8 @@ var resolvePlugin = (id, spec, lock, platform) => {
|
|
|
216222
216758
|
subdir = `plugins/${id}`;
|
|
216223
216759
|
}
|
|
216224
216760
|
const checkout = ensureRepo(repo, wantTag, lock?.repo === repo ? lock : undefined);
|
|
216225
|
-
const dir = subdir ?
|
|
216226
|
-
const manifest = readJson(
|
|
216761
|
+
const dir = subdir ? join15(checkout.dir, subdir) : checkout.dir;
|
|
216762
|
+
const manifest = readJson(join15(dir, "lecodes-plugin.json"));
|
|
216227
216763
|
if (!manifest)
|
|
216228
216764
|
throw new CliError(`Plugin "${id}": no lecodes-plugin.json in ${dir}`);
|
|
216229
216765
|
if (manifest.id !== id)
|
|
@@ -216265,7 +216801,7 @@ var compileImpl = async (args, shell) => {
|
|
|
216265
216801
|
if (out && isNullSink(out)) {
|
|
216266
216802
|
note(`Discarded output (${out})`);
|
|
216267
216803
|
} else if (out) {
|
|
216268
|
-
|
|
216804
|
+
writeFileSync10(resolve3(out), js);
|
|
216269
216805
|
note(`Wrote ${out} (${js.length} bytes)`);
|
|
216270
216806
|
} else {
|
|
216271
216807
|
process.stdout.write(js);
|
|
@@ -216322,12 +216858,18 @@ var buildBundle = async (args, shell, opts = {}) => {
|
|
|
216322
216858
|
if (opts.localAssets && flagBool(args, "remote-assets")) {
|
|
216323
216859
|
warnErr("This is a local project (no server) — assets always load from the working tree.");
|
|
216324
216860
|
}
|
|
216325
|
-
({ entries, warnings } = collectLocalEntries(root, null, ""));
|
|
216326
|
-
name =
|
|
216861
|
+
({ entries, warnings } = collectLocalEntries(root, null, "", { localShaders: localAssets && (opts.localShaders ?? !shell) }));
|
|
216862
|
+
name = basename4(root);
|
|
216327
216863
|
if (shell && shell.remotePatterns.length > 0) {
|
|
216328
216864
|
warnErr("resources.remote needs a server-backed project (a streamed asset needs a server URL) — bundling everything.");
|
|
216329
216865
|
}
|
|
216330
216866
|
}
|
|
216867
|
+
let stagedShaders = [];
|
|
216868
|
+
if (localAssets && (opts.localShaders ?? !shell)) {
|
|
216869
|
+
const local = await applyLocalShaders(entries, { root, local: true });
|
|
216870
|
+
warnings.push(...local.warnings);
|
|
216871
|
+
stagedShaders = local.artifacts;
|
|
216872
|
+
}
|
|
216331
216873
|
const fontsHeader = await applyProjectFonts(entries, manifest && !localAssets ? { mode: "cdn", root, publicUrl } : {
|
|
216332
216874
|
mode: "vendor",
|
|
216333
216875
|
root,
|
|
@@ -216364,6 +216906,9 @@ var buildBundle = async (args, shell, opts = {}) => {
|
|
|
216364
216906
|
absPath: e.absPath,
|
|
216365
216907
|
remote: !!e.remote
|
|
216366
216908
|
}));
|
|
216909
|
+
for (const artifact of stagedShaders) {
|
|
216910
|
+
resources.push({ path: artifact.name, name: artifact.name, absPath: artifact.absPath, remote: false, shader: true });
|
|
216911
|
+
}
|
|
216367
216912
|
return { js, resources };
|
|
216368
216913
|
};
|
|
216369
216914
|
var previewLegacy = async (args) => {
|
|
@@ -217213,17 +217758,17 @@ buildNumber) — edit it there, then re-run \`lecodes app sync\`. Plugins are ma
|
|
|
217213
217758
|
|
|
217214
217759
|
// src/commands/appAndroid.ts
|
|
217215
217760
|
init_manifest();
|
|
217216
|
-
import { execFileSync as execFileSync2, spawn, spawnSync as
|
|
217217
|
-
import { createHash as
|
|
217218
|
-
import { chmodSync, cpSync, existsSync as
|
|
217219
|
-
import { homedir as
|
|
217220
|
-
import { basename as
|
|
217761
|
+
import { execFileSync as execFileSync2, spawn, spawnSync as spawnSync3 } from "node:child_process";
|
|
217762
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
217763
|
+
import { chmodSync, cpSync, existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync14, readdirSync as readdirSync5, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync12 } from "node:fs";
|
|
217764
|
+
import { homedir as homedir5 } from "node:os";
|
|
217765
|
+
import { basename as basename5, dirname as dirname10, join as join17 } from "node:path";
|
|
217221
217766
|
init_util();
|
|
217222
217767
|
|
|
217223
217768
|
// src/commands/appIcon.ts
|
|
217224
217769
|
init_util();
|
|
217225
|
-
import { existsSync as
|
|
217226
|
-
import { dirname as dirname9, join as
|
|
217770
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "node:fs";
|
|
217771
|
+
import { dirname as dirname9, join as join16, resolve as resolve4 } from "node:path";
|
|
217227
217772
|
var MASTER_PX = 1024;
|
|
217228
217773
|
var DENSITIES = [
|
|
217229
217774
|
{ qualifier: "mdpi", scale: 1 },
|
|
@@ -217234,8 +217779,8 @@ var DENSITIES = [
|
|
|
217234
217779
|
];
|
|
217235
217780
|
var loadIconMaster = async (root, config) => {
|
|
217236
217781
|
const rel = config.icon;
|
|
217237
|
-
const path2 =
|
|
217238
|
-
if (!
|
|
217782
|
+
const path2 = resolve4(root, rel);
|
|
217783
|
+
if (!existsSync15(path2))
|
|
217239
217784
|
throw new CliError(`app.json "icon": ${rel} not found`);
|
|
217240
217785
|
const raw = (config.iconBackground ?? "#FFFFFF").trim();
|
|
217241
217786
|
const expanded = /^#[0-9a-fA-F]{3}$/.test(raw) ? "#" + [...raw.slice(1)].map((ch) => ch + ch).join("") : raw;
|
|
@@ -217299,12 +217844,12 @@ var IOS_CONTENTS_JSON = `{
|
|
|
217299
217844
|
`;
|
|
217300
217845
|
var writeIosAppIcon = async (iosDir, master) => {
|
|
217301
217846
|
const { Jimp: Jimp2 } = await Promise.resolve().then(() => (init_esm30(), exports_esm2));
|
|
217302
|
-
const dir =
|
|
217303
|
-
|
|
217847
|
+
const dir = join16(iosDir, "App", "Assets.xcassets", "AppIcon.appiconset");
|
|
217848
|
+
mkdirSync9(dir, { recursive: true });
|
|
217304
217849
|
const flat = new Jimp2({ width: MASTER_PX, height: MASTER_PX, color: master.background });
|
|
217305
217850
|
flat.composite(master.image, 0, 0);
|
|
217306
|
-
|
|
217307
|
-
|
|
217851
|
+
writeFileSync11(join16(dir, "AppIcon.png"), await flat.getBuffer("image/png", { colorType: 2 }));
|
|
217852
|
+
writeFileSync11(join16(dir, "Contents.json"), IOS_CONTENTS_JSON);
|
|
217308
217853
|
log(`${c.green("icon")} AppIcon.png (${MASTER_PX}×${MASTER_PX}) → ios/App/Assets.xcassets/AppIcon.appiconset/`);
|
|
217309
217854
|
};
|
|
217310
217855
|
var ADAPTIVE_ICON_XML = `<?xml version="1.0" encoding="utf-8"?>
|
|
@@ -217323,11 +217868,11 @@ var iconColorXml = (hex) => `<?xml version="1.0" encoding="utf-8"?>
|
|
|
217323
217868
|
`;
|
|
217324
217869
|
var writeAndroidIcons = async (androidDir, master) => {
|
|
217325
217870
|
const { Jimp: Jimp2 } = await Promise.resolve().then(() => (init_esm30(), exports_esm2));
|
|
217326
|
-
const resDir =
|
|
217871
|
+
const resDir = join16(androidDir, "app", "src", "main", "res");
|
|
217327
217872
|
const writeRes = (rel, content) => {
|
|
217328
|
-
const abs =
|
|
217329
|
-
|
|
217330
|
-
|
|
217873
|
+
const abs = join16(resDir, rel);
|
|
217874
|
+
mkdirSync9(dirname9(abs), { recursive: true });
|
|
217875
|
+
writeFileSync11(abs, content);
|
|
217331
217876
|
};
|
|
217332
217877
|
const legacy = new Jimp2({ width: MASTER_PX, height: MASTER_PX, color: master.background });
|
|
217333
217878
|
legacy.composite(master.image, 0, 0);
|
|
@@ -217349,17 +217894,17 @@ var writeAndroidIcons = async (androidDir, master) => {
|
|
|
217349
217894
|
log(`${c.green("icon")} adaptive + legacy launcher icons → android/app/src/main/res/mipmap-*/`);
|
|
217350
217895
|
};
|
|
217351
217896
|
var restampManifestIcon = (androidDir) => {
|
|
217352
|
-
const path2 =
|
|
217353
|
-
if (!
|
|
217897
|
+
const path2 = join16(androidDir, "app", "src", "main", "AndroidManifest.xml");
|
|
217898
|
+
if (!existsSync15(path2))
|
|
217354
217899
|
return;
|
|
217355
|
-
const content =
|
|
217900
|
+
const content = readFileSync13(path2, "utf8");
|
|
217356
217901
|
if (content.includes(`android:icon="@mipmap/ic_launcher"`))
|
|
217357
217902
|
return;
|
|
217358
217903
|
if (!/android:icon="[^"]*"/.test(content)) {
|
|
217359
217904
|
warn(`couldn't find android:icon in app/src/main/AndroidManifest.xml — set it to "@mipmap/ic_launcher" yourself.`);
|
|
217360
217905
|
return;
|
|
217361
217906
|
}
|
|
217362
|
-
|
|
217907
|
+
writeFileSync11(path2, content.replace(/android:icon="[^"]*"/, `android:icon="@mipmap/ic_launcher"`));
|
|
217363
217908
|
log(`${c.green("updated")} android/app/src/main/AndroidManifest.xml: android:icon → @mipmap/ic_launcher`);
|
|
217364
217909
|
};
|
|
217365
217910
|
|
|
@@ -218269,42 +218814,42 @@ var ANDROID_SDK_URL_DEFAULT = `https://github.com/${ANDROID_SDK_REPO}/releases/d
|
|
|
218269
218814
|
var sdkRepoUrl = () => process.env.LECODES_ANDROID_SDK_URL ?? ANDROID_SDK_URL_DEFAULT;
|
|
218270
218815
|
var ANDROID_SDK_OPTS = { repo: ANDROID_SDK_REPO, minVersion: ANDROID_SDK_MIN_VERSION, configKey: "android.sdk" };
|
|
218271
218816
|
var shellSdkVersionAndroid = (androidDir) => {
|
|
218272
|
-
const path2 =
|
|
218273
|
-
if (!
|
|
218817
|
+
const path2 = join17(androidDir, "lecodes-runtime", "build.gradle.kts");
|
|
218818
|
+
if (!existsSync16(path2))
|
|
218274
218819
|
return null;
|
|
218275
|
-
const m = /io\.letary:lecodes-sdk(?:-(?:core|2d|3d))?:(\d+\.\d+\.\d+)@aar/.exec(
|
|
218820
|
+
const m = /io\.letary:lecodes-sdk(?:-(?:core|2d|3d))?:(\d+\.\d+\.\d+)@aar/.exec(readFileSync14(path2, "utf8"));
|
|
218276
218821
|
return m ? m[1] : null;
|
|
218277
218822
|
};
|
|
218278
218823
|
var GRADLE_WRAPPER_JAR_URL = "https://raw.githubusercontent.com/gradle/gradle/v8.13.0/gradle/wrapper/gradle-wrapper.jar";
|
|
218279
218824
|
var GRADLE_WRAPPER_JAR_SHA256 = "81a82aaea5abcc8ff68b3dfcb58b3c3c429378efd98e7433460610fecd7ae45f";
|
|
218280
218825
|
var ensureGradleWrapperJar = async () => {
|
|
218281
|
-
const cached =
|
|
218282
|
-
if (
|
|
218283
|
-
return new Uint8Array(
|
|
218826
|
+
const cached = join17(homedir5(), ".lecodes", "gradle-wrapper", GRADLE_VERSION, "gradle-wrapper.jar");
|
|
218827
|
+
if (existsSync16(cached))
|
|
218828
|
+
return new Uint8Array(readFileSync14(cached));
|
|
218284
218829
|
try {
|
|
218285
218830
|
const res = await fetch(GRADLE_WRAPPER_JAR_URL);
|
|
218286
218831
|
if (!res.ok)
|
|
218287
218832
|
throw new Error(`HTTP ${res.status}`);
|
|
218288
218833
|
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
218289
|
-
const hash =
|
|
218834
|
+
const hash = createHash6("sha256").update(bytes).digest("hex");
|
|
218290
218835
|
if (hash !== GRADLE_WRAPPER_JAR_SHA256)
|
|
218291
218836
|
throw new Error(`sha256 mismatch (${hash})`);
|
|
218292
|
-
|
|
218293
|
-
|
|
218837
|
+
mkdirSync10(dirname10(cached), { recursive: true });
|
|
218838
|
+
writeFileSync12(cached, bytes);
|
|
218294
218839
|
return bytes;
|
|
218295
218840
|
} catch (e2) {
|
|
218296
218841
|
warn(`Could not fetch gradle-wrapper.jar (${e2.message}) — run \`gradle wrapper --gradle-version ${GRADLE_VERSION}\` inside android/, or open the project in Android Studio (it offers to repair the wrapper).`);
|
|
218297
218842
|
return null;
|
|
218298
218843
|
}
|
|
218299
218844
|
};
|
|
218300
|
-
var rmrf = (path2) =>
|
|
218845
|
+
var rmrf = (path2) => rmSync3(path2, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 });
|
|
218301
218846
|
var initAndroid = async (args) => {
|
|
218302
218847
|
const root = projectRoot();
|
|
218303
|
-
const androidDir =
|
|
218304
|
-
if (
|
|
218848
|
+
const androidDir = join17(root, "android");
|
|
218849
|
+
if (existsSync16(androidDir)) {
|
|
218305
218850
|
throw new CliError("android/ already exists — run `lecodes app sync` to update it.");
|
|
218306
218851
|
}
|
|
218307
|
-
const existing = readJson(
|
|
218852
|
+
const existing = readJson(join17(root, "app.json")) ?? {};
|
|
218308
218853
|
const variant = flagStr(args, "variant") ?? existing.android?.variant ?? "auto";
|
|
218309
218854
|
if (variant !== "auto" && !SDK_VARIANTS.includes(variant)) {
|
|
218310
218855
|
throw new CliError(`--variant must be auto, ${SDK_VARIANTS.join(", ")} (got "${variant}")`);
|
|
@@ -218312,7 +218857,7 @@ var initAndroid = async (args) => {
|
|
|
218312
218857
|
const sdkFlag = flagStr(args, "sdk") ?? existing.android?.sdk;
|
|
218313
218858
|
const sdkVersion = await resolveSdkVersion(process.env.LECODES_ANDROID_SDK ?? sdkFlag ?? "latest", null, ANDROID_SDK_OPTS);
|
|
218314
218859
|
const sdk = sdkFlag === "latest" ? "latest" : sdkVersion;
|
|
218315
|
-
const name = flagStr(args, "name") ?? existing.name ??
|
|
218860
|
+
const name = flagStr(args, "name") ?? existing.name ?? basename5(root);
|
|
218316
218861
|
const defaultAppId = `com.example.${slugify(name).replace(/-/g, "")}`;
|
|
218317
218862
|
const config = {
|
|
218318
218863
|
...existing,
|
|
@@ -218327,7 +218872,7 @@ var initAndroid = async (args) => {
|
|
|
218327
218872
|
...existing.android?.signing ? { signing: existing.android.signing } : {}
|
|
218328
218873
|
}
|
|
218329
218874
|
};
|
|
218330
|
-
writeJson(
|
|
218875
|
+
writeJson(join17(root, "app.json"), config);
|
|
218331
218876
|
log(`${c.green("written")} app.json`);
|
|
218332
218877
|
ensureShellIgnored(root, "android");
|
|
218333
218878
|
const identity = {
|
|
@@ -218338,9 +218883,9 @@ var initAndroid = async (args) => {
|
|
|
218338
218883
|
};
|
|
218339
218884
|
const pkgPath = identity.applicationId.replace(/\./g, "/");
|
|
218340
218885
|
const write = (rel, content) => {
|
|
218341
|
-
const abs =
|
|
218342
|
-
|
|
218343
|
-
|
|
218886
|
+
const abs = join17(androidDir, rel);
|
|
218887
|
+
mkdirSync10(dirname10(abs), { recursive: true });
|
|
218888
|
+
writeFileSync12(abs, content);
|
|
218344
218889
|
log(`${c.green("created")} android/${rel}`);
|
|
218345
218890
|
};
|
|
218346
218891
|
write("README.md", ANDROID_README);
|
|
@@ -218353,7 +218898,7 @@ var initAndroid = async (args) => {
|
|
|
218353
218898
|
write("gradlew.bat", GRADLEW_BAT.replace(/\n/g, `\r
|
|
218354
218899
|
`));
|
|
218355
218900
|
if (process.platform !== "win32")
|
|
218356
|
-
chmodSync(
|
|
218901
|
+
chmodSync(join17(androidDir, "gradlew"), 493);
|
|
218357
218902
|
const wrapperJar = await ensureGradleWrapperJar();
|
|
218358
218903
|
if (wrapperJar)
|
|
218359
218904
|
write("gradle/wrapper/gradle-wrapper.jar", wrapperJar);
|
|
@@ -218388,7 +218933,7 @@ var resolveGradlePlugin = (pluginId, entry) => {
|
|
|
218388
218933
|
return { id: entry, version };
|
|
218389
218934
|
};
|
|
218390
218935
|
var syncAndroid = async (root, config, prior) => {
|
|
218391
|
-
const androidDir =
|
|
218936
|
+
const androidDir = join17(root, "android");
|
|
218392
218937
|
const linkedSdk = shellSdkVersionAndroid(androidDir);
|
|
218393
218938
|
const sdkPin = process.env.LECODES_ANDROID_SDK ?? config.android?.sdk ?? linkedSdk ?? "latest";
|
|
218394
218939
|
const sdkVersion = await resolveSdkVersion(sdkPin, linkedSdk, ANDROID_SDK_OPTS);
|
|
@@ -218396,18 +218941,18 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
218396
218941
|
if (!Array.isArray(remotePatterns) || remotePatterns.some((p2) => typeof p2 !== "string")) {
|
|
218397
218942
|
throw new CliError('app.json "resources.remote" must be an array of glob strings (e.g. ["videos/**"]).');
|
|
218398
218943
|
}
|
|
218399
|
-
const assetsDir =
|
|
218944
|
+
const assetsDir = join17(androidDir, "app", "src", "main", "assets");
|
|
218400
218945
|
rmrf(assetsDir);
|
|
218401
|
-
|
|
218946
|
+
mkdirSync10(assetsDir, { recursive: true });
|
|
218402
218947
|
let bundleJs;
|
|
218403
218948
|
let resources;
|
|
218404
218949
|
if (prior) {
|
|
218405
|
-
|
|
218950
|
+
writeFileSync12(join17(assetsDir, "app.js"), prior.bundleJs);
|
|
218406
218951
|
bundleJs = prior.bundleJs;
|
|
218407
218952
|
resources = prior.resources;
|
|
218408
218953
|
} else {
|
|
218409
|
-
resources = await compileShell({ _: [], flags: { o:
|
|
218410
|
-
bundleJs =
|
|
218954
|
+
resources = await compileShell({ _: [], flags: { o: join17(assetsDir, "app.js") } }, { remotePatterns });
|
|
218955
|
+
bundleJs = readFileSync14(join17(assetsDir, "app.js"), "utf8");
|
|
218411
218956
|
}
|
|
218412
218957
|
const requestedVariant = config.android?.variant ?? "auto";
|
|
218413
218958
|
if (requestedVariant !== "auto" && !SDK_VARIANTS.includes(requestedVariant)) {
|
|
@@ -218426,14 +218971,14 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
218426
218971
|
core: "no 3D/2D engine use detected"
|
|
218427
218972
|
};
|
|
218428
218973
|
log(`${c.green("variant")} ${variant} → ${sdkArtifact}${requestedVariant === "auto" ? c.dim(` (auto: ${WHY[detected]})`) : c.dim(" (forced by android.variant)")}`);
|
|
218429
|
-
const lockPath =
|
|
218974
|
+
const lockPath = join17(root, "plugins.lock.json");
|
|
218430
218975
|
const oldLock = readJson(lockPath)?.plugins ?? {};
|
|
218431
218976
|
const pluginSpecs = Object.entries(config.plugins ?? {});
|
|
218432
218977
|
const runtimePlugins = [];
|
|
218433
218978
|
const gradlePlugins = new Map;
|
|
218434
218979
|
const aarWrapperPaths = [];
|
|
218435
218980
|
const newLock = {};
|
|
218436
|
-
const pluginsDir =
|
|
218981
|
+
const pluginsDir = join17(androidDir, "plugins");
|
|
218437
218982
|
for (const [id, spec] of pluginSpecs) {
|
|
218438
218983
|
const plugin = resolvePlugin(id, spec, oldLock[id], "android");
|
|
218439
218984
|
const android = plugin.manifest.android;
|
|
@@ -218441,10 +218986,10 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
218441
218986
|
const register = android.register;
|
|
218442
218987
|
if (!module || !register)
|
|
218443
218988
|
throw new CliError(`Plugin "${id}": manifest android.module / android.register missing`);
|
|
218444
|
-
const src =
|
|
218445
|
-
if (!
|
|
218989
|
+
const src = join17(plugin.dir, android.sources ?? "android");
|
|
218990
|
+
if (!existsSync16(src))
|
|
218446
218991
|
throw new CliError(`Plugin "${id}": sources dir ${src} missing`);
|
|
218447
|
-
const dest =
|
|
218992
|
+
const dest = join17(pluginsDir, module);
|
|
218448
218993
|
rmrf(dest);
|
|
218449
218994
|
cpSync(src, dest, { recursive: true });
|
|
218450
218995
|
if (android.minSdk && VERSION_RE.test(android.minSdk) && cmpVersion(sdkVersion, android.minSdk) < 0) {
|
|
@@ -218455,26 +219000,26 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
218455
219000
|
gradlePlugins.set(resolved.id, resolved.version);
|
|
218456
219001
|
}
|
|
218457
219002
|
const namespace = register.split(".").slice(0, -1).join(".");
|
|
218458
|
-
const libsDir =
|
|
218459
|
-
const aarNames =
|
|
219003
|
+
const libsDir = join17(dest, "libs");
|
|
219004
|
+
const aarNames = existsSync16(libsDir) ? readdirSync5(libsDir).filter((f3) => f3.endsWith(".aar")) : [];
|
|
218460
219005
|
for (const aar of aarNames) {
|
|
218461
219006
|
const base = aar.replace(/\.aar$/, "");
|
|
218462
|
-
const wrapDir =
|
|
218463
|
-
|
|
218464
|
-
|
|
218465
|
-
|
|
219007
|
+
const wrapDir = join17(libsDir, base);
|
|
219008
|
+
mkdirSync10(wrapDir, { recursive: true });
|
|
219009
|
+
renameSync2(join17(libsDir, aar), join17(wrapDir, aar));
|
|
219010
|
+
writeFileSync12(join17(wrapDir, "build.gradle.kts"), aarWrapperBuildGradle(aar));
|
|
218466
219011
|
aarWrapperPaths.push(`:plugins:${module}:libs:${base}`);
|
|
218467
219012
|
}
|
|
218468
|
-
const hasConsumerRules =
|
|
218469
|
-
|
|
219013
|
+
const hasConsumerRules = existsSync16(join17(dest, "consumer-rules.pro"));
|
|
219014
|
+
writeFileSync12(join17(dest, "build.gradle.kts"), pluginBuildGradleTemplate(id, module, namespace, sdkVersion, android.dependencies ?? [], aarNames, hasConsumerRules, sdkArtifact));
|
|
218470
219015
|
runtimePlugins.push({ id, module, register });
|
|
218471
219016
|
newLock[id] = plugin.lock;
|
|
218472
219017
|
log(`${c.green("plugin")} ${id}@${plugin.lock.version} (${plugin.lock.tag} → plugins/${module})`);
|
|
218473
219018
|
}
|
|
218474
219019
|
const keep = new Set(runtimePlugins.map((p2) => p2.module));
|
|
218475
|
-
for (const entry of
|
|
218476
|
-
if (!keep.has(entry) &&
|
|
218477
|
-
rmrf(
|
|
219020
|
+
for (const entry of existsSync16(pluginsDir) ? readdirSync5(pluginsDir) : []) {
|
|
219021
|
+
if (!keep.has(entry) && statSync3(join17(pluginsDir, entry)).isDirectory()) {
|
|
219022
|
+
rmrf(join17(pluginsDir, entry));
|
|
218478
219023
|
log(`${c.yellow("removed")} android/plugins/${entry} (plugin uninstalled)`);
|
|
218479
219024
|
}
|
|
218480
219025
|
}
|
|
@@ -218501,7 +219046,7 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
218501
219046
|
}
|
|
218502
219047
|
if (candidates.length > 1)
|
|
218503
219048
|
warn(`asset name collision: ${name} (${candidates.map((r2) => r2.path).join(", ")}) — embedding ${candidates[0].path}`);
|
|
218504
|
-
cpSync(candidates[0].absPath,
|
|
219049
|
+
cpSync(candidates[0].absPath, join17(assetsDir, name));
|
|
218505
219050
|
embedded++;
|
|
218506
219051
|
}
|
|
218507
219052
|
if (embedded > 0)
|
|
@@ -218516,15 +219061,15 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
218516
219061
|
if (shellUuid)
|
|
218517
219062
|
log(`${c.green("identity")} project ${shellUuid} baked into the shell (world identity)`);
|
|
218518
219063
|
const modules = runtimePlugins.map((p2) => p2.module);
|
|
218519
|
-
|
|
218520
|
-
|
|
218521
|
-
|
|
218522
|
-
|
|
218523
|
-
|
|
218524
|
-
const runtimeKt =
|
|
218525
|
-
|
|
218526
|
-
|
|
218527
|
-
if (pluginSpecs.length > 0 ||
|
|
219064
|
+
writeFileSync12(join17(androidDir, "README.md"), ANDROID_README);
|
|
219065
|
+
writeFileSync12(join17(androidDir, "settings.gradle.kts"), settingsGradleTemplate(slugify(config.name ?? basename5(root)), modules, aarWrapperPaths, sdkRepoUrl()));
|
|
219066
|
+
writeFileSync12(join17(androidDir, "build.gradle.kts"), rootBuildGradleTemplate([...gradlePlugins].map(([id, version]) => ({ id, version }))));
|
|
219067
|
+
writeFileSync12(join17(androidDir, "gradle.properties"), GRADLE_PROPERTIES);
|
|
219068
|
+
writeFileSync12(join17(androidDir, "lecodes-runtime", "build.gradle.kts"), runtimeBuildGradleTemplate(sdkVersion, modules, sdkArtifact));
|
|
219069
|
+
const runtimeKt = join17(androidDir, "lecodes-runtime", "src", "main", "kotlin", "io", "letary", "lecodes", "runtime", "LeCodesRuntime.kt");
|
|
219070
|
+
mkdirSync10(dirname10(runtimeKt), { recursive: true });
|
|
219071
|
+
writeFileSync12(runtimeKt, runtimeKotlinTemplate(runtimePlugins, shellUuid));
|
|
219072
|
+
if (pluginSpecs.length > 0 || existsSync16(lockPath))
|
|
218528
219073
|
writeJson(lockPath, { plugins: newLock });
|
|
218529
219074
|
restampAppBuildGradle(androidDir, config, gradlePlugins);
|
|
218530
219075
|
if (config.icon) {
|
|
@@ -218539,12 +219084,12 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
218539
219084
|
note("Build & run from Android Studio (`lecodes app open android`) — user files in android/app/ were not touched.");
|
|
218540
219085
|
};
|
|
218541
219086
|
var restampAppBuildGradle = (androidDir, config, gradlePlugins) => {
|
|
218542
|
-
const path2 =
|
|
218543
|
-
if (!
|
|
219087
|
+
const path2 = join17(androidDir, "app", "build.gradle.kts");
|
|
219088
|
+
if (!existsSync16(path2)) {
|
|
218544
219089
|
warn("android/app/build.gradle.kts is missing — run `lecodes app init android` on a clean tree to regenerate the shell.");
|
|
218545
219090
|
return;
|
|
218546
219091
|
}
|
|
218547
|
-
let content =
|
|
219092
|
+
let content = readFileSync14(path2, "utf8");
|
|
218548
219093
|
const before = content;
|
|
218549
219094
|
const versionName = config.version ?? "1.0.0";
|
|
218550
219095
|
const versionCode = config.android?.versionCode ?? 1;
|
|
@@ -218560,7 +219105,7 @@ var restampAppBuildGradle = (androidDir, config, gradlePlugins) => {
|
|
|
218560
219105
|
}
|
|
218561
219106
|
const applied = new Set;
|
|
218562
219107
|
for (const id of gradlePlugins.keys()) {
|
|
218563
|
-
if (id === "com.google.gms.google-services" && !
|
|
219108
|
+
if (id === "com.google.gms.google-services" && !existsSync16(join17(androidDir, "app", "google-services.json"))) {
|
|
218564
219109
|
warn(`push needs android/app/google-services.json (download it from the le.codes Firebase project — the app's package name must be registered there). The google-services Gradle plugin stays un-applied until the file exists, so FCM registration will fail.`);
|
|
218565
219110
|
continue;
|
|
218566
219111
|
}
|
|
@@ -218587,13 +219132,13 @@ var restampAppBuildGradle = (androidDir, config, gradlePlugins) => {
|
|
|
218587
219132
|
log(`${c.green("updated")} android/app/build.gradle.kts: + id("${id}")`);
|
|
218588
219133
|
}
|
|
218589
219134
|
if (content !== before)
|
|
218590
|
-
|
|
219135
|
+
writeFileSync12(path2, content);
|
|
218591
219136
|
};
|
|
218592
219137
|
var gradlew = (androidDir, tasks) => {
|
|
218593
219138
|
if (process.platform !== "win32")
|
|
218594
219139
|
return runTool(androidDir, "sh", ["gradlew", ...tasks]);
|
|
218595
|
-
const line = `""${
|
|
218596
|
-
const r2 =
|
|
219140
|
+
const line = `""${join17(androidDir, "gradlew.bat")}" ${tasks.join(" ")}"`;
|
|
219141
|
+
const r2 = spawnSync3("cmd.exe", ["/d", "/s", "/c", line], {
|
|
218597
219142
|
cwd: androidDir,
|
|
218598
219143
|
encoding: "utf8",
|
|
218599
219144
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -218612,19 +219157,19 @@ ${tail}`);
|
|
|
218612
219157
|
};
|
|
218613
219158
|
var buildAndroid = async (args) => {
|
|
218614
219159
|
const root = projectRoot();
|
|
218615
|
-
const config = readJson(
|
|
218616
|
-
const androidDir =
|
|
218617
|
-
if (!config?.android || !
|
|
219160
|
+
const config = readJson(join17(root, "app.json"));
|
|
219161
|
+
const androidDir = join17(root, "android");
|
|
219162
|
+
if (!config?.android || !existsSync16(androidDir)) {
|
|
218618
219163
|
throw new CliError("No Android shell here — run `lecodes app init android` first.");
|
|
218619
219164
|
}
|
|
218620
219165
|
if (!flagBool(args, "no-sync"))
|
|
218621
219166
|
await syncAndroid(root, config, null);
|
|
218622
|
-
if (!
|
|
219167
|
+
if (!existsSync16(join17(androidDir, "local.properties"))) {
|
|
218623
219168
|
const sdkHome = process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT;
|
|
218624
219169
|
if (!sdkHome) {
|
|
218625
219170
|
throw new CliError("Android SDK not found — set ANDROID_HOME, or create android/local.properties with sdk.dir=…");
|
|
218626
219171
|
}
|
|
218627
|
-
|
|
219172
|
+
writeFileSync12(join17(androidDir, "local.properties"), `sdk.dir=${sdkHome.replace(/\\/g, "\\\\")}
|
|
218628
219173
|
`);
|
|
218629
219174
|
note("Wrote android/local.properties (sdk.dir from the environment).");
|
|
218630
219175
|
}
|
|
@@ -218633,27 +219178,27 @@ var buildAndroid = async (args) => {
|
|
|
218633
219178
|
note(`Building (${tasks.join(" ")})… the first run downloads Gradle + dependencies`);
|
|
218634
219179
|
gradlew(androidDir, tasks);
|
|
218635
219180
|
const outputs = [];
|
|
218636
|
-
const apkDir =
|
|
218637
|
-
const aabDir =
|
|
219181
|
+
const apkDir = join17(androidDir, "app", "build", "outputs", "apk", configuration);
|
|
219182
|
+
const aabDir = join17(androidDir, "app", "build", "outputs", "bundle", configuration);
|
|
218638
219183
|
for (const dir of [apkDir, aabDir]) {
|
|
218639
|
-
if (!
|
|
219184
|
+
if (!existsSync16(dir))
|
|
218640
219185
|
continue;
|
|
218641
|
-
for (const f3 of
|
|
219186
|
+
for (const f3 of readdirSync5(dir)) {
|
|
218642
219187
|
if (f3.endsWith(".apk") || f3.endsWith(".aab"))
|
|
218643
|
-
outputs.push(
|
|
219188
|
+
outputs.push(join17(dir, f3));
|
|
218644
219189
|
}
|
|
218645
219190
|
}
|
|
218646
219191
|
for (const out of outputs)
|
|
218647
219192
|
log(`${c.green("output")} ${out}`);
|
|
218648
|
-
if (outputs.some((o2) =>
|
|
219193
|
+
if (outputs.some((o2) => basename5(o2).includes("unsigned"))) {
|
|
218649
219194
|
note("The APK is UNSIGNED — create android/keystore.properties (see android/README.md) and rebuild to ship it.");
|
|
218650
219195
|
}
|
|
218651
219196
|
success(configuration === "debug" ? "Debug build done — `adb install` the APK to test." : "Release build done — upload the .aab in the Play Console (the .apk side-loads for testing).");
|
|
218652
219197
|
};
|
|
218653
219198
|
var openAndroid = async () => {
|
|
218654
219199
|
const root = projectRoot();
|
|
218655
|
-
const androidDir =
|
|
218656
|
-
if (!
|
|
219200
|
+
const androidDir = join17(root, "android");
|
|
219201
|
+
if (!existsSync16(join17(androidDir, "settings.gradle.kts"))) {
|
|
218657
219202
|
throw new CliError("No Android shell here — run `lecodes app init android` first.");
|
|
218658
219203
|
}
|
|
218659
219204
|
if (process.platform === "darwin") {
|
|
@@ -218664,11 +219209,11 @@ var openAndroid = async () => {
|
|
|
218664
219209
|
} catch {}
|
|
218665
219210
|
} else if (process.platform === "win32") {
|
|
218666
219211
|
const candidates = [
|
|
218667
|
-
|
|
219212
|
+
join17(process.env.LOCALAPPDATA ?? "", "Programs", "Android Studio", "bin", "studio64.exe"),
|
|
218668
219213
|
"C:\\Program Files\\Android\\Android Studio\\bin\\studio64.exe"
|
|
218669
219214
|
];
|
|
218670
219215
|
for (const exe of candidates) {
|
|
218671
|
-
if (!
|
|
219216
|
+
if (!existsSync16(exe))
|
|
218672
219217
|
continue;
|
|
218673
219218
|
spawn(exe, [androidDir], { detached: true, stdio: "ignore" }).unref();
|
|
218674
219219
|
success("Opened android/ in Android Studio.");
|
|
@@ -218684,19 +219229,19 @@ var openAndroid = async () => {
|
|
|
218684
219229
|
note("Open the android/ folder in Android Studio.");
|
|
218685
219230
|
};
|
|
218686
219231
|
var doctorAndroidChecks = async (root, config, check) => {
|
|
218687
|
-
const androidDir =
|
|
218688
|
-
const java =
|
|
219232
|
+
const androidDir = join17(root, "android");
|
|
219233
|
+
const java = spawnSync3("java", ["-version"], { encoding: "utf8" });
|
|
218689
219234
|
const javaOut = `${java.stderr ?? ""}${java.stdout ?? ""}`;
|
|
218690
219235
|
const javaMajor = Number(/version "(\d+)/.exec(javaOut)?.[1] ?? 0);
|
|
218691
219236
|
check(javaMajor >= 17, `JDK ${javaMajor || "not found"}`, "install JDK 17+ (Android Studio ships one)");
|
|
218692
|
-
const hasSdkHome = !!(process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT) ||
|
|
219237
|
+
const hasSdkHome = !!(process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT) || existsSync16(join17(androidDir, "local.properties"));
|
|
218693
219238
|
check(hasSdkHome, "Android SDK location", "set ANDROID_HOME or create android/local.properties");
|
|
218694
219239
|
const requestedVariant = config.android?.variant ?? "auto";
|
|
218695
219240
|
const validVariant = requestedVariant === "auto" || SDK_VARIANTS.includes(requestedVariant);
|
|
218696
219241
|
check(validVariant, `android.variant "${requestedVariant}"`, `use "auto" or one of ${SDK_VARIANTS.join(", ")}`);
|
|
218697
|
-
const wrapperJar =
|
|
218698
|
-
check(
|
|
218699
|
-
check(
|
|
219242
|
+
const wrapperJar = join17(androidDir, "gradle", "wrapper", "gradle-wrapper.jar");
|
|
219243
|
+
check(existsSync16(wrapperJar) && statSync3(wrapperJar).size > 0, "Gradle wrapper jar", `run \`gradle wrapper --gradle-version ${GRADLE_VERSION}\` in android/ (or let Android Studio repair it)`);
|
|
219244
|
+
check(existsSync16(join17(androidDir, "app", "src", "main", "assets", "app.js")), "Bundled app.js", "run `lecodes app sync`");
|
|
218700
219245
|
const linked = shellSdkVersionAndroid(androidDir);
|
|
218701
219246
|
const pin = config.android?.sdk ?? "latest";
|
|
218702
219247
|
check(!!linked, `SDK ${linked ?? "version unreadable"}`, "run `lecodes app sync`");
|
|
@@ -218709,25 +219254,24 @@ var doctorAndroidChecks = async (root, config, check) => {
|
|
|
218709
219254
|
check(cmpVersion(linked, latest) >= 0, `SDK is the latest release (${latest})`, "run `lecodes app update`", false);
|
|
218710
219255
|
} catch {}
|
|
218711
219256
|
}
|
|
218712
|
-
const hasKeystore =
|
|
219257
|
+
const hasKeystore = existsSync16(join17(androidDir, "keystore.properties")) || !!process.env.LECODES_ANDROID_KEYSTORE_FILE;
|
|
218713
219258
|
check(hasKeystore, "Release signing configured", "android/keystore.properties or LECODES_ANDROID_* env (release builds come out unsigned without it)", false);
|
|
218714
219259
|
if (Object.keys(config.plugins ?? {}).includes("push")) {
|
|
218715
|
-
check(
|
|
219260
|
+
check(existsSync16(join17(androidDir, "app", "google-services.json")), "google-services.json present (push)", "download it from the le.codes Firebase project — FCM registration fails without it", false);
|
|
218716
219261
|
}
|
|
218717
219262
|
};
|
|
218718
219263
|
|
|
218719
219264
|
// src/commands/appDesktop.ts
|
|
218720
219265
|
import { execFileSync as execFileSync3, spawn as spawn3 } from "node:child_process";
|
|
218721
|
-
import { cpSync as cpSync2, existsSync as
|
|
218722
|
-
import { basename as
|
|
219266
|
+
import { cpSync as cpSync2, existsSync as existsSync18, mkdirSync as mkdirSync12, readdirSync as readdirSync7, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync14 } from "node:fs";
|
|
219267
|
+
import { basename as basename6, dirname as dirname11, join as join19, resolve as resolve6 } from "node:path";
|
|
218723
219268
|
|
|
218724
219269
|
// src/desktopRenderer.ts
|
|
219270
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
219271
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync2, readdirSync as readdirSync6, readFileSync as readFileSync15, rmSync as rmSync4, statSync as statSync4, writeFileSync as writeFileSync13 } from "node:fs";
|
|
219272
|
+
import { homedir as homedir6, tmpdir as tmpdir2 } from "node:os";
|
|
219273
|
+
import { join as join18, resolve as resolve5 } from "node:path";
|
|
218725
219274
|
init_util();
|
|
218726
|
-
import { createHash as createHash5 } from "node:crypto";
|
|
218727
|
-
import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
|
|
218728
|
-
import { copyFileSync, existsSync as existsSync14, mkdirSync as mkdirSync9, mkdtempSync, readdirSync as readdirSync5, readFileSync as readFileSync14, renameSync as renameSync2, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync11 } from "node:fs";
|
|
218729
|
-
import { homedir as homedir5, tmpdir } from "node:os";
|
|
218730
|
-
import { join as join15, resolve as resolve4 } from "node:path";
|
|
218731
219275
|
|
|
218732
219276
|
// ../viewer-lite/src/sourcemapError.ts
|
|
218733
219277
|
var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
@@ -218828,16 +219372,8 @@ var makeStackDemangler = (bundleJs) => {
|
|
|
218828
219372
|
};
|
|
218829
219373
|
|
|
218830
219374
|
// src/desktopRenderer.ts
|
|
218831
|
-
var MANIFEST_URL = "https://storage.yandexcloud.net/le-codes/releases/versions.json";
|
|
218832
219375
|
var DESKTOP_RENDERER_VERSION = "1.1.0";
|
|
218833
|
-
var
|
|
218834
|
-
const pa = a2.split(/[.-]/).map(Number), pb = b.split(/[.-]/).map(Number);
|
|
218835
|
-
for (let i2 = 0;i2 < 3; i2++)
|
|
218836
|
-
if ((pb[i2] ?? 0) !== (pa[i2] ?? 0))
|
|
218837
|
-
return (pb[i2] ?? 0) - (pa[i2] ?? 0);
|
|
218838
|
-
return 0;
|
|
218839
|
-
};
|
|
218840
|
-
var platformKey = () => {
|
|
219376
|
+
var platformKey2 = () => {
|
|
218841
219377
|
if (process.platform === "win32")
|
|
218842
219378
|
return "windows-x64";
|
|
218843
219379
|
if (process.platform === "linux")
|
|
@@ -218847,8 +219383,8 @@ var platformKey = () => {
|
|
|
218847
219383
|
var exeName = () => process.platform === "win32" ? "lecodes-desktop.exe" : "lecodes-desktop";
|
|
218848
219384
|
var findExe = (dir) => {
|
|
218849
219385
|
const name = exeName();
|
|
218850
|
-
for (const entry of
|
|
218851
|
-
const p2 =
|
|
219386
|
+
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
219387
|
+
const p2 = join18(dir, entry.name);
|
|
218852
219388
|
if (entry.isFile() && entry.name === name)
|
|
218853
219389
|
return p2;
|
|
218854
219390
|
if (entry.isDirectory()) {
|
|
@@ -218859,40 +219395,26 @@ var findExe = (dir) => {
|
|
|
218859
219395
|
}
|
|
218860
219396
|
return null;
|
|
218861
219397
|
};
|
|
218862
|
-
var
|
|
218863
|
-
var currentFile = () =>
|
|
219398
|
+
var cacheRoot2 = () => join18(homedir6(), ".lecodes", "renderer");
|
|
219399
|
+
var currentFile = () => join18(cacheRoot2(), "current.json");
|
|
218864
219400
|
var installedDesktopVersions = () => {
|
|
218865
219401
|
try {
|
|
218866
|
-
return
|
|
219402
|
+
return readdirSync6(cacheRoot2(), { withFileTypes: true }).filter((e2) => e2.isDirectory() && e2.name.startsWith("desktop-")).map((e2) => e2.name.slice("desktop-".length)).filter((v) => findExe(join18(cacheRoot2(), `desktop-${v}`)) !== null).sort(semverNewer);
|
|
218867
219403
|
} catch {
|
|
218868
219404
|
return [];
|
|
218869
219405
|
}
|
|
218870
219406
|
};
|
|
218871
219407
|
var selectedDesktopVersion = () => {
|
|
218872
219408
|
try {
|
|
218873
|
-
const saved = JSON.parse(
|
|
219409
|
+
const saved = JSON.parse(readFileSync15(currentFile(), "utf8")).version;
|
|
218874
219410
|
if (saved && /^\d+\.\d+\.\d+/.test(saved) && semverNewer(DESKTOP_RENDERER_VERSION, saved) > 0)
|
|
218875
219411
|
return saved;
|
|
218876
219412
|
} catch {}
|
|
218877
219413
|
return DESKTOP_RENDERER_VERSION;
|
|
218878
219414
|
};
|
|
218879
|
-
var fetchDesktopManifest =
|
|
218880
|
-
let manifest;
|
|
218881
|
-
try {
|
|
218882
|
-
const res = await fetch(MANIFEST_URL);
|
|
218883
|
-
if (!res.ok)
|
|
218884
|
-
throw new Error(`HTTP ${res.status}`);
|
|
218885
|
-
manifest = await res.json();
|
|
218886
|
-
} catch (e2) {
|
|
218887
|
-
throw new CliError(`Can't fetch the release manifest (${MANIFEST_URL}): ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
218888
|
-
}
|
|
218889
|
-
const desktop = manifest.products?.desktop;
|
|
218890
|
-
if (!desktop)
|
|
218891
|
-
throw new CliError("The release manifest has no desktop product.");
|
|
218892
|
-
return desktop;
|
|
218893
|
-
};
|
|
219415
|
+
var fetchDesktopManifest = () => fetchReleaseProduct("desktop", "desktop");
|
|
218894
219416
|
var downloadDesktop = async (version, dest) => {
|
|
218895
|
-
const key =
|
|
219417
|
+
const key = platformKey2();
|
|
218896
219418
|
const entry = (await fetchDesktopManifest()).versions.find((v) => v.version === version);
|
|
218897
219419
|
const file = entry?.files[key];
|
|
218898
219420
|
if (!file) {
|
|
@@ -218900,49 +219422,22 @@ var downloadDesktop = async (version, dest) => {
|
|
|
218900
219422
|
Set LECODES_DESKTOP_EXE to a locally built host, or update lecodes-cli.`);
|
|
218901
219423
|
}
|
|
218902
219424
|
note(`Downloading the native renderer (LeCodes Desktop ${version}, ${(file.size / 1024 / 1024).toFixed(1)} MB)…`);
|
|
218903
|
-
|
|
218904
|
-
|
|
218905
|
-
|
|
218906
|
-
|
|
218907
|
-
|
|
218908
|
-
bytes = Buffer.from(await res.arrayBuffer());
|
|
218909
|
-
} catch (e2) {
|
|
218910
|
-
throw new CliError(`Download failed (${file.url}): ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
218911
|
-
}
|
|
218912
|
-
const sha = createHash5("sha256").update(bytes).digest("hex");
|
|
218913
|
-
if (sha !== file.sha256)
|
|
218914
|
-
throw new CliError(`Downloaded archive is corrupt (sha256 mismatch) — try again.`);
|
|
218915
|
-
mkdirSync9(cacheRoot(), { recursive: true });
|
|
218916
|
-
const stage = mkdtempSync(join15(cacheRoot(), "dl-"));
|
|
218917
|
-
try {
|
|
218918
|
-
const archive = join15(stage, file.url.split("/").pop() ?? "archive");
|
|
218919
|
-
writeFileSync11(archive, bytes);
|
|
218920
|
-
const tar = spawnSync3("tar", ["-xf", archive, "-C", stage], { stdio: ["ignore", "ignore", "pipe"] });
|
|
218921
|
-
if (tar.status !== 0)
|
|
218922
|
-
throw new CliError(`Couldn't extract the renderer archive: ${tar.stderr?.toString() ?? "tar failed"}`);
|
|
218923
|
-
rmSync2(archive);
|
|
218924
|
-
if (!findExe(stage))
|
|
218925
|
-
throw new CliError("The downloaded archive doesn't contain the desktop host binary.");
|
|
218926
|
-
try {
|
|
218927
|
-
renameSync2(stage, dest);
|
|
218928
|
-
} catch {
|
|
218929
|
-
if (!findExe(dest))
|
|
218930
|
-
throw new CliError(`Couldn't move the renderer into ${dest}.`);
|
|
218931
|
-
}
|
|
218932
|
-
} finally {
|
|
218933
|
-
rmSync2(stage, { recursive: true, force: true });
|
|
218934
|
-
}
|
|
219425
|
+
await downloadReleaseArchive(file, {
|
|
219426
|
+
cacheRoot: cacheRoot2(),
|
|
219427
|
+
dest,
|
|
219428
|
+
validate: (dir) => findExe(dir) ? null : "The downloaded archive doesn't contain the desktop host binary."
|
|
219429
|
+
});
|
|
218935
219430
|
};
|
|
218936
219431
|
var resolveDesktopExe = async () => {
|
|
218937
219432
|
const override = process.env.LECODES_DESKTOP_EXE;
|
|
218938
219433
|
if (override) {
|
|
218939
|
-
if (!
|
|
219434
|
+
if (!existsSync17(override))
|
|
218940
219435
|
throw new CliError(`LECODES_DESKTOP_EXE points at "${override}" which doesn't exist.`);
|
|
218941
|
-
return
|
|
219436
|
+
return resolve5(override);
|
|
218942
219437
|
}
|
|
218943
219438
|
const version = selectedDesktopVersion();
|
|
218944
|
-
const dir =
|
|
218945
|
-
let exe =
|
|
219439
|
+
const dir = join18(cacheRoot2(), `desktop-${version}`);
|
|
219440
|
+
let exe = existsSync17(dir) ? findExe(dir) : null;
|
|
218946
219441
|
if (!exe) {
|
|
218947
219442
|
await downloadDesktop(version, dir);
|
|
218948
219443
|
exe = findExe(dir);
|
|
@@ -218957,12 +219452,12 @@ var updateDesktop = async () => {
|
|
|
218957
219452
|
throw new CliError(`The newest published LeCodes Desktop is ${latest}, but this CLI needs ≥ ${DESKTOP_RENDERER_VERSION} ` + `for the render contract. Publish the desktop release first.`);
|
|
218958
219453
|
}
|
|
218959
219454
|
const before = selectedDesktopVersion();
|
|
218960
|
-
const dir =
|
|
218961
|
-
const hadIt =
|
|
219455
|
+
const dir = join18(cacheRoot2(), `desktop-${latest}`);
|
|
219456
|
+
const hadIt = existsSync17(dir) && findExe(dir) !== null;
|
|
218962
219457
|
if (!hadIt)
|
|
218963
219458
|
await downloadDesktop(latest, dir);
|
|
218964
|
-
|
|
218965
|
-
|
|
219459
|
+
mkdirSync11(cacheRoot2(), { recursive: true });
|
|
219460
|
+
writeFileSync13(currentFile(), JSON.stringify({ version: latest }, null, 2));
|
|
218966
219461
|
if (latest === before && hadIt)
|
|
218967
219462
|
note(`Already up to date: LeCodes Desktop ${latest}.`);
|
|
218968
219463
|
else if (latest === before)
|
|
@@ -218988,14 +219483,19 @@ var windowEnv = (opts) => {
|
|
|
218988
219483
|
env.CREATOR_UI_SCALE = "fit";
|
|
218989
219484
|
else if (typeof opts.scale === "number")
|
|
218990
219485
|
env.CREATOR_UI_SCALE = String(opts.scale);
|
|
219486
|
+
if (typeof opts.renderScale === "number")
|
|
219487
|
+
env.CREATOR_RENDER_SCALE = String(opts.renderScale);
|
|
219488
|
+
if (opts.maxRenderSize && opts.maxRenderSize[0] > 0 && opts.maxRenderSize[1] > 0) {
|
|
219489
|
+
env.CREATOR_RENDER_MAX = `${Math.round(opts.maxRenderSize[0])}x${Math.round(opts.maxRenderSize[1])}`;
|
|
219490
|
+
}
|
|
218991
219491
|
return env;
|
|
218992
219492
|
};
|
|
218993
219493
|
var stageBundle = (js, resources) => {
|
|
218994
|
-
const runDir =
|
|
218995
|
-
|
|
219494
|
+
const runDir = mkdtempSync2(join18(tmpdir2(), "lecodes-render-"));
|
|
219495
|
+
writeFileSync13(join18(runDir, "app.js"), js);
|
|
218996
219496
|
for (const r2 of resources) {
|
|
218997
|
-
if (r2.absPath &&
|
|
218998
|
-
|
|
219497
|
+
if (r2.absPath && existsSync17(r2.absPath) && statSync4(r2.absPath).isFile()) {
|
|
219498
|
+
copyFileSync2(r2.absPath, join18(runDir, r2.name));
|
|
218999
219499
|
}
|
|
219000
219500
|
}
|
|
219001
219501
|
return runDir;
|
|
@@ -219023,7 +219523,7 @@ var runDesktopApp = async (js, resources, opts = {}) => {
|
|
|
219023
219523
|
const env = { ...process.env, ...windowEnv(opts) };
|
|
219024
219524
|
const demangle = makeStackDemangler(js);
|
|
219025
219525
|
await new Promise((done, fail) => {
|
|
219026
|
-
const child = demangle ? spawn2(exe, [
|
|
219526
|
+
const child = demangle ? spawn2(exe, [join18(runDir, "app.js")], { cwd: runDir, env, stdio: ["ignore", "pipe", "pipe"] }) : spawn2(exe, [join18(runDir, "app.js")], { cwd: runDir, env, stdio: ["ignore", "inherit", "inherit"] });
|
|
219027
219527
|
if (demangle) {
|
|
219028
219528
|
pipeThroughDemangler(child.stdout, process.stdout, demangle);
|
|
219029
219529
|
pipeThroughDemangler(child.stderr, process.stderr, demangle);
|
|
@@ -219037,7 +219537,7 @@ var runDesktopApp = async (js, resources, opts = {}) => {
|
|
|
219037
219537
|
});
|
|
219038
219538
|
});
|
|
219039
219539
|
} finally {
|
|
219040
|
-
|
|
219540
|
+
rmSync4(runDir, { recursive: true, force: true });
|
|
219041
219541
|
}
|
|
219042
219542
|
};
|
|
219043
219543
|
var runDesktopDev = async (url, opts = {}) => {
|
|
@@ -219053,8 +219553,8 @@ var runDesktopRender = async (js, resources, opts) => {
|
|
|
219053
219553
|
const exe = await resolveDesktopExe();
|
|
219054
219554
|
const runDir = stageBundle(js, resources);
|
|
219055
219555
|
try {
|
|
219056
|
-
const appJs =
|
|
219057
|
-
const outAbs =
|
|
219556
|
+
const appJs = join18(runDir, "app.js");
|
|
219557
|
+
const outAbs = resolve5(opts.outPng);
|
|
219058
219558
|
const env = {
|
|
219059
219559
|
...process.env,
|
|
219060
219560
|
CREATOR_DUMP_PNG: outAbs,
|
|
@@ -219063,8 +219563,8 @@ var runDesktopRender = async (js, resources, opts) => {
|
|
|
219063
219563
|
CREATOR_HIDDEN: "1"
|
|
219064
219564
|
};
|
|
219065
219565
|
if (opts.script !== undefined) {
|
|
219066
|
-
const scriptPath =
|
|
219067
|
-
|
|
219566
|
+
const scriptPath = join18(runDir, "script.txt");
|
|
219567
|
+
writeFileSync13(scriptPath, opts.script);
|
|
219068
219568
|
env.CREATOR_SCRIPT = scriptPath;
|
|
219069
219569
|
}
|
|
219070
219570
|
if (opts.fixedDtMs)
|
|
@@ -219096,7 +219596,7 @@ var runDesktopRender = async (js, resources, opts) => {
|
|
|
219096
219596
|
child.on("exit", (code) => {
|
|
219097
219597
|
clearTimeout(killer);
|
|
219098
219598
|
const output = demangle(chunks.join(""));
|
|
219099
|
-
if (code === 0 &&
|
|
219599
|
+
if (code === 0 && existsSync17(outAbs))
|
|
219100
219600
|
return done({ output, exitCode: code });
|
|
219101
219601
|
const tail = demangle(chunks.slice(-80).join("")).trimEnd();
|
|
219102
219602
|
const detail = tail.length > 0 ? `
|
|
@@ -219105,21 +219605,21 @@ ${tail}` : "";
|
|
|
219105
219605
|
});
|
|
219106
219606
|
});
|
|
219107
219607
|
} finally {
|
|
219108
|
-
|
|
219608
|
+
rmSync4(runDir, { recursive: true, force: true });
|
|
219109
219609
|
}
|
|
219110
219610
|
};
|
|
219111
219611
|
|
|
219112
219612
|
// src/commands/appDesktop.ts
|
|
219113
219613
|
init_util();
|
|
219114
219614
|
var mb = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
219115
|
-
var dirSize = (dir) =>
|
|
219116
|
-
const p2 =
|
|
219117
|
-
return sum + (e2.isDirectory() ? dirSize(p2) :
|
|
219615
|
+
var dirSize = (dir) => readdirSync7(dir, { withFileTypes: true }).reduce((sum, e2) => {
|
|
219616
|
+
const p2 = join19(dir, e2.name);
|
|
219617
|
+
return sum + (e2.isDirectory() ? dirSize(p2) : statSync5(p2).size);
|
|
219118
219618
|
}, 0);
|
|
219119
219619
|
var initDesktop = async (args) => {
|
|
219120
219620
|
const root = projectRoot();
|
|
219121
|
-
const existing = readJson(
|
|
219122
|
-
const name = flagStr(args, "name") ?? existing.name ??
|
|
219621
|
+
const existing = readJson(join19(root, "app.json")) ?? {};
|
|
219622
|
+
const name = flagStr(args, "name") ?? existing.name ?? basename6(root);
|
|
219123
219623
|
const window2 = {
|
|
219124
219624
|
width: 1280,
|
|
219125
219625
|
height: 800,
|
|
@@ -219129,19 +219629,19 @@ var initDesktop = async (args) => {
|
|
|
219129
219629
|
...flagBool(args, "fullscreen") ? { fullscreen: true } : {}
|
|
219130
219630
|
};
|
|
219131
219631
|
const config = { ...existing, name, desktop: { ...existing.desktop, window: window2 } };
|
|
219132
|
-
writeJson(
|
|
219632
|
+
writeJson(join19(root, "app.json"), config);
|
|
219133
219633
|
log(`${c.green("written")} app.json (desktop.window ${window2.width}×${window2.height}${window2.fullscreen ? ", fullscreen" : ""})`);
|
|
219134
219634
|
log(`Next: ${c.bold("lecodes dev --desktop")} to develop, ${c.bold("lecodes app build desktop")} to package.`);
|
|
219135
219635
|
};
|
|
219136
219636
|
var cleanFileName = (name) => name.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "").replace(/[. ]+$/, "").trim() || "app";
|
|
219137
219637
|
var buildDesktop = async (args) => {
|
|
219138
219638
|
const root = projectRoot();
|
|
219139
|
-
const config = readJson(
|
|
219140
|
-
const name = (config.name ??
|
|
219639
|
+
const config = readJson(join19(root, "app.json")) ?? {};
|
|
219640
|
+
const name = (config.name ?? basename6(root)).trim() || "app";
|
|
219141
219641
|
const slug = flagStr(args, "slug") ?? cleanFileName(name);
|
|
219142
|
-
const out =
|
|
219143
|
-
if (
|
|
219144
|
-
const entries =
|
|
219642
|
+
const out = resolve6(root, flagStr(args, "o", "out") ?? join19(root, "dist", slug));
|
|
219643
|
+
if (existsSync18(out)) {
|
|
219644
|
+
const entries = readdirSync7(out);
|
|
219145
219645
|
const looksLikeBuild = entries.length === 0 || entries.includes("app.js");
|
|
219146
219646
|
if (!looksLikeBuild) {
|
|
219147
219647
|
throw new CliError(`${out} exists and doesn't look like a previous desktop build — pick another -o or clear it yourself.`);
|
|
@@ -219150,12 +219650,12 @@ var buildDesktop = async (args) => {
|
|
|
219150
219650
|
if (config.desktop?.entry && flagStr(args, "entry") === undefined)
|
|
219151
219651
|
args.flags.entry = config.desktop.entry;
|
|
219152
219652
|
note(`Building "${name}" for desktop…`);
|
|
219153
|
-
const { js, resources } = await buildBundle(args, { remotePatterns: config.resources?.remote ?? [] });
|
|
219653
|
+
const { js, resources } = await buildBundle(args, { remotePatterns: config.resources?.remote ?? [] }, { localShaders: true });
|
|
219154
219654
|
const wanted = new Set([...js.matchAll(/fetchLocal\("([^"]+)"\)/g)].map((m) => m[1]));
|
|
219155
|
-
const shipped = resources.filter((r2) => !r2.remote && wanted.has(r2.name));
|
|
219655
|
+
const shipped = resources.filter((r2) => !r2.remote && (r2.shader || wanted.has(r2.name)));
|
|
219156
219656
|
const byName = new Map;
|
|
219157
219657
|
for (const r2 of resources) {
|
|
219158
|
-
if (r2.remote || !wanted.has(r2.name))
|
|
219658
|
+
if (r2.remote || r2.shader || !wanted.has(r2.name))
|
|
219159
219659
|
continue;
|
|
219160
219660
|
const set = byName.get(r2.name) ?? new Set;
|
|
219161
219661
|
set.add(r2.path);
|
|
@@ -219169,45 +219669,51 @@ var buildDesktop = async (args) => {
|
|
|
219169
219669
|
`)).join(`
|
|
219170
219670
|
`));
|
|
219171
219671
|
}
|
|
219672
|
+
const exe = await resolveDesktopExe();
|
|
219673
|
+
const hostDir = dirname11(exe);
|
|
219674
|
+
const IMPLICIT_PRELOADS = new Set(["uberarchive", "capture"]);
|
|
219675
|
+
const hostProvided = new Set;
|
|
219676
|
+
try {
|
|
219677
|
+
for (const f3 of readdirSync7(join19(hostDir, "assets")))
|
|
219678
|
+
hostProvided.add(f3);
|
|
219679
|
+
} catch {}
|
|
219172
219680
|
const shippedNames = new Set(shipped.map((r2) => r2.name));
|
|
219173
|
-
const missing = [...wanted].filter((n2) => !shippedNames.has(n2));
|
|
219681
|
+
const missing = [...wanted].filter((n2) => !shippedNames.has(n2) && !hostProvided.has(n2) && !IMPLICIT_PRELOADS.has(n2));
|
|
219174
219682
|
if (missing.length > 0) {
|
|
219175
219683
|
throw new CliError(`The bundle references assets with no local file: ${missing.join(", ")}`);
|
|
219176
219684
|
}
|
|
219177
|
-
const exe = await resolveDesktopExe();
|
|
219178
|
-
const hostDir = dirname11(exe);
|
|
219179
219685
|
const version = process.env.LECODES_DESKTOP_EXE ? "local build" : selectedDesktopVersion();
|
|
219180
|
-
|
|
219181
|
-
|
|
219182
|
-
for (const entry of
|
|
219686
|
+
rmSync5(out, { recursive: true, force: true });
|
|
219687
|
+
mkdirSync12(out, { recursive: true });
|
|
219688
|
+
for (const entry of readdirSync7(hostDir, { withFileTypes: true })) {
|
|
219183
219689
|
const keep = entry.isFile() && entry.name.toLowerCase().endsWith(".dll") || entry.isDirectory() && (entry.name === "assets" || entry.name === "fonts");
|
|
219184
219690
|
if (keep)
|
|
219185
|
-
cpSync2(
|
|
219691
|
+
cpSync2(join19(hostDir, entry.name), join19(out, entry.name), { recursive: true });
|
|
219186
219692
|
}
|
|
219187
|
-
if (!
|
|
219693
|
+
if (!existsSync18(join19(out, "assets", "initial.js"))) {
|
|
219188
219694
|
throw new CliError(`The host at ${hostDir} has no assets/initial.js next to it — a staged build can't boot. ` + `(LECODES_DESKTOP_EXE must point at an exe with its runtime assets/ beside it.)`);
|
|
219189
219695
|
}
|
|
219190
219696
|
const exeName2 = process.platform === "win32" ? `${cleanFileName(name)}.exe` : cleanFileName(name);
|
|
219191
|
-
cpSync2(exe,
|
|
219697
|
+
cpSync2(exe, join19(out, exeName2));
|
|
219192
219698
|
note(` host: LeCodes Desktop ${version}`);
|
|
219193
|
-
|
|
219699
|
+
writeFileSync14(join19(out, "app.js"), js);
|
|
219194
219700
|
let assetBytes = 0, assetCount = 0;
|
|
219195
219701
|
for (const r2 of shipped) {
|
|
219196
|
-
if (!r2.absPath || !
|
|
219702
|
+
if (!r2.absPath || !existsSync18(r2.absPath) || !statSync5(r2.absPath).isFile()) {
|
|
219197
219703
|
warn(`asset "${r2.name}" (${r2.path}) has no local file — skipped`);
|
|
219198
219704
|
continue;
|
|
219199
219705
|
}
|
|
219200
|
-
cpSync2(r2.absPath,
|
|
219201
|
-
assetBytes +=
|
|
219706
|
+
cpSync2(r2.absPath, join19(out, r2.name));
|
|
219707
|
+
assetBytes += statSync5(r2.absPath).size;
|
|
219202
219708
|
assetCount++;
|
|
219203
219709
|
}
|
|
219204
219710
|
note(` assets: ${assetCount} files, ${mb(assetBytes)}`);
|
|
219205
|
-
writeJson(
|
|
219711
|
+
writeJson(join19(out, "app.json"), { name, ...config.desktop ? { desktop: { window: config.desktop.window } } : {} });
|
|
219206
219712
|
if (config.icon) {
|
|
219207
|
-
await writeIco(root, config,
|
|
219713
|
+
await writeIco(root, config, join19(out, "app.ico"));
|
|
219208
219714
|
note(" app.ico rendered from " + config.icon);
|
|
219209
219715
|
}
|
|
219210
|
-
|
|
219716
|
+
writeFileSync14(join19(out, "README.txt"), "\uFEFF" + readmeText(name, exeName2, version));
|
|
219211
219717
|
success(`Portable build: ${out} (${mb(dirSize(out))})`);
|
|
219212
219718
|
if (flagBool(args, "probe"))
|
|
219213
219719
|
await probeBuild(out, exeName2, config, args);
|
|
@@ -219240,15 +219746,15 @@ var writeIco = async (root, config, dest) => {
|
|
|
219240
219746
|
header.writeUInt32LE(offset, e2 + 12);
|
|
219241
219747
|
offset += pngs[i2].length;
|
|
219242
219748
|
});
|
|
219243
|
-
|
|
219749
|
+
writeFileSync14(dest, Buffer.concat([header, ...pngs]));
|
|
219244
219750
|
};
|
|
219245
219751
|
var probeBuild = async (out, exeName2, config, args) => {
|
|
219246
219752
|
const win = config.desktop?.window;
|
|
219247
219753
|
const frames = Number(flagStr(args, "frames")) || 90;
|
|
219248
|
-
const shot =
|
|
219754
|
+
const shot = join19(dirname11(out), `${basename6(out)}-probe.png`);
|
|
219249
219755
|
note(`Probe: hidden render of the staged folder (${frames} frames)…`);
|
|
219250
219756
|
await new Promise((done, fail) => {
|
|
219251
|
-
const child = spawn3(
|
|
219757
|
+
const child = spawn3(join19(out, exeName2), [], {
|
|
219252
219758
|
cwd: out,
|
|
219253
219759
|
env: {
|
|
219254
219760
|
...process.env,
|
|
@@ -219277,7 +219783,7 @@ var probeBuild = async (out, exeName2, config, args) => {
|
|
|
219277
219783
|
});
|
|
219278
219784
|
child.on("exit", (code) => {
|
|
219279
219785
|
clearTimeout(killer);
|
|
219280
|
-
if (code === 0 &&
|
|
219786
|
+
if (code === 0 && existsSync18(shot)) {
|
|
219281
219787
|
success(`Probe: ${shot}`);
|
|
219282
219788
|
done();
|
|
219283
219789
|
} else {
|
|
@@ -219290,7 +219796,7 @@ ${tail.join("").trimEnd()}` : "";
|
|
|
219290
219796
|
};
|
|
219291
219797
|
var zipBuild = (out) => {
|
|
219292
219798
|
const zip = `${out}.zip`;
|
|
219293
|
-
|
|
219799
|
+
rmSync5(zip, { force: true });
|
|
219294
219800
|
note("Zipping…");
|
|
219295
219801
|
if (process.platform === "win32") {
|
|
219296
219802
|
const q = (s2) => s2.replaceAll("'", "''");
|
|
@@ -219304,9 +219810,9 @@ var zipBuild = (out) => {
|
|
|
219304
219810
|
"$zip.Dispose()"
|
|
219305
219811
|
].join(" ")], { stdio: ["ignore", "ignore", "pipe"] });
|
|
219306
219812
|
} else {
|
|
219307
|
-
execFileSync3("zip", ["-rq", zip,
|
|
219813
|
+
execFileSync3("zip", ["-rq", zip, basename6(out)], { cwd: dirname11(out) });
|
|
219308
219814
|
}
|
|
219309
|
-
success(`Zip: ${zip} (${mb(
|
|
219815
|
+
success(`Zip: ${zip} (${mb(statSync5(zip).size)})`);
|
|
219310
219816
|
};
|
|
219311
219817
|
var readmeText = (name, exeName2, version) => `${name}
|
|
219312
219818
|
${"=".repeat(Math.max(name.length, 4))}
|
|
@@ -219327,10 +219833,10 @@ var SDK_REPO = "letary/lecodes-ios-sdk";
|
|
|
219327
219833
|
var SDK_MIN_VERSION = "1.2.0";
|
|
219328
219834
|
var SDK_IDENTITY_VERSION = "1.6.0";
|
|
219329
219835
|
var shellSdkVersion = (iosDir) => {
|
|
219330
|
-
const path2 =
|
|
219331
|
-
if (!
|
|
219836
|
+
const path2 = join20(iosDir, "LeCodesRuntime", "Package.swift");
|
|
219837
|
+
if (!existsSync19(path2))
|
|
219332
219838
|
return null;
|
|
219333
|
-
const m = /lecodes-ios-sdk\.git",\s*(?:exact|from):\s*"([^"]+)"/.exec(
|
|
219839
|
+
const m = /lecodes-ios-sdk\.git",\s*(?:exact|from):\s*"([^"]+)"/.exec(readFileSync16(path2, "utf8"));
|
|
219334
219840
|
return m && VERSION_RE.test(m[1]) ? m[1] : null;
|
|
219335
219841
|
};
|
|
219336
219842
|
var IOS_SDK_OPTS = { repo: SDK_REPO, minVersion: SDK_MIN_VERSION, configKey: "ios.sdk" };
|
|
@@ -219344,11 +219850,11 @@ var appInit = async (args) => {
|
|
|
219344
219850
|
throw new CliError(platform ? `Unsupported platform "${platform}" — use ios, android or desktop.` : "Usage: lecodes app init <ios|android|desktop> [--name <display name>] [--bundle-id <id>] [--team <team id>] [--variant auto|core|2d|3d|full] [--app-id <application id>] [--sdk <version|latest>]");
|
|
219345
219851
|
}
|
|
219346
219852
|
const root = projectRoot();
|
|
219347
|
-
const iosDir =
|
|
219348
|
-
if (
|
|
219853
|
+
const iosDir = join20(root, "ios");
|
|
219854
|
+
if (existsSync19(iosDir)) {
|
|
219349
219855
|
throw new CliError("ios/ already exists — run `lecodes app sync` to update it.");
|
|
219350
219856
|
}
|
|
219351
|
-
const existing = readJson(
|
|
219857
|
+
const existing = readJson(join20(root, "app.json")) ?? {};
|
|
219352
219858
|
const variant = flagStr(args, "variant") ?? existing.ios?.variant ?? "auto";
|
|
219353
219859
|
if (variant !== "auto" && !SDK_VARIANTS.includes(variant)) {
|
|
219354
219860
|
throw new CliError(`--variant must be auto, ${SDK_VARIANTS.join(", ")} (got "${variant}")`);
|
|
@@ -219356,7 +219862,7 @@ var appInit = async (args) => {
|
|
|
219356
219862
|
const sdkFlag = flagStr(args, "sdk") ?? existing.ios?.sdk;
|
|
219357
219863
|
const sdkVersion = await resolveSdkVersion(process.env.LECODES_IOS_SDK ?? sdkFlag ?? "latest", null, IOS_SDK_OPTS);
|
|
219358
219864
|
const sdk = sdkFlag === "latest" ? "latest" : sdkVersion;
|
|
219359
|
-
const name = flagStr(args, "name") ?? existing.name ??
|
|
219865
|
+
const name = flagStr(args, "name") ?? existing.name ?? basename7(root);
|
|
219360
219866
|
const config = {
|
|
219361
219867
|
...existing,
|
|
219362
219868
|
name,
|
|
@@ -219370,7 +219876,7 @@ var appInit = async (args) => {
|
|
|
219370
219876
|
buildNumber: existing.ios?.buildNumber ?? 1
|
|
219371
219877
|
}
|
|
219372
219878
|
};
|
|
219373
|
-
writeJson(
|
|
219879
|
+
writeJson(join20(root, "app.json"), config);
|
|
219374
219880
|
log(`${c.green("written")} app.json`);
|
|
219375
219881
|
ensureShellIgnored(root, "ios");
|
|
219376
219882
|
const identity = {
|
|
@@ -219381,9 +219887,9 @@ var appInit = async (args) => {
|
|
|
219381
219887
|
buildNumber: config.ios.buildNumber
|
|
219382
219888
|
};
|
|
219383
219889
|
const write = (rel, content) => {
|
|
219384
|
-
const abs =
|
|
219385
|
-
|
|
219386
|
-
|
|
219890
|
+
const abs = join20(iosDir, rel);
|
|
219891
|
+
mkdirSync13(dirname12(abs), { recursive: true });
|
|
219892
|
+
writeFileSync15(abs, content);
|
|
219387
219893
|
log(`${c.green("created")} ios/${rel}`);
|
|
219388
219894
|
};
|
|
219389
219895
|
write("README.md", IOS_README);
|
|
@@ -219411,23 +219917,23 @@ var ensureSdkResourcesBundle = async (sdkVersion) => {
|
|
|
219411
219917
|
const override = process.env.LECODES_SDK_RESOURCES;
|
|
219412
219918
|
if (override) {
|
|
219413
219919
|
note(`Using local resources bundle ${override} (LECODES_SDK_RESOURCES)`);
|
|
219414
|
-
return
|
|
219920
|
+
return resolve7(override);
|
|
219415
219921
|
}
|
|
219416
|
-
const dir =
|
|
219417
|
-
const bundle =
|
|
219418
|
-
if (
|
|
219922
|
+
const dir = join20(homedir7(), ".lecodes", "sdk-resources", sdkVersion);
|
|
219923
|
+
const bundle = join20(dir, "LeCodesSDKResources.bundle");
|
|
219924
|
+
if (existsSync19(bundle))
|
|
219419
219925
|
return bundle;
|
|
219420
219926
|
const url = `https://github.com/${SDK_REPO}/releases/download/${sdkVersion}/LeCodesSDKResources.bundle.zip`;
|
|
219421
219927
|
note(`Downloading LeCodesSDKResources.bundle (SDK ${sdkVersion})…`);
|
|
219422
219928
|
const res = await fetch(url);
|
|
219423
219929
|
if (!res.ok)
|
|
219424
219930
|
throw new CliError(`Resources bundle download failed (HTTP ${res.status}) — ${url}`);
|
|
219425
|
-
|
|
219426
|
-
const zip =
|
|
219427
|
-
|
|
219931
|
+
mkdirSync13(dir, { recursive: true });
|
|
219932
|
+
const zip = join20(dir, "LeCodesSDKResources.bundle.zip");
|
|
219933
|
+
writeFileSync15(zip, new Uint8Array(await res.arrayBuffer()));
|
|
219428
219934
|
execFileSync4("ditto", ["-x", "-k", zip, dir]);
|
|
219429
|
-
|
|
219430
|
-
if (!
|
|
219935
|
+
rmSync6(zip);
|
|
219936
|
+
if (!existsSync19(bundle))
|
|
219431
219937
|
throw new CliError(`No LeCodesSDKResources.bundle inside the release asset ${url}`);
|
|
219432
219938
|
return bundle;
|
|
219433
219939
|
};
|
|
@@ -219452,13 +219958,13 @@ var resolveUpdateUrl = (update, root) => {
|
|
|
219452
219958
|
};
|
|
219453
219959
|
var appSync = async (args) => {
|
|
219454
219960
|
const root = projectRoot();
|
|
219455
|
-
const config = readJson(
|
|
219961
|
+
const config = readJson(join20(root, "app.json"));
|
|
219456
219962
|
const platform = args._[1];
|
|
219457
219963
|
if (platform !== undefined && platform !== "ios" && platform !== "android") {
|
|
219458
219964
|
throw new CliError(`Unknown platform "${platform}" — use ios or android.`);
|
|
219459
219965
|
}
|
|
219460
|
-
const hasIos = !!config?.ios &&
|
|
219461
|
-
const hasAndroid = !!config?.android &&
|
|
219966
|
+
const hasIos = !!config?.ios && existsSync19(join20(root, "ios"));
|
|
219967
|
+
const hasAndroid = !!config?.android && existsSync19(join20(root, "android"));
|
|
219462
219968
|
if (platform === "ios" && !hasIos)
|
|
219463
219969
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
219464
219970
|
if (platform === "android" && !hasAndroid)
|
|
@@ -219473,7 +219979,7 @@ var appSync = async (args) => {
|
|
|
219473
219979
|
await syncAndroid(root, config, iosArtifacts);
|
|
219474
219980
|
};
|
|
219475
219981
|
var syncIos = async (root, config) => {
|
|
219476
|
-
const iosDir =
|
|
219982
|
+
const iosDir = join20(root, "ios");
|
|
219477
219983
|
if (!config.ios)
|
|
219478
219984
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
219479
219985
|
const requestedVariant = config.ios.variant ?? "auto";
|
|
@@ -219483,14 +219989,14 @@ var syncIos = async (root, config) => {
|
|
|
219483
219989
|
const linkedSdk = shellSdkVersion(iosDir);
|
|
219484
219990
|
const sdkPin = process.env.LECODES_IOS_SDK ?? config.ios.sdk ?? linkedSdk ?? "latest";
|
|
219485
219991
|
const sdkVersion = await resolveSdkVersion(sdkPin, linkedSdk, IOS_SDK_OPTS);
|
|
219486
|
-
const lockPath =
|
|
219992
|
+
const lockPath = join20(root, "plugins.lock.json");
|
|
219487
219993
|
const oldLock = readJson(lockPath)?.plugins ?? {};
|
|
219488
219994
|
const pluginSpecs = Object.entries(config.plugins ?? {});
|
|
219489
219995
|
const runtimePlugins = [];
|
|
219490
219996
|
const infoValues = {};
|
|
219491
219997
|
const entitlementValues = {};
|
|
219492
219998
|
const newLock = {};
|
|
219493
|
-
const sourcesDir =
|
|
219999
|
+
const sourcesDir = join20(iosDir, "LeCodesRuntime", "Sources");
|
|
219494
220000
|
for (const [id, spec] of pluginSpecs) {
|
|
219495
220001
|
const plugin = resolvePlugin(id, spec, oldLock[id], "ios");
|
|
219496
220002
|
const ios = plugin.manifest.ios;
|
|
@@ -219498,11 +220004,11 @@ var syncIos = async (root, config) => {
|
|
|
219498
220004
|
const register = ios.register;
|
|
219499
220005
|
if (!module || !register)
|
|
219500
220006
|
throw new CliError(`Plugin "${id}": manifest ios.module / ios.register missing`);
|
|
219501
|
-
const src =
|
|
219502
|
-
if (!
|
|
220007
|
+
const src = join20(plugin.dir, ios.sources ?? "ios");
|
|
220008
|
+
if (!existsSync19(src))
|
|
219503
220009
|
throw new CliError(`Plugin "${id}": sources dir ${src} missing`);
|
|
219504
|
-
const dest =
|
|
219505
|
-
|
|
220010
|
+
const dest = join20(sourcesDir, module);
|
|
220011
|
+
rmSync6(dest, { recursive: true, force: true });
|
|
219506
220012
|
cpSync3(src, dest, { recursive: true });
|
|
219507
220013
|
if (ios.minSdk && VERSION_RE.test(ios.minSdk) && cmpVersion(sdkVersion, ios.minSdk) < 0) {
|
|
219508
220014
|
throw new CliError(`Plugin "${id}" needs lecodes-ios-sdk ${ios.minSdk} but the shell links ${sdkVersion} — run \`lecodes app update\` (or set app.json ios.sdk).`);
|
|
@@ -219518,9 +220024,9 @@ var syncIos = async (root, config) => {
|
|
|
219518
220024
|
log(`${c.green("plugin")} ${id}@${plugin.lock.version} (${plugin.lock.tag} → Sources/${module})`);
|
|
219519
220025
|
}
|
|
219520
220026
|
const keep = new Set(["LeCodesRuntime", ...runtimePlugins.map((p2) => p2.module)]);
|
|
219521
|
-
for (const entry of
|
|
219522
|
-
if (!keep.has(entry) &&
|
|
219523
|
-
|
|
220027
|
+
for (const entry of existsSync19(sourcesDir) ? readdirSync8(sourcesDir) : []) {
|
|
220028
|
+
if (!keep.has(entry) && statSync6(join20(sourcesDir, entry)).isDirectory()) {
|
|
220029
|
+
rmSync6(join20(sourcesDir, entry), { recursive: true });
|
|
219524
220030
|
log(`${c.yellow("removed")} ios/LeCodesRuntime/Sources/${entry} (plugin uninstalled)`);
|
|
219525
220031
|
}
|
|
219526
220032
|
}
|
|
@@ -219528,11 +220034,11 @@ var syncIos = async (root, config) => {
|
|
|
219528
220034
|
if (!Array.isArray(remotePatterns) || remotePatterns.some((p2) => typeof p2 !== "string")) {
|
|
219529
220035
|
throw new CliError('app.json "resources.remote" must be an array of glob strings (e.g. ["videos/**"]).');
|
|
219530
220036
|
}
|
|
219531
|
-
const resourcesDir =
|
|
219532
|
-
|
|
219533
|
-
|
|
219534
|
-
const resources = await compileShell({ _: [], flags: { o:
|
|
219535
|
-
const bundleJs =
|
|
220037
|
+
const resourcesDir = join20(iosDir, "App", "Resources");
|
|
220038
|
+
rmSync6(resourcesDir, { recursive: true, force: true });
|
|
220039
|
+
mkdirSync13(resourcesDir, { recursive: true });
|
|
220040
|
+
const resources = await compileShell({ _: [], flags: { o: join20(resourcesDir, "app.js") } }, { remotePatterns });
|
|
220041
|
+
const bundleJs = readFileSync16(join20(resourcesDir, "app.js"), "utf8");
|
|
219536
220042
|
const { variant, detected } = resolveVariant(requestedVariant, bundleJs);
|
|
219537
220043
|
const WHY = {
|
|
219538
220044
|
core: "UI only",
|
|
@@ -219546,8 +220052,8 @@ var syncIos = async (root, config) => {
|
|
|
219546
220052
|
const sdkProvided = new Set;
|
|
219547
220053
|
if (variant === "3d" || variant === "full") {
|
|
219548
220054
|
const sdkResources = await ensureSdkResourcesBundle(sdkVersion);
|
|
219549
|
-
cpSync3(sdkResources,
|
|
219550
|
-
for (const f3 of
|
|
220055
|
+
cpSync3(sdkResources, join20(resourcesDir, "LeCodesSDKResources.bundle"), { recursive: true });
|
|
220056
|
+
for (const f3 of readdirSync8(sdkResources))
|
|
219551
220057
|
if (f3 !== "Info.plist")
|
|
219552
220058
|
sdkProvided.add(f3);
|
|
219553
220059
|
log(`${c.green("resources")} LeCodesSDKResources.bundle (filament) → ios/App/Resources/`);
|
|
@@ -219574,7 +220080,7 @@ var syncIos = async (root, config) => {
|
|
|
219574
220080
|
}
|
|
219575
220081
|
if (candidates.length > 1)
|
|
219576
220082
|
warn(`asset name collision: ${name} (${candidates.map((r2) => r2.path).join(", ")}) — embedding ${candidates[0].path}`);
|
|
219577
|
-
cpSync3(candidates[0].absPath,
|
|
220083
|
+
cpSync3(candidates[0].absPath, join20(resourcesDir, name));
|
|
219578
220084
|
embedded++;
|
|
219579
220085
|
}
|
|
219580
220086
|
if (embedded > 0)
|
|
@@ -219594,14 +220100,14 @@ var syncIos = async (root, config) => {
|
|
|
219594
220100
|
}
|
|
219595
220101
|
if (shellUuid)
|
|
219596
220102
|
log(`${c.green("identity")} project ${shellUuid} baked into the shell (world identity)`);
|
|
219597
|
-
|
|
219598
|
-
|
|
219599
|
-
|
|
220103
|
+
writeFileSync15(join20(iosDir, "README.md"), IOS_README);
|
|
220104
|
+
writeFileSync15(join20(iosDir, "LeCodesRuntime", "Package.swift"), runtimePackageTemplate(sdkVersion, variant, runtimePlugins));
|
|
220105
|
+
writeFileSync15(join20(sourcesDir, "LeCodesRuntime", "LeCodesRuntime.swift"), runtimeSwiftTemplate(runtimePlugins, shellUuid));
|
|
219600
220106
|
const mergedKeys = {};
|
|
219601
220107
|
for (const [key, values] of Object.entries(infoValues))
|
|
219602
220108
|
mergedKeys[key] = [...new Set(values)].join(" ");
|
|
219603
220109
|
const updateUrl = resolveUpdateUrl(config.update, root);
|
|
219604
|
-
|
|
220110
|
+
writeFileSync15(join20(iosDir, "App", "Info.plist"), infoPlistTemplate(mergedKeys, updateUrl));
|
|
219605
220111
|
if (updateUrl)
|
|
219606
220112
|
log(`${c.green("update")} OTA source ${updateUrl} — checked natively at launch, new code applies next launch`);
|
|
219607
220113
|
else if (!config.update) {
|
|
@@ -219612,10 +220118,10 @@ var syncIos = async (root, config) => {
|
|
|
219612
220118
|
if (manifest)
|
|
219613
220119
|
note(`tip: add "update": true to app.json for over-the-air JS updates — ship with \`lecodes push --publish\`, no store release`);
|
|
219614
220120
|
}
|
|
219615
|
-
if (pluginSpecs.length > 0 ||
|
|
220121
|
+
if (pluginSpecs.length > 0 || existsSync19(lockPath))
|
|
219616
220122
|
writeJson(lockPath, { plugins: newLock });
|
|
219617
|
-
const pbxPath =
|
|
219618
|
-
let pbx =
|
|
220123
|
+
const pbxPath = join20(iosDir, "App.xcodeproj", "project.pbxproj");
|
|
220124
|
+
let pbx = readFileSync16(pbxPath, "utf8").replace(/MARKETING_VERSION = [^;]+;/g, `MARKETING_VERSION = ${config.version ?? "1.0.0"};`).replace(/CURRENT_PROJECT_VERSION = [^;]+;/g, `CURRENT_PROJECT_VERSION = ${config.ios.buildNumber ?? 1};`);
|
|
219619
220125
|
const mergedEntitlements = {};
|
|
219620
220126
|
for (const [key, values] of Object.entries(entitlementValues)) {
|
|
219621
220127
|
if (values.every((v) => Array.isArray(v))) {
|
|
@@ -219636,7 +220142,7 @@ var syncIos = async (root, config) => {
|
|
|
219636
220142
|
}
|
|
219637
220143
|
const hasEntitlements = Object.keys(mergedEntitlements).length > 0;
|
|
219638
220144
|
if (pbx.includes("CODE_SIGN_ENTITLEMENTS") || hasEntitlements) {
|
|
219639
|
-
|
|
220145
|
+
writeFileSync15(join20(iosDir, "App", "App.entitlements"), entitlementsTemplate(mergedEntitlements));
|
|
219640
220146
|
if (hasEntitlements)
|
|
219641
220147
|
log(`${c.green("caps")} App/App.entitlements: ${Object.keys(mergedEntitlements).sort().join(", ")}`);
|
|
219642
220148
|
if (!pbx.includes("CODE_SIGN_ENTITLEMENTS")) {
|
|
@@ -219652,7 +220158,7 @@ $2Info.plist,`);
|
|
|
219652
220158
|
}
|
|
219653
220159
|
}
|
|
219654
220160
|
}
|
|
219655
|
-
|
|
220161
|
+
writeFileSync15(pbxPath, pbx);
|
|
219656
220162
|
if (config.icon) {
|
|
219657
220163
|
await writeIosAppIcon(iosDir, await loadIconMaster(root, config));
|
|
219658
220164
|
}
|
|
@@ -219664,7 +220170,7 @@ $2Info.plist,`);
|
|
|
219664
220170
|
var appPlugin = async (args) => {
|
|
219665
220171
|
const sub = args._[1];
|
|
219666
220172
|
const root = projectRoot();
|
|
219667
|
-
const configPath =
|
|
220173
|
+
const configPath = join20(root, "app.json");
|
|
219668
220174
|
const config = readJson(configPath);
|
|
219669
220175
|
if (!config)
|
|
219670
220176
|
throw new CliError("No app.json here — run `lecodes app init <ios|android>` first.");
|
|
@@ -219675,7 +220181,7 @@ var appPlugin = async (args) => {
|
|
|
219675
220181
|
log("No plugins installed.");
|
|
219676
220182
|
return;
|
|
219677
220183
|
}
|
|
219678
|
-
const lock = readJson(
|
|
220184
|
+
const lock = readJson(join20(root, "plugins.lock.json"))?.plugins ?? {};
|
|
219679
220185
|
for (const [id2, spec] of entries) {
|
|
219680
220186
|
const pin = lock[id2] ? c.dim(` ${lock[id2].repo} ${lock[id2].tag} (${lock[id2].commit.slice(0, 7)})`) : c.dim(" (not synced)");
|
|
219681
220187
|
log(`${c.bold(id2)} ${spec}${pin}`);
|
|
@@ -219728,14 +220234,14 @@ var exportOptionsPlist = (method, teamId, destination) => `<?xml version="1.0" e
|
|
|
219728
220234
|
`;
|
|
219729
220235
|
var requireShell = () => {
|
|
219730
220236
|
const root = projectRoot();
|
|
219731
|
-
const config = readJson(
|
|
219732
|
-
if (!config?.ios || !
|
|
220237
|
+
const config = readJson(join20(root, "app.json"));
|
|
220238
|
+
if (!config?.ios || !existsSync19(join20(root, "ios"))) {
|
|
219733
220239
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
219734
220240
|
}
|
|
219735
220241
|
return { root, config };
|
|
219736
220242
|
};
|
|
219737
220243
|
var updateSdkPin = async (root, config, args, checkOnly, platform) => {
|
|
219738
|
-
const [label, repo, minVersion, shellVersion] = platform === "ios" ? ["lecodes-ios-sdk", SDK_REPO, SDK_MIN_VERSION, () => shellSdkVersion(
|
|
220244
|
+
const [label, repo, minVersion, shellVersion] = platform === "ios" ? ["lecodes-ios-sdk", SDK_REPO, SDK_MIN_VERSION, () => shellSdkVersion(join20(root, "ios"))] : ["lecodes-android-sdk", ANDROID_SDK_REPO, ANDROID_SDK_MIN_VERSION, () => shellSdkVersionAndroid(join20(root, "android"))];
|
|
219739
220245
|
const section = config[platform];
|
|
219740
220246
|
const wanted = flagStr(args, "sdk");
|
|
219741
220247
|
let changed = false;
|
|
@@ -219766,19 +220272,19 @@ var updateSdkPin = async (root, config, args, checkOnly, platform) => {
|
|
|
219766
220272
|
}
|
|
219767
220273
|
if (!checkOnly && nextPin !== pin) {
|
|
219768
220274
|
section.sdk = nextPin;
|
|
219769
|
-
writeJson(
|
|
220275
|
+
writeJson(join20(root, "app.json"), config);
|
|
219770
220276
|
}
|
|
219771
220277
|
return changed;
|
|
219772
220278
|
};
|
|
219773
220279
|
var appUpdate = async (args) => {
|
|
219774
220280
|
const root = projectRoot();
|
|
219775
|
-
const config = readJson(
|
|
220281
|
+
const config = readJson(join20(root, "app.json"));
|
|
219776
220282
|
const platform = args._[1];
|
|
219777
220283
|
if (platform !== undefined && platform !== "ios" && platform !== "android") {
|
|
219778
220284
|
throw new CliError(`Unknown platform "${platform}" — use ios or android.`);
|
|
219779
220285
|
}
|
|
219780
|
-
const hasIos = !!config?.ios &&
|
|
219781
|
-
const hasAndroid = !!config?.android &&
|
|
220286
|
+
const hasIos = !!config?.ios && existsSync19(join20(root, "ios"));
|
|
220287
|
+
const hasAndroid = !!config?.android && existsSync19(join20(root, "android"));
|
|
219782
220288
|
if (platform === "ios" && !hasIos)
|
|
219783
220289
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
219784
220290
|
if (platform === "android" && !hasAndroid)
|
|
@@ -219796,7 +220302,7 @@ var appUpdate = async (args) => {
|
|
|
219796
220302
|
changed = await updateSdkPin(root, config, args, checkOnly, "ios") || changed;
|
|
219797
220303
|
if (doAndroid)
|
|
219798
220304
|
changed = await updateSdkPin(root, config, args, checkOnly, "android") || changed;
|
|
219799
|
-
const lockPath =
|
|
220305
|
+
const lockPath = join20(root, "plugins.lock.json");
|
|
219800
220306
|
const lock = readJson(lockPath)?.plugins ?? {};
|
|
219801
220307
|
const specs = Object.entries(config.plugins ?? {});
|
|
219802
220308
|
if (specs.length > 0 && !flagBool(args, "no-plugins")) {
|
|
@@ -219825,7 +220331,7 @@ var appUpdate = async (args) => {
|
|
|
219825
220331
|
log(`${c.green("plugin")} ${id} ${entry?.tag ?? "—"} → ${c.bold(tag)}`);
|
|
219826
220332
|
changed = true;
|
|
219827
220333
|
}
|
|
219828
|
-
if (!checkOnly &&
|
|
220334
|
+
if (!checkOnly && existsSync19(lockPath))
|
|
219829
220335
|
writeJson(lockPath, { plugins: kept });
|
|
219830
220336
|
}
|
|
219831
220337
|
log("");
|
|
@@ -219845,9 +220351,9 @@ var appUpdate = async (args) => {
|
|
|
219845
220351
|
};
|
|
219846
220352
|
var appDoctor = async (_args) => {
|
|
219847
220353
|
const root = projectRoot();
|
|
219848
|
-
const config = readJson(
|
|
219849
|
-
const hasIos = !!config?.ios &&
|
|
219850
|
-
const hasAndroid = !!config?.android &&
|
|
220354
|
+
const config = readJson(join20(root, "app.json"));
|
|
220355
|
+
const hasIos = !!config?.ios && existsSync19(join20(root, "ios"));
|
|
220356
|
+
const hasAndroid = !!config?.android && existsSync19(join20(root, "android"));
|
|
219851
220357
|
if (!hasIos && !hasAndroid) {
|
|
219852
220358
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
219853
220359
|
}
|
|
@@ -219858,14 +220364,14 @@ var appDoctor = async (_args) => {
|
|
|
219858
220364
|
failed++;
|
|
219859
220365
|
};
|
|
219860
220366
|
if (config?.icon) {
|
|
219861
|
-
check(
|
|
220367
|
+
check(existsSync19(join20(root, config.icon)), `Icon master ${config.icon}`, `app.json "icon" points at a missing file — sync will fail`);
|
|
219862
220368
|
}
|
|
219863
220369
|
if (hasIos)
|
|
219864
220370
|
await doctorIosChecks(root, config, check);
|
|
219865
220371
|
if (hasAndroid) {
|
|
219866
220372
|
await doctorAndroidChecks(root, config, check);
|
|
219867
220373
|
if (!hasIos) {
|
|
219868
|
-
const lock = readJson(
|
|
220374
|
+
const lock = readJson(join20(root, "plugins.lock.json"))?.plugins ?? {};
|
|
219869
220375
|
const wanted = Object.keys(config.plugins ?? {});
|
|
219870
220376
|
const inSync = wanted.every((id) => lock[id]) && Object.keys(lock).every((id) => wanted.includes(id));
|
|
219871
220377
|
check(wanted.length === 0 || inSync, "Plugins synced", "run `lecodes app sync`");
|
|
@@ -219884,22 +220390,22 @@ var doctorIosChecks = async (root, config, check) => {
|
|
|
219884
220390
|
} catch {}
|
|
219885
220391
|
check(xcodeVersion !== "", `Xcode CLI (${xcodeVersion || "not found"})`, "install Xcode + command line tools");
|
|
219886
220392
|
check(!!config.ios.teamId, "Development team set", "lecodes app init wrote app.json — fill ios.teamId", false);
|
|
219887
|
-
const iconDir =
|
|
219888
|
-
const hasIcon =
|
|
220393
|
+
const iconDir = join20(root, "ios", "App", "Assets.xcassets", "AppIcon.appiconset");
|
|
220394
|
+
const hasIcon = existsSync19(iconDir) && readdirSync8(iconDir).some((f3) => f3.endsWith(".png"));
|
|
219889
220395
|
check(hasIcon, "App icon present", "drop a 1024×1024 PNG into ios/App/Assets.xcassets/AppIcon.appiconset/ (App Store requires it)", false);
|
|
219890
|
-
const bundle =
|
|
219891
|
-
check(
|
|
220396
|
+
const bundle = join20(root, "ios", "App", "Resources", "app.js");
|
|
220397
|
+
check(existsSync19(bundle), "Bundled app.js", "run `lecodes app sync`");
|
|
219892
220398
|
const requestedVariant = config.ios.variant ?? "auto";
|
|
219893
220399
|
const validVariant = requestedVariant === "auto" || SDK_VARIANTS.includes(requestedVariant);
|
|
219894
220400
|
check(validVariant, `ios.variant "${requestedVariant}"`, `use "auto" or one of ${SDK_VARIANTS.join(", ")}`);
|
|
219895
|
-
if (
|
|
219896
|
-
const { variant } = resolveVariant(requestedVariant,
|
|
219897
|
-
const pkg =
|
|
220401
|
+
if (existsSync19(bundle) && validVariant) {
|
|
220402
|
+
const { variant } = resolveVariant(requestedVariant, readFileSync16(bundle, "utf8"));
|
|
220403
|
+
const pkg = readFileSync16(join20(root, "ios", "LeCodesRuntime", "Package.swift"), "utf8");
|
|
219898
220404
|
const needsResources = variant === "3d" || variant === "full";
|
|
219899
|
-
const hasResources =
|
|
220405
|
+
const hasResources = existsSync19(join20(root, "ios", "App", "Resources", "LeCodesSDKResources.bundle"));
|
|
219900
220406
|
check(pkg.includes(`"LeCodesSDK-${variant}"`) && (!needsResources || hasResources), `SDK variant ${variant}${needsResources ? " (+ filament resources)" : ""}`, "run `lecodes app sync`");
|
|
219901
220407
|
}
|
|
219902
|
-
const linked = shellSdkVersion(
|
|
220408
|
+
const linked = shellSdkVersion(join20(root, "ios"));
|
|
219903
220409
|
const pin = config.ios.sdk ?? "latest";
|
|
219904
220410
|
check(!!linked, `SDK ${linked ?? "version unreadable"}`, "run `lecodes app sync`");
|
|
219905
220411
|
if (linked && pin !== "latest") {
|
|
@@ -219911,27 +220417,27 @@ var doctorIosChecks = async (root, config, check) => {
|
|
|
219911
220417
|
check(cmpVersion(linked, latest) >= 0, `SDK is the latest release (${latest})`, "run `lecodes app update`", false);
|
|
219912
220418
|
} catch {}
|
|
219913
220419
|
}
|
|
219914
|
-
const lock = readJson(
|
|
220420
|
+
const lock = readJson(join20(root, "plugins.lock.json"))?.plugins ?? {};
|
|
219915
220421
|
const wanted = Object.keys(config.plugins ?? {});
|
|
219916
220422
|
const inSync = wanted.every((id) => lock[id]) && Object.keys(lock).every((id) => wanted.includes(id));
|
|
219917
220423
|
check(wanted.length === 0 || inSync, "Plugins synced", "run `lecodes app sync`");
|
|
219918
|
-
const pbxFile =
|
|
219919
|
-
if (
|
|
219920
|
-
check(
|
|
220424
|
+
const pbxFile = join20(root, "ios", "App.xcodeproj", "project.pbxproj");
|
|
220425
|
+
if (existsSync19(pbxFile) && readFileSync16(pbxFile, "utf8").includes("CODE_SIGN_ENTITLEMENTS")) {
|
|
220426
|
+
check(existsSync19(join20(root, "ios", "App", "App.entitlements")), "App/App.entitlements present", "run `lecodes app sync`");
|
|
219921
220427
|
}
|
|
219922
220428
|
if (wanted.includes("push")) {
|
|
219923
|
-
const runtimeSwift =
|
|
219924
|
-
const baked =
|
|
220429
|
+
const runtimeSwift = join20(root, "ios", "LeCodesRuntime", "Sources", "LeCodesRuntime", "LeCodesRuntime.swift");
|
|
220430
|
+
const baked = existsSync19(runtimeSwift) && readFileSync16(runtimeSwift, "utf8").includes("bootProjectUuid");
|
|
219925
220431
|
check(baked, "Push world identity baked", `without it Push.register() rejects "unavailable" — needs a pushed project (uuid), lecodes-ios-sdk ${SDK_IDENTITY_VERSION}+ and \`lecodes app sync\``, false);
|
|
219926
220432
|
}
|
|
219927
220433
|
if (config.update) {
|
|
219928
|
-
const plist =
|
|
220434
|
+
const plist = readFileSync16(join20(root, "ios", "App", "Info.plist"), "utf8");
|
|
219929
220435
|
check(plist.includes("<key>LeCodesUpdateURL</key>"), "OTA update URL in Info.plist", "run `lecodes app sync`", false);
|
|
219930
|
-
const appDir =
|
|
219931
|
-
const callsUpdater =
|
|
220436
|
+
const appDir = join20(root, "ios", "App");
|
|
220437
|
+
const callsUpdater = readdirSync8(appDir).some((f3) => f3.endsWith(".swift") && readFileSync16(join20(appDir, f3), "utf8").includes("LeCodesUpdater.activeBundle"));
|
|
219932
220438
|
check(callsUpdater, "App code launches via LeCodesUpdater", `no App/*.swift calls LeCodesUpdater.activeBundle() — downloaded updates would never run; see "Over-the-air updates" in ios/README.md for the ViewController snippet`, false);
|
|
219933
220439
|
}
|
|
219934
|
-
const asc = readJson(
|
|
220440
|
+
const asc = readJson(join20(homedir7(), ".lecodes", "asc.json"));
|
|
219935
220441
|
check(!!asc?.keyPath, "App Store Connect API key configured", "pass --key-path/--key-id/--issuer-id to `lecodes app upload` once to store it", false);
|
|
219936
220442
|
};
|
|
219937
220443
|
var ARCHIVE_PATH = "ios/build/App.xcarchive";
|
|
@@ -219967,8 +220473,8 @@ var appBuild = async (args) => {
|
|
|
219967
220473
|
success(`Archive: ios/build/App.xcarchive`);
|
|
219968
220474
|
if (flagBool(args, "no-export"))
|
|
219969
220475
|
return;
|
|
219970
|
-
const optionsPath =
|
|
219971
|
-
|
|
220476
|
+
const optionsPath = join20(root, "ios", "build", "ExportOptions.plist");
|
|
220477
|
+
writeFileSync15(optionsPath, exportOptionsPlist(method, config.ios.teamId ?? ""));
|
|
219972
220478
|
note(`Exporting (.ipa, method ${method})…`);
|
|
219973
220479
|
runTool(root, "xcodebuild", [
|
|
219974
220480
|
"-exportArchive",
|
|
@@ -219987,10 +220493,10 @@ var appUpload = async (args) => {
|
|
|
219987
220493
|
throw new CliError("Play upload isn't wired yet — upload android/app/build/outputs/bundle/release/*.aab in the Play Console (`lecodes app build android` produces it).");
|
|
219988
220494
|
}
|
|
219989
220495
|
const { root, config } = requireShell();
|
|
219990
|
-
if (!
|
|
220496
|
+
if (!existsSync19(join20(root, ARCHIVE_PATH))) {
|
|
219991
220497
|
throw new CliError("No archive at ios/build/App.xcarchive — run `lecodes app build ios` first.");
|
|
219992
220498
|
}
|
|
219993
|
-
const ascPath =
|
|
220499
|
+
const ascPath = join20(homedir7(), ".lecodes", "asc.json");
|
|
219994
220500
|
const stored = readJson(ascPath) ?? {};
|
|
219995
220501
|
const keyPath = flagStr(args, "key-path") ?? stored.keyPath;
|
|
219996
220502
|
const keyId = flagStr(args, "key-id") ?? stored.keyId;
|
|
@@ -219999,15 +220505,15 @@ var appUpload = async (args) => {
|
|
|
219999
220505
|
throw new CliError(`App Store Connect API key needed: --key-path <AuthKey.p8> --key-id <id> --issuer-id <id>
|
|
220000
220506
|
(create one at appstoreconnect.apple.com → Users and Access → Integrations; stored in ~/.lecodes/asc.json after first use)`);
|
|
220001
220507
|
}
|
|
220002
|
-
if (!
|
|
220508
|
+
if (!existsSync19(keyPath))
|
|
220003
220509
|
throw new CliError(`API key file not found: ${keyPath}`);
|
|
220004
220510
|
if (flagStr(args, "key-path")) {
|
|
220005
|
-
|
|
220511
|
+
mkdirSync13(dirname12(ascPath), { recursive: true });
|
|
220006
220512
|
writeJson(ascPath, { keyPath, keyId, issuerId });
|
|
220007
220513
|
note("Stored the key reference in ~/.lecodes/asc.json");
|
|
220008
220514
|
}
|
|
220009
|
-
const optionsPath =
|
|
220010
|
-
|
|
220515
|
+
const optionsPath = join20(root, "ios", "build", "UploadOptions.plist");
|
|
220516
|
+
writeFileSync15(optionsPath, exportOptionsPlist("app-store-connect", config.ios.teamId ?? "", "upload"));
|
|
220011
220517
|
note("Uploading to App Store Connect… (processing continues on Apple's side)");
|
|
220012
220518
|
runTool(root, "xcodebuild", [
|
|
220013
220519
|
"-exportArchive",
|
|
@@ -220018,14 +220524,14 @@ var appUpload = async (args) => {
|
|
|
220018
220524
|
"-exportPath",
|
|
220019
220525
|
"ios/build/upload",
|
|
220020
220526
|
"-authenticationKeyPath",
|
|
220021
|
-
|
|
220527
|
+
resolve7(keyPath),
|
|
220022
220528
|
"-authenticationKeyID",
|
|
220023
220529
|
keyId,
|
|
220024
220530
|
"-authenticationKeyIssuerID",
|
|
220025
220531
|
issuerId,
|
|
220026
220532
|
"-allowProvisioningUpdates"
|
|
220027
220533
|
]);
|
|
220028
|
-
const configPath =
|
|
220534
|
+
const configPath = join20(root, "app.json");
|
|
220029
220535
|
const fresh = readJson(configPath);
|
|
220030
220536
|
fresh.ios.buildNumber = (fresh.ios.buildNumber ?? 1) + 1;
|
|
220031
220537
|
writeJson(configPath, fresh);
|
|
@@ -220036,15 +220542,15 @@ var appOpen = async (args) => {
|
|
|
220036
220542
|
const root = projectRoot();
|
|
220037
220543
|
let platform = args._[1];
|
|
220038
220544
|
if (!platform) {
|
|
220039
|
-
const hasIos =
|
|
220040
|
-
const hasAndroid =
|
|
220545
|
+
const hasIos = existsSync19(join20(root, "ios"));
|
|
220546
|
+
const hasAndroid = existsSync19(join20(root, "android"));
|
|
220041
220547
|
if (hasIos && hasAndroid)
|
|
220042
220548
|
throw new CliError("Both shells exist — `lecodes app open ios` or `lecodes app open android`.");
|
|
220043
220549
|
platform = hasAndroid ? "android" : "ios";
|
|
220044
220550
|
}
|
|
220045
220551
|
if (platform === "ios") {
|
|
220046
|
-
const proj =
|
|
220047
|
-
if (!
|
|
220552
|
+
const proj = join20(root, "ios", "App.xcodeproj");
|
|
220553
|
+
if (!existsSync19(proj))
|
|
220048
220554
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
220049
220555
|
if (process.platform !== "darwin")
|
|
220050
220556
|
throw new CliError("Opening Xcode requires macOS.");
|
|
@@ -220081,8 +220587,8 @@ var app = async (args) => {
|
|
|
220081
220587
|
init_api();
|
|
220082
220588
|
init_config();
|
|
220083
220589
|
init_manifest();
|
|
220084
|
-
import { existsSync as
|
|
220085
|
-
import { resolve as
|
|
220590
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync15, readdirSync as readdirSync10 } from "node:fs";
|
|
220591
|
+
import { resolve as resolve8 } from "node:path";
|
|
220086
220592
|
init_types4();
|
|
220087
220593
|
init_ignore();
|
|
220088
220594
|
init_util();
|
|
@@ -220103,11 +220609,11 @@ var clone3 = async (args) => {
|
|
|
220103
220609
|
const uuid = parseUuid(args._[0]);
|
|
220104
220610
|
const project = await getProject(apiUrl, token, uuid);
|
|
220105
220611
|
const dir = args._[1] ?? slug(project.name);
|
|
220106
|
-
const root =
|
|
220107
|
-
if (
|
|
220612
|
+
const root = resolve8(dir);
|
|
220613
|
+
if (existsSync21(root) && readdirSync10(root).length > 0) {
|
|
220108
220614
|
throw new CliError(`Directory "${dir}" already exists and is not empty.`);
|
|
220109
220615
|
}
|
|
220110
|
-
|
|
220616
|
+
mkdirSync15(root, { recursive: true });
|
|
220111
220617
|
info(c.dim(`Fetching project ${c.bold(project.name)}…`));
|
|
220112
220618
|
const files = await materializeAssets(root, project, apiUrl, token, progress);
|
|
220113
220619
|
endProgress();
|
|
@@ -220130,8 +220636,8 @@ init_config();
|
|
|
220130
220636
|
init_manifest();
|
|
220131
220637
|
init_types4();
|
|
220132
220638
|
init_ignore();
|
|
220133
|
-
import { existsSync as
|
|
220134
|
-
import { join as
|
|
220639
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync16, readdirSync as readdirSync11, writeFileSync as writeFileSync17 } from "node:fs";
|
|
220640
|
+
import { join as join22, resolve as resolve9 } from "node:path";
|
|
220135
220641
|
|
|
220136
220642
|
// src/commands/projectTemplates.ts
|
|
220137
220643
|
var appClaudeMd = (name) => `# ${name} — LeCodes app
|
|
@@ -220173,7 +220679,11 @@ are the regression suite. A failing step dumps the failing frame (JSON + PNG) un
|
|
|
220173
220679
|
|
|
220174
220680
|
One action per step (\`tap\`, \`type\`+\`text\`, \`fill\`, \`scroll\` [+\`by\`], \`wait\`, and for
|
|
220175
220681
|
games: \`frames\` (advance N fixed 1/60 steps, no sleeping), \`drag\`+\`to\` [+\`ms\`], \`hold\`+\`ms\`,
|
|
220176
|
-
\`key\`+\`ms\` / \`keyDown\` / \`keyUp\` (KeyboardEvent.code: "ArrowRight", "KeyW", "Space"
|
|
220682
|
+
\`key\`+\`ms\` / \`keyDown\` / \`keyUp\` (KeyboardEvent.code: "ArrowRight", "KeyW", "Space" — mouse
|
|
220683
|
+
buttons are keys too: "MouseLeft"/"MouseRight"), \`look: [dx, dy]\` [+\`ms\`] (mouse motion for
|
|
220684
|
+
\`Input.mouse.delta\`), \`mouseTo: [x, y]\` (cursor position), \`wheel: [dx, dy]\`, gamepad:
|
|
220685
|
+
\`stick: { left: [x, y], right: [x, y], triggers: [l, r] }\` (values persist; \`index\` = pad),
|
|
220686
|
+
\`gamepad: "connect" | "disconnect"\`, pad buttons as keys: \`keyDown: "GamepadSouth"\`), then an
|
|
220177
220687
|
optional \`expect\` (\`screen\`, \`node\`/\`notNode\`, \`text\`/\`notText\`,
|
|
220178
220688
|
\`value: { target, equals }\`, \`log\` — a \`console.log\` line contains it), then outputs
|
|
220179
220689
|
(\`screenshot\` [+\`clip\`: a node name or \`{ x, y, width, height }\`], \`json\`). Targets resolve by
|
|
@@ -220192,7 +220702,9 @@ the 2D scene (sprite \`click\`/\`touchstart\`, \`ev.track()\`). Envelope keys: \
|
|
|
220192
220702
|
|
|
220193
220703
|
Game time is deterministic headless: \`setLoop\`, 2D physics/animation and aspect \`update(dt)\`
|
|
220194
220704
|
advance in fixed 1/60 steps driven by \`wait\`/\`frames\`/\`drag\`/\`key\` — never by a wall clock —
|
|
220195
|
-
so a flow that passes once passes every time. \`Input.key(code)\` reads the keys a flow holds
|
|
220705
|
+
so a flow that passes once passes every time. \`Input.key(code)\` reads the keys a flow holds and
|
|
220706
|
+
\`Input.on('keydown' | 'keyup', e => e.code)\` fires for each \`key\`/\`keyDown\`/\`keyUp\` step
|
|
220707
|
+
(events land before the next loop tick); \`stick\` values are what \`Input.gamepad(0).axis()\` reads.
|
|
220196
220708
|
Headless draws UI + the 2D scene (sprites, tilemaps, Box2D); 3D is captured as JSON and Jolt does
|
|
220197
220709
|
not simulate — use \`--desktop\` for 3D pixels. \`console.log\` + \`expect.log\` is the assertion
|
|
220198
220710
|
channel for game state that has no UI.
|
|
@@ -220262,21 +220774,21 @@ var create = async (args) => {
|
|
|
220262
220774
|
if (!name)
|
|
220263
220775
|
throw new CliError("Usage: lecodes create <name> [directory]");
|
|
220264
220776
|
const dir = args._[1] ?? slug2(name);
|
|
220265
|
-
const root =
|
|
220266
|
-
if (
|
|
220777
|
+
const root = resolve9(dir);
|
|
220778
|
+
if (existsSync22(root) && readdirSync11(root).length > 0) {
|
|
220267
220779
|
throw new CliError(`Directory "${dir}" already exists and is not empty.`);
|
|
220268
220780
|
}
|
|
220269
|
-
|
|
220781
|
+
mkdirSync16(root, { recursive: true });
|
|
220270
220782
|
info(c.dim(`Creating project ${c.bold(name)}…`));
|
|
220271
220783
|
const { uuid } = await createProject(apiUrl, token, name, flagStr(args, "team"));
|
|
220272
220784
|
info(c.dim("Fetching type definitions…"));
|
|
220273
220785
|
await materializeTypes(root, apiUrl);
|
|
220274
220786
|
writeTsconfig(root);
|
|
220275
220787
|
writeDefaultIgnore(root);
|
|
220276
|
-
|
|
220277
|
-
|
|
220278
|
-
|
|
220279
|
-
|
|
220788
|
+
writeFileSync17(join22(root, "main.ts"), STARTER);
|
|
220789
|
+
writeFileSync17(join22(root, "CLAUDE.md"), appClaudeMd(name));
|
|
220790
|
+
mkdirSync16(join22(root, ".claude"), { recursive: true });
|
|
220791
|
+
writeFileSync17(join22(root, ".claude", "settings.json"), CLAUDE_SETTINGS);
|
|
220280
220792
|
const commits = await getCommits(apiUrl, token, uuid);
|
|
220281
220793
|
writeManifest(root, { uuid, apiUrl, name, baseHeadId: commits.headId, files: {} });
|
|
220282
220794
|
success(`Created ${c.bold(name)} in ${c.bold(dir)}/`);
|
|
@@ -220298,6 +220810,15 @@ var resolveScale = (flag, cfg) => {
|
|
|
220298
220810
|
}
|
|
220299
220811
|
return n2;
|
|
220300
220812
|
};
|
|
220813
|
+
var resolveRenderScale = (flag, cfg) => {
|
|
220814
|
+
const raw = flag ?? cfg;
|
|
220815
|
+
if (raw === undefined)
|
|
220816
|
+
return;
|
|
220817
|
+
const n2 = Number(raw);
|
|
220818
|
+
if (!Number.isFinite(n2) || n2 < 0.25 || n2 > 1)
|
|
220819
|
+
throw new CliError(`render-scale must be a number 0.25..1 (got "${raw}")`);
|
|
220820
|
+
return n2;
|
|
220821
|
+
};
|
|
220301
220822
|
var run = async (args) => {
|
|
220302
220823
|
let root;
|
|
220303
220824
|
try {
|
|
@@ -220317,7 +220838,9 @@ var run = async (args) => {
|
|
|
220317
220838
|
resizable: win?.resizable,
|
|
220318
220839
|
title: flagStr(args, "title") ?? win?.title ?? appCfg?.name,
|
|
220319
220840
|
fullscreen: flagBool(args, "fullscreen") ? true : flagBool(args, "windowed") ? false : win?.fullscreen,
|
|
220320
|
-
scale: resolveScale(flagStr(args, "scale"), win?.scale)
|
|
220841
|
+
scale: resolveScale(flagStr(args, "scale"), win?.scale),
|
|
220842
|
+
renderScale: resolveRenderScale(flagStr(args, "render-scale"), appCfg?.desktop?.renderScale),
|
|
220843
|
+
maxRenderSize: appCfg?.desktop?.maxRenderSize
|
|
220321
220844
|
});
|
|
220322
220845
|
};
|
|
220323
220846
|
var status = async () => {
|
|
@@ -220353,15 +220876,16 @@ init_design();
|
|
|
220353
220876
|
// src/commands/dev.ts
|
|
220354
220877
|
init_manifest();
|
|
220355
220878
|
init_ignore();
|
|
220356
|
-
import { existsSync as
|
|
220357
|
-
import { randomBytes as
|
|
220879
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync18, readFileSync as readFileSync20, watch, writeFileSync as writeFileSync19 } from "node:fs";
|
|
220880
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
220358
220881
|
import { networkInterfaces } from "node:os";
|
|
220359
|
-
import { basename as
|
|
220882
|
+
import { basename as basename10, join as join25 } from "node:path";
|
|
220360
220883
|
|
|
220361
220884
|
// src/dev/devServer.ts
|
|
220362
220885
|
init_localFiles();
|
|
220363
220886
|
import { createServer as createServer2 } from "node:http";
|
|
220364
|
-
import { createReadStream, statSync as
|
|
220887
|
+
import { createReadStream, statSync as statSync8 } from "node:fs";
|
|
220888
|
+
import { join as join24 } from "node:path";
|
|
220365
220889
|
init_projectCompile();
|
|
220366
220890
|
init_fonts();
|
|
220367
220891
|
init_assetIcons();
|
|
@@ -220485,9 +221009,9 @@ var renderPrelude = (p2) => DEV_PRELUDE_JS.split("__DEV_ORIGIN__").join(p2.origi
|
|
|
220485
221009
|
var renderDevScreen = (name) => DEV_SCREEN_TS.split("__DEV_NAME__").join(JSON.stringify(name));
|
|
220486
221010
|
|
|
220487
221011
|
// src/dev/wsServer.ts
|
|
220488
|
-
import { createHash as
|
|
221012
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
220489
221013
|
var WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
220490
|
-
var acceptKey = (key) =>
|
|
221014
|
+
var acceptKey = (key) => createHash7("sha1").update(key + WS_GUID).digest("base64");
|
|
220491
221015
|
var encodeTextFrame = (text) => {
|
|
220492
221016
|
const payload = Buffer.from(text, "utf8");
|
|
220493
221017
|
const len = payload.length;
|
|
@@ -220635,14 +221159,14 @@ var sendText = (res, status2, type, body) => {
|
|
|
220635
221159
|
res.end(body);
|
|
220636
221160
|
};
|
|
220637
221161
|
var sendJson = (res, status2, body) => sendText(res, status2, "application/json", JSON.stringify(body));
|
|
220638
|
-
var readBody = (req) => new Promise((
|
|
221162
|
+
var readBody = (req) => new Promise((resolve10) => {
|
|
220639
221163
|
let body = "";
|
|
220640
221164
|
req.on("data", (c4) => {
|
|
220641
221165
|
if (body.length < 256 * 1024)
|
|
220642
221166
|
body += c4.toString("utf8");
|
|
220643
221167
|
});
|
|
220644
|
-
req.on("end", () =>
|
|
220645
|
-
req.on("error", () =>
|
|
221168
|
+
req.on("end", () => resolve10(body));
|
|
221169
|
+
req.on("error", () => resolve10(body));
|
|
220646
221170
|
});
|
|
220647
221171
|
var startDevServer = async (options) => {
|
|
220648
221172
|
const { root, manifest, name, origin, token, entryOverride } = options;
|
|
@@ -220654,7 +221178,8 @@ var startDevServer = async (options) => {
|
|
|
220654
221178
|
const stripVolatile = (js) => js.replace(/^\/\/ last-updated: .*$/m, "");
|
|
220655
221179
|
const compileOnce = async () => {
|
|
220656
221180
|
try {
|
|
220657
|
-
const { entries, warnings } = collectLocalEntries(root, manifest, `${base}/assets
|
|
221181
|
+
const { entries, warnings } = collectLocalEntries(root, manifest, `${base}/assets`, { localShaders: true });
|
|
221182
|
+
warnings.push(...(await applyLocalShaders(entries, { root, srcPrefix: `${base}/shaders` })).warnings);
|
|
220658
221183
|
const fontsHeader = await applyProjectFonts(entries, {
|
|
220659
221184
|
mode: "vendor",
|
|
220660
221185
|
root,
|
|
@@ -220742,6 +221267,21 @@ var startDevServer = async (options) => {
|
|
|
220742
221267
|
printDeviceMessage(await readBody(req));
|
|
220743
221268
|
return sendJson(res, 200, { ok: true });
|
|
220744
221269
|
}
|
|
221270
|
+
if (route.startsWith("/shaders/")) {
|
|
221271
|
+
const name2 = route.slice("/shaders/".length);
|
|
221272
|
+
if (!isShaderArtifactName(name2))
|
|
221273
|
+
return sendText(res, 400, "text/plain", "Bad path");
|
|
221274
|
+
const abs = join24(shadersCacheDir(root), name2);
|
|
221275
|
+
try {
|
|
221276
|
+
if (!statSync8(abs).isFile())
|
|
221277
|
+
throw new Error("not a file");
|
|
221278
|
+
} catch {
|
|
221279
|
+
return sendText(res, 404, "text/plain", "Not found");
|
|
221280
|
+
}
|
|
221281
|
+
res.writeHead(200, { "content-type": "application/octet-stream", "cache-control": "no-store" });
|
|
221282
|
+
createReadStream(abs).pipe(res);
|
|
221283
|
+
return;
|
|
221284
|
+
}
|
|
220745
221285
|
if (route.startsWith("/assets/")) {
|
|
220746
221286
|
let key;
|
|
220747
221287
|
try {
|
|
@@ -220750,7 +221290,7 @@ var startDevServer = async (options) => {
|
|
|
220750
221290
|
return sendText(res, 400, "text/plain", "Bad path");
|
|
220751
221291
|
}
|
|
220752
221292
|
const abs = resolveLocalPath(root, key);
|
|
220753
|
-
if (!abs || !
|
|
221293
|
+
if (!abs || !statSync8(abs).isFile())
|
|
220754
221294
|
return sendText(res, 404, "text/plain", "Not found");
|
|
220755
221295
|
res.writeHead(200, { "content-type": contentType(key), "cache-control": "no-store" });
|
|
220756
221296
|
createReadStream(abs).pipe(res);
|
|
@@ -220793,11 +221333,11 @@ var startDevServer = async (options) => {
|
|
|
220793
221333
|
if (conn)
|
|
220794
221334
|
clients.add(conn);
|
|
220795
221335
|
});
|
|
220796
|
-
await new Promise((
|
|
221336
|
+
await new Promise((resolve10, reject) => {
|
|
220797
221337
|
server2.once("error", reject);
|
|
220798
221338
|
server2.listen(options.port, options.bind ?? "0.0.0.0", () => {
|
|
220799
221339
|
server2.removeListener("error", reject);
|
|
220800
|
-
|
|
221340
|
+
resolve10();
|
|
220801
221341
|
});
|
|
220802
221342
|
});
|
|
220803
221343
|
const address = server2.address();
|
|
@@ -220845,24 +221385,24 @@ var lanCandidates = () => {
|
|
|
220845
221385
|
return out.sort((x2, y2) => x2.rank - y2.rank).map(({ address, iface }) => ({ address, iface }));
|
|
220846
221386
|
};
|
|
220847
221387
|
var loadDevToken = (root) => {
|
|
220848
|
-
const path2 =
|
|
221388
|
+
const path2 = join25(root, LECODES_DIR, "dev.json");
|
|
220849
221389
|
try {
|
|
220850
|
-
const token2 = JSON.parse(
|
|
221390
|
+
const token2 = JSON.parse(readFileSync20(path2, "utf8")).token;
|
|
220851
221391
|
if (token2 && /^[a-z0-9]{8,}$/.test(token2))
|
|
220852
221392
|
return token2;
|
|
220853
221393
|
} catch {}
|
|
220854
|
-
const token =
|
|
220855
|
-
|
|
220856
|
-
|
|
221394
|
+
const token = randomBytes3(6).toString("hex");
|
|
221395
|
+
mkdirSync18(join25(root, LECODES_DIR), { recursive: true });
|
|
221396
|
+
writeFileSync19(path2, JSON.stringify({ token }, null, 2) + `
|
|
220857
221397
|
`);
|
|
220858
221398
|
return token;
|
|
220859
221399
|
};
|
|
220860
221400
|
var printQr = async (url) => {
|
|
220861
221401
|
try {
|
|
220862
221402
|
const qrcode = (await Promise.resolve().then(() => __toESM(require_main(), 1))).default;
|
|
220863
|
-
await new Promise((
|
|
221403
|
+
await new Promise((resolve10) => qrcode.generate(url, { small: true }, (out) => {
|
|
220864
221404
|
log(out);
|
|
220865
|
-
|
|
221405
|
+
resolve10();
|
|
220866
221406
|
}));
|
|
220867
221407
|
} catch {
|
|
220868
221408
|
note("(couldn't render a QR code — open the URL below on the device)");
|
|
@@ -220878,7 +221418,7 @@ var dev = async (args) => {
|
|
|
220878
221418
|
root = process.cwd();
|
|
220879
221419
|
manifest = null;
|
|
220880
221420
|
}
|
|
220881
|
-
const name = manifest?.name ??
|
|
221421
|
+
const name = manifest?.name ?? basename10(root);
|
|
220882
221422
|
if (typeof globalThis.Bun !== "undefined") {
|
|
220883
221423
|
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).");
|
|
220884
221424
|
}
|
|
@@ -220970,7 +221510,7 @@ ${result.error}`);
|
|
|
220970
221510
|
if (alternatives.length > 0) {
|
|
220971
221511
|
note(`Device can't reach it? This machine also has ${alternatives.map((a2) => `${a2.address} (${a2.iface})`).join(", ")} — retry with --host <ip>.`);
|
|
220972
221512
|
}
|
|
220973
|
-
if (
|
|
221513
|
+
if (existsSync24(join25(root, LECODES_DIR, "manifest.json")))
|
|
220974
221514
|
note(`Token stored in ${LECODES_DIR}/dev.json (delete it to rotate the URL).`);
|
|
220975
221515
|
log("");
|
|
220976
221516
|
log(`Watching ${root} — save a file to hot-reload. Ctrl+C to stop.`);
|
|
@@ -220998,8 +221538,8 @@ init_localFiles();
|
|
|
220998
221538
|
|
|
220999
221539
|
// src/serverDiff.ts
|
|
221000
221540
|
init_localFiles();
|
|
221001
|
-
import { readFileSync as
|
|
221002
|
-
import { basename as
|
|
221541
|
+
import { readFileSync as readFileSync21 } from "node:fs";
|
|
221542
|
+
import { basename as basename11 } from "node:path";
|
|
221003
221543
|
var isTextAsset = (t2) => t2 === "text" || t2 === "shader" || t2 === "scene";
|
|
221004
221544
|
var computeServerDiff = (project, scan2) => {
|
|
221005
221545
|
const pathById = buildPaths(project.assets);
|
|
@@ -221016,8 +221556,8 @@ var computeServerDiff = (project, scan2) => {
|
|
|
221016
221556
|
const server2 = serverByPath.get(path2);
|
|
221017
221557
|
const local = localByPath.get(path2);
|
|
221018
221558
|
if (local && !server2) {
|
|
221019
|
-
const text = isTextKind(classifyKind(
|
|
221020
|
-
entries.push({ path: path2, status: "added", binary: !text, serverText: "", localText: text ?
|
|
221559
|
+
const text = isTextKind(classifyKind(basename11(path2)));
|
|
221560
|
+
entries.push({ path: path2, status: "added", binary: !text, serverText: "", localText: text ? readFileSync21(local.absPath, "utf8") : undefined });
|
|
221021
221561
|
continue;
|
|
221022
221562
|
}
|
|
221023
221563
|
if (server2 && !local) {
|
|
@@ -221028,12 +221568,12 @@ var computeServerDiff = (project, scan2) => {
|
|
|
221028
221568
|
if (server2 && local) {
|
|
221029
221569
|
if (isTextAsset(server2.type)) {
|
|
221030
221570
|
const serverText = server2.text ?? "";
|
|
221031
|
-
const localText =
|
|
221571
|
+
const localText = readFileSync21(local.absPath, "utf8");
|
|
221032
221572
|
if (serverText !== localText)
|
|
221033
221573
|
entries.push({ path: path2, status: "modified", binary: false, serverText, localText });
|
|
221034
221574
|
} else {
|
|
221035
221575
|
const serverSize = server2.file?.size;
|
|
221036
|
-
const localSize = Buffer.byteLength(
|
|
221576
|
+
const localSize = Buffer.byteLength(readFileSync21(local.absPath));
|
|
221037
221577
|
if (serverSize !== undefined && serverSize !== localSize) {
|
|
221038
221578
|
entries.push({ path: path2, status: "modified", binary: true });
|
|
221039
221579
|
}
|
|
@@ -221191,8 +221731,8 @@ var statusLabel = (e2) => e2.status === "added" ? c.green(" added ") : e2.st
|
|
|
221191
221731
|
init_manifest();
|
|
221192
221732
|
init_types4();
|
|
221193
221733
|
init_ignore();
|
|
221194
|
-
import { existsSync as
|
|
221195
|
-
import { basename as
|
|
221734
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync19, writeFileSync as writeFileSync20 } from "node:fs";
|
|
221735
|
+
import { basename as basename12, dirname as dirname14, join as join26, resolve as resolve10 } from "node:path";
|
|
221196
221736
|
init_util();
|
|
221197
221737
|
var STARTER2 = `// Welcome to LeCodes — the whole SDK is available as globals, no imports needed.
|
|
221198
221738
|
// Run it live on a device with \`lecodes dev\`, or bundle it with \`lecodes compile -o app.js\`.
|
|
@@ -221300,18 +221840,18 @@ Full command list: \`lecodes --help\`.
|
|
|
221300
221840
|
`;
|
|
221301
221841
|
var init2 = async (args) => {
|
|
221302
221842
|
const dirArg = (args._[0] ?? ".").replace(/[/\\]+$/, "") || ".";
|
|
221303
|
-
const root =
|
|
221304
|
-
const name =
|
|
221305
|
-
|
|
221843
|
+
const root = resolve10(dirArg);
|
|
221844
|
+
const name = basename12(root);
|
|
221845
|
+
mkdirSync19(root, { recursive: true });
|
|
221306
221846
|
let created = 0;
|
|
221307
221847
|
const write = (rel, content) => {
|
|
221308
|
-
const abs =
|
|
221309
|
-
if (
|
|
221848
|
+
const abs = join26(root, rel);
|
|
221849
|
+
if (existsSync25(abs)) {
|
|
221310
221850
|
note(`kept ${rel}`);
|
|
221311
221851
|
return;
|
|
221312
221852
|
}
|
|
221313
|
-
|
|
221314
|
-
|
|
221853
|
+
mkdirSync19(dirname14(abs), { recursive: true });
|
|
221854
|
+
writeFileSync20(abs, content);
|
|
221315
221855
|
log(`${c.green("created")} ${rel}`);
|
|
221316
221856
|
created++;
|
|
221317
221857
|
};
|
|
@@ -221358,8 +221898,8 @@ init_config();
|
|
|
221358
221898
|
init_ignore();
|
|
221359
221899
|
init_localFiles();
|
|
221360
221900
|
init_manifest();
|
|
221361
|
-
import { existsSync as
|
|
221362
|
-
import { basename as
|
|
221901
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
221902
|
+
import { basename as basename14, join as join27, resolve as resolve11 } from "node:path";
|
|
221363
221903
|
init_types4();
|
|
221364
221904
|
init_util();
|
|
221365
221905
|
|
|
@@ -221368,8 +221908,8 @@ init_api();
|
|
|
221368
221908
|
init_config();
|
|
221369
221909
|
init_manifest();
|
|
221370
221910
|
init_localFiles();
|
|
221371
|
-
import { basename as
|
|
221372
|
-
import { readFileSync as
|
|
221911
|
+
import { basename as basename13 } from "node:path";
|
|
221912
|
+
import { readFileSync as readFileSync22 } from "node:fs";
|
|
221373
221913
|
init_util();
|
|
221374
221914
|
var push = async (args) => {
|
|
221375
221915
|
const root = findProjectRoot(process.cwd());
|
|
@@ -221425,14 +221965,14 @@ var push = async (args) => {
|
|
|
221425
221965
|
let uploaded = 0;
|
|
221426
221966
|
for (const file of scan2.files) {
|
|
221427
221967
|
const source = sourceFor(file.path);
|
|
221428
|
-
const kind = source?.type ?? classifyKind(
|
|
221968
|
+
const kind = source?.type ?? classifyKind(basename13(file.path));
|
|
221429
221969
|
const entry = { path: file.path, type: kind };
|
|
221430
221970
|
if (source)
|
|
221431
221971
|
entry.assetId = source.id;
|
|
221432
221972
|
if (isTextKind(kind)) {
|
|
221433
|
-
entry.text =
|
|
221973
|
+
entry.text = readFileSync22(file.absPath, "utf8");
|
|
221434
221974
|
} else if (!source || source.sha !== file.sha) {
|
|
221435
|
-
const blob = await uploadBlob(apiUrl, token, manifest.uuid,
|
|
221975
|
+
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename13(file.path), readFileSync22(file.absPath));
|
|
221436
221976
|
entry.blobFileId = blob.id;
|
|
221437
221977
|
uploaded++;
|
|
221438
221978
|
}
|
|
@@ -221539,10 +222079,10 @@ var link = async (args) => {
|
|
|
221539
222079
|
const apiUrl = requireApiUrl(config);
|
|
221540
222080
|
const token = requireToken(config);
|
|
221541
222081
|
const dirArg = args._[0];
|
|
221542
|
-
const root = dirArg ?
|
|
221543
|
-
if (!
|
|
222082
|
+
const root = dirArg ? resolve11(dirArg) : process.cwd();
|
|
222083
|
+
if (!existsSync26(root))
|
|
221544
222084
|
throw new CliError(`No such directory: ${root}`);
|
|
221545
|
-
if (
|
|
222085
|
+
if (existsSync26(join27(root, LECODES_DIR, "manifest.json")) && !flagBool(args, "force")) {
|
|
221546
222086
|
throw new CliError("This folder is already linked (.lecodes/manifest.json exists) — use `lecodes push`, or `lecodes link --force` to re-link it to another project.");
|
|
221547
222087
|
}
|
|
221548
222088
|
let enclosing = null;
|
|
@@ -221569,7 +222109,7 @@ var link = async (args) => {
|
|
|
221569
222109
|
}
|
|
221570
222110
|
name = project.name;
|
|
221571
222111
|
} else {
|
|
221572
|
-
name = flagStr(args, "name") ??
|
|
222112
|
+
name = flagStr(args, "name") ?? basename14(root);
|
|
221573
222113
|
info(c.dim(`Creating project ${c.bold(name)}…`));
|
|
221574
222114
|
uuid = (await createProject(apiUrl, token, name, flagStr(args, "team"))).uuid;
|
|
221575
222115
|
files = {};
|
|
@@ -221619,9 +222159,91 @@ var login = async (args) => {
|
|
|
221619
222159
|
success(`Logged in as ${c.bold(account.email)} on ${apiUrl}`);
|
|
221620
222160
|
};
|
|
221621
222161
|
|
|
222162
|
+
// src/commands/shaders.ts
|
|
222163
|
+
init_manifest();
|
|
222164
|
+
import { readFileSync as readFileSync23 } from "node:fs";
|
|
222165
|
+
init_util();
|
|
222166
|
+
var collectShaders = () => {
|
|
222167
|
+
let root;
|
|
222168
|
+
let manifest;
|
|
222169
|
+
try {
|
|
222170
|
+
root = findProjectRoot(process.cwd());
|
|
222171
|
+
manifest = readManifest(root);
|
|
222172
|
+
} catch {
|
|
222173
|
+
root = process.cwd();
|
|
222174
|
+
manifest = null;
|
|
222175
|
+
}
|
|
222176
|
+
const { entries } = collectLocalEntries(root, manifest, "", { localShaders: true });
|
|
222177
|
+
return { root, entries, shaders: entries.filter((e2) => e2.type === "shader" && e2.absPath) };
|
|
222178
|
+
};
|
|
222179
|
+
var status2 = async () => {
|
|
222180
|
+
const { root, shaders } = collectShaders();
|
|
222181
|
+
const installed = installedMatcVersions();
|
|
222182
|
+
const matcExe = resolveMatcExeCached();
|
|
222183
|
+
log(`matc ${matcExe ? process.env.LECODES_MATC ? `LECODES_MATC=${process.env.LECODES_MATC}` : `${installed[0]} ${c.dim("(~/.lecodes/matc)")}` : c.dim("not downloaded — fetched by `lecodes shaders compile` / `lecodes dev`")}`);
|
|
222184
|
+
if (shaders.length === 0) {
|
|
222185
|
+
log(c.dim("No .mat shaders in this project."));
|
|
222186
|
+
} else {
|
|
222187
|
+
const matcId = matcExe ? matcIdentity(matcExe) : null;
|
|
222188
|
+
log(`shaders ${c.dim(`(compiled = up-to-date artifacts in ${shadersCacheDir(root)})`)}`);
|
|
222189
|
+
const width = Math.max(...shaders.map((s2) => s2.path.length));
|
|
222190
|
+
for (const shader of shaders) {
|
|
222191
|
+
const state = matcId === null ? c.dim("unknown (matc not downloaded)") : shaderIsCompiled(root, readFileSync23(shader.absPath, "utf8"), matcId) ? "compiled" : c.yellow("not compiled — run `lecodes shaders compile`");
|
|
222192
|
+
log(` ${shader.path.padEnd(width)} ${state}`);
|
|
222193
|
+
}
|
|
222194
|
+
}
|
|
222195
|
+
try {
|
|
222196
|
+
const { latest } = await fetchMatcManifest();
|
|
222197
|
+
const newest = installed[0];
|
|
222198
|
+
const hint = !newest ? "" : semverNewer(newest, latest) > 0 ? ` ${c.yellow("(newer — run `lecodes shaders update`)")}` : latest === newest ? ` ${c.dim("(up to date)")}` : "";
|
|
222199
|
+
log(`published ${latest}${hint}`);
|
|
222200
|
+
} catch (e2) {
|
|
222201
|
+
log(`published ${c.dim(e2 instanceof CliError ? e2.message : "unknown (couldn't reach the release manifest)")}`);
|
|
222202
|
+
}
|
|
222203
|
+
};
|
|
222204
|
+
var compile3 = async () => {
|
|
222205
|
+
const { root, entries, shaders } = collectShaders();
|
|
222206
|
+
if (shaders.length === 0) {
|
|
222207
|
+
note("No .mat shaders in this project.");
|
|
222208
|
+
return;
|
|
222209
|
+
}
|
|
222210
|
+
const matcExe = await resolveMatcExe();
|
|
222211
|
+
const matcId = matcIdentity(matcExe);
|
|
222212
|
+
const wasCompiled = new Map(shaders.map((s2) => [s2.path, shaderIsCompiled(root, readFileSync23(s2.absPath, "utf8"), matcId)]));
|
|
222213
|
+
const errors2 = [];
|
|
222214
|
+
for (const shader of shaders) {
|
|
222215
|
+
try {
|
|
222216
|
+
const { warnings } = await applyLocalShaders([shader], { root, local: true });
|
|
222217
|
+
for (const w of warnings)
|
|
222218
|
+
warnErr(w);
|
|
222219
|
+
log(` ${shader.path} ${wasCompiled.get(shader.path) ? c.dim("compiled (cached)") : "compiled"}`);
|
|
222220
|
+
} catch (e2) {
|
|
222221
|
+
const message = e2 instanceof CliError ? e2.message : String(e2);
|
|
222222
|
+
log(` ${shader.path} ${c.red("FAILED")}`);
|
|
222223
|
+
errors2.push(message);
|
|
222224
|
+
}
|
|
222225
|
+
}
|
|
222226
|
+
for (const e2 of errors2)
|
|
222227
|
+
log(`
|
|
222228
|
+
${c.red("✗")} ${e2}`);
|
|
222229
|
+
if (errors2.length > 0)
|
|
222230
|
+
throw new CliError(`${errors2.length} of ${shaders.length} shader${shaders.length === 1 ? "" : "s"} failed to compile.`);
|
|
222231
|
+
note(`${shaders.length} shader${shaders.length === 1 ? "" : "s"} compiled → ${shadersCacheDir(root)}`);
|
|
222232
|
+
};
|
|
222233
|
+
var shaders = async (args) => {
|
|
222234
|
+
const sub = args._[0];
|
|
222235
|
+
if (sub === undefined)
|
|
222236
|
+
return status2();
|
|
222237
|
+
if (sub === "compile")
|
|
222238
|
+
return compile3();
|
|
222239
|
+
if (sub === "update")
|
|
222240
|
+
return updateMatc();
|
|
222241
|
+
throw new CliError(`Unknown subcommand "shaders ${sub}". Use \`lecodes shaders\` (status), \`lecodes shaders compile\` or \`lecodes shaders update\`.`);
|
|
222242
|
+
};
|
|
222243
|
+
|
|
221622
222244
|
// src/commands/render.ts
|
|
221623
|
-
import { mkdirSync as
|
|
221624
|
-
import { dirname as dirname15, resolve as
|
|
222245
|
+
import { mkdirSync as mkdirSync20, readFileSync as readFileSync24, writeFileSync as writeFileSync21 } from "node:fs";
|
|
222246
|
+
import { dirname as dirname15, resolve as resolve13 } from "node:path";
|
|
221625
222247
|
|
|
221626
222248
|
// src/compile/headlessBundle.ts
|
|
221627
222249
|
init_api();
|
|
@@ -221705,10 +222327,10 @@ init_manifest();
|
|
|
221705
222327
|
|
|
221706
222328
|
// src/desktopScript.ts
|
|
221707
222329
|
init_util();
|
|
221708
|
-
import { resolve as
|
|
222330
|
+
import { resolve as resolve12 } from "node:path";
|
|
221709
222331
|
var isPoint = (v) => typeof v === "object" && v !== null && typeof v.x === "number" && typeof v.y === "number";
|
|
221710
222332
|
var FRAME_MS = 16;
|
|
221711
|
-
var ACTIONS = ["tap", "type", "fill", "scroll", "wait", "frames", "drag", "hold", "key", "keyDown", "keyUp", "camera", "view"];
|
|
222333
|
+
var ACTIONS = ["tap", "type", "fill", "scroll", "wait", "frames", "drag", "hold", "key", "keyDown", "keyUp", "look", "mouseTo", "wheel", "stick", "gamepad", "camera", "view"];
|
|
221712
222334
|
var SEMANTIC_EXPECT = ["text", "notText", "node", "notNode", "screen", "value"];
|
|
221713
222335
|
var VIEW_NAMES = ["iso", "top", "front", "side", "back"];
|
|
221714
222336
|
var isVec3 = (v) => Array.isArray(v) && v.length === 3 && v.every((n2) => typeof n2 === "number");
|
|
@@ -221821,6 +222443,55 @@ var translateScenario = (rawSteps, opts) => {
|
|
|
221821
222443
|
plan.lines.push(emit(`${action.toLowerCase()} ${code}`));
|
|
221822
222444
|
budgetMs += 2 * FRAME_MS;
|
|
221823
222445
|
parts.push(`${action} ${code}`);
|
|
222446
|
+
} else if (action === "look") {
|
|
222447
|
+
const v = s2.look;
|
|
222448
|
+
if (!Array.isArray(v) || v.length !== 2 || !v.every((n2) => typeof n2 === "number"))
|
|
222449
|
+
throw new CliError(`${at}: "look" must be [dx, dy] (logical px of mouse motion).`);
|
|
222450
|
+
const ms = num(at, "ms", s2.ms, FRAME_MS);
|
|
222451
|
+
plan.lines.push(emit(`look ${v[0]} ${v[1]} ${Math.round(ms)}`));
|
|
222452
|
+
budgetMs += ms + 2 * FRAME_MS;
|
|
222453
|
+
parts.push(`look ${v[0]},${v[1]} over ${ms}ms`);
|
|
222454
|
+
} else if (action === "mouseTo") {
|
|
222455
|
+
const v = s2.mouseTo;
|
|
222456
|
+
if (!Array.isArray(v) || v.length !== 2 || !v.every((n2) => typeof n2 === "number"))
|
|
222457
|
+
throw new CliError(`${at}: "mouseTo" must be [x, y] (logical px).`);
|
|
222458
|
+
plan.lines.push(emit(`mouseto ${v[0]} ${v[1]}`));
|
|
222459
|
+
budgetMs += FRAME_MS;
|
|
222460
|
+
parts.push(`mouseTo ${v[0]},${v[1]}`);
|
|
222461
|
+
} else if (action === "wheel") {
|
|
222462
|
+
const v = s2.wheel;
|
|
222463
|
+
if (!Array.isArray(v) || v.length !== 2 || !v.every((n2) => typeof n2 === "number"))
|
|
222464
|
+
throw new CliError(`${at}: "wheel" must be [dx, dy] (notches).`);
|
|
222465
|
+
plan.lines.push(emit(`wheel ${v[0]} ${v[1]}`));
|
|
222466
|
+
budgetMs += 2 * FRAME_MS;
|
|
222467
|
+
parts.push(`wheel ${v[0]},${v[1]}`);
|
|
222468
|
+
} else if (action === "stick") {
|
|
222469
|
+
const st = s2.stick;
|
|
222470
|
+
if (typeof st !== "object" || st === null)
|
|
222471
|
+
throw new CliError(`${at}: "stick" must be { left?: [x, y], right?: [x, y], triggers?: [l, r] }.`);
|
|
222472
|
+
const pad = typeof s2.index === "number" ? s2.index : 0;
|
|
222473
|
+
const parts2 = [];
|
|
222474
|
+
for (const k of ["left", "right", "triggers"]) {
|
|
222475
|
+
const v = st[k];
|
|
222476
|
+
if (v === undefined)
|
|
222477
|
+
continue;
|
|
222478
|
+
if (!Array.isArray(v) || v.length !== 2 || !v.every((n2) => typeof n2 === "number"))
|
|
222479
|
+
throw new CliError(`${at}: "stick.${k}" must be a pair of numbers.`);
|
|
222480
|
+
plan.lines.push(emit(`stick ${pad} ${k} ${v[0]} ${v[1]}`));
|
|
222481
|
+
parts2.push(`${k}=${v[0]},${v[1]}`);
|
|
222482
|
+
}
|
|
222483
|
+
for (const k of Object.keys(st))
|
|
222484
|
+
if (!["left", "right", "triggers"].includes(k))
|
|
222485
|
+
throw new CliError(`${at}: "stick" knows left, right, triggers — not "${k}".`);
|
|
222486
|
+
budgetMs += 2 * FRAME_MS;
|
|
222487
|
+
parts.push(`stick #${pad} ${parts2.join(" ")}`);
|
|
222488
|
+
} else if (action === "gamepad") {
|
|
222489
|
+
if (s2.gamepad !== "connect" && s2.gamepad !== "disconnect")
|
|
222490
|
+
throw new CliError(`${at}: "gamepad" must be "connect" or "disconnect".`);
|
|
222491
|
+
const pad = typeof s2.index === "number" ? s2.index : 0;
|
|
222492
|
+
plan.lines.push(emit(`gamepad ${pad} ${s2.gamepad}`));
|
|
222493
|
+
budgetMs += 2 * FRAME_MS;
|
|
222494
|
+
parts.push(`gamepad ${s2.gamepad} #${pad}`);
|
|
221824
222495
|
} else if (action === "wait") {
|
|
221825
222496
|
const ms = num(at, "wait", s2.wait);
|
|
221826
222497
|
plan.lines.push(emit(`wait ${Math.round(ms)}`));
|
|
@@ -221857,7 +222528,7 @@ var translateScenario = (rawSteps, opts) => {
|
|
|
221857
222528
|
if (s2.screenshot !== undefined) {
|
|
221858
222529
|
if (typeof s2.screenshot !== "string")
|
|
221859
222530
|
throw new CliError(`${at}: "screenshot" must be a path.`);
|
|
221860
|
-
plan.screenshot =
|
|
222531
|
+
plan.screenshot = resolve12(opts.outDir, s2.screenshot);
|
|
221861
222532
|
plan.lines.push(emit(`frames 2`));
|
|
221862
222533
|
plan.lines.push(emit(`shot ${plan.screenshot}`));
|
|
221863
222534
|
budgetMs += 3 * FRAME_MS;
|
|
@@ -221966,9 +222637,9 @@ var requireRenderer = async () => {
|
|
|
221966
222637
|
};
|
|
221967
222638
|
var fileScenarioIO = (dir) => ({
|
|
221968
222639
|
write: (relPath, data) => {
|
|
221969
|
-
const abs =
|
|
221970
|
-
|
|
221971
|
-
|
|
222640
|
+
const abs = resolve13(dir, relPath);
|
|
222641
|
+
mkdirSync20(dirname15(abs), { recursive: true });
|
|
222642
|
+
writeFileSync21(abs, data);
|
|
221972
222643
|
}
|
|
221973
222644
|
});
|
|
221974
222645
|
var resolveFixtures = (value, baseDir) => {
|
|
@@ -221976,9 +222647,9 @@ var resolveFixtures = (value, baseDir) => {
|
|
|
221976
222647
|
return;
|
|
221977
222648
|
let map = value;
|
|
221978
222649
|
if (typeof value === "string") {
|
|
221979
|
-
const abs =
|
|
222650
|
+
const abs = resolve13(baseDir, value);
|
|
221980
222651
|
try {
|
|
221981
|
-
map = JSON.parse(
|
|
222652
|
+
map = JSON.parse(readFileSync24(abs, "utf8"));
|
|
221982
222653
|
} catch (e2) {
|
|
221983
222654
|
throw new CliError(`Can't read fixtures "${value}": ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
221984
222655
|
}
|
|
@@ -221991,7 +222662,7 @@ var resolveFixtures = (value, baseDir) => {
|
|
|
221991
222662
|
var readScenarioFile = (path2) => {
|
|
221992
222663
|
let raw;
|
|
221993
222664
|
try {
|
|
221994
|
-
raw = JSON.parse(
|
|
222665
|
+
raw = JSON.parse(readFileSync24(resolve13(path2), "utf8"));
|
|
221995
222666
|
} catch (e2) {
|
|
221996
222667
|
throw new CliError(`Can't read scenario "${path2}": ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
221997
222668
|
}
|
|
@@ -222041,13 +222712,13 @@ var renderDesktop = async (args) => {
|
|
|
222041
222712
|
if ((camera || view) && scenario)
|
|
222042
222713
|
throw new CliError('--camera/--view apply to a plain render; in a --script run use a { "view": "iso" } or { "camera": { "at", "lookAt" } } step before the screenshot.');
|
|
222043
222714
|
const { js, resources } = await buildBundle(args, undefined, { localAssets: true });
|
|
222044
|
-
|
|
222715
|
+
mkdirSync20(dirname15(resolve13(pngFlag)), { recursive: true });
|
|
222045
222716
|
if (scenario) {
|
|
222046
222717
|
const outDir = flagStr(args, "out-dir") ?? ".";
|
|
222047
222718
|
const plan = translateScenario(scenario.steps, { outDir, headroomFrames: frames });
|
|
222048
222719
|
for (const st of plan.steps)
|
|
222049
222720
|
if (st.screenshot)
|
|
222050
|
-
|
|
222721
|
+
mkdirSync20(dirname15(st.screenshot), { recursive: true });
|
|
222051
222722
|
const timeoutMs2 = Number(flagStr(args, "timeout")) || 60000 + plan.steps.length * 5000;
|
|
222052
222723
|
note(`Running ${plan.steps.length} step${plan.steps.length === 1 ? "" : "s"} (native)…`);
|
|
222053
222724
|
const { output } = await runDesktopRender(js, resources, {
|
|
@@ -222142,7 +222813,7 @@ var render = async (args) => {
|
|
|
222142
222813
|
const settleStr = flagStr(args, "settle") ?? (scenario?.settle !== undefined ? String(scenario.settle) : undefined);
|
|
222143
222814
|
const timeStr = flagStr(args, "time");
|
|
222144
222815
|
const onConsole = flagBool(args, "logs") ? (level, text2) => logErr(`${c.dim(`[${level}]`)} ${text2}`) : undefined;
|
|
222145
|
-
const envelopeFixtures = resolveFixtures(scenario?.fixtures, scriptFlag !== undefined ? dirname15(
|
|
222816
|
+
const envelopeFixtures = resolveFixtures(scenario?.fixtures, scriptFlag !== undefined ? dirname15(resolve13(scriptFlag)) : ".");
|
|
222146
222817
|
const flagFixtures = resolveFixtures(flagStr(args, "fixtures"), ".");
|
|
222147
222818
|
const fixtures = envelopeFixtures || flagFixtures ? { ...envelopeFixtures, ...flagFixtures } : undefined;
|
|
222148
222819
|
if (fixtures) {
|
|
@@ -222181,7 +222852,7 @@ var render = async (args) => {
|
|
|
222181
222852
|
${c.red(st.error ?? "failed")}`);
|
|
222182
222853
|
}
|
|
222183
222854
|
if (!result2.ok && result2.failureArtifacts.length > 0) {
|
|
222184
|
-
logErr(c.dim(` failing frame: ${result2.failureArtifacts.map((p2) =>
|
|
222855
|
+
logErr(c.dim(` failing frame: ${result2.failureArtifacts.map((p2) => resolve13(outDir, p2)).join(", ")}`));
|
|
222185
222856
|
}
|
|
222186
222857
|
logErr(result2.ok ? c.green(`✓ ${result2.steps.length}/${steps.length} steps passed`) : c.red(`✗ failed at step ${result2.steps.length} of ${steps.length}`));
|
|
222187
222858
|
return done((cb) => cb(), result2.ok ? 0 : 1);
|
|
@@ -222205,7 +222876,7 @@ var render = async (args) => {
|
|
|
222205
222876
|
if (isNullSink(pngFlag)) {
|
|
222206
222877
|
note(`Discarded PNG (${pngFlag})`);
|
|
222207
222878
|
} else {
|
|
222208
|
-
|
|
222879
|
+
writeFileSync21(resolve13(pngFlag), png2);
|
|
222209
222880
|
note(`Wrote ${pngFlag} (${png2.length} bytes, ${outW}x${outH}${clip !== undefined ? ", clipped" : ""})`);
|
|
222210
222881
|
}
|
|
222211
222882
|
return done((cb) => cb());
|
|
@@ -222220,7 +222891,7 @@ var render = async (args) => {
|
|
|
222220
222891
|
return done((cb) => cb());
|
|
222221
222892
|
}
|
|
222222
222893
|
if (out) {
|
|
222223
|
-
|
|
222894
|
+
writeFileSync21(resolve13(out), result.text);
|
|
222224
222895
|
note(`Wrote ${out} (${result.text.length} bytes)`);
|
|
222225
222896
|
return done((cb) => cb());
|
|
222226
222897
|
}
|
|
@@ -222235,8 +222906,8 @@ init_api();
|
|
|
222235
222906
|
init_config();
|
|
222236
222907
|
init_manifest();
|
|
222237
222908
|
init_util();
|
|
222238
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
222239
|
-
import { join as
|
|
222909
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync27, readFileSync as readFileSync25, writeFileSync as writeFileSync22 } from "node:fs";
|
|
222910
|
+
import { join as join28 } from "node:path";
|
|
222240
222911
|
var ENV_VAR = "LECODES_PUSH_KEY";
|
|
222241
222912
|
var context = () => {
|
|
222242
222913
|
const root = findProjectRoot(process.cwd());
|
|
@@ -222380,11 +223051,11 @@ var pnKeysList = async (ctx) => {
|
|
|
222380
223051
|
};
|
|
222381
223052
|
var writeEnv = (root, key) => {
|
|
222382
223053
|
const touched = [];
|
|
222383
|
-
const envPath =
|
|
223054
|
+
const envPath = join28(root, ".env");
|
|
222384
223055
|
const line = `${ENV_VAR}=${key}`;
|
|
222385
|
-
const existing =
|
|
223056
|
+
const existing = existsSync27(envPath) ? readFileSync25(envPath, "utf8") : "";
|
|
222386
223057
|
if (new RegExp(`^${ENV_VAR}=`, "m").test(existing)) {
|
|
222387
|
-
|
|
223058
|
+
writeFileSync22(envPath, existing.replace(new RegExp(`^${ENV_VAR}=.*$`, "m"), line));
|
|
222388
223059
|
} else {
|
|
222389
223060
|
const separator = !existing || existing.endsWith(`
|
|
222390
223061
|
`) ? "" : `
|
|
@@ -222393,8 +223064,8 @@ var writeEnv = (root, key) => {
|
|
|
222393
223064
|
`);
|
|
222394
223065
|
}
|
|
222395
223066
|
touched.push(".env");
|
|
222396
|
-
const ignorePath =
|
|
222397
|
-
const ignore =
|
|
223067
|
+
const ignorePath = join28(root, ".gitignore");
|
|
223068
|
+
const ignore = existsSync27(ignorePath) ? readFileSync25(ignorePath, "utf8") : "";
|
|
222398
223069
|
if (!ignore.split(`
|
|
222399
223070
|
`).some((l2) => l2.trim() === ".env")) {
|
|
222400
223071
|
const separator = !ignore || ignore.endsWith(`
|
|
@@ -222493,8 +223164,8 @@ var pn = async (args) => {
|
|
|
222493
223164
|
init_api();
|
|
222494
223165
|
init_config();
|
|
222495
223166
|
init_manifest();
|
|
222496
|
-
import { existsSync as
|
|
222497
|
-
import { dirname as dirname16, join as
|
|
223167
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync21, rmSync as rmSync8, writeFileSync as writeFileSync23 } from "node:fs";
|
|
223168
|
+
import { dirname as dirname16, join as join29 } from "node:path";
|
|
222498
223169
|
init_types4();
|
|
222499
223170
|
init_localFiles();
|
|
222500
223171
|
init_util();
|
|
@@ -222517,9 +223188,9 @@ var pull = async (args) => {
|
|
|
222517
223188
|
const remotePaths = new Set([...buildPaths(project.assets).entries()].filter(([id]) => project.assets.find((a2) => a2.id === id)?.type !== "folder").map(([, path2]) => path2));
|
|
222518
223189
|
for (const path2 of Object.keys(manifest.files)) {
|
|
222519
223190
|
if (!remotePaths.has(path2)) {
|
|
222520
|
-
const abs =
|
|
222521
|
-
if (
|
|
222522
|
-
|
|
223191
|
+
const abs = join29(root, ...path2.replace(/^\//, "").split("/"));
|
|
223192
|
+
if (existsSync28(abs))
|
|
223193
|
+
rmSync8(abs);
|
|
222523
223194
|
}
|
|
222524
223195
|
}
|
|
222525
223196
|
const files = await materializeAssets(root, project, apiUrl, token, progress);
|
|
@@ -222556,18 +223227,18 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
222556
223227
|
const files = { ...manifest.files };
|
|
222557
223228
|
for (const asset of targets) {
|
|
222558
223229
|
const key = pathById.get(asset.id);
|
|
222559
|
-
const abs =
|
|
222560
|
-
|
|
223230
|
+
const abs = join29(root, ...key.replace(/^\//, "").split("/"));
|
|
223231
|
+
mkdirSync21(dirname16(abs), { recursive: true });
|
|
222561
223232
|
let sha;
|
|
222562
223233
|
if (asset.type === "file") {
|
|
222563
223234
|
if (!asset.file?.src)
|
|
222564
223235
|
continue;
|
|
222565
223236
|
const bytes = await downloadBlob(apiUrl, asset.file.src);
|
|
222566
|
-
|
|
223237
|
+
writeFileSync23(abs, bytes);
|
|
222567
223238
|
sha = sha256(bytes);
|
|
222568
223239
|
} else {
|
|
222569
223240
|
const text = asset.text ?? "";
|
|
222570
|
-
|
|
223241
|
+
writeFileSync23(abs, text);
|
|
222571
223242
|
sha = sha256(text);
|
|
222572
223243
|
}
|
|
222573
223244
|
files[key] = { id: asset.id, type: kindOf3(asset.type), sha };
|
|
@@ -222580,8 +223251,8 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
222580
223251
|
|
|
222581
223252
|
// src/commands/scene.ts
|
|
222582
223253
|
init_manifest();
|
|
222583
|
-
import { existsSync as
|
|
222584
|
-
import { basename as
|
|
223254
|
+
import { existsSync as existsSync30, readFileSync as readFileSync27, readdirSync as readdirSync13, statSync as statSync9, writeFileSync as writeFileSync24 } from "node:fs";
|
|
223255
|
+
import { basename as basename15, dirname as dirname17, join as join31, resolve as resolve14 } from "node:path";
|
|
222585
223256
|
|
|
222586
223257
|
// src/compile/sceneCompile.ts
|
|
222587
223258
|
init_localFiles();
|
|
@@ -222589,18 +223260,18 @@ init_ignore();
|
|
|
222589
223260
|
init_distRoot();
|
|
222590
223261
|
init_util();
|
|
222591
223262
|
init_projectCompile();
|
|
222592
|
-
import { existsSync as
|
|
222593
|
-
import { join as
|
|
223263
|
+
import { existsSync as existsSync29, readFileSync as readFileSync26 } from "node:fs";
|
|
223264
|
+
import { join as join30 } from "node:path";
|
|
222594
223265
|
var TEXT_EXT = /\.(ts|js|json|svg|txt|md)$/i;
|
|
222595
223266
|
var rel = (key) => key.replace(/^\//, "");
|
|
222596
223267
|
var resolveHarness = (loadSceneHarness2) => {
|
|
222597
|
-
const inRepo =
|
|
223268
|
+
const inRepo = join30(distRoot, "..", "projects", "src", "scene-editor");
|
|
222598
223269
|
try {
|
|
222599
|
-
return loadSceneHarness2(
|
|
223270
|
+
return loadSceneHarness2(existsSync29(inRepo) ? inRepo : undefined);
|
|
222600
223271
|
} catch {
|
|
222601
|
-
const vendored =
|
|
222602
|
-
if (
|
|
222603
|
-
return JSON.parse(
|
|
223272
|
+
const vendored = join30(distRoot, "runtime", "scene-harness.json");
|
|
223273
|
+
if (existsSync29(vendored))
|
|
223274
|
+
return JSON.parse(readFileSync26(vendored, "utf8"));
|
|
222604
223275
|
throw new CliError("scene-editor harness sources not found — reinstall the CLI or set LECODES_SCENE_HARNESS_DIR.");
|
|
222605
223276
|
}
|
|
222606
223277
|
};
|
|
@@ -222612,7 +223283,7 @@ var compileSceneEditor = async (opts) => {
|
|
|
222612
223283
|
for (const file of scan2.files) {
|
|
222613
223284
|
const path2 = "./" + rel(file.path);
|
|
222614
223285
|
if (TEXT_EXT.test(path2)) {
|
|
222615
|
-
entries.push({ path: path2, type: "text", text:
|
|
223286
|
+
entries.push({ path: path2, type: "text", text: readFileSync26(file.absPath, "utf8") });
|
|
222616
223287
|
} else {
|
|
222617
223288
|
entries.push({ path: path2, type: "resource", fileSrc: "/project/" + rel(file.path) });
|
|
222618
223289
|
}
|
|
@@ -222643,6 +223314,17 @@ var compileSceneEditor = async (opts) => {
|
|
|
222643
223314
|
init_browserAuth();
|
|
222644
223315
|
init_util();
|
|
222645
223316
|
var DEFAULT_PORT3 = 4488;
|
|
223317
|
+
var findAppRoot = (start) => {
|
|
223318
|
+
let dir = start;
|
|
223319
|
+
while (true) {
|
|
223320
|
+
if (existsSync30(join31(dir, "app.json")))
|
|
223321
|
+
return dir;
|
|
223322
|
+
const parent = dirname17(dir);
|
|
223323
|
+
if (parent === dir)
|
|
223324
|
+
return null;
|
|
223325
|
+
dir = parent;
|
|
223326
|
+
}
|
|
223327
|
+
};
|
|
222646
223328
|
var getContext2 = (args) => {
|
|
222647
223329
|
let root;
|
|
222648
223330
|
let manifest;
|
|
@@ -222654,24 +223336,26 @@ var getContext2 = (args) => {
|
|
|
222654
223336
|
manifest = null;
|
|
222655
223337
|
const pathArg = args._[0];
|
|
222656
223338
|
if (pathArg) {
|
|
222657
|
-
const abs =
|
|
222658
|
-
if (
|
|
223339
|
+
const abs = resolve14(process.cwd(), pathArg);
|
|
223340
|
+
if (existsSync30(abs) && statSync9(abs).isDirectory())
|
|
222659
223341
|
root = abs;
|
|
222660
|
-
else if (abs.endsWith(".scene.ts") &&
|
|
222661
|
-
root = dirname17(abs);
|
|
223342
|
+
else if (abs.endsWith(".scene.ts") && existsSync30(abs))
|
|
223343
|
+
root = findAppRoot(dirname17(abs)) ?? dirname17(abs);
|
|
222662
223344
|
else
|
|
222663
223345
|
throw new CliError(`No such folder or .scene.ts: ${pathArg}`);
|
|
223346
|
+
} else {
|
|
223347
|
+
root = findAppRoot(root) ?? root;
|
|
222664
223348
|
}
|
|
222665
223349
|
}
|
|
222666
|
-
return { root, manifest, name: manifest?.name ??
|
|
223350
|
+
return { root, manifest, name: manifest?.name ?? basename15(root) };
|
|
222667
223351
|
};
|
|
222668
223352
|
var writeMcpConfig2 = (root, port) => {
|
|
222669
|
-
const path2 =
|
|
223353
|
+
const path2 = join31(root, ".mcp.json");
|
|
222670
223354
|
const entry = { type: "http", url: `http://localhost:${port}/mcp` };
|
|
222671
223355
|
let config = {};
|
|
222672
|
-
if (
|
|
223356
|
+
if (existsSync30(path2)) {
|
|
222673
223357
|
try {
|
|
222674
|
-
config = JSON.parse(
|
|
223358
|
+
config = JSON.parse(readFileSync27(path2, "utf8"));
|
|
222675
223359
|
} catch {
|
|
222676
223360
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-scene server by hand.");
|
|
222677
223361
|
return;
|
|
@@ -222684,17 +223368,17 @@ var writeMcpConfig2 = (root, port) => {
|
|
|
222684
223368
|
return;
|
|
222685
223369
|
}
|
|
222686
223370
|
config.mcpServers["lecodes-scene"] = entry;
|
|
222687
|
-
|
|
223371
|
+
writeFileSync24(path2, JSON.stringify(config, null, 2) + `
|
|
222688
223372
|
`);
|
|
222689
223373
|
note(`${existing ? "updated" : "created"} .mcp.json (lecodes-scene MCP → ${entry.url})`);
|
|
222690
223374
|
};
|
|
222691
223375
|
var countScenes = (dir) => {
|
|
222692
223376
|
let n2 = 0;
|
|
222693
|
-
for (const name of
|
|
223377
|
+
for (const name of readdirSync13(dir)) {
|
|
222694
223378
|
if (name === "node_modules" || name.startsWith("."))
|
|
222695
223379
|
continue;
|
|
222696
|
-
const full =
|
|
222697
|
-
if (
|
|
223380
|
+
const full = join31(dir, name);
|
|
223381
|
+
if (statSync9(full).isDirectory())
|
|
222698
223382
|
n2 += countScenes(full);
|
|
222699
223383
|
else if (name.endsWith(".scene.ts"))
|
|
222700
223384
|
n2++;
|
|
@@ -222753,7 +223437,7 @@ var assets = async (args) => {
|
|
|
222753
223437
|
init_manifest();
|
|
222754
223438
|
init_localFiles();
|
|
222755
223439
|
init_util();
|
|
222756
|
-
var
|
|
223440
|
+
var status3 = async (_args) => {
|
|
222757
223441
|
const root = findProjectRoot(process.cwd());
|
|
222758
223442
|
const manifest = readManifest(root);
|
|
222759
223443
|
const scan2 = scanProject(root, manifest);
|
|
@@ -222782,8 +223466,8 @@ var status2 = async (_args) => {
|
|
|
222782
223466
|
};
|
|
222783
223467
|
|
|
222784
223468
|
// src/commands/test.ts
|
|
222785
|
-
import { existsSync as
|
|
222786
|
-
import { dirname as dirname18, resolve as
|
|
223469
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync22, readFileSync as readFileSync28, readdirSync as readdirSync14 } from "node:fs";
|
|
223470
|
+
import { dirname as dirname18, resolve as resolve15 } from "node:path";
|
|
222787
223471
|
init_manifest();
|
|
222788
223472
|
init_util();
|
|
222789
223473
|
var deriveDesignFlows = (meta, entryScreen) => {
|
|
@@ -222846,7 +223530,7 @@ var slug3 = (s2) => s2.replace(/[^a-zA-Z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").
|
|
|
222846
223530
|
var readFlowFile = (abs, displayName) => {
|
|
222847
223531
|
let raw;
|
|
222848
223532
|
try {
|
|
222849
|
-
raw = JSON.parse(
|
|
223533
|
+
raw = JSON.parse(readFileSync28(abs, "utf8"));
|
|
222850
223534
|
} catch (e2) {
|
|
222851
223535
|
throw new CliError(`Can't read "${displayName}": ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
222852
223536
|
}
|
|
@@ -222894,40 +223578,40 @@ var test = async (args) => {
|
|
|
222894
223578
|
if (!Number.isFinite(nativeFixedDt) || nativeFixedDt < 0 || nativeFixedDt > 1000)
|
|
222895
223579
|
throw new CliError(`--fixed-dt wants a number of ms (0 = wall clock, max 1000), got "${fixedDtFlag}".`);
|
|
222896
223580
|
const projectViewport = desktopDefaultViewport(root);
|
|
222897
|
-
const testsDir =
|
|
222898
|
-
const designMetaPath =
|
|
223581
|
+
const testsDir = resolve15(root, flagStr(args, "dir") ?? "tests");
|
|
223582
|
+
const designMetaPath = resolve15(root, flagStr(args, "design-dir") ?? "design", "meta.json");
|
|
222899
223583
|
const asJson = flagBool(args, "json");
|
|
222900
223584
|
const out = asJson ? logErr : log;
|
|
222901
223585
|
const cases = [];
|
|
222902
223586
|
if (args._.length > 0) {
|
|
222903
223587
|
for (const given of args._) {
|
|
222904
|
-
const candidates = [
|
|
222905
|
-
const abs = candidates.find((p2) =>
|
|
223588
|
+
const candidates = [resolve15(given), resolve15(testsDir, given), resolve15(testsDir, `${given}.flow.json`)];
|
|
223589
|
+
const abs = candidates.find((p2) => existsSync31(p2));
|
|
222906
223590
|
if (!abs)
|
|
222907
223591
|
throw new CliError(`No such test: "${given}" (looked in ${testsDir}).`);
|
|
222908
223592
|
cases.push(readFlowFile(abs, given));
|
|
222909
223593
|
}
|
|
222910
|
-
} else if (
|
|
222911
|
-
for (const f3 of
|
|
222912
|
-
cases.push(readFlowFile(
|
|
223594
|
+
} else if (existsSync31(testsDir)) {
|
|
223595
|
+
for (const f3 of readdirSync14(testsDir).filter((f4) => f4.endsWith(".flow.json")).sort()) {
|
|
223596
|
+
cases.push(readFlowFile(resolve15(testsDir, f3), f3));
|
|
222913
223597
|
}
|
|
222914
223598
|
}
|
|
222915
223599
|
const fixturesFlag = flagStr(args, "fixtures");
|
|
222916
|
-
const defaultPack =
|
|
222917
|
-
const flowFixtures = fixturesFlag !== undefined ? resolveFixtures(fixturesFlag, ".") :
|
|
223600
|
+
const defaultPack = resolve15(testsDir, "fixtures.json");
|
|
223601
|
+
const flowFixtures = fixturesFlag !== undefined ? resolveFixtures(fixturesFlag, ".") : existsSync31(defaultPack) ? resolveFixtures("fixtures.json", testsDir) : undefined;
|
|
222918
223602
|
const flowsOnly = flagBool(args, "flows");
|
|
222919
223603
|
const wantFlows = !flagBool(args, "no-flows") && args._.length === 0;
|
|
222920
223604
|
if (flowsOnly)
|
|
222921
223605
|
cases.length = 0;
|
|
222922
223606
|
let skipped = [];
|
|
222923
223607
|
let entryScreen = null;
|
|
222924
|
-
if (desktop2 && wantFlows &&
|
|
223608
|
+
if (desktop2 && wantFlows && existsSync31(designMetaPath)) {
|
|
222925
223609
|
note("Design-derived flows skipped with --desktop (they tap by name; run `lecodes test` headless for them).");
|
|
222926
223610
|
}
|
|
222927
|
-
if (!desktop2 && (wantFlows || flowsOnly) &&
|
|
223611
|
+
if (!desktop2 && (wantFlows || flowsOnly) && existsSync31(designMetaPath)) {
|
|
222928
223612
|
let meta;
|
|
222929
223613
|
try {
|
|
222930
|
-
meta = JSON.parse(
|
|
223614
|
+
meta = JSON.parse(readFileSync28(designMetaPath, "utf8"));
|
|
222931
223615
|
} catch (e2) {
|
|
222932
223616
|
throw new CliError(`Can't read ${designMetaPath}: ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
222933
223617
|
}
|
|
@@ -222959,16 +223643,16 @@ var test = async (args) => {
|
|
|
222959
223643
|
}, timeoutMs);
|
|
222960
223644
|
killer.unref?.();
|
|
222961
223645
|
const onConsole = flagBool(args, "logs") ? (level, text) => logErr(`${c.dim(`[${level}]`)} ${text}`) : undefined;
|
|
222962
|
-
const artifactsRoot =
|
|
223646
|
+
const artifactsRoot = resolve15(root, flagStr(args, "artifacts") ?? resolve15(testsDir, ".artifacts"));
|
|
222963
223647
|
const results = [];
|
|
222964
223648
|
const runNativeCase = async (kase) => {
|
|
222965
|
-
const caseDir =
|
|
223649
|
+
const caseDir = resolve15(artifactsRoot, slug3(kase.name));
|
|
222966
223650
|
const plan = translateScenario(kase.steps, { outDir: caseDir, headroomFrames: nativeFrames });
|
|
222967
223651
|
for (const st of plan.steps)
|
|
222968
223652
|
if (st.screenshot)
|
|
222969
|
-
|
|
222970
|
-
|
|
222971
|
-
const finalPng =
|
|
223653
|
+
mkdirSync22(dirname18(st.screenshot), { recursive: true });
|
|
223654
|
+
mkdirSync22(caseDir, { recursive: true });
|
|
223655
|
+
const finalPng = resolve15(caseDir, "final.png");
|
|
222972
223656
|
const { output } = await runDesktopRender(native.js, native.resources, {
|
|
222973
223657
|
width: Number(flagStr(args, "width")) || kase.width || projectViewport?.width || 390,
|
|
222974
223658
|
height: Number(flagStr(args, "height")) || kase.height || projectViewport?.height || 844,
|
|
@@ -223019,14 +223703,14 @@ var test = async (args) => {
|
|
|
223019
223703
|
onConsole
|
|
223020
223704
|
});
|
|
223021
223705
|
try {
|
|
223022
|
-
const io = fileScenarioIO(
|
|
223706
|
+
const io = fileScenarioIO(resolve15(artifactsRoot, slug3(kase.name)));
|
|
223023
223707
|
const outcome = await renderer.runScenario(session, steps, io);
|
|
223024
223708
|
result = {
|
|
223025
223709
|
name: kase.name,
|
|
223026
223710
|
kind: kase.kind,
|
|
223027
223711
|
ok: outcome.ok,
|
|
223028
223712
|
steps: outcome.steps.map((s2) => ({ desc: s2.desc, ok: s2.ok, error: s2.error })),
|
|
223029
|
-
artifacts: outcome.failureArtifacts.map((p2) =>
|
|
223713
|
+
artifacts: outcome.failureArtifacts.map((p2) => resolve15(artifactsRoot, slug3(kase.name), p2))
|
|
223030
223714
|
};
|
|
223031
223715
|
} finally {
|
|
223032
223716
|
session.dispose();
|
|
@@ -223094,11 +223778,11 @@ init_config();
|
|
|
223094
223778
|
init_manifest();
|
|
223095
223779
|
init_types4();
|
|
223096
223780
|
init_util();
|
|
223097
|
-
import { resolve as
|
|
223781
|
+
import { resolve as resolve16 } from "node:path";
|
|
223098
223782
|
var label = (bundle) => `${Object.keys(bundle).length} file(s)`;
|
|
223099
223783
|
var types4 = async (args) => {
|
|
223100
223784
|
const dirArg = args._[0];
|
|
223101
|
-
const root = dirArg ?
|
|
223785
|
+
const root = dirArg ? resolve16(dirArg) : (() => {
|
|
223102
223786
|
try {
|
|
223103
223787
|
return findProjectRoot(process.cwd());
|
|
223104
223788
|
} catch {
|
|
@@ -223157,23 +223841,23 @@ var types4 = async (args) => {
|
|
|
223157
223841
|
};
|
|
223158
223842
|
|
|
223159
223843
|
// src/index.ts
|
|
223160
|
-
import { existsSync as
|
|
223844
|
+
import { existsSync as existsSync33, readFileSync as readFileSync30, realpathSync as realpathSync2 } from "node:fs";
|
|
223161
223845
|
import { createRequire as createRequire4 } from "node:module";
|
|
223162
|
-
import { delimiter as delimiter2, dirname as dirname20, join as
|
|
223846
|
+
import { delimiter as delimiter2, dirname as dirname20, join as join33 } from "node:path";
|
|
223163
223847
|
|
|
223164
223848
|
// src/commands/install.ts
|
|
223165
223849
|
init_util();
|
|
223166
223850
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
223167
|
-
import { appendFileSync as appendFileSync3, chmodSync as chmodSync2, cpSync as cpSync4, existsSync as
|
|
223168
|
-
import { homedir as
|
|
223169
|
-
import { basename as
|
|
223851
|
+
import { appendFileSync as appendFileSync3, chmodSync as chmodSync2, cpSync as cpSync4, existsSync as existsSync32, mkdirSync as mkdirSync23, readFileSync as readFileSync29, realpathSync, rmSync as rmSync9, symlinkSync } from "node:fs";
|
|
223852
|
+
import { homedir as homedir8 } from "node:os";
|
|
223853
|
+
import { basename as basename16, delimiter, dirname as dirname19, join as join32, resolve as resolve17, sep as sep2 } from "node:path";
|
|
223170
223854
|
var isWin = process.platform === "win32";
|
|
223171
|
-
var HOME =
|
|
223855
|
+
var HOME = homedir8();
|
|
223172
223856
|
var safeRealpath = (p2) => {
|
|
223173
223857
|
try {
|
|
223174
223858
|
return realpathSync(p2);
|
|
223175
223859
|
} catch {
|
|
223176
|
-
return
|
|
223860
|
+
return resolve17(p2);
|
|
223177
223861
|
}
|
|
223178
223862
|
};
|
|
223179
223863
|
var onPath = (dir) => {
|
|
@@ -223190,24 +223874,24 @@ var onPath = (dir) => {
|
|
|
223190
223874
|
};
|
|
223191
223875
|
var install = async (args) => {
|
|
223192
223876
|
const bundle = dirname19(process.execPath);
|
|
223193
|
-
const exeName2 =
|
|
223877
|
+
const exeName2 = basename16(process.execPath);
|
|
223194
223878
|
const chiselName = isWin ? "chisel.exe" : "chisel";
|
|
223195
|
-
if (!
|
|
223879
|
+
if (!existsSync32(join32(bundle, "runtime")) || !existsSync32(join32(bundle, chiselName))) {
|
|
223196
223880
|
throw new CliError("`lecodes install` only works from the packaged release build (the archive containing lecodes + " + "chisel + runtime/). You appear to be running from source or an npm install — install globally " + "with your package manager there instead.");
|
|
223197
223881
|
}
|
|
223198
|
-
const companions = [exeName2, chiselName, "runtime", "README.txt"].filter((f3) =>
|
|
223882
|
+
const companions = [exeName2, chiselName, "runtime", "README.txt"].filter((f3) => existsSync32(join32(bundle, f3)));
|
|
223199
223883
|
if (isWin)
|
|
223200
223884
|
return installWindows(args, bundle, companions);
|
|
223201
223885
|
return installUnix(args, bundle, companions);
|
|
223202
223886
|
};
|
|
223203
223887
|
var copyBundle = (bundle, target, companions) => {
|
|
223204
|
-
|
|
223888
|
+
mkdirSync23(target, { recursive: true });
|
|
223205
223889
|
if (safeRealpath(target) === safeRealpath(bundle))
|
|
223206
223890
|
return;
|
|
223207
223891
|
for (const f3 of companions) {
|
|
223208
|
-
const dest =
|
|
223209
|
-
|
|
223210
|
-
cpSync4(
|
|
223892
|
+
const dest = join32(target, f3);
|
|
223893
|
+
rmSync9(dest, { recursive: true, force: true });
|
|
223894
|
+
cpSync4(join32(bundle, f3), dest, { recursive: true });
|
|
223211
223895
|
}
|
|
223212
223896
|
};
|
|
223213
223897
|
var permError = (e2, dir, system) => {
|
|
@@ -223219,41 +223903,41 @@ var permError = (e2, dir, system) => {
|
|
|
223219
223903
|
};
|
|
223220
223904
|
var installUnix = (args, bundle, companions) => {
|
|
223221
223905
|
const system = flagBool(args, "system");
|
|
223222
|
-
const libDir =
|
|
223223
|
-
const binDir =
|
|
223224
|
-
const link2 =
|
|
223906
|
+
const libDir = resolve17(flagStr(args, "dir") ?? (system ? "/usr/local/lib/lecodes-cli" : join32(HOME, ".local", "lib", "lecodes-cli")));
|
|
223907
|
+
const binDir = resolve17(flagStr(args, "bin") ?? (system ? "/usr/local/bin" : join32(HOME, ".local", "bin")));
|
|
223908
|
+
const link2 = join32(binDir, "lecodes");
|
|
223225
223909
|
try {
|
|
223226
223910
|
copyBundle(bundle, libDir, companions);
|
|
223227
|
-
chmodSync2(
|
|
223228
|
-
chmodSync2(
|
|
223911
|
+
chmodSync2(join32(libDir, "lecodes"), 493);
|
|
223912
|
+
chmodSync2(join32(libDir, "chisel"), 493);
|
|
223229
223913
|
} catch (e2) {
|
|
223230
223914
|
throw permError(e2, libDir, system);
|
|
223231
223915
|
}
|
|
223232
223916
|
try {
|
|
223233
|
-
|
|
223234
|
-
|
|
223235
|
-
symlinkSync(
|
|
223917
|
+
mkdirSync23(binDir, { recursive: true });
|
|
223918
|
+
rmSync9(link2, { force: true });
|
|
223919
|
+
symlinkSync(join32(libDir, "lecodes"), link2);
|
|
223236
223920
|
} catch (e2) {
|
|
223237
223921
|
throw permError(e2, binDir, system);
|
|
223238
223922
|
}
|
|
223239
223923
|
success(`Installed to ${c.bold(libDir)}`);
|
|
223240
|
-
log(` ${c.dim(link2 + " → " +
|
|
223924
|
+
log(` ${c.dim(link2 + " → " + join32(libDir, "lecodes"))}`);
|
|
223241
223925
|
finishPathUnix(args, binDir);
|
|
223242
223926
|
};
|
|
223243
223927
|
var shellRc = () => {
|
|
223244
|
-
const shell =
|
|
223928
|
+
const shell = basename16(process.env.SHELL ?? "");
|
|
223245
223929
|
if (shell === "zsh")
|
|
223246
|
-
return { file:
|
|
223930
|
+
return { file: join32(HOME, ".zshrc"), name: "~/.zshrc" };
|
|
223247
223931
|
if (shell === "bash")
|
|
223248
|
-
return { file:
|
|
223249
|
-
return { file:
|
|
223932
|
+
return { file: join32(HOME, ".bashrc"), name: "~/.bashrc" };
|
|
223933
|
+
return { file: join32(HOME, ".profile"), name: "~/.profile" };
|
|
223250
223934
|
};
|
|
223251
223935
|
var PATH_MARKER = "# Added by `lecodes install`";
|
|
223252
223936
|
var appendPathToShellRc = (binDir) => {
|
|
223253
223937
|
const { file, name } = shellRc();
|
|
223254
223938
|
const line = `export PATH="${binDir}:$PATH"`;
|
|
223255
223939
|
try {
|
|
223256
|
-
const existing =
|
|
223940
|
+
const existing = existsSync32(file) ? readFileSync29(file, "utf8") : "";
|
|
223257
223941
|
if (existing.includes(line))
|
|
223258
223942
|
return name;
|
|
223259
223943
|
appendFileSync3(file, `${existing && !existing.endsWith(`
|
|
@@ -223291,7 +223975,7 @@ var finishPathUnix = (args, binDir) => {
|
|
|
223291
223975
|
log(` • or install system-wide (no PATH edit): ${c.bold("sudo lecodes install --system")}`);
|
|
223292
223976
|
};
|
|
223293
223977
|
var installWindows = (args, bundle, companions) => {
|
|
223294
|
-
const target =
|
|
223978
|
+
const target = resolve17(flagStr(args, "dir") ?? join32(process.env.LOCALAPPDATA ?? join32(HOME, "AppData", "Local"), "Programs", "lecodes-cli"));
|
|
223295
223979
|
try {
|
|
223296
223980
|
copyBundle(bundle, target, companions);
|
|
223297
223981
|
} catch (e2) {
|
|
@@ -223331,7 +224015,7 @@ init_util();
|
|
|
223331
224015
|
var STANDALONE = (() => {
|
|
223332
224016
|
const dir = dirname20(process.execPath);
|
|
223333
224017
|
const chisel = process.platform === "win32" ? "chisel.exe" : "chisel";
|
|
223334
|
-
return
|
|
224018
|
+
return existsSync33(join33(dir, "runtime")) && existsSync33(join33(dir, chisel));
|
|
223335
224019
|
})();
|
|
223336
224020
|
var INSTALLED = STANDALONE && (() => {
|
|
223337
224021
|
const exe = process.platform === "win32" ? "lecodes.exe" : "lecodes";
|
|
@@ -223346,8 +224030,8 @@ var INSTALLED = STANDALONE && (() => {
|
|
|
223346
224030
|
if (!d)
|
|
223347
224031
|
return false;
|
|
223348
224032
|
try {
|
|
223349
|
-
const cand =
|
|
223350
|
-
return
|
|
224033
|
+
const cand = join33(d, exe);
|
|
224034
|
+
return existsSync33(cand) && realpathSync2(cand) === self2;
|
|
223351
224035
|
} catch {
|
|
223352
224036
|
return false;
|
|
223353
224037
|
}
|
|
@@ -223535,6 +224219,12 @@ ${c.bold("Commands:")}
|
|
|
223535
224219
|
--public-url <url> uses the server's copies instead)
|
|
223536
224220
|
update move to the newest published release
|
|
223537
224221
|
(cached in ~/.lecodes/renderer)
|
|
224222
|
+
shaders [compile|update] The project's custom shaders (.mat) and their local compile:
|
|
224223
|
+
no args lists the shaders + their compile state; compile
|
|
224224
|
+
compiles them all into .lecodes/shaders (the same pass
|
|
224225
|
+
'lecodes dev' runs on the fly — via Filament's matc,
|
|
224226
|
+
downloaded on first use to ~/.lecodes/matc); update
|
|
224227
|
+
downloads the newest matc (run after an engine upgrade)
|
|
223538
224228
|
scene [path] LeCodes Scene Editor — visually edit a .scene.ts (tree, inspector,
|
|
223539
224229
|
gizmo) on local files (needs the optional 'lecodes-3d-editor' package;
|
|
223540
224230
|
works inside a cloned project or standalone in any folder)
|
|
@@ -223633,7 +224323,7 @@ ${c.bold("Commands:")}
|
|
|
223633
224323
|
— 'new --env' banks it in .env), revoke <id>
|
|
223634
224324
|
|
|
223635
224325
|
Config lives in ~/.lecodes/config.json (override with LECODES_API / LECODES_TOKEN).`;
|
|
223636
|
-
var commands = { init: init2, login, create, clone: clone3, link, status:
|
|
224326
|
+
var commands = { init: init2, login, create, clone: clone3, link, status: status3, diff: diff2, pull, push, compile: compile2, preview: previewLegacy, render, test, types: types4, design, scene, dev, app, pn, desktop, shaders, assets };
|
|
223637
224327
|
if (STANDALONE)
|
|
223638
224328
|
commands.install = install;
|
|
223639
224329
|
var readVersion = () => package_default.version ?? "unknown";
|
|
@@ -223648,9 +224338,9 @@ var peerVersion = (name, probe) => {
|
|
|
223648
224338
|
const require2 = createRequire4(import.meta.url);
|
|
223649
224339
|
let dir = dirname20(require2.resolve(probe));
|
|
223650
224340
|
for (let depth = 0;depth < 16; depth++) {
|
|
223651
|
-
const manifest =
|
|
223652
|
-
if (
|
|
223653
|
-
const parsed = JSON.parse(
|
|
224341
|
+
const manifest = join33(dir, "package.json");
|
|
224342
|
+
if (existsSync33(manifest)) {
|
|
224343
|
+
const parsed = JSON.parse(readFileSync30(manifest, "utf8"));
|
|
223654
224344
|
if (parsed.name === name)
|
|
223655
224345
|
return parsed.version ?? "unknown";
|
|
223656
224346
|
}
|
|
@@ -223670,6 +224360,8 @@ var printVersion = () => {
|
|
|
223670
224360
|
}
|
|
223671
224361
|
const desktopVersion = selectedDesktopVersion();
|
|
223672
224362
|
rows.push(["lecodes-desktop", installedDesktopVersions().includes(desktopVersion) ? desktopVersion : `${desktopVersion} ${c.dim("(not downloaded — fetched on first `render --desktop`)")}`]);
|
|
224363
|
+
const matcVersion = installedMatcVersions()[0];
|
|
224364
|
+
rows.push(["matc", matcVersion ?? c.dim("not downloaded — fetched on the first local shader compile")]);
|
|
223673
224365
|
const width = Math.max(...rows.map(([label2]) => label2.length));
|
|
223674
224366
|
for (const [label2, value] of rows)
|
|
223675
224367
|
log(`${label2.padEnd(width)} ${value}`);
|