var fs = require('fs') var path = require('path') var url = require('url') var vars = (process.config && process.config.variables) || {} var prebuildsOnly = !!process.env.PREBUILDS_ONLY var versions = process.versions var abi = versions.modules if (versions.deno || process.isBun) { // both Deno and Bun made the very poor decision to shoot themselves in the foot and lie about support for ABI // (which they do not have) abi = 'unsupported' } var runtime = isElectron() ? 'electron' : 'node' var arch = process.arch var platform = process.platform var libc = process.env.LIBC || (isMusl(platform) ? 'musl' : 'glibc') var armv = process.env.ARM_VERSION || (arch === 'arm64' ? '8' : vars.arm_version) || '' var uv = (versions.uv || '').split('.')[0] module.exports = load function load (dir) { // Workaround to fix webpack's build warnings: 'the request of a dependency is an expression', but without // reassigning require in a way that breaks Bun. if (typeof __webpack_require__ === 'function') return __non_webpack_require__(load.path(dir)) else return require(load.path(dir)) } load.path = function (dir) { dir = path.resolve(dir || '.') var packageName = '' try { // explanation above if (typeof __webpack_require__ === 'function') packageName = __non_webpack_require__(path.join(dir, 'package.json')).name else packageName = require(path.join(dir, 'package.json')).name var varName = packageName.toUpperCase().replace(/-/g, '_') + '_PREBUILD' if (process.env[varName]) dir = process.env[varName] } catch (err) {} if (!prebuildsOnly) { var release = getFirst(path.join(dir, 'build/Release'), matchBuild) if (release) return release var debug = getFirst(path.join(dir, 'build/Debug'), matchBuild) if (debug) return debug } var prebuild = resolve(dir) if (prebuild) return prebuild var nearby = resolve(path.dirname(process.execPath)) if (nearby) return nearby var platformPackage = (packageName[0] == '@' ? '' : '@' + packageName + '/') + packageName + '-' + platform + '-' + arch try { var prebuildPackage = path.dirname(require('module').createRequire(url.pathToFileURL(path.join(dir, 'package.json'))).resolve(platformPackage)) return resolveFile(prebuildPackage) } catch(error) {} var target = [ 'platform=' + platform, 'arch=' + arch, 'runtime=' + runtime, 'abi=' + abi, 'uv=' + uv, armv ? 'armv=' + armv : '', 'libc=' + libc, 'node=' + process.versions.node, process.versions.electron ? 'electron=' + process.versions.electron : '', typeof __webpack_require__ === 'function' ? 'webpack=true' : '' // eslint-disable-line ].filter(Boolean).join(' ') throw new Error('No native build was found for ' + target + '\n attempted loading from: ' + dir + ' and package:' + ' ' + platformPackage + '\n') function resolve (dir) { // Find matching "prebuilds/-" directory var tuples = readdirSync(path.join(dir, 'prebuilds')).map(parseTuple) var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0] if (!tuple) return return resolveFile(path.join(dir, 'prebuilds', tuple.name)) } function resolveFile (prebuilds) { // Find most specific flavor first var parsed = readdirSync(prebuilds).map(parseTags) var candidates = parsed.filter(matchTags(runtime, abi)) var winner = candidates.sort(compareTags(runtime))[0] if (winner) return path.join(prebuilds, winner.file) } } function readdirSync (dir) { try { return fs.readdirSync(dir) } catch (err) { return [] } } function getFirst (dir, filter) { var files = readdirSync(dir).filter(filter) return files[0] && path.join(dir, files[0]) } function matchBuild (name) { return /\.node$/.test(name) } function parseTuple (name) { // Example: darwin-x64+arm64 var arr = name.split('-') if (arr.length !== 2) return var platform = arr[0] var architectures = arr[1].split('+') if (!platform) return if (!architectures.length) return if (!architectures.every(Boolean)) return return { name, platform, architectures } } function matchTuple (platform, arch) { return function (tuple) { if (tuple == null) return false if (tuple.platform !== platform) return false return tuple.architectures.includes(arch) } } function compareTuples (a, b) { // Prefer single-arch prebuilds over multi-arch return a.architectures.length - b.architectures.length } function parseTags (file) { var arr = file.split('.') var extension = arr.pop() var tags = { file: file, specificity: 0 } if (extension !== 'node') return for (var i = 0; i < arr.length; i++) { var tag = arr[i] if (tag === 'node' || tag === 'electron' || tag === 'node-webkit') { tags.runtime = tag } else if (tag === 'napi') { tags.napi = true } else if (tag.slice(0, 3) === 'abi') { tags.abi = tag.slice(3) } else if (tag.slice(0, 2) === 'uv') { tags.uv = tag.slice(2) } else if (tag.slice(0, 4) === 'armv') { tags.armv = tag.slice(4) } else if (tag === 'glibc' || tag === 'musl') { tags.libc = tag } else { continue } tags.specificity++ } return tags } function matchTags (runtime, abi) { return function (tags) { if (tags == null) return false if (tags.runtime !== runtime && !runtimeAgnostic(tags)) return false if (tags.abi !== abi && !tags.napi) return false if (tags.uv && tags.uv !== uv) return false if (tags.armv && tags.armv !== armv) return false if (tags.libc && tags.libc !== libc) return false return true } } function runtimeAgnostic (tags) { return tags.runtime === 'node' && tags.napi } function compareTags (runtime) { // Precedence: non-agnostic runtime, abi over napi, then by specificity. return function (a, b) { if (a.runtime !== b.runtime) { return a.runtime === runtime ? -1 : 1 } else if (a.abi !== b.abi) { return a.abi ? -1 : 1 } else if (a.specificity !== b.specificity) { return a.specificity > b.specificity ? -1 : 1 } else { return 0 } } } function isElectron () { if (process.versions && process.versions.electron) return true if (process.env.ELECTRON_RUN_AS_NODE) return true return typeof window !== 'undefined' && window.process && window.process.type === 'renderer' } function isMusl (platform) { if (platform !== 'linux') return false; const { familySync, MUSL } = require('detect-libc'); return familySync() === MUSL; } // Exposed for unit tests // TODO: move to lib load.parseTags = parseTags load.matchTags = matchTags load.compareTags = compareTags load.parseTuple = parseTuple load.matchTuple = matchTuple load.compareTuples = compareTuples