UNPKG

4.61 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' : 'node'
12var arch = os.arch()
13var platform = os.platform()
14var libc = process.env.LIBC || (isAlpine(platform) ? 'musl' : 'glibc')
15var armv = process.env.ARM_VERSION || (arch === 'arm64' ? '8' : vars.arm_version) || ''
16var uv = (process.versions.uv || '').split('.')[0]
17
18module.exports = load
19
20function load (dir) {
21 return runtimeRequire(load.path(dir))
22}
23
24load.path = function (dir) {
25 dir = path.resolve(dir || '.')
26
27 try {
28 var name = runtimeRequire(path.join(dir, 'package.json')).name.toUpperCase().replace(/-/g, '_')
29 if (process.env[name + '_PREBUILD']) dir = process.env[name + '_PREBUILD']
30 } catch (err) {}
31
32 if (!prebuildsOnly) {
33 var release = getFirst(path.join(dir, 'build/Release'), matchBuild)
34 if (release) return release
35
36 var debug = getFirst(path.join(dir, 'build/Debug'), matchBuild)
37 if (debug) return debug
38 }
39
40 var prebuild = resolve(dir)
41 if (prebuild) return prebuild
42
43 var nearby = resolve(path.dirname(process.execPath))
44 if (nearby) return nearby
45
46 var target = [
47 'platform=' + platform,
48 'arch=' + arch,
49 'runtime=' + runtime,
50 'abi=' + abi,
51 'uv=' + uv,
52 armv ? 'armv=' + armv : '',
53 'libc=' + libc
54 ].filter(Boolean).join(' ')
55
56 throw new Error('No native build was found for ' + target)
57
58 function resolve (dir) {
59 // Find most specific flavor first
60 var prebuilds = path.join(dir, 'prebuilds', platform + '-' + arch)
61 var parsed = readdirSync(prebuilds).map(parseTags)
62 var candidates = parsed.filter(matchTags(runtime, abi))
63 var winner = candidates.sort(compareTags(runtime))[0]
64 if (winner) return path.join(prebuilds, winner.file)
65 }
66}
67
68function readdirSync (dir) {
69 try {
70 return fs.readdirSync(dir)
71 } catch (err) {
72 return []
73 }
74}
75
76function getFirst (dir, filter) {
77 var files = readdirSync(dir).filter(filter)
78 return files[0] && path.join(dir, files[0])
79}
80
81function matchBuild (name) {
82 return /\.node$/.test(name)
83}
84
85function parseTags (file) {
86 var arr = file.split('.')
87 var extension = arr.pop()
88 var tags = { file: file, specificity: 0 }
89
90 if (extension !== 'node') return
91
92 for (var i = 0; i < arr.length; i++) {
93 var tag = arr[i]
94
95 if (tag === 'node' || tag === 'electron' || tag === 'node-webkit') {
96 tags.runtime = tag
97 } else if (tag === 'napi') {
98 tags.napi = true
99 } else if (tag.slice(0, 3) === 'abi') {
100 tags.abi = tag.slice(3)
101 } else if (tag.slice(0, 2) === 'uv') {
102 tags.uv = tag.slice(2)
103 } else if (tag.slice(0, 4) === 'armv') {
104 tags.armv = tag.slice(4)
105 } else if (tag === 'glibc' || tag === 'musl') {
106 tags.libc = tag
107 } else {
108 continue
109 }
110
111 tags.specificity++
112 }
113
114 return tags
115}
116
117function matchTags (runtime, abi) {
118 return function (tags) {
119 if (tags == null) return false
120 if (tags.runtime !== runtime && !runtimeAgnostic(tags)) return false
121 if (tags.abi !== abi && !tags.napi) return false
122 if (tags.uv && tags.uv !== uv) return false
123 if (tags.armv && tags.armv !== armv) return false
124 if (tags.libc && tags.libc !== libc) return false
125
126 return true
127 }
128}
129
130function runtimeAgnostic (tags) {
131 return tags.runtime === 'node' && tags.napi
132}
133
134function compareTags (runtime) {
135 // Precedence: non-agnostic runtime, abi over napi, then by specificity.
136 return function (a, b) {
137 if (a.runtime !== b.runtime) {
138 return a.runtime === runtime ? -1 : 1
139 } else if (a.abi !== b.abi) {
140 return a.abi ? -1 : 1
141 } else if (a.specificity !== b.specificity) {
142 return a.specificity > b.specificity ? -1 : 1
143 } else {
144 return 0
145 }
146 }
147}
148
149function isElectron () {
150 if (process.versions && process.versions.electron) return true
151 if (process.env.ELECTRON_RUN_AS_NODE) return true
152 return typeof window !== 'undefined' && window.process && window.process.type === 'renderer'
153}
154
155function isAlpine (platform) {
156 return platform === 'linux' && fs.existsSync('/etc/alpine-release')
157}
158
159// Exposed for unit tests
160// TODO: move to lib
161load.parseTags = parseTags
162load.matchTags = matchTags
163load.compareTags = compareTags