electrobun 1.18.1 → 1.18.4-beta.5
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 +2 -0
- package/dist/api/browser/global.d.ts +5 -0
- package/dist/api/browser/index.ts +64 -22
- package/dist/api/bun/ElectrobunConfig.ts +45 -1
- package/dist/api/bun/core/BrowserView.ts +95 -54
- package/dist/api/bun/core/BrowserWindow.ts +19 -29
- package/dist/api/bun/core/BuildConfig.ts +31 -4
- package/dist/api/bun/core/GpuWindow.ts +20 -7
- package/dist/api/bun/core/Socket.ts +13 -196
- package/dist/api/bun/core/Tray.ts +33 -32
- package/dist/api/bun/core/Utils.ts +2 -4
- package/dist/api/bun/core/WGPUView.ts +43 -13
- package/dist/api/bun/index.ts +35 -0
- package/dist/api/bun/preload/.generated/compiled.ts +1 -1
- package/dist/api/bun/preload/globals.d.ts +7 -0
- package/dist/api/bun/preload/index.ts +18 -8
- package/dist/api/bun/preload/webviewTag.ts +32 -3
- package/dist/api/bun/proc/native.ts +914 -745
- package/dist/api/bun/webGPU.ts +1 -1
- package/dist/main.js +26 -22
- package/dist/preload-full.js +913 -0
- package/dist/preload-sandboxed.js +111 -0
- package/dist/zig-sdk/electrobun.zig +1993 -0
- package/package.json +2 -3
- package/src/cli/index.ts +410 -139
- package/dist/api/bun/core/windowIds.ts +0 -5
package/src/cli/index.ts
CHANGED
|
@@ -111,6 +111,9 @@ function getPlatformPaths(
|
|
|
111
111
|
BUN_BINARY: join(platformDistDir, "bun") + binExt,
|
|
112
112
|
LAUNCHER_DEV: join(platformDistDir, "electrobun") + binExt,
|
|
113
113
|
LAUNCHER_RELEASE: join(platformDistDir, "launcher") + binExt,
|
|
114
|
+
CORE_MACOS: join(platformDistDir, "libElectrobunCore.dylib"),
|
|
115
|
+
CORE_WIN: join(platformDistDir, "ElectrobunCore.dll"),
|
|
116
|
+
CORE_LINUX: join(platformDistDir, "libElectrobunCore.so"),
|
|
114
117
|
NATIVE_WRAPPER_MACOS: join(platformDistDir, "libNativeWrapper.dylib"),
|
|
115
118
|
NATIVE_WRAPPER_WIN: join(platformDistDir, "libNativeWrapper.dll"),
|
|
116
119
|
NATIVE_WRAPPER_LINUX: join(platformDistDir, "libNativeWrapper.so"),
|
|
@@ -134,6 +137,8 @@ function getPlatformPaths(
|
|
|
134
137
|
// These work with existing package.json and development workflow
|
|
135
138
|
MAIN_JS: join(sharedDistDir, "main.js"),
|
|
136
139
|
API_DIR: join(sharedDistDir, "api"),
|
|
140
|
+
PRELOAD_FULL_JS: join(sharedDistDir, "preload-full.js"),
|
|
141
|
+
PRELOAD_SANDBOXED_JS: join(sharedDistDir, "preload-sandboxed.js"),
|
|
137
142
|
};
|
|
138
143
|
}
|
|
139
144
|
|
|
@@ -141,6 +146,135 @@ function getPlatformPaths(
|
|
|
141
146
|
// @ts-expect-error - reserved for future use
|
|
142
147
|
const _PATHS = getPlatformPaths(OS, ARCH);
|
|
143
148
|
|
|
149
|
+
function getVendoredZigBinaryPath(): string {
|
|
150
|
+
return join(
|
|
151
|
+
ELECTROBUN_DEP_PATH,
|
|
152
|
+
"vendors",
|
|
153
|
+
"zig",
|
|
154
|
+
OS === "win" ? "zig.exe" : "zig",
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function getZigTarget(
|
|
159
|
+
targetOS: "macos" | "win" | "linux",
|
|
160
|
+
targetArch: "arm64" | "x64",
|
|
161
|
+
): string {
|
|
162
|
+
if (targetOS === "win") {
|
|
163
|
+
return "x86_64-windows";
|
|
164
|
+
}
|
|
165
|
+
if (targetOS === "linux") {
|
|
166
|
+
return targetArch === "arm64" ? "aarch64-linux" : "x86_64-linux";
|
|
167
|
+
}
|
|
168
|
+
return targetArch === "arm64" ? "aarch64-macos" : "x86_64-macos";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function getCEFHelperBaseName(mainProcess: "bun" | "zig"): string {
|
|
172
|
+
return mainProcess === "zig" ? "main" : "bun";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function getCEFHelperNames(mainProcess: "bun" | "zig"): string[] {
|
|
176
|
+
const baseName = getCEFHelperBaseName(mainProcess);
|
|
177
|
+
return [
|
|
178
|
+
`${baseName} Helper`,
|
|
179
|
+
`${baseName} Helper (Alerts)`,
|
|
180
|
+
`${baseName} Helper (GPU)`,
|
|
181
|
+
`${baseName} Helper (Plugin)`,
|
|
182
|
+
`${baseName} Helper (Renderer)`,
|
|
183
|
+
];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function buildZigMainExecutable(options: {
|
|
187
|
+
entrypoint: string;
|
|
188
|
+
buildFolder: string;
|
|
189
|
+
targetOS: "macos" | "win" | "linux";
|
|
190
|
+
targetArch: "arm64" | "x64";
|
|
191
|
+
buildEnvironment: "dev" | "canary" | "stable";
|
|
192
|
+
}) {
|
|
193
|
+
const zigBinary = getVendoredZigBinaryPath();
|
|
194
|
+
if (!existsSync(zigBinary)) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`Vendored Zig compiler not found at ${zigBinary}. Rebuild electrobun/package so vendors/zig is available.`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const zigSdkPath = join(ELECTROBUN_DEP_PATH, "dist", "zig-sdk", "electrobun.zig");
|
|
201
|
+
if (!existsSync(zigSdkPath)) {
|
|
202
|
+
throw new Error(`Electrobun Zig SDK not found at ${zigSdkPath}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const binExt = options.targetOS === "win" ? ".exe" : "";
|
|
206
|
+
const tempBuildDir = join(
|
|
207
|
+
options.buildFolder,
|
|
208
|
+
".electrobun-zig-main",
|
|
209
|
+
`${options.targetOS}-${options.targetArch}`,
|
|
210
|
+
);
|
|
211
|
+
const relativeZigSdkPath = path.relative(tempBuildDir, zigSdkPath) || ".";
|
|
212
|
+
const relativeEntrypointPath = path.relative(tempBuildDir, options.entrypoint) || ".";
|
|
213
|
+
const zigOutBin = join(tempBuildDir, "zig-out", "bin", "main" + binExt);
|
|
214
|
+
const buildScriptPath = join(tempBuildDir, "build.zig");
|
|
215
|
+
mkdirSync(tempBuildDir, { recursive: true });
|
|
216
|
+
|
|
217
|
+
const buildScript = `const std = @import("std");
|
|
218
|
+
|
|
219
|
+
pub fn build(b: *std.Build) void {
|
|
220
|
+
const target = b.standardTargetOptions(.{});
|
|
221
|
+
const optimize = b.standardOptimizeOption(.{});
|
|
222
|
+
|
|
223
|
+
const electrobun = b.createModule(.{
|
|
224
|
+
.root_source_file = b.path(${JSON.stringify(relativeZigSdkPath)}),
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const exe = b.addExecutable(.{
|
|
228
|
+
.name = "main",
|
|
229
|
+
.root_source_file = b.path(${JSON.stringify(relativeEntrypointPath)}),
|
|
230
|
+
.target = target,
|
|
231
|
+
.optimize = optimize,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
exe.root_module.addImport("electrobun", electrobun);
|
|
235
|
+
exe.linkLibC();
|
|
236
|
+
b.installArtifact(exe);
|
|
237
|
+
}
|
|
238
|
+
`;
|
|
239
|
+
writeFileSync(buildScriptPath, buildScript, "utf8");
|
|
240
|
+
|
|
241
|
+
const zigArgs = [
|
|
242
|
+
"build",
|
|
243
|
+
`-Dtarget=${getZigTarget(options.targetOS, options.targetArch)}`,
|
|
244
|
+
];
|
|
245
|
+
|
|
246
|
+
if (options.targetOS === "win") {
|
|
247
|
+
zigArgs.push("-Dcpu=baseline");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (options.buildEnvironment !== "dev") {
|
|
251
|
+
zigArgs.push("-Doptimize=ReleaseSmall");
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const result = Bun.spawnSync([zigBinary, ...zigArgs], {
|
|
255
|
+
cwd: tempBuildDir,
|
|
256
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (result.exitCode !== 0) {
|
|
260
|
+
const stdout = result.stdout ? new TextDecoder().decode(result.stdout) : "";
|
|
261
|
+
const stderr = result.stderr ? new TextDecoder().decode(result.stderr) : "";
|
|
262
|
+
if (stdout.trim()) {
|
|
263
|
+
console.error(stdout);
|
|
264
|
+
}
|
|
265
|
+
if (stderr.trim()) {
|
|
266
|
+
console.error(stderr);
|
|
267
|
+
}
|
|
268
|
+
throw new Error("Build failed: zig main process compilation failed");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (!existsSync(zigOutBin)) {
|
|
272
|
+
throw new Error(`Zig main process binary was not produced at ${zigOutBin}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return zigOutBin;
|
|
276
|
+
}
|
|
277
|
+
|
|
144
278
|
async function ensureCoreDependencies(
|
|
145
279
|
targetOS?: "macos" | "win" | "linux",
|
|
146
280
|
targetArch?: "arm64" | "x64",
|
|
@@ -1483,6 +1617,7 @@ const defaultConfig = {
|
|
|
1483
1617
|
build: {
|
|
1484
1618
|
buildFolder: "build",
|
|
1485
1619
|
artifactFolder: "artifacts",
|
|
1620
|
+
mainProcess: "bun" as "bun" | "zig",
|
|
1486
1621
|
useAsar: false,
|
|
1487
1622
|
asarUnpack: undefined as string[] | undefined, // Glob patterns for files to exclude from ASAR (e.g., ["*.node", "*.dll"])
|
|
1488
1623
|
cefVersion: undefined as string | undefined, // Override CEF version: "CEF_VERSION+chromium-CHROMIUM_VERSION"
|
|
@@ -1524,6 +1659,9 @@ const defaultConfig = {
|
|
|
1524
1659
|
bun: {
|
|
1525
1660
|
entrypoint: "src/bun/index.ts",
|
|
1526
1661
|
},
|
|
1662
|
+
zig: {
|
|
1663
|
+
entrypoint: "src/zig/main.zig",
|
|
1664
|
+
},
|
|
1527
1665
|
views: undefined as
|
|
1528
1666
|
| Record<string, { entrypoint: string; [key: string]: unknown }>
|
|
1529
1667
|
| undefined,
|
|
@@ -1548,6 +1686,24 @@ const defaultConfig = {
|
|
|
1548
1686
|
path?: string;
|
|
1549
1687
|
[key: string]: unknown;
|
|
1550
1688
|
}>;
|
|
1689
|
+
slateUIs?: Record<string, {
|
|
1690
|
+
name?: string;
|
|
1691
|
+
entrypoint?: string;
|
|
1692
|
+
path?: string;
|
|
1693
|
+
[key: string]: unknown;
|
|
1694
|
+
}>;
|
|
1695
|
+
contributions?: {
|
|
1696
|
+
fileActivators?: Array<{
|
|
1697
|
+
baseName?: string;
|
|
1698
|
+
nodeType?: "file" | "dir" | "any";
|
|
1699
|
+
slate: {
|
|
1700
|
+
type: string;
|
|
1701
|
+
name?: string;
|
|
1702
|
+
icon?: string;
|
|
1703
|
+
config?: Record<string, unknown>;
|
|
1704
|
+
};
|
|
1705
|
+
}>;
|
|
1706
|
+
};
|
|
1551
1707
|
carrotOnly?: boolean;
|
|
1552
1708
|
} | undefined,
|
|
1553
1709
|
},
|
|
@@ -2078,8 +2234,10 @@ ${utiDecls}
|
|
|
2078
2234
|
// Get environment
|
|
2079
2235
|
const envArg =
|
|
2080
2236
|
process.argv.find((arg) => arg.startsWith("--env="))?.split("=")[1] || "";
|
|
2081
|
-
const buildEnvironment = ["dev", "canary", "stable"].includes(
|
|
2082
|
-
|
|
2237
|
+
const buildEnvironment: "dev" | "canary" | "stable" = ["dev", "canary", "stable"].includes(
|
|
2238
|
+
envArg,
|
|
2239
|
+
)
|
|
2240
|
+
? (envArg as "dev" | "canary" | "stable")
|
|
2083
2241
|
: "dev";
|
|
2084
2242
|
|
|
2085
2243
|
try {
|
|
@@ -2110,7 +2268,7 @@ ${utiDecls}
|
|
|
2110
2268
|
|
|
2111
2269
|
async function runBuild(
|
|
2112
2270
|
config: Awaited<ReturnType<typeof getConfig>>,
|
|
2113
|
-
buildEnvironment:
|
|
2271
|
+
buildEnvironment: "dev" | "canary" | "stable",
|
|
2114
2272
|
) {
|
|
2115
2273
|
// Determine current platform as default target
|
|
2116
2274
|
const currentTarget = { os: OS, arch: ARCH };
|
|
@@ -2392,20 +2550,34 @@ Categories=Utility;Application;
|
|
|
2392
2550
|
|
|
2393
2551
|
// refresh build folder
|
|
2394
2552
|
if (existsSync(buildFolder)) {
|
|
2395
|
-
rmSync(buildFolder, { recursive: true });
|
|
2553
|
+
rmSync(buildFolder, { recursive: true, force: true });
|
|
2396
2554
|
}
|
|
2397
2555
|
mkdirSync(buildFolder, { recursive: true });
|
|
2398
|
-
|
|
2556
|
+
|
|
2557
|
+
const mainProcess = config.build.mainProcess ?? "bun";
|
|
2399
2558
|
const bunConfig = config.build.bun;
|
|
2400
2559
|
const bunSource = join(projectRoot, bunConfig.entrypoint);
|
|
2560
|
+
const zigConfig = config.build.zig;
|
|
2561
|
+
const zigSource = join(projectRoot, zigConfig.entrypoint);
|
|
2401
2562
|
|
|
2402
|
-
if (
|
|
2563
|
+
if (mainProcess === "bun") {
|
|
2564
|
+
if (!existsSync(bunSource)) {
|
|
2565
|
+
throw new Error(
|
|
2566
|
+
`failed to bundle ${bunSource} because it doesn't exist.\n You need a config.build.bun.entrypoint source file to build.`,
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
2569
|
+
} else if (!existsSync(zigSource)) {
|
|
2403
2570
|
throw new Error(
|
|
2404
|
-
`failed to
|
|
2571
|
+
`failed to compile ${zigSource} because it doesn't exist.\n You need a config.build.zig.entrypoint source file to build.`,
|
|
2405
2572
|
);
|
|
2406
2573
|
}
|
|
2407
2574
|
|
|
2408
2575
|
const isCarrotOnly = config.build.carrot?.carrotOnly === true;
|
|
2576
|
+
if (config.build.carrot && mainProcess === "zig") {
|
|
2577
|
+
throw new Error(
|
|
2578
|
+
`build.carrot is not supported with build.mainProcess = "zig" yet.`,
|
|
2579
|
+
);
|
|
2580
|
+
}
|
|
2409
2581
|
|
|
2410
2582
|
// build macos bundle
|
|
2411
2583
|
// Use display name (with spaces) for macOS bundle folders, sanitized name for other platforms
|
|
@@ -2438,6 +2610,17 @@ Categories=Utility;Application;
|
|
|
2438
2610
|
mkdirSync(appBundleAppCodePath, { recursive: true });
|
|
2439
2611
|
}
|
|
2440
2612
|
|
|
2613
|
+
let zigMainBinarySourcePath: string | null = null;
|
|
2614
|
+
if (mainProcess === "zig") {
|
|
2615
|
+
zigMainBinarySourcePath = await buildZigMainExecutable({
|
|
2616
|
+
entrypoint: zigSource,
|
|
2617
|
+
buildFolder,
|
|
2618
|
+
targetOS,
|
|
2619
|
+
targetArch: targetARCH,
|
|
2620
|
+
buildEnvironment,
|
|
2621
|
+
});
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2441
2624
|
// const bundledBunPath = join(appBundleMacOSPath, 'bun');
|
|
2442
2625
|
// cpSync(bunPath, bundledBunPath);
|
|
2443
2626
|
|
|
@@ -2610,105 +2793,121 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
2610
2793
|
}
|
|
2611
2794
|
}
|
|
2612
2795
|
|
|
2613
|
-
cpSync(targetPaths.
|
|
2796
|
+
cpSync(targetPaths.PRELOAD_FULL_JS, join(appBundleFolderResourcesPath, "preload-full.js"), {
|
|
2614
2797
|
dereference: true,
|
|
2615
2798
|
});
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
config.build.bunVersion,
|
|
2623
|
-
config.build.bunnyBun,
|
|
2799
|
+
cpSync(
|
|
2800
|
+
targetPaths.PRELOAD_SANDBOXED_JS,
|
|
2801
|
+
join(appBundleFolderResourcesPath, "preload-sandboxed.js"),
|
|
2802
|
+
{
|
|
2803
|
+
dereference: true,
|
|
2804
|
+
},
|
|
2624
2805
|
);
|
|
2625
|
-
// Note: .bin/bun binary in node_modules is a symlink to the versioned one in another place
|
|
2626
|
-
// in node_modules, so we have to dereference here to get the actual binary in the bundle.
|
|
2627
|
-
const bunBinaryDestInBundlePath =
|
|
2628
|
-
join(appBundleMacOSPath, "bun") + targetBinExt;
|
|
2629
|
-
const destFolder2 = dirname(bunBinaryDestInBundlePath);
|
|
2630
|
-
if (!existsSync(destFolder2)) {
|
|
2631
|
-
// console.info('creating folder: ', destFolder);
|
|
2632
|
-
mkdirSync(destFolder2, { recursive: true });
|
|
2633
|
-
}
|
|
2634
|
-
cpSync(bunBinarySourcePath, bunBinaryDestInBundlePath, {
|
|
2635
|
-
dereference: true,
|
|
2636
|
-
});
|
|
2637
2806
|
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
const icuDataSource = icuDataFileName ? join(bunDir, icuDataFileName) : "";
|
|
2643
|
-
if (icuDataFileName && existsSync(icuDataSource) && targetOS !== "macos") {
|
|
2644
|
-
const icuDataDest = join(appBundleMacOSPath, icuDataFileName);
|
|
2645
|
-
|
|
2646
|
-
const locales = config.build?.locales;
|
|
2647
|
-
if (locales && locales !== "*" && Array.isArray(locales) && locales.length > 0) {
|
|
2648
|
-
// Trim ICU data to specified locales using icupkg
|
|
2649
|
-
try {
|
|
2650
|
-
await trimICUData(icuDataSource, icuDataDest, locales);
|
|
2651
|
-
const originalSize = statSync(icuDataSource).size;
|
|
2652
|
-
const trimmedSize = statSync(icuDataDest).size;
|
|
2653
|
-
console.log(
|
|
2654
|
-
`Trimmed ICU data: ${(originalSize / 1024 / 1024).toFixed(1)}MB → ${(trimmedSize / 1024 / 1024).toFixed(1)}MB (locales: ${locales.join(", ")})`,
|
|
2655
|
-
);
|
|
2656
|
-
} catch (error) {
|
|
2657
|
-
console.warn(`Warning: Failed to trim ICU data, copying full file: ${error}`);
|
|
2658
|
-
cpSync(icuDataSource, icuDataDest);
|
|
2659
|
-
}
|
|
2660
|
-
} else {
|
|
2661
|
-
cpSync(icuDataSource, icuDataDest);
|
|
2662
|
-
console.log(`Copied ICU data file: ${icuDataFileName}`);
|
|
2663
|
-
}
|
|
2664
|
-
}
|
|
2807
|
+
if (mainProcess === "bun") {
|
|
2808
|
+
cpSync(targetPaths.MAIN_JS, join(appBundleFolderResourcesPath, "main.js"), {
|
|
2809
|
+
dereference: true,
|
|
2810
|
+
});
|
|
2665
2811
|
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
const
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2812
|
+
// Bun runtime binary
|
|
2813
|
+
// todo (yoav): this only works for the current architecture
|
|
2814
|
+
const bunBinarySourcePath = await ensureBunBinary(
|
|
2815
|
+
currentTarget.os,
|
|
2816
|
+
currentTarget.arch,
|
|
2817
|
+
config.build.bunVersion,
|
|
2818
|
+
config.build.bunnyBun,
|
|
2819
|
+
);
|
|
2820
|
+
// Note: .bin/bun binary in node_modules is a symlink to the versioned one in another place
|
|
2821
|
+
// in node_modules, so we have to dereference here to get the actual binary in the bundle.
|
|
2822
|
+
const bunBinaryDestInBundlePath =
|
|
2823
|
+
join(appBundleMacOSPath, "bun") + targetBinExt;
|
|
2824
|
+
const destFolder2 = dirname(bunBinaryDestInBundlePath);
|
|
2825
|
+
if (!existsSync(destFolder2)) {
|
|
2826
|
+
mkdirSync(destFolder2, { recursive: true });
|
|
2827
|
+
}
|
|
2828
|
+
cpSync(bunBinarySourcePath, bunBinaryDestInBundlePath, {
|
|
2829
|
+
dereference: true,
|
|
2830
|
+
});
|
|
2673
2831
|
|
|
2674
|
-
if (
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2832
|
+
// Copy ICU data file if it exists (Linux/Windows external ICU builds)
|
|
2833
|
+
// ICU version varies per platform WebKit build, so detect the filename dynamically
|
|
2834
|
+
const bunDir = dirname(bunBinarySourcePath);
|
|
2835
|
+
const icuDataFileName = readdirSync(bunDir).find((f) => /^icudt\d+l\.dat$/.test(f));
|
|
2836
|
+
const icuDataSource = icuDataFileName ? join(bunDir, icuDataFileName) : "";
|
|
2837
|
+
if (icuDataFileName && existsSync(icuDataSource) && targetOS !== "macos") {
|
|
2838
|
+
const icuDataDest = join(appBundleMacOSPath, icuDataFileName);
|
|
2678
2839
|
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
const
|
|
2684
|
-
|
|
2685
|
-
iconPath = tempIcoPath;
|
|
2840
|
+
const locales = config.build?.locales;
|
|
2841
|
+
if (locales && locales !== "*" && Array.isArray(locales) && locales.length > 0) {
|
|
2842
|
+
try {
|
|
2843
|
+
await trimICUData(icuDataSource, icuDataDest, locales);
|
|
2844
|
+
const originalSize = statSync(icuDataSource).size;
|
|
2845
|
+
const trimmedSize = statSync(icuDataDest).size;
|
|
2686
2846
|
console.log(
|
|
2687
|
-
`
|
|
2847
|
+
`Trimmed ICU data: ${(originalSize / 1024 / 1024).toFixed(1)}MB → ${(trimmedSize / 1024 / 1024).toFixed(1)}MB (locales: ${locales.join(", ")})`,
|
|
2688
2848
|
);
|
|
2849
|
+
} catch (error) {
|
|
2850
|
+
console.warn(`Warning: Failed to trim ICU data, copying full file: ${error}`);
|
|
2851
|
+
cpSync(icuDataSource, icuDataDest);
|
|
2689
2852
|
}
|
|
2853
|
+
} else {
|
|
2854
|
+
cpSync(icuDataSource, icuDataDest);
|
|
2855
|
+
console.log(`Copied ICU data file: ${icuDataFileName}`);
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2690
2858
|
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
console.log(`Successfully embedded icon into bun.exe`);
|
|
2859
|
+
// Embed icon into bun.exe on Windows
|
|
2860
|
+
if (targetOS === "win" && config.build.win?.icon) {
|
|
2861
|
+
const iconSourcePath =
|
|
2862
|
+
config.build.win.icon.startsWith("/") ||
|
|
2863
|
+
config.build.win.icon.match(/^[a-zA-Z]:/)
|
|
2864
|
+
? config.build.win.icon
|
|
2865
|
+
: join(projectRoot, config.build.win.icon);
|
|
2699
2866
|
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2867
|
+
if (existsSync(iconSourcePath)) {
|
|
2868
|
+
console.log(`Embedding icon into bun.exe: ${iconSourcePath}`);
|
|
2869
|
+
try {
|
|
2870
|
+
let iconPath = iconSourcePath;
|
|
2871
|
+
|
|
2872
|
+
if (iconSourcePath.toLowerCase().endsWith(".png")) {
|
|
2873
|
+
const pngToIco = (await import("png-to-ico")).default;
|
|
2874
|
+
const tempIcoPath = join(buildFolder, "temp-bun-icon.ico");
|
|
2875
|
+
const icoBuffer = await pngToIco(iconSourcePath);
|
|
2876
|
+
writeFileSync(tempIcoPath, new Uint8Array(icoBuffer));
|
|
2877
|
+
iconPath = tempIcoPath;
|
|
2878
|
+
console.log(
|
|
2879
|
+
`Converted PNG to ICO format for bun.exe: ${tempIcoPath}`,
|
|
2880
|
+
);
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2883
|
+
const { execFileSync } = await import("child_process");
|
|
2884
|
+
const rceditPkgPath = require.resolve("rcedit/package.json");
|
|
2885
|
+
const rceditDir = dirname(rceditPkgPath);
|
|
2886
|
+
const rceditX64 = join(rceditDir, "bin", "rcedit-x64.exe");
|
|
2887
|
+
const rceditExe = existsSync(rceditX64) ? rceditX64 : join(rceditDir, "bin", "rcedit.exe");
|
|
2888
|
+
execFileSync(rceditExe, [bunBinaryDestInBundlePath, "--set-icon", iconPath]);
|
|
2889
|
+
console.log(`Successfully embedded icon into bun.exe`);
|
|
2890
|
+
|
|
2891
|
+
if (iconPath !== iconSourcePath && existsSync(iconPath)) {
|
|
2892
|
+
unlinkSync(iconPath);
|
|
2893
|
+
}
|
|
2894
|
+
} catch (error) {
|
|
2895
|
+
console.warn(`Warning: Failed to embed icon into bun.exe: ${error}`);
|
|
2703
2896
|
}
|
|
2704
|
-
} catch (error) {
|
|
2705
|
-
console.warn(`Warning: Failed to embed icon into bun.exe: ${error}`);
|
|
2706
2897
|
}
|
|
2707
2898
|
}
|
|
2899
|
+
} else if (zigMainBinarySourcePath) {
|
|
2900
|
+
cpSync(zigMainBinarySourcePath, join(appBundleMacOSPath, "main") + targetBinExt, {
|
|
2901
|
+
dereference: true,
|
|
2902
|
+
});
|
|
2708
2903
|
}
|
|
2709
2904
|
|
|
2710
2905
|
// copy native wrapper dynamic library
|
|
2711
2906
|
if (targetOS === "macos") {
|
|
2907
|
+
cpSync(targetPaths.CORE_MACOS, join(appBundleMacOSPath, "libElectrobunCore.dylib"), {
|
|
2908
|
+
dereference: true,
|
|
2909
|
+
});
|
|
2910
|
+
|
|
2712
2911
|
const nativeWrapperMacosSource = targetPaths.NATIVE_WRAPPER_MACOS;
|
|
2713
2912
|
const nativeWrapperMacosDestination = join(
|
|
2714
2913
|
appBundleMacOSPath,
|
|
@@ -2718,6 +2917,10 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
2718
2917
|
dereference: true,
|
|
2719
2918
|
});
|
|
2720
2919
|
} else if (targetOS === "win") {
|
|
2920
|
+
cpSync(targetPaths.CORE_WIN, join(appBundleMacOSPath, "ElectrobunCore.dll"), {
|
|
2921
|
+
dereference: true,
|
|
2922
|
+
});
|
|
2923
|
+
|
|
2721
2924
|
const nativeWrapperMacosSource = targetPaths.NATIVE_WRAPPER_WIN;
|
|
2722
2925
|
const nativeWrapperMacosDestination = join(
|
|
2723
2926
|
appBundleMacOSPath,
|
|
@@ -2737,6 +2940,9 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
2737
2940
|
} else if (targetOS === "linux") {
|
|
2738
2941
|
// Choose the appropriate native wrapper based on bundleCEF setting
|
|
2739
2942
|
const useCEF = config.build.linux?.bundleCEF;
|
|
2943
|
+
cpSync(targetPaths.CORE_LINUX, join(appBundleMacOSPath, "libElectrobunCore.so"), {
|
|
2944
|
+
dereference: true,
|
|
2945
|
+
});
|
|
2740
2946
|
const nativeWrapperLinuxSource = useCEF
|
|
2741
2947
|
? targetPaths.NATIVE_WRAPPER_LINUX_CEF
|
|
2742
2948
|
: targetPaths.NATIVE_WRAPPER_LINUX;
|
|
@@ -2822,13 +3028,7 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
2822
3028
|
});
|
|
2823
3029
|
|
|
2824
3030
|
// cef helpers
|
|
2825
|
-
const cefHelperNames =
|
|
2826
|
-
"bun Helper",
|
|
2827
|
-
"bun Helper (Alerts)",
|
|
2828
|
-
"bun Helper (GPU)",
|
|
2829
|
-
"bun Helper (Plugin)",
|
|
2830
|
-
"bun Helper (Renderer)",
|
|
2831
|
-
];
|
|
3031
|
+
const cefHelperNames = getCEFHelperNames(mainProcess);
|
|
2832
3032
|
|
|
2833
3033
|
const helperSourcePath = targetPaths.CEF_HELPER_MACOS;
|
|
2834
3034
|
cefHelperNames.forEach((helperName) => {
|
|
@@ -2909,13 +3109,7 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
2909
3109
|
}
|
|
2910
3110
|
|
|
2911
3111
|
// Copy CEF helper processes with different names
|
|
2912
|
-
const cefHelperNames =
|
|
2913
|
-
"bun Helper",
|
|
2914
|
-
"bun Helper (Alerts)",
|
|
2915
|
-
"bun Helper (GPU)",
|
|
2916
|
-
"bun Helper (Plugin)",
|
|
2917
|
-
"bun Helper (Renderer)",
|
|
2918
|
-
];
|
|
3112
|
+
const cefHelperNames = getCEFHelperNames(mainProcess);
|
|
2919
3113
|
|
|
2920
3114
|
const helperSourcePath = targetPaths.CEF_HELPER_WIN;
|
|
2921
3115
|
if (existsSync(helperSourcePath)) {
|
|
@@ -3047,13 +3241,7 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
3047
3241
|
});
|
|
3048
3242
|
|
|
3049
3243
|
// Copy CEF helper processes with different names
|
|
3050
|
-
const cefHelperNames =
|
|
3051
|
-
"bun Helper",
|
|
3052
|
-
"bun Helper (Alerts)",
|
|
3053
|
-
"bun Helper (GPU)",
|
|
3054
|
-
"bun Helper (Plugin)",
|
|
3055
|
-
"bun Helper (Renderer)",
|
|
3056
|
-
];
|
|
3244
|
+
const cefHelperNames = getCEFHelperNames(mainProcess);
|
|
3057
3245
|
|
|
3058
3246
|
const helperSourcePath = targetPaths.CEF_HELPER_LINUX;
|
|
3059
3247
|
if (existsSync(helperSourcePath)) {
|
|
@@ -3222,22 +3410,22 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
3222
3410
|
}
|
|
3223
3411
|
} // end if (!isCarrotOnly)
|
|
3224
3412
|
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
});
|
|
3413
|
+
if (mainProcess === "bun") {
|
|
3414
|
+
// transpile developer's bun code
|
|
3415
|
+
const bunDestFolder = join(appBundleAppCodePath, "bun");
|
|
3416
|
+
const { entrypoint: _bunEntrypoint, ...bunBuildOptions } = bunConfig;
|
|
3417
|
+
const buildResult = await Bun.build({
|
|
3418
|
+
...bunBuildOptions,
|
|
3419
|
+
entrypoints: [bunSource],
|
|
3420
|
+
outdir: bunDestFolder,
|
|
3421
|
+
target: "bun",
|
|
3422
|
+
});
|
|
3236
3423
|
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3424
|
+
if (!buildResult.success) {
|
|
3425
|
+
console.error("failed to build", bunSource);
|
|
3426
|
+
printBuildLogs(buildResult.logs);
|
|
3427
|
+
throw new Error("Build failed: bun build failed");
|
|
3428
|
+
}
|
|
3241
3429
|
}
|
|
3242
3430
|
|
|
3243
3431
|
// transpile developer's view code
|
|
@@ -3402,6 +3590,55 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
3402
3590
|
}
|
|
3403
3591
|
}
|
|
3404
3592
|
|
|
3593
|
+
// Build slate UIs if configured.
|
|
3594
|
+
// slateUIs mirrors remoteUIs, but points at an ESM module entry file instead of an HTML page.
|
|
3595
|
+
const resolvedSlateUIs: Record<string, { name: string; path: string }> = {};
|
|
3596
|
+
if (carrotConfig.slateUIs) {
|
|
3597
|
+
for (const slateUIName in carrotConfig.slateUIs) {
|
|
3598
|
+
const slateUIConfig = carrotConfig.slateUIs[slateUIName]!;
|
|
3599
|
+
const label = slateUIConfig.name || slateUIName;
|
|
3600
|
+
|
|
3601
|
+
if (slateUIConfig.entrypoint) {
|
|
3602
|
+
const slateUISource = join(projectRoot, slateUIConfig.entrypoint);
|
|
3603
|
+
if (!existsSync(slateUISource)) {
|
|
3604
|
+
console.error(`Slate UI entrypoint not found: ${slateUISource}`);
|
|
3605
|
+
continue;
|
|
3606
|
+
}
|
|
3607
|
+
const slateUIDestFolder = join(carrotBuildDir, "slate-ui", slateUIName);
|
|
3608
|
+
mkdirSync(slateUIDestFolder, { recursive: true });
|
|
3609
|
+
|
|
3610
|
+
const { entrypoint: _entrypoint, name: _name, path: _path, ...slateUIBuildOptions } = slateUIConfig;
|
|
3611
|
+
const slateUIBuildResult = await Bun.build({
|
|
3612
|
+
...slateUIBuildOptions,
|
|
3613
|
+
entrypoints: [slateUISource],
|
|
3614
|
+
outdir: slateUIDestFolder,
|
|
3615
|
+
target: "browser",
|
|
3616
|
+
format: "esm",
|
|
3617
|
+
});
|
|
3618
|
+
|
|
3619
|
+
if (!slateUIBuildResult.success) {
|
|
3620
|
+
console.error(`Failed to build slate UI: ${slateUIName}`);
|
|
3621
|
+
printBuildLogs(slateUIBuildResult.logs);
|
|
3622
|
+
continue;
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
resolvedSlateUIs[slateUIName] = {
|
|
3626
|
+
name: label,
|
|
3627
|
+
path: `slate-ui/${slateUIName}/index.js`,
|
|
3628
|
+
};
|
|
3629
|
+
} else if (slateUIConfig.path) {
|
|
3630
|
+
resolvedSlateUIs[slateUIName] = {
|
|
3631
|
+
name: label,
|
|
3632
|
+
path: slateUIConfig.path,
|
|
3633
|
+
};
|
|
3634
|
+
} else {
|
|
3635
|
+
console.warn(
|
|
3636
|
+
`Slate UI "${slateUIName}" has neither entrypoint nor path; skipping.`,
|
|
3637
|
+
);
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
|
|
3405
3642
|
// Write carrot.json manifest
|
|
3406
3643
|
const carrotManifest = {
|
|
3407
3644
|
id: carrotConfig.id,
|
|
@@ -3419,9 +3656,15 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
3419
3656
|
: spec,
|
|
3420
3657
|
]),
|
|
3421
3658
|
),
|
|
3659
|
+
contributions:
|
|
3660
|
+
carrotConfig.contributions &&
|
|
3661
|
+
Object.keys(carrotConfig.contributions).length > 0
|
|
3662
|
+
? carrotConfig.contributions
|
|
3663
|
+
: undefined,
|
|
3422
3664
|
worker: { relativePath: "worker.js" },
|
|
3423
3665
|
view: existsSync(viewsSrc) ? { relativePath: "views/index.html" } : undefined,
|
|
3424
3666
|
remoteUIs: Object.keys(resolvedRemoteUIs).length > 0 ? resolvedRemoteUIs : undefined,
|
|
3667
|
+
slateUIs: Object.keys(resolvedSlateUIs).length > 0 ? resolvedSlateUIs : undefined,
|
|
3425
3668
|
};
|
|
3426
3669
|
writeFileSync(
|
|
3427
3670
|
join(carrotBuildDir, "carrot.json"),
|
|
@@ -3676,13 +3919,16 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
3676
3919
|
const bundlesCEF = platformConfig?.bundleCEF ?? false;
|
|
3677
3920
|
|
|
3678
3921
|
const buildJsonObj: Record<string, unknown> = {
|
|
3922
|
+
mainProcess,
|
|
3679
3923
|
defaultRenderer: platformConfig?.defaultRenderer ?? "native",
|
|
3680
3924
|
availableRenderers: bundlesCEF ? ["native", "cef"] : ["native"],
|
|
3681
3925
|
runtime: config.runtime ?? {},
|
|
3682
3926
|
...(bundlesCEF
|
|
3683
3927
|
? { cefVersion: config.build?.cefVersion ?? DEFAULT_CEF_VERSION_STRING }
|
|
3684
3928
|
: {}),
|
|
3685
|
-
|
|
3929
|
+
...(mainProcess === "bun"
|
|
3930
|
+
? { bunVersion: config.build?.bunVersion ?? BUN_VERSION }
|
|
3931
|
+
: {}),
|
|
3686
3932
|
...(config.build?.bunnyBun ? { bunnyBun: config.build.bunnyBun } : {}),
|
|
3687
3933
|
};
|
|
3688
3934
|
|
|
@@ -3760,10 +4006,9 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
3760
4006
|
// Tar the app bundle for all platforms
|
|
3761
4007
|
createTar(tarPath, buildFolder, [basename(appBundleFolderPath)]);
|
|
3762
4008
|
|
|
3763
|
-
//
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
}
|
|
4009
|
+
// This branch only runs for non-dev release packaging, so the temp app bundle
|
|
4010
|
+
// can always be removed after the tarball is produced.
|
|
4011
|
+
rmSync(appBundleFolderPath, { recursive: true });
|
|
3767
4012
|
|
|
3768
4013
|
// generate bsdiff
|
|
3769
4014
|
// https://storage.googleapis.com/eggbun-static/electrobun-playground/canary/ElectrobunPlayground-canary.app.tar.zst
|
|
@@ -4443,10 +4688,15 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
4443
4688
|
const watchDirs = new Set<string>();
|
|
4444
4689
|
|
|
4445
4690
|
// Bun entrypoint directory
|
|
4446
|
-
if (config.build.bun?.entrypoint) {
|
|
4691
|
+
if (config.build.mainProcess !== "zig" && config.build.bun?.entrypoint) {
|
|
4447
4692
|
watchDirs.add(join(projectRoot, dirname(config.build.bun.entrypoint)));
|
|
4448
4693
|
}
|
|
4449
4694
|
|
|
4695
|
+
// Zig entrypoint directory
|
|
4696
|
+
if (config.build.mainProcess === "zig" && config.build.zig?.entrypoint) {
|
|
4697
|
+
watchDirs.add(join(projectRoot, dirname(config.build.zig.entrypoint)));
|
|
4698
|
+
}
|
|
4699
|
+
|
|
4450
4700
|
// View entrypoint directories
|
|
4451
4701
|
if (config.build.views) {
|
|
4452
4702
|
for (const viewConfig of Object.values(config.build.views)) {
|
|
@@ -4526,17 +4776,37 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
4526
4776
|
);
|
|
4527
4777
|
|
|
4528
4778
|
function shouldIgnore(fullPath: string): boolean {
|
|
4779
|
+
const resolvedFullPath = path.resolve(fullPath);
|
|
4780
|
+
const pathSegments = resolvedFullPath.split(path.sep).filter(Boolean);
|
|
4781
|
+
const genericIgnoredSegments = new Set([
|
|
4782
|
+
"node_modules",
|
|
4783
|
+
path.basename(buildDir),
|
|
4784
|
+
path.basename(artifactDir),
|
|
4785
|
+
".electrobun-cache",
|
|
4786
|
+
]);
|
|
4787
|
+
if (pathSegments.some((segment) => genericIgnoredSegments.has(segment))) {
|
|
4788
|
+
return true;
|
|
4789
|
+
}
|
|
4529
4790
|
// Check built-in ignore dirs
|
|
4530
4791
|
if (
|
|
4531
4792
|
ignoreDirs.some(
|
|
4532
|
-
(ignored) =>
|
|
4533
|
-
|
|
4793
|
+
(ignored) => {
|
|
4794
|
+
const relativeToIgnored = path.relative(ignored, resolvedFullPath);
|
|
4795
|
+
return (
|
|
4796
|
+
relativeToIgnored === "" ||
|
|
4797
|
+
(!relativeToIgnored.startsWith("..") &&
|
|
4798
|
+
!path.isAbsolute(relativeToIgnored))
|
|
4799
|
+
);
|
|
4800
|
+
},
|
|
4534
4801
|
)
|
|
4535
4802
|
) {
|
|
4536
4803
|
return true;
|
|
4537
4804
|
}
|
|
4538
4805
|
// Check user-configured watchIgnore globs (match against project-relative path)
|
|
4539
|
-
const relativePath =
|
|
4806
|
+
const relativePath = path
|
|
4807
|
+
.relative(projectRoot, resolvedFullPath)
|
|
4808
|
+
.split(path.sep)
|
|
4809
|
+
.join("/");
|
|
4540
4810
|
if (ignoreGlobs.some((glob) => glob.match(relativePath))) {
|
|
4541
4811
|
return true;
|
|
4542
4812
|
}
|
|
@@ -4787,6 +5057,10 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
4787
5057
|
...defaultConfig.build.bun,
|
|
4788
5058
|
...(loadedConfig?.build?.bun || {}),
|
|
4789
5059
|
},
|
|
5060
|
+
zig: {
|
|
5061
|
+
...defaultConfig.build.zig,
|
|
5062
|
+
...(loadedConfig?.build?.zig || {}),
|
|
5063
|
+
},
|
|
4790
5064
|
},
|
|
4791
5065
|
runtime: {
|
|
4792
5066
|
...defaultConfig.runtime,
|
|
@@ -5084,13 +5358,10 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
5084
5358
|
}
|
|
5085
5359
|
|
|
5086
5360
|
// Sign CEF helper apps (they're in the main Frameworks directory, not inside CEF framework)
|
|
5087
|
-
const
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
"bun Helper (Alerts).app",
|
|
5092
|
-
"bun Helper (Renderer).app",
|
|
5093
|
-
];
|
|
5361
|
+
const mainProcess = config.build.mainProcess ?? "bun";
|
|
5362
|
+
const cefHelperApps = getCEFHelperNames(mainProcess).map(
|
|
5363
|
+
(helperName) => `${helperName}.app`,
|
|
5364
|
+
);
|
|
5094
5365
|
|
|
5095
5366
|
for (const helperApp of cefHelperApps) {
|
|
5096
5367
|
const helperPath = join(frameworksPath, helperApp);
|