@downdraft/cli 0.4.1 → 0.4.3
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@downdraft/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/knackstedt/downdraft.git",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@aws-sdk/client-s3": "^3.700.0",
|
|
15
|
-
"@downdraft/engine": "^0.4.
|
|
15
|
+
"@downdraft/engine": "^0.4.3",
|
|
16
16
|
"eta": "^3.5.0",
|
|
17
17
|
"jimp": "^1.6.1",
|
|
18
18
|
"resedit": "3.1.0"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// ============================================================================
|
|
11
11
|
|
|
12
12
|
import { execFileSync } from "node:child_process";
|
|
13
|
-
import { cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
14
14
|
import { tmpdir } from "node:os";
|
|
15
15
|
import { dirname, join } from "node:path";
|
|
16
16
|
|
|
@@ -31,7 +31,14 @@ export async function resolvePlatLibDir(platKey, candidates, cacheRoot, version)
|
|
|
31
31
|
|
|
32
32
|
const pkgName = `@downdraft/native-${platKey}`;
|
|
33
33
|
const cacheDir = join(cacheRoot, "downdraft-native", platKey, "lib");
|
|
34
|
-
|
|
34
|
+
const marker = join(cacheRoot, "downdraft-native", platKey, ".version");
|
|
35
|
+
// Cache hit is only valid if it holds the lockstep version — a versionless
|
|
36
|
+
// cache would silently ship stale cdylibs after an engine bump. No marker
|
|
37
|
+
// (pre-versioning cache) → refetch.
|
|
38
|
+
if (existsSync(cacheDir) && readdirSync(cacheDir).length > 0) {
|
|
39
|
+
const cached = existsSync(marker) ? readFileSync(marker, "utf8").trim() : null;
|
|
40
|
+
if (!version || cached === version) return cacheDir;
|
|
41
|
+
}
|
|
35
42
|
|
|
36
43
|
try {
|
|
37
44
|
const meta = await (await fetch(
|
|
@@ -51,6 +58,7 @@ export async function resolvePlatLibDir(platKey, candidates, cacheRoot, version)
|
|
|
51
58
|
mkdirSync(dirname(cacheDir), { recursive: true });
|
|
52
59
|
// cpSync not renameSync: TMPDIR may be a different filesystem.
|
|
53
60
|
cpSync(libDir, cacheDir, { recursive: true });
|
|
61
|
+
writeFileSync(marker, ver);
|
|
54
62
|
console.log(`[package] fetched ${pkgName}@${ver} from npm → ${cacheDir}`);
|
|
55
63
|
return cacheDir;
|
|
56
64
|
} finally {
|
|
@@ -184,6 +184,14 @@ const META_DIR_RE = new RegExp(IMPORT_META.source + String.raw`\.(?:dir|dirname)
|
|
|
184
184
|
const META_URL_RE = new RegExp(IMPORT_META.source + String.raw`\.url\b`, "g");
|
|
185
185
|
const REQUIRE_GLOB_RE = /require\(\s*["'`]@downdraft\/engine\/platform\/glob-polyfill["'`]\s*\)/g;
|
|
186
186
|
const GLOB_CALLEE_RE = new RegExp(IMPORT_META.source + String.raw`\.glob`);
|
|
187
|
+
// globAssets(pattern, import.meta, opts?) — the meta OBJECT escapes the
|
|
188
|
+
// META_DIR_RE/META_URL_RE token rewrites (its .dir/.url are read inside
|
|
189
|
+
// the engine helper). For emit runtimes the real import.meta points into
|
|
190
|
+
// the bundle tree, not dd-assets; for bun-compiled binaries it flattens
|
|
191
|
+
// to /$bunfs/root — either way every asset glob misses. The meta argument
|
|
192
|
+
// is swapped for a synthetic object bound to the module's staged dir.
|
|
193
|
+
const GLOB_ASSETS_META_RE = new RegExp(
|
|
194
|
+
`\\bglobAssets\\(\\s*(["'\`][^"'\`]*["'\`])\\s*,\\s*${IMPORT_META.source}(\\s*,)?`, "g");
|
|
187
195
|
const SCRIPT_EXT_RE = /\.(ts|tsx|js|jsx|mts|cts|cjs)$/i; // .mjs staged as an asset (execArgv loaders)
|
|
188
196
|
const DYN_IMPORT_RE = /import\(\s*([a-zA-Z_$][\w$]*)\s*\)/g;
|
|
189
197
|
|
|
@@ -433,6 +441,12 @@ const makeEmitPlugin = (collectWorkers) => ({
|
|
|
433
441
|
needsGlob = true;
|
|
434
442
|
out = out.replace(REQUIRE_GLOB_RE, "({ createGlob: __ddCreateGlob })");
|
|
435
443
|
}
|
|
444
|
+
// 3c. globAssets(pattern, import.meta, ...) — bind the meta object to
|
|
445
|
+
// the staged dir so caller-relative globs resolve under dd-assets.
|
|
446
|
+
out = out.replace(GLOB_ASSETS_META_RE, (m, p1, comma) => {
|
|
447
|
+
needsGlob = true;
|
|
448
|
+
return `globAssets(${p1}, { dir: __ddModDir, url: __ddModUrl, glob: __ddGlob }${comma ?? ""}`;
|
|
449
|
+
});
|
|
436
450
|
// 4. import.meta.dir/.url/__dirname → module's staged dir under
|
|
437
451
|
// dd-assets (real FS).
|
|
438
452
|
const metaOut = out
|
|
@@ -660,6 +674,8 @@ if (runtime === "bun") {
|
|
|
660
674
|
`SELF="$(readlink -f "$0" 2>/dev/null || realpath "$0" 2>/dev/null || echo "$0")"\n` +
|
|
661
675
|
`DIR="$(dirname "$SELF")"\n` +
|
|
662
676
|
`export DD_APP_ID="${appId}"\n` +
|
|
677
|
+
`export DOWNDRAFT_PACKAGED=1\n` +
|
|
678
|
+
`export DOWNDRAFT_BUNDLE_DIR="$DIR"\n` +
|
|
663
679
|
(iconPath ? `export DD_APP_ICON="$DIR/icon.png"\n` : "") +
|
|
664
680
|
`exec "$DIR/${runtime}" ${rtArgs} "$DIR/bundle/index.js" "$@"\n`);
|
|
665
681
|
chmodSync(join(appdir, exeName), 0o755);
|
|
@@ -713,9 +729,13 @@ if (runtime !== "bun") {
|
|
|
713
729
|
}
|
|
714
730
|
|
|
715
731
|
// build.files — electron-builder-style extra files into the appdir root.
|
|
732
|
+
// `dir/**/*` entries resolve to the dir itself (matched files keep their
|
|
733
|
+
// repo-relative structure under <appdir>/dir/).
|
|
716
734
|
for (const f of (Array.isArray(build.files) ? build.files : []).values()) {
|
|
717
|
-
const
|
|
718
|
-
const
|
|
735
|
+
const from = typeof f === "string" ? f : f.from;
|
|
736
|
+
const rel = from.replace(/\/\*\*?\/\*?$/, "").replace(/\/+$/, "");
|
|
737
|
+
const src = resolve(gameDir, rel);
|
|
738
|
+
const dest = join(appdir, typeof f === "string" ? rel : (f.to ?? rel));
|
|
719
739
|
if (!existsSync(src)) { console.warn(`[package-desktop] files entry missing: ${src}`); continue; }
|
|
720
740
|
cpSync(src, dest, { recursive: true });
|
|
721
741
|
}
|
|
@@ -163,6 +163,13 @@ const META_DIR_RE = new RegExp(IMPORT_META.source + String.raw`\.(?:dir|dirname)
|
|
|
163
163
|
const META_URL_RE = new RegExp(IMPORT_META.source + String.raw`\.url\b`, "g");
|
|
164
164
|
const REQUIRE_GLOB_RE = /require\(\s*["'`]@downdraft\/engine\/platform\/glob-polyfill["'`]\s*\)/g;
|
|
165
165
|
const GLOB_CALLEE_RE = new RegExp(IMPORT_META.source + String.raw`\.glob`);
|
|
166
|
+
// globAssets(pattern, import.meta, opts?) — the meta OBJECT escapes the
|
|
167
|
+
// META_DIR_RE/META_URL_RE token rewrites (its .dir/.url are read inside
|
|
168
|
+
// the engine helper); the real import.meta points into the bundle tree,
|
|
169
|
+
// not dd-assets, so every asset glob would miss. The meta argument is
|
|
170
|
+
// swapped for a synthetic object bound to the module's staged dir.
|
|
171
|
+
const GLOB_ASSETS_META_RE = new RegExp(
|
|
172
|
+
`\\bglobAssets\\(\\s*(["'\`][^"'\`]*["'\`])\\s*,\\s*${IMPORT_META.source}(\\s*,)?`, "g");
|
|
166
173
|
const SCRIPT_EXT_RE = /\.(ts|tsx|js|jsx|mts|cts|cjs)$/i; // .mjs staged as an asset (execArgv loaders)
|
|
167
174
|
const DYN_IMPORT_RE = /import\(\s*([a-zA-Z_$][\w$]*)\s*\)/g;
|
|
168
175
|
|
|
@@ -410,6 +417,12 @@ const makePlugin = (collectWorkers) => ({
|
|
|
410
417
|
needsGlob = true;
|
|
411
418
|
out = out.replace(REQUIRE_GLOB_RE, "({ createGlob: __ddCreateGlob })");
|
|
412
419
|
}
|
|
420
|
+
// 3c. globAssets(pattern, import.meta, ...) — bind the meta object to
|
|
421
|
+
// the staged dir so caller-relative globs resolve under dd-assets.
|
|
422
|
+
out = out.replace(GLOB_ASSETS_META_RE, (m, p1, comma) => {
|
|
423
|
+
needsGlob = true;
|
|
424
|
+
return `globAssets(${p1}, { dir: __ddModDir, url: __ddModUrl, glob: __ddGlob }${comma ?? ""}`;
|
|
425
|
+
});
|
|
413
426
|
// 4. import.meta.dir/.url/__dirname → module's staged dir under
|
|
414
427
|
// dd-assets (real FS after extraction).
|
|
415
428
|
const metaOut = out
|
|
@@ -107,6 +107,13 @@ let outfile = outfileArg;
|
|
|
107
107
|
if (target.exe && !outfile.endsWith(".exe")) outfile += ".exe";
|
|
108
108
|
|
|
109
109
|
const repoRoot = process.cwd();
|
|
110
|
+
// Bun embeds compile-mode modules keyed relative to the common ancestor of
|
|
111
|
+
// all ENTRYPOINT paths — which is NOT necessarily cwd (a game inside games/
|
|
112
|
+
// whose deps resolve under its own dir gets game-dir keys even when this
|
|
113
|
+
// script runs from the monorepo root). Baked $bunfs specifiers must be
|
|
114
|
+
// relative to that same root or workers/dynamic imports miss the embedded FS.
|
|
115
|
+
// Set after the worker-discovery fixpoint, before the compile pass.
|
|
116
|
+
let embedRoot = repoRoot;
|
|
110
117
|
const engineConds = engineConditions(argv, resolve(entry));
|
|
111
118
|
const QUERY_RE = /\?(raw|url|json)$/;
|
|
112
119
|
const WORKER_RE = /new\s+Worker\(\s*new\s+URL\(\s*["'`]([^"'`]+)["'`]\s*,\s*import\.meta\.url\s*\)/g;
|
|
@@ -130,6 +137,14 @@ const META_URL_RE = new RegExp(IMPORT_META.source + String.raw`\.url\b`, "g");
|
|
|
130
137
|
// injected __ddCreateGlob binding.
|
|
131
138
|
const REQUIRE_GLOB_RE = /require\(\s*["'`]@downdraft\/engine\/platform\/glob-polyfill["'`]\s*\)/g;
|
|
132
139
|
const GLOB_CALLEE_RE = new RegExp(IMPORT_META.source + String.raw`\.glob`);
|
|
140
|
+
// globAssets(pattern, import.meta, opts?) — the meta OBJECT escapes the
|
|
141
|
+
// META_DIR_RE/META_URL_RE token rewrites (its .dir/.url are read inside
|
|
142
|
+
// the engine helper), and in compiled binaries import.meta.dir flattens
|
|
143
|
+
// to /$bunfs/root for every module — the helper would glob a nonexistent
|
|
144
|
+
// root and return {} for every asset query. The meta argument is swapped
|
|
145
|
+
// for a synthetic object bound to the module's staged dir.
|
|
146
|
+
const GLOB_ASSETS_META_RE = new RegExp(
|
|
147
|
+
`\\bglobAssets\\(\\s*(["'\`][^"'\`]*["'\`])\\s*,\\s*${IMPORT_META.source}(\\s*,)?`, "g");
|
|
133
148
|
const SCRIPT_EXT_RE = /\.(ts|tsx|js|jsx|mts|mjs|cts|cjs)$/i;
|
|
134
149
|
const DYN_IMPORT_RE = /import\(\s*([a-zA-Z_$][\w$]*)\s*\)/g;
|
|
135
150
|
|
|
@@ -146,7 +161,7 @@ function resolveSpec(spec, importer) {
|
|
|
146
161
|
|
|
147
162
|
/** repo-rel path of a module file as embedded in the binary (.js suffix). */
|
|
148
163
|
function bunfsPath(absPath) {
|
|
149
|
-
const rel = relative(
|
|
164
|
+
const rel = relative(embedRoot, absPath).replace(/\\/g, "/").replace(/\.[jt]sx?$/, ".js");
|
|
150
165
|
// Emitted inside a quoted literal for dynamic import() — Windows needs the
|
|
151
166
|
// B:\~BUN drive path (with escaped separators); /$bunfs doesn't resolve.
|
|
152
167
|
return isWin
|
|
@@ -338,7 +353,7 @@ const makePlugin = (collectWorkers) => ({
|
|
|
338
353
|
let out = src;
|
|
339
354
|
// 1. Worker URLs first — before import.meta.url is rewritten.
|
|
340
355
|
out = out.replace(WORKER_RE, (m, spec) => {
|
|
341
|
-
const rel = relative(
|
|
356
|
+
const rel = relative(embedRoot, resolve(modDir, spec))
|
|
342
357
|
.replace(/\\/g, "/").replace(/\.[jt]sx?$/, ".js");
|
|
343
358
|
// Lands inside new URL(...) — Windows wants a file: URL into the
|
|
344
359
|
// B:\~BUN embedded root; the /$bunfs POSIX form misses it.
|
|
@@ -368,6 +383,12 @@ const makePlugin = (collectWorkers) => ({
|
|
|
368
383
|
needsGlob = true;
|
|
369
384
|
out = out.replace(REQUIRE_GLOB_RE, "({ createGlob: __ddCreateGlob })");
|
|
370
385
|
}
|
|
386
|
+
// 3c. globAssets(pattern, import.meta, ...) — bind the meta object to
|
|
387
|
+
// the staged dir so caller-relative globs resolve under dd-assets.
|
|
388
|
+
out = out.replace(GLOB_ASSETS_META_RE, (m, p1, comma) => {
|
|
389
|
+
needsGlob = true;
|
|
390
|
+
return `globAssets(${p1}, { dir: __ddModDir, url: __ddModUrl, glob: __ddGlob }${comma ?? ""}`;
|
|
391
|
+
});
|
|
371
392
|
// 4. import.meta.dir / .url / bare __dirname → the module's location
|
|
372
393
|
// inside the staged dd-assets tree (real FS — sibling asset reads,
|
|
373
394
|
// glob enumeration, and wasm/binary loads all resolve there).
|
|
@@ -428,6 +449,17 @@ while (workerEntries.size !== prevCount) {
|
|
|
428
449
|
}
|
|
429
450
|
|
|
430
451
|
const entrypoints = [resolve(entry), ...workerEntries];
|
|
452
|
+
// Bun's embedded-FS root is the common ancestor of the input paths —
|
|
453
|
+
// reproduce it so baked module paths land under the same keys.
|
|
454
|
+
embedRoot = entrypoints
|
|
455
|
+
.map((p) => dirname(p))
|
|
456
|
+
.reduce((lca, dir) => {
|
|
457
|
+
if (!lca) return dir;
|
|
458
|
+
const a = lca.split("/"), b = dir.split("/");
|
|
459
|
+
let i = 0;
|
|
460
|
+
while (i < a.length && a[i] === b[i]) i++;
|
|
461
|
+
return a.slice(0, i).join("/") || "/";
|
|
462
|
+
}, null);
|
|
431
463
|
if (workerEntries.size) {
|
|
432
464
|
console.log(`workers: ${[...workerEntries].map((p) => p.split("/").pop()).join(", ")}`);
|
|
433
465
|
}
|
package/src/release.ts
CHANGED
|
@@ -256,7 +256,12 @@ function packageNative(
|
|
|
256
256
|
// appimage, flatpak). Formats come from --format → build.linux.target
|
|
257
257
|
// → "dir".
|
|
258
258
|
const format = opts.format
|
|
259
|
-
?? (Array.isArray(info.build.linux?.target)
|
|
259
|
+
?? (Array.isArray(info.build.linux?.target)
|
|
260
|
+
? info.build.linux.target
|
|
261
|
+
.map((t: unknown) => (typeof t === "string" ? t : (t as { target?: string }).target ?? ""))
|
|
262
|
+
.filter(Boolean)
|
|
263
|
+
.join(",")
|
|
264
|
+
: info.build.linux?.target)
|
|
260
265
|
?? "dir";
|
|
261
266
|
const argv = [
|
|
262
267
|
scriptDesktop,
|