lecodes-cli 0.8.0 → 0.8.1
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 +121 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2007,7 +2007,7 @@ var require_main = __commonJS((exports, module) => {
|
|
|
2007
2007
|
// package.json
|
|
2008
2008
|
var package_default = {
|
|
2009
2009
|
name: "lecodes-cli",
|
|
2010
|
-
version: "0.8.
|
|
2010
|
+
version: "0.8.1",
|
|
2011
2011
|
description: "Clone, edit and push LeCodes projects from your machine.",
|
|
2012
2012
|
type: "module",
|
|
2013
2013
|
license: "MIT",
|
|
@@ -3484,8 +3484,9 @@ var runtimePackageTemplate = (sdkVersion, variant, plugins) => {
|
|
|
3484
3484
|
return `// swift-tools-version:5.9
|
|
3485
3485
|
//
|
|
3486
3486
|
// GENERATED by lecodes — regenerated by \`lecodes app sync\`; do not edit.
|
|
3487
|
-
// Owns the app's native dependency graph: the LeCodesSDK variant (
|
|
3488
|
-
//
|
|
3487
|
+
// Owns the app's native dependency graph: the LeCodesSDK variant (auto-picked from what
|
|
3488
|
+
// the compiled bundle uses, or forced via app.json ios.variant) and one vendored target
|
|
3489
|
+
// per installed plugin (see plugins.lock.json).
|
|
3489
3490
|
|
|
3490
3491
|
import PackageDescription
|
|
3491
3492
|
|
|
@@ -3570,6 +3571,14 @@ CLI-owned — regenerated on every sync, hand edits are overwritten:
|
|
|
3570
3571
|
- the version values inside \`App.xcodeproj/project.pbxproj\` (from app.json)
|
|
3571
3572
|
- this README
|
|
3572
3573
|
|
|
3574
|
+
## SDK variant
|
|
3575
|
+
|
|
3576
|
+
Sync links the smallest SDK product that covers what the compiled bundle actually uses:
|
|
3577
|
+
\`core\` (UI only), \`2d\` (+ 2D engine with physics), \`3d\` (+ 3D/AR engine with physics,
|
|
3578
|
+
~17 MB heavier), or \`full\` (everything). Adding your first 3D scene or 2D game to the
|
|
3579
|
+
project switches it automatically on the next sync; set \`ios.variant\` in \`../app.json\`
|
|
3580
|
+
to pin one instead.
|
|
3581
|
+
|
|
3573
3582
|
## Config
|
|
3574
3583
|
|
|
3575
3584
|
Identity lives in \`../app.json\` (name, version, plugins, ios.bundleId / teamId / variant /
|
|
@@ -3578,7 +3587,8 @@ buildNumber) — edit it there, then re-run \`lecodes app sync\`. Plugins are ma
|
|
|
3578
3587
|
`;
|
|
3579
3588
|
|
|
3580
3589
|
// src/commands/app.ts
|
|
3581
|
-
var SDK_PACKAGE_VERSION = "1.0
|
|
3590
|
+
var SDK_PACKAGE_VERSION = "1.1.0";
|
|
3591
|
+
var SDK_VARIANTS = ["core", "2d", "3d", "full"];
|
|
3582
3592
|
var FIRST_PARTY_PLUGINS = {
|
|
3583
3593
|
camera: "letary/lecodes-plugins",
|
|
3584
3594
|
"qr-scanner": "letary/lecodes-plugins"
|
|
@@ -3679,7 +3689,7 @@ var slugify = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+
|
|
|
3679
3689
|
var appInit = async (args) => {
|
|
3680
3690
|
const platform = args._[1];
|
|
3681
3691
|
if (platform !== "ios") {
|
|
3682
|
-
throw new CliError(platform ? `Unsupported platform "${platform}" — only "ios" for now.` : "Usage: lecodes app init ios [--name <display name>] [--bundle-id <id>] [--team <team id>] [--variant core|full]");
|
|
3692
|
+
throw new CliError(platform ? `Unsupported platform "${platform}" — only "ios" for now.` : "Usage: lecodes app init ios [--name <display name>] [--bundle-id <id>] [--team <team id>] [--variant auto|core|2d|3d|full]");
|
|
3683
3693
|
}
|
|
3684
3694
|
const root = projectRoot();
|
|
3685
3695
|
const iosDir = join11(root, "ios");
|
|
@@ -3687,9 +3697,10 @@ var appInit = async (args) => {
|
|
|
3687
3697
|
throw new CliError("ios/ already exists — run `lecodes app sync` to update it.");
|
|
3688
3698
|
}
|
|
3689
3699
|
const existing = readJson(join11(root, "app.json")) ?? {};
|
|
3690
|
-
const variant = flagStr(args, "variant") ?? existing.ios?.variant ?? "
|
|
3691
|
-
if (variant !== "
|
|
3692
|
-
throw new CliError(`--variant must be "
|
|
3700
|
+
const variant = flagStr(args, "variant") ?? existing.ios?.variant ?? "auto";
|
|
3701
|
+
if (variant !== "auto" && !SDK_VARIANTS.includes(variant)) {
|
|
3702
|
+
throw new CliError(`--variant must be auto, ${SDK_VARIANTS.join(", ")} (got "${variant}")`);
|
|
3703
|
+
}
|
|
3693
3704
|
const name = flagStr(args, "name") ?? existing.name ?? basename4(root);
|
|
3694
3705
|
const config = {
|
|
3695
3706
|
...existing,
|
|
@@ -3739,7 +3750,7 @@ ios/
|
|
|
3739
3750
|
write("App/Assets.xcassets/Contents.json", ASSETS_ROOT_JSON);
|
|
3740
3751
|
write("App/Assets.xcassets/AccentColor.colorset/Contents.json", ACCENT_COLOR_JSON);
|
|
3741
3752
|
write("App/Assets.xcassets/AppIcon.appiconset/Contents.json", APP_ICON_JSON);
|
|
3742
|
-
write("LeCodesRuntime/Package.swift", runtimePackageTemplate(SDK_PACKAGE_VERSION, variant, []));
|
|
3753
|
+
write("LeCodesRuntime/Package.swift", runtimePackageTemplate(SDK_PACKAGE_VERSION, variant === "auto" ? "core" : variant, []));
|
|
3743
3754
|
write("LeCodesRuntime/Sources/LeCodesRuntime/LeCodesRuntime.swift", runtimeSwiftTemplate([]));
|
|
3744
3755
|
log("");
|
|
3745
3756
|
success(`iOS shell for ${c.bold(name)} ready.`);
|
|
@@ -3747,6 +3758,57 @@ ios/
|
|
|
3747
3758
|
log(` ${c.bold("open ios/App.xcodeproj")} then run on your device from Xcode`);
|
|
3748
3759
|
log(` App icon: drop a 1024×1024 PNG into ios/App/Assets.xcassets/AppIcon.appiconset/`);
|
|
3749
3760
|
};
|
|
3761
|
+
var detectEngines = (bundleJs) => {
|
|
3762
|
+
const lines = [];
|
|
3763
|
+
for (const line of bundleJs.split(`
|
|
3764
|
+
`)) {
|
|
3765
|
+
if (!line.startsWith("//"))
|
|
3766
|
+
break;
|
|
3767
|
+
lines.push(line);
|
|
3768
|
+
}
|
|
3769
|
+
const header = lines.join(`
|
|
3770
|
+
`);
|
|
3771
|
+
return { gl: !/^\/\/ gl: disable$/m.test(header), g2: /^\/\/ 2d: enable$/m.test(header) };
|
|
3772
|
+
};
|
|
3773
|
+
var resolveVariant = (requested, bundleJs) => {
|
|
3774
|
+
const uses = detectEngines(bundleJs);
|
|
3775
|
+
const detected = uses.gl && uses.g2 ? "full" : uses.gl ? "3d" : uses.g2 ? "2d" : "core";
|
|
3776
|
+
if (requested === "auto")
|
|
3777
|
+
return { variant: detected, detected };
|
|
3778
|
+
const variant = requested;
|
|
3779
|
+
const missing = [
|
|
3780
|
+
uses.gl && variant !== "3d" && variant !== "full" ? "3D" : null,
|
|
3781
|
+
uses.g2 && variant !== "2d" && variant !== "full" ? "2D" : null
|
|
3782
|
+
].filter(Boolean);
|
|
3783
|
+
if (missing.length > 0) {
|
|
3784
|
+
warn(`project uses the ${missing.join(" + ")} engine but ios.variant forces "${variant}" — those scenes won't run on device; remove ios.variant to auto-select "${detected}".`);
|
|
3785
|
+
}
|
|
3786
|
+
return { variant, detected };
|
|
3787
|
+
};
|
|
3788
|
+
var ensureSdkResourcesBundle = async () => {
|
|
3789
|
+
const override = process.env.LECODES_SDK_RESOURCES;
|
|
3790
|
+
if (override) {
|
|
3791
|
+
note(`Using local resources bundle ${override} (LECODES_SDK_RESOURCES)`);
|
|
3792
|
+
return resolve2(override);
|
|
3793
|
+
}
|
|
3794
|
+
const dir = join11(homedir2(), ".lecodes", "sdk-resources", SDK_PACKAGE_VERSION);
|
|
3795
|
+
const bundle = join11(dir, "LeCodesSDKResources.bundle");
|
|
3796
|
+
if (existsSync8(bundle))
|
|
3797
|
+
return bundle;
|
|
3798
|
+
const url = `https://github.com/letary/lecodes-ios-sdk/releases/download/${SDK_PACKAGE_VERSION}/LeCodesSDKResources.bundle.zip`;
|
|
3799
|
+
note(`Downloading LeCodesSDKResources.bundle (SDK ${SDK_PACKAGE_VERSION})…`);
|
|
3800
|
+
const res = await fetch(url);
|
|
3801
|
+
if (!res.ok)
|
|
3802
|
+
throw new CliError(`Resources bundle download failed (HTTP ${res.status}) — ${url}`);
|
|
3803
|
+
mkdirSync5(dir, { recursive: true });
|
|
3804
|
+
const zip = join11(dir, "LeCodesSDKResources.bundle.zip");
|
|
3805
|
+
writeFileSync7(zip, new Uint8Array(await res.arrayBuffer()));
|
|
3806
|
+
execFileSync("ditto", ["-x", "-k", zip, dir]);
|
|
3807
|
+
rmSync(zip);
|
|
3808
|
+
if (!existsSync8(bundle))
|
|
3809
|
+
throw new CliError(`No LeCodesSDKResources.bundle inside the release asset ${url}`);
|
|
3810
|
+
return bundle;
|
|
3811
|
+
};
|
|
3750
3812
|
var appSync = async (_args) => {
|
|
3751
3813
|
const root = projectRoot();
|
|
3752
3814
|
const config = readJson(join11(root, "app.json"));
|
|
@@ -3754,7 +3816,10 @@ var appSync = async (_args) => {
|
|
|
3754
3816
|
if (!config?.ios || !existsSync8(iosDir)) {
|
|
3755
3817
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
3756
3818
|
}
|
|
3757
|
-
const
|
|
3819
|
+
const requestedVariant = config.ios.variant ?? "auto";
|
|
3820
|
+
if (requestedVariant !== "auto" && !SDK_VARIANTS.includes(requestedVariant)) {
|
|
3821
|
+
throw new CliError(`app.json ios.variant must be "auto" or one of ${SDK_VARIANTS.join(", ")} (got "${requestedVariant}").`);
|
|
3822
|
+
}
|
|
3758
3823
|
const lockPath = join11(root, "plugins.lock.json");
|
|
3759
3824
|
const oldLock = readJson(lockPath)?.plugins ?? {};
|
|
3760
3825
|
const pluginSpecs = Object.entries(config.plugins ?? {});
|
|
@@ -3789,18 +3854,6 @@ var appSync = async (_args) => {
|
|
|
3789
3854
|
log(`${c.yellow("removed")} ios/LeCodesRuntime/Sources/${entry} (plugin uninstalled)`);
|
|
3790
3855
|
}
|
|
3791
3856
|
}
|
|
3792
|
-
writeFileSync7(join11(iosDir, "README.md"), IOS_README);
|
|
3793
|
-
writeFileSync7(join11(iosDir, "LeCodesRuntime", "Package.swift"), runtimePackageTemplate(SDK_PACKAGE_VERSION, variant, runtimePlugins));
|
|
3794
|
-
writeFileSync7(join11(sourcesDir, "LeCodesRuntime", "LeCodesRuntime.swift"), runtimeSwiftTemplate(runtimePlugins));
|
|
3795
|
-
const mergedKeys = {};
|
|
3796
|
-
for (const [key, values] of Object.entries(infoValues))
|
|
3797
|
-
mergedKeys[key] = [...new Set(values)].join(" ");
|
|
3798
|
-
writeFileSync7(join11(iosDir, "App", "Info.plist"), infoPlistTemplate(mergedKeys));
|
|
3799
|
-
if (pluginSpecs.length > 0 || existsSync8(lockPath))
|
|
3800
|
-
writeJson(lockPath, { plugins: newLock });
|
|
3801
|
-
const pbxPath = join11(iosDir, "App.xcodeproj", "project.pbxproj");
|
|
3802
|
-
const pbx = readFileSync10(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};`);
|
|
3803
|
-
writeFileSync7(pbxPath, pbx);
|
|
3804
3857
|
const remotePatterns = config.resources?.remote ?? [];
|
|
3805
3858
|
if (!Array.isArray(remotePatterns) || remotePatterns.some((p) => typeof p !== "string")) {
|
|
3806
3859
|
throw new CliError('app.json "resources.remote" must be an array of glob strings (e.g. ["videos/**"]).');
|
|
@@ -3810,6 +3863,23 @@ var appSync = async (_args) => {
|
|
|
3810
3863
|
mkdirSync5(resourcesDir, { recursive: true });
|
|
3811
3864
|
const resources = await compileShell({ _: [], flags: { o: join11(resourcesDir, "app.js") } }, { remotePatterns });
|
|
3812
3865
|
const bundleJs = readFileSync10(join11(resourcesDir, "app.js"), "utf8");
|
|
3866
|
+
const { variant, detected } = resolveVariant(requestedVariant, bundleJs);
|
|
3867
|
+
const WHY = {
|
|
3868
|
+
core: "UI only",
|
|
3869
|
+
"2d": "uses the 2D engine",
|
|
3870
|
+
"3d": "uses the 3D engine",
|
|
3871
|
+
full: "uses the 3D + 2D engines"
|
|
3872
|
+
};
|
|
3873
|
+
log(`${c.green("variant")} ${variant}${requestedVariant === "auto" ? ` (auto: ${WHY[detected]})` : " (forced by ios.variant)"}`);
|
|
3874
|
+
const sdkProvided = new Set;
|
|
3875
|
+
if (variant === "3d" || variant === "full") {
|
|
3876
|
+
const sdkResources = await ensureSdkResourcesBundle();
|
|
3877
|
+
cpSync(sdkResources, join11(resourcesDir, "LeCodesSDKResources.bundle"), { recursive: true });
|
|
3878
|
+
for (const f2 of readdirSync4(sdkResources))
|
|
3879
|
+
if (f2 !== "Info.plist")
|
|
3880
|
+
sdkProvided.add(f2);
|
|
3881
|
+
log(`${c.green("resources")} LeCodesSDKResources.bundle (filament) → ios/App/Resources/`);
|
|
3882
|
+
}
|
|
3813
3883
|
const preload = /^\/\/ preload: (.*)$/m.exec(bundleJs)?.[1].split(";").map((s) => s.trim()).filter(Boolean) ?? [];
|
|
3814
3884
|
const IMPLICIT_PRELOADS = new Set(["uberarchive", "capture"]);
|
|
3815
3885
|
const byName = new Map;
|
|
@@ -3826,7 +3896,8 @@ var appSync = async (_args) => {
|
|
|
3826
3896
|
continue;
|
|
3827
3897
|
const candidates = byName.get(name) ?? [];
|
|
3828
3898
|
if (candidates.length === 0) {
|
|
3829
|
-
|
|
3899
|
+
if (!sdkProvided.has(name))
|
|
3900
|
+
warn(`bundle needs local asset "${name}" but no project file provides it — it won't load on device`);
|
|
3830
3901
|
continue;
|
|
3831
3902
|
}
|
|
3832
3903
|
if (candidates.length > 1)
|
|
@@ -3839,6 +3910,18 @@ var appSync = async (_args) => {
|
|
|
3839
3910
|
const streamed = resources.filter((r) => r.remote);
|
|
3840
3911
|
if (streamed.length > 0)
|
|
3841
3912
|
log(`${c.green("remote")} ${streamed.length} resource(s) stay on the server (resources.remote): ${streamed.map((r) => r.path).join(", ")}`);
|
|
3913
|
+
writeFileSync7(join11(iosDir, "README.md"), IOS_README);
|
|
3914
|
+
writeFileSync7(join11(iosDir, "LeCodesRuntime", "Package.swift"), runtimePackageTemplate(SDK_PACKAGE_VERSION, variant, runtimePlugins));
|
|
3915
|
+
writeFileSync7(join11(sourcesDir, "LeCodesRuntime", "LeCodesRuntime.swift"), runtimeSwiftTemplate(runtimePlugins));
|
|
3916
|
+
const mergedKeys = {};
|
|
3917
|
+
for (const [key, values] of Object.entries(infoValues))
|
|
3918
|
+
mergedKeys[key] = [...new Set(values)].join(" ");
|
|
3919
|
+
writeFileSync7(join11(iosDir, "App", "Info.plist"), infoPlistTemplate(mergedKeys));
|
|
3920
|
+
if (pluginSpecs.length > 0 || existsSync8(lockPath))
|
|
3921
|
+
writeJson(lockPath, { plugins: newLock });
|
|
3922
|
+
const pbxPath = join11(iosDir, "App.xcodeproj", "project.pbxproj");
|
|
3923
|
+
const pbx = readFileSync10(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};`);
|
|
3924
|
+
writeFileSync7(pbxPath, pbx);
|
|
3842
3925
|
log("");
|
|
3843
3926
|
success(`Shell synced: app.js + ${runtimePlugins.length} plugin(s), variant ${variant}, v${config.version ?? "1.0.0"} (${config.ios.buildNumber ?? 1}).`);
|
|
3844
3927
|
note("Build & run from Xcode (open ios/App.xcodeproj) — user files in ios/App/*.swift were not touched.");
|
|
@@ -3949,6 +4032,16 @@ var appDoctor = async (_args) => {
|
|
|
3949
4032
|
check(hasIcon, "App icon present", "drop a 1024×1024 PNG into ios/App/Assets.xcassets/AppIcon.appiconset/ (App Store requires it)", false);
|
|
3950
4033
|
const bundle = join11(root, "ios", "App", "Resources", "app.js");
|
|
3951
4034
|
check(existsSync8(bundle), "Bundled app.js", "run `lecodes app sync`");
|
|
4035
|
+
const requestedVariant = config.ios.variant ?? "auto";
|
|
4036
|
+
const validVariant = requestedVariant === "auto" || SDK_VARIANTS.includes(requestedVariant);
|
|
4037
|
+
check(validVariant, `ios.variant "${requestedVariant}"`, `use "auto" or one of ${SDK_VARIANTS.join(", ")}`);
|
|
4038
|
+
if (existsSync8(bundle) && validVariant) {
|
|
4039
|
+
const { variant } = resolveVariant(requestedVariant, readFileSync10(bundle, "utf8"));
|
|
4040
|
+
const pkg = readFileSync10(join11(root, "ios", "LeCodesRuntime", "Package.swift"), "utf8");
|
|
4041
|
+
const needsResources = variant === "3d" || variant === "full";
|
|
4042
|
+
const hasResources = existsSync8(join11(root, "ios", "App", "Resources", "LeCodesSDKResources.bundle"));
|
|
4043
|
+
check(pkg.includes(`"LeCodesSDK-${variant}"`) && (!needsResources || hasResources), `SDK variant ${variant}${needsResources ? " (+ filament resources)" : ""}`, "run `lecodes app sync`");
|
|
4044
|
+
}
|
|
3952
4045
|
const lock = readJson(join11(root, "plugins.lock.json"))?.plugins ?? {};
|
|
3953
4046
|
const wanted = Object.keys(config.plugins ?? {});
|
|
3954
4047
|
const inSync = wanted.every((id) => lock[id]) && Object.keys(lock).every((id) => wanted.includes(id));
|
|
@@ -7211,11 +7304,14 @@ ${c.bold("Commands:")}
|
|
|
7211
7304
|
--name <s> display name (default: app.json / folder name)
|
|
7212
7305
|
--bundle-id <id> iOS bundle identifier
|
|
7213
7306
|
--team <id> Apple development team id
|
|
7214
|
-
--variant <v> SDK variant:
|
|
7307
|
+
--variant <v> SDK variant: auto (default — sync picks the
|
|
7308
|
+
smallest of core/2d/3d/full covering what
|
|
7309
|
+
the bundle uses), or one of those to pin
|
|
7215
7310
|
app sync Make the shell match the project: compile the bundle + embed
|
|
7216
7311
|
its assets into ios/App/Resources/ (offline by default, also
|
|
7217
7312
|
for server projects; app.json "resources": {"remote": [globs]}
|
|
7218
|
-
keeps heavy assets streaming from the server),
|
|
7313
|
+
keeps heavy assets streaming from the server), pick the SDK
|
|
7314
|
+
variant from the bundle's engine use, vendor app.json
|
|
7219
7315
|
plugins (pinned in plugins.lock.json), regenerate the
|
|
7220
7316
|
CLI-owned shell files
|
|
7221
7317
|
app plugin <list|add|remove> Manage native plugins in app.json (then run 'lecodes app sync')
|