# Copyright 2026 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

EAPI=8

# Dependency blocks are generated by scripts/bun-deps.sh from the source tree.

# Vendored libraries fetched by Bun's build, as "name repo commit"
BUN_VENDOR=(
	"picohttpparser h2o/picohttpparser f4d94b48b31e0abae029ebeafcfd9ca0680ede58"
	"zlib zlib-ng/zlib-ng 12731092979c6d07f42da27da673a9f6c7b13586"
	"zstd facebook/zstd f8745da6ff1ad1e7bab384bd1f9d742439278e99"
	"brotli google/brotli db24a98aed38a16de78cc05ca15cdd8dc2a0d92d"
	"libdeflate ebiggers/libdeflate 92e6a0db9fa848d742f9eb286c92afc60f2c3dda"
	"libarchive libarchive/libarchive 27cbc7827172698143e440801fc0ba39ccb4f1f5"
	"libjpeg-turbo libjpeg-turbo/libjpeg-turbo c85e6b905bf237038faa936dab160ebfc5da0344"
	"libspng randy408/libspng fb768002d4288590083a476af628e51c3f1d47cd"
	"libwebp webmproject/libwebp b7e29b9d75bd31422b00c2a446d49d7af06c328d"
	"cares c-ares/c-ares c7a3138dcfe3bb0eaaf10c0c24c36dc66dc790ab"
	"hdrhistogram HdrHistogram/HdrHistogram_c 18c7a324383dded1451d15621cd018b0048057d0"
	"highway google/highway 2607d3b5b0113992fe84d3848859eae13b3b52c1"
	"lolhtml oven-sh/lol-html 725ce499aa9b71e38b7a2d0a9fbb6d7294a4079e"
	"rust-argon2 sru-systems/rust-argon2 ed81866f163f0c7026aa6fd8388adf37242eb32a"
	"lshpack litespeedtech/ls-hpack cf0f70dd10b352194c97448eb5d00b4aa484f531"
	"lsqpack litespeedtech/ls-qpack 91567706c41c0d97ab8dc576873ecd472d7869fa"
	"mimalloc oven-sh/mimalloc 92ef6587c57b262f6e6abf513f7776a2f8d12aeb"
	"tinycc oven-sh/tinycc 05f0fafaa3be31e31d7b4b5c17dc60f62c991171"
	"boringssl oven-sh/boringssl 41bf9b59c2ebf277a7aa427e1ecad5cc80dd4d4f"
	"lsquic litespeedtech/lsquic f812ceb9529e26717df78c6064a2ff648b949f3e"
)
BUN_WEBKIT_COMMIT="d6375c72affe0f70222cdcb45012b10f0bf3a9d5"
BUN_NODEJS_HEADERS="https://nodejs.org/dist/v26.3.0/node-v26.3.0-headers.tar.gz"
# Commit of the bun-v${PV} tag, reported by "bun --revision"
BUN_GIT_SHA="38afee6c84fcf48c0edb2466aa4b27d287a59c30"

