lecodes-cli 0.18.2 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1866 -301
- package/package.json +14 -12
- package/runtime/scene-harness.json +1 -1
- package/runtime/sdk-types.json +1 -1
- package/src/api.ts +302 -0
- package/src/browserAuth.ts +87 -0
- package/src/cmgenTool.ts +104 -0
- package/src/commands/app.ts +891 -0
- package/src/commands/appAndroid.ts +603 -0
- package/src/commands/appDesktop.ts +326 -0
- package/src/commands/appDesktopMac.ts +470 -0
- package/src/commands/appIcon.ts +187 -0
- package/src/commands/appShared.ts +448 -0
- package/src/commands/appTemplates.ts +883 -0
- package/src/commands/appTemplatesAndroid.ts +599 -0
- package/src/commands/appTemplatesGradlew.ts +9 -0
- package/src/commands/assets.ts +28 -0
- package/src/commands/clone.ts +60 -0
- package/src/commands/compile.ts +253 -0
- package/src/commands/create.ts +59 -0
- package/src/commands/design.ts +863 -0
- package/src/commands/designTemplates.ts +11 -0
- package/src/commands/desktop.ts +122 -0
- package/src/commands/dev.ts +214 -0
- package/src/commands/diff.ts +59 -0
- package/src/commands/init.ts +191 -0
- package/src/commands/install.ts +198 -0
- package/src/commands/lightmap.ts +290 -0
- package/src/commands/link.ts +147 -0
- package/src/commands/login.ts +24 -0
- package/src/commands/navmesh.ts +225 -0
- package/src/commands/pn.ts +305 -0
- package/src/commands/projectTemplates.ts +129 -0
- package/src/commands/pull.ts +109 -0
- package/src/commands/push.ts +173 -0
- package/src/commands/render.ts +414 -0
- package/src/commands/scene.ts +148 -0
- package/src/commands/shaders.ts +157 -0
- package/src/commands/status.ts +29 -0
- package/src/commands/test.ts +370 -0
- package/src/commands/thumbs.ts +178 -0
- package/src/commands/types.ts +87 -0
- package/src/commands/update.ts +190 -0
- package/src/compile/assetIcons.ts +214 -0
- package/src/compile/collect.ts +46 -0
- package/src/compile/collectLocal.ts +37 -0
- package/src/compile/designCompile.ts +104 -0
- package/src/compile/fonts.ts +156 -0
- package/src/compile/headlessBundle.ts +129 -0
- package/src/compile/nativeStack.ts +21 -0
- package/src/compile/projectCompile.ts +109 -0
- package/src/compile/sceneCompile.ts +113 -0
- package/src/compile/screenEntry.ts +127 -0
- package/src/compile/shaders.ts +245 -0
- package/src/config.ts +42 -0
- package/src/designMeta.ts +35 -0
- package/src/desktopRenderer.ts +532 -0
- package/src/desktopScript.ts +276 -0
- package/src/dev/clientTemplates.ts +160 -0
- package/src/dev/devServer.ts +292 -0
- package/src/dev/wsServer.ts +144 -0
- package/src/distRoot.ts +20 -0
- package/src/ignore.ts +163 -0
- package/src/index.ts +491 -0
- package/src/lecodes-3d-editor.d.ts +41 -0
- package/src/lecodes-assets.d.ts +7 -0
- package/src/lecodes-design.d.ts +191 -0
- package/src/lecodes-renderer.d.ts +131 -0
- package/src/localFiles.ts +144 -0
- package/src/manifest.ts +46 -0
- package/src/matcTool.ts +137 -0
- package/src/peers.ts +40 -0
- package/src/project.ts +61 -0
- package/src/projectEnv.ts +94 -0
- package/src/qrcode-terminal.d.ts +9 -0
- package/src/releases.ts +125 -0
- package/src/serverDiff.ts +78 -0
- package/src/textDiff.ts +103 -0
- package/src/types.ts +0 -0
- package/src/util.ts +146 -0
- package/runtime/sdk/animate/animate.ts +0 -238
- package/runtime/sdk/animate/bezier.ts +0 -138
- package/runtime/sdk/animate/easings.ts +0 -126
- package/runtime/sdk/canvas/Canvas.ts +0 -305
- package/runtime/sdk/core/Aspect.ts +0 -512
- package/runtime/sdk/core/InspectorUI.ts +0 -212
- package/runtime/sdk/core/color.ts +0 -66
- package/runtime/sdk/core/compWrite.ts +0 -42
- package/runtime/sdk/core/events.ts +0 -38
- package/runtime/sdk/core/fields.ts +0 -120
- package/runtime/sdk/core/registry.ts +0 -23
- package/runtime/sdk/core/signals.ts +0 -277
- package/runtime/sdk/core/time.ts +0 -81
- package/runtime/sdk/g2/Camera2D.ts +0 -40
- package/runtime/sdk/g2/CharacterController2D.ts +0 -276
- package/runtime/sdk/g2/Node2D.ts +0 -267
- package/runtime/sdk/g2/OneWay2D.ts +0 -66
- package/runtime/sdk/g2/Physics2D.ts +0 -346
- package/runtime/sdk/g2/Scene2D.ts +0 -209
- package/runtime/sdk/g2/Shape2D.ts +0 -259
- package/runtime/sdk/g2/Sprite.ts +0 -89
- package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
- package/runtime/sdk/g2/SpriteSheet.ts +0 -166
- package/runtime/sdk/g2/Texture2D.ts +0 -47
- package/runtime/sdk/g2/Tilemap.ts +0 -41
- package/runtime/sdk/g2/Tileset.ts +0 -71
- package/runtime/sdk/g2/Trigger2D.ts +0 -77
- package/runtime/sdk/g2/autotile.ts +0 -433
- package/runtime/sdk/g2/cells.ts +0 -91
- package/runtime/sdk/g2/defineScene2d.ts +0 -381
- package/runtime/sdk/g2/groups2d.ts +0 -106
- package/runtime/sdk/g2/loop.ts +0 -50
- package/runtime/sdk/g2/scenarios2d.ts +0 -69
- package/runtime/sdk/g2/touch.ts +0 -83
- package/runtime/sdk/gl/Camera.ts +0 -160
- package/runtime/sdk/gl/CameraPlace.ts +0 -52
- package/runtime/sdk/gl/CharacterController.ts +0 -238
- package/runtime/sdk/gl/Gearbox.ts +0 -212
- package/runtime/sdk/gl/Geometry.ts +0 -279
- package/runtime/sdk/gl/IK.ts +0 -193
- package/runtime/sdk/gl/InstancedMesh.ts +0 -132
- package/runtime/sdk/gl/Light.ts +0 -99
- package/runtime/sdk/gl/Lightmap.ts +0 -179
- package/runtime/sdk/gl/Material.ts +0 -245
- package/runtime/sdk/gl/Mesh.ts +0 -83
- package/runtime/sdk/gl/Model.ts +0 -64
- package/runtime/sdk/gl/Node.ts +0 -350
- package/runtime/sdk/gl/Noise.ts +0 -30
- package/runtime/sdk/gl/Particles.ts +0 -676
- package/runtime/sdk/gl/Physics.ts +0 -222
- package/runtime/sdk/gl/Plane.ts +0 -53
- package/runtime/sdk/gl/Ray.ts +0 -16
- package/runtime/sdk/gl/Scene.ts +0 -479
- package/runtime/sdk/gl/Shape.ts +0 -377
- package/runtime/sdk/gl/Texture.ts +0 -46
- package/runtime/sdk/gl/Trigger.ts +0 -45
- package/runtime/sdk/gl/Vehicle.ts +0 -473
- package/runtime/sdk/gl/Wheel.ts +0 -240
- package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
- package/runtime/sdk/gl/animation/Animator.ts +0 -87
- package/runtime/sdk/gl/animation/Layer.ts +0 -29
- package/runtime/sdk/gl/animation/Loop.ts +0 -25
- package/runtime/sdk/gl/animation/Playback.ts +0 -43
- package/runtime/sdk/gl/animation/core.ts +0 -294
- package/runtime/sdk/gl/controls.ts +0 -95
- package/runtime/sdk/gl/physicsEvents.ts +0 -20
- package/runtime/sdk/gl/scenarios.ts +0 -291
- package/runtime/sdk/gl/state.ts +0 -6
- package/runtime/sdk/gl/touch.ts +0 -68
- package/runtime/sdk/inject.ts +0 -186
- package/runtime/sdk/math/Mathf.ts +0 -118
- package/runtime/sdk/math/mat4.ts +0 -278
- package/runtime/sdk/math/quat.ts +0 -232
- package/runtime/sdk/math/vec.ts +0 -255
- package/runtime/sdk/plugins/camera.ts +0 -81
- package/runtime/sdk/plugins/geolocation.ts +0 -123
- package/runtime/sdk/plugins/oauth.ts +0 -61
- package/runtime/sdk/plugins/permission.ts +0 -7
- package/runtime/sdk/plugins/push.ts +0 -132
- package/runtime/sdk/plugins/qr.ts +0 -73
- package/runtime/sdk/plugins/service.ts +0 -47
- package/runtime/sdk/runtime/app.ts +0 -101
- package/runtime/sdk/runtime/appEvents.ts +0 -54
- package/runtime/sdk/runtime/channel.ts +0 -61
- package/runtime/sdk/runtime/clipboard.ts +0 -20
- package/runtime/sdk/runtime/datetime.ts +0 -329
- package/runtime/sdk/runtime/device.ts +0 -293
- package/runtime/sdk/runtime/fetch.ts +0 -77
- package/runtime/sdk/runtime/files.ts +0 -23
- package/runtime/sdk/runtime/input.ts +0 -175
- package/runtime/sdk/runtime/media.ts +0 -111
- package/runtime/sdk/runtime/misc.ts +0 -16
- package/runtime/sdk/runtime/net.ts +0 -36
- package/runtime/sdk/runtime/rpc.ts +0 -218
- package/runtime/sdk/runtime/service.ts +0 -83
- package/runtime/sdk/runtime/share.ts +0 -9
- package/runtime/sdk/runtime/storage.ts +0 -13
- package/runtime/sdk/runtime/touch.ts +0 -76
- package/runtime/sdk/scene/defineScene.ts +0 -1227
- package/runtime/sdk/scene/editorPlugins.ts +0 -92
- package/runtime/sdk/scene/gizmos.ts +0 -148
- package/runtime/sdk/scene/grammar.ts +0 -120
- package/runtime/sdk/scene/material.ts +0 -188
- package/runtime/sdk/server/auth/appConfig.ts +0 -12
- package/runtime/sdk/server/auth/global.ts +0 -80
- package/runtime/sdk/server/auth/host.ts +0 -318
- package/runtime/sdk/server/auth/models.ts +0 -83
- package/runtime/sdk/server/auth/types.ts +0 -50
- package/runtime/sdk/server/channel.ts +0 -56
- package/runtime/sdk/server/context.ts +0 -36
- package/runtime/sdk/server/db/defineDb.ts +0 -237
- package/runtime/sdk/server/db/fields.ts +0 -132
- package/runtime/sdk/server/db/httpTransport.ts +0 -93
- package/runtime/sdk/server/db/index.ts +0 -7
- package/runtime/sdk/server/db/marci/query.ts +0 -412
- package/runtime/sdk/server/db/types.ts +0 -202
- package/runtime/sdk/server/errors.ts +0 -12
- package/runtime/sdk/server/host.ts +0 -74
- package/runtime/sdk/server/inject.ts +0 -13
- package/runtime/sdk/server/runtime.ts +0 -133
- package/runtime/sdk/server/validate.ts +0 -87
- package/runtime/sdk/ui/NativeView.ts +0 -142
- package/runtime/sdk/ui/UI.ts +0 -39
- package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
- package/runtime/sdk/ui/UIButton.ts +0 -101
- package/runtime/sdk/ui/UIContainer.ts +0 -60
- package/runtime/sdk/ui/UIImage.ts +0 -83
- package/runtime/sdk/ui/UIInput.ts +0 -185
- package/runtime/sdk/ui/UIModal.ts +0 -139
- package/runtime/sdk/ui/UINode.ts +0 -826
- package/runtime/sdk/ui/UIPager.ts +0 -362
- package/runtime/sdk/ui/UIPopover.ts +0 -100
- package/runtime/sdk/ui/UIScreen.ts +0 -123
- package/runtime/sdk/ui/UIScrollable.ts +0 -87
- package/runtime/sdk/ui/UISpacer.ts +0 -14
- package/runtime/sdk/ui/UITabs.ts +0 -236
- package/runtime/sdk/ui/UIText.ts +0 -51
- package/runtime/sdk/ui/UIVideo.ts +0 -88
- package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
- package/runtime/sdk/ui/UIWidget.ts +0 -127
- package/runtime/sdk/ui/fonts.ts +0 -13
- package/runtime/sdk/ui/presentable.ts +0 -117
- package/runtime/sdk/ui/router.ts +0 -132
- package/runtime/sdk/ui/theme.ts +0 -84
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync } from "node:fs"
|
|
2
|
+
import { resolve } from "node:path"
|
|
3
|
+
import { getCommits, getProject } from "../api"
|
|
4
|
+
import { loadConfig, requireApiUrl, requireToken } from "../config"
|
|
5
|
+
import { writeManifest } from "../manifest"
|
|
6
|
+
import { materializeAssets } from "../project"
|
|
7
|
+
import { materializeTypes, writeTsconfig } from "../types"
|
|
8
|
+
import { ensureGitignore, writeDefaultIgnore } from "../ignore"
|
|
9
|
+
import { CliError, c, info, success, progress, endProgress, flagBool, type Args } from "../util"
|
|
10
|
+
|
|
11
|
+
/* `lecodes clone <uuid|url> [dir]` — download a project's files + types into a new folder. */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Accept a bare uuid or a project link and pull out the uuid. Handles editor links
|
|
15
|
+
* (https://le.codes/app/projects/<uuid>) and viewer links (.../view/<uuid>), with any
|
|
16
|
+
* query/hash or trailing slash; falls back to the last path segment.
|
|
17
|
+
*/
|
|
18
|
+
export const parseUuid = (input: string): string => {
|
|
19
|
+
if (!input) throw new CliError("Usage: lecodes clone <project-uuid|url> [directory]")
|
|
20
|
+
if (!input.includes("/")) return input
|
|
21
|
+
const clean = input.split(/[?#]/)[0].replace(/\/+$/, "")
|
|
22
|
+
const match = clean.match(/\/(?:projects|view)\/([^/]+)/)
|
|
23
|
+
return match ? match[1] : clean.slice(clean.lastIndexOf("/") + 1)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const slug = (name: string) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project"
|
|
27
|
+
|
|
28
|
+
export const clone = async (args: Args) => {
|
|
29
|
+
const config = loadConfig()
|
|
30
|
+
const apiUrl = requireApiUrl(config)
|
|
31
|
+
const token = requireToken(config)
|
|
32
|
+
|
|
33
|
+
const uuid = parseUuid(args._[0])
|
|
34
|
+
const project = await getProject(apiUrl, token, uuid)
|
|
35
|
+
|
|
36
|
+
const dir = args._[1] ?? slug(project.name)
|
|
37
|
+
const root = resolve(dir)
|
|
38
|
+
if (existsSync(root) && readdirSync(root).length > 0) {
|
|
39
|
+
throw new CliError(`Directory "${dir}" already exists and is not empty.`)
|
|
40
|
+
}
|
|
41
|
+
mkdirSync(root, { recursive: true })
|
|
42
|
+
|
|
43
|
+
info(c.dim(`Fetching project ${c.bold(project.name)}…`))
|
|
44
|
+
const files = await materializeAssets(root, project, apiUrl, token, progress)
|
|
45
|
+
endProgress()
|
|
46
|
+
|
|
47
|
+
if (!flagBool(args, "no-types")) {
|
|
48
|
+
info(c.dim("Fetching type definitions…"))
|
|
49
|
+
await materializeTypes(root, apiUrl)
|
|
50
|
+
writeTsconfig(root)
|
|
51
|
+
}
|
|
52
|
+
writeDefaultIgnore(root)
|
|
53
|
+
ensureGitignore(root) // .lecodes/ is per-checkout state — keep it out of the project's git repo
|
|
54
|
+
|
|
55
|
+
const commits = await getCommits(apiUrl, token, uuid)
|
|
56
|
+
writeManifest(root, { uuid, apiUrl, name: project.name, baseHeadId: commits.headId, files })
|
|
57
|
+
|
|
58
|
+
success(`Cloned ${c.bold(project.name)} into ${c.bold(dir)}/ (${Object.keys(files).length} files)`)
|
|
59
|
+
info(c.dim(` cd ${dir} — edit, then \`lecodes push -m "your message"\``))
|
|
60
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs"
|
|
2
|
+
import { basename, resolve } from "node:path"
|
|
3
|
+
import { getProject } from "../api"
|
|
4
|
+
import { loadConfig, requireApiUrl, requireToken } from "../config"
|
|
5
|
+
import { findProjectRoot, readManifest, type Manifest } from "../manifest"
|
|
6
|
+
import { collectEntries } from "../compile/collect"
|
|
7
|
+
import { collectLocalEntries } from "../compile/collectLocal"
|
|
8
|
+
import { ambiguousEntryWarning, compileProject, type CompileEntry } from "../compile/projectCompile"
|
|
9
|
+
import { applyProjectFonts } from "../compile/fonts"
|
|
10
|
+
import { applyLocalShaders, desktopShaderPlatform, type StagedShader } from "../compile/shaders"
|
|
11
|
+
import type { DesktopRenderer } from "../desktopRenderer"
|
|
12
|
+
import { applyAppAssetIcons } from "../compile/assetIcons"
|
|
13
|
+
import { buildGlobMatcher } from "../ignore"
|
|
14
|
+
import { readAppConfigAt } from "./appShared"
|
|
15
|
+
import { flagBool, flagStr, isNullSink, note, warnErr, type Args } from "../util"
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* `lecodes compile` — compile the local project into one JS bundle (same pipeline as the server)
|
|
19
|
+
* and print it to stdout (pipeable), or write it with -o/--out. Diagnostics go to stderr.
|
|
20
|
+
* `lecodes preview` is the legacy alias for this command (see `previewLegacy` below).
|
|
21
|
+
*
|
|
22
|
+
* Two modes, picked automatically:
|
|
23
|
+
* - Server-backed project (a cloned/created one with .lecodes/manifest.json): code/.svg/scene
|
|
24
|
+
* content comes from local files (fresh); binary & shader URLs are resolved from the project's
|
|
25
|
+
* server metadata. Local-only / locally-changed assets are flagged.
|
|
26
|
+
* - Local project (from `lecodes init` — no manifest): compile straight from the working tree,
|
|
27
|
+
* no login. `asset()` resources resolve via the host's fetchLocal (the desktop reads the file
|
|
28
|
+
* by name), so the bundle runs on LeCodes Desktop with the assets sitting next to it. Pass
|
|
29
|
+
* `--public-url <url>` to resolve assets from a URL prefix instead.
|
|
30
|
+
*
|
|
31
|
+
* Desktop-host callers (`desktop run`, `render --desktop`, `test --desktop`) pass `localAssets`:
|
|
32
|
+
* in BOTH project modes `asset()` resources then resolve from the working tree (fetchLocal, the
|
|
33
|
+
* files are staged next to app.js), so an edited-but-unpushed image shows up — the same default
|
|
34
|
+
* `lecodes render` has. `--remote-assets` (or an explicit `--public-url`) opts back into server
|
|
35
|
+
* URLs; app.json `resources.remote` matches stay URLs either way (they stream in the built app too).
|
|
36
|
+
*
|
|
37
|
+
* A third mode exists for `lecodes app sync` only (the `shell` parameter): compile for a native
|
|
38
|
+
* shell that ships its assets inside the app. Resources resolve locally (`id:` + fetchLocal) in
|
|
39
|
+
* BOTH project modes, fonts vendor into assets/fonts/ (never CDN — text must render offline,
|
|
40
|
+
* before any network), and resources matching `shell.remotePatterns` keep their server URL for
|
|
41
|
+
* streaming. Returns every resource entry so the caller can copy the bundled ones into the shell.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/** One project resource as the shell compile resolved it (returned to `lecodes app sync`). */
|
|
45
|
+
export type BundleResource = {
|
|
46
|
+
/** Root-relative project path. */
|
|
47
|
+
path: string
|
|
48
|
+
/** The runtime lookup name — the basename the bundle passes to fetchLocal / `// preload:`. */
|
|
49
|
+
name: string
|
|
50
|
+
/** Local file to copy from (always set for resources found in the working tree). */
|
|
51
|
+
absPath?: string
|
|
52
|
+
/** True → left as a server URL (matched app.json resources.remote); not shipped in the app. */
|
|
53
|
+
remote: boolean
|
|
54
|
+
/** A locally compiled shader artifact (.filamat), not a project file: it must ship next to the
|
|
55
|
+
* bundle (the host loads it by name via fetchLocal) but it is not `asset()`-referenced, so the
|
|
56
|
+
* bundle's fetchLocal-literal scan can't see it. */
|
|
57
|
+
shader?: boolean
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Shell-compile options (see the mode note above) — only `lecodes app sync` passes this. */
|
|
61
|
+
export type ShellCompile = { remotePatterns: string[] }
|
|
62
|
+
|
|
63
|
+
/** The `lecodes compile` command. */
|
|
64
|
+
export const compile = async (args: Args): Promise<void> => { await compileImpl(args) }
|
|
65
|
+
|
|
66
|
+
/** `lecodes app sync`'s entry: shell mode (see above), returning the resource inventory. */
|
|
67
|
+
export const compileShell = (args: Args, shell: ShellCompile): Promise<BundleResource[]> => compileImpl(args, shell)
|
|
68
|
+
|
|
69
|
+
/** The compiled project without any output handling — `lecodes render --desktop` runs this bundle in
|
|
70
|
+
* the native desktop host, copying the local-asset resources next to it (fetchLocal by basename). */
|
|
71
|
+
export type BuiltBundle = { js: string, resources: BundleResource[] }
|
|
72
|
+
|
|
73
|
+
/** Extra knobs for the desktop-host callers (see the mode note above). */
|
|
74
|
+
export type BuildBundleOptions = {
|
|
75
|
+
/** Resolve resources from the working tree by default (`--remote-assets` / `--public-url` opt out). */
|
|
76
|
+
localAssets?: boolean
|
|
77
|
+
/** Compile the project's .mat shaders locally and stage the artifacts next to the bundle
|
|
78
|
+
* (fetchLocal). On by default for a non-shell local-asset compile; `app build desktop` opts in
|
|
79
|
+
* explicitly because it builds a fetchLocal host THROUGH the shell path. */
|
|
80
|
+
localShaders?: boolean
|
|
81
|
+
/** The desktop renderer this bundle is being built FOR, when the caller knows (it stages or spawns
|
|
82
|
+
* the very exe that will run it). Narrows the shader compile + staging to the one artifact that
|
|
83
|
+
* host asks for, instead of shipping every native variant. Leave unset when the target is unknown
|
|
84
|
+
* — a generic `preview --local` bundle can be carried anywhere. */
|
|
85
|
+
desktopRenderer?: DesktopRenderer
|
|
86
|
+
/** Synthetic in-memory entries added to the project's own (a virtual entry module, say — see
|
|
87
|
+
* `lecodes lightmap bake`). Paths use the entry convention ("/.name.ts" at the root). */
|
|
88
|
+
extraEntries?: CompileEntry[]
|
|
89
|
+
/** Compile from this entry instead of the detected one / `--entry` (pairs with `extraEntries`). */
|
|
90
|
+
entryOverride?: string
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const compileImpl = async (args: Args, shell?: ShellCompile): Promise<BundleResource[]> => {
|
|
94
|
+
const { js, resources } = await buildBundle(args, shell)
|
|
95
|
+
|
|
96
|
+
const out = flagStr(args, "o", "out")
|
|
97
|
+
if (out && isNullSink(out)) {
|
|
98
|
+
note(`Discarded output (${out})`)
|
|
99
|
+
} else if (out) {
|
|
100
|
+
writeFileSync(resolve(out), js)
|
|
101
|
+
note(`Wrote ${out} (${js.length} bytes)`)
|
|
102
|
+
} else {
|
|
103
|
+
process.stdout.write(js)
|
|
104
|
+
if (!js.endsWith("\n")) process.stdout.write("\n")
|
|
105
|
+
}
|
|
106
|
+
return resources
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const buildBundle = async (args: Args, shell?: ShellCompile, opts: BuildBundleOptions = {}): Promise<BuiltBundle> => {
|
|
110
|
+
let root: string
|
|
111
|
+
let manifest: Manifest | null
|
|
112
|
+
try {
|
|
113
|
+
root = findProjectRoot(process.cwd())
|
|
114
|
+
manifest = readManifest(root)
|
|
115
|
+
} catch {
|
|
116
|
+
root = process.cwd()
|
|
117
|
+
manifest = null
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let entries: CompileEntry[]
|
|
121
|
+
let warnings: string[]
|
|
122
|
+
let name: string
|
|
123
|
+
let publicUrl: string
|
|
124
|
+
let localAssets = false
|
|
125
|
+
|
|
126
|
+
if (manifest) {
|
|
127
|
+
const config = loadConfig()
|
|
128
|
+
const apiUrl = requireApiUrl(config)
|
|
129
|
+
const token = requireToken(config)
|
|
130
|
+
|
|
131
|
+
const publicUrlFlag = flagStr(args, "public-url")
|
|
132
|
+
// Shell compiles always bundle; a desktop-host run reads the tree unless told to use the server.
|
|
133
|
+
localAssets = !!shell || (!!opts.localAssets && publicUrlFlag === undefined && !flagBool(args, "remote-assets"))
|
|
134
|
+
|
|
135
|
+
note("Resolving project assets…")
|
|
136
|
+
const project = await getProject(apiUrl, token, manifest.uuid)
|
|
137
|
+
// Local-asset compiles use URL-less resources as-is instead of degrading, so skip that warning
|
|
138
|
+
// there (an unpushed resources.remote match still warns below).
|
|
139
|
+
;({ entries, warnings } = collectEntries(root, manifest, project, { warnMissingUrl: !localAssets }))
|
|
140
|
+
name = manifest.name
|
|
141
|
+
publicUrl = publicUrlFlag ?? apiUrl
|
|
142
|
+
|
|
143
|
+
// Local-asset compile: everything resolves from the tree except the resources.remote matches,
|
|
144
|
+
// which keep their server URL — and can't without one, so an unpushed match falls back to local.
|
|
145
|
+
const remotePatterns = shell?.remotePatterns ?? (localAssets ? readAppConfigAt(root)?.resources?.remote ?? [] : [])
|
|
146
|
+
if (localAssets && remotePatterns.length > 0) {
|
|
147
|
+
const isRemote = buildGlobMatcher(remotePatterns)
|
|
148
|
+
for (const e of entries) {
|
|
149
|
+
if (e.type !== "resource" || !isRemote(e.path)) continue
|
|
150
|
+
if (!e.fileSrc) { warnErr(`${e.path} matches resources.remote but has no server URL yet — ${shell ? "bundling it (push, then re-sync)" : "reading it from the working tree"}.`); continue }
|
|
151
|
+
e.remote = true
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
// Local project (no server): compile from the tree. Without an explicit --public-url the assets
|
|
156
|
+
// resolve locally (host fetchLocal) so the bundle is self-contained for LeCodes Desktop.
|
|
157
|
+
// fileSrc stays a path (assetBase "") — compileProject prefixes publicUrl exactly once, same as
|
|
158
|
+
// the server/dev conventions. (Passing publicUrl as assetBase too used to double the prefix.)
|
|
159
|
+
const publicUrlFlag = flagStr(args, "public-url")
|
|
160
|
+
localAssets = shell ? true : publicUrlFlag === undefined
|
|
161
|
+
publicUrl = publicUrlFlag ?? ""
|
|
162
|
+
if (opts.localAssets && flagBool(args, "remote-assets")) {
|
|
163
|
+
warnErr("This is a local project (no server) — assets always load from the working tree.")
|
|
164
|
+
}
|
|
165
|
+
// localShaders: a local-asset compile compiles the .mat files itself below, so the
|
|
166
|
+
// "push the project once" degradation notice would be wrong.
|
|
167
|
+
;({ entries, warnings } = collectLocalEntries(root, null, "", { localShaders: localAssets && (opts.localShaders ?? !shell) }))
|
|
168
|
+
name = basename(root)
|
|
169
|
+
if (shell && shell.remotePatterns.length > 0) {
|
|
170
|
+
warnErr("resources.remote needs a server-backed project (a streamed asset needs a server URL) — bundling everything.")
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Custom shaders (.mat) for a host with no asset server: compile them locally (matc) and stage
|
|
175
|
+
// the artifacts next to the bundle, so the desktop host loads them through fetchLocal like the
|
|
176
|
+
// built-in materials. Shell compiles (`app sync ios|android`) keep the URL form for now — the
|
|
177
|
+
// fetchLocal path there is unverified on device. Failure degrades to a warning and the entries
|
|
178
|
+
// keep whatever they had (server URLs, or empty for a local project).
|
|
179
|
+
let stagedShaders: StagedShader[] = []
|
|
180
|
+
if (localAssets && (opts.localShaders ?? !shell)) {
|
|
181
|
+
// A caller that knows its target host (every desktop path) narrows this to the single artifact
|
|
182
|
+
// that host will fetch; otherwise every native variant is compiled and staged, since the
|
|
183
|
+
// runtime picks by `_creator.backend` and the bundle could be carried anywhere.
|
|
184
|
+
const platforms = opts.desktopRenderer
|
|
185
|
+
? [desktopShaderPlatform(process.platform, opts.desktopRenderer)]
|
|
186
|
+
: undefined
|
|
187
|
+
const local = await applyLocalShaders(entries, { root, local: true, platforms })
|
|
188
|
+
warnings.push(...local.warnings)
|
|
189
|
+
stagedShaders = local.artifacts
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// font() macro: a server-backed URL compile bakes CDN registry URLs; a local-asset or shell one
|
|
193
|
+
// vendors faces into assets/fonts/ so the bundle stays self-contained (fetchLocal / --public-url).
|
|
194
|
+
// Shell compiles pass publicUrl "" here so the `// fonts:` header carries no face URLs — the
|
|
195
|
+
// host must register faces from the bundle, never wait on the network before first paint.
|
|
196
|
+
const fontsHeader = await applyProjectFonts(entries, (manifest && !localAssets)
|
|
197
|
+
? { mode: "cdn", root, publicUrl }
|
|
198
|
+
: {
|
|
199
|
+
mode: "vendor", root, vendorDir: root, vendorPrefix: "", publicUrl: shell ? "" : publicUrl,
|
|
200
|
+
makeResourceEntry: (path, absPath) => ({ path, type: "resource", fileSrc: encodeURI(path), absPath }),
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
// assetIcon() macro: icons resolve from the user-level cache (~/.lecodes/cache/icons), fetching
|
|
204
|
+
// misses from the registry, and inline into the bundle as SvgSource — never written into the
|
|
205
|
+
// project. The returned pack list becomes the `// icons:` attribution header.
|
|
206
|
+
if (opts.extraEntries) entries.push(...opts.extraEntries)
|
|
207
|
+
const iconsHeader = await applyAppAssetIcons(entries)
|
|
208
|
+
|
|
209
|
+
note("Compiling…")
|
|
210
|
+
const entryFlag = opts.entryOverride ?? flagStr(args, "entry")
|
|
211
|
+
if (!entryFlag) {
|
|
212
|
+
const ambiguous = ambiguousEntryWarning(entries)
|
|
213
|
+
if (ambiguous) warnErr(ambiguous)
|
|
214
|
+
}
|
|
215
|
+
const js = await compileProject({
|
|
216
|
+
entries,
|
|
217
|
+
name,
|
|
218
|
+
publicUrl,
|
|
219
|
+
entryOverride: entryFlag,
|
|
220
|
+
header: !flagBool(args, "no-header"),
|
|
221
|
+
localAssets,
|
|
222
|
+
fontsHeader,
|
|
223
|
+
iconsHeader,
|
|
224
|
+
// Both default on; `false` only when the flag is present, so the SDK's own defaults stand.
|
|
225
|
+
minify: flagBool(args, "no-minify") ? false : undefined,
|
|
226
|
+
sourcemap: flagBool(args, "no-sourcemap", "no-source-map") ? false : undefined,
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
for (const w of warnings) warnErr(w)
|
|
230
|
+
|
|
231
|
+
// Resource inventory for shell/render callers (includes faces the font pass vendored into entries).
|
|
232
|
+
const resources: BundleResource[] = entries
|
|
233
|
+
.filter((e) => e.type === "resource")
|
|
234
|
+
.map((e) => ({
|
|
235
|
+
path: e.path.replace(/^\//, ""),
|
|
236
|
+
name: e.path.split(/[\\/]/).pop() ?? e.path,
|
|
237
|
+
absPath: e.absPath,
|
|
238
|
+
remote: !!e.remote,
|
|
239
|
+
}))
|
|
240
|
+
// Compiled shader artifacts ship the same way (copied next to app.js, loaded by name), flagged
|
|
241
|
+
// so the desktop packager keeps them — they have no asset() reference for its scan to match.
|
|
242
|
+
for (const artifact of stagedShaders) {
|
|
243
|
+
resources.push({ path: artifact.name, name: artifact.name, absPath: artifact.absPath, remote: false, shader: true })
|
|
244
|
+
}
|
|
245
|
+
return { js, resources }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** `lecodes preview` — the command's former name, kept working for existing scripts. The deprecation
|
|
249
|
+
* goes to stderr, so a piped `lecodes preview > app.js` still yields a clean bundle. */
|
|
250
|
+
export const previewLegacy = async (args: Args) => {
|
|
251
|
+
warnErr("'lecodes preview' is now 'lecodes compile' — the old name still works.")
|
|
252
|
+
await compile(args)
|
|
253
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { join, resolve } from "node:path"
|
|
3
|
+
import { createProject, getCommits } from "../api"
|
|
4
|
+
import { loadConfig, requireApiUrl, requireToken } from "../config"
|
|
5
|
+
import { writeManifest } from "../manifest"
|
|
6
|
+
import { materializeTypes, writeTsconfig } from "../types"
|
|
7
|
+
import { writeDefaultIgnore } from "../ignore"
|
|
8
|
+
import { appClaudeMd, CLAUDE_SETTINGS } from "./projectTemplates"
|
|
9
|
+
import { CliError, c, flagStr, info, success, type Args } from "../util"
|
|
10
|
+
|
|
11
|
+
/* `lecodes create <name> [dir]` — create a brand-new project on the server, then scaffold a local
|
|
12
|
+
* folder for it: the current type definitions (fetched from the server, so IntelliSense matches the
|
|
13
|
+
* deployed runtime), a tsconfig, an ignore file, and an import-free starter. Edit, then `lecodes push`. */
|
|
14
|
+
|
|
15
|
+
const slug = (name: string) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project"
|
|
16
|
+
|
|
17
|
+
const STARTER = `// Welcome to le.codes — no imports needed, the whole SDK is available as globals.
|
|
18
|
+
const scene = new Scene2D({ background: '#10131a' })
|
|
19
|
+
scene.open()
|
|
20
|
+
|
|
21
|
+
setLoop((dt) => {
|
|
22
|
+
// your per-frame logic — dt is seconds since the previous frame
|
|
23
|
+
})
|
|
24
|
+
`
|
|
25
|
+
|
|
26
|
+
export const create = async (args: Args) => {
|
|
27
|
+
const config = loadConfig()
|
|
28
|
+
const apiUrl = requireApiUrl(config)
|
|
29
|
+
const token = requireToken(config)
|
|
30
|
+
|
|
31
|
+
const name = args._[0]
|
|
32
|
+
if (!name) throw new CliError("Usage: lecodes create <name> [directory]")
|
|
33
|
+
|
|
34
|
+
const dir = args._[1] ?? slug(name)
|
|
35
|
+
const root = resolve(dir)
|
|
36
|
+
if (existsSync(root) && readdirSync(root).length > 0) {
|
|
37
|
+
throw new CliError(`Directory "${dir}" already exists and is not empty.`)
|
|
38
|
+
}
|
|
39
|
+
mkdirSync(root, { recursive: true })
|
|
40
|
+
|
|
41
|
+
info(c.dim(`Creating project ${c.bold(name)}…`))
|
|
42
|
+
const { uuid } = await createProject(apiUrl, token, name, flagStr(args, "team"))
|
|
43
|
+
|
|
44
|
+
info(c.dim("Fetching type definitions…"))
|
|
45
|
+
await materializeTypes(root, apiUrl)
|
|
46
|
+
writeTsconfig(root)
|
|
47
|
+
writeDefaultIgnore(root)
|
|
48
|
+
writeFileSync(join(root, "main.ts"), STARTER)
|
|
49
|
+
writeFileSync(join(root, "CLAUDE.md"), appClaudeMd(name))
|
|
50
|
+
mkdirSync(join(root, ".claude"), { recursive: true })
|
|
51
|
+
writeFileSync(join(root, ".claude", "settings.json"), CLAUDE_SETTINGS)
|
|
52
|
+
|
|
53
|
+
// A fresh project's tree is empty on the server; the local main.ts is a pending add for the first push.
|
|
54
|
+
const commits = await getCommits(apiUrl, token, uuid)
|
|
55
|
+
writeManifest(root, { uuid, apiUrl, name, baseHeadId: commits.headId, files: {} })
|
|
56
|
+
|
|
57
|
+
success(`Created ${c.bold(name)} in ${c.bold(dir)}/`)
|
|
58
|
+
info(c.dim(` cd ${dir} — edit main.ts, then \`lecodes push -m "first commit"\` to save it`))
|
|
59
|
+
}
|