@zenbujs/core 0.0.5 → 0.0.8
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/{advice-config-QYB2qEd_.mjs → advice-config-DXSIo0sg.mjs} +40 -39
- package/dist/advice.d.mts +8 -8
- package/dist/advice.mjs +2 -2
- package/dist/{base-window-BbFRRhKP.mjs → base-window-BxBZ2md_.mjs} +51 -7
- package/dist/{transforms-CuTODvDx.d.mts → build-config-Dzg2frpk.d.mts} +98 -28
- package/dist/build-config-pWdmLnrk.mjs +53 -0
- package/dist/{build-electron-CNJ0dLND.mjs → build-electron-Dsbb1EMl.mjs} +308 -120
- package/dist/{build-source-C2puqEVr.mjs → build-source-d1J3shV8.mjs} +62 -27
- package/dist/cli/bin.mjs +7 -7
- package/dist/cli/build.d.mts +2 -2
- package/dist/cli/build.mjs +2 -3
- package/dist/cli/resolve-config.mjs +1 -1
- package/dist/{cli-C3R1LBMY.mjs → cli-kL6mPgBE.mjs} +2 -2
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +2 -3
- package/dist/{db-xjvahRFJ.mjs → db-Bc292RYo.mjs} +2 -2
- package/dist/db.d.mts +1 -1
- package/dist/dev-B2emj0HZ.mjs +301 -0
- package/dist/env-bootstrap.d.mts +1 -1
- package/dist/events.d.mts +19 -0
- package/dist/events.mjs +1 -0
- package/dist/host-version-BIrF8tX7.mjs +65 -0
- package/dist/index-w5QyDjuf.d.mts +780 -0
- package/dist/index.d.mts +5 -6
- package/dist/index.mjs +2 -2
- package/dist/installing-preload.cjs +60 -0
- package/dist/launcher.mjs +2615 -122
- package/dist/{link-c0_aLWQ3.mjs → link-glX89NV5.mjs} +215 -89
- package/dist/{load-config-xMf2wxH8.mjs → load-config-C4Oe2qZO.mjs} +5 -1
- package/dist/loaders/zenbu.mjs +102 -0
- package/dist/node-loader.mjs +1 -1
- package/dist/{publish-source-Dill72NS.mjs → publish-source-Dq2c0iOw.mjs} +2 -2
- package/dist/react.d.mts +55 -6
- package/dist/react.mjs +116 -5
- package/dist/registry-CMp8FYgS.d.mts +47 -0
- package/dist/registry-generated.d.mts +26 -0
- package/dist/registry-generated.mjs +1 -0
- package/dist/registry.d.mts +2 -2
- package/dist/{reloader-DzEO8kJr.mjs → reloader-B22UiNA2.mjs} +2 -4
- package/dist/{renderer-host-Cau9JK0v.mjs → renderer-host-DD16MXhI.mjs} +152 -43
- package/dist/{rpc-JfGv-Wuw.mjs → rpc-C4_NQmpT.mjs} +5 -4
- package/dist/{runtime-pCeVzj--.d.mts → runtime-BQWntcOb.d.mts} +85 -48
- package/dist/runtime.d.mts +2 -2
- package/dist/runtime.mjs +139 -83
- package/dist/{schema-Dl85YjXW.d.mts → schema-CjrMVk36.d.mts} +3 -3
- package/dist/schema.d.mts +1 -1
- package/dist/schema.mjs +1 -1
- package/dist/{server-y3PPbh3l.mjs → server-CZLMF8Dj.mjs} +1 -3
- package/dist/services/default.d.mts +3 -3
- package/dist/services/default.mjs +14 -13
- package/dist/services/index.d.mts +2 -280
- package/dist/services/index.mjs +8 -7
- package/dist/setup-gate.d.mts +1 -1
- package/dist/setup-gate.mjs +117 -24
- package/dist/{transform-CmFYPmt8.mjs → transform-BzrwkEdf.mjs} +22 -916
- package/dist/updater-DCkz9M1c.mjs +1008 -0
- package/dist/{vite-plugins-Do7liKi_.mjs → vite-plugins-tt6KAtyE.mjs} +26 -25
- package/dist/vite.d.mts +3 -3
- package/dist/vite.mjs +1 -1
- package/dist/{window-o2NGUsIb.mjs → window-YFKvAM0l.mjs} +30 -16
- package/package.json +15 -2
- package/dist/build-config-C3a-o3_B.mjs +0 -23
- package/dist/dev-Dazhu66l.mjs +0 -85
- package/dist/registry-eX6e2oql.d.mts +0 -61
- package/dist/transforms-htxfTwsY.mjs +0 -47
- /package/dist/{config-DXRCDUxG.mjs → config-BK78JDRI.mjs} +0 -0
- /package/dist/{env-bootstrap-DW2hVhSO.d.mts → env-bootstrap-rTs8KR3-.d.mts} +0 -0
- /package/dist/{index-M_lSNBrq.d.mts → index-DeDxePAa.d.mts} +0 -0
- /package/dist/{mirror-sync-PDzxhf1w.mjs → mirror-sync-pYU6f3-c.mjs} +0 -0
- /package/dist/{monorepo-3avKJwzJ.mjs → monorepo-Dct-kkbQ.mjs} +0 -0
- /package/dist/{node-_8xShqxr.mjs → node-BhfLKYCi.mjs} +0 -0
- /package/dist/{setup-gate-Dcy8gGPJ.d.mts → setup-gate-BQq0QgZH.d.mts} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { t as
|
|
1
|
+
import { r as writeHostVersion, t as HOST_VERSION_FILENAME } from "./host-version-BIrF8tX7.mjs";
|
|
2
|
+
import { t as loadConfig } from "./load-config-C4Oe2qZO.mjs";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
3
4
|
import fs from "node:fs";
|
|
4
5
|
import os from "node:os";
|
|
@@ -12,50 +13,29 @@ import { promisify } from "node:util";
|
|
|
12
13
|
//#region src/cli/lib/toolchain.ts
|
|
13
14
|
const execFileAsync = promisify(execFile);
|
|
14
15
|
/**
|
|
15
|
-
*
|
|
16
|
-
* `
|
|
17
|
-
*
|
|
18
|
-
* the build's `extraResources/toolchain/` directory.
|
|
16
|
+
* Framework-pinned bun. Used as the runtime bun (the `node`-shim that hosts
|
|
17
|
+
* `npm install`'s lifecycle scripts and JS-package PMs like npm/yarn) when
|
|
18
|
+
* the user picks a non-bun PM.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* Currently darwin-only — Linux/Windows support is a future-PR concern.
|
|
20
|
+
* When the user picks bun as the PM, we provision bun at `spec.version`
|
|
21
|
+
* instead and skip this constant entirely. The runtime bun and the PM bun
|
|
22
|
+
* collapse into a single binary at the user's chosen version.
|
|
25
23
|
*/
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
asset: "bun-darwin-x64.zip",
|
|
37
|
-
sha256: "0f58c53a3e7947f1e626d2f8d285f97c14b7cadcca9c09ebafc0ae9d35b58c3d"
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
pnpm: {
|
|
42
|
-
version: "10.33.0",
|
|
43
|
-
releaseTag: "v10.33.0",
|
|
44
|
-
targets: {
|
|
45
|
-
"darwin-arm64": {
|
|
46
|
-
asset: "pnpm-macos-arm64",
|
|
47
|
-
sha256: "ed8a1f140f4de457b01ebe0be3ae28e9a7e28863315dcd53d22ff1e5a32d63ae"
|
|
48
|
-
},
|
|
49
|
-
"darwin-x64": {
|
|
50
|
-
asset: "pnpm-macos-x64",
|
|
51
|
-
sha256: "c31e29554b0e3f4e03f4617195c949595e4dca36085922003de4896c3ca4057d"
|
|
52
|
-
}
|
|
24
|
+
const PINNED_BUN = {
|
|
25
|
+
version: "1.3.12",
|
|
26
|
+
targets: {
|
|
27
|
+
"darwin-aarch64": {
|
|
28
|
+
asset: "bun-darwin-aarch64.zip",
|
|
29
|
+
sha256: "6c4bb87dd013ed1a8d6a16e357a3d094959fd5530b4d7061f7f3680c3c7cea1c"
|
|
30
|
+
},
|
|
31
|
+
"darwin-x64": {
|
|
32
|
+
asset: "bun-darwin-x64.zip",
|
|
33
|
+
sha256: "0f58c53a3e7947f1e626d2f8d285f97c14b7cadcca9c09ebafc0ae9d35b58c3d"
|
|
53
34
|
}
|
|
54
35
|
}
|
|
55
36
|
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
function cacheRoot() {
|
|
37
|
+
PINNED_BUN.version;
|
|
38
|
+
function defaultCacheRoot() {
|
|
59
39
|
return path.join(os.homedir(), ".zenbu", "cache", "toolchain");
|
|
60
40
|
}
|
|
61
41
|
function bunTarget() {
|
|
@@ -63,11 +43,6 @@ function bunTarget() {
|
|
|
63
43
|
if (process.arch === "x64") return "darwin-x64";
|
|
64
44
|
throw new Error(`zenbu toolchain: unsupported architecture ${process.arch}`);
|
|
65
45
|
}
|
|
66
|
-
function pnpmTarget() {
|
|
67
|
-
if (process.arch === "arm64") return "darwin-arm64";
|
|
68
|
-
if (process.arch === "x64") return "darwin-x64";
|
|
69
|
-
throw new Error(`zenbu toolchain: unsupported architecture ${process.arch}`);
|
|
70
|
-
}
|
|
71
46
|
function download(url, dest) {
|
|
72
47
|
return new Promise((resolve, reject) => {
|
|
73
48
|
https.get(url, (res) => {
|
|
@@ -88,6 +63,29 @@ function download(url, dest) {
|
|
|
88
63
|
}).on("error", reject);
|
|
89
64
|
});
|
|
90
65
|
}
|
|
66
|
+
function fetchText(url) {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
https.get(url, { headers: {
|
|
69
|
+
"User-Agent": "zenbu-toolchain",
|
|
70
|
+
Accept: "application/json, text/plain, */*"
|
|
71
|
+
} }, (res) => {
|
|
72
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
73
|
+
res.resume();
|
|
74
|
+
fetchText(new URL(res.headers.location, url).href).then(resolve, reject);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (res.statusCode !== 200) {
|
|
78
|
+
reject(/* @__PURE__ */ new Error(`GET ${url} -> ${res.statusCode}`));
|
|
79
|
+
res.resume();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const chunks = [];
|
|
83
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
84
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
85
|
+
res.on("error", reject);
|
|
86
|
+
}).on("error", reject);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
91
89
|
async function sha256(filePath) {
|
|
92
90
|
const hash = crypto.createHash("sha256");
|
|
93
91
|
await new Promise((resolve, reject) => {
|
|
@@ -98,9 +96,41 @@ async function sha256(filePath) {
|
|
|
98
96
|
});
|
|
99
97
|
return hash.digest("hex");
|
|
100
98
|
}
|
|
101
|
-
async function
|
|
99
|
+
async function sha512(filePath) {
|
|
100
|
+
const hash = crypto.createHash("sha512");
|
|
101
|
+
await new Promise((resolve, reject) => {
|
|
102
|
+
const stream = fs.createReadStream(filePath);
|
|
103
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
104
|
+
stream.on("end", () => resolve());
|
|
105
|
+
stream.on("error", reject);
|
|
106
|
+
});
|
|
107
|
+
return hash.digest();
|
|
108
|
+
}
|
|
109
|
+
async function verifySha256(filePath, expected) {
|
|
102
110
|
const actual = await sha256(filePath);
|
|
103
|
-
if (actual !== expected) throw new Error(`zenbu toolchain: sha256 mismatch for ${path.basename(filePath)} (expected ${expected}, got ${actual})`);
|
|
111
|
+
if (actual.toLowerCase() !== expected.toLowerCase()) throw new Error(`zenbu toolchain: sha256 mismatch for ${path.basename(filePath)} (expected ${expected}, got ${actual})`);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Verify a file against an SRI-format integrity string from the npm registry,
|
|
115
|
+
* e.g. `sha512-LB7QO/<base64>=`. We only support the algorithms npm currently
|
|
116
|
+
* publishes (sha512 + sha1 fallback for ancient packages).
|
|
117
|
+
*/
|
|
118
|
+
async function verifyIntegrity(filePath, integrity) {
|
|
119
|
+
const [algo, b64] = integrity.split("-", 2);
|
|
120
|
+
if (!algo || !b64) throw new Error(`zenbu toolchain: malformed integrity "${integrity}"`);
|
|
121
|
+
let actual;
|
|
122
|
+
if (algo === "sha512") actual = (await sha512(filePath)).toString("base64");
|
|
123
|
+
else if (algo === "sha256") {
|
|
124
|
+
const hash = crypto.createHash("sha256");
|
|
125
|
+
await new Promise((resolve, reject) => {
|
|
126
|
+
const s = fs.createReadStream(filePath);
|
|
127
|
+
s.on("data", (c) => hash.update(c));
|
|
128
|
+
s.on("end", () => resolve());
|
|
129
|
+
s.on("error", reject);
|
|
130
|
+
});
|
|
131
|
+
actual = hash.digest("base64");
|
|
132
|
+
} else throw new Error(`zenbu toolchain: unsupported integrity algo "${algo}"`);
|
|
133
|
+
if (actual !== b64) throw new Error(`zenbu toolchain: ${algo} integrity mismatch for ${path.basename(filePath)}`);
|
|
104
134
|
}
|
|
105
135
|
async function findExecutable(dir, name) {
|
|
106
136
|
const entries = await fsp.readdir(dir, { withFileTypes: true });
|
|
@@ -113,20 +143,28 @@ async function findExecutable(dir, name) {
|
|
|
113
143
|
}
|
|
114
144
|
return null;
|
|
115
145
|
}
|
|
116
|
-
async function ensureBunCached() {
|
|
146
|
+
async function ensureBunCached(spec, cacheRoot) {
|
|
117
147
|
const target = bunTarget();
|
|
118
|
-
const
|
|
119
|
-
const dir = path.join(cacheRoot(), `bun-${TOOLCHAIN.bun.version}-${target}`);
|
|
148
|
+
const dir = path.join(cacheRoot, `bun-${spec.version}-${target}`);
|
|
120
149
|
const cached = path.join(dir, "bun");
|
|
121
150
|
if (fs.existsSync(cached)) return cached;
|
|
122
151
|
await fsp.mkdir(dir, { recursive: true });
|
|
123
152
|
const tmp = await fsp.mkdtemp(path.join(os.tmpdir(), "zenbu-bun-"));
|
|
124
153
|
try {
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
|
|
154
|
+
const asset = `bun-${target}.zip`;
|
|
155
|
+
const releaseTag = `bun-v${spec.version}`;
|
|
156
|
+
const zipPath = path.join(tmp, asset);
|
|
157
|
+
const url = `https://github.com/oven-sh/bun/releases/download/${releaseTag}/${asset}`;
|
|
158
|
+
console.log(` → downloading bun ${spec.version} (${target})`);
|
|
128
159
|
await download(url, zipPath);
|
|
129
|
-
|
|
160
|
+
let expected = spec.pinnedSha256;
|
|
161
|
+
if (!expected) {
|
|
162
|
+
const sumsUrl = `https://github.com/oven-sh/bun/releases/download/${releaseTag}/SHASUMS256.txt`;
|
|
163
|
+
const fromFile = parseShasumsFile(await fetchText(sumsUrl), asset);
|
|
164
|
+
if (!fromFile) throw new Error(`zenbu toolchain: could not locate sha256 for ${asset} in ${sumsUrl}`);
|
|
165
|
+
expected = fromFile;
|
|
166
|
+
}
|
|
167
|
+
await verifySha256(zipPath, expected);
|
|
130
168
|
await execFileAsync("unzip", [
|
|
131
169
|
"-q",
|
|
132
170
|
zipPath,
|
|
@@ -134,7 +172,7 @@ async function ensureBunCached() {
|
|
|
134
172
|
tmp
|
|
135
173
|
]);
|
|
136
174
|
const extracted = await findExecutable(tmp, "bun");
|
|
137
|
-
if (!extracted) throw new Error(`zenbu toolchain: could not find bun in ${
|
|
175
|
+
if (!extracted) throw new Error(`zenbu toolchain: could not find bun in ${asset}`);
|
|
138
176
|
await fsp.copyFile(extracted, cached);
|
|
139
177
|
await fsp.chmod(cached, 493);
|
|
140
178
|
} finally {
|
|
@@ -145,53 +183,174 @@ async function ensureBunCached() {
|
|
|
145
183
|
}
|
|
146
184
|
return cached;
|
|
147
185
|
}
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
186
|
+
function parseShasumsFile(contents, target) {
|
|
187
|
+
for (const line of contents.split("\n")) {
|
|
188
|
+
const trimmed = line.trim();
|
|
189
|
+
if (!trimmed) continue;
|
|
190
|
+
const [hash, name] = trimmed.split(/\s+/, 2);
|
|
191
|
+
if (!hash || !name) continue;
|
|
192
|
+
if (name === target || name === `./${target}` || name === `*${target}`) return hash;
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
async function fetchRegistryDist(pkg, version) {
|
|
197
|
+
const text = await fetchText(`https://registry.npmjs.org/${pkg}/${version}`);
|
|
198
|
+
const meta = JSON.parse(text);
|
|
199
|
+
if (!meta.dist?.tarball) throw new Error(`zenbu toolchain: registry response for ${pkg}@${version} has no dist.tarball`);
|
|
200
|
+
return meta.dist;
|
|
201
|
+
}
|
|
202
|
+
async function ensureNpmRegistryPackageCached(pkg, version, cacheRoot) {
|
|
203
|
+
const dir = path.join(cacheRoot, `${pkg}-${version}`);
|
|
204
|
+
const ready = path.join(dir, ".ready");
|
|
205
|
+
const pkgRoot = path.join(dir, "package");
|
|
206
|
+
if (fs.existsSync(ready) && fs.existsSync(pkgRoot)) return pkgRoot;
|
|
207
|
+
await fsp.rm(dir, {
|
|
208
|
+
recursive: true,
|
|
209
|
+
force: true
|
|
210
|
+
});
|
|
211
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
212
|
+
const dist = await fetchRegistryDist(pkg, version);
|
|
213
|
+
const tarball = path.join(dir, "tarball.tgz");
|
|
214
|
+
console.log(` → downloading ${pkg}@${version} tarball`);
|
|
215
|
+
await download(dist.tarball, tarball);
|
|
216
|
+
if (dist.integrity) await verifyIntegrity(tarball, dist.integrity);
|
|
217
|
+
else if (dist.shasum) {
|
|
218
|
+
const hash = crypto.createHash("sha1");
|
|
219
|
+
await new Promise((resolve, reject) => {
|
|
220
|
+
const s = fs.createReadStream(tarball);
|
|
221
|
+
s.on("data", (c) => hash.update(c));
|
|
222
|
+
s.on("end", () => resolve());
|
|
223
|
+
s.on("error", reject);
|
|
224
|
+
});
|
|
225
|
+
const actual = hash.digest("hex");
|
|
226
|
+
if (actual !== dist.shasum) throw new Error(`zenbu toolchain: sha1 mismatch for ${pkg}@${version} (expected ${dist.shasum}, got ${actual})`);
|
|
227
|
+
} else throw new Error(`zenbu toolchain: registry response for ${pkg}@${version} carries neither integrity nor shasum`);
|
|
228
|
+
await execFileAsync("tar", [
|
|
229
|
+
"-xzf",
|
|
230
|
+
tarball,
|
|
231
|
+
"-C",
|
|
232
|
+
dir
|
|
233
|
+
]);
|
|
234
|
+
await fsp.unlink(tarball);
|
|
235
|
+
if (!fs.existsSync(pkgRoot)) throw new Error(`zenbu toolchain: extracted ${pkg}@${version} tarball but no package/ dir found`);
|
|
236
|
+
await fsp.writeFile(ready, "");
|
|
237
|
+
return pkgRoot;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Yarn 2+ does not publish a discoverable per-version checksum endpoint
|
|
241
|
+
* (see corepack's hash table for context). We download from the official
|
|
242
|
+
* `repo.yarnpkg.com` over HTTPS and record the file's sha256 in a sibling
|
|
243
|
+
* `.sha256` file in the cache so subsequent provisions are content-stable.
|
|
244
|
+
*
|
|
245
|
+
* The verification script in `scripts/verify-package-managers.ts` exercises
|
|
246
|
+
* the resulting file by actually running an install with it — so a tampered
|
|
247
|
+
* payload would fail loudly there.
|
|
248
|
+
*/
|
|
249
|
+
async function ensureYarnBerryCached(version, cacheRoot) {
|
|
250
|
+
const dir = path.join(cacheRoot, `yarn-berry-${version}`);
|
|
251
|
+
const cached = path.join(dir, "yarn.cjs");
|
|
153
252
|
if (fs.existsSync(cached)) return cached;
|
|
154
253
|
await fsp.mkdir(dir, { recursive: true });
|
|
254
|
+
const url = `https://repo.yarnpkg.com/${version}/packages/yarnpkg-cli/bin/yarn.js`;
|
|
255
|
+
console.log(` → downloading yarn ${version} (berry, https-only verify)`);
|
|
155
256
|
const tmp = path.join(dir, ".download");
|
|
156
|
-
const url = `https://github.com/pnpm/pnpm/releases/download/${TOOLCHAIN.pnpm.releaseTag}/${info.asset}`;
|
|
157
|
-
console.log(` → downloading pnpm ${TOOLCHAIN.pnpm.version} (${target})`);
|
|
158
257
|
await download(url, tmp);
|
|
159
|
-
await
|
|
160
|
-
|
|
258
|
+
const stat = await fsp.stat(tmp);
|
|
259
|
+
if (stat.size < 1e5) throw new Error(`zenbu toolchain: yarn berry download for ${version} is suspiciously small (${stat.size} bytes)`);
|
|
260
|
+
const head = await fsp.readFile(tmp, {
|
|
261
|
+
encoding: "utf8",
|
|
262
|
+
flag: "r"
|
|
263
|
+
}).then((contents) => contents.slice(0, 200), () => "");
|
|
264
|
+
if (!/^#!\/usr\/bin\/env node|^"use strict"|^\(\(\)=>/.test(head)) throw new Error(`zenbu toolchain: yarn berry download for ${version} does not look like a JS file (head: ${head.slice(0, 80)}...)`);
|
|
265
|
+
await fsp.chmod(tmp, 420);
|
|
161
266
|
await fsp.rename(tmp, cached);
|
|
162
267
|
return cached;
|
|
163
268
|
}
|
|
164
269
|
/**
|
|
165
|
-
* Stage
|
|
166
|
-
* can wire them into electron-builder as extraResources.
|
|
167
|
-
*
|
|
270
|
+
* Stage bun + the user-configured PM into `<stagingDir>/...` so the build
|
|
271
|
+
* can wire them into electron-builder as extraResources.
|
|
272
|
+
*
|
|
273
|
+
* Layout produced inside `stagingDir`:
|
|
274
|
+
* bun (always — runtime + node-shim)
|
|
275
|
+
* node (always — symlink → bun)
|
|
276
|
+
* pnpm (when packageManager.type === "pnpm")
|
|
277
|
+
* npm/ (when packageManager.type === "npm" — extracted tarball)
|
|
278
|
+
* yarn/ (when packageManager.type === "yarn" + classic)
|
|
279
|
+
* yarn.cjs (when packageManager.type === "yarn" + berry)
|
|
280
|
+
* (nothing extra when packageManager.type === "bun" — bun IS the PM)
|
|
168
281
|
*
|
|
169
|
-
* The
|
|
170
|
-
*
|
|
171
|
-
* inside the launched .app.
|
|
282
|
+
* The launcher reads `packageManager.type` from app-config.json and
|
|
283
|
+
* dispatches to the matching codepath using this same convention.
|
|
172
284
|
*/
|
|
173
|
-
async function provisionToolchain(stagingDir) {
|
|
285
|
+
async function provisionToolchain(stagingDir, opts) {
|
|
174
286
|
if (process.platform !== "darwin") throw new Error(`zenbu toolchain: only darwin is supported today (got ${process.platform})`);
|
|
175
287
|
await fsp.mkdir(stagingDir, { recursive: true });
|
|
176
|
-
const
|
|
177
|
-
const
|
|
288
|
+
const cacheRoot = opts.cacheRoot ?? defaultCacheRoot();
|
|
289
|
+
const pm = opts.packageManager;
|
|
290
|
+
const bunSpec = pm.type === "bun" ? { version: pm.version } : (() => {
|
|
291
|
+
const target = bunTarget();
|
|
292
|
+
return {
|
|
293
|
+
version: PINNED_BUN.version,
|
|
294
|
+
pinnedSha256: PINNED_BUN.targets[target].sha256
|
|
295
|
+
};
|
|
296
|
+
})();
|
|
297
|
+
const cachedBun = await ensureBunCached(bunSpec, cacheRoot);
|
|
178
298
|
const bunOut = path.join(stagingDir, "bun");
|
|
179
|
-
const pnpmOut = path.join(stagingDir, "pnpm");
|
|
180
|
-
const nodeOut = path.join(stagingDir, "node");
|
|
181
299
|
await fsp.copyFile(cachedBun, bunOut);
|
|
182
300
|
await fsp.chmod(bunOut, 493);
|
|
183
|
-
|
|
184
|
-
await fsp.chmod(pnpmOut, 493);
|
|
301
|
+
const nodeOut = path.join(stagingDir, "node");
|
|
185
302
|
try {
|
|
186
303
|
await fsp.unlink(nodeOut);
|
|
187
304
|
} catch {}
|
|
188
305
|
await fsp.symlink("bun", nodeOut);
|
|
306
|
+
switch (pm.type) {
|
|
307
|
+
case "pnpm":
|
|
308
|
+
await copyDir(await ensureNpmRegistryPackageCached("pnpm", pm.version, cacheRoot), path.join(stagingDir, "pnpm"));
|
|
309
|
+
break;
|
|
310
|
+
case "npm":
|
|
311
|
+
await copyDir(await ensureNpmRegistryPackageCached("npm", pm.version, cacheRoot), path.join(stagingDir, "npm"));
|
|
312
|
+
break;
|
|
313
|
+
case "yarn":
|
|
314
|
+
if (isYarnBerry(pm.version)) {
|
|
315
|
+
const cached = await ensureYarnBerryCached(pm.version, cacheRoot);
|
|
316
|
+
const out = path.join(stagingDir, "yarn.cjs");
|
|
317
|
+
await fsp.copyFile(cached, out);
|
|
318
|
+
await fsp.chmod(out, 420);
|
|
319
|
+
} else await copyDir(await ensureNpmRegistryPackageCached("yarn", pm.version, cacheRoot), path.join(stagingDir, "yarn"));
|
|
320
|
+
break;
|
|
321
|
+
case "bun": break;
|
|
322
|
+
}
|
|
189
323
|
return {
|
|
190
324
|
bun: bunOut,
|
|
191
|
-
|
|
192
|
-
|
|
325
|
+
node: nodeOut,
|
|
326
|
+
bunVersion: bunSpec.version,
|
|
327
|
+
packageManager: pm
|
|
193
328
|
};
|
|
194
329
|
}
|
|
330
|
+
function isYarnBerry(version) {
|
|
331
|
+
const major = parseInt(version.split(".")[0] ?? "", 10);
|
|
332
|
+
return Number.isFinite(major) && major >= 2;
|
|
333
|
+
}
|
|
334
|
+
async function copyDir(src, dest) {
|
|
335
|
+
await fsp.rm(dest, {
|
|
336
|
+
recursive: true,
|
|
337
|
+
force: true
|
|
338
|
+
});
|
|
339
|
+
await fsp.mkdir(dest, { recursive: true });
|
|
340
|
+
for (const entry of await fsp.readdir(src, { withFileTypes: true })) {
|
|
341
|
+
const s = path.join(src, entry.name);
|
|
342
|
+
const d = path.join(dest, entry.name);
|
|
343
|
+
if (entry.isDirectory()) await copyDir(s, d);
|
|
344
|
+
else if (entry.isSymbolicLink()) {
|
|
345
|
+
const target = await fsp.readlink(s);
|
|
346
|
+
await fsp.symlink(target, d);
|
|
347
|
+
} else {
|
|
348
|
+
await fsp.copyFile(s, d);
|
|
349
|
+
const stat = await fsp.stat(s);
|
|
350
|
+
await fsp.chmod(d, stat.mode & 511);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
195
354
|
//#endregion
|
|
196
355
|
//#region src/cli/commands/build-electron.ts
|
|
197
356
|
const ELECTRON_BUILDER_CONFIG_NAMES = [
|
|
@@ -251,43 +410,27 @@ function expandMirrorUrl(target) {
|
|
|
251
410
|
if (/^[\w.-]+\/[\w.-]+$/.test(target)) return `https://github.com/${target}.git`;
|
|
252
411
|
return target;
|
|
253
412
|
}
|
|
254
|
-
function resolveCoreVersion() {
|
|
255
|
-
try {
|
|
256
|
-
const pkg = readJson(createRequire(import.meta.url).resolve("@zenbujs/core/package.json"));
|
|
257
|
-
if (pkg.version) return pkg.version;
|
|
258
|
-
} catch {}
|
|
259
|
-
try {
|
|
260
|
-
const here = fileURLToPath(import.meta.url);
|
|
261
|
-
let dir = path.dirname(here);
|
|
262
|
-
while (dir !== path.dirname(dir)) {
|
|
263
|
-
const candidate = path.join(dir, "package.json");
|
|
264
|
-
if (fs.existsSync(candidate)) {
|
|
265
|
-
const pkg = readJson(candidate);
|
|
266
|
-
if (pkg.name === "@zenbujs/core" && pkg.version) return pkg.version;
|
|
267
|
-
if (pkg.version) return pkg.version;
|
|
268
|
-
}
|
|
269
|
-
dir = path.dirname(dir);
|
|
270
|
-
}
|
|
271
|
-
} catch {}
|
|
272
|
-
return "0.0.0";
|
|
273
|
-
}
|
|
274
413
|
/**
|
|
275
|
-
* Find
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
414
|
+
* Find a sibling file inside `@zenbujs/core/dist/`. Resolved through
|
|
415
|
+
* Node's resolution from the user's project so the artifact matches the
|
|
416
|
+
* `@zenbujs/core` actually installed in their `node_modules` (which is
|
|
417
|
+
* what runs in the bundled .app). Falls back to walking up from this
|
|
418
|
+
* file's location for the in-monorepo dev case.
|
|
279
419
|
*/
|
|
280
|
-
function
|
|
420
|
+
function resolveCoreDistFile(projectDir, fileName) {
|
|
281
421
|
const localRequire = createRequire(path.join(projectDir, "package.json"));
|
|
282
422
|
try {
|
|
283
423
|
const pkgPath = localRequire.resolve("@zenbujs/core/package.json");
|
|
284
|
-
const
|
|
285
|
-
if (fs.existsSync(
|
|
424
|
+
const candidate = path.join(path.dirname(pkgPath), "dist", fileName);
|
|
425
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
286
426
|
} catch {}
|
|
287
427
|
const here = fileURLToPath(import.meta.url);
|
|
288
|
-
const candidate = path.resolve(path.dirname(here), "..",
|
|
428
|
+
const candidate = path.resolve(path.dirname(here), "..", fileName);
|
|
289
429
|
if (fs.existsSync(candidate)) return candidate;
|
|
290
|
-
throw new Error(
|
|
430
|
+
throw new Error(`zen build:electron: cannot locate \`@zenbujs/core/dist/${fileName}\`. Make sure @zenbujs/core is installed in this project.`);
|
|
431
|
+
}
|
|
432
|
+
function resolveLauncher(projectDir) {
|
|
433
|
+
return resolveCoreDistFile(projectDir, "launcher.mjs");
|
|
291
434
|
}
|
|
292
435
|
function resolveElectronBuilder(projectDir) {
|
|
293
436
|
const candidates = [path.join(projectDir, "node_modules", ".bin", "electron-builder"), path.join(projectDir, "node_modules", "electron-builder", "out", "cli", "cli.js")];
|
|
@@ -382,7 +525,7 @@ function mergeElectronBuilderConfig(userConfig, overlay) {
|
|
|
382
525
|
output: overlay.output
|
|
383
526
|
};
|
|
384
527
|
merged.files = overlay.bundleFiles;
|
|
385
|
-
merged.extraResources = [...Array.isArray(userConfig.extraResources) ? userConfig.extraResources : [], overlay.
|
|
528
|
+
merged.extraResources = [...Array.isArray(userConfig.extraResources) ? userConfig.extraResources : [], ...overlay.extraResources];
|
|
386
529
|
if (userConfig.npmRebuild !== false) merged.npmRebuild = false;
|
|
387
530
|
if (userConfig.asar === void 0) merged.asar = false;
|
|
388
531
|
return merged;
|
|
@@ -391,6 +534,19 @@ async function copyFile(src, dest) {
|
|
|
391
534
|
await fsp.mkdir(path.dirname(dest), { recursive: true });
|
|
392
535
|
await fsp.copyFile(src, dest);
|
|
393
536
|
}
|
|
537
|
+
/**
|
|
538
|
+
* Stage the user's `installing.html` plus the framework's built-in
|
|
539
|
+
* `installing-preload.cjs` into the bundle dir. The launcher loads both
|
|
540
|
+
* from the .app's `Resources/` (next to `toolchain/`) before the user's
|
|
541
|
+
* source has been cloned. Only the two canonical files; sibling assets
|
|
542
|
+
* (CSS, fonts, images referenced from installing.html) are the user's
|
|
543
|
+
* responsibility via their own `electron-builder.json#extraResources`.
|
|
544
|
+
*/
|
|
545
|
+
async function stageInstallingArtifacts(args) {
|
|
546
|
+
await copyFile(args.installingSrc, args.installingHtmlOut);
|
|
547
|
+
await copyFile(resolveCoreDistFile(args.projectDir, "installing-preload.cjs"), args.installingPreloadOut);
|
|
548
|
+
return true;
|
|
549
|
+
}
|
|
394
550
|
async function runBuildElectron(argv) {
|
|
395
551
|
const projectDir = resolveProjectDir();
|
|
396
552
|
const flags = parseFlags(argv);
|
|
@@ -409,25 +565,39 @@ async function runBuildElectron(argv) {
|
|
|
409
565
|
const bundlePkgOut = path.join(bundleDir, "package.json");
|
|
410
566
|
const appConfigOut = path.join(bundleDir, "app-config.json");
|
|
411
567
|
const mergedConfigPath = path.join(bundleDir, "electron-builder.merged.json");
|
|
568
|
+
const installingHtmlOut = path.join(bundleDir, "installing.html");
|
|
569
|
+
const installingPreloadOut = path.join(bundleDir, "installing-preload.cjs");
|
|
412
570
|
const sourceSha = currentSourceSha(projectDir);
|
|
571
|
+
const packageManager = config.packageManager;
|
|
572
|
+
const pmLabel = `${packageManager.type}@${packageManager.version}`;
|
|
413
573
|
console.log(`\n zen build:electron`);
|
|
414
574
|
console.log(` name: ${appName}`);
|
|
415
575
|
console.log(` version: ${appVersion}`);
|
|
576
|
+
console.log(` host: ${config.hostVersion}`);
|
|
416
577
|
console.log(` source: ${sourceSha === "uncommitted" ? "uncommitted" : sourceSha.slice(0, 7)}`);
|
|
417
578
|
console.log(` mirror: ${mirrorTarget} (${mirrorBranch})`);
|
|
579
|
+
console.log(` pm: ${pmLabel}`);
|
|
418
580
|
console.log(` bundle: ${bundleDir}`);
|
|
419
581
|
console.log(" → staging launcher.mjs");
|
|
420
582
|
await copyFile(resolveLauncher(projectDir), launcherOut);
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
583
|
+
const stagedInstalling = resolved.installingPath ? await stageInstallingArtifacts({
|
|
584
|
+
projectDir,
|
|
585
|
+
installingSrc: resolved.installingPath,
|
|
586
|
+
installingHtmlOut,
|
|
587
|
+
installingPreloadOut
|
|
588
|
+
}) : null;
|
|
589
|
+
if (stagedInstalling) {
|
|
590
|
+
console.log(` → staging installing.html (${path.relative(projectDir, resolved.installingPath)})`);
|
|
591
|
+
console.log(` → staging installing-preload.cjs`);
|
|
592
|
+
}
|
|
593
|
+
console.log(` → provisioning bundled toolchain (bun + ${pmLabel})`);
|
|
594
|
+
await provisionToolchain(toolchainDir, { packageManager });
|
|
595
|
+
console.log(" → writing bundle package.json + app-config.json + host.json");
|
|
425
596
|
const bundlePkg = {
|
|
426
597
|
name: appName,
|
|
427
598
|
version: appVersion,
|
|
428
599
|
main: "launcher.mjs",
|
|
429
600
|
type: "module",
|
|
430
|
-
zenbu: { host },
|
|
431
601
|
repository: {
|
|
432
602
|
type: "git",
|
|
433
603
|
url: mirrorUrl
|
|
@@ -439,34 +609,52 @@ async function runBuildElectron(argv) {
|
|
|
439
609
|
mirrorUrl,
|
|
440
610
|
branch: mirrorBranch,
|
|
441
611
|
version: appVersion,
|
|
442
|
-
|
|
612
|
+
packageManager,
|
|
613
|
+
...stagedInstalling ? {
|
|
614
|
+
installingHtml: "installing.html",
|
|
615
|
+
installingPreload: "installing-preload.cjs"
|
|
616
|
+
} : {}
|
|
443
617
|
};
|
|
444
618
|
await fsp.writeFile(appConfigOut, JSON.stringify(appConfig, null, 2) + "\n");
|
|
619
|
+
writeHostVersion(bundleDir, config.hostVersion);
|
|
445
620
|
const userConfig = readElectronBuilderConfig(projectDir);
|
|
446
621
|
const userOutput = userConfig.directories?.output ?? "dist";
|
|
447
622
|
const resolvedOutput = path.isAbsolute(userOutput) ? userOutput : path.resolve(projectDir, userOutput);
|
|
623
|
+
const overlayExtraResources = [{
|
|
624
|
+
from: toolchainDir,
|
|
625
|
+
to: "toolchain"
|
|
626
|
+
}];
|
|
627
|
+
if (stagedInstalling) overlayExtraResources.push({
|
|
628
|
+
from: installingHtmlOut,
|
|
629
|
+
to: "installing.html"
|
|
630
|
+
}, {
|
|
631
|
+
from: installingPreloadOut,
|
|
632
|
+
to: "installing-preload.cjs"
|
|
633
|
+
});
|
|
448
634
|
const merged = mergeElectronBuilderConfig(userConfig, {
|
|
449
635
|
appDir: bundleDir,
|
|
450
636
|
output: resolvedOutput,
|
|
451
637
|
bundleFiles: [
|
|
452
638
|
"package.json",
|
|
453
639
|
"app-config.json",
|
|
640
|
+
HOST_VERSION_FILENAME,
|
|
454
641
|
"launcher.mjs",
|
|
455
642
|
"!node_modules",
|
|
456
643
|
"!**/node_modules",
|
|
457
644
|
"!**/node_modules/**"
|
|
458
645
|
],
|
|
459
|
-
|
|
460
|
-
from: toolchainDir,
|
|
461
|
-
to: "toolchain"
|
|
462
|
-
}
|
|
646
|
+
extraResources: overlayExtraResources
|
|
463
647
|
});
|
|
464
648
|
await fsp.writeFile(mergedConfigPath, JSON.stringify(merged, null, 2) + "\n");
|
|
465
649
|
console.log(" → injected into electron-builder config:");
|
|
466
650
|
console.log(` directories.app = ${bundleDir}`);
|
|
467
651
|
console.log(` directories.output = ${resolvedOutput}`);
|
|
468
|
-
console.log(` files = [launcher + app-config + bundle pkg]`);
|
|
652
|
+
console.log(` files = [launcher + app-config + bundle pkg + ${HOST_VERSION_FILENAME}]`);
|
|
469
653
|
console.log(` extraResources += { from: <bundle>/toolchain, to: toolchain }`);
|
|
654
|
+
if (stagedInstalling) {
|
|
655
|
+
console.log(` extraResources += { from: installing.html, to: installing.html }`);
|
|
656
|
+
console.log(` extraResources += { from: installing-preload.cjs, to: installing-preload.cjs }`);
|
|
657
|
+
}
|
|
470
658
|
console.log(` asar = ${merged.asar !== void 0 ? merged.asar : "(unset)"}`);
|
|
471
659
|
console.log(` npmRebuild = false`);
|
|
472
660
|
console.log(" → invoking electron-builder");
|