CRATES="
	addr2line@0.27.1
	adler2@2.0.1
	aho-corasick@1.1.4
	allocator-api2@0.2.21
	anes@0.1.6
	anstyle@1.0.14
	anyhow@1.0.102
	arrayref@0.3.9
	arrayvec@0.7.6
	autocfg@1.5.0
	base64@0.22.1
	bcrypt@0.19.0
	bitflags@2.11.1
	blake2@0.10.6
	blake2b_simd@1.0.4
	block-buffer@0.10.4
	blowfish@0.9.1
	bstr@1.12.1
	bumpalo@3.20.2
	bytemuck@1.25.0
	byteorder@1.5.0
	cast@0.3.0
	cc@1.2.0
	cfg-if@1.0.4
	ciborium-io@0.2.2
	ciborium-ll@0.2.2
	ciborium@0.2.2
	cipher@0.4.4
	clap@4.6.1
	clap_builder@4.6.0
	clap_lex@1.1.0
	const_format@0.2.36
	const_format_proc_macros@0.2.34
	constant_time_eq@0.4.2
	cpufeatures@0.2.17
	criterion-plot@0.5.0
	criterion@0.5.1
	crossbeam-deque@0.8.6
	crossbeam-epoch@0.9.18
	crossbeam-utils@0.8.21
	crunchy@0.2.4
	crypto-common@0.1.7
	cssparser-macros@0.6.1
	cssparser@0.36.0
	darling@0.21.3
	darling_core@0.21.3
	darling_macro@0.21.3
	derive_more-impl@2.1.1
	derive_more@2.1.1
	digest@0.10.7
	dlmalloc@0.2.14
	dtoa-short@0.3.5
	dtoa@1.0.11
	either@1.15.0
	encoding_rs@0.8.35
	enum-map-derive@0.17.0
	enum-map@2.7.3
	enumset@1.1.10
	enumset_derive@0.14.0
	equivalent@1.0.2
	errno@0.3.14
	fastrand@2.4.1
	fnv@1.0.7
	foldhash@0.1.5
	foldhash@0.2.0
	fortanix-sgx-abi@0.6.1
	futures-core@0.3.32
	futures-task@0.3.32
	futures-util@0.3.32
	generic-array@0.14.7
	getopts@0.2.24
	getrandom@0.4.2
	gimli@0.34.0
	half@2.7.1
	hashbrown@0.15.5
	hashbrown@0.16.1
	hashbrown@0.17.0
	hashbrown@0.17.1
	heck@0.5.0
	hermit-abi@0.5.2
	id-arena@2.3.0
	ident_case@1.0.1
	indexmap@2.14.0
	inout@0.1.4
	is-terminal@0.4.17
	itertools@0.10.5
	itoa@1.0.18
	js-sys@0.3.98
	keccak@0.1.6
	konst@0.2.20
	konst_macro_rules@0.2.19
	leb128fmt@0.1.0
	libc@0.2.186
	libc@0.2.189
	linux-raw-sys@0.4.15
	log@0.4.29
	memchr@2.8.0
	memchr@2.8.3
	mime@0.3.17
	miniz_oxide@0.9.1
	moto-rt@0.16.4
	new_debug_unreachable@1.0.6
	num-traits@0.2.19
	object@0.39.1
	once_cell@1.21.4
	oorandom@11.1.5
	paste@1.0.15
	phf@0.13.1
	phf_codegen@0.13.1
	phf_generator@0.13.1
	phf_macros@0.13.1
	phf_shared@0.13.1
	pin-project-lite@0.2.17
	plotters-backend@0.3.7
	plotters-svg@0.3.7
	plotters@0.3.7
	precomputed-hash@0.1.1
	prettyplease@0.2.37
	proc-macro2@1.0.106
	quick-xml@0.38.4
	quote@1.0.45
	r-efi-alloc@2.1.0
	r-efi@5.3.0
	r-efi@6.0.0
	rand@0.9.5
	rand_core@0.9.5
	rand_xorshift@0.4.0
	rayon-core@1.13.0
	rayon@1.12.0
	regex-automata@0.4.14
	regex-syntax@0.8.10
	regex@1.12.3
	roxmltree@0.20.0
	rustc-demangle@0.1.28
	rustc-hash@2.1.2
	rustc-literal-escaper@0.0.8
	rustc_version@0.4.1
	rustix@0.38.44
	rustversion@1.0.22
	same-file@1.0.6
	scopeguard@1.2.0
	selectors@0.33.0
	semver@1.0.28
	serde@1.0.228
	serde_core@1.0.228
	serde_derive@1.0.228
	serde_json@1.0.149
	servo_arc@0.4.3
	sha3@0.10.9
	shlex@1.3.0
	siphasher@1.0.3
	slab@0.4.12
	smallvec@1.15.1
	stable_deref_trait@1.2.1
	strum@0.26.3
	strum_macros@0.26.4
	subtle@2.6.1
	syn@2.0.117
	thiserror-impl@2.0.18
	thiserror@2.0.18
	tinytemplate@1.2.1
	typenum@1.20.0
	unicode-ident@1.0.24
	unicode-xid@0.2.6
	unwinding@0.2.10
	version_check@0.9.5
	vex-sdk@0.27.1
	walkdir@2.5.0
	wasip1@1.0.0
	wasip2@1.0.3+wasi-0.2.9
	wasip2@1.0.4+wasi-0.2.12
	wasip3@0.4.0+wasi-0.3.0-rc-2026-01-06
	wasip3@0.7.0+wasi-0.3.0
	wasm-bindgen-macro-support@0.2.121
	wasm-bindgen-macro@0.2.121
	wasm-bindgen-shared@0.2.121
	wasm-bindgen@0.2.121
	wasm-encoder@0.244.0
	wasm-metadata@0.244.0
	wasmparser@0.244.0
	web-sys@0.3.98
	winapi-util@0.1.11
	windows-link@0.2.1
	windows-sys@0.59.0
	windows-sys@0.61.2
	windows-targets@0.52.6
	windows_aarch64_gnullvm@0.52.6
	windows_aarch64_msvc@0.52.6
	windows_i686_gnu@0.52.6
	windows_i686_gnullvm@0.52.6
	windows_i686_msvc@0.52.6
	windows_x86_64_gnu@0.52.6
	windows_x86_64_gnullvm@0.52.6
	windows_x86_64_msvc@0.52.6
	wit-bindgen-core@0.51.0
	wit-bindgen-rust-macro@0.51.0
	wit-bindgen-rust@0.51.0
	wit-bindgen@0.51.0
	wit-bindgen@0.57.1
	wit-component@0.244.0
	wit-parser@0.244.0
	xml-rs@0.8.29
	zerocopy-derive@0.8.48
	zerocopy@0.8.48
	zeroize@1.8.2
	zmij@1.0.21
