load-plugin
Advanced tools
Comparing version 3.0.0 to 4.0.0
194
index.js
@@ -1,22 +0,30 @@ | ||
'use strict' | ||
/** | ||
* @typedef ResolveOptions | ||
* @property {string} [prefix] | ||
* @property {string|string[]} [cwd] | ||
* @property {boolean} [global] | ||
*/ | ||
var fs = require('fs') | ||
var path = require('path') | ||
var resolve = require('resolve-from').silent | ||
var readNpmConfig = require('libnpmconfig').read | ||
/** | ||
* @typedef {ResolveOptions & {key?: string|false}} LoadOptions | ||
*/ | ||
module.exports = loadPlugin | ||
loadPlugin.resolve = resolvePlugin | ||
import fs from 'fs' | ||
import {pathToFileURL, fileURLToPath} from 'url' | ||
import path from 'path' | ||
import {resolve as esmResolve} from 'import-meta-resolve' | ||
import libNpmConfig from 'libnpmconfig' | ||
var electron = process.versions.electron !== undefined | ||
var windows = process.platform === 'win32' | ||
const electron = process.versions.electron !== undefined | ||
const windows = process.platform === 'win32' | ||
var argv = process.argv[1] || /* istanbul ignore next */ '' | ||
var nvm = process.env.NVM_BIN | ||
var appData = process.env.APPDATA | ||
const argv = process.argv[1] || /* c8 ignore next */ '' | ||
const nvm = process.env.NVM_BIN | ||
const appData = process.env.APPDATA | ||
/* istanbul ignore next */ | ||
var globalsLibrary = windows ? '' : 'lib' | ||
/* c8 ignore next */ | ||
const globalsLibrary = windows ? '' : 'lib' | ||
var builtinNpmConfig | ||
/** @type {{prefix?: string}} */ | ||
let builtinNpmConfig | ||
@@ -26,3 +34,3 @@ // The prefix config defaults to the location where node is installed. | ||
// pass to `libnpmconfig` explicitly: | ||
/* istanbul ignore next */ | ||
/* c8 ignore next 4 */ | ||
if (windows && appData) { | ||
@@ -32,7 +40,12 @@ builtinNpmConfig = {prefix: path.join(appData, 'npm')} | ||
var npmPrefix = readNpmConfig(null, builtinNpmConfig).prefix | ||
/** | ||
* Note: `libnpmconfig` uses `figgy-pudding` which is slated for archival. | ||
* Either `libnpmconfig` will switch to an alternative or we’ll have to. | ||
* @type {string} | ||
*/ | ||
let npmPrefix = libNpmConfig.read(null, builtinNpmConfig).prefix | ||
// If there is no prefix defined, use the defaults | ||
// See: <https://github.com/eush77/npm-prefix/blob/master/index.js> | ||
/* istanbul ignore next */ | ||
/* c8 ignore next 5 */ | ||
if (!npmPrefix) { | ||
@@ -44,4 +57,4 @@ npmPrefix = windows | ||
var globally = electron || argv.indexOf(npmPrefix) === 0 | ||
var globals = path.resolve(npmPrefix, globalsLibrary, 'node_modules') | ||
const globalsDefault = electron || argv.indexOf(npmPrefix) === 0 | ||
let globalDir = path.resolve(npmPrefix, globalsLibrary, 'node_modules') | ||
@@ -56,59 +69,71 @@ // If we’re in Electron, we’re running in a modified Node that cannot really | ||
// and detect the actual modules. | ||
/* istanbul ignore next */ | ||
if (electron && nvm && !fs.existsSync(globals)) { | ||
globals = path.resolve(nvm, '..', globalsLibrary, 'node_modules') | ||
/* c8 ignore next 3 */ | ||
if (electron && nvm && !fs.existsSync(globalDir)) { | ||
globalDir = path.resolve(nvm, '..', globalsLibrary, 'node_modules') | ||
} | ||
// Load the plugin found using `resolvePlugin`. | ||
function loadPlugin(name, options) { | ||
return require(resolvePlugin(name, options) || name) | ||
/** | ||
* Load the plugin found using `resolvePlugin`. | ||
* | ||
* @param {string} name The name to import. | ||
* @param {LoadOptions} [options] | ||
* @returns {Promise<unknown>} | ||
*/ | ||
export async function loadPlugin(name, options = {}) { | ||
const {key = 'default', ...rest} = options | ||
const fp = await resolvePlugin(name, rest) | ||
console.log('resolved:', [name, fp, pathToFileURL(fp).href]) | ||
/** @type {Object.<string, unknown>} */ | ||
// Bug with coverage on Node@12. | ||
/* c8 ignore next 3 */ | ||
const mod = await import(pathToFileURL(fp).href) | ||
return key === false ? mod : mod[key] | ||
} | ||
// Find a plugin. | ||
// | ||
// See also: | ||
// <https://docs.npmjs.com/files/folders#node-modules> | ||
// <https://github.com/sindresorhus/resolve-from> | ||
// | ||
// Uses the standard node module loading strategy to find $name in each given | ||
// `cwd` (and optionally the global `node_modules` directory). | ||
// | ||
// If a prefix is given and $name is not a path, `$prefix-$name` is also | ||
// searched (preferring these over non-prefixed modules). | ||
function resolvePlugin(name, options) { | ||
var settings = options || {} | ||
var prefix = settings.prefix | ||
var cwd = settings.cwd | ||
var global = settings.global | ||
var filePath | ||
var sources | ||
var length | ||
var index | ||
var plugin | ||
var slash | ||
var scope = '' | ||
/** | ||
* Find a plugin. | ||
* | ||
* See also: | ||
* * https://docs.npmjs.com/files/folders#node-modules | ||
* * https://github.com/sindresorhus/resolve-from | ||
* | ||
* Uses the standard node module loading strategy to find `$name` in each given | ||
* `cwd` (and optionally the global `node_modules` directory). | ||
* | ||
* If a prefix is given and `$name` is not a path, `$prefix-$name` is also | ||
* searched (preferring these over non-prefixed modules). | ||
* | ||
* @param {string} name | ||
* @param {ResolveOptions} [options] | ||
* @returns {Promise.<string>} | ||
*/ | ||
export async function resolvePlugin(name, options = {}) { | ||
const prefix = options.prefix | ||
? options.prefix + | ||
(options.prefix.charAt(options.prefix.length - 1) === '-' ? '' : '-') | ||
: undefined | ||
const cwd = options.cwd | ||
const globals = | ||
options.global === undefined || options.global === null | ||
? globalsDefault | ||
: options.global | ||
const sources = Array.isArray(cwd) ? cwd.concat() : [cwd || process.cwd()] | ||
/** @type {string} */ | ||
let plugin | ||
/** @type {Error} */ | ||
let lastError | ||
if (global === null || global === undefined) { | ||
global = globally | ||
} | ||
if (cwd && typeof cwd === 'object') { | ||
sources = cwd.concat() | ||
} else { | ||
sources = [cwd || process.cwd()] | ||
} | ||
// Non-path. | ||
if (name.charAt(0) !== '.') { | ||
if (global) { | ||
sources.push(globals) | ||
if (globals) { | ||
sources.push(globalDir) | ||
} | ||
let scope = '' | ||
// Unprefix module. | ||
if (prefix) { | ||
prefix = prefix.charAt(prefix.length - 1) === '-' ? prefix : prefix + '-' | ||
// Scope? | ||
if (name.charAt(0) === '@') { | ||
slash = name.indexOf('/') | ||
const slash = name.indexOf('/') | ||
@@ -132,15 +157,38 @@ // Let’s keep the algorithm simple. | ||
length = sources.length | ||
index = -1 | ||
let index = -1 | ||
/** @type {string} */ | ||
let fp | ||
while (++index < length) { | ||
cwd = sources[index] | ||
filePath = (plugin && resolve(cwd, plugin)) || resolve(cwd, name) | ||
while (++index < sources.length) { | ||
fp = plugin && (await attempt(sources[index], plugin)) | ||
if (fp) return fp | ||
if (filePath) { | ||
return filePath | ||
fp = await attempt(sources[index], name) | ||
if (fp) return fp | ||
} | ||
// There’s always an error. | ||
// Bug with coverage on Node@12. | ||
/* c8 ignore next 8 */ | ||
throw lastError | ||
/** | ||
* @param {string} base | ||
* @param {string} name | ||
* @returns {Promise<string>} | ||
*/ | ||
async function attempt(base, name) { | ||
try { | ||
console.log('try:', [name, base, pathToFileURL(base).href + '/']) | ||
// `import-meta-resolve` resolves from files, whereas `load-plugin` works | ||
// on folders, which is why we add a `/` at the end. | ||
return fileURLToPath( | ||
await esmResolve(name, pathToFileURL(base).href + '/') | ||
) | ||
// Bug with coverage on Node@12. | ||
/* c8 ignore next 1 */ | ||
} catch (error) { | ||
lastError = error | ||
} | ||
} | ||
return null | ||
} |
{ | ||
"name": "load-plugin", | ||
"version": "3.0.0", | ||
"version": "4.0.0", | ||
"description": "Load a submodule, plugin, or file", | ||
@@ -22,26 +22,34 @@ "license": "MIT", | ||
], | ||
"sideEffects": false, | ||
"type": "module", | ||
"main": "index.js", | ||
"browser": "browser.js", | ||
"types": "index.d.ts", | ||
"files": [ | ||
"browser", | ||
"index.d.ts", | ||
"index.js" | ||
], | ||
"dependencies": { | ||
"libnpmconfig": "^1.0.0", | ||
"resolve-from": "^5.0.0" | ||
"import-meta-resolve": "^1.0.0", | ||
"libnpmconfig": "^1.0.0" | ||
}, | ||
"devDependencies": { | ||
"nyc": "^15.0.0", | ||
"prettier": "^1.0.0", | ||
"remark-cli": "^7.0.0", | ||
"remark-lint": "^6.0.0", | ||
"remark-preset-wooorm": "^6.0.0", | ||
"tape": "^4.0.0", | ||
"xo": "^0.25.0" | ||
"@types/tape": "^4.0.0", | ||
"c8": "^7.0.0", | ||
"prettier": "^2.0.0", | ||
"remark-cli": "^9.0.0", | ||
"remark-lint": "^8.0.0", | ||
"remark-preset-wooorm": "^8.0.0", | ||
"rimraf": "^3.0.0", | ||
"tape": "^5.0.0", | ||
"type-coverage": "^2.0.0", | ||
"typescript": "^4.0.0", | ||
"xo": "^0.39.0" | ||
}, | ||
"scripts": { | ||
"format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", | ||
"test-api": "node test", | ||
"test-coverage": "nyc --reporter lcov tape test/index.js", | ||
"test": "npm run format && npm run test-coverage" | ||
"prepack": "npm run build && npm run format", | ||
"build": "rimraf \"{test/**,}*.d.ts\" && tsc && type-coverage", | ||
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", | ||
"test-api": "node test/index.js", | ||
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test/index.js", | ||
"test": "npm run build && npm run format && npm run test-coverage" | ||
}, | ||
@@ -58,10 +66,6 @@ "prettier": { | ||
"prettier": true, | ||
"esnext": false | ||
"rules": { | ||
"no-await-in-loop": "off" | ||
} | ||
}, | ||
"nyc": { | ||
"check-coverage": true, | ||
"lines": 100, | ||
"functions": 100, | ||
"branches": 100 | ||
}, | ||
"remarkConfig": { | ||
@@ -71,3 +75,9 @@ "plugins": [ | ||
] | ||
}, | ||
"typeCoverage": { | ||
"atLeast": 100, | ||
"detail": true, | ||
"strict": true, | ||
"ignoreCatch": true | ||
} | ||
} |
@@ -13,2 +13,5 @@ # load-plugin | ||
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): | ||
Node 12+ is needed to use it and it must be `import`ed instead of `require`d. | ||
[npm][]: | ||
@@ -20,5 +23,2 @@ | ||
When bundled for the browser, a small [file][browser] is included to warn that, | ||
when any of the below functions are invoked, the action is unsupported. | ||
## Use | ||
@@ -28,16 +28,20 @@ | ||
```javascript | ||
var load = require('load-plugin') | ||
```js | ||
import {loadPlugin, resolvePlugin} from 'load-plugin' | ||
load.resolve('lint', {prefix: 'remark'}) | ||
// => '/Users/tilde/projects/oss/load-plugin/node_modules/remark-lint/index.js' | ||
main() | ||
load.resolve('@babel/function-name', {prefix: 'helper'}) | ||
// => '/Users/tilde/projects/oss/load-plugin/node_modules/@babel/helper-function-name/index.js' | ||
async function main() { | ||
await resolvePlugin('lint', {prefix: 'remark'}) | ||
// => '/Users/tilde/projects/oss/load-plugin/node_modules/remark-lint/index.js' | ||
load.resolve('./index.js', {prefix: 'remark'}) | ||
// => '/Users/tilde/projects/oss/load-plugin/index.js' | ||
await resolvePlugin('@babel/function-name', {prefix: 'helper'}) | ||
// => '/Users/tilde/projects/oss/load-plugin/node_modules/@babel/helper-function-name/lib/index.js' | ||
load.require('lint', {prefix: 'remark'}) | ||
// => [Function: lint] | ||
await resolvePlugin('./index.js', {prefix: 'remark'}) | ||
// => '/Users/tilde/projects/oss/load-plugin/index.js' | ||
await loadPlugin('lint', {prefix: 'remark'}) | ||
// => [Function: lint] | ||
} | ||
``` | ||
@@ -47,11 +51,16 @@ | ||
This package exports the following identifiers: `loadPlugin`, `resolvePlugin`. | ||
There is no default export. | ||
### `loadPlugin(name[, options])` | ||
Uses the standard node module loading strategy to require `name` in each given | ||
`cwd` (and optionally the global `node_modules` directory). | ||
Uses Node’s [resolution algorithm][algo] (through | ||
[`import-meta-resolve`][import-meta-resolve]) to load CJS and ESM packages and | ||
files to import `name` in each given `cwd` (and optionally the global | ||
`node_modules` directory). | ||
If a prefix is given and `name` is not a path, `prefix-name` is also searched | ||
(preferring these over non-prefixed modules). | ||
If name starts with a scope (`@scope/name`), the prefix is applied after it: | ||
`@scope/prefix-name`. | ||
If a `prefix` is given and `name` is not a path, `$prefix-$name` is also | ||
searched (preferring these over non-prefixed modules). | ||
If `name` starts with a scope (`@scope/name`), the prefix is applied after it: | ||
`@scope/$prefix-name`. | ||
@@ -73,5 +82,5 @@ ##### `options` | ||
defaults to whether global is detected). | ||
If this is nully, `load-plugin` will detect if it’s currently running in global | ||
mode: either because it’s in Electron, or because a globally installed package | ||
is running it. | ||
If this is nullish, `load-plugin` will detect if it’s currently running in | ||
global mode: either because it’s in Electron, or because a globally installed | ||
package is running it. | ||
@@ -82,9 +91,15 @@ Note: Electron runs its own version of Node instead of your system Node. | ||
###### Returns | ||
###### `options.key` | ||
The results of `require`ing the first path that exists. | ||
Identifier to take from the exports (`string` or `false`, default: `'default'`). | ||
For example when given `'whatever'`, the value of `export const whatever = 1` | ||
will be returned, when given `'default'`, the value of `export default …` is | ||
used, and when `false` the whole module object is returned. | ||
###### Throws | ||
###### Returns | ||
If `require`ing an existing path fails, or if no existing path exists. | ||
`Promise.<unknown>` — Promise yielding the results of `require`ing the first | ||
path that exists. | ||
The promise rejects if `require`ing an existing path fails, or if no existing | ||
path exists. | ||
@@ -94,5 +109,6 @@ ### `loadPlugin.resolve(name[, options])` | ||
Search for `name`. | ||
Accepts the same parameters as [`loadPlugin`][load-plugin] but returns an | ||
absolute path for `name` instead of requiring it, and `null` if it cannot be | ||
found. | ||
Accepts the same parameters as [`loadPlugin`][load-plugin] (except `key`) but | ||
returns a promise resolving to an absolute path for `name` instead of importing | ||
it. | ||
Throws if `name` cannot be found. | ||
@@ -105,5 +121,5 @@ ## License | ||
[build-badge]: https://img.shields.io/travis/wooorm/load-plugin.svg | ||
[build-badge]: https://github.com/wooorm/load-plugin/actions/workflows/main.yml/badge.svg | ||
[build]: https://travis-ci.org/wooorm/load-plugin | ||
[build]: https://github.com/wooorm/load-plugin/actions | ||
@@ -128,4 +144,2 @@ [coverage-badge]: https://img.shields.io/codecov/c/github/wooorm/load-plugin.svg | ||
[browser]: browser.js | ||
[prefix]: https://docs.npmjs.com/misc/config#prefix | ||
@@ -136,1 +150,5 @@ | ||
[nvm]: https://github.com/creationix/nvm | ||
[algo]: https://nodejs.org/api/esm.html#esm_resolution_algorithm | ||
[import-meta-resolve]: https://github.com/wooorm/import-meta-resolve |
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
13961
205
145
4
Yes
11
1
+ Addedimport-meta-resolve@^1.0.0
+ Addedbuiltins@4.1.0(transitive)
+ Addedimport-meta-resolve@1.1.1(transitive)
+ Addedsemver@7.6.3(transitive)
- Removedresolve-from@^5.0.0
- Removedresolve-from@5.0.0(transitive)