Skip to content

Commit f1f43ec

Browse files
committed
Bundle bootstrap with esbuild
1 parent 61626b4 commit f1f43ec

File tree

3 files changed

+396
-5
lines changed

3 files changed

+396
-5
lines changed

bootstrap/esbuild.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import esbuild, { Plugin } from "esbuild";
2+
import { getAbi } from "node-abi";
3+
import fs from "fs/promises";
4+
import path from "path";
5+
import { glob } from "glob";
6+
7+
interface PrebuildifyOptions {
8+
// CSV list of prebuildify targets. Combination of os.platform() and os.arch(), e.g. `win32-x64`.
9+
// By default, all will be copied.
10+
prebuildifyTargets?: string;
11+
12+
// Ideally exactly the node version you're distributing with. (If only e.g. "node22" is soecified, it will be assumed that the prebuilds have to support 22.0.0!)
13+
// By default, this uses the target set in esbuild options. If neither exists, everything will be copies.
14+
target?: string;
15+
}
16+
17+
const prebuildifyPlugin = ({
18+
prebuildifyTargets,
19+
target,
20+
}: PrebuildifyOptions = {}): Plugin => ({
21+
name: "prebuildify",
22+
setup(build) {
23+
build.onEnd(async () => {
24+
const options = build.initialOptions;
25+
const outdir =
26+
options.outdir || (options.outfile && path.dirname(options.outfile));
27+
28+
if (!outdir) {
29+
throw new Error(
30+
"Either `outfile` or `outdir` must be specified when using this plugin.",
31+
);
32+
}
33+
34+
const outPrebuilds = path.join(outdir, "prebuilds");
35+
await fs.mkdir(outPrebuilds, { recursive: true });
36+
37+
const prebuilds = await glob("node_modules/**/prebuilds/**/*.node");
38+
39+
// Get target ABI version if specified
40+
const targets =
41+
typeof options.target === "string"
42+
? [options.target]
43+
: options.target || target?.split(",");
44+
const targetAbis =
45+
targets &&
46+
new Set(
47+
targets
48+
.filter((target) => target.startsWith("node"))
49+
.map((target) => target.replace("node", ""))
50+
.map((target) => (target.includes(".") ? target : target + ".0.0"))
51+
.map((target) => getAbi(target, "node")),
52+
);
53+
54+
// Copy relevant prebuilds
55+
const parsedPlatforms = prebuildifyTargets?.split(",");
56+
for (const prebuild of prebuilds) {
57+
const filename = path.basename(prebuild);
58+
const platform = path.dirname(prebuild).split(path.sep).pop();
59+
60+
if (
61+
platform &&
62+
parsedPlatforms &&
63+
!parsedPlatforms?.includes(platform)
64+
) {
65+
continue;
66+
}
67+
68+
if (targetAbis) {
69+
const abi = filename.match(/abi([0-9]+)./)?.[1];
70+
if (abi && !targetAbis.has(abi)) {
71+
continue;
72+
}
73+
}
74+
75+
const outPath = path.join(outPrebuilds, platform!);
76+
await fs.mkdir(outPath, { recursive: true });
77+
await fs.copyFile(prebuild, path.join(outPath, filename));
78+
}
79+
});
80+
},
81+
});
82+
83+
await esbuild.build({
84+
entryPoints: ["index.ts"],
85+
bundle: true,
86+
platform: "node",
87+
target: "node22.11.0",
88+
outfile: "dist/index.js",
89+
plugins: [
90+
prebuildifyPlugin({
91+
prebuildifyTargets: "win32-x64",
92+
}),
93+
],
94+
});

bootstrap/package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
"version": "1.0.0",
44
"main": "index.ts",
55
"license": "MIT",
6+
"type": "module",
67
"scripts": {
7-
"build": "rimraf dist && ncc build index.ts",
8+
"build": "rimraf dist && pnpm tsx esbuild.ts",
9+
"build:esfirsttry": "esbuild index.ts --bundle --platform=node --target=node22 --external:*.node --outfile=dist/index.cjs",
10+
"build:old": "rimraf dist && ncc build index.ts",
811
"start": "tsx ."
912
},
1013
"dependencies": {
@@ -13,7 +16,11 @@
1316
"open": "^10.1.0"
1417
},
1518
"devDependencies": {
19+
"@types/node-abi": "^3.0.3",
1620
"@vercel/ncc": "0.38.3",
21+
"esbuild": "^0.24.2",
22+
"glob": "^11.0.1",
23+
"node-abi": "^3.73.0",
1724
"rimraf": "6.0.1",
1825
"tsx": "^4.19.2"
1926
},

0 commit comments

Comments
 (0)