var fs = require('fs') var path = require('path') var os = require('os') // Workaround to fix webpack's build warnings: 'the request of a dependency is an expression' var runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require // eslint-disable-line var vars = (process.config && process.config.variables) || {} var prebuildsOnly = !!process.env.PREBUILDS_ONLY var abi = process.versions.modules // TODO: support old node where this is undef var runtime = isElectron() ? 'electron' : 'node' var arch = os.arch() var platform = os.platform() var libc = process.env.LIBC || (isAlpine(platform) ? 'musl' : 'glibc') var armv = process.env.ARM_VERSION || (arch === 'arm64' ? '8' : vars.arm_version) || '' var uv = (process.versions.uv || '').split('.')[0] module.exports = load function load (dir) { return runtimeRequire(load.path(dir)) } load.path = function (dir) { dir = path.resolve(dir || '.') try { var name = runtimeRequire(path.join(dir, 'package.json')).name.toUpperCase().replace(/-/g, '_') if (process.env[name + '_PREBUILD']) dir = process.env[name + '_PREBUILD'] } 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 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 loaded from: ' + dir + '\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 // Find most specific flavor first var prebuilds = path.join(dir, 'prebuilds', tuple.name) 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 isAlpine (platform) { return platform === 'linux' && fs.existsSync('/etc/alpine-release') } // 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