lecodes-cli 0.20.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -57
- package/dist/index.js +5918 -5020
- package/package.json +7 -5
- package/runtime/materials/decal-relief.mat +12 -1
- package/runtime/materials/decal.mat +17 -3
- package/runtime/materials/lightmap-baked-lite.mat +6 -1
- package/runtime/materials/lightmap-baked.mat +6 -1
- package/runtime/materials/lightmap.mat +3 -0
- package/runtime/scene-harness.json +1 -1
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/assets/createViewerLite-DHqTFhWB.js +866 -0
- package/runtime/web/assets/{index-BMt7AnC5.js → index-Q06c6oHx.js} +1 -1
- package/runtime/web/embed.js +1 -1
- package/src/api.ts +1 -302
- package/src/cli/args.ts +37 -0
- package/src/cli/command.ts +113 -0
- package/src/cli/errors.ts +13 -0
- package/src/cli/help.ts +133 -0
- package/src/cli/index.ts +7 -0
- package/src/cli/output.ts +102 -0
- package/src/cli/run.ts +106 -0
- package/src/commands/{appAndroid.ts → app/android.ts} +451 -42
- package/src/commands/{appDesktop.ts → app/desktop.ts} +63 -32
- package/src/commands/{appDesktopMac.ts → app/desktopMac.ts} +24 -18
- package/src/commands/{appIcon.ts → app/icon.ts} +2 -2
- package/src/commands/{app.ts → app/index.ts} +397 -289
- package/src/commands/{appShared.ts → app/shared.ts} +30 -39
- package/src/commands/{appTemplatesAndroid.ts → app/templates/android.ts} +62 -28
- package/src/commands/{appTemplatesGradlew.ts → app/templates/gradlew.ts} +1 -1
- package/src/commands/{appTemplates.ts → app/templates/ios.ts} +1 -1
- package/src/commands/assets.ts +23 -21
- package/src/commands/clone.ts +48 -51
- package/src/commands/compile.ts +62 -88
- package/src/commands/create.ts +46 -47
- package/src/commands/design/comments.ts +361 -0
- package/src/commands/design/context.ts +101 -0
- package/src/commands/design/index.ts +391 -0
- package/src/commands/design/snapshot.ts +134 -0
- package/src/commands/{designTemplates.ts → design/templates.ts} +4 -3
- package/src/commands/desktop.ts +90 -82
- package/src/commands/dev.ts +201 -176
- package/src/commands/diff.ts +39 -51
- package/src/commands/index.ts +60 -0
- package/src/commands/{init.ts → init/index.ts} +199 -191
- package/src/commands/install.ts +80 -67
- package/src/commands/lightmap.ts +203 -171
- package/src/commands/link.ts +87 -94
- package/src/commands/login.ts +25 -23
- package/src/commands/navmesh.ts +168 -138
- package/src/commands/pn.ts +201 -244
- package/src/commands/pull.ts +49 -56
- package/src/commands/push.ts +48 -54
- package/src/commands/render.ts +158 -99
- package/src/commands/scene.ts +58 -55
- package/src/commands/{shaders.ts → shaders/index.ts} +61 -42
- package/src/commands/{shadersNew.ts → shaders/new.ts} +2 -2
- package/src/commands/shared.ts +79 -0
- package/src/commands/status.ts +22 -26
- package/src/commands/test.ts +398 -371
- package/src/commands/thumbs.ts +185 -175
- package/src/commands/update/index.ts +226 -0
- package/src/commands/{types.ts → update/types.ts} +31 -22
- package/src/compile/collect.ts +5 -5
- package/src/compile/collectLocal.ts +3 -3
- package/src/compile/designCompile.ts +3 -3
- package/src/compile/headlessBundle.ts +133 -129
- package/src/compile/projectCompile.ts +2 -2
- package/src/compile/sceneCompile.ts +10 -8
- package/src/compile/screenEntry.ts +123 -127
- package/src/compile/shaders.ts +3 -3
- package/src/dev/androidDev.ts +1 -1
- package/src/dev/devServer.ts +3 -3
- package/src/dev/webRunner.ts +1 -1
- package/src/{cmgenTool.ts → hosts/cmgenTool.ts} +1 -1
- package/src/{desktopRenderer.ts → hosts/desktopRenderer.ts} +6 -5
- package/src/{desktopScript.ts → hosts/desktopScript.ts} +1 -1
- package/src/{distRoot.ts → hosts/distRoot.ts} +9 -5
- package/src/{matcTool.ts → hosts/matcTool.ts} +1 -1
- package/src/{peerInstall.ts → hosts/peerInstall.ts} +2 -2
- package/src/{releases.ts → hosts/releases.ts} +1 -1
- package/src/index.ts +32 -480
- package/src/platform/api.ts +302 -0
- package/src/{browserAuth.ts → platform/browserAuth.ts} +1 -1
- package/src/{config.ts → platform/config.ts} +1 -1
- package/src/{serverDiff.ts → platform/serverDiff.ts} +3 -3
- package/src/{projectEnv.ts → project/env.ts} +33 -2
- package/src/{localFiles.ts → project/localFiles.ts} +1 -1
- package/src/{manifest.ts → project/manifest.ts} +1 -1
- package/src/{project.ts → project/materialize.ts} +2 -2
- package/src/project/paths.ts +20 -0
- package/src/{textDiff.ts → project/textDiff.ts} +1 -1
- package/src/{types.ts → project/types.ts} +0 -0
- package/runtime/web/assets/createViewerLite-Ct_PZdof.js +0 -852
- package/src/commands/design.ts +0 -846
- package/src/commands/update.ts +0 -211
- package/src/util.ts +0 -146
- /package/src/commands/{projectTemplates.ts → init/templates.ts} +0 -0
- /package/src/{lecodes-3d-editor.d.ts → declarations/lecodes-3d-editor.d.ts} +0 -0
- /package/src/{lecodes-assets.d.ts → declarations/lecodes-assets.d.ts} +0 -0
- /package/src/{lecodes-design.d.ts → declarations/lecodes-design.d.ts} +0 -0
- /package/src/{lecodes-renderer.d.ts → declarations/lecodes-renderer.d.ts} +0 -0
- /package/src/{qrcode-terminal.d.ts → declarations/qrcode-terminal.d.ts} +0 -0
- /package/src/{peers.ts → hosts/peers.ts} +0 -0
- /package/src/{designMeta.ts → project/designMeta.ts} +0 -0
- /package/src/{ignore.ts → project/ignore.ts} +0 -0
|
@@ -1,54 +1,63 @@
|
|
|
1
1
|
import { resolve } from "node:path"
|
|
2
|
-
import { getTypes } from "
|
|
3
|
-
import { DEFAULT_API, loadConfig, normalizeApiUrl } from "
|
|
4
|
-
import { findProjectRoot, LECODES_DIR } from "
|
|
2
|
+
import { getTypes } from "../../platform/api"
|
|
3
|
+
import { DEFAULT_API, loadConfig, normalizeApiUrl } from "../../platform/config"
|
|
4
|
+
import { findProjectRoot, LECODES_DIR } from "../../project/manifest"
|
|
5
5
|
import {
|
|
6
6
|
bundleHash, readLocalTypeBundle, readProjectTypeBundle, writeTsconfig, writeTypeBundle,
|
|
7
7
|
type TypeBundle,
|
|
8
|
-
} from "
|
|
9
|
-
import { CliError, c,
|
|
8
|
+
} from "../../project/types"
|
|
9
|
+
import { CliError, c, log, note, success, warn } from "../../cli"
|
|
10
10
|
|
|
11
11
|
/*
|
|
12
|
-
* The types step of `lecodes update` (formerly `lecodes types [dir]`) — re-materialize the IDE type
|
|
12
|
+
* The types step of `lecodes update` (formerly `lecodes types [dir]`) — re-materialize the IDE type
|
|
13
|
+
* surface (<root>/.lecodes/types/ + tsconfig).
|
|
13
14
|
*
|
|
14
15
|
* Every project gets these types once, at scaffold time: `clone`/`create`/`pull` fetch them from
|
|
15
16
|
* GET /api/types (the deployed runtime's surface), while `init` and `design` — which run with no
|
|
16
17
|
* server and no login — write the bundle vendored into the CLI at publish time. Neither source
|
|
17
|
-
* refreshes on its own, so a local project's types age with the CLI binary that made it. This
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* refreshes on its own, so a local project's types age with the CLI binary that made it. This step
|
|
19
|
+
* is the refresh: server by default (newest SDK the platform actually runs), `local` for the CLI's
|
|
20
|
+
* own copy, `check` to only report drift (a CliError when stale).
|
|
20
21
|
*
|
|
21
22
|
* IDE-only: the compiler reads the SDK sources vendored in runtime/sdk, never .lecodes/types. When
|
|
22
|
-
* the two sources disagree the
|
|
23
|
+
* the two sources disagree the step says so — types promising APIs this CLI can't compile is
|
|
23
24
|
* exactly the confusing case worth naming out loud.
|
|
24
25
|
*/
|
|
25
26
|
|
|
27
|
+
export type RefreshTypesOptions = {
|
|
28
|
+
/** The project folder (default: the project root above the cwd, else the cwd). */
|
|
29
|
+
dir?: string
|
|
30
|
+
/** The bundle vendored in this CLI instead of the server's. */
|
|
31
|
+
local: boolean
|
|
32
|
+
/** Only report drift — write nothing, throw when stale. */
|
|
33
|
+
check: boolean
|
|
34
|
+
/** API origin for the server bundle (default: the configured one). */
|
|
35
|
+
api?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
const label = (bundle: TypeBundle) => `${Object.keys(bundle).length} file(s)`
|
|
27
39
|
|
|
28
|
-
export const
|
|
29
|
-
const
|
|
30
|
-
const root = dirArg ? resolve(dirArg) : (() => {
|
|
40
|
+
export const refreshTypes = async (opts: RefreshTypesOptions): Promise<void> => {
|
|
41
|
+
const root = opts.dir ? resolve(opts.dir) : (() => {
|
|
31
42
|
try { return findProjectRoot(process.cwd()) } catch { return process.cwd() }
|
|
32
43
|
})()
|
|
33
44
|
|
|
34
|
-
const useLocal = flagBool(args, "local")
|
|
35
|
-
const checkOnly = flagBool(args, "check")
|
|
36
45
|
const local = readLocalTypeBundle()
|
|
37
46
|
|
|
38
47
|
// ---- pick the source ----
|
|
39
48
|
let bundle: TypeBundle | null = null
|
|
40
49
|
let source = ""
|
|
41
|
-
if (
|
|
50
|
+
if (opts.local) {
|
|
42
51
|
if (!local) throw new CliError("This CLI ships no vendored type bundle — drop --local to fetch them from the server.")
|
|
43
52
|
bundle = local
|
|
44
53
|
source = "the CLI's bundled SDK"
|
|
45
54
|
} else {
|
|
46
|
-
const apiUrl = normalizeApiUrl(
|
|
55
|
+
const apiUrl = normalizeApiUrl(opts.api ?? loadConfig().apiUrl ?? DEFAULT_API)
|
|
47
56
|
try {
|
|
48
57
|
bundle = await getTypes(apiUrl)
|
|
49
58
|
source = apiUrl
|
|
50
59
|
} catch (e) {
|
|
51
|
-
if (!local ||
|
|
60
|
+
if (!local || opts.check) throw e
|
|
52
61
|
warn(`${(e as Error).message}`)
|
|
53
62
|
bundle = local
|
|
54
63
|
source = "the CLI's bundled SDK (server unreachable)"
|
|
@@ -56,10 +65,10 @@ export const types = async (args: Args) => {
|
|
|
56
65
|
}
|
|
57
66
|
log(`${c.green("source")} ${source} — ${label(bundle)}`)
|
|
58
67
|
|
|
59
|
-
// ---- report drift, write unless
|
|
68
|
+
// ---- report drift, write unless check ----
|
|
60
69
|
const onDisk = readProjectTypeBundle(root)
|
|
61
70
|
const same = bundleHash(onDisk) === bundleHash(bundle)
|
|
62
|
-
if (
|
|
71
|
+
if (opts.check) {
|
|
63
72
|
if (Object.keys(onDisk).length === 0) throw new CliError(`No types in ${LECODES_DIR}/types/ — run \`lecodes update\`.`)
|
|
64
73
|
if (!same) throw new CliError(`Types are out of date (${label(onDisk)} on disk) — run \`lecodes update\`.`)
|
|
65
74
|
success(`Types are up to date (${label(onDisk)}).`)
|
|
@@ -77,11 +86,11 @@ export const types = async (args: Args) => {
|
|
|
77
86
|
|
|
78
87
|
// The compiler uses the SDK vendored in this CLI, not these declarations — worth saying when the
|
|
79
88
|
// server's surface is a different one, since that gap shows up as "autocompletes but won't build".
|
|
80
|
-
if (!
|
|
89
|
+
if (!opts.local && local && bundleHash(local) !== bundleHash(bundle)) {
|
|
81
90
|
warn("The server's SDK surface differs from the one this CLI compiles with — if something autocompletes but fails to compile, update the CLI (`npm install -g lecodes-cli@latest`) or re-run with --local.")
|
|
82
91
|
}
|
|
83
92
|
|
|
84
93
|
log("")
|
|
85
94
|
success(stats.added + stats.updated + stats.removed > 0 ? "Types synced." : "Types already current.")
|
|
86
|
-
if (!
|
|
95
|
+
if (!opts.dir) note("Reload the TypeScript server in your editor to pick them up.")
|
|
87
96
|
}
|
package/src/compile/collect.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs"
|
|
2
2
|
import { basename } from "node:path"
|
|
3
|
-
import type { Project } from "../api"
|
|
4
|
-
import type { Manifest } from "../manifest"
|
|
5
|
-
import { classifyKind, scanLocal } from "../localFiles"
|
|
6
|
-
import { loadIgnore } from "../ignore"
|
|
7
|
-
import { buildPaths } from "../project"
|
|
3
|
+
import type { Project } from "../platform/api"
|
|
4
|
+
import type { Manifest } from "../project/manifest"
|
|
5
|
+
import { classifyKind, scanLocal } from "../project/localFiles"
|
|
6
|
+
import { loadIgnore } from "../project/ignore"
|
|
7
|
+
import { buildPaths } from "../project/materialize"
|
|
8
8
|
import type { CompileEntry } from "./projectCompile"
|
|
9
9
|
|
|
10
10
|
/*
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs"
|
|
2
2
|
import { basename } from "node:path"
|
|
3
|
-
import type { Manifest } from "../manifest"
|
|
4
|
-
import { classifyKind, scanLocal } from "../localFiles"
|
|
5
|
-
import { loadIgnore } from "../ignore"
|
|
3
|
+
import type { Manifest } from "../project/manifest"
|
|
4
|
+
import { classifyKind, scanLocal } from "../project/localFiles"
|
|
5
|
+
import { loadIgnore } from "../project/ignore"
|
|
6
6
|
import type { CompileEntry } from "./projectCompile"
|
|
7
7
|
|
|
8
8
|
/*
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs"
|
|
2
2
|
import { basename } from "node:path"
|
|
3
|
-
import type { Manifest } from "../manifest"
|
|
4
|
-
import { classifyKind, scanLocal } from "../localFiles"
|
|
5
|
-
import { loadIgnore } from "../ignore"
|
|
3
|
+
import type { Manifest } from "../project/manifest"
|
|
4
|
+
import { classifyKind, scanLocal } from "../project/localFiles"
|
|
5
|
+
import { loadIgnore } from "../project/ignore"
|
|
6
6
|
import { compileProject, type CompileEntry } from "./projectCompile"
|
|
7
7
|
import { applyAssetIcons } from "./assetIcons"
|
|
8
8
|
import { applyProjectFonts } from "./fonts"
|
|
@@ -1,129 +1,133 @@
|
|
|
1
|
-
import { getProject } from "../api"
|
|
2
|
-
import { loadConfig, requireApiUrl, requireToken } from "../config"
|
|
3
|
-
import { findProjectRoot, readManifest, type Manifest } from "../manifest"
|
|
4
|
-
import { collectEntries } from "./collect"
|
|
5
|
-
import { collectLocalEntries } from "./collectLocal"
|
|
6
|
-
import { ambiguousEntryWarning, compileProject } from "./projectCompile"
|
|
7
|
-
import { applyProjectFonts } from "./fonts"
|
|
8
|
-
import { applyAppAssetIcons } from "./assetIcons"
|
|
9
|
-
import type { ScreenTarget } from "./screenEntry"
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
/*
|
|
13
|
-
* Compile the project into the iife bundle the headless renderer runs — shared by `lecodes render`
|
|
14
|
-
* and `lecodes test`. Two modes, picked automatically (same split as `lecodes compile`):
|
|
15
|
-
* - Server-backed project (.lecodes/manifest.json): entries resolve from local files + the
|
|
16
|
-
* project's server metadata (needs login).
|
|
17
|
-
* - Local project (from `lecodes init` — no manifest): compiles straight from the working tree,
|
|
18
|
-
* no login.
|
|
19
|
-
* In both, resource assets default to LOCAL disk reads: each asset('./x.png') URL becomes
|
|
20
|
-
* `lecodesfile://<abs-path>` which the renderer reads off disk — unpushed/edited files render with
|
|
21
|
-
* no network. `--remote-assets` / an explicit `--public-url` switch to server URLs instead
|
|
22
|
-
* (server-backed projects only; then data needs --wait-network).
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
// URL scheme emitted for a local project's resources so the renderer reads them off disk. Must match
|
|
26
|
-
// lecodes-renderer's LOCAL_ASSET_SCHEME (headless/runProject.ts).
|
|
27
|
-
export const LOCAL_ASSET_SCHEME = "lecodesfile://"
|
|
28
|
-
|
|
29
|
-
export type HeadlessBundle = {
|
|
30
|
-
js: string
|
|
31
|
-
/** The project root the bundle was compiled from. */
|
|
32
|
-
root: string
|
|
33
|
-
/** Non-fatal compile-time notices (missing URLs, engine hints) — print via warnErr. */
|
|
34
|
-
warnings: string[]
|
|
35
|
-
/** Whether asset URLs resolve from the local disk (`lecodesfile://`). */
|
|
36
|
-
useLocalAssets: boolean
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
1
|
+
import { getProject } from "../platform/api"
|
|
2
|
+
import { loadConfig, requireApiUrl, requireToken } from "../platform/config"
|
|
3
|
+
import { findProjectRoot, readManifest, type Manifest } from "../project/manifest"
|
|
4
|
+
import { collectEntries } from "./collect"
|
|
5
|
+
import { collectLocalEntries } from "./collectLocal"
|
|
6
|
+
import { ambiguousEntryWarning, compileProject } from "./projectCompile"
|
|
7
|
+
import { applyProjectFonts } from "./fonts"
|
|
8
|
+
import { applyAppAssetIcons } from "./assetIcons"
|
|
9
|
+
import type { ScreenTarget } from "./screenEntry"
|
|
10
|
+
import { note } from "../cli"
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* Compile the project into the iife bundle the headless renderer runs — shared by `lecodes render`
|
|
14
|
+
* and `lecodes test`. Two modes, picked automatically (same split as `lecodes compile`):
|
|
15
|
+
* - Server-backed project (.lecodes/manifest.json): entries resolve from local files + the
|
|
16
|
+
* project's server metadata (needs login).
|
|
17
|
+
* - Local project (from `lecodes init` — no manifest): compiles straight from the working tree,
|
|
18
|
+
* no login.
|
|
19
|
+
* In both, resource assets default to LOCAL disk reads: each asset('./x.png') URL becomes
|
|
20
|
+
* `lecodesfile://<abs-path>` which the renderer reads off disk — unpushed/edited files render with
|
|
21
|
+
* no network. `--remote-assets` / an explicit `--public-url` switch to server URLs instead
|
|
22
|
+
* (server-backed projects only; then data needs --wait-network).
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// URL scheme emitted for a local project's resources so the renderer reads them off disk. Must match
|
|
26
|
+
// lecodes-renderer's LOCAL_ASSET_SCHEME (headless/runProject.ts).
|
|
27
|
+
export const LOCAL_ASSET_SCHEME = "lecodesfile://"
|
|
28
|
+
|
|
29
|
+
export type HeadlessBundle = {
|
|
30
|
+
js: string
|
|
31
|
+
/** The project root the bundle was compiled from. */
|
|
32
|
+
root: string
|
|
33
|
+
/** Non-fatal compile-time notices (missing URLs, engine hints) — print via warnErr. */
|
|
34
|
+
warnings: string[]
|
|
35
|
+
/** Whether asset URLs resolve from the local disk (`lecodesfile://`). */
|
|
36
|
+
useLocalAssets: boolean
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** What a headless compile honours: `--entry` / `--public-url` / `--remote-assets` / `--wait-network`,
|
|
40
|
+
* and the screen module a `render <file>` named. */
|
|
41
|
+
export type HeadlessOptions = { entry?: string, publicUrl?: string, remoteAssets?: boolean, waitNetwork?: boolean, screen?: ScreenTarget }
|
|
42
|
+
|
|
43
|
+
/** Compile the local project for a headless run. */
|
|
44
|
+
export const compileHeadlessBundle = async (opts: HeadlessOptions = {}): Promise<HeadlessBundle> => {
|
|
45
|
+
let root: string
|
|
46
|
+
let manifest: Manifest | null
|
|
47
|
+
try {
|
|
48
|
+
root = findProjectRoot(process.cwd())
|
|
49
|
+
manifest = readManifest(root)
|
|
50
|
+
} catch {
|
|
51
|
+
root = process.cwd()
|
|
52
|
+
manifest = null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const publicUrlFlag = opts.publicUrl
|
|
56
|
+
// A local (server-less) project has no remote asset URLs — it always reads from disk.
|
|
57
|
+
const useLocalAssets = manifest ? (!publicUrlFlag && !opts.remoteAssets) : true
|
|
58
|
+
|
|
59
|
+
let entries
|
|
60
|
+
let warnings: string[]
|
|
61
|
+
let name: string
|
|
62
|
+
let publicUrl: string
|
|
63
|
+
|
|
64
|
+
if (manifest) {
|
|
65
|
+
const config = loadConfig()
|
|
66
|
+
const apiUrl = requireApiUrl(config)
|
|
67
|
+
const token = requireToken(config)
|
|
68
|
+
|
|
69
|
+
note("Resolving project assets…")
|
|
70
|
+
const project = await getProject(apiUrl, token, manifest.uuid)
|
|
71
|
+
;({ entries, warnings } = collectEntries(root, manifest, project))
|
|
72
|
+
name = manifest.name
|
|
73
|
+
publicUrl = useLocalAssets ? LOCAL_ASSET_SCHEME : (publicUrlFlag ?? apiUrl)
|
|
74
|
+
// In local mode the "push it so its URL resolves" notices are moot (we read from disk).
|
|
75
|
+
if (useLocalAssets) warnings = warnings.filter((w) => !w.includes("No server URL"))
|
|
76
|
+
} else {
|
|
77
|
+
;({ entries, warnings } = collectLocalEntries(root, null, ""))
|
|
78
|
+
name = root.split(/[\\/]/).pop() ?? "project"
|
|
79
|
+
publicUrl = LOCAL_ASSET_SCHEME
|
|
80
|
+
if (opts.remoteAssets || publicUrlFlag) {
|
|
81
|
+
warnings.push("This is a local project (no server) — assets always load from the working tree.")
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Local assets (default): resolve each resource (asset('./x.png')) from the working tree instead of
|
|
86
|
+
// the server, so unpushed / edited images show up with no --wait-network. Each resource's URL becomes
|
|
87
|
+
// `lecodesfile://<abs-path>`, which the renderer reads off disk.
|
|
88
|
+
const compileEntries = useLocalAssets
|
|
89
|
+
? entries.map((e) => (e.type === "resource" && e.absPath)
|
|
90
|
+
? { ...e, fileSrc: encodeURIComponent(e.absPath) }
|
|
91
|
+
: e)
|
|
92
|
+
: [...entries]
|
|
93
|
+
// Rendering one screen: the generated entry that imports it and calls .open() (see screenEntry.ts).
|
|
94
|
+
if (opts.screen) compileEntries.push(opts.screen.entry)
|
|
95
|
+
|
|
96
|
+
// font() macro: vendor registry faces into assets/fonts/ so headless renders read them off disk
|
|
97
|
+
// (lecodesfile://); with --remote-assets they stay CDN URLs like every other remote resource.
|
|
98
|
+
await applyProjectFonts(compileEntries, useLocalAssets
|
|
99
|
+
? {
|
|
100
|
+
mode: "vendor", root, vendorDir: root, vendorPrefix: "",
|
|
101
|
+
makeResourceEntry: (path, absPath) => ({ path, type: "resource", fileSrc: encodeURIComponent(absPath), absPath }),
|
|
102
|
+
}
|
|
103
|
+
: { mode: "cdn", root })
|
|
104
|
+
|
|
105
|
+
// assetIcon() macro: resolve from the user-level cache (~/.lecodes/cache/icons) and inline, so
|
|
106
|
+
// headless renders stay deterministic and offline after the first fetch (like local assets).
|
|
107
|
+
await applyAppAssetIcons(compileEntries)
|
|
108
|
+
|
|
109
|
+
note("Compiling…")
|
|
110
|
+
const entryFlag = opts.screen?.entryPath ?? opts.entry
|
|
111
|
+
if (!entryFlag) {
|
|
112
|
+
const ambiguous = ambiguousEntryWarning(compileEntries)
|
|
113
|
+
if (ambiguous) warnings.push(ambiguous)
|
|
114
|
+
}
|
|
115
|
+
const js = await compileProject({
|
|
116
|
+
entries: compileEntries,
|
|
117
|
+
name,
|
|
118
|
+
publicUrl,
|
|
119
|
+
entryOverride: entryFlag,
|
|
120
|
+
header: false,
|
|
121
|
+
format: "iife",
|
|
122
|
+
})
|
|
123
|
+
if (js.includes("_creator.createScene") || js.includes("_creator.createEntity")) {
|
|
124
|
+
warnings.push("3D project: the scene graph is captured as `scene3d` in the JSON output; 3D isn't rasterized into PNGs.")
|
|
125
|
+
}
|
|
126
|
+
// With --remote-assets, 2D textures come from the network; without --wait-network they resolve empty
|
|
127
|
+
// and sprites render as flat placeholders. Local assets (default) load from disk, so no hint needed.
|
|
128
|
+
if (js.includes("_creator2d") && !useLocalAssets && !opts.waitNetwork) {
|
|
129
|
+
warnings.push("2D scene detected: sprite textures load over the network — add --wait-network to include them (else sprites render as flat placeholders).")
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return { js, root, warnings, useLocalAssets }
|
|
133
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync } from "node:fs"
|
|
2
2
|
import { createRequire } from "node:module"
|
|
3
3
|
import { dirname, join } from "node:path"
|
|
4
|
-
import type { FileKind } from "../manifest"
|
|
5
|
-
import { distRoot } from "../distRoot"
|
|
4
|
+
import type { FileKind } from "../project/manifest"
|
|
5
|
+
import { distRoot } from "../hosts/distRoot"
|
|
6
6
|
import { compileProject as sdkCompileProject, detectEntry, scanModulesChisel, ENTRY_CONVENTION } from "lecodes-sdk/compile"
|
|
7
7
|
|
|
8
8
|
// Every project compiles through the SDK's one import-free compiler, which bundles with chisel (the
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto"
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
3
3
|
import { join } from "node:path"
|
|
4
|
-
import type { Manifest } from "../manifest"
|
|
5
|
-
import { scanLocal } from "../localFiles"
|
|
6
|
-
import { loadIgnore } from "../ignore"
|
|
7
|
-
import { distRoot } from "../distRoot"
|
|
8
|
-
import { CliError, warnErr } from "../
|
|
4
|
+
import type { Manifest } from "../project/manifest"
|
|
5
|
+
import { scanLocal } from "../project/localFiles"
|
|
6
|
+
import { loadIgnore } from "../project/ignore"
|
|
7
|
+
import { distRoot } from "../hosts/distRoot"
|
|
8
|
+
import { CliError, warnErr } from "../cli"
|
|
9
9
|
import { compileProject, type CompileEntry } from "./projectCompile"
|
|
10
10
|
import { applyLocalShaders, shadersCacheDir } from "./shaders"
|
|
11
11
|
|
|
@@ -55,7 +55,7 @@ export const compileSceneEditor = async (opts: {
|
|
|
55
55
|
scenePath: string
|
|
56
56
|
}): Promise<{ js: string | null, error?: string }> => {
|
|
57
57
|
const { root, scenePath } = opts
|
|
58
|
-
const { loadSceneHarness, sceneEditorEntries, SCENE_ENTRY_PATH } = await import("lecodes-sdk/compile")
|
|
58
|
+
const { loadSceneHarness, sceneEditorEntries, sceneImportsOf, SCENE_ENTRY_PATH } = await import("lecodes-sdk/compile")
|
|
59
59
|
|
|
60
60
|
const entries: CompileEntry[] = []
|
|
61
61
|
const scan = scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(opts.manifest?.files ?? {})) })
|
|
@@ -96,10 +96,12 @@ export const compileSceneEditor = async (opts: {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
const scene = scenePath.startsWith("./") ? scenePath : "./" + rel(scenePath)
|
|
99
|
-
|
|
99
|
+
const sceneEntry = entries.find((e) => e.path === scene && e.type === "text")
|
|
100
|
+
if (!sceneEntry) {
|
|
100
101
|
return { js: null, error: `Scene file not found: ${scene}` }
|
|
101
102
|
}
|
|
102
|
-
|
|
103
|
+
const paths = entries.map((e) => e.path)
|
|
104
|
+
entries.push(...sceneEditorEntries(scene, resolveHarness(loadSceneHarness), paths, sceneImportsOf(sceneEntry.text ?? "", scene, paths)))
|
|
103
105
|
|
|
104
106
|
try {
|
|
105
107
|
const js = await compileProject({
|