UNPKG

6.05 kBJavaScriptView Raw
1var fs = require('fs')
2var path = require('path')
3var os = require('os')
4
5// Workaround to fix webpack's build warnings: 'the request of a dependency is an expression'
6var runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require // eslint-disable-line
7
8var vars = (process.config && process.config.variables) || {}
9var prebuildsOnly = !!process.env.PREBUILDS_ONLY
10var abi = process.versions.modules // TODO: support old node where this is undef
11var runtime = isElectron() ? 'electron' : (isNwjs() ? 'node-webkit' : 'node')
12
13var arch = process.env.npm_config_arch || os.arch()
14var platform = process.env.npm_config_platform || os.platform()
15var libc = process.env.LIBC || (isAlpine(platform) ? 'musl' : 'glibc')
16var armv = process.env.ARM_VERSION || (arch === 'arm64' ? '8' : vars.arm_version) || ''
17var uv = (process.versions.uv || '').split('.')[0]
18
19module.exports = load
20
21function load (dir) {
22 return runtimeRequire(load.resolve(dir))
23}
24
25load.resolve = load.path = function (dir) {
26 dir = path.resolve(dir || '.')
27
28 try {
29 var name = runtimeRequire(path.join(dir, 'package.json')).name.toUpperCase().replace(/-/g, '_')
30 if (process.env[name + '_PREBUILD']) dir = process.env[name + '_PREBUILD']
31 } catch (err) {}
32
33 if (!prebuildsOnly) {
34 var release = getFirst(path.join(dir, 'build/Release'), matchBuild)
35 if (release) return release
36
37 var debug = getFirst(path.join(dir, 'build/Debug'), matchBuild)
38 if (debug) return debug
39 }
40
41 var prebuild = resolve(dir)
42 if (prebuild) return prebuild
43
44 var nearby = resolve(path.dirname(process.execPath))
45 if (nearby) return nearby
46
47 var target = [
48 'platform=' + platform,
49 'arch=' + arch,
50 'runtime=' + runtime,
51 'abi=' + abi,
52 'uv=' + uv,
53 armv ? 'armv=' + armv : '',
54 'libc=' + libc,
55 'node=' + process.versions.node,
56 process.versions.electron ? 'electron=' + process.versions.electron : '',
57 typeof __webpack_require__ === 'function' ? 'webpack=true' : '' // eslint-disable-line
58 ].filter(Boolean).join(' ')
59
60 throw new Error('No native build was found for ' + target + '\n loaded from: ' + dir + '\n')
61
62 function resolve (dir) {
63 // Find matching "prebuilds/<platform>-<arch>" directory
64 var tuples = readdirSync(path.join(dir, 'prebuilds')).map(parseTuple)
65 var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0]
66 if (!tuple) return
67
68 // Find most specific flavor first
69 var prebuilds = path.join(dir, 'prebuilds', tuple.name)
70 var parsed = readdirSync(prebuilds).map(parseTags)
71 var candidates = parsed.filter(matchTags(runtime, abi))
72 var winner = candidates.sort(compareTags(runtime))[0]
73 if (winner) return path.join(prebuilds, winner.file)
74 }
75}
76
77function readdirSync (dir) {
78 try {
79 return fs.readdirSync(dir)
80 } catch (err) {
81 return []
82 }
83}
84
85function getFirst (dir, filter) {
86 var files = readdirSync(dir).filter(filter)
87 return files[0] && path.join(dir, files[0])
88}
89
90function matchBuild (name) {
91 return /\.node$/.test(name)
92}
93
94function parseTuple (name) {
95 // Example: darwin-x64+arm64
96 var arr = name.split('-')
97 if (arr.length !== 2) return
98
99 var platform = arr[0]
100 var architectures = arr[1].split('+')
101
102 if (!platform) return
103 if (!architectures.length) return
104 if (!architectures.every(Boolean)) return
105
106 return { name, platform, architectures }
107}
108
109function matchTuple (platform, arch) {
110 return function (tuple) {
111 if (tuple == null) return false
112 if (tuple.platform !== platform) return false
113 return tuple.architectures.includes(arch)
114 }
115}
116
117function compareTuples (a, b) {
118 // Prefer single-arch prebuilds over multi-arch
119 return a.architectures.length - b.architectures.length
120}
121
122function parseTags (file) {
123 var arr = file.split('.')
124 var extension = arr.pop()
125 var tags = { file: file, specificity: 0 }
126
127 if (extension !== 'node') return
128
129 for (var i = 0; i < arr.length; i++) {
130 var tag = arr[i]
131
132 if (tag === 'node' || tag === 'electron' || tag === 'node-webkit') {
133 tags.runtime = tag
134 } else if (tag === 'napi') {
135 tags.napi = true
136 } else if (tag.slice(0, 3) === 'abi') {
137 tags.abi = tag.slice(3)
138 } else if (tag.slice(0, 2) === 'uv') {
139 tags.uv = tag.slice(2)
140 } else if (tag.slice(0, 4) === 'armv') {
141 tags.armv = tag.slice(4)
142 } else if (tag === 'glibc' || tag === 'musl') {
143 tags.libc = tag
144 } else {
145 continue
146 }
147
148 tags.specificity++
149 }
150
151 return tags
152}
153
154function matchTags (runtime, abi) {
155 return function (tags) {
156 if (tags == null) return false
157 if (tags.runtime !== runtime && !runtimeAgnostic(tags)) return false
158 if (tags.abi !== abi && !tags.napi) return false
159 if (tags.uv && tags.uv !== uv) return false
160 if (tags.armv && tags.armv !== armv) return false
161 if (tags.libc && tags.libc !== libc) return false
162
163 return true
164 }
165}
166
167function runtimeAgnostic (tags) {
168 return tags.runtime === 'node' && tags.napi
169}
170
171function compareTags (runtime) {
172 // Precedence: non-agnostic runtime, abi over napi, then by specificity.
173 return function (a, b) {
174 if (a.runtime !== b.runtime) {
175 return a.runtime === runtime ? -1 : 1
176 } else if (a.abi !== b.abi) {
177 return a.abi ? -1 : 1
178 } else if (a.specificity !== b.specificity) {
179 return a.specificity > b.specificity ? -1 : 1
180 } else {
181 return 0
182 }
183 }
184}
185
186function isNwjs () {
187 return !!(process.versions && process.versions.nw)
188}
189
190function isElectron () {
191 if (process.versions && process.versions.electron) return true
192 if (process.env.ELECTRON_RUN_AS_NODE) return true
193 return typeof window !== 'undefined' && window.process && window.process.type === 'renderer'
194}
195
196function isAlpine (platform) {
197 return platform === 'linux' && fs.existsSync('/etc/alpine-release')
198}
199
200// Exposed for unit tests
201// TODO: move to lib
202load.parseTags = parseTags
203load.matchTags = matchTags
204load.compareTags = compareTags
205load.parseTuple = parseTuple
206load.matchTuple = matchTuple
207load.compareTuples = compareTuples