1+ /**
2+ * Module dependencies.
3+ */
4+
5+ import fs from 'fs' ;
6+ import path from 'path' ;
7+ import { fileURLToPath } from 'url' ;
8+ import { createRequire } from 'module' ;
9+
10+ const require = createRequire ( import . meta. url ) ;
11+
12+ const join = path . join ;
13+ const dirname = path . dirname ;
14+ const exists =
15+ ( fs . accessSync &&
16+ function ( path ) {
17+ try {
18+ fs . accessSync ( path ) ;
19+ } catch ( e ) {
20+ return false ;
21+ }
22+ return true ;
23+ } ) ||
24+ fs . existsSync ||
25+ path . existsSync ;
26+
27+ const defaults = {
28+ arrow : process . env . NODE_BINDINGS_ARROW || ' → ' ,
29+ compiled : process . env . NODE_BINDINGS_COMPILED_DIR || 'compiled' ,
30+ platform : process . platform ,
31+ arch : process . arch ,
32+ nodePreGyp :
33+ 'node-v' +
34+ process . versions . modules +
35+ '-' +
36+ process . platform +
37+ '-' +
38+ process . arch ,
39+ version : process . versions . node ,
40+ bindings : 'bindings.node' ,
41+ try : [
42+ // node-gyp's linked version in the "build" dir
43+ [ 'module_root' , 'build' , 'bindings' ] ,
44+ // node-waf and gyp_addon (a.k.a node-gyp)
45+ [ 'module_root' , 'build' , 'Debug' , 'bindings' ] ,
46+ [ 'module_root' , 'build' , 'Release' , 'bindings' ] ,
47+ // Debug files, for development (legacy behavior, remove for node v0.9)
48+ [ 'module_root' , 'out' , 'Debug' , 'bindings' ] ,
49+ [ 'module_root' , 'Debug' , 'bindings' ] ,
50+ // Release files, but manually compiled (legacy behavior, remove for node v0.9)
51+ [ 'module_root' , 'out' , 'Release' , 'bindings' ] ,
52+ [ 'module_root' , 'Release' , 'bindings' ] ,
53+ // Legacy from node-waf, node <= 0.4.x
54+ [ 'module_root' , 'build' , 'default' , 'bindings' ] ,
55+ // Production "Release" buildtype binary (meh...)
56+ [ 'module_root' , 'compiled' , 'version' , 'platform' , 'arch' , 'bindings' ] ,
57+ // node-qbs builds
58+ [ 'module_root' , 'addon-build' , 'release' , 'install-root' , 'bindings' ] ,
59+ [ 'module_root' , 'addon-build' , 'debug' , 'install-root' , 'bindings' ] ,
60+ [ 'module_root' , 'addon-build' , 'default' , 'install-root' , 'bindings' ] ,
61+ // node-pre-gyp path ./lib/binding/{node_abi}-{platform}-{arch}
62+ [ 'module_root' , 'lib' , 'binding' , 'nodePreGyp' , 'bindings' ]
63+ ]
64+ } ;
65+
66+ /**
67+ * The main `bindings()` function loads the compiled bindings for a given module.
68+ * It uses V8's Error API to determine the parent filename that this function is
69+ * being invoked from, which is then used to find the root directory.
70+ */
71+
72+ function bindings ( opts ) {
73+ // Argument surgery
74+ if ( typeof opts == 'string' ) {
75+ opts = { bindings : opts } ;
76+ } else if ( ! opts ) {
77+ opts = { } ;
78+ }
79+
80+ // maps `defaults` onto `opts` object
81+ Object . keys ( defaults ) . map ( function ( i ) {
82+ if ( ! ( i in opts ) ) opts [ i ] = defaults [ i ] ;
83+ } ) ;
84+
85+ // Get the module root
86+ if ( ! opts . module_root ) {
87+ opts . module_root = getRoot ( getFileName ( ) ) ;
88+ }
89+
90+ // Ensure the given bindings name ends with .node
91+ if ( path . extname ( opts . bindings ) != '.node' ) {
92+ opts . bindings += '.node' ;
93+ }
94+
95+ // https://github.com/webpack/webpack/issues/4175#issuecomment-342931035
96+ const requireFunc =
97+ typeof __webpack_require__ === 'function'
98+ ? __non_webpack_require__
99+ : require ;
100+
101+ const tries = [ ] ;
102+ let i = 0 ;
103+ const l = opts . try . length ;
104+ let n , b , err ;
105+
106+ for ( ; i < l ; i ++ ) {
107+ n = join . apply (
108+ null ,
109+ opts . try [ i ] . map ( function ( p ) {
110+ return opts [ p ] || p ;
111+ } )
112+ ) ;
113+ tries . push ( n ) ;
114+ try {
115+ b = opts . path ? requireFunc . resolve ( n ) : requireFunc ( n ) ;
116+ if ( ! opts . path ) {
117+ b . path = n ;
118+ }
119+ return b ;
120+ } catch ( e ) {
121+ if (
122+ e . code !== 'MODULE_NOT_FOUND' &&
123+ e . code !== 'QUALIFIED_PATH_RESOLUTION_FAILED' &&
124+ ! / n o t f i n d / i. test ( e . message )
125+ ) {
126+ throw e ;
127+ }
128+ }
129+ }
130+
131+ err = new Error (
132+ 'Could not locate the bindings file. Tried:\n' +
133+ tries
134+ . map ( function ( a ) {
135+ return opts . arrow + a ;
136+ } )
137+ . join ( '\n' )
138+ ) ;
139+ err . tries = tries ;
140+ throw err ;
141+ }
142+
143+ /**
144+ * Gets the filename of the JavaScript file that invokes this function.
145+ * Used to help find the root directory of a module.
146+ * Optionally accepts an filename argument to skip when searching for the invoking filename
147+ */
148+
149+ function getFileName ( calling_file ) {
150+ const origPST = Error . prepareStackTrace ;
151+ const origSTL = Error . stackTraceLimit ;
152+ const dummy = { } ;
153+ let fileName ;
154+
155+ Error . stackTraceLimit = 10 ;
156+
157+ Error . prepareStackTrace = function ( e , st ) {
158+ for ( let i = 0 , l = st . length ; i < l ; i ++ ) {
159+ fileName = st [ i ] . getFileName ( ) ;
160+ if ( fileName !== import . meta. url ) {
161+ if ( calling_file ) {
162+ if ( fileName !== calling_file ) {
163+ return ;
164+ }
165+ } else {
166+ return ;
167+ }
168+ }
169+ }
170+ } ;
171+
172+ // run the 'prepareStackTrace' function above
173+ Error . captureStackTrace ( dummy ) ;
174+ dummy . stack ;
175+
176+ // cleanup
177+ Error . prepareStackTrace = origPST ;
178+ Error . stackTraceLimit = origSTL ;
179+
180+ // handle filename that starts with "file://"
181+ const fileSchema = 'file://' ;
182+ if ( fileName && fileName . indexOf ( fileSchema ) === 0 ) {
183+ fileName = fileURLToPath ( fileName ) ;
184+ }
185+
186+ return fileName ;
187+ }
188+
189+ /**
190+ * Gets the root directory of a module, given an arbitrary filename
191+ * somewhere in the module tree. The "root directory" is the directory
192+ * containing the `package.json` file.
193+ *
194+ * In: /home/nate/node-native-module/lib/index.js
195+ * Out: /home/nate/node-native-module
196+ */
197+
198+ function getRoot ( file ) {
199+ let dir = dirname ( file ) ;
200+ let prev ;
201+ while ( true ) {
202+ if ( dir === '.' ) {
203+ // Avoids an infinite loop in rare cases, like the REPL
204+ dir = process . cwd ( ) ;
205+ }
206+ if (
207+ exists ( join ( dir , 'package.json' ) ) ||
208+ exists ( join ( dir , 'node_modules' ) )
209+ ) {
210+ // Found the 'package.json' file or 'node_modules' dir; we're done
211+ return dir ;
212+ }
213+ if ( prev === dir ) {
214+ // Got to the top
215+ throw new Error (
216+ 'Could not find module root given file: "' +
217+ file +
218+ '". Do you have a `package.json` file? '
219+ ) ;
220+ }
221+ // Try the parent dir next
222+ prev = dir ;
223+ dir = join ( dir , '..' ) ;
224+ }
225+ }
226+
227+ export default bindings ;
228+ export { getFileName , getRoot } ;
0 commit comments