lecodes-cli 2.4.10 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2510 -1035
- package/package.json +10 -10
- package/runtime/materials/decal-relief.mat +21 -46
- package/runtime/materials/decal.mat +41 -61
- package/runtime/materials/foliage-cards.mat +28 -140
- package/runtime/materials/foliage-impostor.mat +485 -0
- package/runtime/materials/foliage.mat +232 -0
- package/runtime/materials/grass.mat +244 -0
- package/runtime/materials/particles-quad.mat +5 -3
- package/runtime/materials/particles.mat +5 -3
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/VERSION.json +3 -3
- package/runtime/web/assets/materials-Cs_z1zLi.bin +0 -0
- package/runtime/web/assets/materials-world-BtoAfznA.bin +0 -0
- package/runtime/web/player.js +3 -3
- package/runtime/web-lite/assets/{canvas-WxiusXCB.js → canvas-wFjS40IX.js} +1 -1
- package/runtime/web-lite/assets/creator2d-1pSkYRCn.js +1 -0
- package/runtime/web-lite/assets/creator2d-Ci5-pZGr.wasm +0 -0
- package/runtime/web-lite/assets/{pager-DpGa2uaE.js → pager-BZAO9gMC.js} +1 -1
- package/runtime/web-lite/assets/{plugins-CuEPHbHL.js → plugins-B7TuO_p4.js} +1 -1
- package/runtime/web-lite/assets/scene2d-F6T1Io7I.js +1 -0
- package/runtime/web-lite/assets/{tween-BnWYhJ7P.js → tween-DbrQTld4.js} +1 -1
- package/runtime/web-lite/assets/{video-BLj61nDx.js → video-bzR_t2i6.js} +1 -1
- package/runtime/web-lite/assets/{vlist-CwSYpLz-.js → vlist-Ap-F4H5J.js} +1 -1
- package/runtime/web-lite/features.json +18 -6
- package/runtime/web-lite/lite.js +6 -6
- package/src/commands/compile.ts +2 -1
- package/src/commands/hub/create.ts +100 -0
- package/src/commands/hub/credential.ts +32 -0
- package/src/commands/hub/index.ts +21 -0
- package/src/commands/hub/login.ts +89 -0
- package/src/commands/hub/status.ts +109 -0
- package/src/commands/index.ts +5 -1
- package/src/commands/level.ts +123 -0
- package/src/commands/lightmap.ts +41 -79
- package/src/commands/navmesh.ts +84 -67
- package/src/commands/secrets.ts +147 -0
- package/src/commands/update/index.ts +154 -64
- package/src/compile/designCompile.ts +2 -0
- package/src/compile/headlessBundle.ts +2 -0
- package/src/compile/projectCompile.ts +12 -1
- package/src/compile/sceneCompile.ts +2 -1
- package/src/dev/backendWorker.ts +6 -1
- package/src/dev/devServer.ts +2 -1
- package/src/dev/localBackend.ts +21 -14
- package/src/dev/projectBackend.ts +7 -3
- package/src/dev/watchProject.ts +2 -0
- package/src/hosts/desktopRenderer.ts +10 -2
- package/src/hosts/peerInstall.ts +7 -3
- package/src/hub/api.ts +220 -0
- package/src/hub/config.ts +53 -0
- package/src/hub/git.ts +148 -0
- package/src/platform/api.ts +21 -0
- package/src/project/env.ts +29 -0
- package/src/web/build.ts +149 -88
- package/runtime/web/assets/materials-CJzxzdUK.bin +0 -0
- package/runtime/web/assets/materials-world-BNQb0Z1l.bin +0 -0
package/src/web/build.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readAppEnv } from "../project/env"
|
|
1
2
|
import { createHash } from "node:crypto"
|
|
2
3
|
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs"
|
|
3
4
|
import { dirname, extname, join, resolve } from "node:path"
|
|
@@ -5,7 +6,7 @@ import { buildLiteMaterialArtifact } from "lecodes-sdk/compile"
|
|
|
5
6
|
import { collectLocalEntries } from "../compile/collectLocal"
|
|
6
7
|
import { applyProjectFonts } from "../compile/fonts"
|
|
7
8
|
import { applyAppAssetIcons } from "../compile/assetIcons"
|
|
8
|
-
import { ambiguousEntryWarning, compileProject } from "../compile/projectCompile"
|
|
9
|
+
import { ambiguousEntryWarning, compileProject, type CompileEntry } from "../compile/projectCompile"
|
|
9
10
|
import { readAppConfigAt } from "../commands/app/shared"
|
|
10
11
|
import type { Manifest } from "../project/manifest"
|
|
11
12
|
import { CliError } from "../cli"
|
|
@@ -98,70 +99,62 @@ const walk = (dir: string, prefix = ""): { path: string, bytes: number }[] => {
|
|
|
98
99
|
return out
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
/** The bundle with its header's time stamp as `-`: the same project builds into the same bytes
|
|
103
|
+
* (the line stays, for the source map's count of lines). */
|
|
104
|
+
export const stableBundle = (js: string): string => js.replace(/^\/\/ last-updated: .*$/m, "// last-updated: -")
|
|
105
|
+
|
|
106
|
+
/** The plugins a bundle's header lists (`// plugins: ar;camera`): the ones its page carries. */
|
|
107
|
+
export const listedPlugins = (js: string): Set<string> =>
|
|
108
|
+
new Set((/^\/\/ plugins: (.+)$/m.exec(js.slice(0, 2000))?.[1] ?? "").split(";").map((id) => id.trim()).filter(Boolean))
|
|
109
|
+
|
|
110
|
+
/** A project's web half, built (web/halves.ts): what the page carries for a plugin it lists. */
|
|
111
|
+
export type BuiltHalf = { half: ProjectHalf, js: string }
|
|
112
|
+
|
|
113
|
+
/** The project's own web halves of the plugins the bundle lists, built — before anything is
|
|
114
|
+
* written: one that does not build throws, and the folder of the last build stays as it was. */
|
|
115
|
+
export const buildListedHalves = async (root: string, js: string, opts: { minify?: boolean } = {}): Promise<BuiltHalf[]> => {
|
|
116
|
+
const listed = listedPlugins(js)
|
|
117
|
+
const halves: BuiltHalf[] = []
|
|
118
|
+
for (const half of projectHalves(root)) if (listed.has(half.id)) halves.push({ half, js: await buildHalf(half, { minify: opts.minify }) })
|
|
119
|
+
return halves
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type LitePageOptions = {
|
|
123
|
+
/** The bundle's text: what decides the features and the plugins the page keeps. */
|
|
124
|
+
js: string
|
|
125
|
+
/** The bundle as the page names it: `app.<hash>.js` (relative — `web build`), `/app.js` (a host
|
|
126
|
+
* of its own — the deploy worker). */
|
|
127
|
+
bundleSrc: string
|
|
128
|
+
/** app.json's `web`. */
|
|
129
|
+
web: WebConfig
|
|
130
|
+
/** The page's title when `web.title` names none. */
|
|
131
|
+
name: string
|
|
132
|
+
/** app.json's `icon`: the file (absolute) and what app.json says (for the warning); null = none. */
|
|
133
|
+
icon: { file: string, declared: string } | null
|
|
134
|
+
/** The project's halves of the plugins the bundle lists (buildListedHalves). */
|
|
135
|
+
halves: BuiltHalf[]
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The page of Web Lite for one bundle, into `out` (an existing, empty folder): Web Lite's dist
|
|
140
|
+
* (`dist`) copied, the features and fonts the bundle never names pruned, the plugins it does not
|
|
141
|
+
* list dropped and the project's halves put beside the first-party ones (plugins.json), and
|
|
142
|
+
* `index.html` from the dist's (title, theme color, icon, the bundle's src; `<base>` and `404.html`
|
|
143
|
+
* in path mode). The bundle and the project's files are the caller's (copyNamedAssets after this).
|
|
144
|
+
* `lecodes web build` and the deploy worker (tools/worker) both build their folder through this: a
|
|
145
|
+
* project the platform deploys gets the page `web build` would have written. Returns its warnings.
|
|
146
|
+
*/
|
|
147
|
+
export const assembleLitePage = (dist: string, out: string, opts: LitePageOptions): string[] => {
|
|
148
|
+
const { js, web: config, halves } = opts
|
|
108
149
|
const mode = config.urls === "path" ? "path" : "hash"
|
|
109
150
|
const base = normalBase(config.base)
|
|
110
151
|
const warnings: string[] = []
|
|
111
|
-
|
|
112
|
-
// ---- the bundle: every asset a url relative to the page ------------------------------------------------
|
|
113
|
-
const assetUrl = (path: string) => "assets" + encodeURI(path)
|
|
114
|
-
const collected = collectLocalEntries(root, manifest, "assets", { localShaders: true })
|
|
115
|
-
const entries = collected.entries.filter((e) => {
|
|
116
|
-
// what a build made is no part of the project
|
|
117
|
-
const abs = e.absPath ? resolve(e.absPath) : null
|
|
118
|
-
return !(abs && (abs === out || abs.startsWith(out + "\\") || abs.startsWith(out + "/")))
|
|
119
|
-
})
|
|
120
|
-
warnings.push(...collected.warnings)
|
|
121
|
-
// A shader's artifact is named by a hash of its text (a hosting keeps a file of one name for
|
|
122
|
-
// good). The bundle builds the name at run time from the `_opengl` template (compileProject):
|
|
123
|
-
// on the page that is `<hash>_webgl-lite.filamat`, the only artifact written.
|
|
124
|
-
const shaders = new Map<string, string>() // hash → the lite artifact's JSON
|
|
125
|
-
for (const entry of entries) {
|
|
126
|
-
if (entry.type !== "shader" || !entry.absPath) continue
|
|
127
|
-
const text = readFileSync(entry.absPath, "utf8")
|
|
128
|
-
const hash = createHash("sha256").update(text).digest("hex").slice(0, 16)
|
|
129
|
-
shaders.set(hash, JSON.stringify(buildLiteMaterialArtifact(text)))
|
|
130
|
-
entry.shaders = [{ platform: "opengl", src: `shaders/${hash}_opengl.filamat` }]
|
|
131
|
-
}
|
|
132
|
-
const fontsHeader = await applyProjectFonts(entries, {
|
|
133
|
-
mode: "vendor", root, vendorDir: root, vendorPrefix: "", publicUrl: "", relative: true,
|
|
134
|
-
makeResourceEntry: (path, absPath) => ({ path, type: "resource", fileSrc: assetUrl(path), absPath }),
|
|
135
|
-
})
|
|
136
|
-
const iconsHeader = await applyAppAssetIcons(entries)
|
|
137
|
-
if (!options.entry) {
|
|
138
|
-
const ambiguous = ambiguousEntryWarning(entries)
|
|
139
|
-
if (ambiguous) warnings.push(ambiguous)
|
|
140
|
-
}
|
|
141
|
-
const compiled = await compileProject({
|
|
142
|
-
entries, name, publicUrl: "", entryOverride: options.entry, header: true, fontsHeader, iconsHeader,
|
|
143
|
-
sourcemap: options.sourcemap === true, minify: options.minify,
|
|
144
|
-
onWarning: (w) => warnings.push(w),
|
|
145
|
-
})
|
|
146
|
-
// The same project builds into the same bytes: the bundle's name is its hash, and a hosting
|
|
147
|
-
// keeps a file of that name for good. The header's time stamp is the one line that would
|
|
148
|
-
// differ (the line stays, for the source map's count of lines).
|
|
149
|
-
const js = compiled.replace(/^\/\/ last-updated: .*$/m, "// last-updated: -")
|
|
150
|
-
const header = js.slice(0, 2000)
|
|
151
|
-
if (/^\/\/ 2d: enable/m.test(header)) {
|
|
152
|
+
if (/^\/\/ 2d: enable/m.test(js.slice(0, 2000))) {
|
|
152
153
|
warnings.push("This project uses 2D scenes, which Web Lite does not show yet: the page runs its UI and its 3D, a 2D scene's calls fail. (`lecodes dev --web` runs the whole app on the web player.)")
|
|
153
154
|
}
|
|
155
|
+
const listed = listedPlugins(js)
|
|
154
156
|
|
|
155
|
-
|
|
156
|
-
// that does not build leaves the last build as it was
|
|
157
|
-
const listed = new Set((/^\/\/ plugins: (.+)$/m.exec(header)?.[1] ?? "").split(";").map((id) => id.trim()).filter(Boolean))
|
|
158
|
-
const halves: { half: ProjectHalf, js: string }[] = []
|
|
159
|
-
for (const half of projectHalves(root)) if (listed.has(half.id)) halves.push({ half, js: await buildHalf(half, { minify: options.minify }) })
|
|
160
|
-
|
|
161
|
-
// ---- the folder ----------------------------------------------------------------------------------------------
|
|
162
|
-
rmSync(out, { recursive: true, force: true })
|
|
163
|
-
mkdirSync(out, { recursive: true })
|
|
164
|
-
cpSync(lite, out, { recursive: true })
|
|
157
|
+
cpSync(dist, out, { recursive: true })
|
|
165
158
|
// the features this bundle never names are not part of its page
|
|
166
159
|
const table = join(out, "features.json")
|
|
167
160
|
if (existsSync(table)) {
|
|
@@ -200,44 +193,23 @@ export const buildWeb = async (options: WebBuildOptions): Promise<WebBuildResult
|
|
|
200
193
|
else { rmSync(pluginsTable, { force: true }); rmSync(join(out, "plugins"), { recursive: true, force: true }) }
|
|
201
194
|
}
|
|
202
195
|
|
|
203
|
-
const bundle = `app.${createHash("sha256").update(js).digest("hex").slice(0, 10)}.js`
|
|
204
|
-
writeFileSync(join(out, bundle), js)
|
|
205
|
-
|
|
206
|
-
// the project's assets: the ones the bundle names
|
|
207
|
-
for (const entry of entries) {
|
|
208
|
-
if (entry.type !== "resource" || !entry.absPath || !entry.fileSrc) continue
|
|
209
|
-
if (!js.includes(entry.fileSrc)) continue
|
|
210
|
-
const to = join(out, "assets", ...entry.path.split("/").filter(Boolean))
|
|
211
|
-
if (existsSync(to)) { warnings.push(`${entry.path}: a file of the page has this name in the built folder — the project's file was not copied`); continue }
|
|
212
|
-
mkdirSync(dirname(to), { recursive: true })
|
|
213
|
-
copyFileSync(entry.absPath, to)
|
|
214
|
-
}
|
|
215
|
-
// …and its shaders: the ones the bundle names
|
|
216
|
-
for (const [hash, artifact] of shaders) {
|
|
217
|
-
if (!js.includes(`shaders/${hash}_`)) continue
|
|
218
|
-
mkdirSync(join(out, "shaders"), { recursive: true })
|
|
219
|
-
writeFileSync(join(out, "shaders", `${hash}_webgl-lite.filamat`), artifact)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
196
|
// ---- the page ----------------------------------------------------------------------------------------------
|
|
223
|
-
|
|
224
|
-
let html = readFileSync(join(lite, "index.html"), "utf8")
|
|
197
|
+
let html = readFileSync(join(dist, "index.html"), "utf8")
|
|
225
198
|
const head: string[] = []
|
|
226
199
|
head.push(`<meta name="theme-color" content="${escapeHtml(config.themeColor ?? "#000000")}" />`)
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
copyFileSync(icon, join(out, file))
|
|
200
|
+
if (opts.icon) {
|
|
201
|
+
if (existsSync(opts.icon.file)) {
|
|
202
|
+
const file = `icon${extname(opts.icon.file).toLowerCase() || ".png"}`
|
|
203
|
+
copyFileSync(opts.icon.file, join(out, file))
|
|
232
204
|
head.push(`<link rel="icon" href="${file}" />`, `<link rel="apple-touch-icon" href="${file}" />`)
|
|
233
205
|
} else {
|
|
234
|
-
warnings.push(`app.json icon "${
|
|
206
|
+
warnings.push(`app.json icon "${opts.icon.declared}" is not there — the page has no icon`)
|
|
235
207
|
}
|
|
236
208
|
}
|
|
237
209
|
// in path mode a route is a path: what the page names relatively is under the base, whatever the route
|
|
238
210
|
if (mode === "path") head.unshift(`<base href="${escapeHtml(base)}" />`)
|
|
239
211
|
const attrs = [
|
|
240
|
-
`data-src="${
|
|
212
|
+
`data-src="${escapeHtml(opts.bundleSrc)}"`,
|
|
241
213
|
...(mode === "path" ? [`data-urls="path"`, `data-base="${escapeHtml(base)}"`] : []),
|
|
242
214
|
...(config.font === "system" ? [`data-font="system"`] : []),
|
|
243
215
|
].join(" ")
|
|
@@ -246,12 +218,101 @@ export const buildWeb = async (options: WebBuildOptions): Promise<WebBuildResult
|
|
|
246
218
|
if (next === html) throw new CliError(`The page of Web Lite has no ${String(from)} to fill — this CLI and its page are of different versions.`)
|
|
247
219
|
html = next
|
|
248
220
|
}
|
|
249
|
-
replace(/<title>[^<]*<\/title>/, `<title>${escapeHtml(config.title ??
|
|
221
|
+
replace(/<title>[^<]*<\/title>/, `<title>${escapeHtml(config.title ?? opts.name)}</title>`)
|
|
250
222
|
replace(`<div id="app"></div>`, `<div id="app" ${attrs}></div>`)
|
|
251
223
|
replace("</head>", ` ${head.join("\n ")}\n </head>`)
|
|
252
224
|
if (config.lang) replace(/<html lang="[^"]*">/, `<html lang="${escapeHtml(config.lang)}">`)
|
|
253
225
|
writeFileSync(join(out, "index.html"), html)
|
|
254
226
|
if (mode === "path") writeFileSync(join(out, "404.html"), html)
|
|
227
|
+
return warnings
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** The project's resources the bundle names (`js` holds their `fileSrc`), copied to
|
|
231
|
+
* `out/assets/<path>` — after the page (assembleLitePage): a file of the page keeps a name both
|
|
232
|
+
* have. Returns its warnings. */
|
|
233
|
+
export const copyNamedAssets = (entries: CompileEntry[], js: string, out: string): string[] => {
|
|
234
|
+
const warnings: string[] = []
|
|
235
|
+
for (const entry of entries) {
|
|
236
|
+
if (entry.type !== "resource" || !entry.absPath || !entry.fileSrc) continue
|
|
237
|
+
if (!js.includes(entry.fileSrc)) continue
|
|
238
|
+
const to = join(out, "assets", ...entry.path.split("/").filter(Boolean))
|
|
239
|
+
if (existsSync(to)) { warnings.push(`${entry.path}: a file of the page has this name in the built folder — the project's file was not copied`); continue }
|
|
240
|
+
mkdirSync(dirname(to), { recursive: true })
|
|
241
|
+
copyFileSync(entry.absPath, to)
|
|
242
|
+
}
|
|
243
|
+
return warnings
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export const buildWeb = async (options: WebBuildOptions): Promise<WebBuildResult> => {
|
|
247
|
+
const { root, manifest, name, lite } = options
|
|
248
|
+
const out = resolve(root, options.out)
|
|
249
|
+
if (out === resolve(root) || resolve(root).startsWith(out + "\\") || resolve(root).startsWith(out + "/")) {
|
|
250
|
+
throw new CliError(`The output folder is cleaned before a build: "${options.out}" holds the project itself.`)
|
|
251
|
+
}
|
|
252
|
+
const config: WebConfig = (readAppConfigAt(root) as { web?: WebConfig } | null)?.web ?? {}
|
|
253
|
+
const warnings: string[] = []
|
|
254
|
+
|
|
255
|
+
// ---- the bundle: every asset a url relative to the page ------------------------------------------------
|
|
256
|
+
const assetUrl = (path: string) => "assets" + encodeURI(path)
|
|
257
|
+
const collected = collectLocalEntries(root, manifest, "assets", { localShaders: true })
|
|
258
|
+
const entries = collected.entries.filter((e) => {
|
|
259
|
+
// what a build made is no part of the project
|
|
260
|
+
const abs = e.absPath ? resolve(e.absPath) : null
|
|
261
|
+
return !(abs && (abs === out || abs.startsWith(out + "\\") || abs.startsWith(out + "/")))
|
|
262
|
+
})
|
|
263
|
+
warnings.push(...collected.warnings)
|
|
264
|
+
// A shader's artifact is named by a hash of its text (a hosting keeps a file of one name for
|
|
265
|
+
// good). The bundle builds the name at run time from the `_opengl` template (compileProject):
|
|
266
|
+
// on the page that is `<hash>_webgl-lite.filamat`, the only artifact written.
|
|
267
|
+
const shaders = new Map<string, string>() // hash → the lite artifact's JSON
|
|
268
|
+
for (const entry of entries) {
|
|
269
|
+
if (entry.type !== "shader" || !entry.absPath) continue
|
|
270
|
+
const text = readFileSync(entry.absPath, "utf8")
|
|
271
|
+
const hash = createHash("sha256").update(text).digest("hex").slice(0, 16)
|
|
272
|
+
shaders.set(hash, JSON.stringify(buildLiteMaterialArtifact(text)))
|
|
273
|
+
entry.shaders = [{ platform: "opengl", src: `shaders/${hash}_opengl.filamat` }]
|
|
274
|
+
}
|
|
275
|
+
const fontsHeader = await applyProjectFonts(entries, {
|
|
276
|
+
mode: "vendor", root, vendorDir: root, vendorPrefix: "", publicUrl: "", relative: true,
|
|
277
|
+
makeResourceEntry: (path, absPath) => ({ path, type: "resource", fileSrc: assetUrl(path), absPath }),
|
|
278
|
+
})
|
|
279
|
+
const iconsHeader = await applyAppAssetIcons(entries)
|
|
280
|
+
if (!options.entry) {
|
|
281
|
+
const ambiguous = ambiguousEntryWarning(entries)
|
|
282
|
+
if (ambiguous) warnings.push(ambiguous)
|
|
283
|
+
}
|
|
284
|
+
const compiled = await compileProject({
|
|
285
|
+
entries, name, publicUrl: "", entryOverride: options.entry, header: true, fontsHeader, iconsHeader,
|
|
286
|
+
sourcemap: options.sourcemap === true, minify: options.minify, env: readAppEnv(root),
|
|
287
|
+
onWarning: (w) => warnings.push(w),
|
|
288
|
+
})
|
|
289
|
+
// The same project builds into the same bytes: the bundle's name is its hash, and a hosting
|
|
290
|
+
// keeps a file of that name for good.
|
|
291
|
+
const js = stableBundle(compiled)
|
|
292
|
+
|
|
293
|
+
// the project's own web halves of the plugins it lists, built before the folder is cleaned: one
|
|
294
|
+
// that does not build leaves the last build as it was
|
|
295
|
+
const halves = await buildListedHalves(root, js, { minify: options.minify })
|
|
296
|
+
|
|
297
|
+
// ---- the folder ----------------------------------------------------------------------------------------------
|
|
298
|
+
rmSync(out, { recursive: true, force: true })
|
|
299
|
+
mkdirSync(out, { recursive: true })
|
|
300
|
+
const bundle = `app.${createHash("sha256").update(js).digest("hex").slice(0, 10)}.js`
|
|
301
|
+
const app = readAppConfigAt(root)
|
|
302
|
+
warnings.push(...assembleLitePage(lite, out, {
|
|
303
|
+
js, bundleSrc: bundle, web: config, name: app?.name ?? name, halves,
|
|
304
|
+
icon: app?.icon ? { file: resolve(root, app.icon), declared: app.icon } : null,
|
|
305
|
+
}))
|
|
306
|
+
writeFileSync(join(out, bundle), js)
|
|
307
|
+
|
|
308
|
+
// the project's assets: the ones the bundle names
|
|
309
|
+
warnings.push(...copyNamedAssets(entries, js, out))
|
|
310
|
+
// …and its shaders: the ones the bundle names
|
|
311
|
+
for (const [hash, artifact] of shaders) {
|
|
312
|
+
if (!js.includes(`shaders/${hash}_`)) continue
|
|
313
|
+
mkdirSync(join(out, "shaders"), { recursive: true })
|
|
314
|
+
writeFileSync(join(out, "shaders", `${hash}_webgl-lite.filamat`), artifact)
|
|
315
|
+
}
|
|
255
316
|
|
|
256
317
|
return { out, bundle, files: walk(out), warnings }
|
|
257
318
|
}
|
|
Binary file
|
|
Binary file
|