"

NPM_PKGS="
	@esbuild/linux-arm64@0.21.5|arm64
	@esbuild/linux-x64@0.21.5|amd64
	@lezer/common@1.5.2
	@lezer/cpp@1.1.6
	@lezer/highlight@1.2.3
	@lezer/lr@1.4.10
	@types/node@26.2.0
	@types/prop-types@15.7.15
	@types/react@18.3.31
	@types/react-dom@18.3.7
	abort-controller@3.0.0
	asn1.js@4.10.1
	assert@2.1.0
	available-typed-arrays@1.0.7
	base64-js@1.5.1
	bn.js@4.12.2
	bn.js@5.2.2
	brorand@1.1.0
	browserify-aes@1.2.0
	browserify-cipher@1.0.1
	browserify-des@1.0.2
	browserify-rsa@4.1.1
	browserify-sign@4.2.5
	browserify-zlib@0.2.0
	buffer@5.0.5
	buffer@6.0.3
	buffer-xor@1.0.3
	builtin-status-codes@3.0.0
	call-bind@1.0.8
	call-bind-apply-helpers@1.0.2
	call-bound@1.0.4
	cipher-base@1.0.7
	core-util-is@1.0.3
	create-ecdh@4.0.4
	create-hash@1.2.0
	create-hmac@1.1.7
	crypto-browserify@3.12.1
	csstype@3.2.3
	define-data-property@1.1.4
	define-properties@1.2.1
	des.js@1.1.0
	diffie-hellman@5.0.3
	domain-browser@4.23.0
	dunder-proto@1.0.1
	elliptic@6.6.1
	es-define-property@1.0.1
	es-errors@1.3.0
	es-object-atoms@1.1.1
	esbuild@0.21.5
	event-target-shim@5.0.1
	events@3.3.0
	evp_bytestokey@1.0.3
	for-each@0.3.5
	function-bind@1.1.2
	generator-function@2.0.1
	get-intrinsic@1.3.0
	get-proto@1.0.1
	gopd@1.2.0
	has-property-descriptors@1.0.2
	has-symbols@1.1.0
	has-tostringtag@1.0.2
	hash-base@3.0.5
	hash-base@3.1.2
	hash.js@1.1.7
	hasown@2.0.2
	hmac-drbg@1.0.1
	https-browserify@1.0.0
	ieee754@1.2.1
	inherits@2.0.4
	is-arguments@1.2.0
	is-callable@1.2.7
	is-generator-function@1.1.2
	is-nan@1.3.2
	is-regex@1.2.1
	is-typed-array@1.1.15
	isarray@1.0.0
	isarray@2.0.5
	js-tokens@4.0.0
	loose-envify@1.4.0
	math-intrinsics@1.1.0
	md5.js@1.3.5
	miller-rabin@4.0.1
	minimalistic-assert@1.0.1
	minimalistic-crypto-utils@1.0.1
	object-is@1.1.6
	object-keys@1.1.1
	object.assign@4.1.7
	pako@1.0.11
	parse-asn1@5.1.9
	pbkdf2@3.1.5
	possible-typed-array-names@1.1.0
	preact@10.27.2
	prettier@3.9.9
	process@0.11.10
	process-nextick-args@2.0.1
	public-encrypt@4.0.3
	punycode@2.3.1
	querystring-es3@1.0.0-0
	randombytes@2.1.0
	randomfill@1.0.4
	react@18.3.1
	react-dom@18.3.1
	react-refresh@0.17.0
	readable-stream@2.3.8
	readable-stream@3.6.2
	readable-stream@4.7.0
	ripemd160@2.0.3
	safe-buffer@5.1.2
	safe-buffer@5.2.1
	safe-regex-test@1.1.0
	scheduler@0.23.2
	set-function-length@1.2.2
	sha.js@2.4.12
	source-map-js@1.2.1
	stream-http@3.2.0
	string_decoder@1.1.1
	string_decoder@1.3.0
	to-buffer@1.2.2
	typed-array-buffer@1.0.3
	typescript@6.0.2
	undici-types@8.3.0
	util@0.12.5
	util-deprecate@1.0.2
	which-typed-array@1.1.19
	xtend@4.0.2
"

NPM_STUB_PKGS="
	@esbuild/aix-ppc64@0.21.5
	@esbuild/android-arm64@0.21.5
	@esbuild/android-arm@0.21.5
	@esbuild/android-x64@0.21.5
	@esbuild/darwin-arm64@0.21.5
	@esbuild/darwin-x64@0.21.5
	@esbuild/freebsd-arm64@0.21.5
	@esbuild/freebsd-x64@0.21.5
	@esbuild/linux-arm@0.21.5
	@esbuild/linux-ia32@0.21.5
	@esbuild/linux-loong64@0.21.5
	@esbuild/linux-mips64el@0.21.5
	@esbuild/linux-ppc64@0.21.5
	@esbuild/linux-riscv64@0.21.5
	@esbuild/linux-s390x@0.21.5
	@esbuild/netbsd-x64@0.21.5
	@esbuild/openbsd-x64@0.21.5
	@esbuild/sunos-x64@0.21.5
	@esbuild/win32-arm64@0.21.5
	@esbuild/win32-ia32@0.21.5
	@esbuild/win32-x64@0.21.5
