lecodes-cli 2.4.5 → 2.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +450 -138
- package/package.json +8 -8
- package/runtime/materials/lightmap.mat +7 -4
- package/runtime/materials/terrain-lightmap.mat +2 -1
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/VERSION.json +3 -3
- package/runtime/web/assets/materials-world-aKwAWYRd.bin +0 -0
- package/runtime/web/player.js +3 -3
- package/runtime/web-lite/lite.js +1 -1
- package/src/commands/app/android.ts +81 -3
- package/src/commands/app/index.ts +3 -12
- package/src/commands/app/play.ts +107 -0
- package/src/commands/assets.ts +1 -1
- package/src/commands/lightmap.ts +130 -20
- package/src/declarations/lecodes-assets.d.ts +31 -4
- package/src/hosts/desktopRenderer.ts +112 -11
- package/runtime/web/assets/materials-world-n3tLX5vO.bin +0 -0
package/dist/index.js
CHANGED
|
@@ -64,7 +64,7 @@ var package_default;
|
|
|
64
64
|
var init_package = __esm(() => {
|
|
65
65
|
package_default = {
|
|
66
66
|
name: "lecodes-cli",
|
|
67
|
-
version: "2.4.
|
|
67
|
+
version: "2.4.7",
|
|
68
68
|
dependencies: {
|
|
69
69
|
"@letary/chisel": "^0.15.0",
|
|
70
70
|
jimp: "^1.6.1",
|
|
@@ -89,7 +89,7 @@ var init_package = __esm(() => {
|
|
|
89
89
|
},
|
|
90
90
|
peerDependencies: {
|
|
91
91
|
"lecodes-design": "^0.11.0",
|
|
92
|
-
"lecodes-headless": "^2.4.
|
|
92
|
+
"lecodes-headless": "^2.4.7",
|
|
93
93
|
"lecodes-3d-editor": "^0.9.0",
|
|
94
94
|
"lecodes-assets": "^0.2.1",
|
|
95
95
|
"heic-decode": "^2.1.0",
|
|
@@ -159,7 +159,7 @@ var init_package = __esm(() => {
|
|
|
159
159
|
});
|
|
160
160
|
|
|
161
161
|
// ../../sdk/src/version.ts
|
|
162
|
-
var SDK_VERSION = "2.4.
|
|
162
|
+
var SDK_VERSION = "2.4.7";
|
|
163
163
|
|
|
164
164
|
// src/cli/args.ts
|
|
165
165
|
var isNegativeNumber = (s) => /^-(\d|\.\d)/.test(s), isValue = (s) => !s.startsWith("-") || isNegativeNumber(s), parseArgv = (argv, booleans = new Set) => {
|
|
@@ -206398,7 +206398,7 @@ var init_esm29 = __esm(() => {
|
|
|
206398
206398
|
|
|
206399
206399
|
// src/project/types.ts
|
|
206400
206400
|
import { dirname as dirname18, join as join29, relative as relative5, sep as sep3 } from "node:path";
|
|
206401
|
-
import { existsSync as existsSync27, mkdirSync as mkdirSync17, readdirSync as readdirSync12, readFileSync as
|
|
206401
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync17, readdirSync as readdirSync12, readFileSync as readFileSync24, rmSync as rmSync9, statSync as statSync10, writeFileSync as writeFileSync20 } from "node:fs";
|
|
206402
206402
|
import { createRequire as createRequire5 } from "node:module";
|
|
206403
206403
|
var TSCONFIG, listBundleFiles = (dir, base = dir) => {
|
|
206404
206404
|
if (!existsSync27(dir))
|
|
@@ -206427,7 +206427,7 @@ var TSCONFIG, listBundleFiles = (dir, base = dir) => {
|
|
|
206427
206427
|
const dest = join29(typesDir, path);
|
|
206428
206428
|
if (!before.has(path))
|
|
206429
206429
|
stats.added++;
|
|
206430
|
-
else if (
|
|
206430
|
+
else if (readFileSync24(dest, "utf8") !== text)
|
|
206431
206431
|
stats.updated++;
|
|
206432
206432
|
else
|
|
206433
206433
|
continue;
|
|
@@ -206446,7 +206446,7 @@ var TSCONFIG, listBundleFiles = (dir, base = dir) => {
|
|
|
206446
206446
|
const typesDir = join29(root, LECODES_DIR, "types");
|
|
206447
206447
|
const bundle = {};
|
|
206448
206448
|
for (const path of listBundleFiles(typesDir))
|
|
206449
|
-
bundle[path] =
|
|
206449
|
+
bundle[path] = readFileSync24(join29(typesDir, path), "utf8");
|
|
206450
206450
|
return bundle;
|
|
206451
206451
|
}, materializeTypes = async (root, apiUrl) => writeTypeBundle(root, await getTypes(apiUrl)), readLocalTypeBundle = () => {
|
|
206452
206452
|
const fromPackage = (() => {
|
|
@@ -206464,7 +206464,7 @@ var TSCONFIG, listBundleFiles = (dir, base = dir) => {
|
|
|
206464
206464
|
for (const path of candidates) {
|
|
206465
206465
|
if (existsSync27(path)) {
|
|
206466
206466
|
try {
|
|
206467
|
-
return JSON.parse(
|
|
206467
|
+
return JSON.parse(readFileSync24(path, "utf8"));
|
|
206468
206468
|
} catch {}
|
|
206469
206469
|
}
|
|
206470
206470
|
}
|
|
@@ -206502,7 +206502,7 @@ var init_types4 = __esm(() => {
|
|
|
206502
206502
|
});
|
|
206503
206503
|
|
|
206504
206504
|
// src/compile/designCompile.ts
|
|
206505
|
-
import { readFileSync as
|
|
206505
|
+
import { readFileSync as readFileSync25 } from "node:fs";
|
|
206506
206506
|
var LOCAL_ASSET_SCHEME = "lecodesfile://", compileDesignScreen = async (opts) => {
|
|
206507
206507
|
const { root, manifest, designDir, designKey, screenId, state } = opts;
|
|
206508
206508
|
const entries = [];
|
|
@@ -206519,7 +206519,7 @@ var LOCAL_ASSET_SCHEME = "lecodesfile://", compileDesignScreen = async (opts) =>
|
|
|
206519
206519
|
} else if (type === "shader") {
|
|
206520
206520
|
entries.push({ path: file.path, type, shaders: [] });
|
|
206521
206521
|
} else {
|
|
206522
|
-
entries.push({ path: file.path, type, text:
|
|
206522
|
+
entries.push({ path: file.path, type, text: readFileSync25(file.absPath, "utf8") });
|
|
206523
206523
|
}
|
|
206524
206524
|
}
|
|
206525
206525
|
const invoke = state !== undefined ? `screen(${JSON.stringify(state)})` : "screen()";
|
|
@@ -206641,11 +206641,11 @@ var init_browserAuth = __esm(() => {
|
|
|
206641
206641
|
});
|
|
206642
206642
|
|
|
206643
206643
|
// src/project/designMeta.ts
|
|
206644
|
-
import { readFileSync as
|
|
206644
|
+
import { readFileSync as readFileSync26 } from "node:fs";
|
|
206645
206645
|
import { join as join31 } from "node:path";
|
|
206646
206646
|
var PHONE_SIZE, readMetaFrames = (designDir) => {
|
|
206647
206647
|
try {
|
|
206648
|
-
const meta = JSON.parse(
|
|
206648
|
+
const meta = JSON.parse(readFileSync26(join31(designDir, "meta.json"), "utf8"));
|
|
206649
206649
|
const d = meta?.defaultSize;
|
|
206650
206650
|
const valid = Array.isArray(d) && d.length === 2 && d.every((n) => typeof n === "number" && Number.isFinite(n) && n >= 64 && n <= 8192);
|
|
206651
206651
|
return {
|
|
@@ -206996,7 +206996,7 @@ var init_comments = __esm(() => {
|
|
|
206996
206996
|
});
|
|
206997
206997
|
|
|
206998
206998
|
// src/commands/design/snapshot.ts
|
|
206999
|
-
import { existsSync as existsSync32, mkdirSync as mkdirSync20, readdirSync as readdirSync15, readFileSync as
|
|
206999
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync20, readdirSync as readdirSync15, readFileSync as readFileSync27, writeFileSync as writeFileSync22 } from "node:fs";
|
|
207000
207000
|
import { join as join33 } from "node:path";
|
|
207001
207001
|
var listScreens = (designDir) => {
|
|
207002
207002
|
const dir = join33(designDir, "screens");
|
|
@@ -207068,7 +207068,7 @@ var init_snapshot = __esm(() => {
|
|
|
207068
207068
|
const [width, height] = frames.screens[id]?.size ?? frames.defaultSize;
|
|
207069
207069
|
let plan = [{ state: undefined, bases: [id] }];
|
|
207070
207070
|
if (discover) {
|
|
207071
|
-
const { states, canonical } = discover(
|
|
207071
|
+
const { states, canonical } = discover(readFileSync27(join33(ctx.designDir, "screens", `${id}.ts`), "utf8"));
|
|
207072
207072
|
if (stateFlag !== undefined) {
|
|
207073
207073
|
if (!states.includes(stateFlag)) {
|
|
207074
207074
|
if (args.length === 1)
|
|
@@ -207598,7 +207598,7 @@ var init_templates2 = __esm(() => {
|
|
|
207598
207598
|
});
|
|
207599
207599
|
|
|
207600
207600
|
// src/commands/design/index.ts
|
|
207601
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync33, mkdirSync as mkdirSync21, readFileSync as
|
|
207601
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync33, mkdirSync as mkdirSync21, readFileSync as readFileSync28, writeFileSync as writeFileSync23 } from "node:fs";
|
|
207602
207602
|
import { join as join34 } from "node:path";
|
|
207603
207603
|
var firstLine = (body, max = 64) => {
|
|
207604
207604
|
const line = body.split(`
|
|
@@ -207673,7 +207673,7 @@ var firstLine = (body, max = 64) => {
|
|
|
207673
207673
|
let config = {};
|
|
207674
207674
|
if (existsSync33(path)) {
|
|
207675
207675
|
try {
|
|
207676
|
-
config = JSON.parse(
|
|
207676
|
+
config = JSON.parse(readFileSync28(path, "utf8"));
|
|
207677
207677
|
} catch {
|
|
207678
207678
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-design server by hand.");
|
|
207679
207679
|
return;
|
|
@@ -207753,7 +207753,7 @@ var init_design = __esm(() => {
|
|
|
207753
207753
|
}
|
|
207754
207754
|
const ignorePath = join34(ctx.root, ".lecodesignore");
|
|
207755
207755
|
const ignoreLines = [`${ctx.dirName}/.snapshots/`, `${ctx.dirName}/comments/`];
|
|
207756
|
-
const current = existsSync33(ignorePath) ?
|
|
207756
|
+
const current = existsSync33(ignorePath) ? readFileSync28(ignorePath, "utf8") : "";
|
|
207757
207757
|
const present = new Set(current.split(/\r?\n/));
|
|
207758
207758
|
const missing = ignoreLines.filter((line) => !present.has(line));
|
|
207759
207759
|
if (missing.length) {
|
|
@@ -227112,7 +227112,7 @@ import { delimiter as delimiter3, dirname as dirname31, join as join63 } from "n
|
|
|
227112
227112
|
// src/commands/app/index.ts
|
|
227113
227113
|
init_manifest();
|
|
227114
227114
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
227115
|
-
import { cpSync as cpSync4, existsSync as existsSync25, mkdirSync as mkdirSync16, readFileSync as
|
|
227115
|
+
import { cpSync as cpSync4, existsSync as existsSync25, mkdirSync as mkdirSync16, readFileSync as readFileSync23, readdirSync as readdirSync10, rmSync as rmSync8, statSync as statSync9, writeFileSync as writeFileSync19 } from "node:fs";
|
|
227116
227116
|
import { homedir as homedir10 } from "node:os";
|
|
227117
227117
|
import { basename as basename10, dirname as dirname17, join as join27, resolve as resolve14 } from "node:path";
|
|
227118
227118
|
|
|
@@ -229431,7 +229431,7 @@ init_compile();
|
|
|
229431
229431
|
init_manifest();
|
|
229432
229432
|
import { execFileSync as execFileSync2, spawn, spawnSync as spawnSync5 } from "node:child_process";
|
|
229433
229433
|
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
229434
|
-
import { chmodSync, cpSync, existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as
|
|
229434
|
+
import { chmodSync, cpSync, existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync21, readdirSync as readdirSync6, renameSync as renameSync2, rmSync as rmSync4, statSync as statSync5, writeFileSync as writeFileSync15 } from "node:fs";
|
|
229435
229435
|
import { homedir as homedir8 } from "node:os";
|
|
229436
229436
|
import { basename as basename7, dirname as dirname15, join as join23, relative as relative4, resolve as resolve10 } from "node:path";
|
|
229437
229437
|
init_cli();
|
|
@@ -231343,6 +231343,84 @@ if "%OS%"=="Windows_NT" endlocal
|
|
|
231343
231343
|
:omega
|
|
231344
231344
|
`;
|
|
231345
231345
|
|
|
231346
|
+
// src/commands/app/play.ts
|
|
231347
|
+
init_cli();
|
|
231348
|
+
import { sign } from "node:crypto";
|
|
231349
|
+
import { readFileSync as readFileSync20 } from "node:fs";
|
|
231350
|
+
var SCOPE = "https://www.googleapis.com/auth/androidpublisher";
|
|
231351
|
+
var apiBase = () => process.env.LECODES_PLAY_API_URL ?? "https://androidpublisher.googleapis.com";
|
|
231352
|
+
var readServiceAccount = (path) => {
|
|
231353
|
+
let key;
|
|
231354
|
+
try {
|
|
231355
|
+
key = JSON.parse(readFileSync20(path, "utf8"));
|
|
231356
|
+
} catch {
|
|
231357
|
+
throw new CliError(`${path} is not a JSON key file.`);
|
|
231358
|
+
}
|
|
231359
|
+
if (key.type !== "service_account" || !key.client_email || !key.private_key) {
|
|
231360
|
+
throw new CliError(`${path} is not a service account key (Google Cloud → IAM → Service accounts → Keys → Add key → JSON).`);
|
|
231361
|
+
}
|
|
231362
|
+
return key;
|
|
231363
|
+
};
|
|
231364
|
+
var serviceAccountToken = async (key, now = Math.floor(Date.now() / 1000)) => {
|
|
231365
|
+
const tokenUri = key.token_uri ?? "https://oauth2.googleapis.com/token";
|
|
231366
|
+
const part = (v) => Buffer.from(JSON.stringify(v)).toString("base64url");
|
|
231367
|
+
const unsigned = `${part({ alg: "RS256", typ: "JWT" })}.${part({ iss: key.client_email, scope: SCOPE, aud: tokenUri, iat: now, exp: now + 3600 })}`;
|
|
231368
|
+
const signature = sign("RSA-SHA256", Buffer.from(unsigned), key.private_key).toString("base64url");
|
|
231369
|
+
const res = await fetch(tokenUri, {
|
|
231370
|
+
method: "POST",
|
|
231371
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
231372
|
+
body: new URLSearchParams({ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", assertion: `${unsigned}.${signature}` })
|
|
231373
|
+
});
|
|
231374
|
+
const body = await res.json().catch(() => ({}));
|
|
231375
|
+
if (!res.ok || !body.access_token) {
|
|
231376
|
+
throw new CliError(`Google refused the key of ${key.client_email}: ${body.error_description ?? body.error ?? `HTTP ${res.status}`} (a deleted or rotated key fails here — make a new one).`);
|
|
231377
|
+
}
|
|
231378
|
+
return body.access_token;
|
|
231379
|
+
};
|
|
231380
|
+
var HINTS = [
|
|
231381
|
+
[/caller does not have permission|insufficient permissions/i, () => 'invite the service account in Play Console → Users and permissions with "Release to testing tracks" for this app (a new grant can take hours to reach the API)'],
|
|
231382
|
+
[/package not found/i, (r) => `Play Console has no app ${r.packageName} — create it there first (the API cannot); app.json android.applicationId must match`],
|
|
231383
|
+
[/version code .*already been used/i, () => "bump app.json android.versionCode and build again"],
|
|
231384
|
+
[/only releases with status draft/i, () => "the app was never published — upload with --draft, then roll the release out in Play Console"],
|
|
231385
|
+
[/has not been used in project|is disabled/i, () => "enable the Google Play Android Developer API in the key's Cloud project"]
|
|
231386
|
+
];
|
|
231387
|
+
var playError = (step, status, text, release) => {
|
|
231388
|
+
let message = text;
|
|
231389
|
+
try {
|
|
231390
|
+
message = JSON.parse(text).error?.message ?? text;
|
|
231391
|
+
} catch {}
|
|
231392
|
+
const hint = HINTS.find(([re]) => re.test(message))?.[1](release);
|
|
231393
|
+
return new CliError(`Google Play refused the ${step} (HTTP ${status}): ${message.trim()}${hint ? `
|
|
231394
|
+
→ ${hint}` : ""}`);
|
|
231395
|
+
};
|
|
231396
|
+
var uploadToPlay = async (token, release) => {
|
|
231397
|
+
const app = `applications/${encodeURIComponent(release.packageName)}`;
|
|
231398
|
+
const call = async (step, method, path, body) => {
|
|
231399
|
+
const bytes = body instanceof Uint8Array;
|
|
231400
|
+
const res = await fetch(`${apiBase()}/${path}`, {
|
|
231401
|
+
method,
|
|
231402
|
+
headers: {
|
|
231403
|
+
authorization: `Bearer ${token}`,
|
|
231404
|
+
...body ? { "content-type": bytes ? "application/octet-stream" : "application/json" } : {}
|
|
231405
|
+
},
|
|
231406
|
+
body: bytes ? body : body ? JSON.stringify(body) : undefined
|
|
231407
|
+
});
|
|
231408
|
+
const text = await res.text();
|
|
231409
|
+
if (!res.ok)
|
|
231410
|
+
throw playError(step, res.status, text, release);
|
|
231411
|
+
return text ? JSON.parse(text) : {};
|
|
231412
|
+
};
|
|
231413
|
+
const edit = await call("edit", "POST", `androidpublisher/v3/${app}/edits`, {});
|
|
231414
|
+
const editPath = `androidpublisher/v3/${app}/edits/${encodeURIComponent(edit.id)}`;
|
|
231415
|
+
const bundle = await call("bundle", "POST", `upload/${editPath}/bundles?uploadType=media`, release.aab);
|
|
231416
|
+
await call("track", "PUT", `${editPath}/tracks/${encodeURIComponent(release.track)}`, {
|
|
231417
|
+
track: release.track,
|
|
231418
|
+
releases: [{ versionCodes: [String(bundle.versionCode)], status: release.status }]
|
|
231419
|
+
});
|
|
231420
|
+
await call("commit", "POST", `${editPath}:commit`);
|
|
231421
|
+
return { versionCode: bundle.versionCode };
|
|
231422
|
+
};
|
|
231423
|
+
|
|
231346
231424
|
// src/commands/app/android.ts
|
|
231347
231425
|
var ANDROID_SDK_REPO = "letary/lecodes-android-sdk";
|
|
231348
231426
|
var ANDROID_SDK_MIN_VERSION = "2.0.13";
|
|
@@ -231354,7 +231432,7 @@ var shellSdkVersionAndroid = (androidDir) => {
|
|
|
231354
231432
|
const path = join23(androidDir, "lecodes-runtime", "build.gradle.kts");
|
|
231355
231433
|
if (!existsSync21(path))
|
|
231356
231434
|
return null;
|
|
231357
|
-
const m = /io\.letary:lecodes-sdk(?:-(?:core|2d|3d))?:(\d+\.\d+\.\d+)@aar/.exec(
|
|
231435
|
+
const m = /io\.letary:lecodes-sdk(?:-(?:core|2d|3d))?:(\d+\.\d+\.\d+)@aar/.exec(readFileSync21(path, "utf8"));
|
|
231358
231436
|
return m ? m[1] : null;
|
|
231359
231437
|
};
|
|
231360
231438
|
var GRADLE_WRAPPER_JAR_URL = "https://raw.githubusercontent.com/gradle/gradle/v8.13.0/gradle/wrapper/gradle-wrapper.jar";
|
|
@@ -231362,7 +231440,7 @@ var GRADLE_WRAPPER_JAR_SHA256 = "81a82aaea5abcc8ff68b3dfcb58b3c3c429378efd98e743
|
|
|
231362
231440
|
var ensureGradleWrapperJar = async () => {
|
|
231363
231441
|
const cached = join23(homedir8(), ".lecodes", "gradle-wrapper", GRADLE_VERSION, "gradle-wrapper.jar");
|
|
231364
231442
|
if (existsSync21(cached))
|
|
231365
|
-
return new Uint8Array(
|
|
231443
|
+
return new Uint8Array(readFileSync21(cached));
|
|
231366
231444
|
try {
|
|
231367
231445
|
const res = await fetch(GRADLE_WRAPPER_JAR_URL);
|
|
231368
231446
|
if (!res.ok)
|
|
@@ -231493,7 +231571,7 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
231493
231571
|
resources = prior.resources;
|
|
231494
231572
|
} else {
|
|
231495
231573
|
resources = await compileShell(join23(assetsDir, "app.js"), { remotePatterns });
|
|
231496
|
-
bundleJs =
|
|
231574
|
+
bundleJs = readFileSync21(join23(assetsDir, "app.js"), "utf8");
|
|
231497
231575
|
}
|
|
231498
231576
|
const requestedVariant = config.android?.variant ?? "auto";
|
|
231499
231577
|
if (requestedVariant !== "auto" && !ANDROID_SDK_VARIANTS.includes(requestedVariant)) {
|
|
@@ -231638,7 +231716,7 @@ var restampManifestBlock = (androidDir, tag, block, what, missing, after = []) =
|
|
|
231638
231716
|
const path = join23(androidDir, "app", "src", "main", "AndroidManifest.xml");
|
|
231639
231717
|
if (!existsSync21(path))
|
|
231640
231718
|
return;
|
|
231641
|
-
const content =
|
|
231719
|
+
const content = readFileSync21(path, "utf8");
|
|
231642
231720
|
const tagged = new RegExp(`\\n[ \\t]*<!-- lecodes:${tag}[\\s\\S]*?<!-- \\/lecodes:${tag} -->`);
|
|
231643
231721
|
let next;
|
|
231644
231722
|
if (tagged.test(content))
|
|
@@ -231667,7 +231745,7 @@ var restampAppBuildGradle = (androidDir, config, gradlePlugins) => {
|
|
|
231667
231745
|
warn("android/app/build.gradle.kts is missing — run `lecodes app init android` on a clean tree to regenerate the shell.");
|
|
231668
231746
|
return;
|
|
231669
231747
|
}
|
|
231670
|
-
let content =
|
|
231748
|
+
let content = readFileSync21(path, "utf8");
|
|
231671
231749
|
const before = content;
|
|
231672
231750
|
const versionName = config.version ?? "1.0.0";
|
|
231673
231751
|
const versionCode = config.android?.versionCode ?? 1;
|
|
@@ -231818,10 +231896,9 @@ var buildAndroid = async (flags) => {
|
|
|
231818
231896
|
}
|
|
231819
231897
|
if (produced.length === 0)
|
|
231820
231898
|
throw new CliError("Gradle finished but produced no .apk/.aab — check android/app/build/outputs.");
|
|
231821
|
-
const slug = flags.slug ?? slugify(config.name ?? basename7(root));
|
|
231822
231899
|
const outDir = resolve10(root, flags.out ?? join23(root, "dist"));
|
|
231823
231900
|
mkdirSync12(outDir, { recursive: true });
|
|
231824
|
-
const stem = `${
|
|
231901
|
+
const stem = `${artifactStem(root, config, flags.slug)}${configuration === "debug" ? "-debug" : ""}`;
|
|
231825
231902
|
for (const src of produced) {
|
|
231826
231903
|
const unsigned = configuration === "release" && !signing;
|
|
231827
231904
|
const dest = join23(outDir, `${stem}${unsigned ? "-unsigned" : ""}${src.endsWith(".aab") ? ".aab" : ".apk"}`);
|
|
@@ -231829,7 +231906,7 @@ var buildAndroid = async (flags) => {
|
|
|
231829
231906
|
const size = (statSync5(dest).size / (1024 * 1024)).toFixed(1);
|
|
231830
231907
|
log(`${c.green("output")} ${relative4(root, dest) || dest}${c.dim(` (${size} MB)`)}`);
|
|
231831
231908
|
}
|
|
231832
|
-
success(configuration === "debug" ? "Debug build done — `adb install` the .apk to test (or `lecodes dev --android --apk <file>`)." : signing ? "Release build done — upload the .aab
|
|
231909
|
+
success(configuration === "debug" ? "Debug build done — `adb install` the .apk to test (or `lecodes dev --android --apk <file>`)." : signing ? "Release build done — `lecodes app upload android` sends the .aab to Play (the .apk side-loads for testing)." : "Unsigned release build done — sign it before it goes anywhere.");
|
|
231833
231910
|
};
|
|
231834
231911
|
var androidBuild = defineCommand({
|
|
231835
231912
|
name: "android",
|
|
@@ -231839,6 +231916,69 @@ var androidBuild = defineCommand({
|
|
|
231839
231916
|
examples: ["lecodes app build android", "lecodes app build android --configuration Debug --apk", "lecodes app build android -o out/"],
|
|
231840
231917
|
run: ({ flags }) => buildAndroid(flags)
|
|
231841
231918
|
});
|
|
231919
|
+
var artifactStem = (root, config, slug) => `${slug ?? slugify(config.name ?? basename7(root))}-${config.version ?? "1.0.0"}-${config.android?.versionCode ?? 1}`;
|
|
231920
|
+
var PLAY_KEY_ENV = "LECODES_PLAY_SERVICE_ACCOUNT";
|
|
231921
|
+
var androidUploadFlags = {
|
|
231922
|
+
aab: str("the bundle (default: the dist/<name>-<version>-<versionCode>.aab `app build android` wrote)", { value: "<file>" }),
|
|
231923
|
+
track: str("internal | alpha | beta | production | a closed track's name", { value: "<t>", default: "internal" }),
|
|
231924
|
+
draft: bool("a draft release, rolled out in Play Console (the only kind a never-published app takes)"),
|
|
231925
|
+
key: str("the service account's JSON key (remembered in the project .env)", { value: "<json>" }),
|
|
231926
|
+
slug: str("file name stem of the default bundle, as given to `app build android --slug`", { value: "<name>" })
|
|
231927
|
+
};
|
|
231928
|
+
var uploadAndroid = async (flags) => {
|
|
231929
|
+
const root = projectRoot();
|
|
231930
|
+
const config = readAppConfigAt(root);
|
|
231931
|
+
if (!config?.android?.applicationId || !existsSync21(join23(root, "android"))) {
|
|
231932
|
+
throw new CliError("No Android shell here — run `lecodes app init android` first.");
|
|
231933
|
+
}
|
|
231934
|
+
const packageName = config.android.applicationId;
|
|
231935
|
+
const aabPath = flags.aab ? resolve10(flags.aab) : join23(root, "dist", `${artifactStem(root, config, flags.slug)}.aab`);
|
|
231936
|
+
if (!existsSync21(aabPath)) {
|
|
231937
|
+
throw new CliError(`No bundle at ${relative4(root, aabPath) || aabPath} — run \`lecodes app build android\` first (it names the file by app.json's version + versionCode).`);
|
|
231938
|
+
}
|
|
231939
|
+
const given = flags.key;
|
|
231940
|
+
const keyPath = given ? resolve10(given.startsWith("~/") ? join23(homedir8(), given.slice(2)) : given) : process.env[PLAY_KEY_ENV];
|
|
231941
|
+
if (!keyPath) {
|
|
231942
|
+
throw new CliError([
|
|
231943
|
+
`A Google Play service account key is needed: --key <file.json> (remembered in the project .env as ${PLAY_KEY_ENV}).`,
|
|
231944
|
+
" Google Cloud: enable the Google Play Android Developer API, then IAM → Service accounts → Keys → Add key → JSON;",
|
|
231945
|
+
` Play Console → Users and permissions: invite the account's email with "Release to testing tracks" for the app.`
|
|
231946
|
+
].join(`
|
|
231947
|
+
`));
|
|
231948
|
+
}
|
|
231949
|
+
if (!existsSync21(keyPath))
|
|
231950
|
+
throw new CliError(`Service account key not found: ${keyPath}`);
|
|
231951
|
+
const key = readServiceAccount(keyPath);
|
|
231952
|
+
if (given) {
|
|
231953
|
+
upsertDotEnv(root, { [PLAY_KEY_ENV]: envValue(envPathFor(root, keyPath)) });
|
|
231954
|
+
note(`Stored the key reference in .env (${PLAY_KEY_ENV})`);
|
|
231955
|
+
}
|
|
231956
|
+
const size = (statSync5(aabPath).size / (1024 * 1024)).toFixed(1);
|
|
231957
|
+
note(`Uploading ${relative4(root, aabPath) || aabPath} (${size} MB) to ${packageName}, track ${flags.track}${flags.draft ? " as a draft" : ""} — as ${key.client_email}`);
|
|
231958
|
+
const token = await serviceAccountToken(key);
|
|
231959
|
+
const { versionCode } = await uploadToPlay(token, {
|
|
231960
|
+
packageName,
|
|
231961
|
+
aab: readFileSync21(aabPath),
|
|
231962
|
+
track: flags.track,
|
|
231963
|
+
status: flags.draft ? "draft" : "completed"
|
|
231964
|
+
});
|
|
231965
|
+
const fresh = readAppConfigAt(root);
|
|
231966
|
+
const next = Math.max(fresh.android.versionCode ?? 1, versionCode + 1);
|
|
231967
|
+
if (next !== fresh.android.versionCode) {
|
|
231968
|
+
fresh.android.versionCode = next;
|
|
231969
|
+
writeJson(join23(root, "app.json"), fresh);
|
|
231970
|
+
}
|
|
231971
|
+
success(`Uploaded versionCode ${versionCode} to the ${flags.track} track${flags.draft ? " as a draft — roll it out in Play Console" : ""}.`);
|
|
231972
|
+
note(`android.versionCode is ${next} for the next build.`);
|
|
231973
|
+
};
|
|
231974
|
+
var androidUpload = defineCommand({
|
|
231975
|
+
name: "android",
|
|
231976
|
+
summary: "Upload the .aab to a Google Play track (then bumps android.versionCode)",
|
|
231977
|
+
description: `One edit of the Google Play Developer API — the bundle onto the track as one release, then the commit; Play changes only when every step went through. Authenticated with a Cloud service account's JSON key: --key on first use, then the project .env remembers it (${PLAY_KEY_ENV}; in CI set the variable to the key's path, e.g. a GitLab File variable). The app must already exist in Play Console under app.json android.applicationId (the API cannot create it), the account be invited there with "Release to testing tracks". A successful upload spends the versionCode, so app.json android.versionCode moves past it for the next build.`,
|
|
231978
|
+
flags: androidUploadFlags,
|
|
231979
|
+
examples: ["lecodes app upload android --key ~/keys/play.json", "lecodes app upload android --track internal", "lecodes app upload android --draft"],
|
|
231980
|
+
run: ({ flags }) => uploadAndroid(flags)
|
|
231981
|
+
});
|
|
231842
231982
|
var ANDROID_SIGNING_ENV = {
|
|
231843
231983
|
storeFile: "LECODES_ANDROID_KEYSTORE_FILE",
|
|
231844
231984
|
storePassword: "LECODES_ANDROID_KEYSTORE_PASSWORD",
|
|
@@ -231847,7 +231987,7 @@ var ANDROID_SIGNING_ENV = {
|
|
|
231847
231987
|
};
|
|
231848
231988
|
var readProperties = (path) => {
|
|
231849
231989
|
const out = {};
|
|
231850
|
-
for (const raw of
|
|
231990
|
+
for (const raw of readFileSync21(path, "utf8").split(/\r?\n/)) {
|
|
231851
231991
|
const line = raw.trim();
|
|
231852
231992
|
if (!line || line.startsWith("#") || line.startsWith("!"))
|
|
231853
231993
|
continue;
|
|
@@ -231947,7 +232087,7 @@ var writeSigningConfig = (root, config, storeFile, alias, storePassword, keyPass
|
|
|
231947
232087
|
const rel = relative4(root, storeFile);
|
|
231948
232088
|
if (rel && !rel.startsWith("..") && !rel.includes(":") && !rel.replace(/\\/g, "/").startsWith("android/")) {
|
|
231949
232089
|
const ignorePath = join23(root, ".gitignore");
|
|
231950
|
-
const ignore = existsSync21(ignorePath) ?
|
|
232090
|
+
const ignore = existsSync21(ignorePath) ? readFileSync21(ignorePath, "utf8") : "";
|
|
231951
232091
|
if (!ignore.split(`
|
|
231952
232092
|
`).some((l) => l.trim() === "*.keystore")) {
|
|
231953
232093
|
writeFileSync15(ignorePath, ignore + (!ignore || ignore.endsWith(`
|
|
@@ -232185,10 +232325,10 @@ var doctorAndroidChecks = async (root, config, check) => {
|
|
|
232185
232325
|
}
|
|
232186
232326
|
if (config.update) {
|
|
232187
232327
|
const manifest = join23(androidDir, "lecodes-runtime", "src", "main", "AndroidManifest.xml");
|
|
232188
|
-
check(existsSync21(manifest) &&
|
|
232328
|
+
check(existsSync21(manifest) && readFileSync21(manifest, "utf8").includes("io.letary.lecodes.UPDATE_URL"), "OTA update URL in the runtime manifest", "run `lecodes app sync`", false);
|
|
232189
232329
|
const appSrc = join23(androidDir, "app", "src", "main", "java");
|
|
232190
232330
|
const kt = existsSync21(appSrc) ? readdirSync6(appSrc, { recursive: true }).map(String).filter((f) => f.endsWith(".kt")) : [];
|
|
232191
|
-
const sources = kt.map((f) =>
|
|
232331
|
+
const sources = kt.map((f) => readFileSync21(join23(appSrc, f), "utf8"));
|
|
232192
232332
|
const boots = sources.some((src) => src.includes("LeCodesShell.boot"));
|
|
232193
232333
|
check(boots, "App code launches via the updater (LeCodesShell.boot)", 'no app/src/main/java/**/*.kt calls LeCodesShell.boot(engine, activity) — downloaded updates would never run; see "Over-the-air updates" in android/README.md for the AppViewModel snippet', false);
|
|
232194
232334
|
const waits = sources.some((src) => /LeCodesShell\.boot\(\s*engine\s*,\s*activity\b/.test(src));
|
|
@@ -232208,7 +232348,7 @@ var doctorAndroidChecks = async (root, config, check) => {
|
|
|
232208
232348
|
}
|
|
232209
232349
|
check(!!findKeytool(), "keytool", "install a JDK or set JAVA_HOME (`lecodes app keystore …` needs it)", false);
|
|
232210
232350
|
const appGradle = join23(androidDir, "app", "build.gradle.kts");
|
|
232211
|
-
const gradleText = existsSync21(appGradle) ?
|
|
232351
|
+
const gradleText = existsSync21(appGradle) ? readFileSync21(appGradle, "utf8") : "";
|
|
232212
232352
|
check(gradleText.includes("abiFilters"), "abiFilters in app/build.gradle.kts", 'add `ndk { abiFilters += listOf("arm64-v8a", "armeabi-v7a") }` to defaultConfig — x86_64 devices crash at launch without it', false);
|
|
232213
232353
|
check(["isMinifyEnabled", "isShrinkResources"].every((k) => new RegExp(`${k} = (true|false)`).test(gradleText)), "R8 block in app/build.gradle.kts", 'shell predates R8-by-default — see android/README.md "Building"', false);
|
|
232214
232354
|
if (Object.keys(config.plugins ?? {}).includes("push")) {
|
|
@@ -232223,7 +232363,7 @@ import { basename as basename9, dirname as dirname16, join as join26, resolve as
|
|
|
232223
232363
|
|
|
232224
232364
|
// src/hosts/desktopRenderer.ts
|
|
232225
232365
|
import { execFileSync as execFileSync3, spawn as spawn2, spawnSync as spawnSync6 } from "node:child_process";
|
|
232226
|
-
import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync2, readdirSync as readdirSync7, readFileSync as
|
|
232366
|
+
import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync2, readdirSync as readdirSync7, readFileSync as readFileSync22, rmSync as rmSync5, statSync as statSync6, writeFileSync as writeFileSync16, linkSync } from "node:fs";
|
|
232227
232367
|
import { homedir as homedir9, tmpdir as tmpdir2 } from "node:os";
|
|
232228
232368
|
import { delimiter, join as join24, resolve as resolve11 } from "node:path";
|
|
232229
232369
|
init_cli();
|
|
@@ -232368,7 +232508,7 @@ var installedDesktopVersions = () => {
|
|
|
232368
232508
|
};
|
|
232369
232509
|
var selectedDesktopVersion = () => {
|
|
232370
232510
|
try {
|
|
232371
|
-
const saved = JSON.parse(
|
|
232511
|
+
const saved = JSON.parse(readFileSync22(currentFile(), "utf8")).version;
|
|
232372
232512
|
if (saved && /^\d+\.\d+\.\d+/.test(saved) && semverNewer(DESKTOP_RENDERER_VERSION, saved) > 0)
|
|
232373
232513
|
return saved;
|
|
232374
232514
|
} catch {}
|
|
@@ -232493,7 +232633,84 @@ var resolveServerExe = async (renderer = "gl") => {
|
|
|
232493
232633
|
}
|
|
232494
232634
|
return exe;
|
|
232495
232635
|
};
|
|
232496
|
-
var
|
|
232636
|
+
var bakeExeName = () => process.platform === "win32" ? "lecodes-bake.exe" : "lecodes-bake";
|
|
232637
|
+
var bakePlatformKey = () => {
|
|
232638
|
+
if (process.platform === "win32")
|
|
232639
|
+
return "windows-x64";
|
|
232640
|
+
if (process.platform === "linux")
|
|
232641
|
+
return "linux-x64";
|
|
232642
|
+
throw new CliError(`The lightmap bake host (lecodes-bake) isn't available on ${process.platform}: the bake traces with Vulkan ray query or OptiX — run it on Windows or Linux.`);
|
|
232643
|
+
};
|
|
232644
|
+
var bakeCacheRoot = () => join24(homedir9(), ".lecodes", "bake");
|
|
232645
|
+
var findBakeExe = (dir) => {
|
|
232646
|
+
for (const entry of readdirSync7(dir, { withFileTypes: true })) {
|
|
232647
|
+
const p = join24(dir, entry.name);
|
|
232648
|
+
if (entry.isFile() && entry.name === bakeExeName())
|
|
232649
|
+
return p;
|
|
232650
|
+
if (entry.isDirectory()) {
|
|
232651
|
+
const found = findBakeExe(p);
|
|
232652
|
+
if (found)
|
|
232653
|
+
return found;
|
|
232654
|
+
}
|
|
232655
|
+
}
|
|
232656
|
+
return null;
|
|
232657
|
+
};
|
|
232658
|
+
var installedBakeVersions = () => {
|
|
232659
|
+
try {
|
|
232660
|
+
return readdirSync7(bakeCacheRoot(), { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("bake-")).map((e) => e.name.slice("bake-".length)).filter((v) => findBakeExe(join24(bakeCacheRoot(), `bake-${v}`)) !== null).sort(semverNewer);
|
|
232661
|
+
} catch {
|
|
232662
|
+
return [];
|
|
232663
|
+
}
|
|
232664
|
+
};
|
|
232665
|
+
var resolveBakeExe = async () => {
|
|
232666
|
+
const override = process.env.LECODES_BAKE_EXE;
|
|
232667
|
+
if (override) {
|
|
232668
|
+
if (!existsSync22(override))
|
|
232669
|
+
throw new CliError(`LECODES_BAKE_EXE points at "${override}" which doesn't exist.`);
|
|
232670
|
+
return resolve11(override);
|
|
232671
|
+
}
|
|
232672
|
+
const key = bakePlatformKey();
|
|
232673
|
+
const selected = selectedDesktopVersion();
|
|
232674
|
+
const cachedDir = (version) => join24(bakeCacheRoot(), `bake-${version}`);
|
|
232675
|
+
const cachedSelected = existsSync22(cachedDir(selected)) ? findBakeExe(cachedDir(selected)) : null;
|
|
232676
|
+
if (cachedSelected)
|
|
232677
|
+
return cachedSelected;
|
|
232678
|
+
let pick;
|
|
232679
|
+
try {
|
|
232680
|
+
const { versions } = await fetchReleaseProduct("bake", "lightmap bake host (lecodes-bake)");
|
|
232681
|
+
const exact = versions.find((v) => v.version === selected)?.files[key];
|
|
232682
|
+
pick = exact ? { version: selected, file: exact } : newestForPlatform(versions, key);
|
|
232683
|
+
} catch (e) {
|
|
232684
|
+
const newest = installedBakeVersions()[0];
|
|
232685
|
+
if (newest) {
|
|
232686
|
+
warnErr(`${e instanceof Error ? e.message : String(e)}
|
|
232687
|
+
Using the cached lightmap bake host ${newest}.`);
|
|
232688
|
+
return findBakeExe(cachedDir(newest));
|
|
232689
|
+
}
|
|
232690
|
+
throw new CliError(`${e instanceof Error ? e.message : String(e)}
|
|
232691
|
+
` + " The lightmap bake runs in lecodes-bake, downloaded from the release manifest on first use. Point LECODES_BAKE_EXE at a local build " + "(hosts/desktop: build.ps1 -Bake → build-bake/lecodes-bake.exe; build.sh with CREATOR_DESKTOP_BAKE=ON → build-bake-linux/lecodes-bake).");
|
|
232692
|
+
}
|
|
232693
|
+
if (!pick) {
|
|
232694
|
+
throw new CliError(`The lightmap bake host (lecodes-bake) isn't published for ${key} yet. Point LECODES_BAKE_EXE at a local build ` + "(hosts/desktop: build.ps1 -Bake → build-bake/lecodes-bake.exe; build.sh with CREATOR_DESKTOP_BAKE=ON → build-bake-linux/lecodes-bake).");
|
|
232695
|
+
}
|
|
232696
|
+
const dir = cachedDir(pick.version);
|
|
232697
|
+
let exe = existsSync22(dir) ? findBakeExe(dir) : null;
|
|
232698
|
+
if (!exe) {
|
|
232699
|
+
if (pick.version !== selected)
|
|
232700
|
+
note(`No lightmap bake host is published for LeCodes Desktop ${selected} — using lecodes-bake ${pick.version}.`);
|
|
232701
|
+
note(`Downloading the lightmap bake host (lecodes-bake ${pick.version} [${key}], ${(pick.file.size / 1024 / 1024).toFixed(1)} MB)…`);
|
|
232702
|
+
await downloadReleaseArchive(pick.file, {
|
|
232703
|
+
cacheRoot: bakeCacheRoot(),
|
|
232704
|
+
dest: dir,
|
|
232705
|
+
validate: (d) => findBakeExe(d) ? null : "The downloaded archive doesn't contain the lecodes-bake binary."
|
|
232706
|
+
});
|
|
232707
|
+
exe = findBakeExe(dir);
|
|
232708
|
+
if (!exe)
|
|
232709
|
+
throw new CliError(`Bake host cache at ${dir} is broken — delete the folder and retry.`);
|
|
232710
|
+
}
|
|
232711
|
+
return exe;
|
|
232712
|
+
};
|
|
232713
|
+
var resolveHostExe = (host, renderer) => host === "bake" ? resolveBakeExe() : host === "server" ? resolveServerExe(renderer) : resolveDesktopExe(renderer);
|
|
232497
232714
|
var updateDesktop = async () => {
|
|
232498
232715
|
const manifest = await fetchDesktopManifest();
|
|
232499
232716
|
const latest = isMac() ? pickMacVersion(manifest.versions, manifest.latest, platformKey2()) : manifest.latest;
|
|
@@ -232680,7 +232897,8 @@ var runDesktopDev = async (url, opts = {}) => {
|
|
|
232680
232897
|
});
|
|
232681
232898
|
};
|
|
232682
232899
|
var runDesktopRender = async (js, resources, opts) => {
|
|
232683
|
-
const server2 = opts.host === "server";
|
|
232900
|
+
const server2 = opts.host === "server" || opts.host === "bake";
|
|
232901
|
+
const what = opts.host === "bake" ? "bake" : server2 ? "server" : "desktop";
|
|
232684
232902
|
const exe = await resolveHostExe(opts.host, opts.renderer);
|
|
232685
232903
|
const runDir = stageBundle(js, resources);
|
|
232686
232904
|
try {
|
|
@@ -232729,7 +232947,7 @@ var runDesktopRender = async (js, resources, opts) => {
|
|
|
232729
232947
|
}, opts.timeoutMs);
|
|
232730
232948
|
child.on("error", (e) => {
|
|
232731
232949
|
clearTimeout(killer);
|
|
232732
|
-
fail(new CliError(`Can't run the ${
|
|
232950
|
+
fail(new CliError(`Can't run the ${what} host: ${e.message}`));
|
|
232733
232951
|
});
|
|
232734
232952
|
child.on("exit", (code) => {
|
|
232735
232953
|
clearTimeout(killer);
|
|
@@ -232741,7 +232959,7 @@ var runDesktopRender = async (js, resources, opts) => {
|
|
|
232741
232959
|
const tail = demangle(chunks.slice(-80).join("")).trimEnd();
|
|
232742
232960
|
const detail = tail.length > 0 ? `
|
|
232743
232961
|
${tail}` : "";
|
|
232744
|
-
fail(new CliError(code === 0 ? `The renderer exited cleanly but produced no PNG — did the app open a screen?${detail}` : `The ${server2 ?
|
|
232962
|
+
fail(new CliError(code === 0 ? `The renderer exited cleanly but produced no PNG — did the app open a screen?${detail}` : `The ${server2 ? `${what} host` : "renderer"} exited with code ${code}.${detail}`));
|
|
232745
232963
|
});
|
|
232746
232964
|
});
|
|
232747
232965
|
} finally {
|
|
@@ -233412,7 +233630,7 @@ var shellSdkVersion = (iosDir) => {
|
|
|
233412
233630
|
const path = join27(iosDir, "LeCodesRuntime", "Package.swift");
|
|
233413
233631
|
if (!existsSync25(path))
|
|
233414
233632
|
return null;
|
|
233415
|
-
const m = /lecodes-ios-sdk\.git",\s*(?:exact|from):\s*"(\d+\.\d+\.\d+)(?:-(?:core|2d|3d))?"/.exec(
|
|
233633
|
+
const m = /lecodes-ios-sdk\.git",\s*(?:exact|from):\s*"(\d+\.\d+\.\d+)(?:-(?:core|2d|3d))?"/.exec(readFileSync23(path, "utf8"));
|
|
233416
233634
|
return m && VERSION_RE.test(m[1]) ? m[1] : null;
|
|
233417
233635
|
};
|
|
233418
233636
|
var IOS_SDK_OPTS = { repo: SDK_REPO, minVersion: SDK_MIN_VERSION, configKey: "ios.sdk" };
|
|
@@ -233618,7 +233836,7 @@ var syncIos = async (root, config) => {
|
|
|
233618
233836
|
rmSync8(resourcesDir, { recursive: true, force: true });
|
|
233619
233837
|
mkdirSync16(resourcesDir, { recursive: true });
|
|
233620
233838
|
const resources = await compileShell(join27(resourcesDir, "app.js"), { remotePatterns });
|
|
233621
|
-
const bundleJs =
|
|
233839
|
+
const bundleJs = readFileSync23(join27(resourcesDir, "app.js"), "utf8");
|
|
233622
233840
|
const { variant, why } = resolveVariant(requestedVariant, bundleJs);
|
|
233623
233841
|
log(`${c.green("variant")} ${variant}${requestedVariant === "auto" ? ` (auto: ${why})` : " (forced by ios.variant)"}`);
|
|
233624
233842
|
const sdkWhy = sdkPin === "latest" ? "latest" : config.ios.sdk ? "pinned by ios.sdk" : "from the shell — set ios.sdk or run `lecodes app update`";
|
|
@@ -233684,7 +233902,7 @@ var syncIos = async (root, config) => {
|
|
|
233684
233902
|
if (pluginSpecs.length > 0 || existsSync25(lockPath))
|
|
233685
233903
|
writeJson(lockPath, { plugins: newLock });
|
|
233686
233904
|
const pbxPath = join27(iosDir, "App.xcodeproj", "project.pbxproj");
|
|
233687
|
-
let pbx =
|
|
233905
|
+
let pbx = readFileSync23(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};`);
|
|
233688
233906
|
const mergedEntitlements = {};
|
|
233689
233907
|
const appLinks = appLinksOf(config);
|
|
233690
233908
|
if (appLinks.length) {
|
|
@@ -233976,8 +234194,8 @@ var doctorIosChecks = async (root, config, check) => {
|
|
|
233976
234194
|
const validVariant = requestedVariant === "auto" || SDK_VARIANTS.includes(requestedVariant);
|
|
233977
234195
|
check(validVariant, `ios.variant "${requestedVariant}"`, `use "auto" or one of ${SDK_VARIANTS.join(", ")}`);
|
|
233978
234196
|
if (existsSync25(bundle) && validVariant) {
|
|
233979
|
-
const { variant } = resolveVariant(requestedVariant,
|
|
233980
|
-
const pkg =
|
|
234197
|
+
const { variant } = resolveVariant(requestedVariant, readFileSync23(bundle, "utf8"));
|
|
234198
|
+
const pkg = readFileSync23(join27(root, "ios", "LeCodesRuntime", "Package.swift"), "utf8");
|
|
233981
234199
|
const tag = /lecodes-ios-sdk\.git",\s*exact:\s*"([^"]+)"/.exec(pkg)?.[1] ?? "";
|
|
233982
234200
|
const tagVariant = /-(core|2d|3d)$/.exec(tag)?.[1] ?? "full";
|
|
233983
234201
|
check(tagVariant === variant, `SDK variant ${variant}`, "run `lecodes app sync`");
|
|
@@ -233999,25 +234217,25 @@ var doctorIosChecks = async (root, config, check) => {
|
|
|
233999
234217
|
const inSync = wanted.every((id) => lock[id]) && Object.keys(lock).every((id) => wanted.includes(id));
|
|
234000
234218
|
check(wanted.length === 0 || inSync, "Plugins synced", "run `lecodes app sync`");
|
|
234001
234219
|
const pbxFile = join27(root, "ios", "App.xcodeproj", "project.pbxproj");
|
|
234002
|
-
if (existsSync25(pbxFile) &&
|
|
234220
|
+
if (existsSync25(pbxFile) && readFileSync23(pbxFile, "utf8").includes("CODE_SIGN_ENTITLEMENTS")) {
|
|
234003
234221
|
check(existsSync25(join27(root, "ios", "App", "App.entitlements")), "App/App.entitlements present", "run `lecodes app sync`");
|
|
234004
234222
|
}
|
|
234005
234223
|
if (wanted.includes("push")) {
|
|
234006
234224
|
const runtimeSwift = join27(root, "ios", "LeCodesRuntime", "Sources", "LeCodesRuntime", "LeCodesRuntime.swift");
|
|
234007
|
-
const baked = existsSync25(runtimeSwift) &&
|
|
234225
|
+
const baked = existsSync25(runtimeSwift) && readFileSync23(runtimeSwift, "utf8").includes("bootProjectUuid");
|
|
234008
234226
|
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);
|
|
234009
234227
|
}
|
|
234010
234228
|
if (config.update) {
|
|
234011
|
-
const plist =
|
|
234229
|
+
const plist = readFileSync23(join27(root, "ios", "App", "Info.plist"), "utf8");
|
|
234012
234230
|
check(plist.includes("<key>LeCodesUpdateURL</key>"), "OTA update URL in Info.plist", "run `lecodes app sync`", false);
|
|
234013
234231
|
const appDir = join27(root, "ios", "App");
|
|
234014
|
-
const swift = readdirSync10(appDir).filter((f) => f.endsWith(".swift")).map((f) =>
|
|
234232
|
+
const swift = readdirSync10(appDir).filter((f) => f.endsWith(".swift")).map((f) => readFileSync23(join27(appDir, f), "utf8"));
|
|
234015
234233
|
const launchesViaUpdater = swift.some((t) => t.includes("LeCodesAppViewController") || t.includes("LeCodesUpdater.activeBundle"));
|
|
234016
234234
|
check(launchesViaUpdater, "App code launches via the updater", `no App/*.swift subclasses LeCodesAppViewController or calls LeCodesUpdater.activeBundle() — downloaded updates would never run; see "Over-the-air updates" in ios/README.md`, false);
|
|
234017
234235
|
}
|
|
234018
234236
|
{
|
|
234019
234237
|
const vc = join27(root, "ios", "App", "ViewController.swift");
|
|
234020
|
-
const is2x = !existsSync25(vc) ||
|
|
234238
|
+
const is2x = !existsSync25(vc) || readFileSync23(vc, "utf8").includes("LeCodesAppViewController");
|
|
234021
234239
|
check(is2x, "App/ViewController.swift is the 2.x shell (LeCodesAppViewController)", "a 1.x shell's App/*.swift: replace AppDelegate.swift, SceneDelegate.swift and ViewController.swift with the ones `lecodes app init ios` writes today (your own code goes into ViewController.configure(_:))", false);
|
|
234022
234240
|
}
|
|
234023
234241
|
const asc = readJson2(join27(homedir10(), ".lecodes", "asc.json"));
|
|
@@ -234202,18 +234420,8 @@ var iosUpload = defineCommand({
|
|
|
234202
234420
|
});
|
|
234203
234421
|
var upload = defineCommand({
|
|
234204
234422
|
name: "upload",
|
|
234205
|
-
summary: "Upload a build to the store: ios (App Store Connect)",
|
|
234206
|
-
commands: [
|
|
234207
|
-
iosUpload,
|
|
234208
|
-
defineCommand({
|
|
234209
|
-
name: "android",
|
|
234210
|
-
summary: "Play upload (not wired yet)",
|
|
234211
|
-
hidden: true,
|
|
234212
|
-
run: () => {
|
|
234213
|
-
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).");
|
|
234214
|
-
}
|
|
234215
|
-
})
|
|
234216
|
-
]
|
|
234423
|
+
summary: "Upload a build to the store: ios (App Store Connect) | android (Google Play)",
|
|
234424
|
+
commands: [iosUpload, androidUpload]
|
|
234217
234425
|
});
|
|
234218
234426
|
var open = defineCommand({
|
|
234219
234427
|
name: "open",
|
|
@@ -234337,7 +234545,7 @@ var resolveCmgenExe = async () => {
|
|
|
234337
234545
|
init_peerInstall();
|
|
234338
234546
|
var assets_default = defineCommand({
|
|
234339
234547
|
name: "assets",
|
|
234340
|
-
summary: "The asset pipeline: convert / probe / doctor / retarget / unpack / scene / sky / terrain-pack …",
|
|
234548
|
+
summary: "The asset pipeline: convert / probe / doctor / unwrap / retarget / unpack / scene / sky / terrain-pack …",
|
|
234341
234549
|
usage: "<command> [args…]",
|
|
234342
234550
|
passthrough: true,
|
|
234343
234551
|
examples: ["lecodes assets convert hero.fbx --clips anims/*.fbx -o hero.glb", "lecodes assets doctor hero.glb --fix --ktx2", "lecodes assets --help"],
|
|
@@ -235367,7 +235575,7 @@ var desktop_default = defineCommand({
|
|
|
235367
235575
|
|
|
235368
235576
|
// src/commands/dev.ts
|
|
235369
235577
|
init_manifest();
|
|
235370
|
-
import { existsSync as existsSync38, mkdirSync as mkdirSync22, readFileSync as
|
|
235578
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync22, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "node:fs";
|
|
235371
235579
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
235372
235580
|
import { networkInterfaces } from "node:os";
|
|
235373
235581
|
import { basename as basename15, join as join43 } from "node:path";
|
|
@@ -235375,7 +235583,7 @@ import { basename as basename15, join as join43 } from "node:path";
|
|
|
235375
235583
|
// src/dev/devServer.ts
|
|
235376
235584
|
init_localFiles();
|
|
235377
235585
|
import { createHash as createHash8 } from "node:crypto";
|
|
235378
|
-
import { createReadStream as createReadStream2, readFileSync as
|
|
235586
|
+
import { createReadStream as createReadStream2, readFileSync as readFileSync30, statSync as statSync11 } from "node:fs";
|
|
235379
235587
|
import { join as join39 } from "node:path";
|
|
235380
235588
|
import { Readable as Readable3 } from "node:stream";
|
|
235381
235589
|
init_projectCompile();
|
|
@@ -235510,7 +235718,7 @@ init_paths();
|
|
|
235510
235718
|
// src/web/halves.ts
|
|
235511
235719
|
init_compile();
|
|
235512
235720
|
init_cli();
|
|
235513
|
-
import { cpSync as cpSync5, existsSync as existsSync34, readFileSync as
|
|
235721
|
+
import { cpSync as cpSync5, existsSync as existsSync34, readFileSync as readFileSync29 } from "node:fs";
|
|
235514
235722
|
import { join as join38, sep as sep4 } from "node:path";
|
|
235515
235723
|
var halfModule = (half) => `plugins/${half.folder}/index.js`;
|
|
235516
235724
|
var asCliError = (e) => new CliError(e instanceof Error ? e.message : String(e));
|
|
@@ -235521,7 +235729,7 @@ var projectHalves = (root) => {
|
|
|
235521
235729
|
const folder = dir.split(sep4).pop();
|
|
235522
235730
|
let web;
|
|
235523
235731
|
try {
|
|
235524
|
-
web = webHalfManifestOf(
|
|
235732
|
+
web = webHalfManifestOf(readFileSync29(join38(dir, MANIFEST_FILE), "utf8"), folder, id);
|
|
235525
235733
|
} catch (e) {
|
|
235526
235734
|
throw asCliError(e);
|
|
235527
235735
|
}
|
|
@@ -235662,7 +235870,7 @@ var startDevServer = async (options) => {
|
|
|
235662
235870
|
const liteTable = () => {
|
|
235663
235871
|
let plugins = {};
|
|
235664
235872
|
try {
|
|
235665
|
-
plugins = JSON.parse(
|
|
235873
|
+
plugins = JSON.parse(readFileSync30(join39(lite, "plugins.json"), "utf8")).plugins ?? {};
|
|
235666
235874
|
} catch {}
|
|
235667
235875
|
const table = { ...plugins };
|
|
235668
235876
|
for (const { half, hash } of halves) {
|
|
@@ -236153,7 +236361,7 @@ var androidCrashLines = (session) => {
|
|
|
236153
236361
|
|
|
236154
236362
|
// src/dev/iosDev.ts
|
|
236155
236363
|
init_cli();
|
|
236156
|
-
import { existsSync as existsSync37, readdirSync as readdirSync17, readFileSync as
|
|
236364
|
+
import { existsSync as existsSync37, readdirSync as readdirSync17, readFileSync as readFileSync31, statSync as statSync13, unlinkSync } from "node:fs";
|
|
236157
236365
|
import { homedir as homedir12 } from "node:os";
|
|
236158
236366
|
import { join as join42 } from "node:path";
|
|
236159
236367
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
@@ -236186,7 +236394,7 @@ var listDevices2 = () => {
|
|
|
236186
236394
|
if (!ok || !existsSync37(tmp))
|
|
236187
236395
|
return [];
|
|
236188
236396
|
try {
|
|
236189
|
-
const json = JSON.parse(
|
|
236397
|
+
const json = JSON.parse(readFileSync31(tmp, "utf8"));
|
|
236190
236398
|
return (json.result?.devices ?? []).map((d) => ({
|
|
236191
236399
|
udid: d.identifier,
|
|
236192
236400
|
name: d.deviceProperties?.name ?? d.identifier,
|
|
@@ -236233,7 +236441,7 @@ var deviceHasApp = (udid, bundleId) => {
|
|
|
236233
236441
|
if (!ok || !existsSync37(tmp))
|
|
236234
236442
|
return false;
|
|
236235
236443
|
try {
|
|
236236
|
-
const json = JSON.parse(
|
|
236444
|
+
const json = JSON.parse(readFileSync31(tmp, "utf8"));
|
|
236237
236445
|
return (json.result?.apps ?? []).some((a) => a.bundleIdentifier === bundleId);
|
|
236238
236446
|
} catch {
|
|
236239
236447
|
return false;
|
|
@@ -236273,7 +236481,7 @@ var launchOnIos = (opts) => {
|
|
|
236273
236481
|
if (!start.ok)
|
|
236274
236482
|
throw new CliError(`Could not launch ${bundleId} on ${target.name}: ${start.out}`);
|
|
236275
236483
|
try {
|
|
236276
|
-
const json = JSON.parse(
|
|
236484
|
+
const json = JSON.parse(readFileSync31(tmp, "utf8"));
|
|
236277
236485
|
pid = json.result?.process?.processIdentifier?.toString() ?? null;
|
|
236278
236486
|
} catch {
|
|
236279
236487
|
pid = null;
|
|
@@ -236345,7 +236553,7 @@ var lanCandidates = () => {
|
|
|
236345
236553
|
var loadDevToken = (root) => {
|
|
236346
236554
|
const path = join43(root, LECODES_DIR, "dev.json");
|
|
236347
236555
|
try {
|
|
236348
|
-
const token = JSON.parse(
|
|
236556
|
+
const token = JSON.parse(readFileSync32(path, "utf8")).token;
|
|
236349
236557
|
if (token && /^[a-z0-9]{8,}$/.test(token))
|
|
236350
236558
|
return token;
|
|
236351
236559
|
} catch {}
|
|
@@ -236580,7 +236788,7 @@ init_api();
|
|
|
236580
236788
|
|
|
236581
236789
|
// src/platform/serverDiff.ts
|
|
236582
236790
|
init_localFiles();
|
|
236583
|
-
import { readFileSync as
|
|
236791
|
+
import { readFileSync as readFileSync33 } from "node:fs";
|
|
236584
236792
|
var isTextAsset = (t) => t === "text" || t === "shader" || t === "scene";
|
|
236585
236793
|
var computeServerDiff = (project, scan) => {
|
|
236586
236794
|
const pathById = buildPaths(project.assets);
|
|
@@ -236598,7 +236806,7 @@ var computeServerDiff = (project, scan) => {
|
|
|
236598
236806
|
const local = localByPath.get(path);
|
|
236599
236807
|
if (local && !server2) {
|
|
236600
236808
|
const text = isTextKind(local.kind);
|
|
236601
|
-
entries.push({ path, status: "added", binary: !text, serverText: "", localText: text ?
|
|
236809
|
+
entries.push({ path, status: "added", binary: !text, serverText: "", localText: text ? readFileSync33(local.absPath, "utf8") : undefined });
|
|
236602
236810
|
continue;
|
|
236603
236811
|
}
|
|
236604
236812
|
if (server2 && !local) {
|
|
@@ -236609,12 +236817,12 @@ var computeServerDiff = (project, scan) => {
|
|
|
236609
236817
|
if (server2 && local) {
|
|
236610
236818
|
if (isTextAsset(server2.type)) {
|
|
236611
236819
|
const serverText = server2.text ?? "";
|
|
236612
|
-
const localText =
|
|
236820
|
+
const localText = readFileSync33(local.absPath, "utf8");
|
|
236613
236821
|
if (serverText !== localText)
|
|
236614
236822
|
entries.push({ path, status: "modified", binary: false, serverText, localText });
|
|
236615
236823
|
} else {
|
|
236616
236824
|
const serverSize = server2.file?.size;
|
|
236617
|
-
const localSize = Buffer.byteLength(
|
|
236825
|
+
const localSize = Buffer.byteLength(readFileSync33(local.absPath));
|
|
236618
236826
|
if (serverSize !== undefined && serverSize !== localSize) {
|
|
236619
236827
|
entries.push({ path, status: "modified", binary: true });
|
|
236620
236828
|
}
|
|
@@ -236946,7 +237154,7 @@ var init3 = (dir, forDesktop) => {
|
|
|
236946
237154
|
// src/commands/install.ts
|
|
236947
237155
|
init_cli();
|
|
236948
237156
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
236949
|
-
import { appendFileSync as appendFileSync3, chmodSync as chmodSync2, cpSync as cpSync6, existsSync as existsSync40, mkdirSync as mkdirSync24, readFileSync as
|
|
237157
|
+
import { appendFileSync as appendFileSync3, chmodSync as chmodSync2, cpSync as cpSync6, existsSync as existsSync40, mkdirSync as mkdirSync24, readFileSync as readFileSync34, realpathSync as realpathSync2, rmSync as rmSync11, symlinkSync as symlinkSync2 } from "node:fs";
|
|
236950
237158
|
import { homedir as homedir13 } from "node:os";
|
|
236951
237159
|
import { basename as basename17, delimiter as delimiter2, dirname as dirname22, join as join45, resolve as resolve21 } from "node:path";
|
|
236952
237160
|
var isWin = process.platform === "win32";
|
|
@@ -237000,7 +237208,7 @@ var appendPathToShellRc = (binDir) => {
|
|
|
237000
237208
|
const { file, name } = shellRc();
|
|
237001
237209
|
const line = `export PATH="${binDir}:$PATH"`;
|
|
237002
237210
|
try {
|
|
237003
|
-
const existing = existsSync40(file) ?
|
|
237211
|
+
const existing = existsSync40(file) ? readFileSync34(file, "utf8") : "";
|
|
237004
237212
|
if (existing.includes(line))
|
|
237005
237213
|
return name;
|
|
237006
237214
|
appendFileSync3(file, `${existing && !existing.endsWith(`
|
|
@@ -237122,10 +237330,11 @@ var install_default = defineCommand({
|
|
|
237122
237330
|
});
|
|
237123
237331
|
|
|
237124
237332
|
// src/commands/lightmap.ts
|
|
237125
|
-
import { existsSync as existsSync41, mkdirSync as mkdirSync25, mkdtempSync as mkdtempSync4, readFileSync as
|
|
237333
|
+
import { closeSync, existsSync as existsSync41, mkdirSync as mkdirSync25, mkdtempSync as mkdtempSync4, openSync, readFileSync as readFileSync35, readSync, rmSync as rmSync12, statSync as statSync14, writeFileSync as writeFileSync27 } from "node:fs";
|
|
237126
237334
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
237127
237335
|
import { basename as basename18, join as join46, relative as relative7, resolve as resolve22 } from "node:path";
|
|
237128
237336
|
import { deflateSync } from "node:zlib";
|
|
237337
|
+
init_peerInstall();
|
|
237129
237338
|
init_cli();
|
|
237130
237339
|
init_shared();
|
|
237131
237340
|
var range = (name, value, min, max) => {
|
|
@@ -237136,7 +237345,7 @@ var range = (name, value, min, max) => {
|
|
|
237136
237345
|
var readBake = (file) => {
|
|
237137
237346
|
if (!existsSync41(file))
|
|
237138
237347
|
throw new CliError(`No bake at ${file} — run \`lecodes lightmap bake\` first.`);
|
|
237139
|
-
const data = JSON.parse(
|
|
237348
|
+
const data = JSON.parse(readFileSync35(file, "utf8"));
|
|
237140
237349
|
if (data.version !== 4)
|
|
237141
237350
|
throw new CliError(`${file} is not a creator-bake file (version ${String(data.version)}) — rebake.`);
|
|
237142
237351
|
return data;
|
|
@@ -237152,6 +237361,26 @@ var probeBoxOf = (s) => {
|
|
|
237152
237361
|
throw new CliError(`--probe-box wants six numbers x0,y0,z0,x1,y1,z1, got "${s}".`);
|
|
237153
237362
|
return [Math.min(v[0], v[3]), Math.min(v[1], v[4]), Math.min(v[2], v[5]), Math.max(v[0], v[3]), Math.max(v[1], v[4]), Math.max(v[2], v[5])];
|
|
237154
237363
|
};
|
|
237364
|
+
var cubePatternsOf = (argv) => {
|
|
237365
|
+
const out = [];
|
|
237366
|
+
for (let i = 0;i < argv.length; i++) {
|
|
237367
|
+
const a = argv[i];
|
|
237368
|
+
if (a === "--")
|
|
237369
|
+
break;
|
|
237370
|
+
if (a.startsWith("--cube="))
|
|
237371
|
+
out.push(a.slice("--cube=".length));
|
|
237372
|
+
else if (a === "--cube" && i + 1 < argv.length && (!argv[i + 1].startsWith("-") || /^-(\d|\.\d)/.test(argv[i + 1])))
|
|
237373
|
+
out.push(argv[++i]);
|
|
237374
|
+
}
|
|
237375
|
+
for (const p of out) {
|
|
237376
|
+
try {
|
|
237377
|
+
new RegExp(p);
|
|
237378
|
+
} catch (e) {
|
|
237379
|
+
throw new CliError(`bad --cube pattern: ${p} (${e instanceof Error ? e.message : String(e)})`);
|
|
237380
|
+
}
|
|
237381
|
+
}
|
|
237382
|
+
return out.filter((p) => p !== "");
|
|
237383
|
+
};
|
|
237155
237384
|
var placeholderBake = (stem) => JSON.stringify({ version: 4, placeholder: true, size: 4, pages: 0, texel: 0, lightScale: 0, light: [`${stem}-light.ktx2`], aux: [`${stem}-aux.ktx2`], instances: [] }) + `
|
|
237156
237385
|
`;
|
|
237157
237386
|
var ktx2Placeholder = (vkFormat) => {
|
|
@@ -237197,13 +237426,74 @@ var ensurePlaceholders = (outDir, stem) => {
|
|
|
237197
237426
|
const grid = join46(outDir, `${stem}.lgrid`);
|
|
237198
237427
|
if (missing(grid))
|
|
237199
237428
|
writeFileSync27(grid, "NONE");
|
|
237429
|
+
const uv1 = join46(outDir, `${stem}-uv1.bin`);
|
|
237430
|
+
if (missing(uv1))
|
|
237431
|
+
writeFileSync27(uv1, "NONE");
|
|
237432
|
+
};
|
|
237433
|
+
var readGlbJson = (file) => {
|
|
237434
|
+
let fd;
|
|
237435
|
+
try {
|
|
237436
|
+
fd = openSync(file, "r");
|
|
237437
|
+
const head = Buffer.alloc(20);
|
|
237438
|
+
if (readSync(fd, head, 0, 20, 0) !== 20)
|
|
237439
|
+
return null;
|
|
237440
|
+
if (head.readUInt32LE(0) !== 1179937895 || head.readUInt32LE(16) !== 1313821514)
|
|
237441
|
+
return null;
|
|
237442
|
+
const json = Buffer.alloc(head.readUInt32LE(12));
|
|
237443
|
+
if (readSync(fd, json, 0, json.length, 20) !== json.length)
|
|
237444
|
+
return null;
|
|
237445
|
+
return JSON.parse(json.toString("utf8"));
|
|
237446
|
+
} catch {
|
|
237447
|
+
return null;
|
|
237448
|
+
} finally {
|
|
237449
|
+
if (fd !== undefined)
|
|
237450
|
+
closeSync(fd);
|
|
237451
|
+
}
|
|
237452
|
+
};
|
|
237453
|
+
var lacksLightmapUv = (g) => {
|
|
237454
|
+
const skinned = new Set((g.nodes ?? []).filter((n) => n.skin !== undefined && n.mesh !== undefined).map((n) => n.mesh));
|
|
237455
|
+
const count = (accessor) => accessor === undefined ? 0 : g.accessors?.[accessor]?.count ?? 0;
|
|
237456
|
+
return (g.meshes ?? []).some((mesh, i) => !skinned.has(i) && (mesh.primitives ?? []).some((p) => {
|
|
237457
|
+
const pos = count(p.attributes?.POSITION);
|
|
237458
|
+
return (p.mode ?? 4) === 4 && pos > 0 && (p.indices === undefined ? pos : count(p.indices)) >= 3 && p.attributes?.TEXCOORD_1 === undefined;
|
|
237459
|
+
}));
|
|
237460
|
+
};
|
|
237461
|
+
var referencedGlbs = (js, resources) => {
|
|
237462
|
+
const wanted = new Set([...js.matchAll(/(?:fetchLocal|_creatorFetch\.local)\("([^"]+)"\)/g)].map((m) => m[1]));
|
|
237463
|
+
return [...new Set(resources.filter((r) => !r.remote && !r.shader && r.absPath && /\.glb$/i.test(r.name) && wanted.has(r.name)).map((r) => r.absPath))];
|
|
237464
|
+
};
|
|
237465
|
+
var unwrapReferenced = async (js, resources, root, texel) => {
|
|
237466
|
+
const glbs = referencedGlbs(js, resources);
|
|
237467
|
+
const raw = glbs.filter((f) => {
|
|
237468
|
+
const g = readGlbJson(f);
|
|
237469
|
+
return g !== null && lacksLightmapUv(g);
|
|
237470
|
+
});
|
|
237471
|
+
if (raw.length === 0) {
|
|
237472
|
+
note(`Lightmap uvs: the ${glbs.length} GLB(s) the app references carry them`);
|
|
237473
|
+
return;
|
|
237474
|
+
}
|
|
237475
|
+
note(`Unwrapping ${raw.length} of the ${glbs.length} GLB(s) the app references — no lightmap uvs; written in place (--no-unwrap skips)…`);
|
|
237476
|
+
const assets = await loadPeer("lecodes-assets", "cli", { for: "lecodes lightmap bake (lightmap uvs)" });
|
|
237477
|
+
const t0 = performance.now();
|
|
237478
|
+
const rel = (f) => relative7(root, f).replace(/\\/g, "/");
|
|
237479
|
+
const reports = await assets.unwrapGlbFiles(raw, {
|
|
237480
|
+
texel,
|
|
237481
|
+
onFile: (r) => {
|
|
237482
|
+
if (r.status === "failed")
|
|
237483
|
+
warnErr(`${rel(r.input)}: ${r.summary} — it bakes without lightmap uvs`);
|
|
237484
|
+
else
|
|
237485
|
+
logErr(c.dim(` ${r.status === "unwrapped" ? "unwrapped " : ""}${rel(r.input)}: ${r.summary}`));
|
|
237486
|
+
}
|
|
237487
|
+
});
|
|
237488
|
+
const count = (s) => reports.filter((r) => r.status === s).length;
|
|
237489
|
+
note(`Lightmap uvs: ${count("unwrapped")} file(s) unwrapped${count("skipped") ? `, ${count("skipped")} had nothing to unwrap` : ""}${count("failed") ? `, ${count("failed")} failed` : ""} in ${((performance.now() - t0) / 1000).toFixed(1)} s`);
|
|
237200
237490
|
};
|
|
237201
237491
|
var bake = defineCommand({
|
|
237202
237492
|
name: "bake",
|
|
237203
237493
|
summary: "Bake the sun, the sky and the lamps into a lux atlas for the level's statics",
|
|
237204
|
-
description: `Runs the app in the
|
|
237494
|
+
description: `Runs the app in bake mode in lecodes-bake — the windowless host with the bake built in (no physics, 2D, audio, net or navmesh), downloaded on first use like the desktop host; LECODES_BAKE_EXE points at a local build (hosts/desktop build.ps1 -Bake). The statics are what the app registers with Lightmap.add (a scene file does it for every node no Physics aspect moves), the bake fires at its Lightmap.load call. Writes <out>/<name>.bake + <name>-light[_n].ktx2 (irradiance, BC6H) + <name>-aux[_n].ktx2 (sky visibility, the directional share and direction of the light) + <name>-probes.ktx2 (the reflection probes: a cubemap of the level's radiance per probe, prefiltered per roughness; --probe-spacing 0 = none). Needs a GPU with hardware ray tracing (--tracer).
|
|
237205
237495
|
|
|
237206
|
-
|
|
237496
|
+
Lightmap UVs: the GLBs the app references that have none (TEXCOORD_1) are unwrapped first, IN PLACE — the same as lecodes assets unwrap, run on just those files; a file that has them is left alone, --no-unwrap skips the step. A whole level in one GLB that wants --groups / --zone is unwrapped by hand first (lecodes assets unwrap --help).`,
|
|
237207
237497
|
flags: {
|
|
237208
237498
|
out: str("where the atlas lands", { value: "<dir>", default: "assets/lightmap" }),
|
|
237209
237499
|
name: str("file stem — one per baked level", { value: "<stem>", default: "lightmap" }),
|
|
@@ -237219,7 +237509,9 @@ Props need lightmap UVs first: lecodes assets doctor assets/props/*.glb --lightm
|
|
|
237219
237509
|
bounces: num("path vertices at most (Russian roulette from the third); 0 = direct light only", { default: 3 }),
|
|
237220
237510
|
"patch-samples": num("next-event samples per texel over the BRIGHT PATCHES of a first direct pass (sunlit floors, the ceiling around a lamp) — what keeps the bounce in dark rooms from arriving as impulses (every path vertex takes one more); 0 = the cosine paths alone", { default: 64 }),
|
|
237221
237511
|
split: bool('also write <name>-direct[_n].ktx2 / <name>-indirect[_n].ktx2 — the pages the "direct" / "indirect" views bind (list them as env.lightmap.direct / .indirect)'),
|
|
237222
|
-
denoise: str("
|
|
237512
|
+
denoise: str("the AI denoiser (--denoiser) over: indirect = the bounce alone (the range 2026-09-16: bounce noise 57 % -> 23 % of the mean, per-instance means unchanged), no-sun = + the sky and the lamps (the sky through a small opening: 0..3 hits of 256 rays, a texel is one of four values), all = + the sun (its shadow edges too: a sunlit pillar's mean moved 6 %), none. School, two seeds: no-sun took the sky 1.8 % -> 0.5 %, the lamps 43 % -> 21 %", { value: "<none|indirect|no-sun|all>", default: "no-sun" }),
|
|
237513
|
+
tracer: str("which GPU API traces: auto = OptiX where an NVIDIA driver with OptiX opens, else Vulkan ray query — any card with hardware ray tracing (RTX, Radeon RX 6000+, Arc) with its driver alone; optix / vulkan by name (the bake fails when it cannot open)", { value: "<auto|optix|vulkan>", default: "auto" }),
|
|
237514
|
+
denoiser: str("which AI denoiser --denoise runs: auto = OptiX's on an NVIDIA card, else Intel Open Image Denoise (its CUDA / HIP / CPU device, the DLLs next to the host); optix / oidn by name. Independent of --tracer; one that cannot open is a warning and the pages stay as traced", { value: "<auto|optix|oidn>", default: "auto" }),
|
|
237223
237515
|
"light-radius": num("lamps: the emitter's radius (m) — soft shadows, no 1/d² singularity", { default: 0.05 }),
|
|
237224
237516
|
"sun-angle": num("sun angular diameter (deg)", { default: 0.53 }),
|
|
237225
237517
|
bias: num("ray start offset from the surface (m)", { default: 0.01 }),
|
|
@@ -237231,6 +237523,9 @@ Props need lightmap UVs first: lecodes assets doctor assets/props/*.glb --lightm
|
|
|
237231
237523
|
"probe-indoor": bool("keep only the probes under a roof — those whose open share of the sphere (rays from the point that meet nothing) is at most 15 %, holes in the geometry or not: outdoor statics reflect the sky through their baked sky visibility instead — an environment map without the ground, but no outdoor probe can reach indoors and the atlas shrinks"),
|
|
237232
237524
|
"probe-layout": str("rooms = every indoor point of the grid slides to the middle of its room and the points of one room merge: one probe per room, far from the walls, its box the room (stations every ~8 m along a room over 12 m), outdoor points stay on the grid; grid = everything stays on the grid. Either way the pages carry which probes each block of texels sees (rays at bake time)", { value: "<rooms|grid>", default: "rooms" }),
|
|
237233
237525
|
"min-texels": num("instances smaller than this many ATLAS TEXELS along every axis (x --texel: 12 at 5 cm = 0.6 m) get no place in the atlas: they still cast their baked shadow and bounce, their own light is baked where they stand (six sides, no atlas texels) — cartridges, boxes, pipe elbows, a fire extinguisher: a baked shadow does not read on them and their tight charts are where the seams are. 0 = everything bakes; --dry-run lists what falls under it", { default: 12 }),
|
|
237526
|
+
"thin-texels": num("ROUND AND NARROW objects leave the atlas like --min-texels: an instance none of whose lightmap charts is wider than this many texels across its short side (at --texel) — a railing's tubes, a cable, a pipe run, blinds, a gun barrel. A strip of 1-3 texels around a tube cannot hold the light's change around it and its uv seam shows as a step (the range's railing: two texels 5 mm apart on the seam, one lit, one shadowed); the ambient cube baked where it stands is smooth. A flat object has a wide chart somewhere and stays. 0 = off; --dry-run lists what falls under it", { default: 4 }),
|
|
237527
|
+
cube: str(`BAKE AS CUBES BY NAME: a regular expression (ECMAScript, case-sensitive) searched in every record's key — <node path>@<ordinal>, one record per mesh of a model, so SM_pipe takes every pipe node and @1$ the second mesh of each. A record that matches leaves the atlas like a --min-texels / --thin-texels one, whatever its size or charts: an occluder whose own light is baked where it stands as ambient cubes (in slabs along a long one, --object-slab) — a pipe run, a fence, a prop whose lightmap seams show. Repeat the flag (--cube SM_pipe --cube SM_fence) or join with | in one (--cube "SM_pipe|SM_fence", quoted for the shell); a pattern that starts with - goes as --cube=<regex>. The scene's env.lightmap.cube adds its own patterns, a node's lightmap: 'cube' marks one node by hand; --dry-run lists what matches`, { value: "<regex>" }),
|
|
237528
|
+
"object-slab": num("A LONG object the atlas left out (--min-texels, --thin-texels, --cube) takes its light in SLABS of this many metres along the longest axis of its bounds — round(length / this) ambient cubes, 16 at most — a 6 m pipe run, a railing along a wall: one end under a lamp, the other in the dark, where one cube gave all of it the mean. The shader blends the two slabs around each pixel along that axis. 0 = one cube an object", { value: "<m>", default: 1 }),
|
|
237234
237529
|
"emissive-nits": num("EMISSIVE SURFACES LIGHT THE LEVEL: cd/m² of emission 1.0 (emissive factor x strength x map) — every lit region of an emissive surface (a panel drawn in a ceiling's texture, a screen, a sign) bakes as a rectangle lamp. Default: what the level's camera makes of 1.0, 1.2 x 2^EV100, so a panel lights as brightly as it looks. 0 = emissive surfaces light nothing"),
|
|
237235
237530
|
"detail-texels": num("A SMALL SURFACE GETS A FINER TEXEL: an instance whose surface is under this many atlas texels (area / texel²) is laid out denser by sqrt(this / its texels), 3x at most — a pipe of 1 m is ~300 texels at 5 cm, its charts a few texels wide and every chart edge a step; at 2000 it bakes at ~2 cm. 0 = off", { default: 2000 }),
|
|
237236
237531
|
volume: num("THE LIGHT GRID for movers: the cell size in metres (0.75 of it tall) — per cell in free space the baked ambient light (sky, lamps, every bounce; not the direct sun, which stays real-time on movers), links between cells a ray connects so nothing blends through a wall; written as <name>.lgrid, list it as env.lightmap.volume. 0 = none", { default: 1 }),
|
|
@@ -237239,16 +237534,18 @@ Props need lightmap UVs first: lecodes assets doctor assets/props/*.glb --lightm
|
|
|
237239
237534
|
"dry-run": bool("only the atlas LAYOUT: pages, texel size, the largest rects — no tracing, nothing written (seconds, not minutes; the host still loads the level)"),
|
|
237240
237535
|
frames: num("host frames to run before giving up — load headroom", { default: 900 }),
|
|
237241
237536
|
timeout: num("host time cap (ms; default: none)"),
|
|
237242
|
-
width: num("host
|
|
237243
|
-
height: num("host
|
|
237244
|
-
renderer: str("
|
|
237245
|
-
logs: bool("forward the host's output")
|
|
237537
|
+
width: num("the bake host's virtual viewport width — it has no window; nothing in the bake depends on it (default: app.json desktop.window, else 1280)"),
|
|
237538
|
+
height: num("the bake host's virtual viewport height (default: app.json desktop.window, else 720)"),
|
|
237539
|
+
renderer: str("ignored: the bake host is one binary (lecodes-bake, GL shaders on Filament's NOOP backend); kept so older scripts still parse", { value: "<gl|vulkan>" }),
|
|
237540
|
+
logs: bool("forward the host's output"),
|
|
237541
|
+
"no-unwrap": bool("leave the referenced GLBs as they are: no lightmap unwrap of those without TEXCOORD_1 (they bake without lightmap uvs)")
|
|
237246
237542
|
},
|
|
237247
237543
|
examples: ["lecodes lightmap bake", "lecodes lightmap bake --entry school.ts --name school --texel 0.04 --pages 4 --debug-dir .lightmap/school --logs"],
|
|
237248
|
-
run: async ({ flags }) => {
|
|
237544
|
+
run: async ({ flags, argv }) => {
|
|
237249
237545
|
const { root } = localProject();
|
|
237250
237546
|
const outDir = resolve22(root, flags.out);
|
|
237251
237547
|
const stem = flags.name;
|
|
237548
|
+
const cube = cubePatternsOf(argv);
|
|
237252
237549
|
if (!/^[A-Za-z0-9_-]+$/.test(stem))
|
|
237253
237550
|
throw new CliError(`--name wants a plain file stem (letters, digits, - and _), got "${stem}".`);
|
|
237254
237551
|
const entryFile = flags.entry;
|
|
@@ -237256,6 +237553,12 @@ Props need lightmap UVs first: lecodes assets doctor assets/props/*.glb --lightm
|
|
|
237256
237553
|
throw new CliError(`No ${entryFile} in ${root} — pass --entry <file> (the file that builds the scene and calls Lightmap.load).`);
|
|
237257
237554
|
}
|
|
237258
237555
|
const debugDir = flags["debug-dir"] ? resolve22(root, flags["debug-dir"]) : "";
|
|
237556
|
+
if (!["none", "indirect", "no-sun", "all"].includes(flags.denoise))
|
|
237557
|
+
throw new CliError(`--denoise wants none, indirect, no-sun or all, got "${flags.denoise}".`);
|
|
237558
|
+
if (!["auto", "optix", "vulkan"].includes(flags.tracer))
|
|
237559
|
+
throw new CliError(`--tracer wants auto, optix or vulkan, got "${flags.tracer}".`);
|
|
237560
|
+
if (!["auto", "optix", "oidn"].includes(flags.denoiser))
|
|
237561
|
+
throw new CliError(`--denoiser wants auto, optix or oidn, got "${flags.denoiser}".`);
|
|
237259
237562
|
const cfg = {
|
|
237260
237563
|
outDir: outDir.replace(/\\/g, "/"),
|
|
237261
237564
|
stem,
|
|
@@ -237274,6 +237577,8 @@ Props need lightmap UVs first: lecodes assets doctor assets/props/*.glb --lightm
|
|
|
237274
237577
|
patchSamples: range("patch-samples", flags["patch-samples"], 0, 1024),
|
|
237275
237578
|
split: !!flags.split,
|
|
237276
237579
|
denoise: flags.denoise,
|
|
237580
|
+
tracer: flags.tracer,
|
|
237581
|
+
denoiser: flags.denoiser,
|
|
237277
237582
|
filter: range("filter", flags.filter, 0, 4),
|
|
237278
237583
|
probeSpacing: range("probe-spacing", flags["probe-spacing"], 0, 100),
|
|
237279
237584
|
probeSize: range("probe-size", flags["probe-size"], 16, 128),
|
|
@@ -237281,6 +237586,9 @@ Props need lightmap UVs first: lecodes assets doctor assets/props/*.glb --lightm
|
|
|
237281
237586
|
probeIndoor: !!flags["probe-indoor"],
|
|
237282
237587
|
probeLayout: probeLayoutOf(flags["probe-layout"]),
|
|
237283
237588
|
minTexels: range("min-texels", flags["min-texels"], 0, 1e4),
|
|
237589
|
+
thinTexels: range("thin-texels", flags["thin-texels"], 0, 64),
|
|
237590
|
+
objectSlab: range("object-slab", flags["object-slab"], 0, 100),
|
|
237591
|
+
...cube.length ? { cube } : {},
|
|
237284
237592
|
volumeSpacing: range("volume", flags.volume, 0, 100),
|
|
237285
237593
|
detailTexels: range("detail-texels", flags["detail-texels"], 0, 1e7),
|
|
237286
237594
|
...flags["emissive-nits"] !== undefined ? { emissiveNits: range("emissive-nits", flags["emissive-nits"], 0, 1e7) } : {},
|
|
@@ -237300,13 +237608,15 @@ import ${JSON.stringify(importPath)}
|
|
|
237300
237608
|
ensurePlaceholders(outDir, stem);
|
|
237301
237609
|
if (debugDir)
|
|
237302
237610
|
mkdirSync25(debugDir, { recursive: true });
|
|
237303
|
-
const desktopRenderer =
|
|
237611
|
+
const desktopRenderer = "gl";
|
|
237304
237612
|
const { js, resources } = await buildBundle(bundleOptions(flags), {
|
|
237305
237613
|
localAssets: true,
|
|
237306
237614
|
desktopRenderer,
|
|
237307
237615
|
extraEntries: [{ path: entryPath, type: "text", text: entryText }],
|
|
237308
237616
|
entryOverride: entryPath
|
|
237309
237617
|
});
|
|
237618
|
+
if (!flags["no-unwrap"])
|
|
237619
|
+
await unwrapReferenced(js, resources, root, cfg.texel);
|
|
237310
237620
|
const scratch = mkdtempSync4(join46(tmpdir4(), "lecodes-lightmap-"));
|
|
237311
237621
|
const outPng = join46(scratch, "frame.png");
|
|
237312
237622
|
const bounce = cfg.bounceRays > 0 && cfg.bounces > 0 ? `, ${cfg.bounceRays} bounce paths of ${cfg.bounces}${cfg.patchSamples > 0 ? ` + ${cfg.patchSamples} patch samples` : ""}${cfg.denoise !== "none" ? `, denoised (${cfg.denoise})` : ", raw"}` : ", no bounce";
|
|
@@ -237317,6 +237627,7 @@ import ${JSON.stringify(importPath)}
|
|
|
237317
237627
|
try {
|
|
237318
237628
|
const timeoutMs = flags.timeout || 24 * 3600 * 1000;
|
|
237319
237629
|
const res = await runDesktopRender(js, resources, {
|
|
237630
|
+
host: "bake",
|
|
237320
237631
|
renderer: desktopRenderer,
|
|
237321
237632
|
width,
|
|
237322
237633
|
height,
|
|
@@ -237346,7 +237657,7 @@ import ${JSON.stringify(importPath)}
|
|
|
237346
237657
|
}
|
|
237347
237658
|
if (flags["dry-run"] && /\[lightmap\] done/.test(output)) {
|
|
237348
237659
|
for (const l of lines)
|
|
237349
|
-
if (/atlas \d+ x|
|
|
237660
|
+
if (/atlas \d+ x|packed per file|largest rect|do not fit|fill:|--cube|lightmap: 'cube'/.test(l))
|
|
237350
237661
|
logErr(l.replace(/^\[lightmap\] /, " "));
|
|
237351
237662
|
logErr(c.green("✓ layout only — nothing written"));
|
|
237352
237663
|
return;
|
|
@@ -237359,15 +237670,16 @@ import ${JSON.stringify(importPath)}
|
|
|
237359
237670
|
const splitDef = data.direct && data.indirect ? `, direct: ${assets(data.direct)}, indirect: ${assets(data.indirect)}` : "";
|
|
237360
237671
|
const probeFiles = data.probes ? ` + ${data.probes.file} (${data.probes.count} probe(s) of ${data.probes.size}² at ${data.probes.spacing} m)` : "";
|
|
237361
237672
|
const gridFile = data.volume ? ` + ${data.volume.file} (light grid ${data.volume.n.join(" x ")}, ${data.volume.valid} cell(s) in free space)` : "";
|
|
237362
|
-
const probeDef = (data.probes ? `, probes: asset('<rel>/${rel(data.probes.file)}')` : "") + (data.volume ? `, volume: asset('<rel>/${rel(data.volume.file)}')` : "");
|
|
237363
|
-
|
|
237673
|
+
const probeDef = (data.probes ? `, probes: asset('<rel>/${rel(data.probes.file)}')` : "") + (data.volume ? `, volume: asset('<rel>/${rel(data.volume.file)}')` : "") + (data.uv1 ? `, uv1: asset('<rel>/${rel(data.uv1)}')` : "");
|
|
237674
|
+
const uv1File = data.uv1 ? ` + ${data.uv1} (the packed lightmap uv1 — list it as env.lightmap.uv1, the rects map it)` : "";
|
|
237675
|
+
logErr(c.green(`✓ baked → ${relative7(root, outDir) || "."}/${stem}.bake + ${data.light.join(", ")} + ${data.aux.join(", ")}${splitFiles}${probeFiles}${gridFile}${uv1File}`));
|
|
237364
237676
|
logErr(c.dim(` scene file: env.lightmap: { data: asset('<rel>/${rel(`${stem}.bake`)}'), light: ${assets(data.light)}, aux: ${assets(data.aux)}${splitDef}${probeDef} } — asset() paths are relative to the scene file`));
|
|
237365
237677
|
if (debugDir)
|
|
237366
237678
|
logErr(c.dim(` debug layers: ${relative7(root, debugDir)} (lecodes lightmap inspect --bake ${relative7(root, join46(outDir, `${stem}.bake`)).replace(/\\/g, "/")} --debug-dir ${relative7(root, debugDir).replace(/\\/g, "/")})`));
|
|
237367
237679
|
return;
|
|
237368
237680
|
}
|
|
237369
|
-
if (
|
|
237370
|
-
throw new CliError("This
|
|
237681
|
+
if (/\[lightmap\] error: this host has no bake module/.test(output)) {
|
|
237682
|
+
throw new CliError("This host has no bake module — the bake runs in lecodes-bake. Point LECODES_BAKE_EXE at it (hosts/desktop: build.ps1 -Bake → build-bake/lecodes-bake.exe), or unset it to use the published one.");
|
|
237371
237683
|
}
|
|
237372
237684
|
if (lines.some((l) => /error/.test(l)))
|
|
237373
237685
|
throw new CliError("The bake failed — see the lines above.");
|
|
@@ -237519,7 +237831,7 @@ var diff = defineCommand({
|
|
|
237519
237831
|
}
|
|
237520
237832
|
});
|
|
237521
237833
|
var readPfm = (file) => {
|
|
237522
|
-
const buf =
|
|
237834
|
+
const buf = readFileSync35(file);
|
|
237523
237835
|
let o = 0;
|
|
237524
237836
|
const token = () => {
|
|
237525
237837
|
while (o < buf.length && /\s/.test(String.fromCharCode(buf[o])))
|
|
@@ -237785,7 +238097,7 @@ init_types4();
|
|
|
237785
238097
|
// src/commands/push.ts
|
|
237786
238098
|
init_cli();
|
|
237787
238099
|
init_api();
|
|
237788
|
-
import { readFileSync as
|
|
238100
|
+
import { readFileSync as readFileSync37 } from "node:fs";
|
|
237789
238101
|
import { basename as basename19 } from "node:path";
|
|
237790
238102
|
init_localFiles();
|
|
237791
238103
|
init_manifest();
|
|
@@ -237794,7 +238106,7 @@ init_manifest();
|
|
|
237794
238106
|
init_cli();
|
|
237795
238107
|
init_api();
|
|
237796
238108
|
init_localFiles();
|
|
237797
|
-
import { existsSync as existsSync42, readFileSync as
|
|
238109
|
+
import { existsSync as existsSync42, readFileSync as readFileSync36 } from "node:fs";
|
|
237798
238110
|
import { resolve as resolve23 } from "node:path";
|
|
237799
238111
|
init_shared();
|
|
237800
238112
|
var compareVersions = (a, b) => {
|
|
@@ -237881,11 +238193,11 @@ var upload2 = defineCommand({
|
|
|
237881
238193
|
const path = resolve23(file);
|
|
237882
238194
|
if (!existsSync42(path))
|
|
237883
238195
|
throw new CliError(`No such file: ${file}`);
|
|
237884
|
-
const code =
|
|
238196
|
+
const code = readFileSync36(path, "utf8");
|
|
237885
238197
|
const sdk = /^\/\/ sdk: (\S+)/m.exec(code)?.[1];
|
|
237886
238198
|
if (!sdk)
|
|
237887
238199
|
throw new CliError(`${file} is not a LeCodes bundle: no \`// sdk:\` header line. \`lecodes compile --remote-assets -o app.js\` writes one.`);
|
|
237888
|
-
const map = existsSync42(`${path}.map`) ?
|
|
238200
|
+
const map = existsSync42(`${path}.map`) ? readFileSync36(`${path}.map`, "utf8") : null;
|
|
237889
238201
|
const { root, manifest, apiUrl, token } = linkedProject();
|
|
237890
238202
|
if (hasChanges(diffLocal(manifest, scanProject(root, manifest))))
|
|
237891
238203
|
await runPush({ message: flags.message || "Release" });
|
|
@@ -237990,9 +238302,9 @@ var runPush = async (opts) => {
|
|
|
237990
238302
|
if (source)
|
|
237991
238303
|
entry.assetId = source.id;
|
|
237992
238304
|
if (isTextKind(kind))
|
|
237993
|
-
entry.text =
|
|
238305
|
+
entry.text = readFileSync37(file.absPath, "utf8");
|
|
237994
238306
|
else if (!source || source.sha !== file.sha) {
|
|
237995
|
-
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename19(file.path),
|
|
238307
|
+
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename19(file.path), readFileSync37(file.absPath));
|
|
237996
238308
|
entry.blobFileId = blob.id;
|
|
237997
238309
|
uploaded++;
|
|
237998
238310
|
}
|
|
@@ -238192,7 +238504,7 @@ var login_default = defineCommand({
|
|
|
238192
238504
|
});
|
|
238193
238505
|
|
|
238194
238506
|
// src/commands/navmesh.ts
|
|
238195
|
-
import { existsSync as existsSync44, mkdirSync as mkdirSync26, mkdtempSync as mkdtempSync5, readdirSync as readdirSync18, readFileSync as
|
|
238507
|
+
import { existsSync as existsSync44, mkdirSync as mkdirSync26, mkdtempSync as mkdtempSync5, readdirSync as readdirSync18, readFileSync as readFileSync38, rmSync as rmSync13, statSync as statSync15, unlinkSync as unlinkSync2, writeFileSync as writeFileSync28 } from "node:fs";
|
|
238196
238508
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
238197
238509
|
import { basename as basename21, join as join48, relative as relative8, resolve as resolve25 } from "node:path";
|
|
238198
238510
|
init_cli();
|
|
@@ -239003,7 +239315,7 @@ var guessName = (root) => {
|
|
|
239003
239315
|
if (!f.endsWith(".scene.ts"))
|
|
239004
239316
|
continue;
|
|
239005
239317
|
try {
|
|
239006
|
-
if (/\bnavmesh\s*:/.test(
|
|
239318
|
+
if (/\bnavmesh\s*:/.test(readFileSync38(join48(dir, f), "utf8")))
|
|
239007
239319
|
return f.slice(0, -".scene.ts".length);
|
|
239008
239320
|
} catch {}
|
|
239009
239321
|
}
|
|
@@ -239153,7 +239465,7 @@ import ${JSON.stringify(importPath)}
|
|
|
239153
239465
|
const geoPath = join48(outDir, `${name}.navgeo`);
|
|
239154
239466
|
if (!existsSync44(geoPath))
|
|
239155
239467
|
throw new CliError(`The host reported a dump but ${geoPath} is missing.`);
|
|
239156
|
-
const geo = readNgeo(new Uint8Array(
|
|
239468
|
+
const geo = readNgeo(new Uint8Array(readFileSync38(geoPath)));
|
|
239157
239469
|
const params = {
|
|
239158
239470
|
agentRadius: range2("radius", flags.radius, config.agent.radius, 0.05, 10),
|
|
239159
239471
|
agentHeight: range2("height", flags.height, config.agent.height, 0.1, 20),
|
|
@@ -239227,7 +239539,7 @@ init_cli();
|
|
|
239227
239539
|
|
|
239228
239540
|
// src/plugin/generate.ts
|
|
239229
239541
|
init_cli();
|
|
239230
|
-
import { existsSync as existsSync45, mkdirSync as mkdirSync27, readFileSync as
|
|
239542
|
+
import { existsSync as existsSync45, mkdirSync as mkdirSync27, readFileSync as readFileSync39, readdirSync as readdirSync19, statSync as statSync16, writeFileSync as writeFileSync29 } from "node:fs";
|
|
239231
239543
|
import { createRequire as createRequire7 } from "node:module";
|
|
239232
239544
|
import { basename as basename22, join as join49, relative as relative9, resolve as resolve26, sep as sep5 } from "node:path";
|
|
239233
239545
|
|
|
@@ -242101,7 +242413,7 @@ const wire = {
|
|
|
242101
242413
|
var shown = (from, to) => relative9(from, to).split(sep5).join("/");
|
|
242102
242414
|
var implementsOf = (dir) => {
|
|
242103
242415
|
try {
|
|
242104
|
-
const raw = JSON.parse(
|
|
242416
|
+
const raw = JSON.parse(readFileSync39(join49(dir, MANIFEST_FILE), "utf8").replace(/^/, ""));
|
|
242105
242417
|
return typeof raw.implements === "string" ? raw.implements : null;
|
|
242106
242418
|
} catch {
|
|
242107
242419
|
return null;
|
|
@@ -242132,7 +242444,7 @@ var generatePlugin = (ts, plugin) => {
|
|
|
242132
242444
|
const manifest = readManifestIfAny(plugin.dir, plugin.id);
|
|
242133
242445
|
const implemented = manifest?.implements;
|
|
242134
242446
|
const file = implemented ? firstPartyContractFile(implemented) : join49(plugin.dir, CONTRACT_FILE);
|
|
242135
|
-
const contract = readContract(ts, file, implemented ? firstPartyContractText(file) :
|
|
242447
|
+
const contract = readContract(ts, file, implemented ? firstPartyContractText(file) : readFileSync39(file, "utf8"));
|
|
242136
242448
|
checkPermissions(plugin, contract, manifest);
|
|
242137
242449
|
const id = implemented ?? plugin.id;
|
|
242138
242450
|
const handWritten = existsSync45(join49(plugin.dir, "sdk", `${id}.ts`));
|
|
@@ -242172,7 +242484,7 @@ var checkPermissions = (plugin, contract, manifest) => {
|
|
|
242172
242484
|
if (kinds.length === 0)
|
|
242173
242485
|
return;
|
|
242174
242486
|
const androidManifest = join49(plugin.dir, "android", "src", "main", "AndroidManifest.xml");
|
|
242175
|
-
const androidXml = manifest.android ? existsSync45(androidManifest) ?
|
|
242487
|
+
const androidXml = manifest.android ? existsSync45(androidManifest) ? readFileSync39(androidManifest, "utf8") : "" : null;
|
|
242176
242488
|
const missing = missingDeclarations(kinds, manifest.ios ? manifest.ios.infoPlist ?? {} : null, androidXml === null ? null : usesPermissions(androidXml), androidXml === null ? null : usesFeatures(androidXml));
|
|
242177
242489
|
const problems = [];
|
|
242178
242490
|
if (missing.infoPlist.length)
|
|
@@ -242196,7 +242508,7 @@ var hostImports = (web) => {
|
|
|
242196
242508
|
}
|
|
242197
242509
|
if (!/\.(ts|tsx|js|mjs)$/.test(entry))
|
|
242198
242510
|
continue;
|
|
242199
|
-
|
|
242511
|
+
readFileSync39(path, "utf8").split(`
|
|
242200
242512
|
`).forEach((line, i) => {
|
|
242201
242513
|
const m = /(?:from\s+|import\s*\(\s*|import\s+)["'](lecodes-[^"']*)["']/.exec(line);
|
|
242202
242514
|
if (m && !/^\s*\/\//.test(line))
|
|
@@ -242233,7 +242545,7 @@ var repoSdkPath = (root) => {
|
|
|
242233
242545
|
if (!existsSync45(file))
|
|
242234
242546
|
return DEFAULT_SDK_PATH;
|
|
242235
242547
|
try {
|
|
242236
|
-
const pkg = JSON.parse(
|
|
242548
|
+
const pkg = JSON.parse(readFileSync39(file, "utf8"));
|
|
242237
242549
|
const path = pkg.lecodes?.iosSdkPath;
|
|
242238
242550
|
if (path !== undefined && typeof path !== "string")
|
|
242239
242551
|
throw new PackageError(`package.json lecodes.iosSdkPath: a path, as a string`);
|
|
@@ -242253,7 +242565,7 @@ var generate = (path, opts = {}) => {
|
|
|
242253
242565
|
const report = { plugins: [], repo: { written: [], stale: [], fresh: [] }, failures: [] };
|
|
242254
242566
|
const place = (f, entry) => {
|
|
242255
242567
|
const name = shown(root, f.file) || basename22(f.file);
|
|
242256
|
-
const current = existsSync45(f.file) ?
|
|
242568
|
+
const current = existsSync45(f.file) ? readFileSync39(f.file, "utf8") : null;
|
|
242257
242569
|
if (current === f.text) {
|
|
242258
242570
|
entry.fresh.push(name);
|
|
242259
242571
|
return;
|
|
@@ -242690,7 +243002,7 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
242690
243002
|
};
|
|
242691
243003
|
|
|
242692
243004
|
// src/commands/render.ts
|
|
242693
|
-
import { mkdirSync as mkdirSync30, readFileSync as
|
|
243005
|
+
import { mkdirSync as mkdirSync30, readFileSync as readFileSync41, writeFileSync as writeFileSync32 } from "node:fs";
|
|
242694
243006
|
import { dirname as dirname25, resolve as resolve30 } from "node:path";
|
|
242695
243007
|
|
|
242696
243008
|
// src/compile/headlessBundle.ts
|
|
@@ -242778,7 +243090,7 @@ var compileHeadlessBundle = async (opts = {}) => {
|
|
|
242778
243090
|
init_designMeta();
|
|
242779
243091
|
init_cli();
|
|
242780
243092
|
init_paths();
|
|
242781
|
-
import { existsSync as existsSync47, readFileSync as
|
|
243093
|
+
import { existsSync as existsSync47, readFileSync as readFileSync40, statSync as statSync17 } from "node:fs";
|
|
242782
243094
|
import { isAbsolute as isAbsolute3, join as join51, relative as relative10, resolve as resolve27 } from "node:path";
|
|
242783
243095
|
var SCREEN_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs"];
|
|
242784
243096
|
var SCREEN_ENTRY_PATH = "/.screen-entry.ts";
|
|
@@ -242812,7 +243124,7 @@ var resolveScreenTarget = (root, file, opts = {}) => {
|
|
|
242812
243124
|
throw new CliError(`"${file}" is outside the project (${root}) — a screen renders as part of its project, so its ` + `imports and asset() files resolve from the same tree.`);
|
|
242813
243125
|
}
|
|
242814
243126
|
const key = toKey(rel);
|
|
242815
|
-
if (!/^\s*export\s+default\b/m.test(
|
|
243127
|
+
if (!/^\s*export\s+default\b/m.test(readFileSync40(abs, "utf8"))) {
|
|
242816
243128
|
throw new CliError(`${key.slice(1)} has no default export. A screen file ends with ` + `\`export default UIScreen([...])\` — or \`export default (state) => UIScreen([...])\` for screen states.`);
|
|
242817
243129
|
}
|
|
242818
243130
|
const invoke = opts.state !== undefined ? `screen(${JSON.stringify(opts.state)})` : "screen()";
|
|
@@ -243609,7 +243921,7 @@ var resolveFixtures = (value, baseDir) => {
|
|
|
243609
243921
|
if (typeof value === "string") {
|
|
243610
243922
|
const abs = resolve30(baseDir, value);
|
|
243611
243923
|
try {
|
|
243612
|
-
map = JSON.parse(
|
|
243924
|
+
map = JSON.parse(readFileSync41(abs, "utf8"));
|
|
243613
243925
|
} catch (e) {
|
|
243614
243926
|
throw new CliError(`Can't read fixtures "${value}": ${e instanceof Error ? e.message : String(e)}`);
|
|
243615
243927
|
}
|
|
@@ -243622,7 +243934,7 @@ var resolveFixtures = (value, baseDir) => {
|
|
|
243622
243934
|
var readScenarioFile = (path) => {
|
|
243623
243935
|
let raw;
|
|
243624
243936
|
try {
|
|
243625
|
-
raw = JSON.parse(
|
|
243937
|
+
raw = JSON.parse(readFileSync41(resolve30(path), "utf8"));
|
|
243626
243938
|
} catch (e) {
|
|
243627
243939
|
throw new CliError(`Can't read scenario "${path}": ${e instanceof Error ? e.message : String(e)}`);
|
|
243628
243940
|
}
|
|
@@ -243967,7 +244279,7 @@ var renderHeadless = async (flags, file) => {
|
|
|
243967
244279
|
|
|
243968
244280
|
// src/commands/scene.ts
|
|
243969
244281
|
init_cli();
|
|
243970
|
-
import { existsSync as existsSync50, readFileSync as
|
|
244282
|
+
import { existsSync as existsSync50, readFileSync as readFileSync43, readdirSync as readdirSync20, statSync as statSync18, writeFileSync as writeFileSync34 } from "node:fs";
|
|
243971
244283
|
import { basename as basename23, dirname as dirname26, join as join54, relative as relative11, resolve as resolve31 } from "node:path";
|
|
243972
244284
|
|
|
243973
244285
|
// src/compile/sceneCompile.ts
|
|
@@ -243976,7 +244288,7 @@ init_ignore();
|
|
|
243976
244288
|
init_cli();
|
|
243977
244289
|
init_projectCompile();
|
|
243978
244290
|
import { createHash as createHash9 } from "node:crypto";
|
|
243979
|
-
import { existsSync as existsSync49, mkdirSync as mkdirSync31, readFileSync as
|
|
244291
|
+
import { existsSync as existsSync49, mkdirSync as mkdirSync31, readFileSync as readFileSync42, writeFileSync as writeFileSync33 } from "node:fs";
|
|
243980
244292
|
import { join as join53 } from "node:path";
|
|
243981
244293
|
var TEXT_EXT = /\.(ts|js|json|svg|txt|md)$/i;
|
|
243982
244294
|
var rel = (key) => key.replace(/^\//, "");
|
|
@@ -243988,9 +244300,9 @@ var compileSceneEditor = async (opts) => {
|
|
|
243988
244300
|
for (const file of scan.files) {
|
|
243989
244301
|
const path = "./" + rel(file.path);
|
|
243990
244302
|
if (TEXT_EXT.test(path)) {
|
|
243991
|
-
entries.push({ path, type: "text", text:
|
|
244303
|
+
entries.push({ path, type: "text", text: readFileSync42(file.absPath, "utf8") });
|
|
243992
244304
|
} else if (/\.mat$/i.test(path)) {
|
|
243993
|
-
entries.push({ path, type: "shader", text:
|
|
244305
|
+
entries.push({ path, type: "shader", text: readFileSync42(file.absPath, "utf8"), absPath: file.absPath });
|
|
243994
244306
|
} else {
|
|
243995
244307
|
entries.push({ path, type: "resource", fileSrc: "/project/" + rel(file.path) });
|
|
243996
244308
|
}
|
|
@@ -244097,7 +244409,7 @@ var writeMcpConfig2 = (root, port) => {
|
|
|
244097
244409
|
let config = {};
|
|
244098
244410
|
if (existsSync50(path)) {
|
|
244099
244411
|
try {
|
|
244100
|
-
config = JSON.parse(
|
|
244412
|
+
config = JSON.parse(readFileSync43(path, "utf8"));
|
|
244101
244413
|
} catch {
|
|
244102
244414
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-scene server by hand.");
|
|
244103
244415
|
return;
|
|
@@ -244212,7 +244524,7 @@ var serve_default = defineCommand({
|
|
|
244212
244524
|
});
|
|
244213
244525
|
|
|
244214
244526
|
// src/commands/shaders/index.ts
|
|
244215
|
-
import { readFileSync as
|
|
244527
|
+
import { readFileSync as readFileSync45 } from "node:fs";
|
|
244216
244528
|
import { relative as relative13 } from "node:path";
|
|
244217
244529
|
init_cli();
|
|
244218
244530
|
init_shared();
|
|
@@ -244221,7 +244533,7 @@ init_shared();
|
|
|
244221
244533
|
init_compile();
|
|
244222
244534
|
init_distRoot();
|
|
244223
244535
|
init_cli();
|
|
244224
|
-
import { existsSync as existsSync51, mkdirSync as mkdirSync32, readdirSync as readdirSync21, readFileSync as
|
|
244536
|
+
import { existsSync as existsSync51, mkdirSync as mkdirSync32, readdirSync as readdirSync21, readFileSync as readFileSync44, writeFileSync as writeFileSync35 } from "node:fs";
|
|
244225
244537
|
import { basename as basename25, join as join55, relative as relative12, resolve as resolve32 } from "node:path";
|
|
244226
244538
|
var engineMaterialsDir = () => {
|
|
244227
244539
|
const candidates = [
|
|
@@ -244328,7 +244640,7 @@ var writeNewShader = (o) => {
|
|
|
244328
244640
|
const known = engineMaterialNames();
|
|
244329
244641
|
if (!known.includes(ref))
|
|
244330
244642
|
throw new CliError(`no engine material "${ref}". Known: ${known.join(", ")}`);
|
|
244331
|
-
mat = refMat(name, ref,
|
|
244643
|
+
mat = refMat(name, ref, readFileSync44(join55(srcDir, `${ref}.mat`), "utf8"));
|
|
244332
244644
|
} else
|
|
244333
244645
|
mat = starterMat(name);
|
|
244334
244646
|
mkdirSync32(dir, { recursive: true });
|
|
@@ -244360,7 +244672,7 @@ var status2 = async () => {
|
|
|
244360
244672
|
if (matcId === null)
|
|
244361
244673
|
state = c.dim("unknown (matc not downloaded)");
|
|
244362
244674
|
else {
|
|
244363
|
-
const text =
|
|
244675
|
+
const text = readFileSync45(shader.absPath, "utf8");
|
|
244364
244676
|
if (shaderIsCompiled(root, text, matcId))
|
|
244365
244677
|
state = "compiled";
|
|
244366
244678
|
else {
|
|
@@ -244369,7 +244681,7 @@ var status2 = async () => {
|
|
|
244369
244681
|
}
|
|
244370
244682
|
}
|
|
244371
244683
|
log(` ${shader.path.padEnd(width)} ${state}`);
|
|
244372
|
-
const schema = parseShaderSchema(
|
|
244684
|
+
const schema = parseShaderSchema(readFileSync45(shader.absPath, "utf8"));
|
|
244373
244685
|
for (const p of schema.params) {
|
|
244374
244686
|
const range = p.min !== undefined ? ` [${p.min}..${p.max}]` : "";
|
|
244375
244687
|
const def = p.default !== null && p.default !== undefined ? ` = ${JSON.stringify(p.default)}` : "";
|
|
@@ -244410,7 +244722,7 @@ var compileShaders = async (platforms) => {
|
|
|
244410
244722
|
}
|
|
244411
244723
|
const matcExe = await resolveMatcExe();
|
|
244412
244724
|
const matcId = matcIdentity(matcExe);
|
|
244413
|
-
const wasCompiled = new Map(shaders.map((s) => [s.path, shaderIsCompiled(root,
|
|
244725
|
+
const wasCompiled = new Map(shaders.map((s) => [s.path, shaderIsCompiled(root, readFileSync45(s.absPath, "utf8"), matcId)]));
|
|
244414
244726
|
const errors = [];
|
|
244415
244727
|
for (const shader of shaders) {
|
|
244416
244728
|
try {
|
|
@@ -244512,7 +244824,7 @@ var status_default = defineCommand({
|
|
|
244512
244824
|
});
|
|
244513
244825
|
|
|
244514
244826
|
// src/commands/test.ts
|
|
244515
|
-
import { existsSync as existsSync53, mkdirSync as mkdirSync34, readFileSync as
|
|
244827
|
+
import { existsSync as existsSync53, mkdirSync as mkdirSync34, readFileSync as readFileSync46, readdirSync as readdirSync22 } from "node:fs";
|
|
244516
244828
|
import { dirname as dirname28, resolve as resolve33 } from "node:path";
|
|
244517
244829
|
init_manifest();
|
|
244518
244830
|
|
|
@@ -245029,7 +245341,7 @@ var slug = (s) => s.replace(/[^a-zA-Z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").sli
|
|
|
245029
245341
|
var readFlowFile = (abs, displayName) => {
|
|
245030
245342
|
let raw;
|
|
245031
245343
|
try {
|
|
245032
|
-
raw = JSON.parse(
|
|
245344
|
+
raw = JSON.parse(readFileSync46(abs, "utf8"));
|
|
245033
245345
|
} catch (e) {
|
|
245034
245346
|
throw new CliError(`Can't read "${displayName}": ${e instanceof Error ? e.message : String(e)}`);
|
|
245035
245347
|
}
|
|
@@ -245155,7 +245467,7 @@ var runTests = async (args, flags) => {
|
|
|
245155
245467
|
if (!desktop && !lite && (wantFlows || flowsOnly) && existsSync53(designMetaPath)) {
|
|
245156
245468
|
let meta;
|
|
245157
245469
|
try {
|
|
245158
|
-
meta = JSON.parse(
|
|
245470
|
+
meta = JSON.parse(readFileSync46(designMetaPath, "utf8"));
|
|
245159
245471
|
} catch (e) {
|
|
245160
245472
|
throw new CliError(`Can't read ${designMetaPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
245161
245473
|
}
|
|
@@ -245228,7 +245540,7 @@ var runTests = async (args, flags) => {
|
|
|
245228
245540
|
script: plan.text,
|
|
245229
245541
|
fixedDtMs: nativeFixedDt
|
|
245230
245542
|
});
|
|
245231
|
-
const r = evaluateDesktopRun(plan, output, (path) => existsSync53(path) ?
|
|
245543
|
+
const r = evaluateDesktopRun(plan, output, (path) => existsSync53(path) ? readFileSync46(path, "utf8") : null);
|
|
245232
245544
|
if (!r.loaded)
|
|
245233
245545
|
throw new CliError("This desktop host doesn't support scripted input (CREATOR_SCRIPT). Update it: lecodes desktop update (or point LECODES_DESKTOP_EXE at a newer build).");
|
|
245234
245546
|
return {
|
|
@@ -245267,7 +245579,7 @@ var runTests = async (args, flags) => {
|
|
|
245267
245579
|
height: flags.height || kase.height || projectViewport?.height || 844,
|
|
245268
245580
|
onConsole
|
|
245269
245581
|
});
|
|
245270
|
-
const r = evaluateDesktopRun(plan, output, (path) => existsSync53(path) ?
|
|
245582
|
+
const r = evaluateDesktopRun(plan, output, (path) => existsSync53(path) ? readFileSync46(path, "utf8") : null);
|
|
245271
245583
|
return {
|
|
245272
245584
|
name: kase.name,
|
|
245273
245585
|
kind: kase.kind,
|
|
@@ -245793,7 +246105,7 @@ init_cli();
|
|
|
245793
246105
|
// src/web/build.ts
|
|
245794
246106
|
init_compile();
|
|
245795
246107
|
import { createHash as createHash10 } from "node:crypto";
|
|
245796
|
-
import { copyFileSync as copyFileSync2, cpSync as cpSync7, existsSync as existsSync56, mkdirSync as mkdirSync35, readFileSync as
|
|
246108
|
+
import { copyFileSync as copyFileSync2, cpSync as cpSync7, existsSync as existsSync56, mkdirSync as mkdirSync35, readFileSync as readFileSync47, readdirSync as readdirSync25, rmSync as rmSync18, statSync as statSync20, writeFileSync as writeFileSync37 } from "node:fs";
|
|
245797
246109
|
import { dirname as dirname30, extname as extname2, join as join59, resolve as resolve37 } from "node:path";
|
|
245798
246110
|
init_fonts();
|
|
245799
246111
|
init_assetIcons();
|
|
@@ -245837,7 +246149,7 @@ var buildWeb = async (options) => {
|
|
|
245837
246149
|
for (const entry of entries) {
|
|
245838
246150
|
if (entry.type !== "shader" || !entry.absPath)
|
|
245839
246151
|
continue;
|
|
245840
|
-
const text =
|
|
246152
|
+
const text = readFileSync47(entry.absPath, "utf8");
|
|
245841
246153
|
const hash = createHash10("sha256").update(text).digest("hex").slice(0, 16);
|
|
245842
246154
|
shaders.set(hash, JSON.stringify(buildLiteMaterialArtifact(text)));
|
|
245843
246155
|
entry.shaders = [{ platform: "opengl", src: `shaders/${hash}_opengl.filamat` }];
|
|
@@ -245884,7 +246196,7 @@ var buildWeb = async (options) => {
|
|
|
245884
246196
|
cpSync7(lite, out, { recursive: true });
|
|
245885
246197
|
const table = join59(out, "features.json");
|
|
245886
246198
|
if (existsSync56(table)) {
|
|
245887
|
-
const { features, fonts } = JSON.parse(
|
|
246199
|
+
const { features, fonts } = JSON.parse(readFileSync47(table, "utf8"));
|
|
245888
246200
|
if (config.font === "system")
|
|
245889
246201
|
for (const file of fonts ?? [])
|
|
245890
246202
|
rmSync18(join59(out, file), { force: true });
|
|
@@ -245898,7 +246210,7 @@ var buildWeb = async (options) => {
|
|
|
245898
246210
|
}
|
|
245899
246211
|
const pluginsTable = join59(out, "plugins.json");
|
|
245900
246212
|
if (existsSync56(pluginsTable) || halves.length > 0) {
|
|
245901
|
-
const { plugins } = existsSync56(pluginsTable) ? JSON.parse(
|
|
246213
|
+
const { plugins } = existsSync56(pluginsTable) ? JSON.parse(readFileSync47(pluginsTable, "utf8")) : { plugins: {} };
|
|
245902
246214
|
const kept = {};
|
|
245903
246215
|
for (const id of Object.keys(plugins)) {
|
|
245904
246216
|
if (listed.has(id))
|
|
@@ -245948,7 +246260,7 @@ var buildWeb = async (options) => {
|
|
|
245948
246260
|
writeFileSync37(join59(out, "shaders", `${hash}_webgl-lite.filamat`), artifact);
|
|
245949
246261
|
}
|
|
245950
246262
|
const app = readAppConfigAt(root);
|
|
245951
|
-
let html =
|
|
246263
|
+
let html = readFileSync47(join59(lite, "index.html"), "utf8");
|
|
245952
246264
|
const head = [];
|
|
245953
246265
|
head.push(`<meta name="theme-color" content="${escapeHtml(config.themeColor ?? "#000000")}" />`);
|
|
245954
246266
|
if (app?.icon) {
|
|
@@ -246043,7 +246355,7 @@ var startPreview = async (dir, port) => {
|
|
|
246043
246355
|
init_shared();
|
|
246044
246356
|
|
|
246045
246357
|
// src/dev/https.ts
|
|
246046
|
-
import { existsSync as existsSync58, mkdirSync as mkdirSync36, readFileSync as
|
|
246358
|
+
import { existsSync as existsSync58, mkdirSync as mkdirSync36, readFileSync as readFileSync48, writeFileSync as writeFileSync38 } from "node:fs";
|
|
246047
246359
|
import { homedir as homedir15 } from "node:os";
|
|
246048
246360
|
import { join as join61 } from "node:path";
|
|
246049
246361
|
var DIR = () => join61(homedir15(), ".lecodes", "dev-https");
|
|
@@ -246053,12 +246365,12 @@ var devCertificate = async (names, dir = DIR()) => {
|
|
|
246053
246365
|
const files = { cert: join61(dir, "cert.pem"), key: join61(dir, "key.pem"), meta: join61(dir, "names.json") };
|
|
246054
246366
|
let kept = null;
|
|
246055
246367
|
try {
|
|
246056
|
-
kept = JSON.parse(
|
|
246368
|
+
kept = JSON.parse(readFileSync48(files.meta, "utf8"));
|
|
246057
246369
|
} catch {}
|
|
246058
246370
|
const wanted = [...new Set(["localhost", "127.0.0.1", ...names])];
|
|
246059
246371
|
const fresh = kept && Date.now() - kept.made < RENEW_DAYS * 86400000 && wanted.every((n) => kept.names.includes(n));
|
|
246060
246372
|
if (fresh && existsSync58(files.cert) && existsSync58(files.key)) {
|
|
246061
|
-
return { cert:
|
|
246373
|
+
return { cert: readFileSync48(files.cert, "utf8"), key: readFileSync48(files.key, "utf8") };
|
|
246062
246374
|
}
|
|
246063
246375
|
const { generate } = await Promise.resolve().then(() => __toESM(require_selfsigned(), 1));
|
|
246064
246376
|
const all = [...new Set([...kept?.names ?? [], ...wanted])];
|