Comparing version 0.0.3 to 0.0.4
227
lib/loady.js
/*! | ||
* loady.js - dynamic loader for node.js | ||
* Copyright (c) 2019, Christopher Jeffrey (MIT License). | ||
* Copyright (c) 2019-2020, Christopher Jeffrey (MIT License). | ||
* https://github.com/chjj/loady | ||
* | ||
* Parts of this software are based on TooTallNate/node-bindings: | ||
* Copyright (c) 2012, Nathan Rajlich <nathan@tootallnate.net> | ||
* https://github.com/TooTallNate/node-bindings | ||
*/ | ||
/* global __webpack_require__, __non_webpack_require__ */ | ||
/* eslint camelcase: "off" */ | ||
'use strict'; | ||
const {dirname, extname, join, resolve} = require('path'); | ||
const {existsSync} = require('fs'); | ||
const {versions} = process; | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
@@ -24,160 +16,142 @@ /* | ||
const defaults = { | ||
__proto__: null, | ||
root: '/', | ||
name: 'bindings.node', | ||
arch: process.arch, | ||
compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled', | ||
pregyp: `node-v${versions.modules}-${process.platform}-${process.arch}`, | ||
platform: process.platform, | ||
version: process.versions.node | ||
}; | ||
const paths = [ | ||
// node-gyp's linked version in the "build" dir. | ||
['$root', 'build', '$name'], | ||
// node-waf and gyp_addon (a.k.a node-gyp). | ||
['$root', 'build', 'Debug', '$name'], | ||
['$root', 'build', 'Release', '$name'], | ||
['$root', 'build', 'MinSizeRel', '$name'], | ||
['$root', 'build', 'RelWithDebInfo', '$name'], | ||
// Debug files, for development (legacy behavior). | ||
['$root', 'out', 'Debug', '$name'], | ||
['$root', 'Debug', '$name'], | ||
// Release files, but manually compiled (legacy behavior). | ||
['$root', 'out', 'Release', '$name'], | ||
['$root', 'Release', '$name'], | ||
// Legacy from node-waf, node <= 0.4.x. | ||
['$root', 'build', 'default', '$name'], | ||
// Production "Release" buildtype binary (meh...). | ||
['$root', '$compiled', '$version', '$platform', '$arch', '$name'], | ||
// node-qbs builds | ||
['$root', 'addon-build', 'release', 'install-root', '$name'], | ||
['$root', 'addon-build', 'debug', 'install-root', '$name'], | ||
['$root', 'addon-build', 'default', 'install-root', '$name'], | ||
// node-pre-gyp path ./lib/binding/{node_abi}-{platform}-{arch}. | ||
['$root', 'lib', 'binding', '$pregyp', '$name'] | ||
const types = [ | ||
'Debug', | ||
'Release', | ||
'MinSizeRel', | ||
'RelWithDebInfo' | ||
]; | ||
const cache = Object.create(null); | ||
/** | ||
* Loady | ||
* Resolve | ||
*/ | ||
function loady(name, path) { | ||
function resolve(name, root) { | ||
if (typeof name !== 'string') | ||
throw new TypeError('"name" must be a string.'); | ||
if (typeof path !== 'string') | ||
throw new TypeError('"path" must be a string.'); | ||
if (typeof root !== 'string') | ||
throw new TypeError('"root" must be a string.'); | ||
if (extname(name) !== '.node') | ||
if (!fs.existsSync) | ||
throw moduleError(name); | ||
if (path.extname(name) !== '.node') | ||
name += '.node'; | ||
path = ensurePath(path); | ||
root = ensurePath(root); | ||
const key = `${name}\0${path}`; | ||
const key = `${name}\0${root}`; | ||
if (cache[key]) | ||
return cache[key]; | ||
if (resolve.cache[key]) | ||
return resolve.cache[key]; | ||
const loader = typeof __webpack_require__ === 'function' | ||
? __non_webpack_require__ | ||
: require; | ||
if (isPath(name)) { | ||
const file = path.resolve(root, name); | ||
const tries = []; | ||
const options = Object.create(defaults); | ||
if (!fs.existsSync(file)) | ||
throw moduleError(file); | ||
options.root = getRoot(path); | ||
options.name = name; | ||
const real = realpath(file); | ||
for (const parts of paths) { | ||
const names = []; | ||
resolve.cache[key] = real; | ||
for (const part of parts) { | ||
if (part[0] === '$') | ||
names.push(options[part.substring(1)]); | ||
else | ||
names.push(part); | ||
return real; | ||
} | ||
for (;;) { | ||
const build = path.join(root, 'build'); | ||
if (fs.existsSync(build)) { | ||
const files = [path.join(build, name)]; | ||
for (const type of types) | ||
files.push(path.join(build, type, name)); | ||
for (const file of files) { | ||
if (fs.existsSync(file)) { | ||
const real = realpath(file); | ||
resolve.cache[key] = real; | ||
return real; | ||
} | ||
} | ||
} | ||
const file = join(...names); | ||
const next = path.dirname(root); | ||
tries.push(file); | ||
if (next === root) | ||
break; | ||
let binding = null; | ||
root = next; | ||
} | ||
try { | ||
binding = loader(file); | ||
} catch (e) { | ||
if (e.code === 'MODULE_NOT_FOUND') | ||
continue; | ||
throw moduleError(name); | ||
} | ||
if (e.code === 'QUALIFIED_PATH_RESOLUTION_FAILED') | ||
continue; | ||
resolve.cache = Object.create(null); | ||
if (/not find/i.test(e.message)) | ||
continue; | ||
/** | ||
* Load | ||
*/ | ||
throw e; | ||
} | ||
function load(name, root) { | ||
const file = resolve(name, root); | ||
cache[key] = binding; | ||
if (load.cache[file]) | ||
return load.cache[file]; | ||
return binding; | ||
} | ||
if (!process.dlopen) | ||
throw new Error(`${name}: cannot open shared object file`); | ||
const msg = tries.map(file => ` - ${file}`).join('\n'); | ||
const err = new Error(`Could not locate the bindings file. Tried:\n${msg}`); | ||
const module = { exports: {} }; | ||
err.code = 'ERR_BINDINGS_NOT_FOUND'; | ||
err.tries = tries; | ||
process.dlopen(module, file); | ||
throw err; | ||
load.cache[file] = module.exports; | ||
return module.exports; | ||
} | ||
function getRoot(path) { | ||
let root = resolve(path); | ||
load.cache = Object.create(null); | ||
for (;;) { | ||
if (existsSync(join(root, 'package.json')) | ||
|| existsSync(join(root, 'node_modules'))) { | ||
break; | ||
} | ||
/* | ||
* Helpers | ||
*/ | ||
const next = dirname(root); | ||
function isPath(str) { | ||
if (process.platform === 'win32') | ||
str = str.replace('\\', '/'); | ||
if (next === root) { | ||
const err = new Error(`Could not find module root given file: "${path}". ` | ||
+ 'Do you have a `package.json` file?'); | ||
err.code = 'ERR_BINDINGS_NO_ROOT'; | ||
err.path = path; | ||
throw err; | ||
} | ||
root = next; | ||
} | ||
return root; | ||
return str[0] === '/' | ||
|| str.startsWith('./') | ||
|| str.startsWith('../'); | ||
} | ||
function ensurePath(path) { | ||
if (path.indexOf('file:') === 0) { | ||
const {fileURLToPath} = require('url'); | ||
function ensurePath(str) { | ||
if (str.indexOf('file:') === 0) { | ||
const url = require('url'); | ||
if (!fileURLToPath) { | ||
const err = new Error('File URLs are unsupported on this platform.'); | ||
err.code = 'ERR_BINDINGS_FILE_URI'; | ||
err.url = path; | ||
throw err; | ||
} | ||
if (!url.fileURLToPath) | ||
throw new Error('File URLs are unsupported on this platform.'); | ||
// Assume this is an import.meta.url. | ||
return dirname(fileURLToPath(path)); | ||
return path.resolve(url.fileURLToPath(str), '..'); | ||
} | ||
return path; | ||
return path.resolve(str); | ||
} | ||
function realpath(file) { | ||
try { | ||
return fs.realpathSync(file); | ||
} catch (e) { | ||
return path.resolve(file); | ||
} | ||
} | ||
function moduleError(name) { | ||
const err = new Error(`Cannot find module '${name}'`); | ||
err.code = 'MODULE_NOT_FOUND'; | ||
throw err; | ||
} | ||
/* | ||
@@ -187,2 +161,5 @@ * Expose | ||
module.exports = loady; | ||
load.load = load; | ||
load.resolve = resolve; | ||
module.exports = load; |
{ | ||
"name": "loady", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"description": "dynamic loader for node.js", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -29,3 +29,3 @@ # loady | ||
- Copyright (c) 2019, Christopher Jeffrey (MIT License). | ||
- Copyright (c) 2019-2020, Christopher Jeffrey (MIT License). | ||
@@ -32,0 +32,0 @@ See LICENSE for more info. |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
2
5208
116
1