"

LLVM_COMPAT=( 23 )
PYTHON_COMPAT=( python3_{11..14} )
# JavaScriptCore's offlineasm, same range as net-libs/webkit-gtk
USE_RUBY="ruby32 ruby33 ruby34 ruby40"
# CRATES includes std's own crates for -Zbuild-std, which are per Rust version
RUST_MIN_VER="1.99.0"
RUST_MAX_VER="${RUST_MIN_VER}"
RUST_NEEDS_LLVM=1
NPM_OPTIONAL=1

inherit cargo check-reqs edo flag-o-matic multiprocessing python-any-r1 ruby-single shell-completion toolchain-funcs

DESCRIPTION="Fast all-in-one JavaScript runtime, bundler, test runner and package manager"
HOMEPAGE="https://bun.com https://github.com/oven-sh/bun"

LICENSE="MIT"
# Vendored libraries and JavaScriptCore
LICENSE+=" Apache-2.0 BSD BSD-2 ISC LGPL-2 LGPL-2.1 MIT openssl ZLIB"
# Dependent crate licenses
LICENSE+=" Apache-2.0 BSD MIT Unicode-3.0 ZLIB"
SLOT="0"
KEYWORDS="~amd64 ~arm64"
IUSE="system-bootstrap"

S="${WORKDIR}/bun-bun-v${PV}"

RESTRICT="mirror test"

DEPEND="
	dev-libs/icu:=
"
RDEPEND="${DEPEND}"
# Inlined from npm.eclass
NPM_NODE_DEPEND="net-libs/nodejs[npm]"
IUSE+=" elibc_llvm"

