Compare commits

...

255 Commits

Author SHA1 Message Date
pfg
fc6a168981 . 2025-01-06 13:29:20 -08:00
pfg
ef5427ddc6 Merge branch 'main' into pfg/fix-15536 2025-01-06 13:10:50 -08:00
Jarred Sumner
e92a487fad Handle duplicate column names and numeric column names in Bun.sql (#16178) 2025-01-06 12:05:09 -08:00
Don Isaac
178e373712 build(bindgen): check for corresponding .zig file (#15896)
Co-authored-by: Don Isaac <don@bun.sh>
2025-01-06 11:46:25 -08:00
Don Isaac
189684f173 feat(node/path): support matchesGlob (#15917) 2025-01-06 10:36:11 -08:00
Eric Liu
8d82302ec5 docs(plugins): fix typos (#16174) 2025-01-05 18:50:03 -08:00
Ciro Spaciari
034f776047 WIP: S3 improvements (#16167) 2025-01-04 19:57:35 -08:00
Jarred Sumner
8a469cce7e Default to "auto" instead of "us-east-1" 2025-01-04 06:21:32 -08:00
Jarred Sumner
e532456cfe Update Bun.S3 type definitions 2025-01-04 05:24:57 -08:00
Jarred Sumner
cc52828d54 Remove rejectOnNextTick (#16161) 2025-01-04 04:17:03 -08:00
Jarred Sumner
5fe9b6f426 Improve MinIO support in Bun.S3 2025-01-04 02:44:17 -08:00
Ciro Spaciari
a53f2e6aaa fix test on windows (#16151) 2025-01-04 01:22:48 -08:00
Dylan Conway
79aa5d16df skip root scripts if root is filtered out with --filter (#16152)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2025-01-04 01:22:24 -08:00
Jarred Sumner
4454ebb152 Allow http:// endpoints in Bun.S3 2025-01-04 01:08:27 -08:00
Jarred Sumner
33233b1607 Don't include //# sourcemap comments in .html or .css files (#16159) 2025-01-04 00:32:17 -08:00
Don Isaac
ed0b4e1a6e fix(build/html): handle relative paths in script src (#16153) 2025-01-04 00:23:51 -08:00
Jarred Sumner
debd8a0eba Support BUN_CONFIG_VERBOSE_FETCH in S3 2025-01-04 00:05:14 -08:00
Jarred Sumner
cc5ee01752 Initial S3 docs 2025-01-03 23:08:14 -08:00
Jarred Sumner
d5fc928ca8 S3 cleanup (#16039)
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
2025-01-03 19:11:48 -08:00
Dylan Conway
2043613a62 support bun install --filter <pattern> (#16093) 2025-01-03 18:39:41 -08:00
Michael H
5caeeb9549 docs: contributing windows link be absolute to bun.sh (#16127) 2025-01-03 17:56:00 -08:00
Dylan Conway
fa7376b042 add bun install --lockfile-only (#16143) 2025-01-03 17:55:40 -08:00
Jarred Sumner
fd9d9242d8 Support absolute paths when bundling HTML (#16149) 2025-01-03 17:54:07 -08:00
190n
78498b4244 Include array length and promise status in V8 heap snapshots (oven-sh/WebKit#75) (#16141) 2025-01-03 17:33:17 -08:00
Dylan Conway
c713c0319b fix(install): extra quotes in bun.lock (#16139) 2025-01-03 15:16:52 -08:00
Jarred Sumner
912a2cbc12 Expose some no-ops (#16125)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2025-01-03 13:57:46 -08:00
Dylan Conway
c130df6c58 start verdaccio in multiple test files (#16118) 2025-01-03 08:21:00 -08:00
Jarred Sumner
f0cb1b723e Remove spinlock in libpas on Linux (#16130) 2025-01-03 04:32:27 -08:00
Jarred Sumner
79430091a1 Add v8.writeHeapSnapshot (#16123) 2025-01-02 21:24:16 -08:00
Jarred Sumner
ab8fe1a6c3 Bump 2025-01-02 21:17:47 -08:00
Michael H
dda49d17f9 docs: fix #16116 (#16122) 2025-01-02 20:29:05 -08:00
Jarred Sumner
faec20080d Update nodejs-apis.md 2025-01-02 20:27:30 -08:00
Jarred Sumner
f834304c27 Support generating V8 Heap Snapshots (#16109) 2025-01-02 20:15:13 -08:00
Jarred Sumner
b59e7c7682 Add missing exception checks to JSPropertyIterator (#16121)
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2025-01-02 18:55:38 -08:00
Yiheng
40724d29ac Update cache.md (#16028) 2025-01-02 18:24:03 -08:00
Dylan Conway
d9125143b7 lockfile: escape names in bun.lock (#16120) 2025-01-02 18:22:39 -08:00
Jarred Sumner
4dcfd686b4 Fix build 2025-01-02 16:22:58 -08:00
Jarred Sumner
012d70f42e Fix bug with PATH in Bun.spawn (#16067) 2025-01-02 16:03:42 -08:00
Dylan Conway
a85bd42989 Add short flag for --filter (#16058) 2025-01-02 15:53:45 -08:00
Chawye Hsu
d714943d87 fix(install): read bunfig install.cache.dir (#10699)
Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>
2025-01-02 15:46:27 -08:00
Ciro Spaciari
ae18cc0ef3 fix(server) HEAD Requests followup (#16115) 2025-01-02 15:08:03 -08:00
Jarred Sumner
a8b3f732c5 Report memory size of performance.measure / performance.mark (#16094) 2025-01-01 19:23:13 -08:00
KOMIYA Atsushi
ee955591e2 Update defines to define in cc function documentation (#16097) 2025-01-01 19:07:18 -08:00
Ciro Spaciari
7a52ec55a5 fix(server) HEAD requests (#16099) 2025-01-01 19:06:08 -08:00
Johan Bergström
aa1b0c9c40 fix: avoid world-writable permissions for lockfiles (#16018) 2025-01-01 10:56:10 -08:00
Jarred Sumner
be959e111a Do not assert valid windows path in chdirOSPath because the SetCurrentDirectoryW function will validate the path 2024-12-31 21:08:07 -08:00
Jarred Sumner
19191659cf Avoid resolving substrings in bun:sqlite and Buffer.byteLength (#16092) 2024-12-31 19:48:33 -08:00
Jarred Sumner
30008ed0fc Bump WebKit again (#16091) 2024-12-31 18:17:56 -08:00
Jarred Sumner
e3a1d026f9 Fix crash in bake on load (#16021) 2024-12-31 17:16:12 -08:00
Jarred Sumner
02196cbf0e Avoid resolving substrings unnecessarily (#16090) 2024-12-31 17:06:49 -08:00
Jarred Sumner
1ae855223c Bump WebKit (#16068) 2024-12-31 14:48:54 -08:00
Dylan Conway
5058bd3913 handle bundle(d)Dependencies in bun install (#16055) 2024-12-31 13:40:55 -08:00
Don Isaac
b406509afd refactor: remove unused script execution context file (#16059) 2024-12-31 13:31:33 -08:00
Dylan Conway
82f9b13e08 docs: fix bun.lock section (#16088) 2024-12-31 11:40:28 -08:00
Dylan Conway
37e7f5ba8f transpiler: fix crash with malformed enums (#16084) 2024-12-31 09:09:09 -08:00
Navishkar Rao
babd8b6028 Update nextjs.md docs with starter example (#16072) 2024-12-30 22:26:19 -08:00
Don Isaac
ab52058439 fix(us): memory leak when getting root certificate (#16073) 2024-12-30 22:20:15 -08:00
Lars Volkheimer
e96dded366 fix formatting of Set in Bun.inspect() (#16013) 2024-12-30 13:44:40 -08:00
Jarred Sumner
76bfceae81 Support jsonb, idle_timeout, connection_timeout, max_lifetime timeouts in bun:sql. Add onopen and onclose callbacks. Fix missing "code" property appearing in errors. Add error codes for postgres. (#16045) 2024-12-30 13:25:01 -08:00
Dylan Conway
f0073bfa81 fix(install): free correct pointer in bun patch --commit (#16064) 2024-12-30 12:38:39 -08:00
Devanand Sharma
18ac7f9509 Add remove() and isRemoved in HTMLRewriterTypes.Doctype interface (#16031) 2024-12-28 22:57:25 -08:00
Ciro Spaciari
fe4176e403 feat(s3) s3 client (#15740)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: cirospaciari <cirospaciari@users.noreply.github.com>
2024-12-28 17:46:22 -08:00
Jarred Sumner
ed0980cf94 Make creating errors slightly faster (#16023) 2024-12-28 01:32:32 -08:00
Jarred Sumner
dd243a06a5 Log slow lifecycle scripts (#16027) 2024-12-28 01:31:30 -08:00
Jarred Sumner
7b06872abb Deflake fetch tests (#16000) 2024-12-27 14:07:41 -08:00
Don Isaac
d8e644fc25 fix(node/path): crash when joining long paths (#16019) 2024-12-27 17:58:21 +00:00
Meghan Denny
4bcc5b25d9 node: fix all of test-event-emitter (#16009) 2024-12-27 01:34:49 -08:00
Jarred Sumner
19675f474a Update .cursorignore 2024-12-26 11:48:30 -08:00
Jarred Sumner
bba998a611 Create .cursorignore 2024-12-26 11:48:11 -08:00
Jarred Sumner
145a7fd92e Better unicode identifier start / continue check (#15455) 2024-12-25 23:02:46 -08:00
Jarred Sumner
d4c0432a5f Refactor JS parser visitor step into individual functions to reduce stack space usage (#15993) 2024-12-25 23:02:05 -08:00
Jarred Sumner
379c79ee2e Fix typo 2024-12-25 22:35:52 -08:00
Jarred Sumner
2b2ca3275c Improve stack overflow, show more properties in Error objects (#15985)
Co-authored-by: Dave Caruso <me@paperdave.net>
2024-12-25 21:47:13 -08:00
Jarred Sumner
7317c7b4a2 Compress completions list to make zig build a little faster (#15992) 2024-12-25 18:04:46 -08:00
Jarred Sumner
608101c975 Add zlib microbenchmark
need to improve this
2024-12-24 04:20:24 -08:00
Jarred Sumner
52a568d2b2 Fix flaky zlib dictionary test (#15976) 2024-12-24 02:27:07 -08:00
Jarred Sumner
60cb505a98 Use JSObject instead of JSFunction in Bun.plugin (#15968) 2024-12-23 12:33:11 -08:00
Jarred Sumner
da54e81955 Support bundling HTML files and their js, css, and assets in Bun.build and bun build (#15940) 2024-12-23 11:04:38 -08:00
Jarred Sumner
774e30d383 Make originalLine and originalColumn getter calls not observable (#15951) 2024-12-23 03:40:51 -08:00
Jarred Sumner
c6b22d399f Fix showing source code that looks like export default "file-path" (#15957) 2024-12-23 03:40:00 -08:00
Jarred Sumner
1fa6d9e695 +2 passing node:events tests (#15952) 2024-12-23 01:45:13 -08:00
Jarred Sumner
4f8a6b33c4 +5 passing node:zlib tests (#15944) 2024-12-22 20:39:42 -08:00
Martin Amps
a6ad3b9be4 add --elide-lines override flag for workspace filtering (#15837) 2024-12-22 00:14:46 -08:00
github-actions[bot]
b63a6c83b4 deps: update libdeflate to v1.23 (#15934)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2024-12-21 21:28:17 -08:00
Don Isaac
14b44aeb49 fix(process): process.kill allows zero or negative pids (#15920) 2024-12-21 08:45:39 +00:00
Jarred Sumner
d6b9c444c1 Rename src/bundler.zig to src/transpiler.zig (#15921)
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2024-12-21 00:59:37 -08:00
Don Isaac
3c37b7f806 fix(lexer): do not treat '#bun' in a url as a pragma (#15912)
Co-authored-by: Don Isaac <don@bun.sh>
Co-authored-by: DonIsaac <DonIsaac@users.noreply.github.com>
2024-12-21 04:57:42 +00:00
Don Isaac
acb9fdfcf5 refactor: organize native glob code (#15914)
Co-authored-by: DonIsaac <DonIsaac@users.noreply.github.com>
2024-12-20 20:59:07 -08:00
Jarred Sumner
50eec0025b Add regression test for #15902 2024-12-20 19:28:13 -08:00
Jarred Sumner
ac3cd09a42 Bump 2024-12-20 17:54:39 -08:00
Dylan Conway
6e222c8523 fix #15902 (#15911) 2024-12-20 17:03:37 -08:00
Jarred Sumner
b8f28ed8af Bump 2024-12-20 03:44:55 -08:00
dave caruso
7b3554f90c feat(bundler): add --windows-icon, --windows-no-console, fix bun.exe's main icon (#15894)
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2024-12-20 03:22:16 -08:00
Jarred Sumner
0c50b0fcec Fix potential runtime crash if transpiler generates invalid commonjs (#15898) 2024-12-20 02:12:08 -08:00
Jarred Sumner
bf9c6fdc00 Revert "fix(lexer): do not treat '#bun' in a url as a pragma" (#15899) 2024-12-20 01:31:48 -08:00
Don Isaac
1d9fbe7d67 fix(lexer): do not treat '#bun' in a url as a pragma (#15888)
Co-authored-by: Don Isaac <don@bun.sh>
Co-authored-by: DonIsaac <DonIsaac@users.noreply.github.com>
2024-12-20 01:26:30 -08:00
Brian Kim
a8893dcae5 Fix macro imports (#15833) 2024-12-20 08:34:45 +00:00
dave caruso
8a4852b8b0 fix: pass homedir test (#15811)
Co-authored-by: paperdave <paperdave@users.noreply.github.com>
Co-authored-by: Ashcon Partovi <ashcon@partovi.net>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2024-12-20 00:36:59 -08:00
Dylan Conway
45ca9e08c3 fix(install): peer/dev/optional = false lockfile fix (#15874)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-20 00:34:21 -08:00
Jarred Sumner
e3fed49082 Implement expect().toHaveBeenCalledOnce() (#15871)
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2024-12-20 00:23:55 -08:00
Dylan Conway
9164760a5a fix pnpm.test.ts (#15897) 2024-12-19 23:52:50 -08:00
Dylan Conway
747828965e fix(install): sort tree dependencies by behavior and name (#15895) 2024-12-19 23:14:33 -08:00
Jarred Sumner
35679b3178 Update node_util_binding.zig 2024-12-19 17:34:38 -08:00
Don Isaac
960b2b2c11 perf(node:util): fast path for extractedSplitNewLines (#15838)
Co-authored-by: Don Isaac <don@bun.sh>
Co-authored-by: DonIsaac <DonIsaac@users.noreply.github.com>
2024-12-19 23:42:18 +00:00
Don Isaac
f546a9b605 chore: add usage messages to check-node.sh (#15885)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-19 22:32:25 +00:00
Ashcon Partovi
3cbcd43f9a ci: Enable merge queue, disable soft failing tests 2024-12-19 11:18:13 -08:00
Jarred Sumner
b254e69322 Fix svelte testing guide 2024-12-19 03:44:46 -08:00
Jarred Sumner
5dcfc6f10f Update svelte-test.md 2024-12-19 03:17:02 -08:00
Jarred Sumner
d9b2396948 Update svelte-test.md 2024-12-19 03:16:06 -08:00
Jarred Sumner
e21050dc6f Update svelte-test.md 2024-12-19 03:09:21 -08:00
Jarred Sumner
276da2dbf5 Create svelte-test.md 2024-12-19 03:09:10 -08:00
Jarred Sumner
b539ca32ea Make "use strict" become CommonJS if we don't know whether it's ESM or CJS (#15868) 2024-12-18 23:23:50 -08:00
Jarred Sumner
ebc2eb5c5b Support colors array in util.styleText (#15872) 2024-12-18 23:23:42 -08:00
Jarred Sumner
10990f5213 Fixes #3554 (#15870) 2024-12-18 22:54:11 -08:00
Jarred Sumner
42f23f0966 PR feedback from #15865 2024-12-18 19:42:33 -08:00
Jarred Sumner
ac6723eab7 +13 passing node:vm tests (#15865) 2024-12-18 19:41:37 -08:00
Michael H
8e20d02b9b update registry scope guide (.npmrc is supported) (#15866) 2024-12-18 19:28:23 -08:00
dave caruso
41924211f2 add throw: true in Bun.build, to be made default in 1.2 (#15861) 2024-12-18 19:27:59 -08:00
Michael H
5d2b72aa3b don't make inline sourcemap in normal vscode terminal (#15862) 2024-12-18 18:30:39 -08:00
Don Isaac
e66a347158 fix(module-loader): use a more descriptive crash message (#15831)
Co-authored-by: Don Isaac <don@bun.sh>
Co-authored-by: DonIsaac <DonIsaac@users.noreply.github.com>
2024-12-18 14:10:46 -08:00
Jarred Sumner
b5b51004e8 Bump WebKit (#15828)
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2024-12-17 20:59:10 -08:00
Dylan Conway
2272b852ba fix(install): npm version to git resolution package-lock.json migration (#15810) 2024-12-17 19:59:23 -08:00
Michael H
df5f95b19e vscode: allow trailing comma in bun.lock (#15747) 2024-12-17 18:05:30 -08:00
190n
59e06b0df5 fix(napi): set lossless parameter in napi_get_value_bigint_{int64,uint64}, and trim leading zeroes in napi_create_bigint_words (#15804) 2024-12-17 17:38:12 -08:00
Dylan Conway
430c1dd583 add install.saveTextLockfile to bunfig.toml (#15827) 2024-12-17 16:52:04 -08:00
Jarred Sumner
ad1738d23c Fix process.on from non-mainthread (#15825) 2024-12-17 16:51:19 -08:00
Jarred Sumner
b7efaa5b19 Bump 2024-12-17 15:30:18 -08:00
Jarred Sumner
1d48561709 Update plugins.md 2024-12-17 01:49:02 -08:00
Jarred Sumner
f2e0d606b6 Update plugins.md 2024-12-17 01:34:17 -08:00
Jarred Sumner
385868f504 Update plugins.md 2024-12-17 01:34:00 -08:00
Jarred Sumner
eecbeb32ec Move bundler plugin docs 2024-12-17 01:31:14 -08:00
Jarred Sumner
903d8bfa4a Be more careful about setting the rlimit max 2024-12-17 01:15:24 -08:00
Don Isaac
9524e1c86a fix: Bun.deepMatch on circular objects causing segfault (#15672)
Co-authored-by: Don Isaac <don@bun.sh>
Co-authored-by: DonIsaac <DonIsaac@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-16 22:33:34 -08:00
dave caruso
77acfa23a7 pass all upstream node:os tests, all supported node:async_hooks tests (#15802) 2024-12-16 22:22:54 -08:00
Jarred Sumner
9d3b461a25 CI: Remove unnecessary config 2024-12-16 20:38:05 -08:00
Jarred Sumner
9d63ee0edf CI: test concurrency group 2024-12-16 20:33:56 -08:00
Jarred Sumner
a3090fc204 CI: cancel previous canary build 2024-12-16 20:33:09 -08:00
Jarred Sumner
32c1fdf205 Rename estimateDirectMemoryUsageOf to estimateShallowMemoryUsageOf 2024-12-16 20:18:04 -08:00
Jarred Sumner
aada6f930f Fix heap snapshots memory usage stats. Introduce estimateDirectMemoryUsageOf function in "bun:jsc" (#15790) 2024-12-16 20:16:23 -08:00
Zack Radisic
3906d02e2c CSS fixes (#15806) 2024-12-16 19:40:53 -08:00
pfg
f276484f25 Add lldb scripts for zig & jsc (#15807) 2024-12-16 18:31:41 -08:00
Jarred Sumner
4bef96e8d1 Prevent unnecessary postinstall script from causing bun install to hang in unreliable networks 2024-12-16 18:19:43 -08:00
Michael H
f2d955f686 vscode extension: use new debug terminal provider (#15801)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-16 17:29:12 -08:00
Ashcon Partovi
e8b85cff40 ci: Retry and detect flaky tests (#15798) 2024-12-16 17:04:33 -08:00
Dylan Conway
d5f1f2f8ad Use the same hoisting logic for text lockfile (#15778) 2024-12-16 16:37:46 -08:00
Michael H
67e4aec990 attempt to fix debugger (#15788)
Co-authored-by: RiskyMH <RiskyMH@users.noreply.github.com>
2024-12-16 16:34:55 -08:00
Jarred Sumner
540a0a89ab Fix text input with ink (#15800) 2024-12-16 16:33:15 -08:00
190n
4eae3a90e8 fix(napi): Make napi_wrap work on regular objects (#15622)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-16 15:54:39 -08:00
Jarred Sumner
9604733ee1 ✂️ 2024-12-16 13:51:45 -08:00
Sam
7633f3cc35 docs: dns.prefetch doesn't require port anymore (#15792) 2024-12-16 06:52:49 -08:00
Michael H
1fa0dee5e9 document npm:<package-name> in install docs (#15754) 2024-12-15 07:19:34 -08:00
Jarred Sumner
80b0b88315 Deflake doesnt_crash.test.ts 2024-12-15 06:54:34 -08:00
github-actions[bot]
6a24a06741 deps: update c-ares to v1.34.4 (#15773)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2024-12-15 04:38:39 -08:00
Jarred Sumner
8a64038fae Deflake require.cache test 2024-12-15 00:55:18 -08:00
Jarred Sumner
65f5156589 Deflake process test 2024-12-15 00:47:59 -08:00
Brian Donovan
00a8392656 docs(bun-native-plugin-rs): fix typos (#15764) 2024-12-14 23:50:03 -08:00
Jarred Sumner
c218bffd94 Add "bin" field to bun.lock (#15763)
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2024-12-14 22:52:17 -08:00
Jarred Sumner
3ce6ffa6be Make git dependencies faster + further optimize bun install (#15771) 2024-12-14 19:42:23 -08:00
Jarred Sumner
5326a998c7 Don't open node_modules 1,618 times (#15762) 2024-12-14 04:48:57 -08:00
Jarred Sumner
0d97c8157f Add debugger to entitlements plist 2024-12-14 01:57:08 -08:00
Jarred Sumner
ebc33327d3 Delete incorrect debug assertion 2024-12-14 01:56:55 -08:00
Dylan Conway
3df39f4bb7 bun.lock: fix --frozen-lockfile and resolving extra dependencies (#15748) 2024-12-13 22:40:12 -08:00
Jarred Sumner
c7020c2edc Make --expose gc work in nodetests 2024-12-13 22:30:26 -08:00
Meghan Denny
ac12438f69 node: fix test-zlib-from-gzip-with-trailing-garbage.js (#15757) 2024-12-13 21:51:02 -08:00
Jarred Sumner
1e19672841 fix clangd 2024-12-13 21:20:43 -08:00
Jarred Sumner
20f9cf0047 Fix flaky signal handlers on posix (#15751) 2024-12-13 20:13:56 -08:00
Don Isaac
bd1c5e9876 feat: add JSObject constructors (#15742)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-12 22:04:19 -08:00
Don Isaac
bbb56acdf7 test(ws): do not create temporary .sock files in root repo directory (#15670)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-12 21:39:30 -08:00
Jarred Sumner
f64ca29c0e Fix symbols test. Bump Webkit. (#15741) 2024-12-12 20:53:02 -08:00
Dylan Conway
8b3b1442fd bun.lock workspace sorting and comma bugfix (#15739) 2024-12-12 19:33:44 -08:00
Jarred Sumner
e72692801a [ci] Reduce number of environment variables we send (#15730) 2024-12-12 17:48:53 -08:00
Dylan Conway
e146734596 bun.lock fixes (#15724) 2024-12-12 16:45:26 -08:00
Jarred Sumner
7ded578547 [publish images] 2024-12-12 03:22:45 -08:00
Dylan Conway
71af1950fb bump webkit (#15328)
Co-authored-by: dylan-conway <dylan-conway@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Ben Grant <ben@bun.sh>
Co-authored-by: Meghan Denny <meghan@bun.sh>
Co-authored-by: Ashcon Partovi <ashcon@partovi.net>
2024-12-12 03:21:56 -08:00
Jarred Sumner
7991be86a3 Fix build 2024-12-12 02:18:25 -08:00
Jarred Sumner
6f50f51528 Deflake a test 2024-12-12 02:07:29 -08:00
Jarred Sumner
2bdf33cac8 Remove silly hack 2024-12-12 01:42:03 -08:00
Jarred Sumner
b3628a526d ✂️ 2024-12-12 01:39:34 -08:00
pfg
1b5cb891c8 More passing console tests (#15676)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-12 01:37:10 -08:00
Don Isaac
fe1e3be104 test(node): add parallel/test-path-resolve.js (#15707)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-12 01:36:36 -08:00
dave caruso
79dc13ca79 pass all string decoder tests (#15723)
Co-authored-by: paperdave <paperdave@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-12 01:35:08 -08:00
Jarred Sumner
2ccdf0122c Fix edgecase with socketpair() impacting shell and spawn (#15725) 2024-12-12 01:23:40 -08:00
Zack Radisic
fddc28d608 CSS moar fixes (#15719) 2024-12-11 21:45:41 -08:00
Meghan Denny
834b6436c6 fix canary 2024-12-11 20:06:42 -08:00
Zack Radisic
113b62be82 Native plugin follow up (#15632)
Co-authored-by: zackradisic <zackradisic@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-11 17:51:21 -08:00
pfg
2e0f229722 test(events): 66% -> 94% (#15716) 2024-12-11 17:43:19 -08:00
Don Isaac
08e2cf3761 test: mock 'node:test' module in node test harness (#15696)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-11 17:40:44 -08:00
pfg
0e8f075191 Pass node querystring tests (#15695) 2024-12-11 17:39:46 -08:00
Ashcon Partovi
667821c53a ci: Fix canary releases (#15713) 2024-12-11 09:47:17 -08:00
Dylan Conway
b55ca429c7 Implement text-based lockfile (#15705) 2024-12-11 05:05:49 -08:00
Don Isaac
78445c543e refactor: set default for name in ErrorCode.ts (#15699)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-11 01:07:57 -08:00
Don Isaac
24d73e948a test(node): add passing path parse format test (#15703)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-11 00:34:58 -08:00
Jarred Sumner
5cfa4cc0af ✂️ 2024-12-11 00:34:19 -08:00
Don Isaac
0bc57eebcb test(deno): use expect.toBeGreaterThanorEqual on failing deno perf … (#15700)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-10 22:05:46 -08:00
Meghan Denny
455de2a449 deps: update boringssl (#15677) 2024-12-10 20:33:36 -08:00
Meghan Denny
81bc01d477 fix test-event-emitter-method-names.js on windows (#15692) 2024-12-10 16:33:57 -08:00
snwy
d21444a681 test: 100% punycode (#15691) 2024-12-10 15:38:48 -08:00
Don Isaac
2d5ea4993f fix(codegen): better error messages for internals using module.exports (#15687) 2024-12-10 15:10:21 -08:00
dave caruso
b39632c921 feat: new binding generator (#15638) 2024-12-10 12:43:17 -08:00
Jarred Sumner
38325aa41c Introduce env option in Bun.build() and bun build to let you inject FOO_PUBLIC_*-style env vars (#15678) 2024-12-10 01:09:46 -08:00
Jarred Sumner
969bab3848 [build images] 2024-12-10 00:54:04 -08:00
Jarred Sumner
5bd4972d5b Add passing node tests (#15675) 2024-12-10 00:02:09 -08:00
Meghan Denny
68780faee2 fix windows build 2024-12-09 22:30:44 -08:00
Jarred Sumner
0bbc18fd19 Fix rare crash in bun install (#15651) 2024-12-09 20:59:29 -08:00
Meghan Denny
53318c8b13 ci: run re-enable node tests on all platforms (#15572) 2024-12-09 19:08:30 -08:00
Jarred Sumner
abe69901b2 make the helper quieter 2024-12-09 17:42:40 -08:00
Jarred Sumner
c0cf0414a0 Add helper for running node tests 2024-12-09 17:37:53 -08:00
Natt Nguyen
3dc3527171 fix: testing library docs (#15667) 2024-12-09 16:29:34 -08:00
Don Isaac
af4f1c7d39 test: fix case to allow bun-debug (#15660)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-08 23:48:43 -08:00
github-actions[bot]
2c1dea818c deps: update sqlite to 3.470.200 (#15652)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2024-12-08 02:16:41 -08:00
Kai Tamkun
cc125b475f Fix missing "readable" events (#15629) 2024-12-06 23:59:47 -08:00
Don Isaac
cbbf88f3a6 refactor: remove unused main_api.zig file (#15635)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-06 21:01:25 -08:00
Don Isaac
8064a55a48 test(bake): fix double free (#15634)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-06 20:06:26 -08:00
pfg
93fdbed85d add back whitespace checks but limited to 33 bytes 2024-12-06 20:03:20 -08:00
Jarred Sumner
0531d6756c Ci is doing too much 2024-12-06 19:28:28 -08:00
Ciro Spaciari
6135b3dec9 fix(CI) deflaky node-http.test.ts (#15625) 2024-12-06 19:16:59 -08:00
Don Isaac
b08dd8795e test(web): fix setTimeout refresh test (#15630)
Co-authored-by: Don Isaac <don@bun.sh>
2024-12-06 19:14:07 -08:00
Ciro Spaciari
c1eba5886f fix(net) signal should destroy the connection and propagate the error properly (#15624) 2024-12-06 16:10:33 -08:00
pfg
1982df220b remove empty file checks & remove isAllWhitespace 2024-12-06 15:37:50 -08:00
pfg
7cd823feeb Fix importing empty .txt file 2024-12-06 15:23:40 -08:00
Ciro Spaciari
fcca2cc398 fix(fetch) fix redirect + Connection: close (#15623) 2024-12-06 15:06:11 -08:00
Yuto Ogino
dd32e6b416 Fix zsh auto-completion for package.json scripts with name containing colons (#15619) 2024-12-06 10:53:43 -08:00
Jarred Sumner
b453360dff Fixes #15480 (#15611) 2024-12-05 21:15:21 -08:00
pfg
1476e4c958 implement toThrowErrorMatchingSnapshot, toThrowErrorMatchingInlineSnapshot (#15607) 2024-12-05 19:07:18 -08:00
Ashcon Partovi
eacf89e5bf ci: Fix CPU count on build runners 2024-12-05 14:20:05 -08:00
Ashcon Partovi
fa6ac405a4 ci: Add bootstrap.ps1 and automate Windows build images (#15606) 2024-12-05 15:16:37 -07:00
pfg
4c8cbecb08 Support flag parameter in readFileSync (#15595)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-05 13:41:44 -08:00
Michael H
00b7d6479b bun repl disable inspector/debugger (#15594) 2024-12-05 13:41:21 -08:00
pfg
bcf023c829 Implement expect().toMatchInlineSnapshot() (#15570) 2024-12-05 13:07:10 -08:00
Jarred Sumner
b7b1ca8ebe Fixes https://github.com/oven-sh/bun/issues/15307 2024-12-05 02:39:34 -08:00
Jarred Sumner
784bc4e012 Introduce high-performance native addon API in Bun.build, starting with build.onBeforeParse hook (#14971)
Co-authored-by: Zack Radisic <56137411+zackradisic@users.noreply.github.com>
Co-authored-by: zackradisic <zackradisic@users.noreply.github.com>
2024-12-04 22:35:43 -08:00
Ciro Spaciari
dd5c40dab7 fix(node:http) fix node:http chunked encoding on server and add chunked encoding support on the client (#15579)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-04 17:58:21 -08:00
190n
3a4a9ae4e9 Add v8::api_internal::FromJustIsNothing (#15583)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-04 17:57:40 -08:00
Jarred Sumner
9d1a35b658 Fixes https://github.com/oven-sh/bun/issues/15556 (#15582)
Co-authored-by: Andres Gutierrez <andresgutierrez535@gmail.com>
2024-12-04 17:57:05 -08:00
Meghan Denny
61cc9c3947 Revert "ci: Add bootstrap.ps1 and automate Windows build images" (#15591) 2024-12-04 17:07:35 -08:00
Ashcon Partovi
e904a181d8 ci: Add bootstrap.ps1 and automate Windows build images (#15466)
Co-authored-by: Electroid <Electroid@users.noreply.github.com>
2024-12-04 17:33:00 -07:00
Jarred Sumner
55a0bdc68d deflake process.test.js 2024-12-04 13:52:37 -08:00
Meghan Denny
55454f7910 [publish images] 2024-12-04 13:46:17 -08:00
Jarred Sumner
e4aeb761e4 Ensure we always drain the dependency list in runTasks() (#15511) 2024-12-04 12:40:11 -08:00
pfg
f9efe94b85 Fixes ^C on bun vite (#15545) 2024-12-04 12:39:55 -08:00
Robert Shuford
7eb8a3feae Fixes #14433 - global .npmrc not using auth (#15539) 2024-12-04 12:37:18 -08:00
Dylan Conway
d7ed9c673e add a --config test for bun install (#15546) 2024-12-04 12:36:10 -08:00
Ashcon Partovi
b4dce96c40 ci: Publish musl releases to npm 2024-12-04 10:19:15 -08:00
Meghan Denny
52ef8b1778 ci: make annotations always link to file content by commit (#15573) 2024-12-04 01:30:26 -08:00
dave caruso
baff3c900e bake: fix the big regressions (#15544)
Co-authored-by: paperdave <paperdave@users.noreply.github.com>
2024-12-03 22:15:59 -08:00
Meghan Denny
23299dadf6 ci: run node tests directly instead of translated files (#15565)
Co-authored-by: nektro <nektro@users.noreply.github.com>
2024-12-03 22:10:50 -08:00
Jarred Sumner
0d5e4e162b spawnSync shouldn't throw (#15561)
Co-authored-by: Meghan Denny <meghan@bun.sh>
2024-12-03 19:26:36 -08:00
Don Isaac
d27594ecf4 fix(deps/boringssl): re-enable BIO_new_mem_buf (#15559)
Co-authored-by: Don Isaac <don@bun.sh>
Co-authored-by: DonIsaac <DonIsaac@users.noreply.github.com>
2024-12-03 16:11:42 -08:00
Ciro Spaciari
a2e2d114e9 fix(net/tls) fix backpressure pause on socket (#15543) 2024-12-03 12:53:48 -08:00
Kai Tamkun
da3d64b1ef Remove a duplicate if statement (#15555) 2024-12-03 12:33:27 -08:00
Jarred Sumner
ce64e04b16 Reduce memory usage of WebSocket server (#15553) 2024-12-03 12:33:04 -08:00
Dylan Conway
55473cb64a fix(node:crypto): use options from createHash(alg, options) (#15547) 2024-12-03 12:32:41 -08:00
Meghan Denny
752441d911 package.json: put :local builds into their own folder (#15540) 2024-12-03 12:22:46 -08:00
Leah Lundqvist
da5d4d791c docs: add .env.test to guides/runtime/set-env for consistency with do… (#15542) 2024-12-02 15:01:08 -08:00
Dylan Conway
6d453be7d9 fix 14540 (#15498) 2024-12-02 14:57:49 -08:00
2873 changed files with 145945 additions and 88645 deletions

170
.buildkite/Dockerfile Normal file
View File

@@ -0,0 +1,170 @@
ARG LLVM_VERSION="18"
ARG REPORTED_LLVM_VERSION="18.1.8"
ARG OLD_BUN_VERSION="1.1.38"
ARG DEFAULT_CFLAGS="-mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -ffunction-sections -fdata-sections -faddrsig -fno-unwind-tables -fno-asynchronous-unwind-tables"
ARG DEFAULT_CXXFLAGS="-flto=full -fwhole-program-vtables -fforce-emit-vtables"
ARG BUILDKITE_AGENT_TAGS="queue=linux,os=linux,arch=${TARGETARCH}"
FROM --platform=$BUILDPLATFORM ubuntu:20.04 as base-arm64
FROM --platform=$BUILDPLATFORM ubuntu:18.04 as base-amd64
FROM base-$TARGETARCH as base
ARG LLVM_VERSION
ARG OLD_BUN_VERSION
ARG TARGETARCH
ARG DEFAULT_CXXFLAGS
ARG DEFAULT_CFLAGS
ARG REPORTED_LLVM_VERSION
ENV DEBIAN_FRONTEND=noninteractive \
CI=true \
DOCKER=true
RUN echo "Acquire::Queue-Mode \"host\";" > /etc/apt/apt.conf.d/99-apt-queue-mode.conf \
&& echo "Acquire::Timeout \"120\";" >> /etc/apt/apt.conf.d/99-apt-timeout.conf \
&& echo "Acquire::Retries \"3\";" >> /etc/apt/apt.conf.d/99-apt-retries.conf \
&& echo "APT::Install-Recommends \"false\";" >> /etc/apt/apt.conf.d/99-apt-install-recommends.conf \
&& echo "APT::Install-Suggests \"false\";" >> /etc/apt/apt.conf.d/99-apt-install-suggests.conf
RUN apt-get update && apt-get install -y --no-install-recommends \
wget curl git python3 python3-pip ninja-build \
software-properties-common apt-transport-https \
ca-certificates gnupg lsb-release unzip \
libxml2-dev ruby ruby-dev bison gawk perl make golang \
&& add-apt-repository ppa:ubuntu-toolchain-r/test \
&& apt-get update \
&& apt-get install -y gcc-13 g++-13 libgcc-13-dev libstdc++-13-dev \
libasan6 libubsan1 libatomic1 libtsan0 liblsan0 \
libgfortran5 libc6-dev \
&& wget https://apt.llvm.org/llvm.sh \
&& chmod +x llvm.sh \
&& ./llvm.sh ${LLVM_VERSION} all \
&& rm llvm.sh
RUN --mount=type=tmpfs,target=/tmp \
cmake_version="3.30.5" && \
if [ "$TARGETARCH" = "arm64" ]; then \
cmake_url="https://github.com/Kitware/CMake/releases/download/v${cmake_version}/cmake-${cmake_version}-linux-aarch64.sh"; \
else \
cmake_url="https://github.com/Kitware/CMake/releases/download/v${cmake_version}/cmake-${cmake_version}-linux-x86_64.sh"; \
fi && \
wget -O /tmp/cmake.sh "$cmake_url" && \
sh /tmp/cmake.sh --skip-license --prefix=/usr
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 130 \
--slave /usr/bin/g++ g++ /usr/bin/g++-13 \
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-13 \
--slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-13 \
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-13
RUN echo "ARCH_PATH=$([ "$TARGETARCH" = "arm64" ] && echo "aarch64-linux-gnu" || echo "x86_64-linux-gnu")" >> /etc/environment \
&& echo "BUN_ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "aarch64" || echo "x64")" >> /etc/environment
ENV LD_LIBRARY_PATH=/usr/lib/gcc/${ARCH_PATH}/13:/usr/lib/${ARCH_PATH} \
LIBRARY_PATH=/usr/lib/gcc/${ARCH_PATH}/13:/usr/lib/${ARCH_PATH} \
CPLUS_INCLUDE_PATH=/usr/include/c++/13:/usr/include/${ARCH_PATH}/c++/13 \
C_INCLUDE_PATH=/usr/lib/gcc/${ARCH_PATH}/13/include \
CFLAGS=${DEFAULT_CFLAGS} \
CXXFLAGS="${DEFAULT_CFLAGS} ${DEFAULT_CXXFLAGS}"
RUN if [ "$TARGETARCH" = "arm64" ]; then \
export ARCH_PATH="aarch64-linux-gnu"; \
else \
export ARCH_PATH="x86_64-linux-gnu"; \
fi \
&& mkdir -p /usr/lib/gcc/${ARCH_PATH}/13 \
&& ln -sf /usr/lib/${ARCH_PATH}/libstdc++.so.6 /usr/lib/gcc/${ARCH_PATH}/13/ \
&& echo "/usr/lib/gcc/${ARCH_PATH}/13" > /etc/ld.so.conf.d/gcc-13.conf \
&& echo "/usr/lib/${ARCH_PATH}" >> /etc/ld.so.conf.d/gcc-13.conf \
&& ldconfig
RUN for f in /usr/lib/llvm-${LLVM_VERSION}/bin/*; do ln -sf "$f" /usr/bin; done \
&& ln -sf /usr/bin/clang-${LLVM_VERSION} /usr/bin/clang \
&& ln -sf /usr/bin/clang++-${LLVM_VERSION} /usr/bin/clang++ \
&& ln -sf /usr/bin/lld-${LLVM_VERSION} /usr/bin/lld \
&& ln -sf /usr/bin/lldb-${LLVM_VERSION} /usr/bin/lldb \
&& ln -sf /usr/bin/clangd-${LLVM_VERSION} /usr/bin/clangd \
&& ln -sf /usr/bin/llvm-ar-${LLVM_VERSION} /usr/bin/llvm-ar \
&& ln -sf /usr/bin/ld.lld /usr/bin/ld \
&& ln -sf /usr/bin/clang /usr/bin/cc \
&& ln -sf /usr/bin/clang++ /usr/bin/c++
ENV CC="clang" \
CXX="clang++" \
AR="llvm-ar-${LLVM_VERSION}" \
RANLIB="llvm-ranlib-${LLVM_VERSION}" \
LD="lld-${LLVM_VERSION}"
RUN --mount=type=tmpfs,target=/tmp \
bash -c '\
set -euxo pipefail && \
source /etc/environment && \
echo "Downloading bun-v${OLD_BUN_VERSION}/bun-linux-$BUN_ARCH.zip from https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/bun-v${OLD_BUN_VERSION}/bun-linux-$BUN_ARCH.zip" && \
curl -fsSL https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/bun-v${OLD_BUN_VERSION}/bun-linux-$BUN_ARCH.zip -o /tmp/bun.zip && \
unzip /tmp/bun.zip -d /tmp/bun && \
mv /tmp/bun/*/bun /usr/bin/bun && \
chmod +x /usr/bin/bun'
ENV LLVM_VERSION=${REPORTED_LLVM_VERSION}
WORKDIR /workspace
FROM --platform=$BUILDPLATFORM base as buildkite
ARG BUILDKITE_AGENT_TAGS
# Install Rust nightly
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
&& export PATH=$HOME/.cargo/bin:$PATH \
&& rustup install nightly \
&& rustup default nightly
RUN ARCH=$(if [ "$TARGETARCH" = "arm64" ]; then echo "arm64"; else echo "amd64"; fi) && \
echo "Downloading buildkite" && \
curl -fsSL "https://github.com/buildkite/agent/releases/download/v3.87.0/buildkite-agent-linux-${ARCH}-3.87.0.tar.gz" -o /tmp/buildkite-agent.tar.gz && \
mkdir -p /tmp/buildkite-agent && \
tar -xzf /tmp/buildkite-agent.tar.gz -C /tmp/buildkite-agent && \
mv /tmp/buildkite-agent/buildkite-agent /usr/bin/buildkite-agent
RUN mkdir -p /var/cache/buildkite-agent /var/log/buildkite-agent /var/run/buildkite-agent /etc/buildkite-agent /var/lib/buildkite-agent/cache/bun
COPY ../*/agent.mjs /var/bun/scripts/
ENV BUN_INSTALL_CACHE=/var/lib/buildkite-agent/cache/bun
ENV BUILDKITE_AGENT_TAGS=${BUILDKITE_AGENT_TAGS}
WORKDIR /var/bun/scripts
ENV PATH=/root/.cargo/bin:$PATH
CMD ["bun", "/var/bun/scripts/agent.mjs", "start"]
FROM --platform=$BUILDPLATFORM base as bun-build-linux-local
ARG LLVM_VERSION
WORKDIR /workspace/bun
COPY . /workspace/bun
# Install Rust nightly
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
&& export PATH=$HOME/.cargo/bin:$PATH \
&& rustup install nightly \
&& rustup default nightly
ENV PATH=/root/.cargo/bin:$PATH
ENV LLVM_VERSION=${REPORTED_LLVM_VERSION}
RUN --mount=type=tmpfs,target=/workspace/bun/build \
ls -la \
&& bun run build:release \
&& mkdir -p /target \
&& cp -r /workspace/bun/build/release/bun /target/bun

View File

@@ -0,0 +1,122 @@
#!/usr/bin/env bash
set -euo pipefail
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "error: must run as root"
exit 1
fi
# Check OS compatibility
if ! command -v dnf &> /dev/null; then
echo "error: this script requires dnf (RHEL/Fedora/CentOS)"
exit 1
fi
# Ensure /tmp/agent.mjs, /tmp/Dockerfile are present
if [ ! -f /tmp/agent.mjs ] || [ ! -f /tmp/Dockerfile ]; then
# Print each missing file
if [ ! -f /tmp/agent.mjs ]; then
echo "error: /tmp/agent.mjs is missing"
fi
if [ ! -f /tmp/Dockerfile ]; then
echo "error: /tmp/Dockerfile is missing"
fi
exit 1
fi
# Install Docker
dnf update -y
dnf install -y docker
systemctl enable docker
systemctl start docker || {
echo "error: failed to start Docker"
exit 1
}
# Create builder
docker buildx create --name builder --driver docker-container --bootstrap --use || {
echo "error: failed to create Docker buildx builder"
exit 1
}
# Set up Docker to start on boot
cat << 'EOF' > /etc/systemd/system/buildkite-agent.service
[Unit]
Description=Buildkite Docker Container
After=docker.service network-online.target
Requires=docker.service network-online.target
[Service]
TimeoutStartSec=0
Restart=always
RestartSec=5
ExecStartPre=-/usr/bin/docker stop buildkite
ExecStartPre=-/usr/bin/docker rm buildkite
ExecStart=/usr/bin/docker run \
--name buildkite \
--restart=unless-stopped \
--network host \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp:/tmp \
buildkite:latest
[Install]
WantedBy=multi-user.target
EOF
echo "Building Buildkite image"
# Clean up any previous build artifacts
rm -rf /tmp/fakebun
mkdir -p /tmp/fakebun/scripts /tmp/fakebun/.buildkite
# Copy required files
cp /tmp/agent.mjs /tmp/fakebun/scripts/ || {
echo "error: failed to copy agent.mjs"
exit 1
}
cp /tmp/Dockerfile /tmp/fakebun/.buildkite/Dockerfile || {
echo "error: failed to copy Dockerfile"
exit 1
}
cd /tmp/fakebun || {
echo "error: failed to change directory"
exit 1
}
# Build the Buildkite image
docker buildx build \
--platform $(uname -m | sed 's/aarch64/linux\/arm64/;s/x86_64/linux\/amd64/') \
--tag buildkite:latest \
--target buildkite \
-f .buildkite/Dockerfile \
--load \
. || {
echo "error: Docker build failed"
exit 1
}
# Create container to ensure image is cached in AMI
docker container create \
--name buildkite \
--restart=unless-stopped \
buildkite:latest || {
echo "error: failed to create buildkite container"
exit 1
}
# Reload systemd to pick up new service
systemctl daemon-reload
# Enable the service, but don't start it yet
systemctl enable buildkite-agent || {
echo "error: failed to enable buildkite-agent service"
exit 1
}
echo "Bootstrap complete"
echo "To start the Buildkite agent, run: "
echo " systemctl start buildkite-agent"

View File

@@ -13,19 +13,4 @@ steps:
agents:
queue: "build-darwin"
command:
- ".buildkite/scripts/prepare-build.sh"
- if: "build.branch == 'main' && !build.pull_request.repository.fork"
label: ":github:"
agents:
queue: "test-darwin"
depends_on:
- "darwin-aarch64-build-bun"
- "darwin-x64-build-bun"
- "linux-aarch64-build-bun"
- "linux-x64-build-bun"
- "linux-x64-baseline-build-bun"
- "windows-x64-build-bun"
- "windows-x64-baseline-build-bun"
command:
- ".buildkite/scripts/upload-release.sh"
- "node .buildkite/ci.mjs"

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +0,0 @@
#!/bin/bash
set -eo pipefail
function run_command() {
set -x
"$@"
{ set +x; } 2>/dev/null
}
run_command node ".buildkite/ci.mjs"

View File

@@ -3,10 +3,6 @@
set -eo pipefail
function assert_main() {
if [ "$RELEASE" == "1" ]; then
echo "info: Skipping canary release because this is a release build"
exit 0
fi
if [ -z "$BUILDKITE_REPO" ]; then
echo "error: Cannot find repository for this build"
exit 1
@@ -194,8 +190,6 @@ function create_release() {
local artifacts=(
bun-darwin-aarch64.zip
bun-darwin-aarch64-profile.zip
bun-darwin-x64.zip
bun-darwin-x64-profile.zip
bun-linux-aarch64.zip
bun-linux-aarch64-profile.zip
bun-linux-x64.zip
@@ -237,8 +231,7 @@ function create_release() {
}
function assert_canary() {
local canary="$(buildkite-agent meta-data get canary 2>/dev/null)"
if [ -z "$canary" ] || [ "$canary" == "0" ]; then
if [ -z "$CANARY" ] || [ "$CANARY" == "0" ]; then
echo "warn: Skipping release because this is not a canary build"
exit 0
fi

View File

@@ -3,3 +3,6 @@ Index:
CompileFlags:
CompilationDatabase: build/debug
Diagnostics:
UnusedIncludes: None

7
.cursorignore Normal file
View File

@@ -0,0 +1,7 @@
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
bench
vendor
*-fixture.{js,ts}
zig-cache
packages/bun-uws/fuzzing
build

View File

@@ -16,3 +16,6 @@ zig-out
build
vendor
node_modules
*.trace
packages/bun-uws/fuzzing

2
.gitignore vendored
View File

@@ -116,8 +116,10 @@ scripts/env.local
sign.*.json
sign.json
src/bake/generated.ts
src/generated_enum_extractor.zig
src/bun.js/bindings-obj
src/bun.js/bindings/GeneratedJS2Native.zig
src/bun.js/bindings/GeneratedBindings.zig
src/bun.js/debug-bindings-obj
src/deps/zig-clap/.gitattributes
src/deps/zig-clap/.github

View File

@@ -1,4 +1,4 @@
command script import vendor/zig/tools/lldb_pretty_printers.py
# command script import vendor/zig/tools/lldb_pretty_printers.py
command script import vendor/WebKit/Tools/lldb/lldb_webkit.py
# type summary add --summary-string "${var} | inner=${var[0-30]}, source=${var[33-64]}, tag=${var[31-32]}" "unsigned long"

View File

@@ -5,6 +5,5 @@ test/js/deno
test/node.js
src/react-refresh.js
*.min.js
test/js/node/test/fixtures
test/js/node/test/common
test/snippets
test/js/node/test

203
.vscode/launch.json generated vendored
View File

@@ -15,15 +15,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "${file}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "1",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -32,15 +32,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "--only", "${file}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "1",
"BUN_DEBUG_jest": "1",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -55,15 +55,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "${file}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "0",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -72,15 +72,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "${file}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "0",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -89,15 +89,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "--watch", "${file}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -106,15 +106,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "--hot", "${file}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -123,8 +123,8 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "${file}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
@@ -132,7 +132,7 @@
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
"serverReadyAction": {
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
@@ -146,8 +146,8 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "${file}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
@@ -155,7 +155,7 @@
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
"serverReadyAction": {
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
@@ -170,6 +170,7 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["run", "${fileBasename}"],
"cwd": "${fileDirname}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "0",
"BUN_DEBUG_QUIET_LOGS": "1",
@@ -177,7 +178,7 @@
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -186,8 +187,8 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["run", "${fileBasename}"],
"cwd": "${fileDirname}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "0",
"BUN_DEBUG_IncrementalGraph": "1",
@@ -197,7 +198,7 @@
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -206,14 +207,14 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["run", "${fileBasename}"],
"cwd": "${fileDirname}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "0",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -222,8 +223,8 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["run", "--watch", "${fileBasename}"],
"cwd": "${fileDirname}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
// "BUN_DEBUG_DEBUGGER": "1",
// "BUN_DEBUG_INTERNAL_DEBUGGER": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
@@ -232,7 +233,7 @@
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -241,14 +242,14 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["run", "--hot", "${fileBasename}"],
"cwd": "${fileDirname}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -257,6 +258,7 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["run", "${fileBasename}"],
"cwd": "${fileDirname}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "0",
"BUN_DEBUG_QUIET_LOGS": "1",
@@ -265,7 +267,7 @@
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
"serverReadyAction": {
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
@@ -279,6 +281,7 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["run", "${fileBasename}"],
"cwd": "${fileDirname}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "0",
"BUN_DEBUG_QUIET_LOGS": "1",
@@ -287,7 +290,7 @@
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
"serverReadyAction": {
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
@@ -302,15 +305,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "${input:testName}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -319,15 +322,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "${input:testName}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "0",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -336,15 +339,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "${input:testName}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -353,15 +356,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "--watch", "${input:testName}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -370,15 +373,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "--hot", "${input:testName}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -387,8 +390,8 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "${input:testName}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
@@ -396,7 +399,7 @@
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
"serverReadyAction": {
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
@@ -410,8 +413,8 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test", "${input:testName}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_jest": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
@@ -419,7 +422,7 @@
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
"serverReadyAction": {
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
@@ -434,14 +437,14 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["exec", "${input:testName}"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
// bun test [*]
{
@@ -451,14 +454,14 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -467,14 +470,14 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "0",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -483,15 +486,15 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["test"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
"BUN_INSPECT": "ws://localhost:0/",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
"serverReadyAction": {
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
@@ -505,14 +508,14 @@
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["install"],
"cwd": "${fileDirname}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
{
"type": "lldb",
@@ -521,14 +524,14 @@
"program": "node",
"args": ["test/runner.node.mjs"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
// Windows: bun test [file]
{
@@ -542,10 +545,6 @@
"args": ["test", "${file}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -571,10 +570,6 @@
"args": ["test", "--only", "${file}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -600,10 +595,6 @@
"args": ["test", "${file}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -629,10 +620,6 @@
"args": ["test", "${file}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "0",
@@ -658,10 +645,6 @@
"args": ["test", "${file}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -696,10 +679,6 @@
"args": ["test", "${file}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -735,10 +714,6 @@
"args": ["run", "${fileBasename}"],
"cwd": "${fileDirname}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -764,10 +739,6 @@
"args": ["install"],
"cwd": "${fileDirname}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -789,10 +760,6 @@
"args": ["run", "${fileBasename}"],
"cwd": "${fileDirname}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -814,10 +781,6 @@
"args": ["run", "${fileBasename}"],
"cwd": "${fileDirname}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -848,10 +811,6 @@
"args": ["run", "${fileBasename}"],
"cwd": "${fileDirname}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -883,10 +842,6 @@
"args": ["test", "${input:testName}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -912,10 +867,6 @@
"args": ["test", "${input:testName}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -941,10 +892,6 @@
"args": ["test", "${input:testName}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "0",
@@ -970,10 +917,6 @@
"args": ["test", "--watch", "${input:testName}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -999,10 +942,6 @@
"args": ["test", "--hot", "${input:testName}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -1028,10 +967,6 @@
"args": ["test", "${input:testName}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -1066,10 +1001,6 @@
"args": ["test", "${input:testName}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -1105,10 +1036,6 @@
"args": ["exec", "${input:testName}"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -1131,10 +1058,6 @@
"args": ["test"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -1156,10 +1079,6 @@
"args": ["test"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -1185,10 +1104,6 @@
"args": ["test"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -1223,10 +1138,6 @@
"args": ["test/runner.node.mjs"],
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1",
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1",
@@ -1242,7 +1153,7 @@
],
"console": "internalConsole",
// Don't pause when the GC runs while the debugger is open.
"postRunCommands": ["process handle -p true -s false -n false SIGUSR1"],
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
},
],
"inputs": [

View File

@@ -63,7 +63,6 @@
"editor.tabSize": 4,
"editor.defaultFormatter": "xaver.clang-format",
},
"clangd.arguments": ["-header-insertion=never"],
// JavaScript
"prettier.enable": true,

View File

@@ -1,11 +1,6 @@
Configuring a development environment for Bun can take 10-30 minutes depending on your internet connection and computer speed. You will need ~10GB of free disk space for the repository and build artifacts.
If you are using Windows, please refer to [this guide](/docs/project/building-windows.md)
{% details summary="For Ubuntu users" %}
TL;DR: Ubuntu 22.04 is suggested.
Bun currently requires `glibc >=2.32` in development which means if you're on Ubuntu 20.04 (glibc == 2.31), you may likely meet `error: undefined symbol: __libc_single_threaded `. You need to take extra configurations. Also, according to this [issue](https://github.com/llvm/llvm-project/issues/97314), LLVM 16 is no longer maintained on Ubuntu 24.04 (noble). And instead, you might want `brew` to install LLVM 16 for your Ubuntu 24.04.
{% /details %}
If you are using Windows, please refer to [this guide](https://bun.sh/docs/project/building-windows)
## Install Dependencies
@@ -58,7 +53,7 @@ $ brew install bun
## Install LLVM
Bun requires LLVM 16 (`clang` is part of LLVM). This version requirement is to match WebKit (precompiled), as mismatching versions will cause memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
Bun requires LLVM 18 (`clang` is part of LLVM). This version requirement is to match WebKit (precompiled), as mismatching versions will cause memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
{% codetabs group="os" %}
@@ -89,7 +84,7 @@ $ sudo zypper install clang16 lld16 llvm16
If none of the above solutions apply, you will have to install it [manually](https://github.com/llvm/llvm-project/releases/tag/llvmorg-16.0.6).
Make sure Clang/LLVM 16 is in your path:
Make sure Clang/LLVM 18 is in your path:
```bash
$ which clang-16

2
LATEST
View File

@@ -1 +1 @@
1.1.38
1.1.42

Binary file not shown.

View File

@@ -13,7 +13,7 @@
"execa": "^8.0.1",
"fast-glob": "3.3.1",
"fdir": "^6.1.0",
"mitata": "^1.0.10",
"mitata": "^1.0.25",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"string-width": "7.1.0",

View File

@@ -0,0 +1,27 @@
import { Buffer } from "node:buffer";
import { bench, run } from "../runner.mjs";
const variations = [
["latin1", "hello world"],
["utf16", "hello emoji 🤔"],
];
for (const [label, string] of variations) {
const big = Buffer.alloc(1000000, string).toString();
const small = Buffer.from(string).toString();
const substring = big.slice(0, big.length - 2);
bench(`${substring.length}`, () => {
return Buffer.byteLength(substring, "utf8");
});
bench(`${small.length}`, () => {
return Buffer.byteLength(small);
});
bench(`${big.length}`, () => {
return Buffer.byteLength(big);
});
}
await run();

View File

@@ -1,20 +1,14 @@
import { noOpForTesting as noop } from "bun:internal-for-testing";
import { bench, run } from "../runner.mjs";
// These are no-op C++ functions that are exported to JS.
const lazy = globalThis[Symbol.for("Bun.lazy")];
const noop = lazy("noop");
const fn = noop.function;
const regular = noop.functionRegular;
const callback = noop.callback;
bench("C++ callback into JS", () => {
callback(() => {});
});
bench("C++ fn regular", () => {
regular();
});
bench("C++ fn", () => {
fn();
});

View File

@@ -0,0 +1,37 @@
import { bench, run } from "../runner.mjs";
import { brotliCompress, brotliDecompress, createBrotliCompress, createBrotliDecompress } from "node:zlib";
import { promisify } from "node:util";
import { pipeline } from "node:stream/promises";
import { Readable } from "node:stream";
import { readFileSync } from "node:fs";
const brotliCompressAsync = promisify(brotliCompress);
const brotliDecompressAsync = promisify(brotliDecompress);
const testData =
process.argv.length > 2
? readFileSync(process.argv[2])
: Buffer.alloc(1024 * 1024 * 16, "abcdefghijklmnopqrstuvwxyz");
let compressed;
bench("brotli compress", async () => {
compressed = await brotliCompressAsync(testData);
});
bench("brotli decompress", async () => {
await brotliDecompressAsync(compressed);
});
bench("brotli compress stream", async () => {
const source = Readable.from([testData]);
const compress = createBrotliCompress();
await pipeline(source, compress);
});
bench("brotli decompress stream", async () => {
const source = Readable.from([compressed]);
const decompress = createBrotliDecompress();
await pipeline(source, decompress);
});
await run();

62
bench/snippets/zlib.mjs Normal file
View File

@@ -0,0 +1,62 @@
import { bench, run } from "../runner.mjs";
import zlib from "node:zlib";
import { promisify } from "node:util";
const deflate = promisify(zlib.deflate);
const inflate = promisify(zlib.inflate);
const short = "Hello World!";
const long = "Hello World!".repeat(1024);
const veryLong = "Hello World!".repeat(10240);
// Pre-compress some data for decompression tests
const shortBuf = Buffer.from(short);
const longBuf = Buffer.from(long);
const veryLongBuf = Buffer.from(veryLong);
let [shortCompressed, longCompressed, veryLongCompressed] = await Promise.all([
deflate(shortBuf, { level: 6 }),
deflate(longBuf, { level: 6 }),
deflate(veryLongBuf, { level: 6 }),
]);
const format = new Intl.NumberFormat("en-US", { notation: "compact", unit: "byte" });
// Compression tests at different levels
bench(`deflate ${format.format(short.length)}B (level 1)`, async () => {
await deflate(shortBuf, { level: 1 });
});
bench(`deflate ${format.format(short.length)} (level 6)`, async () => {
await deflate(shortBuf, { level: 6 });
});
bench(`deflate ${format.format(long.length)} (level 1)`, async () => {
await deflate(longBuf, { level: 1 });
});
bench(`deflate ${format.format(long.length)} (level 6)`, async () => {
await deflate(longBuf, { level: 6 });
});
bench(`deflate ${format.format(veryLong.length)} (level 1)`, async () => {
await deflate(veryLongBuf, { level: 1 });
});
bench(`deflate ${format.format(veryLong.length)} (level 6)`, async () => {
await deflate(veryLongBuf, { level: 6 });
});
// Decompression tests
bench(`inflate ${format.format(short.length)}`, async () => {
await inflate(shortCompressed);
});
bench(`inflate ${format.format(long.length)}`, async () => {
await inflate(longCompressed);
});
bench(`inflate ${format.format(veryLong.length)}`, async () => {
await inflate(veryLongCompressed);
});
await run();

View File

@@ -327,6 +327,25 @@ pub fn build(b: *Build) !void {
.{ .os = .windows, .arch = .x86_64 },
});
}
// zig build translate-c-headers
{
const step = b.step("translate-c", "Copy generated translated-c-headers.zig to zig-out");
step.dependOn(&b.addInstallFile(getTranslateC(b, b.host, .Debug).getOutput(), "translated-c-headers.zig").step);
}
// zig build enum-extractor
{
// const step = b.step("enum-extractor", "Extract enum definitions (invoked by a code generator)");
// const exe = b.addExecutable(.{
// .name = "enum_extractor",
// .root_source_file = b.path("./src/generated_enum_extractor.zig"),
// .target = b.graph.host,
// .optimize = .Debug,
// });
// const run = b.addRunArtifact(exe);
// step.dependOn(&run.step);
}
}
pub fn addMultiCheck(
@@ -367,6 +386,25 @@ pub fn addMultiCheck(
}
}
fn getTranslateC(b: *Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *Step.TranslateC {
const translate_c = b.addTranslateC(.{
.root_source_file = b.path("src/c-headers-for-zig.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
inline for ([_](struct { []const u8, bool }){
.{ "WINDOWS", translate_c.target.result.os.tag == .windows },
.{ "POSIX", translate_c.target.result.os.tag != .windows },
.{ "LINUX", translate_c.target.result.os.tag == .linux },
.{ "DARWIN", translate_c.target.result.os.tag.isDarwin() },
}) |entry| {
const str, const value = entry;
translate_c.defineCMacroRaw(b.fmt("{s}={d}", .{ str, @intFromBool(value) }));
}
return translate_c;
}
pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile {
const obj = b.addObject(.{
.name = if (opts.optimize == .Debug) "bun-debug" else "bun",
@@ -414,6 +452,10 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile {
}
addInternalPackages(b, obj, opts);
obj.root_module.addImport("build_options", opts.buildOptionsModule(b));
const translate_c = getTranslateC(b, opts.target, opts.optimize);
obj.root_module.addImport("translated-c-headers", translate_c.createModule());
return obj;
}

BIN
bun.lockb

Binary file not shown.

View File

@@ -176,6 +176,10 @@ if(LINUX)
DESCRIPTION "Disable relocation read-only (RELRO)"
-Wl,-z,norelro
)
register_compiler_flags(
DESCRIPTION "Disable semantic interposition"
-fno-semantic-interposition
)
endif()
# --- Assertions ---

View File

@@ -291,7 +291,7 @@ function(find_command)
set_property(GLOBAL PROPERTY ${FIND_NAME} "${exe}: ${reason}" APPEND)
if(version)
satisfies_range(${version} ${${FIND_VERSION_VARIABLE}} ${variable})
satisfies_range(${version} ${FIND_VERSION} ${variable})
set(${variable} ${${variable}} PARENT_SCOPE)
endif()
endfunction()

View File

@@ -20,7 +20,7 @@ else()
setx(RELEASE OFF)
endif()
if(CMAKE_BUILD_TYPE MATCHES "Debug|RelWithDebInfo")
if(CMAKE_BUILD_TYPE MATCHES "Debug")
setx(DEBUG ON)
else()
setx(DEBUG OFF)
@@ -67,13 +67,7 @@ optionx(ENABLE_ASSERTIONS BOOL "If debug assertions should be enabled" DEFAULT $
optionx(ENABLE_CANARY BOOL "If canary features should be enabled" DEFAULT ON)
if(ENABLE_CANARY AND BUILDKITE)
execute_process(
COMMAND buildkite-agent meta-data get "canary"
OUTPUT_VARIABLE DEFAULT_CANARY_REVISION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
elseif(ENABLE_CANARY)
if(ENABLE_CANARY)
set(DEFAULT_CANARY_REVISION "1")
else()
set(DEFAULT_CANARY_REVISION "0")

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
oven-sh/boringssl
COMMIT
29a2cd359458c9384694b75456026e4b57e3e567
914b005ef3ece44159dca0ffad74eb42a9f6679f
)
register_cmake_command(

View File

@@ -318,13 +318,13 @@ register_command(
TARGET
bun-bake-codegen
COMMENT
"Bundling Kit Runtime"
"Bundling Bake Runtime"
COMMAND
${BUN_EXECUTABLE}
run
${BUN_BAKE_RUNTIME_CODEGEN_SCRIPT}
--debug=${DEBUG}
--codegen_root=${CODEGEN_PATH}
--codegen-root=${CODEGEN_PATH}
SOURCES
${BUN_BAKE_RUNTIME_SOURCES}
${BUN_BAKE_RUNTIME_CODEGEN_SOURCES}
@@ -334,6 +334,39 @@ register_command(
${BUN_BAKE_RUNTIME_OUTPUTS}
)
set(BUN_BINDGEN_SCRIPT ${CWD}/src/codegen/bindgen.ts)
file(GLOB_RECURSE BUN_BINDGEN_SOURCES ${CONFIGURE_DEPENDS}
${CWD}/src/**/*.bind.ts
)
set(BUN_BINDGEN_CPP_OUTPUTS
${CODEGEN_PATH}/GeneratedBindings.cpp
)
set(BUN_BINDGEN_ZIG_OUTPUTS
${CWD}/src/bun.js/bindings/GeneratedBindings.zig
)
register_command(
TARGET
bun-binding-generator
COMMENT
"Processing \".bind.ts\" files"
COMMAND
${BUN_EXECUTABLE}
run
${BUN_BINDGEN_SCRIPT}
--debug=${DEBUG}
--codegen-root=${CODEGEN_PATH}
SOURCES
${BUN_BINDGEN_SOURCES}
${BUN_BINDGEN_SCRIPT}
OUTPUTS
${BUN_BINDGEN_CPP_OUTPUTS}
${BUN_BINDGEN_ZIG_OUTPUTS}
)
set(BUN_JS_SINK_SCRIPT ${CWD}/src/codegen/generate-jssink.ts)
set(BUN_JS_SINK_SOURCES
@@ -385,7 +418,6 @@ set(BUN_OBJECT_LUT_OUTPUTS
${CODEGEN_PATH}/NodeModuleModule.lut.h
)
macro(WEBKIT_ADD_SOURCE_DEPENDENCIES _source _deps)
set(_tmp)
get_source_file_property(_tmp ${_source} OBJECT_DEPENDS)
@@ -461,6 +493,7 @@ list(APPEND BUN_ZIG_SOURCES
${CWD}/build.zig
${CWD}/root.zig
${CWD}/root_wasm.zig
${BUN_BINDGEN_ZIG_OUTPUTS}
)
set(BUN_ZIG_GENERATED_SOURCES
@@ -482,7 +515,6 @@ endif()
set(BUN_ZIG_OUTPUT ${BUILD_PATH}/bun-zig.o)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64")
if(APPLE)
set(ZIG_CPU "apple_m1")
@@ -544,6 +576,7 @@ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "build.zig")
set(BUN_USOCKETS_SOURCE ${CWD}/packages/bun-usockets)
# hand written cpp source files. Full list of "source" code (including codegen) is in BUN_CPP_SOURCES
file(GLOB BUN_CXX_SOURCES ${CONFIGURE_DEPENDS}
${CWD}/src/io/*.cpp
${CWD}/src/bun.js/modules/*.cpp
@@ -567,7 +600,8 @@ file(GLOB BUN_C_SOURCES ${CONFIGURE_DEPENDS}
)
if(WIN32)
list(APPEND BUN_C_SOURCES ${CWD}/src/bun.js/bindings/windows/musl-memmem.c)
list(APPEND BUN_CXX_SOURCES ${CWD}/src/bun.js/bindings/windows/rescle.cpp)
list(APPEND BUN_CXX_SOURCES ${CWD}/src/bun.js/bindings/windows/rescle-binding.cpp)
endif()
register_repository(
@@ -600,12 +634,14 @@ register_command(
list(APPEND BUN_CPP_SOURCES
${BUN_C_SOURCES}
${BUN_CXX_SOURCES}
${BUN_ERROR_CODE_OUTPUTS}
${VENDOR_PATH}/picohttpparser/picohttpparser.c
${NODEJS_HEADERS_PATH}/include/node/node_version.h
${BUN_ZIG_GENERATED_CLASSES_OUTPUTS}
${BUN_JS_SINK_OUTPUTS}
${BUN_JAVASCRIPT_OUTPUTS}
${BUN_OBJECT_LUT_OUTPUTS}
${BUN_BINDGEN_CPP_OUTPUTS}
)
if(WIN32)
@@ -615,11 +651,19 @@ if(WIN32)
set(Bun_VERSION_WITH_TAG ${VERSION})
endif()
set(BUN_ICO_PATH ${CWD}/src/bun.ico)
configure_file(${CWD}/src/bun.ico ${CODEGEN_PATH}/bun.ico COPYONLY)
configure_file(
${CWD}/src/windows-app-info.rc
${CODEGEN_PATH}/windows-app-info.rc
@ONLY
)
list(APPEND BUN_CPP_SOURCES ${CODEGEN_PATH}/windows-app-info.rc)
add_custom_command(
OUTPUT ${CODEGEN_PATH}/windows-app-info.res
COMMAND rc.exe /fo ${CODEGEN_PATH}/windows-app-info.res ${CODEGEN_PATH}/windows-app-info.rc
DEPENDS ${CODEGEN_PATH}/windows-app-info.rc ${CODEGEN_PATH}/bun.ico
COMMENT "Adding Windows resource file ${CODEGEN_PATH}/windows-app-info.res with ico in ${CODEGEN_PATH}/bun.ico"
)
set(WINDOWS_RESOURCES ${CODEGEN_PATH}/windows-app-info.res)
endif()
# --- Executable ---
@@ -627,7 +671,7 @@ endif()
set(BUN_CPP_OUTPUT ${BUILD_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}${bun}${CMAKE_STATIC_LIBRARY_SUFFIX})
if(BUN_LINK_ONLY)
add_executable(${bun} ${BUN_CPP_OUTPUT} ${BUN_ZIG_OUTPUT})
add_executable(${bun} ${BUN_CPP_OUTPUT} ${BUN_ZIG_OUTPUT} ${WINDOWS_RESOURCES})
set_target_properties(${bun} PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(${bun} PRIVATE ${BUN_CPP_OUTPUT})
elseif(BUN_CPP_ONLY)
@@ -645,7 +689,7 @@ elseif(BUN_CPP_ONLY)
${BUN_CPP_OUTPUT}
)
else()
add_executable(${bun} ${BUN_CPP_SOURCES})
add_executable(${bun} ${BUN_CPP_SOURCES} ${WINDOWS_RESOURCES})
target_link_libraries(${bun} PRIVATE ${BUN_ZIG_OUTPUT})
endif()
@@ -815,7 +859,7 @@ endif()
if(WIN32)
target_link_options(${bun} PUBLIC
/STACK:0x1200000,0x100000
/STACK:0x1200000,0x200000
/errorlimit:0
)
if(RELEASE)
@@ -851,48 +895,28 @@ endif()
if(LINUX)
if(NOT ABI STREQUAL "musl")
if(ARCH STREQUAL "aarch64")
target_link_options(${bun} PUBLIC
-Wl,--wrap=fcntl64
-Wl,--wrap=statx
)
endif()
if(ARCH STREQUAL "x64")
target_link_options(${bun} PUBLIC
-Wl,--wrap=fcntl
-Wl,--wrap=fcntl64
-Wl,--wrap=fstat
-Wl,--wrap=fstat64
-Wl,--wrap=fstatat
-Wl,--wrap=fstatat64
-Wl,--wrap=lstat
-Wl,--wrap=lstat64
-Wl,--wrap=mknod
-Wl,--wrap=mknodat
-Wl,--wrap=stat
-Wl,--wrap=stat64
-Wl,--wrap=statx
)
endif()
# on arm64
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64")
target_link_options(${bun} PUBLIC
-Wl,--wrap=cosf
-Wl,--wrap=exp
-Wl,--wrap=expf
-Wl,--wrap=fmod
-Wl,--wrap=fmodf
-Wl,--wrap=fcntl64
-Wl,--wrap=log
-Wl,--wrap=log10f
-Wl,--wrap=log2
-Wl,--wrap=log2f
-Wl,--wrap=logf
-Wl,--wrap=pow
-Wl,--wrap=powf
-Wl,--wrap=sincosf
-Wl,--wrap=sinf
-Wl,--wrap=tanf
)
else()
target_link_options(${bun} PUBLIC
-Wl,--wrap=exp
-Wl,--wrap=expf
-Wl,--wrap=log2f
-Wl,--wrap=logf
-Wl,--wrap=powf
)
endif()
endif()
if(NOT ABI STREQUAL "musl")
@@ -921,7 +945,7 @@ if(LINUX)
-Wl,-z,combreloc
-Wl,--no-eh-frame-hdr
-Wl,--sort-section=name
-Wl,--hash-style=gnu
-Wl,--hash-style=both
-Wl,--build-id=sha1 # Better for debugging than default
-Wl,-Map=${bun}.linker-map
)
@@ -933,6 +957,7 @@ if(WIN32)
set(BUN_SYMBOLS_PATH ${CWD}/src/symbols.def)
target_link_options(${bun} PUBLIC /DEF:${BUN_SYMBOLS_PATH})
elseif(APPLE)
set(BUN_SYMBOLS_PATH ${CWD}/src/symbols.txt)
target_link_options(${bun} PUBLIC -exported_symbols_list ${BUN_SYMBOLS_PATH})
else()

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
c-ares/c-ares
COMMIT
41ee334af3e3d0027dca5e477855d0244936bd49
4f4912bce7374f787b10576851b687935f018e17
)
register_cmake_command(

View File

@@ -18,7 +18,7 @@ register_cmake_command(
-DENABLE_INSTALL=OFF
-DENABLE_TEST=OFF
-DENABLE_WERROR=OFF
-DENABLE_BZIP2=OFF
-DENABLE_BZip2=OFF
-DENABLE_CAT=OFF
-DENABLE_EXPAT=OFF
-DENABLE_ICONV=OFF

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
ebiggers/libdeflate
COMMIT
9d624d1d8ba82c690d6d6be1d0a961fc5a983ea4
733848901289eca058804ca0737f8796875204c8
)
register_cmake_command(

View File

@@ -49,6 +49,8 @@ register_command(
CARGO_TERM_VERBOSE=true
CARGO_TERM_DIAGNOSTIC=true
CARGO_ENCODED_RUSTFLAGS=${RUSTFLAGS}
CARGO_HOME=${CARGO_HOME}
RUSTUP_HOME=${RUSTUP_HOME}
)
target_link_libraries(${bun} PRIVATE ${LOLHTML_LIBRARY})

View File

@@ -5,6 +5,11 @@ if(NOT ENABLE_CCACHE OR CACHE_STRATEGY STREQUAL "none")
return()
endif()
if (CI AND NOT APPLE)
setenv(CCACHE_DISABLE 1)
return()
endif()
find_command(
VARIABLE
CCACHE_PROGRAM
@@ -38,7 +43,8 @@ setenv(CCACHE_FILECLONE 1)
setenv(CCACHE_STATSLOG ${BUILD_PATH}/ccache.log)
if(CI)
setenv(CCACHE_SLOPPINESS "pch_defines,time_macros,locale,clang_index_store,gcno_cwd,include_file_ctime,include_file_mtime")
# FIXME: Does not work on Ubuntu 18.04
# setenv(CCACHE_SLOPPINESS "pch_defines,time_macros,locale,clang_index_store,gcno_cwd,include_file_ctime,include_file_mtime")
else()
setenv(CCACHE_SLOPPINESS "pch_defines,time_macros,locale,random_seed,clang_index_store,gcno_cwd")
endif()

View File

@@ -1,14 +1,18 @@
optionx(ENABLE_LLVM BOOL "If LLVM should be used for compilation" DEFAULT ON)
set(DEFAULT_ENABLE_LLVM ON)
# if target is bun-zig, set ENABLE_LLVM to OFF
if(TARGET bun-zig)
set(DEFAULT_ENABLE_LLVM OFF)
endif()
optionx(ENABLE_LLVM BOOL "If LLVM should be used for compilation" DEFAULT ${DEFAULT_ENABLE_LLVM})
if(NOT ENABLE_LLVM)
return()
endif()
if(CMAKE_HOST_WIN32 OR CMAKE_HOST_APPLE OR EXISTS "/etc/alpine-release")
set(DEFAULT_LLVM_VERSION "18.1.8")
else()
set(DEFAULT_LLVM_VERSION "16.0.6")
endif()
set(DEFAULT_LLVM_VERSION "18.1.8")
optionx(LLVM_VERSION STRING "The version of LLVM to use" DEFAULT ${DEFAULT_LLVM_VERSION})
@@ -73,7 +77,7 @@ macro(find_llvm_command variable command)
VERSION_VARIABLE LLVM_VERSION
COMMAND ${commands}
PATHS ${LLVM_PATHS}
VERSION ${LLVM_VERSION}
VERSION >=${LLVM_VERSION_MAJOR}.1.0
)
list(APPEND CMAKE_ARGS -D${variable}=${${variable}})
endmacro()

View File

@@ -1,15 +1,42 @@
if(DEFINED ENV{CARGO_HOME})
set(CARGO_HOME $ENV{CARGO_HOME})
elseif(CMAKE_HOST_WIN32)
set(CARGO_HOME $ENV{USERPROFILE}/.cargo)
if(NOT EXISTS ${CARGO_HOME})
set(CARGO_HOME $ENV{PROGRAMFILES}/Rust/cargo)
endif()
else()
set(CARGO_HOME $ENV{HOME}/.cargo)
endif()
if(DEFINED ENV{RUSTUP_HOME})
set(RUSTUP_HOME $ENV{RUSTUP_HOME})
elseif(CMAKE_HOST_WIN32)
set(RUSTUP_HOME $ENV{USERPROFILE}/.rustup)
if(NOT EXISTS ${RUSTUP_HOME})
set(RUSTUP_HOME $ENV{PROGRAMFILES}/Rust/rustup)
endif()
else()
set(RUSTUP_HOME $ENV{HOME}/.rustup)
endif()
find_command(
VARIABLE
CARGO_EXECUTABLE
COMMAND
cargo
PATHS
$ENV{HOME}/.cargo/bin
${CARGO_HOME}/bin
REQUIRED
OFF
)
if(EXISTS ${CARGO_EXECUTABLE})
if(CARGO_EXECUTABLE MATCHES "^${CARGO_HOME}")
setx(CARGO_HOME ${CARGO_HOME})
setx(RUSTUP_HOME ${RUSTUP_HOME})
endif()
return()
endif()

View File

@@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use")
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
if(NOT WEBKIT_VERSION)
set(WEBKIT_VERSION 8f9ae4f01a047c666ef548864294e01df731d4ea)
set(WEBKIT_VERSION e1a802a2287edfe7f4046a9dd8307c8b59f5d816)
endif()
if(WEBKIT_LOCAL)

View File

@@ -671,7 +671,7 @@ _bun() {
cmd)
local -a scripts_list
IFS=$'\n' scripts_list=($(SHELL=zsh bun getcompletes i))
scripts="scripts:scripts:(($scripts_list))"
scripts="scripts:scripts:((${scripts_list//:/\\\\:}))"
IFS=$'\n' files_list=($(SHELL=zsh bun getcompletes j))
main_commands=(
@@ -871,8 +871,8 @@ _bun_run_param_script_completion() {
IFS=$'\n' scripts_list=($(SHELL=zsh bun getcompletes s))
IFS=$'\n' bins=($(SHELL=zsh bun getcompletes b))
_alternative "scripts:scripts:(($scripts_list))"
_alternative "bin:bin:(($bins))"
_alternative "scripts:scripts:((${scripts_list//:/\\\\:}))"
_alternative "bin:bin:((${bins//:/\\\\:}))"
_alternative "files:file:_files -g '*.(js|ts|jsx|tsx|wasm)'"
}

View File

@@ -179,16 +179,16 @@ type Flags = string | string[];
These are flags like `-I` for include directories and `-D` for preprocessor definitions.
#### `defines: Record<string, string>`
#### `define: Record<string, string>`
The `defines` is an optional object that should be passed to the TinyCC compiler.
The `define` is an optional object that should be passed to the TinyCC compiler.
```ts
type Defines = Record<string, string>;
cc({
source: "hello.c",
defines: {
define: {
"NDEBUG": "1",
},
});

View File

@@ -234,7 +234,7 @@ To prefetch a DNS entry, you can use the `dns.prefetch` API. This API is useful
```ts
import { dns } from "bun";
dns.prefetch("bun.sh", 443);
dns.prefetch("bun.sh");
```
#### DNS caching

549
docs/api/s3.md Normal file
View File

@@ -0,0 +1,549 @@
Production servers often read, upload, and write files to S3-compatible object storage services instead of the local filesystem. Historically, that means local filesystem APIs you use in development can't be used in production. When you use Bun, things are different.
Bun provides fast, native bindings for interacting with S3-compatible object storage services. Bun's S3 API is designed to be simple and feel similar to fetch's `Response` and `Blob` APIs (like Bun's local filesystem APIs).
```ts
import { s3, write, S3 } from "bun";
const metadata = await s3("123.json", {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
});
// Download from S3 as JSON
const data = await metadata.json();
// Upload to S3
await write(metadata, JSON.stringify({ name: "John", age: 30 }));
// Presign a URL (synchronous - no network request needed)
const url = metadata.presign({
acl: "public-read",
expiresIn: 60 * 60 * 24, // 1 day
});
```
S3 is the [de facto standard](https://en.wikipedia.org/wiki/De_facto_standard) internet filesystem. You can use Bun's S3 API with S3-compatible storage services like:
- AWS S3
- Cloudflare R2
- DigitalOcean Spaces
- MinIO
- Backblaze B2
- ...and any other S3-compatible storage service
## Basic Usage
There are several ways to interact with Bun's S3 API.
### Using `Bun.s3()`
The `s3()` helper function is used to create one-off `S3File` instances for a single file.
```ts
import { s3 } from "bun";
// Using the s3() helper
const s3file = s3("my-file.txt", {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com", // optional
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
// endpoint: "https://<region>.digitaloceanspaces.com", // DigitalOcean Spaces
// endpoint: "http://localhost:9000", // MinIO
});
```
### Reading Files
You can read files from S3 using similar methods to Bun's file system APIs:
```ts
// Read an S3File as text
const text = await s3file.text();
// Read an S3File as JSON
const json = await s3file.json();
// Read an S3File as an ArrayBuffer
const buffer = await s3file.arrayBuffer();
// Get only the first 1024 bytes
const partial = await s3file.slice(0, 1024).text();
// Stream the file
const stream = s3file.stream();
for await (const chunk of stream) {
console.log(chunk);
}
```
## Writing Files
Writing to S3 is just as simple:
```ts
// Write a string (replacing the file)
await s3file.write("Hello World!");
// Write with content type
await s3file.write(JSON.stringify({ name: "John", age: 30 }), {
type: "application/json",
});
// Write using a writer (streaming)
const writer = s3file.writer({ type: "application/json" });
writer.write("Hello");
writer.write(" World!");
await writer.end();
// Write using Bun.write
await Bun.write(s3file, "Hello World!");
```
### Working with large files (streams)
Bun automatically handles multipart uploads for large files and provides streaming capabilities. The same API that works for local files also works for S3 files.
```ts
// Write a large file
const bigFile = Buffer.alloc(10 * 1024 * 1024); // 10MB
const writer = s3file.writer({
// Automatically retry on network errors up to 3 times
retry: 3,
// Queue up to 10 requests at a time
queueSize: 10,
// Upload in 5 MB chunks
partSize: 5 * 1024 * 1024,
});
for (let i = 0; i < 10; i++) {
await writer.write(bigFile);
}
await writer.end();
```
## Presigning URLs
When your production service needs to let users upload files to your server, it's often more reliable for the user to upload directly to S3 instead of your server acting as an intermediary.
To facilitate this, you can presign URLs for S3 files. This generates a URL with a signature that allows a user to securely upload that specific file to S3, without exposing your credentials or granting them unnecessary access to your bucket.
```ts
// Generate a presigned URL that expires in 24 hours (default)
const url = s3file.presign();
// Custom expiration time (in seconds)
const url2 = s3file.presign({ expiresIn: 3600 }); // 1 hour
// Using static method
const url3 = Bun.S3.presign("my-file.txt", {
bucket: "my-bucket",
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
// endpoint: "https://s3.us-east-1.amazonaws.com",
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
expiresIn: 3600,
});
```
### Setting ACLs
To set an ACL (access control list) on a presigned URL, pass the `acl` option:
```ts
const url = s3file.presign({
acl: "public-read",
expiresIn: 3600,
});
```
You can pass any of the following ACLs:
| ACL | Explanation |
| ----------------------------- | ------------------------------------------------------------------- |
| `"public-read"` | The object is readable by the public. |
| `"private"` | The object is readable only by the bucket owner. |
| `"public-read-write"` | The object is readable and writable by the public. |
| `"authenticated-read"` | The object is readable by the bucket owner and authenticated users. |
| `"aws-exec-read"` | The object is readable by the AWS account that made the request. |
| `"bucket-owner-read"` | The object is readable by the bucket owner. |
| `"bucket-owner-full-control"` | The object is readable and writable by the bucket owner. |
| `"log-delivery-write"` | The object is writable by AWS services used for log delivery. |
### Expiring URLs
To set an expiration time for a presigned URL, pass the `expiresIn` option.
```ts
const url = s3file.presign({
// Seconds
expiresIn: 3600, // 1 hour
});
```
### `method`
To set the HTTP method for a presigned URL, pass the `method` option.
```ts
const url = s3file.presign({
method: "PUT",
// method: "DELETE",
// method: "GET",
// method: "HEAD",
// method: "POST",
// method: "PUT",
});
```
### `new Response(S3File)`
To quickly redirect users to a presigned URL for an S3 file, you can pass an `S3File` instance to a `Response` object as the body.
```ts
const response = new Response(s3file);
console.log(response);
```
This will automatically redirect the user to the presigned URL for the S3 file, saving you the memory, time, and bandwidth cost of downloading the file to your server and sending it back to the user.
```ts
Response (0 KB) {
ok: false,
url: "",
status: 302,
statusText: "",
headers: Headers {
"location": "https://<account-id>.r2.cloudflarestorage.com/...",
},
redirected: true,
bodyUsed: false
}
```
## Support for S3-Compatible Services
Bun's S3 implementation works with any S3-compatible storage service. Just specify the appropriate endpoint:
```ts
import { s3 } from "bun";
// CloudFlare R2
const r2file = s3("my-file.txt", {
accessKeyId: "access-key",
secretAccessKey: "secret-key",
bucket: "my-bucket",
endpoint: "https://<account-id>.r2.cloudflarestorage.com",
});
// DigitalOcean Spaces
const spacesFile = s3("my-file.txt", {
accessKeyId: "access-key",
secretAccessKey: "secret-key",
bucket: "my-bucket",
endpoint: "https://<region>.digitaloceanspaces.com",
});
// MinIO
const minioFile = s3("my-file.txt", {
accessKeyId: "access-key",
secretAccessKey: "secret-key",
bucket: "my-bucket",
endpoint: "http://localhost:9000",
});
```
## Credentials
Credentials are one of the hardest parts of using S3, and we've tried to make it as easy as possible. By default, Bun reads the following environment variables for credentials.
| Option name | Environment variable |
| ----------------- | ---------------------- |
| `accessKeyId` | `S3_ACCESS_KEY_ID` |
| `secretAccessKey` | `S3_SECRET_ACCESS_KEY` |
| `region` | `S3_REGION` |
| `endpoint` | `S3_ENDPOINT` |
| `bucket` | `S3_BUCKET` |
| `sessionToken` | `S3_SESSION_TOKEN` |
If the `S3_*` environment variable is not set, Bun will also check for the `AWS_*` environment variable, for each of the above options.
| Option name | Fallback environment variable |
| ----------------- | ----------------------------- |
| `accessKeyId` | `AWS_ACCESS_KEY_ID` |
| `secretAccessKey` | `AWS_SECRET_ACCESS_KEY` |
| `region` | `AWS_REGION` |
| `endpoint` | `AWS_ENDPOINT` |
| `bucket` | `AWS_BUCKET` |
| `sessionToken` | `AWS_SESSION_TOKEN` |
These environment variables are read from [`.env` files](/docs/runtime/env) or from the process environment at initialization time (`process.env` is not used for this).
These defaults are overriden by the options you pass to `s3(credentials)`, `new Bun.S3(credentials)`, or any of the methods that accept credentials. So if, for example, you use the same credentials for different buckets, you can set the credentials once in your `.env` file and then pass `bucket: "my-bucket"` to the `s3()` helper function without having to specify all the credentials again.
### `S3` Buckets
Passing around all of these credentials can be cumbersome. To make it easier, you can create a `S3` bucket instance.
```ts
import { S3 } from "bun";
const bucket = new S3({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// sessionToken: "..."
endpoint: "https://s3.us-east-1.amazonaws.com",
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
// endpoint: "http://localhost:9000", // MinIO
});
// bucket is a function that creates `S3File` instances (lazy)
const file = bucket("my-file.txt");
// Write to S3
await file.write("Hello World!");
// Read from S3
const text = await file.text();
// Write using a Response
await file.write(new Response("Hello World!"));
// Presign a URL
const url = file.presign({
expiresIn: 60 * 60 * 24, // 1 day
acl: "public-read",
});
// Delete the file
await file.unlink();
```
#### Read a file from an `S3` bucket
The `S3` bucket instance is itself a function that creates `S3File` instances. It provides a more convenient API for interacting with S3.
```ts
const s3file = bucket("my-file.txt");
const text = await s3file.text();
const json = await s3file.json();
const bytes = await s3file.bytes();
const arrayBuffer = await s3file.arrayBuffer();
```
#### Write a file to S3
To write a file to the bucket, you can use the `write` method.
```ts
const bucket = new Bun.S3({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
endpoint: "https://s3.us-east-1.amazonaws.com",
bucket: "my-bucket",
});
await bucket.write("my-file.txt", "Hello World!");
await bucket.write("my-file.txt", new Response("Hello World!"));
```
You can also call `.write` on the `S3File` instance created by the `S3` bucket instance.
```ts
const s3file = bucket("my-file.txt");
await s3file.write("Hello World!", {
type: "text/plain",
});
await s3file.write(new Response("Hello World!"));
```
#### Delete a file from S3
To delete a file from the bucket, you can use the `delete` method.
```ts
const bucket = new Bun.S3({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
});
await bucket.delete("my-file.txt");
```
You can also use the `unlink` method, which is an alias for `delete`.
```ts
// "delete" and "unlink" are aliases of each other.
await bucket.unlink("my-file.txt");
```
## `S3File`
`S3File` instances are created by calling the `S3` instance method or the `s3()` helper function. Like `Bun.file()`, `S3File` instances are lazy. They don't refer to something that necessarily exists at the time of creation. That's why all the methods that don't involve network requests are fully synchronous.
```ts
interface S3File extends Blob {
slice(start: number, end?: number): S3File;
exists(): Promise<boolean>;
unlink(): Promise<void>;
presign(options: S3Options): string;
text(): Promise<string>;
json(): Promise<any>;
bytes(): Promise<Uint8Array>;
arrayBuffer(): Promise<ArrayBuffer>;
stream(options: S3Options): ReadableStream;
write(
data:
| string
| Uint8Array
| ArrayBuffer
| Blob
| ReadableStream
| Response
| Request,
options?: BlobPropertyBag,
): Promise<void>;
readonly size: Promise<number>;
// ... more omitted for brevity
}
```
Like `Bun.file()`, `S3File` extends [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob), so all the methods that are available on `Blob` are also available on `S3File`. The same API for reading data from a local file is also available for reading data from S3.
| Method | Output |
| ---------------------------- | ---------------- |
| `await s3File.text()` | `string` |
| `await s3File.bytes()` | `Uint8Array` |
| `await s3File.json()` | `JSON` |
| `await s3File.stream()` | `ReadableStream` |
| `await s3File.arrayBuffer()` | `ArrayBuffer` |
That means using `S3File` instances with `fetch()`, `Response`, and other web APIs that accept `Blob` instances just works.
### Partial reads
To read a partial range of a file, you can use the `slice` method.
```ts
const partial = s3file.slice(0, 1024);
// Read the partial range as a Uint8Array
const bytes = await partial.bytes();
// Read the partial range as a string
const text = await partial.text();
```
Internally, this works by using the HTTP `Range` header to request only the bytes you want. This `slice` method is the same as [`Blob.prototype.slice`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice).
## Error codes
When Bun's S3 API throws an error, it will have a `code` property that matches one of the following values:
- `ERR_S3_MISSING_CREDENTIALS`
- `ERR_S3_INVALID_METHOD`
- `ERR_S3_INVALID_PATH`
- `ERR_S3_INVALID_ENDPOINT`
- `ERR_S3_INVALID_SIGNATURE`
- `ERR_S3_INVALID_SESSION_TOKEN`
When the S3 Object Storage service returns an error (that is, not Bun), it will be an `S3Error` instance (an `Error` instance with the name `"S3Error"`).
## `S3` static methods
The `S3` class provides several static methods for interacting with S3.
### `S3.presign`
To generate a presigned URL for an S3 file, you can use the `S3.presign` method.
```ts
import { S3 } from "bun";
const url = S3.presign("my-file.txt", {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
expiresIn: 3600,
// endpoint: "https://s3.us-east-1.amazonaws.com",
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
});
```
This is the same as `S3File.prototype.presign` and `new S3(credentials).presign`, as a static method on the `S3` class.
### `S3.exists`
To check if an S3 file exists, you can use the `S3.exists` method.
```ts
import { S3 } from "bun";
const exists = await S3.exists("my-file.txt", {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
});
```
The same method also works on `S3File` instances.
```ts
const s3file = Bun.s3("my-file.txt", {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
});
const exists = await s3file.exists();
```
### `S3.size`
To get the size of an S3 file, you can use the `S3.size` method.
```ts
import { S3 } from "bun";
const size = await S3.size("my-file.txt", {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
});
```
### `S3.unlink`
To delete an S3 file, you can use the `S3.unlink` method.
```ts
import { S3 } from "bun";
await S3.unlink("my-file.txt", {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
});
```
## s3:// protocol
To make it easier to use the same code for local files and S3 files, the `s3://` protocol is supported in `fetch` and `Bun.file()`.
```ts
const response = await fetch("s3://my-bucket/my-file.txt");
const file = Bun.file("s3://my-bucket/my-file.txt");
```
This is the equivalent of calling `Bun.s3("my-file.txt", { bucket: "my-bucket" })`.
This `s3://` protocol exists to make it easier to use the same code for local files and S3 files.

View File

@@ -771,3 +771,28 @@ console.log(obj); // => { foo: "bar" }
```
Internally, [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) and [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) serialize and deserialize the same way. This exposes the underlying [HTML Structured Clone Algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) to JavaScript as an ArrayBuffer.
## `estimateShallowMemoryUsageOf` in `bun:jsc`
The `estimateShallowMemoryUsageOf` function returns a best-effort estimate of the memory usage of an object in bytes, excluding the memory usage of properties or other objects it references. For accurate per-object memory usage, use `Bun.generateHeapSnapshot`.
```js
import { estimateShallowMemoryUsageOf } from "bun:jsc";
const obj = { foo: "bar" };
const usage = estimateShallowMemoryUsageOf(obj);
console.log(usage); // => 16
const buffer = Buffer.alloc(1024 * 1024);
estimateShallowMemoryUsageOf(buffer);
// => 1048624
const req = new Request("https://bun.sh");
estimateShallowMemoryUsageOf(req);
// => 167
const array = Array(1024).fill({ a: 1 });
// Arrays are usually not stored contiguously in memory, so this will not return a useful value (which isn't a bug).
estimateShallowMemoryUsageOf(array);
// => 16
```

View File

@@ -279,6 +279,19 @@ $ bun build --compile --asset-naming="[name].[ext]" ./index.ts
To trim down the size of the executable a little, pass `--minify` to `bun build --compile`. This uses Bun's minifier to reduce the code size. Overall though, Bun's binary is still way too big and we need to make it smaller.
## Windows-specific flags
When compiling a standalone executable on Windows, there are two platform-specific options that can be used to customize metadata on the generated `.exe` file:
- `--windows-icon=path/to/icon.ico` to customize the executable file icon.
- `--windows-hide-console` to disable the background terminal, which can be used for applications that do not need a TTY.
{% callout %}
These flags currently cannot be used when cross-compiling because they depend on Windows APIs.
{% /callout %}
## Unsupported CLI arguments
Currently, the `--compile` flag can only accept a single entrypoint at a time and does not support the following flags:

110
docs/bundler/html.md Normal file
View File

@@ -0,0 +1,110 @@
As of Bun v1.1.43, Bun's bundler now has first-class support for HTML. Build static sites, landing pages, and web applications with zero configuration. Just point Bun at your HTML file and it handles everything else.
```html#index.html
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<script src="./app.ts" type="module"></script>
</head>
<body>
<img src="./logo.png" />
</body>
</html>
```
One command is all you need (won't be experimental after Bun v1.2):
{% codetabs %}
```bash#CLI
$ bun build --experimental-html --experimental-css ./index.html --outdir=dist
```
```ts#API
Bun.build({
entrypoints: ["./index.html"],
outdir: "./dist",
// On by default in Bun v1.2+
html: true,
experimentalCss: true,
});
```
{% /codetabs %}
Bun automatically:
- Bundles, tree-shakes, and optimizes your JavaScript, JSX and TypeScript
- Bundles and optimizes your CSS
- Copies & hashes images and other assets
- Updates all references to local files or packages in your HTML
## Zero Config, Maximum Performance
The HTML bundler is enabled by default after Bun v1.2+. Drop in your existing HTML files and Bun will handle:
- **TypeScript & JSX** - Write modern JavaScript for browsers without the setup
- **CSS** - Bundle CSS stylesheets directly from `<link rel="stylesheet">` or `@import`
- **Images & Assets** - Automatic copying & hashing & rewriting of assets in JavaScript, CSS, and HTML
## Watch mode
You can run `bun build --watch` to watch for changes and rebuild automatically.
You've never seen a watch mode this fast.
## Plugin API
Need more control? Configure the bundler through the JavaScript API and use Bun's builtin `HTMLRewriter` to preprocess HTML.
```ts
await Bun.build({
entrypoints: ["./index.html"],
outdir: "./dist",
html: true,
experimentalCss: true,
minify: true,
plugins: [
{
// A plugin that makes every HTML tag lowercase
name: "lowercase-html-plugin",
setup({ onLoad }) {
const rewriter = new HTMLRewriter().on("*", {
element(element) {
element.tagName = element.tagName.toLowerCase();
},
text(element) {
element.replace(element.text.toLowerCase());
},
});
onLoad({ filter: /\.html$/ }, async args => {
const html = await Bun.file(args.path).text();
return {
// Bun's bundler will scan the HTML for <script> tags, <link rel="stylesheet"> tags, and other assets
// and bundle them automatically
contents: rewriter.transform(html),
loader: "html",
};
});
},
},
],
});
```
## What Gets Processed?
Bun automatically handles all common web assets:
- Scripts (`<script src>`) are run through Bun's JavaScript/TypeScript/JSX bundler
- Stylesheets (`<link rel="stylesheet">`) are run through Bun's CSS parser & bundler
- Images (`<img>`, `<picture>`) are copied and hashed
- Media (`<video>`, `<audio>`, `<source>`) are copied and hashed
- Any `<link>` tag with an `href` attribute pointing to a local file is rewritten to the new path, and hashed
All paths are resolved relative to your HTML file, making it easy to organize your project however you want.

View File

@@ -546,6 +546,113 @@ export type ImportKind =
By design, the manifest is a simple JSON object that can easily be serialized or written to disk. It is also compatible with esbuild's [`metafile`](https://esbuild.github.io/api/#metafile) format. -->
### `env`
Controls how environment variables are handled during bundling. Internally, this uses `define` to inject environment variables into the bundle, but makes it easier to specify the environment variables to inject.
#### `env: "inline"`
Injects environment variables into the bundled output by converting `process.env.FOO` references to string literals containing the actual environment variable values.
{% codetabs group="a" %}
```ts#JavaScript
await Bun.build({
entrypoints: ['./index.tsx'],
outdir: './out',
env: "inline",
})
```
```bash#CLI
$ FOO=bar BAZ=123 bun build ./index.tsx --outdir ./out --env inline
```
{% /codetabs %}
For the input below:
```js#input.js
console.log(process.env.FOO);
console.log(process.env.BAZ);
```
The generated bundle will contain the following code:
```js#output.js
console.log("bar");
console.log("123");
```
#### `env: "PUBLIC_*"` (prefix)
Inlines environment variables matching the given prefix (the part before the `*` character), replacing `process.env.FOO` with the actual environment variable value. This is useful for selectively inlining environment variables for things like public-facing URLs or client-side tokens, without worrying about injecting private credentials into output bundles.
{% codetabs group="a" %}
```ts#JavaScript
await Bun.build({
entrypoints: ['./index.tsx'],
outdir: './out',
// Inline all env vars that start with "ACME_PUBLIC_"
env: "ACME_PUBLIC_*",
})
```
```bash#CLI
$ FOO=bar BAZ=123 ACME_PUBLIC_URL=https://acme.com bun build ./index.tsx --outdir ./out --env 'ACME_PUBLIC_*'
```
{% /codetabs %}
For example, given the following environment variables:
```bash
$ FOO=bar BAZ=123 ACME_PUBLIC_URL=https://acme.com
```
And source code:
```ts#index.tsx
console.log(process.env.FOO);
console.log(process.env.ACME_PUBLIC_URL);
console.log(process.env.BAZ);
```
The generated bundle will contain the following code:
```js
console.log(process.env.FOO);
console.log("https://acme.com");
console.log(process.env.BAZ);
```
#### `env: "disable"`
Disables environment variable injection entirely.
For example, given the following environment variables:
```bash
$ FOO=bar BAZ=123 ACME_PUBLIC_URL=https://acme.com
```
And source code:
```ts#index.tsx
console.log(process.env.FOO);
console.log(process.env.ACME_PUBLIC_URL);
console.log(process.env.BAZ);
```
The generated bundle will contain the following code:
```js
console.log(process.env.FOO);
console.log(process.env.BAZ);
```
### `sourcemap`
Specifies the type of sourcemap to generate.
@@ -1152,7 +1259,7 @@ $ bun build ./index.tsx --outdir ./out --drop=console --drop=debugger --drop=any
### `experimentalCss`
Whether to enable _experimental_ support for bundling CSS files. Defaults to `false`.
Whether to enable _experimental_ support for bundling CSS files. Defaults to `false`. In 1.2, this property will be deleted, and CSS bundling will always be enabled.
This supports bundling CSS files imported from JS, as well as CSS entrypoints.
@@ -1168,6 +1275,12 @@ const result = await Bun.build({
{% /codetabs %}
### `throw`
If set to `true`, `Bun.build` will throw on build failure. See the section ["Logs and Errors"](#logs-and-errors) for more details on the error message structure.
In 1.2, this will default to `true`, with the previous behavior as `throw: false`
## Outputs
The `Bun.build` function returns a `Promise<BuildOutput>`, defined as:
@@ -1307,7 +1420,70 @@ Refer to [Bundler > Executables](https://bun.sh/docs/bundler/executables) for co
## Logs and errors
`Bun.build` only throws if invalid options are provided. Read the `success` property to determine if the build was successful; the `logs` property will contain additional details.
<!-- 1.2 documentation -->
<!-- On failure, `Bun.build` returns a rejected promise with an `AggregateError`. This can be logged to the console for pretty printing of the error list, or programmatically read with a `try`/`catch` block.
```ts
try {
const result = await Bun.build({
entrypoints: ["./index.tsx"],
outdir: "./out",
});
} catch (e) {
// TypeScript does not allow annotations on the catch clause
const error = e as AggregateError;
console.error("Build Failed");
// Example: Using the built-in formatter
console.error(error);
// Example: Serializing the failure as a JSON string.
console.error(JSON.stringify(error, null, 2));
}
```
{% callout %}
Most of the time, an explicit `try`/`catch` is not needed, as Bun will neatly print uncaught exceptions. It is enough to just use a top-level `await` on the `Bun.build` call.
{% /callout %}
Each item in `error.errors` is an instance of `BuildMessage` or `ResolveMessage` (subclasses of Error), containing detailed information for each error.
```ts
class BuildMessage {
name: string;
position?: Position;
message: string;
level: "error" | "warning" | "info" | "debug" | "verbose";
}
class ResolveMessage extends BuildMessage {
code: string;
referrer: string;
specifier: string;
importKind: ImportKind;
}
```
On build success, the returned object contains a `logs` property, which contains bundler warnings and info messages.
```ts
const result = await Bun.build({
entrypoints: ["./index.tsx"],
outdir: "./out",
});
if (result.logs.length > 0) {
console.warn("Build succeeded with warnings:");
for (const message of result.logs) {
// Bun will pretty print the message object
console.warn(message);
}
}
``` -->
By default, `Bun.build` only throws if invalid options are provided. Read the `success` property to determine if the build was successful; the `logs` property will contain additional details.
```ts
const result = await Bun.build({
@@ -1350,6 +1526,27 @@ if (!result.success) {
}
```
In Bun 1.2, throwing an aggregate error like this will become the default beahavior. You can opt-into it early using the `throw: true` option.
```ts
try {
const result = await Bun.build({
entrypoints: ["./index.tsx"],
outdir: "./out",
});
} catch (e) {
// TypeScript does not allow annotations on the catch clause
const error = e as AggregateError;
console.error("Build Failed");
// Example: Using the built-in formatter
console.error(error);
// Example: Serializing the failure as a JSON string.
console.error(JSON.stringify(error, null, 2));
}
```
## Reference
```ts
@@ -1371,39 +1568,23 @@ interface BuildConfig {
*
* @default "esm"
*/
format?: /**
* ECMAScript Module format
*/
| "esm"
/**
* CommonJS format
* **Experimental**
*/
| "cjs"
/**
* IIFE format
* **Experimental**
*/
| "iife";
format?: "esm" | "cjs" | "iife";
naming?:
| string
| {
chunk?: string;
entry?: string;
asset?: string;
}; // | string;
};
root?: string; // project root
splitting?: boolean; // default true, enable code splitting
plugins?: BunPlugin[];
// manifest?: boolean; // whether to return manifest
external?: string[];
packages?: "bundle" | "external";
publicPath?: string;
define?: Record<string, string>;
// origin?: string; // e.g. http://mydomain.com
loader?: { [k in string]: Loader };
sourcemap?: "none" | "linked" | "inline" | "external" | "linked"; // default: "none", true -> "inline"
sourcemap?: "none" | "linked" | "inline" | "external" | "linked" | boolean; // default: "none", true -> "inline"
/**
* package.json `exports` conditions used when resolving imports
*
@@ -1412,6 +1593,18 @@ interface BuildConfig {
* https://nodejs.org/api/packages.html#exports
*/
conditions?: Array<string> | string;
/**
* Controls how environment variables are handled during bundling.
*
* Can be one of:
* - `"inline"`: Injects environment variables into the bundled output by converting `process.env.FOO`
* references to string literals containing the actual environment variable values
* - `"disable"`: Disables environment variable injection entirely
* - A string ending in `*`: Inlines environment variables that match the given prefix.
* For example, `"MY_PUBLIC_*"` will only include env vars starting with "MY_PUBLIC_"
*/
env?: "inline" | "disable" | `${string}*`;
minify?:
| boolean
| {
@@ -1429,20 +1622,6 @@ interface BuildConfig {
* Force emitting @__PURE__ annotations even if minify.whitespace is true.
*/
emitDCEAnnotations?: boolean;
// treeshaking?: boolean;
// jsx?:
// | "automatic"
// | "classic"
// | /* later: "preserve" */ {
// runtime?: "automatic" | "classic"; // later: "preserve"
// /** Only works when runtime=classic */
// factory?: string; // default: "React.createElement"
// /** Only works when runtime=classic */
// fragment?: string; // default: "React.Fragment"
// /** Only works when runtime=automatic */
// importSource?: string; // default: "react"
// };
/**
* Generate bytecode for the output. This can dramatically improve cold
@@ -1455,6 +1634,37 @@ interface BuildConfig {
* @default false
*/
bytecode?: boolean;
/**
* Add a banner to the bundled code such as "use client";
*/
banner?: string;
/**
* Add a footer to the bundled code such as a comment block like
*
* `// made with bun!`
*/
footer?: string;
/**
* **Experimental**
*
* Enable CSS support.
*/
experimentalCss?: boolean;
/**
* Drop function calls to matching property accesses.
*/
drop?: string[];
/**
* When set to `true`, the returned promise rejects with an AggregateError when a build failure happens.
* When set to `false`, the `success` property of the returned object will be `false` when a build failure happens.
*
* This defaults to `false` in Bun 1.1 and will change to `true` in Bun 1.2
* as most usage of `Bun.build` forgets to check for errors.
*/
throw?: boolean;
}
interface BuildOutput {
@@ -1512,32 +1722,3 @@ declare class ResolveMessage {
toString(): string;
}
```
<!--
interface BuildManifest {
inputs: {
[path: string]: {
output: {
path: string;
};
imports: {
path: string;
kind: ImportKind;
external?: boolean;
asset?: boolean; // whether the import defaulted to "file" loader
}[];
};
};
outputs: {
[path: string]: {
type: "chunk" | "entrypoint" | "asset";
inputs: { path: string }[];
imports: {
path: string;
kind: ImportKind;
external?: boolean;
}[];
exports: string[];
};
};
} -->

View File

@@ -1,6 +1,6 @@
The Bun bundler implements a set of default loaders out of the box. As a rule of thumb, the bundler and the runtime both support the same set of file types out of the box.
`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.toml` `.json` `.txt` `.wasm` `.node`
`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.toml` `.json` `.txt` `.wasm` `.node` `.html`
Bun uses the file extension to determine which built-in _loader_ should be used to parse the file. Every loader has a name, such as `js`, `tsx`, or `json`. These names are used when building [plugins](https://bun.sh/docs/bundler/plugins) that extend Bun with custom loaders.
@@ -203,6 +203,81 @@ When using a [standalone executable](https://bun.sh/docs/bundler/executables), t
Otherwise, the database to embed is copied into the `outdir` with a hashed filename.
### `html`
**HTML loader**. Default for `.html` after Bun v1.2.0.
To enable the html loader:
- For `Bun.build`: set `html: true`
- For `bun build`: `--experimental-html` CLI flag
You most likely want to use the `html` loader in conjunction with `experimentalCss: true` or `--experimental-css`.
The html loader processes HTML files and bundles any referenced assets. It will:
- Bundle and hash referenced JavaScript files (`<script src="...">`)
- Bundle and hash referenced CSS files (`<link rel="stylesheet" href="...">`)
- Hash referenced images (`<img src="...">`)
- Preserve external URLs (by default, anything starting with `http://` or `https://`)
For example, given this HTML file:
{% codetabs %}
```html#src/index.html
<!DOCTYPE html>
<html>
<body>
<img src="./image.jpg" alt="Local image">
<img src="https://example.com/image.jpg" alt="External image">
<script type="module" src="./script.js"></script>
</body>
</html>
```
{% /codetabs %}
It will output a new HTML file with the bundled assets:
{% codetabs %}
```html#dist/output.html
<!DOCTYPE html>
<html>
<body>
<img src="./image-HASHED.jpg" alt="Local image">
<img src="https://example.com/image.jpg" alt="External image">
<script type="module" src="./output-ALSO-HASHED.js"></script>
</body>
</html>
```
{% /codetabs %}
Under the hood, it uses [`lol-html`](https://github.com/cloudflare/lol-html) to extract script and link tags as entrypoints, and other assets as external.
Currently, the list of selectors is:
- `audio[src]`
- `iframe[src]`
- `img[src]`
- `img[srcset]`
- `link:not([rel~='stylesheet']):not([rel~='modulepreload']):not([rel~='manifest']):not([rel~='icon']):not([rel~='apple-touch-icon'])[href]`
- `link[as='font'][href], link[type^='font/'][href]`
- `link[as='image'][href]`
- `link[as='style'][href]`
- `link[as='video'][href], link[as='audio'][href]`
- `link[as='worker'][href]`
- `link[rel='icon'][href], link[rel='apple-touch-icon'][href]`
- `link[rel='manifest'][href]`
- `link[rel='stylesheet'][href]`
- `script[src]`
- `source[src]`
- `source[srcset]`
- `video[poster]`
- `video[src]`
### `sh` loader
**Bun Shell loader**. Default for `.sh` files

View File

@@ -2,11 +2,47 @@ Bun provides a universal plugin API that can be used to extend both the _runtime
Plugins intercept imports and perform custom loading logic: reading files, transpiling code, etc. They can be used to add support for additional file types, like `.scss` or `.yaml`. In the context of Bun's bundler, plugins can be used to implement framework-level features like CSS extraction, macros, and client-server code co-location.
For more complete documentation of the Plugin API, see [Runtime > Plugins](https://bun.sh/docs/runtime/plugins).
## Lifecycle hooks
Plugins can register callbacks to be run at various points in the lifecycle of a bundle:
- [`onStart()`](#onstart): Run once the bundler has started a bundle
- [`onResolve()`](#onresolve): Run before a module is resolved
- [`onLoad()`](#onload): Run before a module is loaded.
- [`onBeforeParse()`](#onbeforeparse): Run zero-copy native addons in the parser thread before a file is parsed.
### Reference
A rough overview of the types (please refer to Bun's `bun.d.ts` for the full type definitions):
```ts
type PluginBuilder = {
onStart(callback: () => void): void;
onResolve: (
args: { filter: RegExp; namespace?: string },
callback: (args: { path: string; importer: string }) => {
path: string;
namespace?: string;
} | void,
) => void;
onLoad: (
args: { filter: RegExp; namespace?: string },
defer: () => Promise<void>,
callback: (args: { path: string }) => {
loader?: Loader;
contents?: string;
exports?: Record<string, any>;
},
) => void;
config: BuildConfig;
};
type Loader = "js" | "jsx" | "ts" | "tsx" | "css" | "json" | "toml";
```
## Usage
A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function. Register a plugin with Bun using the `plugin` function.
A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function.
```tsx#myPlugin.ts
import type { BunPlugin } from "bun";
@@ -22,9 +58,343 @@ const myPlugin: BunPlugin = {
This plugin can be passed into the `plugins` array when calling `Bun.build`.
```ts
Bun.build({
await Bun.build({
entrypoints: ["./app.ts"],
outdir: "./out",
plugins: [myPlugin],
});
```
## Plugin lifecycle
### Namespaces
`onLoad` and `onResolve` accept an optional `namespace` string. What is a namespace?
Every module has a namespace. Namespaces are used to prefix the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` will transform an import from `./myfile.yaml` into `yaml:./myfile.yaml`.
The default namespace is `"file"` and it is not necessary to specify it, for instance: `import myModule from "./my-module.ts"` is the same as `import myModule from "file:./my-module.ts"`.
Other common namespaces are:
- `"bun"`: for Bun-specific modules (e.g. `"bun:test"`, `"bun:sqlite"`)
- `"node"`: for Node.js modules (e.g. `"node:fs"`, `"node:path"`)
### `onStart`
```ts
onStart(callback: () => void): Promise<void> | void;
```
Registers a callback to be run when the bundler starts a new bundle.
```ts
import { plugin } from "bun";
plugin({
name: "onStart example",
setup(build) {
build.onStart(() => {
console.log("Bundle started!");
});
},
});
```
The callback can return a `Promise`. After the bundle process has initialized, the bundler waits until all `onStart()` callbacks have completed before continuing.
For example:
```ts
const result = await Bun.build({
entrypoints: ["./app.ts"],
outdir: "./dist",
sourcemap: "external",
plugins: [
{
name: "Sleep for 10 seconds",
setup(build) {
build.onStart(async () => {
await Bunlog.sleep(10_000);
});
},
},
{
name: "Log bundle time to a file",
setup(build) {
build.onStart(async () => {
const now = Date.now();
await Bun.$`echo ${now} > bundle-time.txt`;
});
},
},
],
});
```
In the above example, Bun will wait until the first `onStart()` (sleeping for 10 seconds) has completed, _as well as_ the second `onStart()` (writing the bundle time to a file).
Note that `onStart()` callbacks (like every other lifecycle callback) do not have the ability to modify the `build.config` object. If you want to mutate `build.config`, you must do so directly in the `setup()` function.
### `onResolve`
```ts
onResolve(
args: { filter: RegExp; namespace?: string },
callback: (args: { path: string; importer: string }) => {
path: string;
namespace?: string;
} | void,
): void;
```
To bundle your project, Bun walks down the dependency tree of all modules in your project. For each imported module, Bun actually has to find and read that module. The "finding" part is known as "resolving" a module.
The `onResolve()` plugin lifecycle callback allows you to configure how a module is resolved.
The first argument to `onResolve()` is an object with a `filter` and [`namespace`](#what-is-a-namespace) property. The filter is a regular expression which is run on the import string. Effectively, these allow you to filter which modules your custom resolution logic will apply to.
The second argument to `onResolve()` is a callback which is run for each module import Bun finds that matches the `filter` and `namespace` defined in the first argument.
The callback receives as input the _path_ to the matching module. The callback can return a _new path_ for the module. Bun will read the contents of the _new path_ and parse it as a module.
For example, redirecting all imports to `images/` to `./public/images/`:
```ts
import { plugin } from "bun";
plugin({
name: "onResolve example",
setup(build) {
build.onResolve({ filter: /.*/, namespace: "file" }, args => {
if (args.path.startsWith("images/")) {
return {
path: args.path.replace("images/", "./public/images/"),
};
}
});
},
});
```
### `onLoad`
```ts
onLoad(
args: { filter: RegExp; namespace?: string },
defer: () => Promise<void>,
callback: (args: { path: string, importer: string, namespace: string, kind: ImportKind }) => {
loader?: Loader;
contents?: string;
exports?: Record<string, any>;
},
): void;
```
After Bun's bundler has resolved a module, it needs to read the contents of the module and parse it.
The `onLoad()` plugin lifecycle callback allows you to modify the _contents_ of a module before it is read and parsed by Bun.
Like `onResolve()`, the first argument to `onLoad()` allows you to filter which modules this invocation of `onLoad()` will apply to.
The second argument to `onLoad()` is a callback which is run for each matching module _before_ Bun loads the contents of the module into memory.
This callback receives as input the _path_ to the matching module, the _importer_ of the module (the module that imported the module), the _namespace_ of the module, and the _kind_ of the module.
The callback can return a new `contents` string for the module as well as a new `loader`.
For example:
```ts
import { plugin } from "bun";
const envPlugin: BunPlugin = {
name: "env plugin",
setup(build) {
build.onLoad({ filter: /env/, namespace: "file" }, args => {
return {
contents: `export default ${JSON.stringify(process.env)}`,
loader: "js",
};
});
},
});
Bun.build({
entrypoints: ["./app.ts"],
outdir: "./dist",
plugins: [envPlugin],
});
// import env from "env"
// env.FOO === "bar"
```
This plugin will transform all imports of the form `import env from "env"` into a JavaScript module that exports the current environment variables.
#### `.defer()`
One of the arguments passed to the `onLoad` callback is a `defer` function. This function returns a `Promise` that is resolved when all _other_ modules have been loaded.
This allows you to delay execution of the `onLoad` callback until all other modules have been loaded.
This is useful for returning contents of a module that depends on other modules.
##### Example: tracking and reporting unused exports
```ts
import { plugin } from "bun";
plugin({
name: "track imports",
setup(build) {
const transpiler = new Bun.Transpiler();
let trackedImports: Record<string, number> = {};
// Each module that goes through this onLoad callback
// will record its imports in `trackedImports`
build.onLoad({ filter: /\.ts/ }, async ({ path }) => {
const contents = await Bun.file(path).arrayBuffer();
const imports = transpiler.scanImports(contents);
for (const i of imports) {
trackedImports[i.path] = (trackedImports[i.path] || 0) + 1;
}
return undefined;
});
build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => {
// Wait for all files to be loaded, ensuring
// that every file goes through the above `onLoad()` function
// and their imports tracked
await defer();
// Emit JSON containing the stats of each import
return {
contents: `export default ${JSON.stringify(trackedImports)}`,
loader: "json",
};
});
},
});
```
Note that the `.defer()` function currently has the limitation that it can only be called once per `onLoad` callback.
## Native plugins
One of the reasons why Bun's bundler is so fast is that it is written in native code and leverages multi-threading to load and parse modules in parallel.
However, one limitation of plugins written in JavaScript is that JavaScript itself is single-threaded.
Native plugins are written as [NAPI](/docs/node-api) modules and can be run on multiple threads. This allows native plugins to run much faster than JavaScript plugins.
In addition, native plugins can skip unnecessary work such as the UTF-8 -> UTF-16 conversion needed to pass strings to JavaScript.
These are the following lifecycle hooks which are available to native plugins:
- [`onBeforeParse()`](#onbeforeparse): Called on any thread before a file is parsed by Bun's bundler.
Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions.
To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement.
### Creating a native plugin in Rust
Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions.
To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement.
```bash
bun add -g @napi-rs/cli
napi new
```
Then install this crate:
```bash
cargo add bun-native-plugin
```
Now, inside the `lib.rs` file, we'll use the `bun_native_plugin::bun` proc macro to define a function which
will implement our native plugin.
Here's an example implementing the `onBeforeParse` hook:
```rs
use bun_native_plugin::{define_bun_plugin, OnBeforeParse, bun, Result, anyhow, BunLoader};
use napi_derive::napi;
/// Define the plugin and its name
define_bun_plugin!("replace-foo-with-bar");
/// Here we'll implement `onBeforeParse` with code that replaces all occurrences of
/// `foo` with `bar`.
///
/// We use the #[bun] macro to generate some of the boilerplate code.
///
/// The argument of the function (`handle: &mut OnBeforeParse`) tells
/// the macro that this function implements the `onBeforeParse` hook.
#[bun]
pub fn replace_foo_with_bar(handle: &mut OnBeforeParse) -> Result<()> {
// Fetch the input source code.
let input_source_code = handle.input_source_code()?;
// Get the Loader for the file
let loader = handle.output_loader();
let output_source_code = input_source_code.replace("foo", "bar");
handle.set_output_source_code(output_source_code, BunLoader::BUN_LOADER_JSX);
Ok(())
}
```
And to use it in Bun.build():
```typescript
import myNativeAddon from "./my-native-addon";
Bun.build({
entrypoints: ["./app.tsx"],
plugins: [
{
name: "my-plugin",
setup(build) {
build.onBeforeParse(
{
namespace: "file",
filter: "**/*.tsx",
},
{
napiModule: myNativeAddon,
symbol: "replace_foo_with_bar",
// external: myNativeAddon.getSharedState()
},
);
},
},
],
});
```
### `onBeforeParse`
```ts
onBeforeParse(
args: { filter: RegExp; namespace?: string },
callback: { napiModule: NapiModule; symbol: string; external?: unknown },
): void;
```
This lifecycle callback is run immediately before a file is parsed by Bun's bundler.
As input, it receives the file's contents and can optionally return new source code.
This callback can be called from any thread and so the napi module implementation must be thread-safe.

View File

@@ -695,7 +695,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
- In Bun, `minify` can be a boolean or an object.
```ts
Bun.build({
await Bun.build({
entrypoints: ['./index.tsx'],
// enable all minification
minify: true

View File

@@ -47,6 +47,9 @@ registry = "https://registry.yarnpkg.com/"
# Install for production? This is the equivalent to the "--production" CLI argument
production = false
# Save a text-based lockfile? This is equivalent to the "--save-text-lockfile" CLI argument
saveTextLockfile = false
# Disallow changes to lockfile? This is the equivalent to the "--frozen-lockfile" CLI argument
frozenLockfile = false
@@ -54,12 +57,15 @@ frozenLockfile = false
dryRun = true
# Install optionalDependencies (default: true)
# Setting this to false is equivalent to the `--omit=optional` CLI argument
optional = true
# Install local devDependencies (default: true)
# Setting this to false is equivalent to the `--omit=dev` CLI argument
dev = true
# Install peerDependencies (default: true)
# Setting this to false is equivalent to the `--omit=peer` CLI argument
peer = true
# Max number of concurrent lifecycle scripts (default: (cpu count or GOMAXPROCS) x2)
@@ -108,6 +114,7 @@ export interface Install {
scopes: Scopes;
registry: Registry;
production: boolean;
saveTextLockfile: boolean;
frozenLockfile: boolean;
dryRun: boolean;
optional: boolean;

View File

@@ -130,6 +130,20 @@ $ bun install --frozen-lockfile
For more information on Bun's binary lockfile `bun.lockb`, refer to [Package manager > Lockfile](https://bun.sh/docs/install/lockfile).
## Omitting dependencies
To omit dev, peer, or optional dependencies use the `--omit` flag.
```bash
# Exclude "devDependencies" from the installation. This will apply to the
# root package and workspaces if they exist. Transitive dependencies will
# not have "devDependencies".
$ bun install --omit dev
# Install only dependencies from "dependencies"
$ bun install --omit=dev --omit=peer --omit=optional
```
## Dry run
To perform a dry run (i.e. don't actually install anything):
@@ -149,7 +163,8 @@ Bun supports installing dependencies from Git, GitHub, and local or remotely-hos
"lodash": "git+ssh://github.com/lodash/lodash.git#4.17.21",
"moment": "git@github.com:moment/moment.git",
"zod": "github:colinhacks/zod",
"react": "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
"react": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"bun-types": "npm:@types/bun"
}
}
```
@@ -173,6 +188,9 @@ peer = true
# equivalent to `--production` flag
production = false
# equivalent to `--save-text-lockfile` flag
saveTextLockfile = false
# equivalent to `--frozen-lockfile` flag
frozenLockfile = false

View File

@@ -15,6 +15,14 @@ $ bun create next-app
Creating a new Next.js app in /path/to/my-app.
```
You can specify a starter template using the `--example` flag.
```sh
$ bun create next-app --example with-supabase
✔ What is your project named? … my-app
...
```
---
To start the dev server with Bun, run `bun --bun run dev` from the project root.

View File

@@ -2,7 +2,9 @@
name: Configure a private registry for an organization scope with bun install
---
Bun does not read `.npmrc` files; instead private registries are configured via `bunfig.toml`. To configure a registry for a particular npm scope:
Private registries can be configured using either [`.npmrc`](https://bun.sh/docs/install/npmrc) or [`bunfig.toml`](https://bun.sh/docs/runtime/bunfig#install-registry). While both are supported, we recommend using **bunfig.toml** for enhanced flexibility and Bun-specific options.
To configure a registry for a particular npm scope:
```toml#bunfig.toml
[install.scopes]

View File

@@ -16,7 +16,7 @@ Set these variables in a `.env` file.
Bun reads the following files automatically (listed in order of increasing precedence).
- `.env`
- `.env.production` or `.env.development` (depending on value of `NODE_ENV`)
- `.env.production`, `.env.development`, `.env.test` (depending on value of `NODE_ENV`)
- `.env.local`
```txt#.env

View File

@@ -30,7 +30,6 @@ Bun implements the vast majority of Jest's matchers, but compatibility isn't 100
Some notable missing features:
- `expect().toMatchInlineSnapshot()`
- `expect().toHaveReturned()`
---

View File

@@ -4,10 +4,6 @@ name: Use snapshot testing in `bun test`
Bun's test runner supports Jest-style snapshot testing via `.toMatchSnapshot()`.
{% callout %}
The `.toMatchInlineSnapshot()` method is not yet supported.
{% /callout %}
```ts#snap.test.ts
import { test, expect } from "bun:test";
@@ -96,4 +92,4 @@ Ran 1 tests across 1 files. [102.00ms]
---
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner.
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/snapshots) for complete documentation on snapshots with the Bun test runner.

View File

@@ -0,0 +1,120 @@
---
name: "import, require, and test Svelte components with bun test"
---
Bun's [Plugin API](/docs/runtime/plugins) lets you add custom loaders to your project. The `test.preload` option in `bunfig.toml` lets you configure your loader to start before your tests run.
Firstly, install `@testing-library/svelte`, `svelte`, and `@happy-dom/global-registrator`.
```bash
$ bun add @testing-library/svelte svelte@4 @happy-dom/global-registrator
```
Then, save this plugin in your project.
```ts#svelte-loader.js
import { plugin } from "bun";
import { compile } from "svelte/compiler";
import { readFileSync } from "fs";
import { beforeEach, afterEach } from "bun:test";
import { GlobalRegistrator } from "@happy-dom/global-registrator";
beforeEach(async () => {
await GlobalRegistrator.register();
});
afterEach(async () => {
await GlobalRegistrator.unregister();
});
plugin({
name: "svelte loader",
setup(builder) {
builder.onLoad({ filter: /\.svelte(\?[^.]+)?$/ }, ({ path }) => {
try {
const source = readFileSync(
path.substring(
0,
path.includes("?") ? path.indexOf("?") : path.length
),
"utf-8"
);
const result = compile(source, {
filename: path,
generate: "client",
dev: false,
});
return {
contents: result.js.code,
loader: "js",
};
} catch (err) {
throw new Error(`Failed to compile Svelte component: ${err.message}`);
}
});
},
});
```
---
Add this to `bunfig.toml` to tell Bun to preload the plugin, so it loads before your tests run.
```toml#bunfig.toml
[test]
# Tell Bun to load this plugin before your tests run
preload = ["./svelte-loader.js"]
# This also works:
# test.preload = ["./svelte-loader.js"]
```
---
Add an example `.svelte` file in your project.
```html#Counter.svelte
<script>
export let initialCount = 0;
let count = initialCount;
</script>
<button on:click={() => (count += 1)}>+1</button>
```
---
Now you can `import` or `require` `*.svelte` files in your tests, and it will load the Svelte component as a JavaScript module.
```ts#hello-svelte.test.ts
import { test, expect } from "bun:test";
import { render, fireEvent } from "@testing-library/svelte";
import Counter from "./Counter.svelte";
test("Counter increments when clicked", async () => {
const { getByText, component } = render(Counter);
const button = getByText("+1");
// Initial state
expect(component.$$.ctx[0]).toBe(0); // initialCount is the first prop
// Click the increment button
await fireEvent.click(button);
// Check the new state
expect(component.$$.ctx[0]).toBe(1);
});
```
---
Use `bun test` to run your tests.
```bash
$ bun test
```
---

View File

@@ -49,7 +49,7 @@ Next, add these preload scripts to your `bunfig.toml` (you can also have everyth
```toml#bunfig.toml
[test]
preload = ["happydom.ts", "testing-library.ts"]
preload = ["./happydom.ts", "./testing-library.ts"]
```
---
@@ -84,4 +84,4 @@ test('Can use Testing Library', () => {
---
Refer to the [Testing Library docs](https://testing-library.com/), [Happy DOM repo](https://github.com/capricorn86/happy-dom) and [Docs > Test runner > DOM](https://bun.sh/docs/test/dom) for complete documentation on writing browser tests with Bun.
Refer to the [Testing Library docs](https://testing-library.com/), [Happy DOM repo](https://github.com/capricorn86/happy-dom) and [Docs > Test runner > DOM](https://bun.sh/docs/test/dom) for complete documentation on writing browser tests with Bun.

View File

@@ -4,10 +4,6 @@ name: Update snapshots in `bun test`
Bun's test runner supports Jest-style snapshot testing via `.toMatchSnapshot()`.
{% callout %}
The `.toMatchInlineSnapshot()` method is not yet supported.
{% /callout %}
```ts#snap.test.ts
import { test, expect } from "bun:test";
@@ -47,4 +43,4 @@ Ran 1 tests across 1 files. [102.00ms]
---
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner.
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/snapshots) for complete documentation on snapshots with the Bun test runner.

View File

@@ -1,4 +1,4 @@
All packages downloaded from the registry are stored in a global cache at `~/.bun/install/cache`. They are stored in subdirectories named like `${name}@${version}`, so multiple versions of a package can be cached.
All packages downloaded from the registry are stored in a global cache at `~/.bun/install/cache`, or the path defined by the environment variable `BUN_INSTALL_CACHE_DIR`. They are stored in subdirectories named like `${name}@${version}`, so multiple versions of a package can be cached.
{% details summary="Configuring cache behavior (bunfig.toml)" %}

View File

@@ -55,12 +55,25 @@ To install dependencies without allowing changes to lockfile (useful on CI):
$ bun install --frozen-lockfile
```
To perform a dry run (i.e. don't actually install anything):
To exclude dependency types from installing, use `--omit` with `dev`, `optional`, or `peer`:
```bash
# Disable devDependencies and optionalDependencies
$ bun install --omit=dev --omit=optional
```
To perform a dry run (i.e. don't actually install anything or update the lockfile):
```bash
$ bun install --dry-run
```
To generate a lockfile without install packages:
```bash
$ bun install --lockfile-only
```
To modify logging verbosity:
```bash
@@ -86,6 +99,9 @@ peer = true
# equivalent to `--production` flag
production = false
# equivalent to `--save-text-lockfile` flag
saveTextLockfile = false
# equivalent to `--frozen-lockfile` flag
frozenLockfile = false

View File

@@ -49,6 +49,18 @@ Packages, metadata for those packages, the hoisted install order, dependencies f
It uses linear arrays for all data. [Packages](https://github.com/oven-sh/bun/blob/be03fc273a487ac402f19ad897778d74b6d72963/src/install/install.zig#L1825) are referenced by an auto-incrementing integer ID or a hash of the package name. Strings longer than 8 characters are de-duplicated. Prior to saving on disk, the lockfile is garbage-collected & made deterministic by walking the package tree and cloning the packages in dependency order.
#### Generate a lockfile without installing?
To generate a lockfile without installing to `node_modules` you can use the `--lockfile-only` flag. The lockfile will always be saved to disk, even if it is up-to-date with the `package.json`(s) for your project.
```bash
$ bun install --lockfile-only
```
{% callout %}
**Note** - using `--lockfile-only` will still populate the global install cache with registry metadata and git/tarball dependencies.
{% endcallout %}
#### Can I opt out?
To install without creating a lockfile:
@@ -74,6 +86,24 @@ print = "yarn"
{% /codetabs %}
### Text-based lockfile
Bun v1.1.39 introduced `bun.lock`, a JSONC formatted lockfile. `bun.lock` is human-readable and git-diffable without configuration, at [no cost to performance](https://bun.sh/blog/bun-lock-text-lockfile#cached-bun-install-gets-30-faster).
To generate the lockfile, use `--save-text-lockfile` with `bun install`. You can do this for new projects and existing projects already using `bun.lockb` (resolutions will be preserved).
```bash
$ bun install --save-text-lockfile
$ head -n3 bun.lock
{
"lockfileVersion": 0,
"workspaces": {
```
Once `bun.lock` is generated, Bun will use it for all subsequent installs and updates through commands that read and modify the lockfile. If both lockfiles exist, `bun.lock` will be choosen over `bun.lockb`.
Bun v1.2.0 will switch the default lockfile format to `bun.lock`.
{% details summary="Configuring lockfile" %}
```toml

View File

@@ -6,7 +6,7 @@ Bun supports loading configuration options from [`.npmrc`](https://docs.npmjs.co
{% /callout %}
# Supported options
## Supported options
### `registry`: Set the default registry

View File

@@ -214,9 +214,9 @@ export default {
page("bundler", "`Bun.build`", {
description: "Bundle code for consumption in the browser with Bun's native bundler.",
}),
// page("bundler/intro", "How bundlers work", {
// description: "A visual introduction to bundling",
// }),
page("bundler/html", "HTML", {
description: `Bundle html files with Bun's native bundler.`,
}),
page("bundler/loaders", "Loaders", {
description: "Bun's built-in loaders for the bundler and runtime",
}),
@@ -226,6 +226,7 @@ export default {
page("bundler/macros", "Macros", {
description: `Run JavaScript functions at bundle-time and inline the results into your bundle`,
}),
page("bundler/vs-esbuild", "vs esbuild", {
description: `Guides for migrating from other bundlers to Bun.`,
}),
@@ -310,6 +311,9 @@ export default {
page("api/streams", "Streams", {
description: `Reading, writing, and manipulating streams of data in Bun.`,
}), // "`Bun.serve`"),
page("api/s3", "S3 Object Storage", {
description: `Bun provides fast, native bindings for interacting with S3-compatible object storage services.`,
}),
page("api/file-io", "File I/O", {
description: `Read and write files fast with Bun's heavily optimized file system API.`,
}), // "`Bun.write`"),
@@ -402,6 +406,9 @@ export default {
page("project/building-windows", "Building Windows", {
description: "Learn how to setup a development environment for contributing to the Windows build of Bun.",
}),
page("project/bindgen", "Bindgen", {
description: "About the bindgen code generator",
}),
page("project/licensing", "License", {
description: `Bun is a MIT-licensed project with a large number of statically-linked dependencies with various licenses.`,
}),

225
docs/project/bindgen.md Normal file
View File

@@ -0,0 +1,225 @@
{% callout %}
This document is for maintainers and contributors to Bun, and describes internal implementation details.
{% /callout %}
The new bindings generator, introduced to the codebase in Dec 2024, scans for
`*.bind.ts` to find function and class definition, and generates glue code to
interop between JavaScript and native code.
There are currently other code generators and systems that achieve similar
purposes. The following will all eventually be completely phased out in favor of
this one:
- "Classes generator", converting `*.classes.ts` for custom classes.
- "JS2Native", allowing ad-hoc calls from `src/js` to native code.
## Creating JS Functions in Zig
Given a file implementing a simple function, such as `add`
```zig#src/bun.js/math.zig
pub fn add(global: *JSC.JSGlobalObject, a: i32, b: i32) !i32 {
return std.math.add(i32, a, b) catch {
// Binding functions can return `error.OutOfMemory` and `error.JSError`.
// Others like `error.Overflow` from `std.math.add` must be converted.
// Remember to be descriptive.
return global.throwPretty("Integer overflow while adding", .{});
};
}
const gen = bun.gen.math; // "math" being this file's basename
const std = @import("std");
const bun = @import("root").bun;
const JSC = bun.JSC;
```
Then describe the API schema using a `.bind.ts` function. The binding file goes next to the Zig file.
```ts#src/bun.js/math.bind.ts
import { t, fn } from 'bindgen';
export const add = fn({
args: {
global: t.globalObject,
a: t.i32,
b: t.i32.default(1),
},
ret: t.i32,
});
```
This function declaration is equivalent to:
```ts
/**
* Throws if zero arguments are provided.
* Wraps out of range numbers using modulo.
*/
declare function add(a: number, b: number = 1): number;
```
The code generator will provide `bun.gen.math.jsAdd`, which is the native
function implementation. To pass to JavaScript, use
`bun.gen.math.createAddCallback(global)`. JS files in `src/js/` may use
`$bindgenFn("math.bind.ts", "add")` to get a handle to the implementation.
## Strings
The type for receiving strings is one of [`t.DOMString`](https://webidl.spec.whatwg.org/#idl-DOMString), [`t.ByteString`](https://webidl.spec.whatwg.org/#idl-ByteString), and [`t.USVString`](https://webidl.spec.whatwg.org/#idl-USVString). These map directly to their WebIDL counterparts, and have slightly different conversion logic. Bindgen will pass BunString to native code in all cases.
When in doubt, use DOMString.
`t.UTF8String` can be used in place of `t.DOMString`, but will call `bun.String.toUTF8`. The native callback gets `[]const u8` (WTF-8 data) passed to native code, freeing it after the function returns.
TLDRs from WebIDL spec:
- ByteString can only contain valid latin1 characters. It is not safe to assume bun.String is already in 8-bit format, but it is extremely likely.
- USVString will not contain invalid surrogate pairs, aka text that can be represented correctly in UTF-8.
- DOMString is the loosest but also most recommended strategy.
## Function Variants
A `variants` can specify multiple variants (also known as overloads).
```ts#src/bun.js/math.bind.ts
import { t, fn } from 'bindgen';
export const action = fn({
variants: [
{
args: {
a: t.i32,
},
ret: t.i32,
},
{
args: {
a: t.DOMString,
},
ret: t.DOMString,
},
]
});
```
In Zig, each variant gets a number, based on the order the schema defines.
```zig
fn action1(a: i32) i32 {
return a;
}
fn action2(a: bun.String) bun.String {
return a;
}
```
## `t.dictionary`
A `dictionary` is a definition for a JavaScript object, typically as a function inputs. For function outputs, it is usually a smarter idea to declare a class type to add functions and destructuring.
## Enumerations
To use [WebIDL's enumeration](https://webidl.spec.whatwg.org/#idl-enums) type, use either:
- `t.stringEnum`: Create and codegen a new enum type.
- `t.zigEnum`: Derive a bindgen type off of an existing enum in the codebase.
An example of `stringEnum` as used in `fmt.zig` / `bun:internal-for-testing`
```ts
export const Formatter = t.stringEnum(
"highlight-javascript",
"escape-powershell",
);
export const fmtString = fn({
args: {
global: t.globalObject,
code: t.UTF8String,
formatter: Formatter,
},
ret: t.DOMString,
});
```
WebIDL strongly encourages using kebab case for enumeration values, to be consistent with existing Web APIs.
### Deriving enums from Zig code
TODO: zigEnum
## `t.oneOf`
A `oneOf` is a union between two or more types. It is represented by `union(enum)` in Zig.
TODO:
## Attributes
There are set of attributes that can be chained onto `t.*` types. On all types there are:
- `.required`, in dictionary parameters only
- `.optional`, in function arguments only
- `.default(T)`
When a value is optional, it is lowered to a Zig optional.
Depending on the type, there are more attributes available. See the type definitions in auto-complete for more details. Note that one of the above three can only be applied, and they must be applied at the end.
### Integer Attributes
Integer types allow customizing the overflow behavior with `clamp` or `enforceRange`
```ts
import { t, fn } from "bindgen";
export const add = fn({
args: {
global: t.globalObject,
// enforce in i32 range
a: t.i32.enforceRange(),
// clamp to u16 range
b: t.u16,
// enforce in arbitrary range, with a default if not provided
c: t.i32.enforceRange(0, 1000).default(5),
// clamp to arbitrary range, or null
d: t.u16.clamp(0, 10).optional,
},
ret: t.i32,
});
```
Various Node.js validator functions such as `validateInteger`, `validateNumber`, and more are available. Use these when implementing Node.js APIs, so the error messages match 1:1 what Node would do.
Unlike `enforceRange`, which is taken from WebIDL, `validate*` functions are much more strict on the input they accept. For example, Node's numerical validator check `typeof value === 'number'`, while WebIDL uses `ToNumber` for lossy conversion.
```ts
import { t, fn } from "bindgen";
export const add = fn({
args: {
global: t.globalObject,
// throw if not given a number
a: t.f64.validateNumber(),
// valid in i32 range
a: t.i32.validateInt32(),
// f64 within safe integer range
b: t.f64.validateInteger(),
// f64 in given range
c: t.f64.validateNumber(-10000, 10000),
},
ret: t.i32,
});
```
## Callbacks
TODO
## Classes
TODO

View File

@@ -238,6 +238,17 @@ By default Bun uses caret ranges; if the `latest` version of a package is `2.4.1
exact = false
```
### `install.saveTextLockfile`
Generate `bun.lock`, a human-readable text-based lockfile. Once generated, Bun will use this file instead of `bun.lockb`, choosing it over the binary lockfile if both are present.
Default `false`. In Bun v1.2.0 the default lockfile format will change to `bun.lock`.
```toml
[install]
saveTextLockfile = true
```
<!--
### `install.prefer`

View File

@@ -259,6 +259,7 @@ await Bun.build({
conditions: ["react-server"],
target: "bun",
entryPoints: ["./app/foo/route.js"],
throw: true,
});
```

View File

@@ -53,7 +53,7 @@ Some methods are not optimized yet.
### [`node:events`](https://nodejs.org/api/events.html)
🟡 `events.addAbortListener` & `events.getMaxListeners` do not support (web api) `EventTarget`
🟢 Fully implemented. `EventEmitterAsyncResource` uses `AsyncResource` underneath.
### [`node:fs`](https://nodejs.org/api/fs.html)
@@ -157,11 +157,11 @@ Some methods are not optimized yet.
### [`node:v8`](https://nodejs.org/api/v8.html)
🔴 `serialize` and `deserialize` use JavaScriptCore's wire format instead of V8's. Otherwise, not implemented. For profiling, use [`bun:jsc`](https://bun.sh/docs/project/benchmarking#bunjsc) instead.
🟡 `writeHeapSnapshot` and `getHeapSnapshot` are implemented. `serialize` and `deserialize` use JavaScriptCore's wire format instead of V8's. Other methods are not implemented. For profiling, use [`bun:jsc`](https://bun.sh/docs/project/benchmarking#bunjsc) instead.
### [`node:vm`](https://nodejs.org/api/vm.html)
🟡 Core functionality works, but experimental VM ES modules are not implemented, including `vm.Module`, `vm.SourceTextModule`, `vm.SyntheticModule`,`importModuleDynamically`, and `vm.measureMemory`. Options like `timeout`, `breakOnSigint`, `cachedData` are not implemented yet. There is a bug with `this` value for contextified options not having the correct prototype.
🟡 Core functionality works, but experimental VM ES modules are not implemented, including `vm.Module`, `vm.SourceTextModule`, `vm.SyntheticModule`,`importModuleDynamically`, and `vm.measureMemory`. Options like `timeout`, `breakOnSigint`, `cachedData` are not implemented yet.
### [`node:wasi`](https://nodejs.org/api/wasi.html)

View File

@@ -307,7 +307,7 @@ await import("my-object-virtual-module"); // { baz: "quix" }
Plugins can read and write to the [build config](https://bun.sh/docs/bundler#api) with `build.config`.
```ts
Bun.build({
await Bun.build({
entrypoints: ["./app.ts"],
outdir: "./dist",
sourcemap: "external",
@@ -324,6 +324,7 @@ Bun.build({
},
},
],
throw: true,
});
```
@@ -332,7 +333,7 @@ Bun.build({
**NOTE**: Plugin lifcycle callbacks (`onStart()`, `onResolve()`, etc.) do not have the ability to modify the `build.config` object in the `setup()` function. If you want to mutate `build.config`, you must do so directly in the `setup()` function:
```ts
Bun.build({
await Bun.build({
entrypoints: ["./app.ts"],
outdir: "./dist",
sourcemap: "external",
@@ -350,12 +351,13 @@ Bun.build({
},
},
],
throw: true,
});
```
{% /callout %}
## Lifecycle callbacks
## Lifecycle hooks
Plugins can register callbacks to be run at various points in the lifecycle of a bundle:
@@ -363,6 +365,8 @@ Plugins can register callbacks to be run at various points in the lifecycle of a
- [`onResolve()`](#onresolve): Run before a module is resolved
- [`onLoad()`](#onload): Run before a module is loaded.
### Reference
A rough overview of the types (please refer to Bun's `bun.d.ts` for the full type definitions):
```ts
@@ -551,55 +555,3 @@ plugin({
```
This plugin will transform all imports of the form `import env from "env"` into a JavaScript module that exports the current environment variables.
#### `.defer()`
One of the arguments passed to the `onLoad` callback is a `defer` function. This function returns a `Promise` that is resolved when all _other_ modules have been loaded.
This allows you to delay execution of the `onLoad` callback until all other modules have been loaded.
This is useful for returning contens of a module that depends on other modules.
##### Example: tracking and reporting unused exports
```ts
import { plugin } from "bun";
plugin({
name: "track imports",
setup(build) {
const transpiler = new Bun.Transpiler();
let trackedImports: Record<string, number> = {};
// Each module that goes through this onLoad callback
// will record its imports in `trackedImports`
build.onLoad({ filter: /\.ts/ }, async ({ path }) => {
const contents = await Bun.file(path).arrayBuffer();
const imports = transpiler.scanImports(contents);
for (const i of imports) {
trackedImports[i.path] = (trackedImports[i.path] || 0) + 1;
}
return undefined;
});
build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => {
// Wait for all files to be loaded, ensuring
// that every file goes through the above `onLoad()` function
// and their imports tracked
await defer();
// Emit JSON containing the stats of each import
return {
contents: `export default ${JSON.stringify(trackedImports)}`,
loader: "json",
};
});
},
});
```
Note that the `.defer()` function currently has the limitation that it can only be called once per `onLoad` callback.

View File

@@ -531,17 +531,17 @@ Bun implements the following matchers. Full Jest compatibility is on the roadmap
---
-
-
- [`.toMatchInlineSnapshot()`](https://jestjs.io/docs/expect#tomatchinlinesnapshotpropertymatchers-inlinesnapshot)
---
-
-
- [`.toThrowErrorMatchingSnapshot()`](https://jestjs.io/docs/expect#tothrowerrormatchingsnapshothint)
---
-
-
- [`.toThrowErrorMatchingInlineSnapshot()`](https://jestjs.io/docs/expect#tothrowerrormatchinginlinesnapshotinlinesnapshot)
{% /table %}

View File

@@ -14,5 +14,7 @@
<true/>
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.cs.debugger</key>
<true/>
</dict>
</plist>
</plist>

View File

@@ -1,234 +0,0 @@
const std = @import("std");
const bun = @import("root").bun;
const string = bun.string;
const Output = bun.Output;
const Global = bun.Global;
const Environment = bun.Environment;
const strings = bun.strings;
const MutableString = bun.MutableString;
const stringZ = bun.stringZ;
const default_allocator = bun.default_allocator;
const C = bun.C;
pub usingnamespace @import("root").bun;
const clap = bun.clap;
const URL = @import("../src/url.zig").URL;
const Headers = bun.http.Headers;
const Method = @import("../src/http/method.zig").Method;
const ColonListType = @import("../src/cli/colon_list_type.zig").ColonListType;
const HeadersTuple = ColonListType(string, noop_resolver);
const path_handler = @import("../src/resolver/resolve_path.zig");
const HTTPThread = bun.http.HTTPThread;
const HTTP = bun.http;
fn noop_resolver(in: string) !string {
return in;
}
const VERSION = "0.0.0";
const params = [_]clap.Param(clap.Help){
clap.parseParam("-v, --verbose Show headers & status code") catch unreachable,
clap.parseParam("-H, --header <STR>... Add a header") catch unreachable,
clap.parseParam("-r, --max-redirects <STR> Maximum number of redirects to follow (default: 128)") catch unreachable,
clap.parseParam("-b, --body <STR> HTTP request body as a string") catch unreachable,
clap.parseParam("-f, --file <STR> File path to load as body") catch unreachable,
clap.parseParam("-q, --quiet Quiet mode") catch unreachable,
clap.parseParam("--no-gzip Disable gzip") catch unreachable,
clap.parseParam("--no-deflate Disable deflate") catch unreachable,
clap.parseParam("--no-compression Disable gzip & deflate") catch unreachable,
clap.parseParam("--version Print the version and exit") catch unreachable,
clap.parseParam("--turbo Skip sending TLS shutdown signals") catch unreachable,
clap.parseParam("<POS>... ") catch unreachable,
};
const MethodNames = std.ComptimeStringMap(Method, .{
.{ "GET", Method.GET },
.{ "get", Method.GET },
.{ "POST", Method.POST },
.{ "post", Method.POST },
.{ "PUT", Method.PUT },
.{ "put", Method.PUT },
.{ "PATCH", Method.PATCH },
.{ "patch", Method.PATCH },
.{ "OPTIONS", Method.OPTIONS },
.{ "options", Method.OPTIONS },
.{ "HEAD", Method.HEAD },
.{ "head", Method.HEAD },
});
var file_path_buf: bun.PathBuffer = undefined;
var cwd_buf: bun.PathBuffer = undefined;
pub const Arguments = struct {
url: URL,
method: Method,
verbose: bool = false,
headers: Headers.Entries,
headers_buf: string,
body: string = "",
turbo: bool = false,
quiet: bool = false,
pub fn parse(allocator: std.mem.Allocator) !Arguments {
var diag = clap.Diagnostic{};
var args = clap.parse(clap.Help, &params, .{
.diagnostic = &diag,
.allocator = allocator,
}) catch |err| {
// Report useful error and exit
diag.report(Output.errorWriter(), err) catch {};
return err;
};
var positionals = args.positionals();
var raw_args: std.ArrayListUnmanaged(string) = undefined;
if (positionals.len > 0) {
raw_args = .{ .capacity = positionals.len, .items = @as([*][]const u8, @ptrFromInt(@intFromPtr(positionals.ptr)))[0..positionals.len] };
} else {
raw_args = .{};
}
if (args.flag("--version")) {
try Output.writer().writeAll(VERSION);
Global.exit(0);
}
var method = Method.GET;
var url: URL = .{};
var body_string: string = args.option("--body") orelse "";
if (args.option("--file")) |file_path| {
if (file_path.len > 0) {
var cwd = try std.process.getCwd(&cwd_buf);
var parts = [_]string{file_path};
var absolute_path = path_handler.joinAbsStringBuf(cwd, &file_path_buf, &parts, .auto);
file_path_buf[absolute_path.len] = 0;
file_path_buf[absolute_path.len + 1] = 0;
var absolute_path_len = absolute_path.len;
var absolute_path_ = file_path_buf[0..absolute_path_len :0];
var body_file = std.fs.openFileAbsoluteZ(absolute_path_, .{ .mode = .read_only }) catch |err| {
Output.printErrorln("<r><red>{s}<r> opening file {s}", .{ @errorName(err), absolute_path });
Global.exit(1);
};
var file_contents = body_file.readToEndAlloc(allocator, try body_file.getEndPos()) catch |err| {
Output.printErrorln("<r><red>{s}<r> reading file {s}", .{ @errorName(err), absolute_path });
Global.exit(1);
};
body_string = file_contents;
}
}
{
var raw_arg_i: usize = 0;
while (raw_arg_i < raw_args.items.len) : (raw_arg_i += 1) {
const arg = raw_args.items[raw_arg_i];
if (MethodNames.get(arg[0..])) |method_| {
method = method_;
_ = raw_args.swapRemove(raw_arg_i);
}
}
if (raw_args.items.len == 0) {
Output.prettyErrorln("<r><red>error<r><d>:<r> <b>Missing URL<r>\n\nExample:\n<r><b>fetch GET https://example.com<r>\n\n<b>fetch example.com/foo<r>\n\n", .{});
Global.exit(1);
}
const url_position = raw_args.items.len - 1;
url = URL.parse(raw_args.swapRemove(url_position));
if (!url.isAbsolute()) {
Output.prettyErrorln("<r><red>error<r><d>:<r> <b>Invalid URL<r>\n\nExample:\n<r><b>fetch GET https://example.com<r>\n\n<b>fetch example.com/foo<r>\n\n", .{});
Global.exit(1);
}
}
return Arguments{
.url = url,
.method = method,
.verbose = args.flag("--verbose"),
.headers = .{},
.headers_buf = "",
.body = body_string,
.turbo = args.flag("--turbo"),
.quiet = args.flag("--quiet"),
};
}
};
pub fn main() anyerror!void {
var stdout_ = std.io.getStdOut();
var stderr_ = std.io.getStdErr();
var output_source = Output.Source.init(stdout_, stderr_);
Output.Source.set(&output_source);
defer Output.flush();
var args = try Arguments.parse(default_allocator);
var body_out_str = try MutableString.init(default_allocator, 1024);
var channel = try default_allocator.create(HTTP.HTTPChannel);
channel.* = HTTP.HTTPChannel.init();
var response_body_string = try default_allocator.create(MutableString);
response_body_string.* = body_out_str;
try channel.buffer.ensureTotalCapacity(1);
HTTPThread.init();
var ctx = try default_allocator.create(HTTP.HTTPChannelContext);
ctx.* = .{
.channel = channel,
.http = try HTTP.AsyncHTTP.init(
default_allocator,
args.method,
args.url,
args.headers,
args.headers_buf,
response_body_string,
args.body,
HTTP.FetchRedirect.follow,
),
};
ctx.http.callback = HTTP.HTTPChannelContext.callback;
var batch = HTTPThread.Batch{};
ctx.http.schedule(default_allocator, &batch);
ctx.http.client.verbose = args.verbose;
ctx.http.verbose = args.verbose;
HTTPThread.global.schedule(batch);
while (true) {
while (channel.tryReadItem() catch null) |http| {
var response = http.response orelse {
Output.prettyErrorln("<r><red>error<r><d>:<r> <b>HTTP response missing<r>", .{});
Global.exit(1);
};
switch (response.status_code) {
200, 302 => {},
else => {
if (args.verbose) {
Output.prettyErrorln("{}", .{response});
}
},
}
if (!args.quiet) {
Output.flush();
Output.disableBuffering();
try Output.writer().writeAll(response_body_string.list.items);
Output.enableBuffering();
}
return;
}
}
}

View File

@@ -1,172 +0,0 @@
// Thank you @evanw for this code!!!
const fs = require("fs");
const path = require("path");
// ES5 reference: https://es5.github.io/
//
// A conforming implementation of this International standard shall interpret
// characters in conformance with the Unicode Standard, Version 3.0 or later
// and ISO/IEC 10646-1 with either UCS-2 or UTF-16 as the adopted encoding
// form, implementation level 3. If the adopted ISO/IEC 10646-1 subset is not
// otherwise specified, it is presumed to be the BMP subset, collection 300.
//
// UnicodeLetter: any character in the Unicode categories “Uppercase letter (Lu)”,
// “Lowercase letter (Ll)”, “Titlecase letter (Lt)”, “Modifier letter (Lm)”,
// “Other letter (Lo)”, or “Letter number (Nl)”.
const idStartES5 = []
.concat(
require("@unicode/unicode-3.0.0/General_Category/Uppercase_Letter/code-points"),
require("@unicode/unicode-3.0.0/General_Category/Lowercase_Letter/code-points"),
require("@unicode/unicode-3.0.0/General_Category/Titlecase_Letter/code-points"),
require("@unicode/unicode-3.0.0/General_Category/Modifier_Letter/code-points"),
require("@unicode/unicode-3.0.0/General_Category/Other_Letter/code-points"),
// The "letter number" category is not included because old versions of Safari
// had a bug where they didn't include it. This means it does not match ES5.
// We need to make sure we escape these characters so Safari can read them.
// See https://github.com/evanw/esbuild/issues/1349 for more information.
// require('@unicode/unicode-3.0.0/General_Category/Letter_Number/code-points'),
)
.sort((a, b) => a - b);
// UnicodeCombiningMark: any character in the Unicode categories “Non-spacing mark (Mn)”
// or “Combining spacing mark (Mc)”
// UnicodeDigit: any character in the Unicode category “Decimal number (Nd)”
// UnicodeConnectorPunctuation: any character in the Unicode category “Connector punctuation (Pc)”
const idContinueES5 = idStartES5
.concat(
require("@unicode/unicode-3.0.0/General_Category/Nonspacing_Mark/code-points"),
require("@unicode/unicode-3.0.0/General_Category/Spacing_Mark/code-points"),
require("@unicode/unicode-3.0.0/General_Category/Decimal_Number/code-points"),
require("@unicode/unicode-3.0.0/General_Category/Connector_Punctuation/code-points"),
)
.sort((a, b) => a - b);
// ESNext reference: https://tc39.es/ecma262/
//
// A conforming implementation of ECMAScript must interpret source text input
// in conformance with the Unicode Standard, Version 5.1.0 or later and ISO/IEC
// 10646. If the adopted ISO/IEC 10646-1 subset is not otherwise specified, it
// is presumed to be the Unicode set, collection 10646.
//
// UnicodeIDStart: any Unicode code point with the Unicode property “ID_Start”
const idStartESNext = require("@unicode/unicode-13.0.0/Binary_Property/ID_Start/code-points");
const idStartESNextSet = new Set(idStartESNext);
// UnicodeIDContinue: any Unicode code point with the Unicode property “ID_Continue”
const idContinueESNext = require("@unicode/unicode-13.0.0/Binary_Property/ID_Continue/code-points");
const idContinueESNextSet = new Set(idContinueESNext);
// These identifiers are valid in both ES5 and ES6+ (i.e. an intersection of both)
const idStartES5AndESNext = idStartES5.filter(n => idStartESNextSet.has(n));
const idContinueES5AndESNext = idContinueES5.filter(n => idContinueESNextSet.has(n));
// These identifiers are valid in either ES5 or ES6+ (i.e. a union of both)
const idStartES5OrESNext = [...new Set(idStartES5.concat(idStartESNext))].sort((a, b) => a - b);
const idContinueES5OrESNext = [...new Set(idContinueES5.concat(idContinueESNext))].sort((a, b) => a - b);
function generateRangeTable(codePoints) {
let lines = [];
let index = 0;
let latinOffset = 0;
while (latinOffset < codePoints.length && codePoints[latinOffset] <= 0xff) {
latinOffset++;
}
lines.push(`RangeTable.init(`, ` ${latinOffset},`, ` &[_]R16Range{`);
// 16-bit code points
while (index < codePoints.length && codePoints[index] < 0x1000) {
let start = codePoints[index];
index++;
while (index < codePoints.length && codePoints[index] < 0x1000 && codePoints[index] === codePoints[index - 1] + 1) {
index++;
}
let end = codePoints[index - 1];
lines.push(` .{0x${start.toString(16)}, 0x${end.toString(16)}},`);
}
lines.push(` },`, `&[_]R32Range{`);
// 32-bit code points
while (index < codePoints.length) {
let start = codePoints[index];
index++;
while (index < codePoints.length && codePoints[index] === codePoints[index - 1] + 1) {
index++;
}
let end = codePoints[index - 1];
lines.push(` .{0x${start.toString(16)}, 0x${end.toString(16)}},`);
}
lines.push(` },`, `);`);
return lines.join("\n");
}
function generateBigSwitchStatement(codePoints) {
let lines = [];
let index = 0;
let latinOffset = 0;
while (latinOffset < codePoints.length && codePoints[latinOffset] <= 0xff) {
latinOffset++;
}
lines.push(`return switch(codepoint) {`);
// 16-bit code points
while (index < codePoints.length && codePoints[index] < 0x1000) {
let start = codePoints[index];
index++;
while (index < codePoints.length && codePoints[index] < 0x1000 && codePoints[index] === codePoints[index - 1] + 1) {
index++;
}
let end = codePoints[index - 1];
lines.push(`0x${start.toString(16)}...0x${end.toString(16)},`);
}
// 32-bit code points
while (index < codePoints.length) {
let start = codePoints[index];
index++;
while (index < codePoints.length && codePoints[index] === codePoints[index - 1] + 1) {
index++;
}
let end = codePoints[index - 1];
lines.push(` 0x${start.toString(16)}...0x${end.toString(16)},`);
}
lines.push(` => true,
else => false
};`);
return lines.join("\n");
}
fs.writeFileSync(
path.join(__dirname, "..", "src", "js_lexer", "unicode.zig"),
`// This file was automatically generated by ${path.basename(__filename)}. Do not edit.
const RangeTable = @import("./range_table.zig");
// ES5 || ESNext
pub const id_start = ${generateRangeTable(idStartES5OrESNext)}
// ES5 || ESNext
pub const id_continue = ${generateRangeTable(idContinueES5OrESNext)}
pub const printable_id_start = ${generateRangeTable(idStartESNext)}
pub const printable_id_continue = ${generateRangeTable(idContinueESNext)}
pub fn isIdentifierStart(comptime Codepoint: type, codepoint: Codepoint) bool{
${generateBigSwitchStatement(idStartES5OrESNext)}
}
pub fn isIdentifierContinue(comptime Codepoint: type, codepoint: Codepoint) bool{
${generateBigSwitchStatement(idContinueES5OrESNext)}
}
`,
);

View File

@@ -0,0 +1,108 @@
import { Generator, Context } from "./unicode-generator";
// Create sets for fast lookups
const idStartES5Set = new Set([
...require("@unicode/unicode-3.0.0/General_Category/Uppercase_Letter/code-points"),
...require("@unicode/unicode-3.0.0/General_Category/Lowercase_Letter/code-points"),
...require("@unicode/unicode-3.0.0/General_Category/Titlecase_Letter/code-points"),
...require("@unicode/unicode-3.0.0/General_Category/Modifier_Letter/code-points"),
...require("@unicode/unicode-3.0.0/General_Category/Other_Letter/code-points"),
]);
const idContinueES5Set = new Set([
...idStartES5Set,
...require("@unicode/unicode-3.0.0/General_Category/Nonspacing_Mark/code-points"),
...require("@unicode/unicode-3.0.0/General_Category/Spacing_Mark/code-points"),
...require("@unicode/unicode-3.0.0/General_Category/Decimal_Number/code-points"),
...require("@unicode/unicode-3.0.0/General_Category/Connector_Punctuation/code-points"),
]);
const idStartESNextSet = new Set(require("@unicode/unicode-15.1.0/Binary_Property/ID_Start/code-points"));
const idContinueESNextSet = new Set(require("@unicode/unicode-15.1.0/Binary_Property/ID_Continue/code-points"));
// Exclude known problematic codepoints
const ID_Continue_mistake = new Set([0x30fb, 0xff65]);
function bitsToU64Array(bits: number[]): bigint[] {
const result: bigint[] = [];
for (let i = 0; i < bits.length; i += 64) {
let value = 0n;
for (let j = 0; j < 64 && i + j < bits.length; j++) {
if (bits[i + j]) {
value |= 1n << BigInt(j);
}
}
result.push(value);
}
return result;
}
async function generateTable(table: string, name: string, checkFn: (cp: number) => boolean) {
const context: Context<boolean> = {
get: (cp: number) => checkFn(cp),
eql: (a: boolean, b: boolean) => a === b,
};
const generator = new Generator(context);
const tables = await generator.generate();
return `
pub fn ${name}(cp: u21) bool {
if (cp > 0x10FFFF) return false;
const high = cp >> 8;
const low = cp & 0xFF;
const stage2_idx = ${table}.stage1[high];
const bit_pos = stage2_idx + low;
const u64_idx = bit_pos >> 6;
const bit_idx = @as(u6, @intCast(bit_pos & 63));
return (${table}.stage2[u64_idx] & (@as(u64, 1) << bit_idx)) != 0;
}
const ${table} = struct {
pub const stage1 = [_]u16{${tables.stage1.join(",")}};
pub const stage2 = [_]u64{${bitsToU64Array(tables.stage2)
.map(n => n.toString())
.join(",")}};
};
`;
}
async function main() {
const functions = [
{
name: "isIDStartES5",
table: "idStartES5",
check: (cp: number) => idStartES5Set.has(cp),
},
{
name: "isIDContinueES5",
table: "idContinueES5",
check: (cp: number) => idContinueES5Set.has(cp),
},
{
name: "isIDStartESNext",
table: "idStartESNext",
check: (cp: number) => idStartESNextSet.has(cp),
},
{
name: "isIDContinueESNext",
table: "idContinueESNext",
check: (cp: number) => idContinueESNextSet.has(cp) && !ID_Continue_mistake.has(cp),
},
];
const results = await Promise.all(
functions.map(async ({ name, check, table }) => {
const code = await generateTable(table, name, check);
return `
/// ${name} checks if a codepoint is valid in the ${name} category
${code}`;
}),
);
console.log(`/// This file is auto-generated. Do not edit.
${results.join("\n\n")}`);
}
main();

View File

@@ -0,0 +1,231 @@
import * as fs from "fs";
import path from "path";
import { execSync } from "child_process";
interface LetterGroup {
offset: number;
length: number;
packages: string[];
}
// Read and parse input file
const content = fs.readFileSync(path.join(__dirname, "..", "src", "cli", "add_completions.txt"), "utf8");
const packages = content
.split("\n")
.map(line => line.trim())
.filter(line => line.length > 0)
.sort();
// Group packages by first letter
const letterGroups = new Map<string, LetterGroup>();
let currentOffset = 0;
let maxListSize = 0;
for (const pkg of packages) {
if (pkg.length === 0) continue;
const firstLetter = pkg[0].toLowerCase();
if (!letterGroups.has(firstLetter)) {
letterGroups.set(firstLetter, {
offset: currentOffset,
length: 0,
packages: [],
});
}
const group = letterGroups.get(firstLetter)!;
group.packages.push(pkg);
group.length++;
maxListSize = Math.max(maxListSize, group.length);
}
// Helper to ensure temp dir exists
const tmpDir = path.join(__dirname, "tmp");
if (!fs.existsSync(tmpDir)) {
fs.mkdirSync(tmpDir);
}
// Create a single buffer with all package data
const dataChunks: Buffer[] = [];
let totalUncompressed = 0;
// Store total package count first
const totalCountBuf = Buffer.alloc(4);
totalCountBuf.writeUInt32LE(packages.length, 0);
dataChunks.push(totalCountBuf);
totalUncompressed += 4;
// Then all packages with length prefixes
for (const pkg of packages) {
const lenBuf = Buffer.alloc(2);
lenBuf.writeUInt16LE(pkg.length, 0);
dataChunks.push(lenBuf);
dataChunks.push(Buffer.from(pkg, "utf8"));
totalUncompressed += 2 + pkg.length;
}
const uncompressedData = Buffer.concat(dataChunks);
// Write to temp file and compress with zstd
const uncompressedPath = path.join(tmpDir, "packages.bin");
const compressedPath = path.join(tmpDir, "packages.bin.zst");
fs.writeFileSync(uncompressedPath, uncompressedData);
execSync(`zstd -1 --rm -f "${uncompressedPath}" -o "${compressedPath}"`);
// Read back compressed data
const compressedData = fs.readFileSync(compressedPath);
fs.unlinkSync(compressedPath);
// Calculate compression ratio
const totalCompressed = compressedData.length;
const ratio = ((totalCompressed / totalUncompressed) * 100).toFixed(1);
console.log("\nCompression statistics:");
console.log(`Uncompressed size: ${totalUncompressed} bytes`);
console.log(`Compressed size: ${totalCompressed} bytes`);
console.log(`Compression ratio: ${ratio}%`);
// Generate Zig code
const chunks: string[] = [];
// Header with comments and imports
chunks.push(`// Auto-generated file. Do not edit.
// To regenerate this file, run:
//
// bun misctools/generate-add-completions.ts
//
// If you update add_completions.txt, then you should run this script again.
//
// This used to be a comptime block, but it made the build too slow.
// Compressing the completions list saves about 100 KB of binary size.
const std = @import("std");
const bun = @import("root").bun;
const zstd = bun.zstd;
const Environment = bun.Environment;
pub const FirstLetter = enum(u8) {
a = 'a',
b = 'b',
c = 'c',
d = 'd',
e = 'e',
f = 'f',
g = 'g',
h = 'h',
i = 'i',
j = 'j',
k = 'k',
l = 'l',
m = 'm',
n = 'n',
o = 'o',
p = 'p',
q = 'q',
r = 'r',
s = 's',
t = 't',
u = 'u',
v = 'v',
w = 'w',
x = 'x',
y = 'y',
z = 'z',
};`);
// Add the compressed data
chunks.push(`const compressed_data = [_]u8{${[...compressedData].join(",")}};`);
// Add uncompressed size constant
chunks.push(`const uncompressed_size: usize = ${totalUncompressed};`);
// Generate index entries
const indexEntries: string[] = [];
let offset = 0;
for (const letter of "abcdefghijklmnopqrstuvwxyz") {
const group = letterGroups.get(letter);
if (group) {
indexEntries.push(` .${letter} = .{ .offset = ${offset}, .length = ${group.length} }`);
offset += group.length;
} else {
indexEntries.push(` .${letter} = .{ .offset = ${offset}, .length = 0 }`);
}
}
// Generate index type and instance
chunks.push(`pub const IndexEntry = struct {
offset: usize,
length: usize,
};
pub const Index = std.EnumArray(FirstLetter, IndexEntry);
pub const index = Index.init(.{
${indexEntries.join(",\n")}
});`);
// Generate the decompression and access function
chunks.push(`var decompressed_data: ?[]u8 = null;
var packages_list: ?[][]const u8 = null;
pub fn init(allocator: std.mem.Allocator) !void {
// Decompress data
var data = try allocator.alloc(u8, uncompressed_size);
errdefer allocator.free(data);
const result = zstd.decompress(data, &compressed_data);
decompressed_data = data[0..result.success];
// Parse package list
const total_count = std.mem.readInt(u32, data[0..4], .little);
var packages = try allocator.alloc([]const u8, total_count);
errdefer allocator.free(packages);
var pos: usize = 4;
var i: usize = 0;
while (i < total_count) : (i += 1) {
const len = std.mem.readInt(u16, data[pos..][0..2], .little);
pos += 2;
packages[i] = data[pos..pos + len];
pos += len;
}
packages_list = packages;
}
pub fn deinit(allocator: std.mem.Allocator) void {
if (packages_list) |pkgs| {
allocator.free(pkgs);
packages_list = null;
}
if (decompressed_data) |data| {
allocator.free(data);
decompressed_data = null;
}
}
pub fn getPackages(letter: FirstLetter) []const []const u8 {
const entry = index.get(letter);
if (entry.length == 0) return &[_][]const u8{};
return packages_list.?[entry.offset..entry.offset + entry.length];
}`);
// Add biggest_list constant
chunks.push(`pub const biggest_list: usize = ${maxListSize};`);
// Write the output
let zigCode = chunks.join("\n\n");
zigCode = execSync("zig fmt --stdin", {
input: zigCode,
encoding: "utf8",
}).toString();
fs.writeFileSync(path.join(__dirname, "..", "src", "cli", "add_completions.zig"), zigCode);
// Clean up temp dir
try {
fs.rmdirSync(tmpDir);
} catch {}
console.log(`\nGenerated Zig completions for ${packages.length} packages`);

View File

@@ -0,0 +1,7 @@
process handle -p true -s false -n false SIGUSR1
command script import misctools/lldb/lldb_pretty_printers.py
type category enable zig.lang
type category enable zig.std
command script import misctools/lldb/lldb_webkit.py

View File

@@ -0,0 +1,733 @@
# https://github.com/ziglang/zig/blob/master/tools/lldb_pretty_printers.py
# pretty printing for the zig language, zig standard library, and zig stage 2 compiler.
# put commands in ~/.lldbinit to run them automatically when starting lldb
# `command script import /path/to/zig/tools/lldb_pretty_printers.py` to import this file
# `type category enable zig.lang` to enable pretty printing for the zig language
# `type category enable zig.std` to enable pretty printing for the zig standard library
# `type category enable zig.stage2` to enable pretty printing for the zig stage 2 compiler
import lldb
import re
page_size = 1 << 12
def log2_int(i): return i.bit_length() - 1
# Define Zig Language
zig_keywords = {
'addrspace',
'align',
'allowzero',
'and',
'anyframe',
'anytype',
'asm',
'async',
'await',
'break',
'callconv',
'catch',
'comptime',
'const',
'continue',
'defer',
'else',
'enum',
'errdefer',
'error',
'export',
'extern',
'fn',
'for',
'if',
'inline',
'noalias',
'noinline',
'nosuspend',
'opaque',
'or',
'orelse',
'packed',
'pub',
'resume',
'return',
'linksection',
'struct',
'suspend',
'switch',
'test',
'threadlocal',
'try',
'union',
'unreachable',
'usingnamespace',
'var',
'volatile',
'while',
}
zig_primitives = {
'anyerror',
'anyframe',
'anyopaque',
'bool',
'c_int',
'c_long',
'c_longdouble',
'c_longlong',
'c_short',
'c_uint',
'c_ulong',
'c_ulonglong',
'c_ushort',
'comptime_float',
'comptime_int',
'f128',
'f16',
'f32',
'f64',
'f80',
'false',
'isize',
'noreturn',
'null',
'true',
'type',
'undefined',
'usize',
'void',
}
zig_integer_type = re.compile('[iu][1-9][0-9]+')
zig_identifier_regex = re.compile('[A-Z_a-z][0-9A-Z_a-z]*')
def zig_IsVariableName(string): return string != '_' and string not in zig_keywords and string not in zig_primitives and not zig_integer_type.fullmatch(string) and zig_identifier_regex.fullmatch(string)
def zig_IsFieldName(string): return string not in zig_keywords and zig_identifier_regex.fullmatch(string)
class zig_Slice_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.ptr = self.value.GetChildMemberWithName('ptr')
self.len = self.value.GetChildMemberWithName('len').unsigned if self.ptr.unsigned > page_size else 0
self.elem_type = self.ptr.type.GetPointeeType()
self.elem_size = self.elem_type.size
except: pass
def has_children(self): return True
def num_children(self): return self.len or 0
def get_child_index(self, name):
try: return int(name.removeprefix('[').removesuffix(']'))
except: return -1
def get_child_at_index(self, index):
if index not in range(self.len): return None
try: return self.ptr.CreateChildAtOffset('[%d]' % index, index * self.elem_size, self.elem_type)
except: return None
def zig_String_decode(value, offset=0, length=None):
try:
value = value.GetNonSyntheticValue()
data = value.GetChildMemberWithName('ptr').GetPointeeData(offset, length if length is not None else value.GetChildMemberWithName('len').unsigned)
b = bytes(data.uint8)
b = b.replace(b'\\', b'\\\\')
b = b.replace(b'\n', b'\\n')
b = b.replace(b'\r', b'\\r')
b = b.replace(b'\t', b'\\t')
b = b.replace(b'"', b'\\"')
b = b.replace(b'\'', b'\\\'')
s = b.decode(encoding='ascii', errors='backslashreplace')
return s if s.isprintable() else ''.join((c if c.isprintable() else '\\x%02x' % ord(c) for c in s))
except: return None
def zig_String_SummaryProvider(value, _=None): return '"%s"' % zig_String_decode(value)
def zig_String_AsIdentifier(value, pred):
string = zig_String_decode(value)
return string if pred(string) else '@"%s"' % string
class zig_Optional_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.child = self.value.GetChildMemberWithName('some').unsigned == 1 and self.value.GetChildMemberWithName('data').Clone('child')
except: pass
def has_children(self): return bool(self.child)
def num_children(self): return int(self.child)
def get_child_index(self, name): return 0 if self.child and (name == 'child' or name == '?') else -1
def get_child_at_index(self, index): return self.child if self.child and index == 0 else None
def zig_Optional_SummaryProvider(value, _=None):
child = value.GetChildMemberWithName('child')
return child or 'null'
class zig_ErrorUnion_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.error_set = self.value.GetChildMemberWithName('tag').Clone('error_set')
self.payload = self.value.GetChildMemberWithName('value').Clone('payload') if self.error_set.unsigned == 0 else None
except: pass
def has_children(self): return True
def num_children(self): return 1
def get_child_index(self, name): return 0 if name == ('payload' if self.payload else 'error_set') else -1
def get_child_at_index(self, index): return self.payload or self.error_set if index == 0 else None
class zig_TaggedUnion_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.tag = self.value.GetChildMemberWithName('tag')
self.payload = self.value.GetChildMemberWithName('payload').GetChildMemberWithName(self.tag.value)
except: pass
def has_children(self): return True
def num_children(self): return 1 + (self.payload is not None)
def get_child_index(self, name):
try: return ('tag', 'payload').index(name)
except: return -1
def get_child_at_index(self, index): return (self.tag, self.payload)[index] if index in range(2) else None
# Define Zig Standard Library
class std_SegmentedList_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.prealloc_segment = self.value.GetChildMemberWithName('prealloc_segment')
self.dynamic_segments = zig_Slice_SynthProvider(self.value.GetChildMemberWithName('dynamic_segments'))
self.dynamic_segments.update()
self.len = self.value.GetChildMemberWithName('len').unsigned
except: pass
def has_children(self): return True
def num_children(self): return self.len
def get_child_index(self, name):
try: return int(name.removeprefix('[').removesuffix(']'))
except: return -1
def get_child_at_index(self, index):
try:
if index not in range(self.len): return None
prealloc_item_count = len(self.prealloc_segment)
if index < prealloc_item_count: return self.prealloc_segment.child[index]
prealloc_exp = prealloc_item_count.bit_length() - 1
shelf_index = log2_int(index + 1) if prealloc_item_count == 0 else log2_int(index + prealloc_item_count) - prealloc_exp - 1
shelf = self.dynamic_segments.get_child_at_index(shelf_index)
box_index = (index + 1) - (1 << shelf_index) if prealloc_item_count == 0 else index + prealloc_item_count - (1 << ((prealloc_exp + 1) + shelf_index))
elem_type = shelf.type.GetPointeeType()
return shelf.CreateChildAtOffset('[%d]' % index, box_index * elem_type.size, elem_type)
except: return None
class std_MultiArrayList_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.len = 0
value_type = self.value.type
for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
ptr_self_type, ptr_child_type, ptr_field_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes()
if ptr_self_type.GetPointeeType() == value_type: break
else: return
self.entry_type = ptr_entry_type.GetPointeeType()
self.bytes = self.value.GetChildMemberWithName('bytes')
self.len = self.value.GetChildMemberWithName('len').unsigned
self.capacity = self.value.GetChildMemberWithName('capacity').unsigned
except: pass
def has_children(self): return True
def num_children(self): return self.len
def get_child_index(self, name):
try: return int(name.removeprefix('[').removesuffix(']'))
except: return -1
def get_child_at_index(self, index):
try:
if index not in range(self.len): return None
offset = 0
data = lldb.SBData()
for field in self.entry_type.fields:
field_type = field.type.GetPointeeType()
field_size = field_type.size
data.Append(self.bytes.CreateChildAtOffset(field.name, offset + index * field_size, field_type).address_of.data)
offset += self.capacity * field_size
return self.bytes.CreateValueFromData('[%d]' % index, data, self.entry_type)
except: return None
class std_MultiArrayList_Slice_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.len = 0
value_type = self.value.type
for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
ptr_self_type, ptr_child_type, ptr_field_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes()
if ptr_self_type.GetPointeeType() == value_type: break
else: return
self.fields = {member.name: index for index, member in enumerate(ptr_field_type.GetPointeeType().enum_members)}
self.entry_type = ptr_entry_type.GetPointeeType()
self.ptrs = self.value.GetChildMemberWithName('ptrs')
self.len = self.value.GetChildMemberWithName('len').unsigned
self.capacity = self.value.GetChildMemberWithName('capacity').unsigned
except: pass
def has_children(self): return True
def num_children(self): return self.len
def get_child_index(self, name):
try: return int(name.removeprefix('[').removesuffix(']'))
except: return -1
def get_child_at_index(self, index):
try:
if index not in range(self.len): return None
data = lldb.SBData()
for field in self.entry_type.fields:
field_type = field.type.GetPointeeType()
data.Append(self.ptrs.child[self.fields[field.name.removesuffix('_ptr')]].CreateChildAtOffset(field.name, index * field_type.size, field_type).address_of.data)
return self.ptrs.CreateValueFromData('[%d]' % index, data, self.entry_type)
except: return None
class std_HashMapUnmanaged_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.capacity = 0
self.indices = tuple()
self.metadata = self.value.GetChildMemberWithName('metadata')
if not self.metadata.unsigned: return
value_type = self.value.type
for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
ptr_self_type, ptr_hdr_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes()
if ptr_self_type.GetPointeeType() == value_type: break
else: return
self.entry_type = ptr_entry_type.GetPointeeType()
hdr_type = ptr_hdr_type.GetPointeeType()
hdr = self.metadata.CreateValueFromAddress('header', self.metadata.deref.load_addr - hdr_type.size, hdr_type)
self.values = hdr.GetChildMemberWithName('values')
self.keys = hdr.GetChildMemberWithName('keys')
self.capacity = hdr.GetChildMemberWithName('capacity').unsigned
self.indices = tuple(i for i, value in enumerate(self.metadata.GetPointeeData(0, self.capacity).sint8) if value < 0)
except: pass
def has_children(self): return True
def num_children(self): return len(self.indices)
def get_capacity(self): return self.capacity
def get_child_index(self, name):
try: return int(name.removeprefix('[').removesuffix(']'))
except: return -1
def get_child_at_index(self, index):
try:
fields = {name: base.CreateChildAtOffset(name, self.indices[index] * pointee_type.size, pointee_type).address_of.data for name, base, pointee_type in ((name, base, base.type.GetPointeeType()) for name, base in (('key_ptr', self.keys), ('value_ptr', self.values)))}
data = lldb.SBData()
for field in self.entry_type.fields: data.Append(fields[field.name])
return self.metadata.CreateValueFromData('[%d]' % index, data, self.entry_type)
except: return None
def std_HashMapUnmanaged_SummaryProvider(value, _=None):
synth = std_HashMapUnmanaged_SynthProvider(value.GetNonSyntheticValue(), _)
synth.update()
return 'len=%d capacity=%d' % (synth.num_children(), synth.get_capacity())
# formats a struct of fields of the form `name_ptr: *Type` by auto dereferencing its fields
class std_Entry_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.children = tuple(child.Clone(child.name.removesuffix('_ptr')) for child in self.value.children if child.type.GetPointeeType().size != 0)
self.indices = {child.name: i for i, child in enumerate(self.children)}
except: pass
def has_children(self): return self.num_children() != 0
def num_children(self): return len(self.children)
def get_child_index(self, name): return self.indices.get(name)
def get_child_at_index(self, index): return self.children[index].deref if index in range(len(self.children)) else None
# Define Zig Stage2 Compiler
class TagAndPayload_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.tag = self.value.GetChildMemberWithName('tag') or self.value.GetChildMemberWithName('tag_ptr').deref.Clone('tag')
data = self.value.GetChildMemberWithName('data_ptr') or self.value.GetChildMemberWithName('data')
self.payload = data.GetChildMemberWithName('payload').GetChildMemberWithName(data.GetChildMemberWithName('tag').value)
except: pass
def has_children(self): return True
def num_children(self): return 2
def get_child_index(self, name):
try: return ('tag', 'payload').index(name)
except: return -1
def get_child_at_index(self, index): return (self.tag, self.payload)[index] if index in range(2) else None
def InstRef_SummaryProvider(value, _=None):
return value if any(value.unsigned == member.unsigned for member in value.type.enum_members) else (
'InternPool.Index(%d)' % value.unsigned if value.unsigned < 0x80000000 else 'instructions[%d]' % (value.unsigned - 0x80000000))
def InstIndex_SummaryProvider(value, _=None):
return 'instructions[%d]' % value.unsigned
class zig_DeclIndex_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
ip = InternPool_Find(self.value.thread)
if not ip: return
self.ptr = ip.GetChildMemberWithName('allocated_decls').GetChildAtIndex(self.value.unsigned).address_of.Clone('decl')
except: pass
def has_children(self): return True
def num_children(self): return 1
def get_child_index(self, name): return 0 if name == 'decl' else -1
def get_child_at_index(self, index): return self.ptr if index == 0 else None
class Module_Namespace__Module_Namespace_Index_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
ip = InternPool_Find(self.value.thread)
if not ip: return
self.ptr = ip.GetChildMemberWithName('allocated_namespaces').GetChildAtIndex(self.value.unsigned).address_of.Clone('namespace')
except: pass
def has_children(self): return True
def num_children(self): return 1
def get_child_index(self, name): return 0 if name == 'namespace' else -1
def get_child_at_index(self, index): return self.ptr if index == 0 else None
class TagOrPayloadPtr_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
value_type = self.value.type
for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
ptr_self_type, ptr_tag_to_payload_map_type = helper.function.type.GetFunctionArgumentTypes()
self_type = ptr_self_type.GetPointeeType()
if self_type == value_type: break
else: return
tag_to_payload_map = {field.name: field.type for field in ptr_tag_to_payload_map_type.GetPointeeType().fields}
tag = self.value.GetChildMemberWithName('tag_if_small_enough')
if tag.unsigned < page_size:
self.tag = tag.Clone('tag')
self.payload = None
else:
ptr_otherwise = self.value.GetChildMemberWithName('ptr_otherwise')
self.tag = ptr_otherwise.GetChildMemberWithName('tag')
self.payload = ptr_otherwise.Cast(tag_to_payload_map[self.tag.value]).GetChildMemberWithName('data').Clone('payload')
except: pass
def has_children(self): return True
def num_children(self): return 1 + (self.payload is not None)
def get_child_index(self, name):
try: return ('tag', 'payload').index(name)
except: return -1
def get_child_at_index(self, index): return (self.tag, self.payload)[index] if index in range(2) else None
def Module_Decl_name(decl):
error = lldb.SBError()
return decl.process.ReadCStringFromMemory(decl.GetChildMemberWithName('name').deref.load_addr, 256, error)
def Module_Namespace_RenderFullyQualifiedName(namespace):
parent = namespace.GetChildMemberWithName('parent')
if parent.unsigned < page_size: return zig_String_decode(namespace.GetChildMemberWithName('file_scope').GetChildMemberWithName('sub_file_path')).removesuffix('.zig').replace('/', '.')
return '.'.join((Module_Namespace_RenderFullyQualifiedName(parent), Module_Decl_name(namespace.GetChildMemberWithName('ty').GetChildMemberWithName('payload').GetChildMemberWithName('owner_decl').GetChildMemberWithName('decl'))))
def Module_Decl_RenderFullyQualifiedName(decl): return '.'.join((Module_Namespace_RenderFullyQualifiedName(decl.GetChildMemberWithName('src_namespace')), Module_Decl_name(decl)))
def OwnerDecl_RenderFullyQualifiedName(payload): return Module_Decl_RenderFullyQualifiedName(payload.GetChildMemberWithName('owner_decl').GetChildMemberWithName('decl'))
def InternPool_Find(thread):
for frame in thread:
ip = frame.FindVariable('ip') or frame.FindVariable('intern_pool')
if ip: return ip
mod = frame.FindVariable('zcu') or frame.FindVariable('mod') or frame.FindVariable('module')
if mod:
ip = mod.GetChildMemberWithName('intern_pool')
if ip: return ip
class InternPool_Index_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
index_type = self.value.type
for helper in self.value.target.FindFunctions('%s.dbHelper' % index_type.name, lldb.eFunctionNameTypeFull):
ptr_self_type, ptr_tag_to_encoding_map_type = helper.function.type.GetFunctionArgumentTypes()
if ptr_self_type.GetPointeeType() == index_type: break
else: return
tag_to_encoding_map = {field.name: field.type for field in ptr_tag_to_encoding_map_type.GetPointeeType().fields}
ip = InternPool_Find(self.value.thread)
if not ip: return
self.item = ip.GetChildMemberWithName('items').GetChildAtIndex(self.value.unsigned)
extra = ip.GetChildMemberWithName('extra').GetChildMemberWithName('items')
self.tag = self.item.GetChildMemberWithName('tag').Clone('tag')
self.data = None
self.trailing = None
data = self.item.GetChildMemberWithName('data')
encoding_type = tag_to_encoding_map[self.tag.value]
dynamic_values = {}
for encoding_field in encoding_type.fields:
if encoding_field.name == 'data':
if encoding_field.type.IsPointerType():
extra_index = data.unsigned
self.data = extra.GetChildAtIndex(extra_index).address_of.Cast(encoding_field.type).deref.Clone('data')
extra_index += encoding_field.type.GetPointeeType().num_fields
else:
self.data = data.Cast(encoding_field.type).Clone('data')
elif encoding_field.name == 'trailing':
trailing_data = lldb.SBData()
for trailing_field in encoding_field.type.fields:
trailing_data.Append(extra.GetChildAtIndex(extra_index).address_of.data)
trailing_len = dynamic_values['trailing.%s.len' % trailing_field.name].unsigned
trailing_data.Append(lldb.SBData.CreateDataFromInt(trailing_len, trailing_data.GetAddressByteSize()))
extra_index += trailing_len
self.trailing = self.data.CreateValueFromData('trailing', trailing_data, encoding_field.type)
else:
for path in encoding_field.type.GetPointeeType().name.removeprefix('%s::' % encoding_type.name).removeprefix('%s.' % encoding_type.name).partition('__')[0].split(' orelse '):
if path.startswith('data.'):
root = self.data
path = path[len('data'):]
else: return
dynamic_value = root.GetValueForExpressionPath(path)
if dynamic_value:
dynamic_values[encoding_field.name] = dynamic_value
break
except: pass
def has_children(self): return True
def num_children(self): return 2 + (self.trailing is not None)
def get_child_index(self, name):
try: return ('tag', 'data', 'trailing').index(name)
except: return -1
def get_child_at_index(self, index): return (self.tag, self.data, self.trailing)[index] if index in range(3) else None
def InternPool_NullTerminatedString_SummaryProvider(value, _=None):
try:
ip = InternPool_Find(value.thread)
if not ip: return
items = ip.GetChildMemberWithName('string_bytes').GetChildMemberWithName('items')
b = bytearray()
i = 0
while True:
x = items.GetChildAtIndex(value.unsigned + i).GetValueAsUnsigned()
if x == 0: break
b.append(x)
i += 1
s = b.decode(encoding='utf8', errors='backslashreplace')
s1 = s if s.isprintable() else ''.join((c if c.isprintable() else '\\x%02x' % ord(c) for c in s))
return '"%s"' % s1
except:
pass
def type_Type_pointer(payload):
pointee_type = payload.GetChildMemberWithName('pointee_type')
sentinel = payload.GetChildMemberWithName('sentinel').GetChildMemberWithName('child')
align = payload.GetChildMemberWithName('align').unsigned
addrspace = payload.GetChildMemberWithName('addrspace').value
bit_offset = payload.GetChildMemberWithName('bit_offset').unsigned
host_size = payload.GetChildMemberWithName('host_size').unsigned
vector_index = payload.GetChildMemberWithName('vector_index')
allowzero = payload.GetChildMemberWithName('allowzero').unsigned
const = not payload.GetChildMemberWithName('mutable').unsigned
volatile = payload.GetChildMemberWithName('volatile').unsigned
size = payload.GetChildMemberWithName('size').value
if size == 'One': summary = '*'
elif size == 'Many': summary = '[*'
elif size == 'Slice': summary = '['
elif size == 'C': summary = '[*c'
if sentinel: summary += ':%s' % value_Value_SummaryProvider(sentinel)
if size != 'One': summary += ']'
if allowzero: summary += 'allowzero '
if align != 0 or host_size != 0 or vector_index.value != 'none': summary += 'align(%d%s%s) ' % (align, ':%d:%d' % (bit_offset, host_size) if bit_offset != 0 or host_size != 0 else '', ':?' if vector_index.value == 'runtime' else ':%d' % vector_index.unsigned if vector_index.value != 'none' else '')
if addrspace != 'generic': summary += 'addrspace(.%s) ' % addrspace
if const: summary += 'const '
if volatile: summary += 'volatile '
summary += type_Type_SummaryProvider(pointee_type)
return summary
def type_Type_function(payload):
param_types = payload.GetChildMemberWithName('param_types').children
comptime_params = payload.GetChildMemberWithName('comptime_params').GetPointeeData(0, len(param_types)).uint8
return_type = payload.GetChildMemberWithName('return_type')
alignment = payload.GetChildMemberWithName('alignment').unsigned
noalias_bits = payload.GetChildMemberWithName('noalias_bits').unsigned
cc = payload.GetChildMemberWithName('cc').value
is_var_args = payload.GetChildMemberWithName('is_var_args').unsigned
return 'fn(%s)%s%s %s' % (', '.join(tuple(''.join(('comptime ' if comptime_param else '', 'noalias ' if noalias_bits & 1 << i else '', type_Type_SummaryProvider(param_type))) for i, (comptime_param, param_type) in enumerate(zip(comptime_params, param_types))) + (('...',) if is_var_args else ())), ' align(%d)' % alignment if alignment != 0 else '', ' callconv(.%s)' % cc if cc != 'Unspecified' else '', type_Type_SummaryProvider(return_type))
def type_Type_SummaryProvider(value, _=None):
tag = value.GetChildMemberWithName('tag').value
return type_tag_handlers.get(tag, lambda payload: tag)(value.GetChildMemberWithName('payload'))
type_tag_handlers = {
'atomic_order': lambda payload: 'std.builtin.AtomicOrder',
'atomic_rmw_op': lambda payload: 'std.builtin.AtomicRmwOp',
'calling_convention': lambda payload: 'std.builtin.CallingConvention',
'address_space': lambda payload: 'std.builtin.AddressSpace',
'float_mode': lambda payload: 'std.builtin.FloatMode',
'reduce_op': lambda payload: 'std.builtin.ReduceOp',
'modifier': lambda payload: 'std.builtin.CallModifier',
'prefetch_options': lambda payload: 'std.builtin.PrefetchOptions',
'export_options': lambda payload: 'std.builtin.ExportOptions',
'extern_options': lambda payload: 'std.builtin.ExternOptions',
'type_info': lambda payload: 'std.builtin.Type',
'enum_literal': lambda payload: '@TypeOf(.enum_literal)',
'null': lambda payload: '@TypeOf(null)',
'undefined': lambda payload: '@TypeOf(undefined)',
'empty_struct_literal': lambda payload: '@TypeOf(.{})',
'anyerror_void_error_union': lambda payload: 'anyerror!void',
'slice_const_u8': lambda payload: '[]const u8',
'slice_const_u8_sentinel_0': lambda payload: '[:0]const u8',
'fn_noreturn_no_args': lambda payload: 'fn() noreturn',
'fn_void_no_args': lambda payload: 'fn() void',
'fn_naked_noreturn_no_args': lambda payload: 'fn() callconv(.Naked) noreturn',
'fn_ccc_void_no_args': lambda payload: 'fn() callconv(.C) void',
'single_const_pointer_to_comptime_int': lambda payload: '*const comptime_int',
'manyptr_u8': lambda payload: '[*]u8',
'manyptr_const_u8': lambda payload: '[*]const u8',
'manyptr_const_u8_sentinel_0': lambda payload: '[*:0]const u8',
'function': type_Type_function,
'error_union': lambda payload: '%s!%s' % (type_Type_SummaryProvider(payload.GetChildMemberWithName('error_set')), type_Type_SummaryProvider(payload.GetChildMemberWithName('payload'))),
'array_u8': lambda payload: '[%d]u8' % payload.unsigned,
'array_u8_sentinel_0': lambda payload: '[%d:0]u8' % payload.unsigned,
'vector': lambda payload: '@Vector(%d, %s)' % (payload.GetChildMemberWithName('len').unsigned, type_Type_SummaryProvider(payload.GetChildMemberWithName('elem_type'))),
'array': lambda payload: '[%d]%s' % (payload.GetChildMemberWithName('len').unsigned, type_Type_SummaryProvider(payload.GetChildMemberWithName('elem_type'))),
'array_sentinel': lambda payload: '[%d:%s]%s' % (payload.GetChildMemberWithName('len').unsigned, value_Value_SummaryProvider(payload.GetChildMemberWithName('sentinel')), type_Type_SummaryProvider(payload.GetChildMemberWithName('elem_type'))),
'tuple': lambda payload: 'tuple{%s}' % ', '.join(('comptime %%s = %s' % value_Value_SummaryProvider(value) if value.GetChildMemberWithName('tag').value != 'unreachable_value' else '%s') % type_Type_SummaryProvider(type) for type, value in zip(payload.GetChildMemberWithName('types').children, payload.GetChildMemberWithName('values').children)),
'anon_struct': lambda payload: 'struct{%s}' % ', '.join(('comptime %%s: %%s = %s' % value_Value_SummaryProvider(value) if value.GetChildMemberWithName('tag').value != 'unreachable_value' else '%s: %s') % (zig_String_AsIdentifier(name, zig_IsFieldName), type_Type_SummaryProvider(type)) for name, type, value in zip(payload.GetChildMemberWithName('names').children, payload.GetChildMemberWithName('types').children, payload.GetChildMemberWithName('values').children)),
'pointer': type_Type_pointer,
'single_const_pointer': lambda payload: '*const %s' % type_Type_SummaryProvider(payload),
'single_mut_pointer': lambda payload: '*%s' % type_Type_SummaryProvider(payload),
'many_const_pointer': lambda payload: '[*]const %s' % type_Type_SummaryProvider(payload),
'many_mut_pointer': lambda payload: '[*]%s' % type_Type_SummaryProvider(payload),
'c_const_pointer': lambda payload: '[*c]const %s' % type_Type_SummaryProvider(payload),
'c_mut_pointer': lambda payload: '[*c]%s' % type_Type_SummaryProvider(payload),
'slice_const': lambda payload: '[]const %s' % type_Type_SummaryProvider(payload),
'mut_slice': lambda payload: '[]%s' % type_Type_SummaryProvider(payload),
'int_signed': lambda payload: 'i%d' % payload.unsigned,
'int_unsigned': lambda payload: 'u%d' % payload.unsigned,
'optional': lambda payload: '?%s' % type_Type_SummaryProvider(payload),
'optional_single_mut_pointer': lambda payload: '?*%s' % type_Type_SummaryProvider(payload),
'optional_single_const_pointer': lambda payload: '?*const %s' % type_Type_SummaryProvider(payload),
'anyframe_T': lambda payload: 'anyframe->%s' % type_Type_SummaryProvider(payload),
'error_set': lambda payload: type_tag_handlers['error_set_merged'](payload.GetChildMemberWithName('names')),
'error_set_single': lambda payload: 'error{%s}' % zig_String_AsIdentifier(payload, zig_IsFieldName),
'error_set_merged': lambda payload: 'error{%s}' % ','.join(zig_String_AsIdentifier(child.GetChildMemberWithName('key'), zig_IsFieldName) for child in payload.GetChildMemberWithName('entries').children),
'error_set_inferred': lambda payload: '@typeInfo(@typeInfo(@TypeOf(%s)).@"fn".return_type.?).error_union.error_set' % OwnerDecl_RenderFullyQualifiedName(payload.GetChildMemberWithName('func')),
'enum_full': OwnerDecl_RenderFullyQualifiedName,
'enum_nonexhaustive': OwnerDecl_RenderFullyQualifiedName,
'enum_numbered': OwnerDecl_RenderFullyQualifiedName,
'enum_simple': OwnerDecl_RenderFullyQualifiedName,
'struct': OwnerDecl_RenderFullyQualifiedName,
'union': OwnerDecl_RenderFullyQualifiedName,
'union_safety_tagged': OwnerDecl_RenderFullyQualifiedName,
'union_tagged': OwnerDecl_RenderFullyQualifiedName,
'opaque': OwnerDecl_RenderFullyQualifiedName,
}
def value_Value_str_lit(payload):
for frame in payload.thread:
mod = frame.FindVariable('zcu') or frame.FindVariable('mod') or frame.FindVariable('module')
if mod: break
else: return
return '"%s"' % zig_String_decode(mod.GetChildMemberWithName('string_literal_bytes').GetChildMemberWithName('items'), payload.GetChildMemberWithName('index').unsigned, payload.GetChildMemberWithName('len').unsigned)
def value_Value_SummaryProvider(value, _=None):
tag = value.GetChildMemberWithName('tag').value
return value_tag_handlers.get(tag, lambda payload: tag.removesuffix('_type'))(value.GetChildMemberWithName('payload'))
value_tag_handlers = {
'undef': lambda payload: 'undefined',
'zero': lambda payload: '0',
'one': lambda payload: '1',
'void_value': lambda payload: '{}',
'unreachable_value': lambda payload: 'unreachable',
'null_value': lambda payload: 'null',
'bool_true': lambda payload: 'true',
'bool_false': lambda payload: 'false',
'empty_struct_value': lambda payload: '.{}',
'empty_array': lambda payload: '.{}',
'ty': type_Type_SummaryProvider,
'int_type': lambda payload: '%c%d' % (payload.GetChildMemberWithName('bits').unsigned, 's' if payload.GetChildMemberWithName('signed').unsigned == 1 else 'u'),
'int_u64': lambda payload: '%d' % payload.unsigned,
'int_i64': lambda payload: '%d' % payload.signed,
'int_big_positive': lambda payload: sum(child.unsigned << i * child.type.size * 8 for i, child in enumerate(payload.children)),
'int_big_negative': lambda payload: '-%s' % value_tag_handlers['int_big_positive'](payload),
'function': OwnerDecl_RenderFullyQualifiedName,
'extern_fn': OwnerDecl_RenderFullyQualifiedName,
'variable': lambda payload: value_Value_SummaryProvider(payload.GetChildMemberWithName('decl').GetChildMemberWithName('val')),
'runtime_value': value_Value_SummaryProvider,
'decl_ref': lambda payload: value_Value_SummaryProvider(payload.GetChildMemberWithName('decl').GetChildMemberWithName('val')),
'decl_ref_mut': lambda payload: value_Value_SummaryProvider(payload.GetChildMemberWithName('decl_index').GetChildMemberWithName('decl').GetChildMemberWithName('val')),
'comptime_field_ptr': lambda payload: '&%s' % value_Value_SummaryProvider(payload.GetChildMemberWithName('field_val')),
'elem_ptr': lambda payload: '(%s)[%d]' % (value_Value_SummaryProvider(payload.GetChildMemberWithName('array_ptr')), payload.GetChildMemberWithName('index').unsigned),
'field_ptr': lambda payload: '(%s).field[%d]' % (value_Value_SummaryProvider(payload.GetChildMemberWithName('container_ptr')), payload.GetChildMemberWithName('field_index').unsigned),
'bytes': lambda payload: '"%s"' % zig_String_decode(payload),
'str_lit': value_Value_str_lit,
'repeated': lambda payload: '.{%s} ** _' % value_Value_SummaryProvider(payload),
'empty_array_sentinel': lambda payload: '.{%s}' % value_Value_SummaryProvider(payload),
'slice': lambda payload: '(%s)[0..%s]' % tuple(value_Value_SummaryProvider(payload.GetChildMemberWithName(name)) for name in ('ptr', 'len')),
'float_16': lambda payload: payload.value,
'float_32': lambda payload: payload.value,
'float_64': lambda payload: payload.value,
'float_80': lambda payload: payload.value,
'float_128': lambda payload: payload.value,
'enum_literal': lambda payload: '.%s' % zig_String_AsIdentifier(payload, zig_IsFieldName),
'enum_field_index': lambda payload: 'field[%d]' % payload.unsigned,
'error': lambda payload: 'error.%s' % zig_String_AsIdentifier(payload.GetChildMemberWithName('name'), zig_IsFieldName),
'eu_payload': value_Value_SummaryProvider,
'eu_payload_ptr': lambda payload: '&((%s).* catch unreachable)' % value_Value_SummaryProvider(payload.GetChildMemberWithName('container_ptr')),
'opt_payload': value_Value_SummaryProvider,
'opt_payload_ptr': lambda payload: '&(%s).*.?' % value_Value_SummaryProvider(payload.GetChildMemberWithName('container_ptr')),
'aggregate': lambda payload: '.{%s}' % ', '.join(map(value_Value_SummaryProvider, payload.children)),
'union': lambda payload: '.{.%s = %s}' % tuple(value_Value_SummaryProvider(payload.GetChildMemberWithName(name)) for name in ('tag', 'val')),
'lazy_align': lambda payload: '@alignOf(%s)' % type_Type_SummaryProvider(payload),
'lazy_size': lambda payload: '@sizeOf(%s)' % type_Type_SummaryProvider(payload),
}
# Initialize
def add(debugger, *, category, regex=False, type, identifier=None, synth=False, inline_children=False, expand=False, summary=False):
prefix = '.'.join((__name__, (identifier or type).replace('.', '_').replace(':', '_')))
if summary: debugger.HandleCommand('type summary add --category %s%s%s "%s"' % (category, ' --inline-children' if inline_children else ''.join((' --expand' if expand else '', ' --python-function %s_SummaryProvider' % prefix if summary == True else ' --summary-string "%s"' % summary)), ' --regex' if regex else '', type))
if synth: debugger.HandleCommand('type synthetic add --category %s%s --python-class %s_SynthProvider "%s"' % (category, ' --regex' if regex else '', prefix, type))
def MultiArrayList_Entry(type): return '^multi_array_list\\.MultiArrayList\\(%s\\)\\.Entry__struct_[1-9][0-9]*$' % type
def __lldb_init_module(debugger, _=None):
# Initialize Zig Categories
debugger.HandleCommand('type category define --language c99 zig.lang zig.std')
# Initialize Zig Language
add(debugger, category='zig.lang', regex=True, type='^\\[\\]', identifier='zig_Slice', synth=True, expand=True, summary='len=${svar%#}')
add(debugger, category='zig.lang', type='[]u8', identifier='zig_String', summary=True)
add(debugger, category='zig.lang', regex=True, type='^\\?', identifier='zig_Optional', synth=True, summary=True)
add(debugger, category='zig.lang', regex=True, type='^(error{.*}|anyerror)!', identifier='zig_ErrorUnion', synth=True, inline_children=True, summary=True)
# Initialize Zig Standard Library
add(debugger, category='zig.std', type='mem.Allocator', summary='${var.ptr}')
add(debugger, category='zig.std', regex=True, type='^segmented_list\\.SegmentedList\\(.*\\)$', identifier='std_SegmentedList', synth=True, expand=True, summary='len=${var.len}')
add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)$', identifier='std_MultiArrayList', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}')
add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)\\.Slice$', identifier='std_MultiArrayList_Slice', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}')
add(debugger, category='zig.std', regex=True, type=MultiArrayList_Entry('.*'), identifier='std_Entry', synth=True, inline_children=True, summary=True)
add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)$', identifier='std_HashMapUnmanaged', synth=True, expand=True, summary=True)
add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)\\.Entry$', identifier = 'std_Entry', synth=True, inline_children=True, summary=True)
# Initialize Zig Stage2 Compiler
add(debugger, category='zig.stage2', type='Zir.Inst', identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
add(debugger, category='zig.stage2', regex=True, type=MultiArrayList_Entry('Zir\\.Inst'), identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
add(debugger, category='zig.stage2', regex=True, type='^Zir\\.Inst\\.Data\\.Data__struct_[1-9][0-9]*$', inline_children=True, summary=True)
add(debugger, category='zig.stage2', type='Zir.Inst::Zir.Inst.Ref', identifier='InstRef', summary=True)
add(debugger, category='zig.stage2', type='Zir.Inst::Zir.Inst.Index', identifier='InstIndex', summary=True)
add(debugger, category='zig.stage2', type='Air.Inst', identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
add(debugger, category='zig.stage2', type='Air.Inst::Air.Inst.Ref', identifier='InstRef', summary=True)
add(debugger, category='zig.stage2', type='Air.Inst::Air.Inst.Index', identifier='InstIndex', summary=True)
add(debugger, category='zig.stage2', regex=True, type=MultiArrayList_Entry('Air\\.Inst'), identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
add(debugger, category='zig.stage2', regex=True, type='^Air\\.Inst\\.Data\\.Data__struct_[1-9][0-9]*$', inline_children=True, summary=True)
add(debugger, category='zig.stage2', type='zig.DeclIndex', synth=True)
add(debugger, category='zig.stage2', type='Module.Namespace::Module.Namespace.Index', synth=True)
add(debugger, category='zig.stage2', type='Module.LazySrcLoc', identifier='zig_TaggedUnion', synth=True)
add(debugger, category='zig.stage2', type='InternPool.Index', synth=True)
add(debugger, category='zig.stage2', type='InternPool.NullTerminatedString', summary=True)
add(debugger, category='zig.stage2', type='InternPool.Key', identifier='zig_TaggedUnion', synth=True)
add(debugger, category='zig.stage2', type='InternPool.Key.Int.Storage', identifier='zig_TaggedUnion', synth=True)
add(debugger, category='zig.stage2', type='InternPool.Key.ErrorUnion.Value', identifier='zig_TaggedUnion', synth=True)
add(debugger, category='zig.stage2', type='InternPool.Key.Float.Storage', identifier='zig_TaggedUnion', synth=True)
add(debugger, category='zig.stage2', type='InternPool.Key.Ptr.Addr', identifier='zig_TaggedUnion', synth=True)
add(debugger, category='zig.stage2', type='InternPool.Key.Aggregate.Storage', identifier='zig_TaggedUnion', synth=True)
add(debugger, category='zig.stage2', type='arch.x86_64.CodeGen.MCValue', identifier='zig_TaggedUnion', synth=True, inline_children=True, summary=True)

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,10 @@
"license": "MIT",
"devDependencies": {
"@unicode/unicode-13.0.0": "^1.2.1",
"@unicode/unicode-3.0.0": "^1.2.1",
"@unicode/unicode-3.0.0": "^1.6.5",
"semver": "^7.3.7"
},
"dependencies": {
"@unicode/unicode-15.1.0": "^1.6.5"
}
}

View File

@@ -0,0 +1,138 @@
import crypto from "crypto";
// Types to mirror Zig's structures
interface Context<Elem> {
get(codepoint: number): Promise<Elem> | Elem;
eql(a: Elem, b: Elem): boolean;
}
interface Tables<Elem> {
stage1: number[];
stage2: number[];
stage3: Elem[];
}
class Generator<Elem> {
private static readonly BLOCK_SIZE = 256;
private readonly ctx: Context<Elem>;
private readonly blockMap = new Map<string, number>();
constructor(ctx: Context<Elem>) {
this.ctx = ctx;
}
private hashBlock(block: number[]): string {
const hash = crypto.createHash("sha256");
hash.update(Buffer.from(new Uint16Array(block).buffer));
return hash.digest("hex");
}
async generate(): Promise<Tables<Elem>> {
const stage1: number[] = [];
const stage2: number[] = [];
const stage3: Elem[] = [];
let block = new Array(Generator.BLOCK_SIZE).fill(0);
let blockLen = 0;
// Maximum Unicode codepoint is 0x10FFFF
for (let cp = 0; cp <= 0x10ffff; cp++) {
// Get the mapping for this codepoint
const elem = await this.ctx.get(cp);
// Find or add the element in stage3
let blockIdx = stage3.findIndex(item => this.ctx.eql(item, elem));
if (blockIdx === -1) {
blockIdx = stage3.length;
stage3.push(elem);
}
if (blockIdx > 0xffff) {
throw new Error("Block index too large");
}
// Add to current block
block[blockLen] = blockIdx;
blockLen++;
// Check if we need to finalize this block
if (blockLen < Generator.BLOCK_SIZE && cp !== 0x10ffff) {
continue;
}
// Fill remaining block space with zeros if needed
if (blockLen < Generator.BLOCK_SIZE) {
block.fill(0, blockLen);
}
// Get or create stage2 index for this block
const blockHash = this.hashBlock(block);
let stage2Idx = this.blockMap.get(blockHash);
if (stage2Idx === undefined) {
stage2Idx = stage2.length;
this.blockMap.set(blockHash, stage2Idx);
stage2.push(...block.slice(0, blockLen));
}
if (stage2Idx > 0xffff) {
throw new Error("Stage2 index too large");
}
// Add mapping to stage1
stage1.push(stage2Idx);
// Reset block
block = new Array(Generator.BLOCK_SIZE).fill(0);
blockLen = 0;
}
return { stage1, stage2, stage3 };
}
// Generates Zig code for the lookup tables
static writeZig<Elem>(tableName: string, tables: Tables<Elem>, elemToString: (elem: Elem) => string): string {
let output = `/// Auto-generated. Do not edit.\n`;
output += `fn ${tableName}(comptime Elem: type) type {\n`;
output += " return struct {\n";
// Stage 1
output += `pub const stage1: [${tables.stage1.length}]u16 = .{`;
output += tables.stage1.join(",");
output += "};\n\n";
// Stage 2
output += `pub const stage2: [${tables.stage2.length}]u8 = .{`;
output += tables.stage2.join(",");
output += "};\n\n";
// Stage 3
output += `pub const stage3: [${tables.stage3.length}]Elem = .{`;
output += tables.stage3.map(elemToString).join(",");
output += "};\n";
output += " };\n}\n";
return output;
}
}
// Example usage:
async function example() {
// Example context that maps codepoints to their category
const ctx: Context<string> = {
get: async (cp: number) => {
// This would normally look up the actual Unicode category
return "Lu";
},
eql: (a: string, b: string) => a === b,
};
const generator = new Generator(ctx);
const tables = await generator.generate();
// Generate Zig code
const zigCode = Generator.writeZig(tables, (elem: string) => `"${elem}"`);
console.log(zigCode);
}
export { Generator, type Context, type Tables };

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "bun",
"version": "1.1.39",
"version": "1.1.43",
"workspaces": [
"./packages/bun-types"
],
@@ -21,7 +21,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"source-map-js": "^1.2.0",
"typescript": "^5.4.5",
"typescript": "^5.7.2",
"caniuse-lite": "^1.0.30001620",
"autoprefixer": "^10.4.19",
"@mdn/browser-compat-data": "~5.5.28"
@@ -30,8 +30,8 @@
"bun-types": "workspace:packages/bun-types"
},
"scripts": {
"build": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug",
"build:debug": "bun run build",
"build": "bun run build:debug",
"build:debug": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug",
"build:valgrind": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -DENABLE_BASELINE=ON -ENABLE_VALGRIND=ON -B build/debug-valgrind",
"build:release": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -B build/release",
"build:ci": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=ON -DCI=true -B build/release-ci --verbose --fresh",
@@ -39,8 +39,8 @@
"build:logs": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DENABLE_LOGS=ON -B build/release-logs",
"build:safe": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DZIG_OPTIMIZE=ReleaseSafe -B build/release-safe",
"build:smol": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -B build/release-smol",
"build:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -DWEBKIT_LOCAL=ON -B build/debug",
"build:release:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DWEBKIT_LOCAL=ON -B build/release",
"build:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -DWEBKIT_LOCAL=ON -B build/debug-local",
"build:release:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DWEBKIT_LOCAL=ON -B build/release-local",
"build:release:with_logs": "cmake . -DCMAKE_BUILD_TYPE=Release -DENABLE_LOGS=true -GNinja -Bbuild-release && ninja -Cbuild-release",
"build:debug-zig-release": "cmake . -DCMAKE_BUILD_TYPE=Release -DZIG_OPTIMIZE=Debug -GNinja -Bbuild-debug-zig-release && ninja -Cbuild-debug-zig-release",
"css-properties": "bun run src/css/properties/generate_properties.ts",
@@ -73,6 +73,7 @@
"prettier": "bun run analysis:no-llvm --target prettier",
"prettier:check": "bun run analysis:no-llvm --target prettier-check",
"prettier:extra": "bun run analysis:no-llvm --target prettier-extra",
"prettier:diff": "bun run analysis:no-llvm --target prettier-diff"
"prettier:diff": "bun run analysis:no-llvm --target prettier-diff",
"node:test": "node ./scripts/runner.node.mjs --quiet --exec-path=$npm_execpath --node-tests "
}
}

View File

@@ -0,0 +1,5 @@
[target.aarch64-unknown-linux-musl]
linker = "aarch64-linux-musl-gcc"
rustflags = ["-C", "target-feature=-crt-static"]
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]

202
packages/bun-build-mdx-rs/.gitignore vendored Normal file
View File

@@ -0,0 +1,202 @@
# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# End of https://www.toptal.com/developers/gitignore/api/node
# Created by https://www.toptal.com/developers/gitignore/api/macos
# Edit at https://www.toptal.com/developers/gitignore?templates=macos
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
# End of https://www.toptal.com/developers/gitignore/api/macos
# Created by https://www.toptal.com/developers/gitignore/api/windows
# Edit at https://www.toptal.com/developers/gitignore?templates=windows
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.toptal.com/developers/gitignore/api/windows
#Added by cargo
/target
Cargo.lock
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
*.node
dist/
index.js
index.d.ts

View File

@@ -0,0 +1,13 @@
target
Cargo.lock
.cargo
.github
npm
.eslintrc
.prettierignore
rustfmt.toml
yarn.lock
*.node
.yarn
__test__
renovate.json

View File

@@ -0,0 +1,21 @@
[package]
edition = "2021"
name = "bun-mdx-rs"
version = "0.0.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
napi = { version = "2.12.2", default-features = false, features = ["napi4"] }
napi-derive = "2.12.2"
mdxjs = "0.2.11"
bun-native-plugin = { path = "../bun-native-plugin-rs" }
[build-dependencies]
napi-build = "2.0.1"
[profile.release]
lto = true
strip = "symbols"

View File

@@ -0,0 +1,34 @@
# bun-build-mdx-rs
This is a proof of concept for using a third-party native addon in `Bun.build()`.
This uses `mdxjs-rs` to convert MDX to JSX.
TODO: **This needs to be built & published to npm.**
## Building locally:
```sh
cargo build --release
```
```js
import { build } from "bun";
import mdx from "./index.js";
// TODO: This needs to be prebuilt for the current platform
// Probably use a napi-rs template for this
import addon from "./target/release/libmdx_bun.dylib" with { type: "file" };
const results = await build({
entrypoints: ["./hello.jsx"],
plugins: [mdx({ addon })],
minify: true,
outdir: "./dist",
define: {
"process.env.NODE_ENV": JSON.stringify("production"),
},
});
console.log(results);
```

View File

@@ -0,0 +1,7 @@
import test from 'ava'
import { sum } from '../index.js'
test('sum from native', (t) => {
t.is(sum(1, 2), 3)
})

View File

@@ -0,0 +1,5 @@
extern crate napi_build;
fn main() {
napi_build::setup();
}

View File

@@ -0,0 +1,6 @@
import page1 from "./page1.mdx";
import page2 from "./page2.mdx";
import page3 from "./page3.mdx";
import page4 from "./page4.mdx";
console.log(page1, page2, page3, page4);

View File

@@ -0,0 +1,11 @@
# Hello World
This is a sample MDX file that demonstrates various MDX features.
## Components
You can use JSX components directly in MDX:
<Button onClick={() => alert("Hello!")}>Click me</Button>
## Code Blocks

View File

@@ -0,0 +1,11 @@
# Hello World
This is a sample MDX file that demonstrates various MDX features.
## Components
You can use JSX components directly in MDX:
<Button onClick={() => alert("Hello!")}>Click me</Button>
## Code Blocks

View File

@@ -0,0 +1,11 @@
# Hello World
This is a sample MDX file that demonstrates various MDX features.
## Components
You can use JSX components directly in MDX:
<Button onClick={() => alert("Hello!")}>Click me</Button>
## Code Blocks

View File

@@ -0,0 +1,11 @@
# Hello World
This is a sample MDX file that demonstrates various MDX features.
## Components
You can use JSX components directly in MDX:
<Button onClick={() => alert("Hello!")}>Click me</Button>
## Code Blocks

View File

@@ -0,0 +1,3 @@
# `bun-mdx-rs-darwin-arm64`
This is the **aarch64-apple-darwin** binary for `bun-mdx-rs`

View File

@@ -0,0 +1,18 @@
{
"name": "bun-mdx-rs-darwin-arm64",
"version": "0.0.0",
"os": [
"darwin"
],
"cpu": [
"arm64"
],
"main": "bun-mdx-rs.darwin-arm64.node",
"files": [
"bun-mdx-rs.darwin-arm64.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@@ -0,0 +1,3 @@
# `bun-mdx-rs-darwin-x64`
This is the **x86_64-apple-darwin** binary for `bun-mdx-rs`

View File

@@ -0,0 +1,18 @@
{
"name": "bun-mdx-rs-darwin-x64",
"version": "0.0.0",
"os": [
"darwin"
],
"cpu": [
"x64"
],
"main": "bun-mdx-rs.darwin-x64.node",
"files": [
"bun-mdx-rs.darwin-x64.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@@ -0,0 +1,3 @@
# `bun-mdx-rs-linux-arm64-gnu`
This is the **aarch64-unknown-linux-gnu** binary for `bun-mdx-rs`

View File

@@ -0,0 +1,21 @@
{
"name": "bun-mdx-rs-linux-arm64-gnu",
"version": "0.0.0",
"os": [
"linux"
],
"cpu": [
"arm64"
],
"main": "bun-mdx-rs.linux-arm64-gnu.node",
"files": [
"bun-mdx-rs.linux-arm64-gnu.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
},
"libc": [
"glibc"
]
}

View File

@@ -0,0 +1,3 @@
# `bun-mdx-rs-linux-arm64-musl`
This is the **aarch64-unknown-linux-musl** binary for `bun-mdx-rs`

View File

@@ -0,0 +1,21 @@
{
"name": "bun-mdx-rs-linux-arm64-musl",
"version": "0.0.0",
"os": [
"linux"
],
"cpu": [
"arm64"
],
"main": "bun-mdx-rs.linux-arm64-musl.node",
"files": [
"bun-mdx-rs.linux-arm64-musl.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
},
"libc": [
"musl"
]
}

Some files were not shown because too many files have changed in this diff Show More