# Inlined npm eclass functions - must be defined before use
_npm_distfile() {
	local name=${1//\//+}
	echo "${name}-${2}.npm.tgz"
}

_npm_split() {
	local spec=${1%%|*}
	conds=
	[[ ${1} == *\|* ]] && conds=${1#*|}
	name=${spec%@*}
	version=${spec##*@}
	if [[ -z ${name} || -z ${version} || ${name} == "${spec}" ]]; then
		die "cannot parse NPM_PKGS entry: ${1}"
	fi
}

_npm_set_pkg_uris() {
	local entry name version conds base cond open close file
	local -a cond_list
	NPM_PKG_URIS=
	for entry in ${NPM_PKGS}; do
		_npm_split "${entry}"
		base=${name##*/}
		file=${name//\//+}-${version}.npm.tgz
		open= close=
		if [[ -n ${conds} ]]; then
			local IFS=,
			cond_list=( ${conds} )
			unset IFS
			for cond in "${cond_list[@]}"; do
				open+="${cond}? ( "
				close+=" )"
			done
		fi
		NPM_PKG_URIS+="${open}${NPM_REGISTRY_URI}/${name}/-/${base}-${version}.tgz -> ${file}${close}
"
	done
	if [[ -n ${NPM_PNPM_VERSION} ]]; then
		NPM_PKG_URIS+="${NPM_REGISTRY_URI}/pnpm/-/pnpm-${NPM_PNPM_VERSION}.tgz -> pnpm-${NPM_PNPM_VERSION}.npm-tool.tgz
"
	fi
}
_npm_set_pkg_uris

_bun_set_src_uri() {
	local dep name repo commit parts
	SRC_URI="
		https://github.com/oven-sh/bun/archive/refs/tags/bun-v${PV}.tar.gz -> ${P}.gh.tar.gz
		${BUN_NODEJS_HEADERS} -> bun-${BUN_NODEJS_HEADERS##*/}
		https://github.com/FireBurn/Overlay/releases/download/webkit-${BUN_WEBKIT_COMMIT}/WebKit-${BUN_WEBKIT_COMMIT}.tar.xz
	"
	for dep in "${BUN_VENDOR[@]}"; do
		parts=( ${dep} )
		name=${parts[0]} repo=${parts[1]} commit=${parts[2]}
		SRC_URI+=" https://github.com/${repo}/archive/${commit}.tar.gz -> bun-vendor-${name}-${commit}.gh.tar.gz"
	done
	# The build runs a handful of codegen scripts with bun itself
	SRC_URI+="
		!system-bootstrap? (
			amd64? ( https://github.com/oven-sh/bun/releases/download/bun-v${PV}/bun-linux-x64-baseline.zip
				-> bun-bootstrap-${PV}-x64-baseline.zip )
			arm64? ( https://github.com/oven-sh/bun/releases/download/bun-v${PV}/bun-linux-aarch64.zip
				-> bun-bootstrap-${PV}-aarch64.zip )
		)
		${CARGO_CRATE_URIS}
		${NPM_PKG_URIS}
	"
}
_bun_set_src_uri
unset -f _bun_set_src_uri

BDEPEND="
	${PYTHON_DEPS}
	${RUST_DEPEND}
	$([[ -z ${NPM_OPTIONAL} ]] && echo ${NPM_NODE_DEPEND})
	>=net-libs/nodejs-25
	$(llvm_gen_dep '
		llvm-core/clang:${LLVM_SLOT}
		llvm-core/lld:${LLVM_SLOT}
		llvm-core/llvm:${LLVM_SLOT}
	')
	app-alternatives/ninja
	dev-build/cmake
	${RUBY_DEPS}
	dev-lang/perl
	dev-util/gperf
	virtual/pkgconfig
	system-bootstrap? ( dev-lang/bun )
	!system-bootstrap? ( app-arch/unzip )
"

QA_FLAGS_IGNORED="usr/bin/bun"

CHECKREQS_DISK_BUILD="25G"
CHECKREQS_MEMORY="16G"

# Inlined functions from npm.eclass

_npm_write_registry() {
	cat > "${1}" <<-'EOF' || die
	import { createServer } from "node:http";
	import { createHash } from "node:crypto";
	import { appendFileSync, readFileSync, writeFileSync } from "node:fs";
	import { gunzipSync } from "node:zlib";

	const dir = process.argv[2];
	const pkgs = new Map();
	for (const line of readFileSync(`${dir}/index`, "utf8").split("\n")) {
		if (!line) continue;
		let [name, version, path] = line.split("\t");
		const entry = path === "-" ? { stub: true } : { path };
		if (!name) {
			// Local tarball: name and version come from its package.json
			({ name, version } = load(entry).manifest);
		}
		if (!pkgs.has(name)) pkgs.set(name, new Map());
		pkgs.get(name).set(version, entry);
	}

	function manifest(tgz) {
		const tar = gunzipSync(tgz);
		let paxPath;
		for (let off = 0; off + 512 <= tar.length; ) {
			const hdr = tar.subarray(off, off + 512);
			if (hdr.every(b => b === 0)) break;
			const str = (a, b) => hdr.subarray(a, b).toString("utf8").replace(/\0.*$/s, "");
			const size = parseInt(str(124, 136).trim() || "0", 8);
			const type = str(156, 157);
			const data = tar.subarray(off + 512, off + 512 + size);
			let path = paxPath ?? (str(345, 500) ? `${str(345, 500)}/${str(0, 100)}` : str(0, 100));
			paxPath = undefined;
			if (type === "x") {
				const m = /\d+ path=([^\n]*)\n/.exec(data.toString("utf8"));
				if (m) paxPath = m[1];
			} else if (/^[^/]+\/package\.json$/.test(path)) {
				return JSON.parse(data.toString("utf8"));
			}
			off += 512 + Math.ceil(size / 512) * 512;
		}
		throw new Error("no package.json");
	}

	function load(entry) {
		if (entry.stub) return entry;
		if (!entry.data) {
			const data = readFileSync(entry.path);
			entry.data = data;
			entry.integrity = "sha512-" + createHash("sha512").update(data).digest("base64");
			entry.shasum = createHash("sha1").update(data).digest("hex");
			try { entry.manifest = manifest(data); } catch { entry.manifest = {}; }
		}
		return entry;
	}

	function semverGt(a, b) {
		const pa = a.split(/[.+]/).map(Number), pb = b.split(/[.+]/).map(Number);
		for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return pa[i] > pb[i];
		return false;
	}

	function missing(what) {
		appendFileSync(`${dir}/missing.log`, what + "\n");
	}

	const server = createServer((req, res) => {
		const base = `http://${req.headers.host}/`;
		const path = decodeURIComponent(new URL(req.url, base).pathname).replace(/^\/+/, "");
		const tgz = path.indexOf("/-/");
		const send = (code, body, type) => {
			res.writeHead(code, { "content-type": type });
			res.end(body);
		};
		if (tgz !== -1) {
			const name = path.slice(0, tgz);
			const file = path.slice(tgz + 3);
			const prefix = `${name.split("/").pop()}-`;
			const version = file.startsWith(prefix) ? file.slice(prefix.length).replace(/\.tgz$/, "") : "";
			const entry = pkgs.get(name)?.get(version);
			if (!entry || entry.stub) { missing(`tarball\t${name}@${version || file}`); return send(404, "{}", "application/json"); }
			return send(200, load(entry).data, "application/octet-stream");
		}
		const versions = pkgs.get(path);
		if (!versions) { missing(`packument\t${path}`); return send(404, "{}", "application/json"); }
		// Fixed old publish times keep min-release-age style policies happy
		const epoch = "2000-01-01T00:00:00.000Z";
		const out = { name: path, versions: {}, "dist-tags": {}, time: { created: epoch, modified: epoch } };
		let latest;
		for (const [version, entry] of versions) {
			load(entry);
			out.time[version] = epoch;
			out.versions[version] = entry.stub ? {
				// Described so an installer can skip it, never downloaded
				name: path, version, os: ["none"], cpu: ["none"],
				dist: { tarball: `${base}${path}/-/stub-${version}.tgz` },
			} : {
				...entry.manifest, name: path, version,
				dist: {
					tarball: `${base}${path}/-/${path.split("/").pop()}-${version}.tgz`,
					integrity: entry.integrity, shasum: entry.shasum,
				},
			};
			if (!/-/.test(version) && (!latest || semverGt(version, latest))) latest = version;
		}
		out["dist-tags"].latest = latest ?? [...versions.keys()].pop();
		send(200, JSON.stringify(out), "application/json");
	});

	server.listen(0, "127.0.0.1", () => {
		writeFileSync(`${dir}/port`, String(server.address().port));
	});
	EOF
}

npm_setup_env() {
	debug-print-function ${FUNCNAME} "$@"

	export HOME="${T}/npm-home"
	mkdir -p "${HOME}" || die

	tc-export CC CXX AR RANLIB
	export CC_host=$(tc-getBUILD_CC) CXX_host=$(tc-getBUILD_CXX)

	# npm and node-gyp
	export npm_config_cache="${T}/npm-cache"
	export npm_config_nodedir="${BROOT}/usr"
	export npm_config_build_from_source=true
	export npm_config_audit=false
	export npm_config_fund=false
	export npm_config_update_notifier=false
	export npm_config_foreground_scripts=true
	export npm_config_progress=false
	export JOBS=$(makeopts_jobs)

	# npm's libc check reads /usr/bin/ldd, which can be a wrapper (e.g.
	# llvm-libc's) that names no family, so packages pinned to a libc are
	# silently skipped. The Node runtime report knows node's own libc.
	local glibc_ver
	glibc_ver=$(node -p "process.report.excludeNetwork = true;
		process.report.getReport().header.glibcVersionRuntime || ''" 2>/dev/null) \
		|| glibc_ver=
	[[ -n ${glibc_ver} ]] && export npm_config_libc=glibc

	# pnpm, yarn and bun keep their own stores
	export XDG_CONFIG_HOME="${HOME}/.config"
	export PNPM_HOME="${T}/pnpm-home"
	export YARN_CACHE_FOLDER="${T}/yarn-cache"
	export YARN_ENABLE_GLOBAL_CACHE=false
	export YARN_ENABLE_TELEMETRY=0
	export BUN_INSTALL_CACHE_DIR="${T}/bun-cache"
	export BUN_RUNTIME_TRANSPILER_CACHE_PATH=0
	export COREPACK_ENABLE_NETWORK=0
	export COREPACK_ENABLE_STRICT=0

	if [[ -n ${NPM_PNPM_VERSION} ]]; then
		local tooldir="${T}/npm-tools"
		if [[ ! -x ${tooldir}/bin/pnpm ]]; then
			mkdir -p "${tooldir}/pnpm" "${tooldir}/bin" || die
			tar -C "${tooldir}/pnpm" --strip-components=1 -xzf \
				"${DISTDIR}/pnpm-${NPM_PNPM_VERSION}.npm-tool.tgz" || die
			printf '#!/bin/sh\nexec node "%s" "$@"\n' "${tooldir}/pnpm/bin/pnpm.cjs" \
				> "${tooldir}/bin/pnpm" || die
			chmod +x "${tooldir}/bin/pnpm" || die
		fi
		[[ :${PATH}: == *:${tooldir}/bin:* ]] || export PATH="${tooldir}/bin:${PATH}"
		# The version pinned in packageManager is the one we provide
		export npm_config_manage_package_manager_versions=false
	fi

	# Things that would otherwise try the network or fetch binaries
	export CI=true
	export HUSKY=0
	export DO_NOT_TRACK=1
	export DISABLE_TELEMETRY=1
	export NEXT_TELEMETRY_DISABLED=1
	export ELECTRON_SKIP_BINARY_DOWNLOAD=1
	export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
	export PUPPETEER_SKIP_DOWNLOAD=1
	export CYPRESS_INSTALL_BINARY=0
	export SHARP_IGNORE_GLOBAL_LIBVIPS=1
	export SENTRYCLI_SKIP_DOWNLOAD=1
}

npm_registry_start() {
	debug-print-function ${FUNCNAME} "$@"

	[[ -n ${_NPM_REGISTRY_PID} ]] && return
	npm_setup_env

	local dir="${T}/npm-registry"
	mkdir -p "${dir}" || die
	rm -f "${dir}"/{port,missing.log} || die

	local entry name version conds file
	local distdir=${PORTAGE_ACTUAL_DISTDIR:-${DISTDIR}}
	# Which distfiles this build actually has, as a set: NPM_PKGS runs to
	# thousands of entries, and searching ${A} for each one does not scale
	local -A fetched=()
	for file in ${A}; do
		fetched[${file}]=1
	done
	: > "${dir}/index" || die
	for entry in ${NPM_PKGS}; do
		_npm_split "${entry}"
		file=$(_npm_distfile "${name}" "${version}")
		if [[ -n ${fetched[${file}]} ]]; then
			printf '%s\t%s\t%s\n' "${name}" "${version}" "${distdir}/${file}" \
				>> "${dir}/index" || die
		else
			# Not fetched for this arch or libc: describe it as a platform
			# that cannot match, like NPM_STUB_PKGS
			printf '%s\t%s\t-\n' "${name}" "${version}" >> "${dir}/index" || die
		fi
	done
	for entry in ${NPM_STUB_PKGS}; do
		_npm_split "${entry}"
		printf '%s\t%s\t-\n' "${name}" "${version}" >> "${dir}/index" || die
	done
	for file in "${_NPM_LOCAL_TARBALLS[@]}"; do
		printf '\t\t%s\n' "${file}" >> "${dir}/index" || die
	done

	_npm_write_registry "${dir}/registry.mjs"

	node "${dir}/registry.mjs" "${dir}" > "${dir}/registry.log" 2>&1 &
	_NPM_REGISTRY_PID=$!

	local i
	for (( i = 0; i < 300; i++ )); do
		[[ -s ${dir}/port ]] && break
		kill -0 "${_NPM_REGISTRY_PID}" 2>/dev/null || break
		sleep 0.1
	done
	if [[ ! -s ${dir}/port ]]; then
		cat "${dir}/registry.log" >&2
		die "${ECLASS}: offline registry failed to start"
	fi

	local url="http://127.0.0.1:$(<"${dir}/port")/"
	# pnpm ignores npm_config_registry, so put it in the user config too
	cat > "${HOME}/.npmrc" <<-EOF || die
	registry=${url}
	store-dir=${T}/pnpm-store
	EOF
	export NPM_REGISTRY_LOCAL=${url}
	export npm_config_registry=${url}
	export NPM_CONFIG_REGISTRY=${url}
	export BUN_CONFIG_REGISTRY=${url}
	export YARN_REGISTRY=${url}
	export YARN_NPM_REGISTRY_SERVER=${url%/}
	export YARN_UNSAFE_HTTP_WHITELIST=127.0.0.1
	einfo "Offline npm registry serving $(wc -l < "${dir}/index") packages at ${url}"
	einfo "The install that follows unpacks and links every one of them, and"
	einfo "prints nothing while it does; expect several quiet minutes."
}

npm_registry_stop() {
	debug-print-function ${FUNCNAME} "$@"

	[[ -n ${_NPM_REGISTRY_PID} ]] || return
	kill "${_NPM_REGISTRY_PID}" 2>/dev/null
	wait "${_NPM_REGISTRY_PID}" 2>/dev/null
	unset _NPM_REGISTRY_PID NPM_REGISTRY_LOCAL

	# Tarball misses break installs; packument misses are usually optional
	# or platform-specific packages that were never needed.
	local log="${T}/npm-registry/missing.log"
	if [[ -s ${log} ]]; then
		local kind what
		while IFS=$'\t' read -r kind what; do
			case ${kind} in
				tarball) ewarn "Offline registry: tarball not in NPM_PKGS: ${what}" ;;
				*) debug-print "${ECLASS}: packument not in NPM_PKGS: ${what}" ;;
			esac
		done < <(sort -u "${log}")
		einfo "Unresolved registry requests are listed in ${log}"
	fi
}


pkg_pretend() {
	[[ ${MERGE_TYPE} != binary ]] && check-reqs_pkg_pretend
}

pkg_setup() {
	if [[ ${MERGE_TYPE} != binary ]]; then
		check-reqs_pkg_setup
		llvm-r2_pkg_setup
		rust_pkg_setup
		python-any-r1_pkg_setup
	fi
}

src_unpack() {
	unpack "${P}.gh.tar.gz"
	local archive
	for archive in ${A}; do
		[[ ${archive} == bun-bootstrap-*.zip ]] && unpack "${archive}"
	done

	# GitHub cannot archive the oven-sh/WebKit fork (too large), so
	# .github/workflows/webkit-tarball.yml publishes a git archive of the
	# pinned commit, without test suites, to the overlay's releases
	unpack "WebKit-${BUN_WEBKIT_COMMIT}.tar.xz"

	cargo_crate_unpack
	cargo_gen_config

	# Bun's downloader looks in the prefetch cache first, keyed by URL, so
	# point it at the verified distfiles
	local prefetch="${WORKDIR}/bun-prefetch/by-url" dep name repo commit key parts
	mkdir -p "${prefetch}" || die
	_bun_prefetch() {
		key=$(printf %s "${1}" | sha256sum) || die
		ln -s "${DISTDIR}/${2}" "${prefetch}/${key:0:32}" || die
	}
	for dep in "${BUN_VENDOR[@]}"; do
		parts=( ${dep} )
		name=${parts[0]} repo=${parts[1]} commit=${parts[2]}
		_bun_prefetch "https://github.com/${repo}/archive/${commit}.tar.gz" \
			"bun-vendor-${name}-${commit}.gh.tar.gz"
	done
	_bun_prefetch "${BUN_NODEJS_HEADERS}" "bun-${BUN_NODEJS_HEADERS##*/}"
}

src_prepare() {
	default

	eapply "${FILESDIR}/${P}-type-info.patch"
	eapply "${FILESDIR}/${P}-once-default.patch"

	# Use the CPU target from CFLAGS instead of upstream's portable baseline.
	# The same table feeds WebKit's flags and rustc's -Ctarget-cpu.
	local gentoo='process.env.BUN_GENTOO_CPU_FLAGS'
	local table="${gentoo} ? [{ flag: ${gentoo}.split(\" \"), when: () => true, desc: \"CFLAGS\" }] : ["
	sed -i -e "s|^export const cpuTargetFlags: Flag\[\] = \[\$|export const cpuTargetFlags: Flag[] = ${table}|" \
		scripts/build/flags.ts || die
	grep -q BUN_GENTOO_CPU_FLAGS scripts/build/flags.ts || die "failed to patch CPU target flags"

	# The libc crate versions its termios symbols for glibc targets, and an
	# unversioned libc has nothing for @GLIBC_x to bind to
	local crate
	for crate in "${ECARGO_HOME}/gentoo"/libc-0.2.*/src/unix/mod.rs; do
		[[ -f ${crate} ]] && sed -i -E \
			's/link_name = "([A-Za-z0-9_]+)@GLIBC_[0-9.]+"/link_name = "\1"/' "${crate}" || die
	done

	# Bun picks libstdc++ names for the V8 entry points that take std::
	# types on Linux, but this C++ is built against libc++ (std::__1::)
	if [[ $(tc-get-cxx-stdlib) == libc++ ]]; then
		sed -i \
			-e 's/EmSt8functionIFNS_10MaybeLocalINS_5ValueEEEvEE/EmNSt3__18functionIFNS_10MaybeLocalINS_5ValueEEEvEEE/' \
			-e 's/ESt8optionalImE/ENSt3__18optionalImEE/' \
			src/runtime/napi/napi_body.rs || die
	fi
}

src_compile() {
	local cpu=() flag
	if use amd64; then
		for flag in march mtune; do
			is-flagq "-${flag}=*" && cpu+=( "-${flag}=$(get-flag "${flag}")" )
		done
	else
		# Bun only translates -march=armv8-a for rustc; -mcpu maps directly
		is-flagq "-mcpu=*" && cpu+=( "-mcpu=$(get-flag mcpu)" )
	fi
	export BUN_GENTOO_CPU_FLAGS="${cpu[*]}"
	[[ -n ${BUN_GENTOO_CPU_FLAGS} ]] && einfo "CPU target: ${BUN_GENTOO_CPU_FLAGS}"

	export BUN_BUILD_PREFETCH_DIR="${WORKDIR}/bun-prefetch"
	export BUN_WEBKIT_PATH="${WORKDIR}/WebKit-${BUN_WEBKIT_COMMIT}"
	export BUN_TOOLCHAIN_LLVM=$(get_llvm_prefix -b)
	export BUN_TOOLCHAIN_RUST=$(get_rust_prefix)
	export CARGO_HOME="${ECARGO_HOME}"
	# The Rust sources use nightly features
	export RUSTC_BOOTSTRAP=1
	export GIT_SHA="${BUN_GIT_SHA}"

	npm_setup_env
	if ! use system-bootstrap; then
		local bootstrap=( "${WORKDIR}"/bun-linux-*/ )
		export PATH="${bootstrap[0]%/}:${PATH}"
	fi

	local lto=off
	tc-is-lto && lto=on

	npm_registry_start
	edo node --experimental-strip-types --disable-warning=MODULE_TYPELESS_PACKAGE_JSON \
		scripts/build.ts --profile=release-local --canary=off --lto=${lto} \
		-j$(makeopts_jobs) -l$(makeopts_loadavg)
	npm_registry_stop
}

src_install() {
	dobin build/release/bun
	dosym bun /usr/bin/bunx

	newbashcomp completions/bun.bash bun
	newzshcomp completions/bun.zsh _bun
	dofishcomp completions/bun.fish

	einstalldocs
}