2025/2/8第一次更新

This commit is contained in:
爱吃咸鱼小猫咪
2025-02-08 18:50:38 +08:00
commit d7af560866
26519 changed files with 5046029 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 200
[*.js]
block_comment_start = /*
block_comment = *
block_comment_end = */
[*.yml]
indent_size = 1
[package.json]
indent_style = tab
[lib/core.json]
indent_style = tab
[CHANGELOG.md]
indent_style = space
indent_size = 2
[{*.json,Makefile}]
max_line_length = off
[test/{dotdot,resolver,module_dir,multirepo,node_path,pathfilter,precedence}/**/*]
indent_style = off
indent_size = off
max_line_length = off
insert_final_newline = off
+65
View File
@@ -0,0 +1,65 @@
{
"root": true,
"extends": "@ljharb",
"rules": {
"indent": [2, 4],
"strict": 0,
"complexity": 0,
"consistent-return": 0,
"curly": 0,
"dot-notation": [2, { "allowKeywords": true }],
"func-name-matching": 0,
"func-style": 0,
"global-require": 1,
"id-length": [2, { "min": 1, "max": 40 }],
"max-lines": [2, 350],
"max-lines-per-function": 0,
"max-nested-callbacks": 0,
"max-params": 0,
"max-statements-per-line": [2, { "max": 2 }],
"max-statements": 0,
"no-magic-numbers": 0,
"no-shadow": 0,
"no-use-before-define": 0,
"sort-keys": 0,
},
"overrides": [
{
"files": "bin/**",
"rules": {
"no-process-exit": "off",
},
},
{
"files": "example/**",
"rules": {
"no-console": 0,
},
},
{
"files": "test/resolver/nested_symlinks/mylib/*.js",
"rules": {
"no-throw-literal": 0,
},
},
{
"files": "test/**",
"parserOptions": {
"ecmaVersion": 5,
"allowReserved": false,
},
"rules": {
"dot-notation": [2, { "allowPattern": "throws" }],
"max-lines": 0,
"max-lines-per-function": 0,
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
},
},
],
"ignorePatterns": [
"./test/resolver/malformed_package_json/package.json",
],
}
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [ljharb]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: npm/resolve
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2012 James Halliday
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+3
View File
@@ -0,0 +1,3 @@
# Security
Please email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/security if you have a potential security vulnerability to report.
+3
View File
@@ -0,0 +1,3 @@
'use strict';
module.exports = require('./lib/async');
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env node
'use strict';
var path = require('path');
var fs = require('fs');
if (
String(process.env.npm_lifecycle_script).slice(0, 8) !== 'resolve '
&& (
!process.argv
|| process.argv.length < 2
|| (process.argv[1] !== __filename && fs.statSync(process.argv[1]).ino !== fs.statSync(__filename).ino)
|| (process.env.npm_lifecycle_event !== 'npx' && process.env._ && fs.realpathSync(path.resolve(process.env._)) !== __filename)
)
) {
console.error('Error: `resolve` must be run directly as an executable');
process.exit(1);
}
var supportsPreserveSymlinkFlag = require('supports-preserve-symlinks-flag');
var preserveSymlinks = false;
for (var i = 2; i < process.argv.length; i += 1) {
if (process.argv[i].slice(0, 2) === '--') {
if (supportsPreserveSymlinkFlag && process.argv[i] === '--preserve-symlinks') {
preserveSymlinks = true;
} else if (process.argv[i].length > 2) {
console.error('Unknown argument ' + process.argv[i].replace(/[=].*$/, ''));
process.exit(2);
}
process.argv.splice(i, 1);
i -= 1;
if (process.argv[i] === '--') { break; } // eslint-disable-line no-restricted-syntax
}
}
if (process.argv.length < 3) {
console.error('Error: `resolve` expects a specifier');
process.exit(2);
}
var resolve = require('../');
var result = resolve.sync(process.argv[2], {
basedir: process.cwd(),
preserveSymlinks: preserveSymlinks
});
console.log(result);
+5
View File
@@ -0,0 +1,5 @@
var resolve = require('../');
resolve('tap', { basedir: __dirname }, function (err, res) {
if (err) console.error(err);
else console.log(res);
});
+3
View File
@@ -0,0 +1,3 @@
var resolve = require('../');
var res = resolve.sync('tap', { basedir: __dirname });
console.log(res);
+6
View File
@@ -0,0 +1,6 @@
var async = require('./lib/async');
async.core = require('./lib/core');
async.isCore = require('./lib/is-core');
async.sync = require('./lib/sync');
module.exports = async;
+329
View File
@@ -0,0 +1,329 @@
var fs = require('fs');
var getHomedir = require('./homedir');
var path = require('path');
var caller = require('./caller');
var nodeModulesPaths = require('./node-modules-paths');
var normalizeOptions = require('./normalize-options');
var isCore = require('is-core-module');
var realpathFS = process.platform !== 'win32' && fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath;
var homedir = getHomedir();
var defaultPaths = function () {
return [
path.join(homedir, '.node_modules'),
path.join(homedir, '.node_libraries')
];
};
var defaultIsFile = function isFile(file, cb) {
fs.stat(file, function (err, stat) {
if (!err) {
return cb(null, stat.isFile() || stat.isFIFO());
}
if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
return cb(err);
});
};
var defaultIsDir = function isDirectory(dir, cb) {
fs.stat(dir, function (err, stat) {
if (!err) {
return cb(null, stat.isDirectory());
}
if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
return cb(err);
});
};
var defaultRealpath = function realpath(x, cb) {
realpathFS(x, function (realpathErr, realPath) {
if (realpathErr && realpathErr.code !== 'ENOENT') cb(realpathErr);
else cb(null, realpathErr ? x : realPath);
});
};
var maybeRealpath = function maybeRealpath(realpath, x, opts, cb) {
if (opts && opts.preserveSymlinks === false) {
realpath(x, cb);
} else {
cb(null, x);
}
};
var defaultReadPackage = function defaultReadPackage(readFile, pkgfile, cb) {
readFile(pkgfile, function (readFileErr, body) {
if (readFileErr) cb(readFileErr);
else {
try {
var pkg = JSON.parse(body);
cb(null, pkg);
} catch (jsonErr) {
cb(null);
}
}
});
};
var getPackageCandidates = function getPackageCandidates(x, start, opts) {
var dirs = nodeModulesPaths(start, opts, x);
for (var i = 0; i < dirs.length; i++) {
dirs[i] = path.join(dirs[i], x);
}
return dirs;
};
module.exports = function resolve(x, options, callback) {
var cb = callback;
var opts = options;
if (typeof options === 'function') {
cb = opts;
opts = {};
}
if (typeof x !== 'string') {
var err = new TypeError('Path must be a string.');
return process.nextTick(function () {
cb(err);
});
}
opts = normalizeOptions(x, opts);
var isFile = opts.isFile || defaultIsFile;
var isDirectory = opts.isDirectory || defaultIsDir;
var readFile = opts.readFile || fs.readFile;
var realpath = opts.realpath || defaultRealpath;
var readPackage = opts.readPackage || defaultReadPackage;
if (opts.readFile && opts.readPackage) {
var conflictErr = new TypeError('`readFile` and `readPackage` are mutually exclusive.');
return process.nextTick(function () {
cb(conflictErr);
});
}
var packageIterator = opts.packageIterator;
var extensions = opts.extensions || ['.js'];
var includeCoreModules = opts.includeCoreModules !== false;
var basedir = opts.basedir || path.dirname(caller());
var parent = opts.filename || basedir;
opts.paths = opts.paths || defaultPaths();
// ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory
var absoluteStart = path.resolve(basedir);
maybeRealpath(
realpath,
absoluteStart,
opts,
function (err, realStart) {
if (err) cb(err);
else init(realStart);
}
);
var res;
function init(basedir) {
if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) {
res = path.resolve(basedir, x);
if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/';
if ((/\/$/).test(x) && res === basedir) {
loadAsDirectory(res, opts.package, onfile);
} else loadAsFile(res, opts.package, onfile);
} else if (includeCoreModules && isCore(x)) {
return cb(null, x);
} else loadNodeModules(x, basedir, function (err, n, pkg) {
if (err) cb(err);
else if (n) {
return maybeRealpath(realpath, n, opts, function (err, realN) {
if (err) {
cb(err);
} else {
cb(null, realN, pkg);
}
});
} else {
var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'");
moduleError.code = 'MODULE_NOT_FOUND';
cb(moduleError);
}
});
}
function onfile(err, m, pkg) {
if (err) cb(err);
else if (m) cb(null, m, pkg);
else loadAsDirectory(res, function (err, d, pkg) {
if (err) cb(err);
else if (d) {
maybeRealpath(realpath, d, opts, function (err, realD) {
if (err) {
cb(err);
} else {
cb(null, realD, pkg);
}
});
} else {
var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'");
moduleError.code = 'MODULE_NOT_FOUND';
cb(moduleError);
}
});
}
function loadAsFile(x, thePackage, callback) {
var loadAsFilePackage = thePackage;
var cb = callback;
if (typeof loadAsFilePackage === 'function') {
cb = loadAsFilePackage;
loadAsFilePackage = undefined;
}
var exts = [''].concat(extensions);
load(exts, x, loadAsFilePackage);
function load(exts, x, loadPackage) {
if (exts.length === 0) return cb(null, undefined, loadPackage);
var file = x + exts[0];
var pkg = loadPackage;
if (pkg) onpkg(null, pkg);
else loadpkg(path.dirname(file), onpkg);
function onpkg(err, pkg_, dir) {
pkg = pkg_;
if (err) return cb(err);
if (dir && pkg && opts.pathFilter) {
var rfile = path.relative(dir, file);
var rel = rfile.slice(0, rfile.length - exts[0].length);
var r = opts.pathFilter(pkg, x, rel);
if (r) return load(
[''].concat(extensions.slice()),
path.resolve(dir, r),
pkg
);
}
isFile(file, onex);
}
function onex(err, ex) {
if (err) return cb(err);
if (ex) return cb(null, file, pkg);
load(exts.slice(1), x, pkg);
}
}
}
function loadpkg(dir, cb) {
if (dir === '' || dir === '/') return cb(null);
if (process.platform === 'win32' && (/^\w:[/\\]*$/).test(dir)) {
return cb(null);
}
if ((/[/\\]node_modules[/\\]*$/).test(dir)) return cb(null);
maybeRealpath(realpath, dir, opts, function (unwrapErr, pkgdir) {
if (unwrapErr) return loadpkg(path.dirname(dir), cb);
var pkgfile = path.join(pkgdir, 'package.json');
isFile(pkgfile, function (err, ex) {
// on err, ex is false
if (!ex) return loadpkg(path.dirname(dir), cb);
readPackage(readFile, pkgfile, function (err, pkgParam) {
if (err) cb(err);
var pkg = pkgParam;
if (pkg && opts.packageFilter) {
pkg = opts.packageFilter(pkg, pkgfile);
}
cb(null, pkg, dir);
});
});
});
}
function loadAsDirectory(x, loadAsDirectoryPackage, callback) {
var cb = callback;
var fpkg = loadAsDirectoryPackage;
if (typeof fpkg === 'function') {
cb = fpkg;
fpkg = opts.package;
}
maybeRealpath(realpath, x, opts, function (unwrapErr, pkgdir) {
if (unwrapErr) return cb(unwrapErr);
var pkgfile = path.join(pkgdir, 'package.json');
isFile(pkgfile, function (err, ex) {
if (err) return cb(err);
if (!ex) return loadAsFile(path.join(x, 'index'), fpkg, cb);
readPackage(readFile, pkgfile, function (err, pkgParam) {
if (err) return cb(err);
var pkg = pkgParam;
if (pkg && opts.packageFilter) {
pkg = opts.packageFilter(pkg, pkgfile);
}
if (pkg && pkg.main) {
if (typeof pkg.main !== 'string') {
var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string');
mainError.code = 'INVALID_PACKAGE_MAIN';
return cb(mainError);
}
if (pkg.main === '.' || pkg.main === './') {
pkg.main = 'index';
}
loadAsFile(path.resolve(x, pkg.main), pkg, function (err, m, pkg) {
if (err) return cb(err);
if (m) return cb(null, m, pkg);
if (!pkg) return loadAsFile(path.join(x, 'index'), pkg, cb);
var dir = path.resolve(x, pkg.main);
loadAsDirectory(dir, pkg, function (err, n, pkg) {
if (err) return cb(err);
if (n) return cb(null, n, pkg);
loadAsFile(path.join(x, 'index'), pkg, cb);
});
});
return;
}
loadAsFile(path.join(x, '/index'), pkg, cb);
});
});
});
}
function processDirs(cb, dirs) {
if (dirs.length === 0) return cb(null, undefined);
var dir = dirs[0];
isDirectory(path.dirname(dir), isdir);
function isdir(err, isdir) {
if (err) return cb(err);
if (!isdir) return processDirs(cb, dirs.slice(1));
loadAsFile(dir, opts.package, onfile);
}
function onfile(err, m, pkg) {
if (err) return cb(err);
if (m) return cb(null, m, pkg);
loadAsDirectory(dir, opts.package, ondir);
}
function ondir(err, n, pkg) {
if (err) return cb(err);
if (n) return cb(null, n, pkg);
processDirs(cb, dirs.slice(1));
}
}
function loadNodeModules(x, start, cb) {
var thunk = function () { return getPackageCandidates(x, start, opts); };
processDirs(
cb,
packageIterator ? packageIterator(x, start, thunk, opts) : thunk()
);
}
};
+8
View File
@@ -0,0 +1,8 @@
module.exports = function () {
// see https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
var origPrepareStackTrace = Error.prepareStackTrace;
Error.prepareStackTrace = function (_, stack) { return stack; };
var stack = (new Error()).stack;
Error.prepareStackTrace = origPrepareStackTrace;
return stack[2].getFileName();
};
+12
View File
@@ -0,0 +1,12 @@
'use strict';
var isCoreModule = require('is-core-module');
var data = require('./core.json');
var core = {};
for (var mod in data) { // eslint-disable-line no-restricted-syntax
if (Object.prototype.hasOwnProperty.call(data, mod)) {
core[mod] = isCoreModule(mod);
}
}
module.exports = core;
+162
View File
@@ -0,0 +1,162 @@
{
"assert": true,
"node:assert": [">= 14.18 && < 15", ">= 16"],
"assert/strict": ">= 15",
"node:assert/strict": ">= 16",
"async_hooks": ">= 8",
"node:async_hooks": [">= 14.18 && < 15", ">= 16"],
"buffer_ieee754": ">= 0.5 && < 0.9.7",
"buffer": true,
"node:buffer": [">= 14.18 && < 15", ">= 16"],
"child_process": true,
"node:child_process": [">= 14.18 && < 15", ">= 16"],
"cluster": ">= 0.5",
"node:cluster": [">= 14.18 && < 15", ">= 16"],
"console": true,
"node:console": [">= 14.18 && < 15", ">= 16"],
"constants": true,
"node:constants": [">= 14.18 && < 15", ">= 16"],
"crypto": true,
"node:crypto": [">= 14.18 && < 15", ">= 16"],
"_debug_agent": ">= 1 && < 8",
"_debugger": "< 8",
"dgram": true,
"node:dgram": [">= 14.18 && < 15", ">= 16"],
"diagnostics_channel": [">= 14.17 && < 15", ">= 15.1"],
"node:diagnostics_channel": [">= 14.18 && < 15", ">= 16"],
"dns": true,
"node:dns": [">= 14.18 && < 15", ">= 16"],
"dns/promises": ">= 15",
"node:dns/promises": ">= 16",
"domain": ">= 0.7.12",
"node:domain": [">= 14.18 && < 15", ">= 16"],
"events": true,
"node:events": [">= 14.18 && < 15", ">= 16"],
"freelist": "< 6",
"fs": true,
"node:fs": [">= 14.18 && < 15", ">= 16"],
"fs/promises": [">= 10 && < 10.1", ">= 14"],
"node:fs/promises": [">= 14.18 && < 15", ">= 16"],
"_http_agent": ">= 0.11.1",
"node:_http_agent": [">= 14.18 && < 15", ">= 16"],
"_http_client": ">= 0.11.1",
"node:_http_client": [">= 14.18 && < 15", ">= 16"],
"_http_common": ">= 0.11.1",
"node:_http_common": [">= 14.18 && < 15", ">= 16"],
"_http_incoming": ">= 0.11.1",
"node:_http_incoming": [">= 14.18 && < 15", ">= 16"],
"_http_outgoing": ">= 0.11.1",
"node:_http_outgoing": [">= 14.18 && < 15", ">= 16"],
"_http_server": ">= 0.11.1",
"node:_http_server": [">= 14.18 && < 15", ">= 16"],
"http": true,
"node:http": [">= 14.18 && < 15", ">= 16"],
"http2": ">= 8.8",
"node:http2": [">= 14.18 && < 15", ">= 16"],
"https": true,
"node:https": [">= 14.18 && < 15", ">= 16"],
"inspector": ">= 8",
"node:inspector": [">= 14.18 && < 15", ">= 16"],
"inspector/promises": [">= 19"],
"node:inspector/promises": [">= 19"],
"_linklist": "< 8",
"module": true,
"node:module": [">= 14.18 && < 15", ">= 16"],
"net": true,
"node:net": [">= 14.18 && < 15", ">= 16"],
"node-inspect/lib/_inspect": ">= 7.6 && < 12",
"node-inspect/lib/internal/inspect_client": ">= 7.6 && < 12",
"node-inspect/lib/internal/inspect_repl": ">= 7.6 && < 12",
"os": true,
"node:os": [">= 14.18 && < 15", ">= 16"],
"path": true,
"node:path": [">= 14.18 && < 15", ">= 16"],
"path/posix": ">= 15.3",
"node:path/posix": ">= 16",
"path/win32": ">= 15.3",
"node:path/win32": ">= 16",
"perf_hooks": ">= 8.5",
"node:perf_hooks": [">= 14.18 && < 15", ">= 16"],
"process": ">= 1",
"node:process": [">= 14.18 && < 15", ">= 16"],
"punycode": ">= 0.5",
"node:punycode": [">= 14.18 && < 15", ">= 16"],
"querystring": true,
"node:querystring": [">= 14.18 && < 15", ">= 16"],
"readline": true,
"node:readline": [">= 14.18 && < 15", ">= 16"],
"readline/promises": ">= 17",
"node:readline/promises": ">= 17",
"repl": true,
"node:repl": [">= 14.18 && < 15", ">= 16"],
"node:sea": [">= 20.12 && < 21", ">= 21.7"],
"smalloc": ">= 0.11.5 && < 3",
"node:sqlite": ">= 23.4",
"_stream_duplex": ">= 0.9.4",
"node:_stream_duplex": [">= 14.18 && < 15", ">= 16"],
"_stream_transform": ">= 0.9.4",
"node:_stream_transform": [">= 14.18 && < 15", ">= 16"],
"_stream_wrap": ">= 1.4.1",
"node:_stream_wrap": [">= 14.18 && < 15", ">= 16"],
"_stream_passthrough": ">= 0.9.4",
"node:_stream_passthrough": [">= 14.18 && < 15", ">= 16"],
"_stream_readable": ">= 0.9.4",
"node:_stream_readable": [">= 14.18 && < 15", ">= 16"],
"_stream_writable": ">= 0.9.4",
"node:_stream_writable": [">= 14.18 && < 15", ">= 16"],
"stream": true,
"node:stream": [">= 14.18 && < 15", ">= 16"],
"stream/consumers": ">= 16.7",
"node:stream/consumers": ">= 16.7",
"stream/promises": ">= 15",
"node:stream/promises": ">= 16",
"stream/web": ">= 16.5",
"node:stream/web": ">= 16.5",
"string_decoder": true,
"node:string_decoder": [">= 14.18 && < 15", ">= 16"],
"sys": [">= 0.4 && < 0.7", ">= 0.8"],
"node:sys": [">= 14.18 && < 15", ">= 16"],
"test/reporters": ">= 19.9 && < 20.2",
"node:test/reporters": [">= 18.17 && < 19", ">= 19.9", ">= 20"],
"test/mock_loader": ">= 22.3 && < 22.7",
"node:test/mock_loader": ">= 22.3 && < 22.7",
"node:test": [">= 16.17 && < 17", ">= 18"],
"timers": true,
"node:timers": [">= 14.18 && < 15", ">= 16"],
"timers/promises": ">= 15",
"node:timers/promises": ">= 16",
"_tls_common": ">= 0.11.13",
"node:_tls_common": [">= 14.18 && < 15", ">= 16"],
"_tls_legacy": ">= 0.11.3 && < 10",
"_tls_wrap": ">= 0.11.3",
"node:_tls_wrap": [">= 14.18 && < 15", ">= 16"],
"tls": true,
"node:tls": [">= 14.18 && < 15", ">= 16"],
"trace_events": ">= 10",
"node:trace_events": [">= 14.18 && < 15", ">= 16"],
"tty": true,
"node:tty": [">= 14.18 && < 15", ">= 16"],
"url": true,
"node:url": [">= 14.18 && < 15", ">= 16"],
"util": true,
"node:util": [">= 14.18 && < 15", ">= 16"],
"util/types": ">= 15.3",
"node:util/types": ">= 16",
"v8/tools/arguments": ">= 10 && < 12",
"v8/tools/codemap": [">= 4.4 && < 5", ">= 5.2 && < 12"],
"v8/tools/consarray": [">= 4.4 && < 5", ">= 5.2 && < 12"],
"v8/tools/csvparser": [">= 4.4 && < 5", ">= 5.2 && < 12"],
"v8/tools/logreader": [">= 4.4 && < 5", ">= 5.2 && < 12"],
"v8/tools/profile_view": [">= 4.4 && < 5", ">= 5.2 && < 12"],
"v8/tools/splaytree": [">= 4.4 && < 5", ">= 5.2 && < 12"],
"v8": ">= 1",
"node:v8": [">= 14.18 && < 15", ">= 16"],
"vm": true,
"node:vm": [">= 14.18 && < 15", ">= 16"],
"wasi": [">= 13.4 && < 13.5", ">= 18.17 && < 19", ">= 20"],
"node:wasi": [">= 18.17 && < 19", ">= 20"],
"worker_threads": ">= 11.7",
"node:worker_threads": [">= 14.18 && < 15", ">= 16"],
"zlib": ">= 0.5",
"node:zlib": [">= 14.18 && < 15", ">= 16"]
}
+24
View File
@@ -0,0 +1,24 @@
'use strict';
var os = require('os');
// adapted from https://github.com/sindresorhus/os-homedir/blob/11e089f4754db38bb535e5a8416320c4446e8cfd/index.js
module.exports = os.homedir || function homedir() {
var home = process.env.HOME;
var user = process.env.LOGNAME || process.env.USER || process.env.LNAME || process.env.USERNAME;
if (process.platform === 'win32') {
return process.env.USERPROFILE || process.env.HOMEDRIVE + process.env.HOMEPATH || home || null;
}
if (process.platform === 'darwin') {
return home || (user ? '/Users/' + user : null);
}
if (process.platform === 'linux') {
return home || (process.getuid() === 0 ? '/root' : (user ? '/home/' + user : null)); // eslint-disable-line no-extra-parens
}
return home || null;
};
+5
View File
@@ -0,0 +1,5 @@
var isCoreModule = require('is-core-module');
module.exports = function isCore(x) {
return isCoreModule(x);
};
+42
View File
@@ -0,0 +1,42 @@
var path = require('path');
var parse = path.parse || require('path-parse'); // eslint-disable-line global-require
var getNodeModulesDirs = function getNodeModulesDirs(absoluteStart, modules) {
var prefix = '/';
if ((/^([A-Za-z]:)/).test(absoluteStart)) {
prefix = '';
} else if ((/^\\\\/).test(absoluteStart)) {
prefix = '\\\\';
}
var paths = [absoluteStart];
var parsed = parse(absoluteStart);
while (parsed.dir !== paths[paths.length - 1]) {
paths.push(parsed.dir);
parsed = parse(parsed.dir);
}
return paths.reduce(function (dirs, aPath) {
return dirs.concat(modules.map(function (moduleDir) {
return path.resolve(prefix, aPath, moduleDir);
}));
}, []);
};
module.exports = function nodeModulesPaths(start, opts, request) {
var modules = opts && opts.moduleDirectory
? [].concat(opts.moduleDirectory)
: ['node_modules'];
if (opts && typeof opts.paths === 'function') {
return opts.paths(
request,
start,
function () { return getNodeModulesDirs(start, modules); },
opts
);
}
var dirs = getNodeModulesDirs(start, modules);
return opts && opts.paths ? dirs.concat(opts.paths) : dirs;
};
+10
View File
@@ -0,0 +1,10 @@
module.exports = function (x, opts) {
/**
* This file is purposefully a passthrough. It's expected that third-party
* environments will override it at runtime in order to inject special logic
* into `resolve` (by manipulating the options). One such example is the PnP
* code path in Yarn.
*/
return opts || {};
};
+208
View File
@@ -0,0 +1,208 @@
var isCore = require('is-core-module');
var fs = require('fs');
var path = require('path');
var getHomedir = require('./homedir');
var caller = require('./caller');
var nodeModulesPaths = require('./node-modules-paths');
var normalizeOptions = require('./normalize-options');
var realpathFS = process.platform !== 'win32' && fs.realpathSync && typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync;
var homedir = getHomedir();
var defaultPaths = function () {
return [
path.join(homedir, '.node_modules'),
path.join(homedir, '.node_libraries')
];
};
var defaultIsFile = function isFile(file) {
try {
var stat = fs.statSync(file, { throwIfNoEntry: false });
} catch (e) {
if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false;
throw e;
}
return !!stat && (stat.isFile() || stat.isFIFO());
};
var defaultIsDir = function isDirectory(dir) {
try {
var stat = fs.statSync(dir, { throwIfNoEntry: false });
} catch (e) {
if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false;
throw e;
}
return !!stat && stat.isDirectory();
};
var defaultRealpathSync = function realpathSync(x) {
try {
return realpathFS(x);
} catch (realpathErr) {
if (realpathErr.code !== 'ENOENT') {
throw realpathErr;
}
}
return x;
};
var maybeRealpathSync = function maybeRealpathSync(realpathSync, x, opts) {
if (opts && opts.preserveSymlinks === false) {
return realpathSync(x);
}
return x;
};
var defaultReadPackageSync = function defaultReadPackageSync(readFileSync, pkgfile) {
var body = readFileSync(pkgfile);
try {
var pkg = JSON.parse(body);
return pkg;
} catch (jsonErr) {}
};
var getPackageCandidates = function getPackageCandidates(x, start, opts) {
var dirs = nodeModulesPaths(start, opts, x);
for (var i = 0; i < dirs.length; i++) {
dirs[i] = path.join(dirs[i], x);
}
return dirs;
};
module.exports = function resolveSync(x, options) {
if (typeof x !== 'string') {
throw new TypeError('Path must be a string.');
}
var opts = normalizeOptions(x, options);
var isFile = opts.isFile || defaultIsFile;
var readFileSync = opts.readFileSync || fs.readFileSync;
var isDirectory = opts.isDirectory || defaultIsDir;
var realpathSync = opts.realpathSync || defaultRealpathSync;
var readPackageSync = opts.readPackageSync || defaultReadPackageSync;
if (opts.readFileSync && opts.readPackageSync) {
throw new TypeError('`readFileSync` and `readPackageSync` are mutually exclusive.');
}
var packageIterator = opts.packageIterator;
var extensions = opts.extensions || ['.js'];
var includeCoreModules = opts.includeCoreModules !== false;
var basedir = opts.basedir || path.dirname(caller());
var parent = opts.filename || basedir;
opts.paths = opts.paths || defaultPaths();
// ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory
var absoluteStart = maybeRealpathSync(realpathSync, path.resolve(basedir), opts);
if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) {
var res = path.resolve(absoluteStart, x);
if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/';
var m = loadAsFileSync(res) || loadAsDirectorySync(res);
if (m) return maybeRealpathSync(realpathSync, m, opts);
} else if (includeCoreModules && isCore(x)) {
return x;
} else {
var n = loadNodeModulesSync(x, absoluteStart);
if (n) return maybeRealpathSync(realpathSync, n, opts);
}
var err = new Error("Cannot find module '" + x + "' from '" + parent + "'");
err.code = 'MODULE_NOT_FOUND';
throw err;
function loadAsFileSync(x) {
var pkg = loadpkg(path.dirname(x));
if (pkg && pkg.dir && pkg.pkg && opts.pathFilter) {
var rfile = path.relative(pkg.dir, x);
var r = opts.pathFilter(pkg.pkg, x, rfile);
if (r) {
x = path.resolve(pkg.dir, r); // eslint-disable-line no-param-reassign
}
}
if (isFile(x)) {
return x;
}
for (var i = 0; i < extensions.length; i++) {
var file = x + extensions[i];
if (isFile(file)) {
return file;
}
}
}
function loadpkg(dir) {
if (dir === '' || dir === '/') return;
if (process.platform === 'win32' && (/^\w:[/\\]*$/).test(dir)) {
return;
}
if ((/[/\\]node_modules[/\\]*$/).test(dir)) return;
var pkgfile = path.join(maybeRealpathSync(realpathSync, dir, opts), 'package.json');
if (!isFile(pkgfile)) {
return loadpkg(path.dirname(dir));
}
var pkg = readPackageSync(readFileSync, pkgfile);
if (pkg && opts.packageFilter) {
// v2 will pass pkgfile
pkg = opts.packageFilter(pkg, /*pkgfile,*/ dir); // eslint-disable-line spaced-comment
}
return { pkg: pkg, dir: dir };
}
function loadAsDirectorySync(x) {
var pkgfile = path.join(maybeRealpathSync(realpathSync, x, opts), '/package.json');
if (isFile(pkgfile)) {
try {
var pkg = readPackageSync(readFileSync, pkgfile);
} catch (e) {}
if (pkg && opts.packageFilter) {
// v2 will pass pkgfile
pkg = opts.packageFilter(pkg, /*pkgfile,*/ x); // eslint-disable-line spaced-comment
}
if (pkg && pkg.main) {
if (typeof pkg.main !== 'string') {
var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string');
mainError.code = 'INVALID_PACKAGE_MAIN';
throw mainError;
}
if (pkg.main === '.' || pkg.main === './') {
pkg.main = 'index';
}
try {
var m = loadAsFileSync(path.resolve(x, pkg.main));
if (m) return m;
var n = loadAsDirectorySync(path.resolve(x, pkg.main));
if (n) return n;
} catch (e) {}
}
}
return loadAsFileSync(path.join(x, '/index'));
}
function loadNodeModulesSync(x, start) {
var thunk = function () { return getPackageCandidates(x, start, opts); };
var dirs = packageIterator ? packageIterator(x, start, thunk, opts) : thunk();
for (var i = 0; i < dirs.length; i++) {
var dir = dirs[i];
if (isDirectory(path.dirname(dir))) {
var m = loadAsFileSync(dir);
if (m) return m;
var n = loadAsDirectorySync(dir);
if (n) return n;
}
}
}
};
+71
View File
@@ -0,0 +1,71 @@
{
"name": "resolve",
"description": "resolve like require.resolve() on behalf of files asynchronously and synchronously",
"version": "1.22.9",
"repository": {
"type": "git",
"url": "git://github.com/browserify/resolve.git"
},
"bin": {
"resolve": "./bin/resolve"
},
"main": "index.js",
"keywords": [
"resolve",
"require",
"node",
"module"
],
"scripts": {
"prepack": "npmignore --auto --commentLines=autogenerated && cp node_modules/is-core-module/core.json ./lib/ ||:",
"prepublishOnly": "safe-publish-latest",
"prepublish": "not-in-publish || npm run prepublishOnly",
"prelint": "eclint check $(git ls-files | xargs find 2> /dev/null | grep -vE 'node_modules|\\.git')",
"lint": "eslint --ext=js,mjs --no-eslintrc -c .eslintrc . 'bin/**'",
"pretests-only": "cd ./test/resolver/nested_symlinks && node mylib/sync && node mylib/async",
"tests-only": "tape test/*.js",
"pretest": "npm run lint",
"test": "npm run --silent tests-only",
"posttest": "npm run test:multirepo && npx npm@'>= 10.2' audit --production",
"test:multirepo": "cd ./test/resolver/multirepo && npm install && npm test"
},
"devDependencies": {
"@ljharb/eslint-config": "^21.1.1",
"array.prototype.map": "^1.0.7",
"copy-dir": "^1.3.0",
"eclint": "^2.8.1",
"eslint": "=8.8.0",
"in-publish": "^2.0.1",
"mkdirp": "^0.5.5",
"mv": "^2.1.1",
"npmignore": "^0.3.1",
"object-keys": "^1.1.1",
"rimraf": "^2.7.1",
"safe-publish-latest": "^2.0.0",
"semver": "^6.3.1",
"tap": "0.4.13",
"tape": "^5.9.0",
"tmp": "^0.0.31"
},
"license": "MIT",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
},
"dependencies": {
"is-core-module": "^2.16.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"publishConfig": {
"ignore": [
".github/workflows",
"appveyor.yml",
"test/resolver/malformed_package_json"
]
}
}
+301
View File
@@ -0,0 +1,301 @@
# resolve <sup>[![Version Badge][2]][1]</sup>
implements the [node `require.resolve()` algorithm](https://nodejs.org/api/modules.html#modules_all_together) such that you can `require.resolve()` on behalf of a file asynchronously and synchronously
[![github actions][actions-image]][actions-url]
[![coverage][codecov-image]][codecov-url]
[![dependency status][5]][6]
[![dev dependency status][7]][8]
[![License][license-image]][license-url]
[![Downloads][downloads-image]][downloads-url]
[![npm badge][11]][1]
# example
asynchronously resolve:
```js
var resolve = require('resolve/async'); // or, require('resolve')
resolve('tap', { basedir: __dirname }, function (err, res) {
if (err) console.error(err);
else console.log(res);
});
```
```
$ node example/async.js
/home/substack/projects/node-resolve/node_modules/tap/lib/main.js
```
synchronously resolve:
```js
var resolve = require('resolve/sync'); // or, `require('resolve').sync
var res = resolve('tap', { basedir: __dirname });
console.log(res);
```
```
$ node example/sync.js
/home/substack/projects/node-resolve/node_modules/tap/lib/main.js
```
# methods
```js
var resolve = require('resolve');
var async = require('resolve/async');
var sync = require('resolve/sync');
```
For both the synchronous and asynchronous methods, errors may have any of the following `err.code` values:
- `MODULE_NOT_FOUND`: the given path string (`id`) could not be resolved to a module
- `INVALID_BASEDIR`: the specified `opts.basedir` doesn't exist, or is not a directory
- `INVALID_PACKAGE_MAIN`: a `package.json` was encountered with an invalid `main` property (eg. not a string)
## resolve(id, opts={}, cb)
Asynchronously resolve the module path string `id` into `cb(err, res [, pkg])`, where `pkg` (if defined) is the data from `package.json`.
options are:
* opts.basedir - directory to begin resolving from
* opts.package - `package.json` data applicable to the module being loaded
* opts.extensions - array of file extensions to search in order
* opts.includeCoreModules - set to `false` to exclude node core modules (e.g. `fs`) from the search
* opts.readFile - how to read files asynchronously
* opts.isFile - function to asynchronously test whether a file exists
* opts.isDirectory - function to asynchronously test whether a file exists and is a directory
* opts.realpath - function to asynchronously resolve a potential symlink to its real path
* `opts.readPackage(readFile, pkgfile, cb)` - function to asynchronously read and parse a package.json file
* readFile - the passed `opts.readFile` or `fs.readFile` if not specified
* pkgfile - path to package.json
* cb - callback
* `opts.packageFilter(pkg, pkgfile, dir)` - transform the parsed package.json contents before looking at the "main" field
* pkg - package data
* pkgfile - path to package.json
* dir - directory that contains package.json
* `opts.pathFilter(pkg, path, relativePath)` - transform a path within a package
* pkg - package data
* path - the path being resolved
* relativePath - the path relative from the package.json location
* returns - a relative path that will be joined from the package.json location
* opts.paths - require.paths array to use if nothing is found on the normal `node_modules` recursive walk (probably don't use this)
For advanced users, `paths` can also be a `opts.paths(request, start, opts)` function
* request - the import specifier being resolved
* start - lookup path
* getNodeModulesDirs - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution
* opts - the resolution options
* `opts.packageIterator(request, start, opts)` - return the list of candidate paths where the packages sources may be found (probably don't use this)
* request - the import specifier being resolved
* start - lookup path
* getPackageCandidates - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution
* opts - the resolution options
* opts.moduleDirectory - directory (or directories) in which to recursively look for modules. default: `"node_modules"`
* opts.preserveSymlinks - if true, doesn't resolve `basedir` to real path before resolving.
This is the way Node resolves dependencies when executed with the [--preserve-symlinks](https://nodejs.org/api/all.html#cli_preserve_symlinks) flag.
**Note:** this property is currently `true` by default but it will be changed to
`false` in the next major version because *Node's resolution algorithm does not preserve symlinks by default*.
default `opts` values:
```js
{
paths: [],
basedir: __dirname,
extensions: ['.js'],
includeCoreModules: true,
readFile: fs.readFile,
isFile: function isFile(file, cb) {
fs.stat(file, function (err, stat) {
if (!err) {
return cb(null, stat.isFile() || stat.isFIFO());
}
if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
return cb(err);
});
},
isDirectory: function isDirectory(dir, cb) {
fs.stat(dir, function (err, stat) {
if (!err) {
return cb(null, stat.isDirectory());
}
if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
return cb(err);
});
},
realpath: function realpath(file, cb) {
var realpath = typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath;
realpath(file, function (realPathErr, realPath) {
if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr);
else cb(null, realPathErr ? file : realPath);
});
},
readPackage: function defaultReadPackage(readFile, pkgfile, cb) {
readFile(pkgfile, function (readFileErr, body) {
if (readFileErr) cb(readFileErr);
else {
try {
var pkg = JSON.parse(body);
cb(null, pkg);
} catch (jsonErr) {
cb(null);
}
}
});
},
moduleDirectory: 'node_modules',
preserveSymlinks: true
}
```
## resolve.sync(id, opts)
Synchronously resolve the module path string `id`, returning the result and
throwing an error when `id` can't be resolved.
options are:
* opts.basedir - directory to begin resolving from
* opts.extensions - array of file extensions to search in order
* opts.includeCoreModules - set to `false` to exclude node core modules (e.g. `fs`) from the search
* opts.readFileSync - how to read files synchronously
* opts.isFile - function to synchronously test whether a file exists
* opts.isDirectory - function to synchronously test whether a file exists and is a directory
* opts.realpathSync - function to synchronously resolve a potential symlink to its real path
* `opts.readPackageSync(readFileSync, pkgfile)` - function to synchronously read and parse a package.json file
* readFileSync - the passed `opts.readFileSync` or `fs.readFileSync` if not specified
* pkgfile - path to package.json
* `opts.packageFilter(pkg, dir)` - transform the parsed package.json contents before looking at the "main" field
* pkg - package data
* dir - directory that contains package.json (Note: the second argument will change to "pkgfile" in v2)
* `opts.pathFilter(pkg, path, relativePath)` - transform a path within a package
* pkg - package data
* path - the path being resolved
* relativePath - the path relative from the package.json location
* returns - a relative path that will be joined from the package.json location
* opts.paths - require.paths array to use if nothing is found on the normal `node_modules` recursive walk (probably don't use this)
For advanced users, `paths` can also be a `opts.paths(request, start, opts)` function
* request - the import specifier being resolved
* start - lookup path
* getNodeModulesDirs - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution
* opts - the resolution options
* `opts.packageIterator(request, start, opts)` - return the list of candidate paths where the packages sources may be found (probably don't use this)
* request - the import specifier being resolved
* start - lookup path
* getPackageCandidates - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution
* opts - the resolution options
* opts.moduleDirectory - directory (or directories) in which to recursively look for modules. default: `"node_modules"`
* opts.preserveSymlinks - if true, doesn't resolve `basedir` to real path before resolving.
This is the way Node resolves dependencies when executed with the [--preserve-symlinks](https://nodejs.org/api/all.html#cli_preserve_symlinks) flag.
**Note:** this property is currently `true` by default but it will be changed to
`false` in the next major version because *Node's resolution algorithm does not preserve symlinks by default*.
default `opts` values:
```js
{
paths: [],
basedir: __dirname,
extensions: ['.js'],
includeCoreModules: true,
readFileSync: fs.readFileSync,
isFile: function isFile(file) {
try {
var stat = fs.statSync(file);
} catch (e) {
if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false;
throw e;
}
return stat.isFile() || stat.isFIFO();
},
isDirectory: function isDirectory(dir) {
try {
var stat = fs.statSync(dir);
} catch (e) {
if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false;
throw e;
}
return stat.isDirectory();
},
realpathSync: function realpathSync(file) {
try {
var realpath = typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync;
return realpath(file);
} catch (realPathErr) {
if (realPathErr.code !== 'ENOENT') {
throw realPathErr;
}
}
return file;
},
readPackageSync: function defaultReadPackageSync(readFileSync, pkgfile) {
var body = readFileSync(pkgfile);
try {
var pkg = JSON.parse(body);
return pkg;
} catch (jsonErr) {}
},
moduleDirectory: 'node_modules',
preserveSymlinks: true
}
```
# install
With [npm](https://npmjs.org) do:
```sh
npm install resolve
```
# license
MIT
[1]: https://npmjs.org/package/resolve
[2]: https://versionbadg.es/browserify/resolve.svg
[5]: https://david-dm.org/browserify/resolve.svg
[6]: https://david-dm.org/browserify/resolve
[7]: https://david-dm.org/browserify/resolve/dev-status.svg
[8]: https://david-dm.org/browserify/resolve#info=devDependencies
[11]: https://nodei.co/npm/resolve.png?downloads=true&stars=true
[license-image]: https://img.shields.io/npm/l/resolve.svg
[license-url]: LICENSE
[downloads-image]: https://img.shields.io/npm/dm/resolve.svg
[downloads-url]: https://npm-stat.com/charts.html?package=resolve
[codecov-image]: https://codecov.io/gh/browserify/resolve/branch/main/graphs/badge.svg
[codecov-url]: https://app.codecov.io/gh/browserify/resolve/
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/browserify/resolve
[actions-url]: https://github.com/browserify/resolve/actions
+3
View File
@@ -0,0 +1,3 @@
'use strict';
module.exports = require('./lib/sync');
+88
View File
@@ -0,0 +1,88 @@
var test = require('tape');
var keys = require('object-keys');
var semver = require('semver');
var resolve = require('../');
var brokenNode = semver.satisfies(process.version, '11.11 - 11.13');
test('core modules', function (t) {
t.test('isCore()', function (st) {
st.ok(resolve.isCore('fs'));
st.ok(resolve.isCore('net'));
st.ok(resolve.isCore('http'));
st.ok(!resolve.isCore('seq'));
st.ok(!resolve.isCore('../'));
st.ok(!resolve.isCore('toString'));
st.end();
});
t.test('core list', function (st) {
var cores = keys(resolve.core);
st.plan(cores.length);
for (var i = 0; i < cores.length; ++i) {
var mod = cores[i];
// note: this must be require, not require.resolve, due to https://github.com/nodejs/node/issues/43274
var requireFunc = function () { require(mod); }; // eslint-disable-line no-loop-func
t.comment(mod + ': ' + resolve.core[mod]);
if (resolve.core[mod]) {
st.doesNotThrow(requireFunc, mod + ' supported; requiring does not throw');
} else if (brokenNode) {
st.ok(true, 'this version of node is broken: attempting to require things that fail to resolve breaks "home_paths" tests');
} else {
st.throws(requireFunc, mod + ' not supported; requiring throws');
}
}
st.end();
});
t.test('core via repl module', { skip: !resolve.core.repl }, function (st) {
var libs = require('repl')._builtinLibs; // eslint-disable-line no-underscore-dangle
if (!libs) {
st.skip('module.builtinModules does not exist');
return st.end();
}
for (var i = 0; i < libs.length; ++i) {
var mod = libs[i];
st.ok(resolve.core[mod], mod + ' is a core module');
st.doesNotThrow(
function () { require(mod); }, // eslint-disable-line no-loop-func
'requiring ' + mod + ' does not throw'
);
}
st.end();
});
t.test('core via builtinModules list', { skip: !resolve.core.module }, function (st) {
var libs = require('module').builtinModules;
if (!libs) {
st.skip('module.builtinModules does not exist');
return st.end();
}
var blacklist = [
'_debug_agent',
'v8/tools/tickprocessor-driver',
'v8/tools/SourceMap',
'v8/tools/tickprocessor',
'v8/tools/profile'
];
for (var i = 0; i < libs.length; ++i) {
var mod = libs[i];
if (blacklist.indexOf(mod) === -1) {
st.ok(resolve.core[mod], mod + ' is a core module');
st.doesNotThrow(
function () { require(mod); }, // eslint-disable-line no-loop-func
'requiring ' + mod + ' does not throw'
);
}
}
st.end();
});
t.end();
});
+29
View File
@@ -0,0 +1,29 @@
var path = require('path');
var test = require('tape');
var resolve = require('../');
test('dotdot', function (t) {
t.plan(4);
var dir = path.join(__dirname, '/dotdot/abc');
resolve('..', { basedir: dir }, function (err, res, pkg) {
t.ifError(err);
t.equal(res, path.join(__dirname, 'dotdot/index.js'));
});
resolve('.', { basedir: dir }, function (err, res, pkg) {
t.ifError(err);
t.equal(res, path.join(dir, 'index.js'));
});
});
test('dotdot sync', function (t) {
t.plan(2);
var dir = path.join(__dirname, '/dotdot/abc');
var a = resolve.sync('..', { basedir: dir });
t.equal(a, path.join(__dirname, 'dotdot/index.js'));
var b = resolve.sync('.', { basedir: dir });
t.equal(b, path.join(dir, 'index.js'));
});
+2
View File
@@ -0,0 +1,2 @@
var x = require('..');
console.log(x);
+1
View File
@@ -0,0 +1 @@
module.exports = 'whatever';
+29
View File
@@ -0,0 +1,29 @@
var test = require('tape');
var path = require('path');
var resolve = require('../');
test('faulty basedir must produce error in windows', { skip: process.platform !== 'win32' }, function (t) {
t.plan(1);
var resolverDir = 'C:\\a\\b\\c\\d';
resolve('tape/lib/test.js', { basedir: resolverDir }, function (err, res, pkg) {
t.equal(!!err, true);
});
});
test('non-existent basedir should not throw when preserveSymlinks is false', function (t) {
t.plan(2);
var opts = {
basedir: path.join(path.sep, 'unreal', 'path', 'that', 'does', 'not', 'exist'),
preserveSymlinks: false
};
var module = './dotdot/abc';
resolve(module, opts, function (err, res) {
t.equal(err.code, 'MODULE_NOT_FOUND');
t.equal(res, undefined);
});
});
+34
View File
@@ -0,0 +1,34 @@
var path = require('path');
var test = require('tape');
var resolve = require('../');
test('filter', function (t) {
t.plan(4);
var dir = path.join(__dirname, 'resolver');
var packageFilterArgs;
resolve('./baz', {
basedir: dir,
packageFilter: function (pkg, pkgfile) {
pkg.main = 'doom'; // eslint-disable-line no-param-reassign
packageFilterArgs = [pkg, pkgfile];
return pkg;
}
}, function (err, res, pkg) {
if (err) t.fail(err);
t.equal(res, path.join(dir, 'baz/doom.js'), 'changing the package "main" works');
var packageData = packageFilterArgs[0];
t.equal(pkg, packageData, 'first packageFilter argument is "pkg"');
t.equal(packageData.main, 'doom', 'package "main" was altered');
var packageFile = packageFilterArgs[1];
t.equal(
packageFile,
path.join(dir, 'baz/package.json'),
'second packageFilter argument is "pkgfile"'
);
t.end();
});
});
+33
View File
@@ -0,0 +1,33 @@
var path = require('path');
var test = require('tape');
var resolve = require('../');
test('filter', function (t) {
var dir = path.join(__dirname, 'resolver');
var packageFilterArgs;
var res = resolve.sync('./baz', {
basedir: dir,
// NOTE: in v2.x, this will be `pkg, pkgfile, dir`, but must remain "broken" here in v1.x for compatibility
packageFilter: function (pkg, /*pkgfile,*/ dir) { // eslint-disable-line spaced-comment
pkg.main = 'doom'; // eslint-disable-line no-param-reassign
packageFilterArgs = 'is 1.x' ? [pkg, dir] : [pkg, pkgfile, dir]; // eslint-disable-line no-constant-condition, no-undef
return pkg;
}
});
t.equal(res, path.join(dir, 'baz/doom.js'), 'changing the package "main" works');
var packageData = packageFilterArgs[0];
t.equal(packageData.main, 'doom', 'package "main" was altered');
if (!'is 1.x') { // eslint-disable-line no-constant-condition
var packageFile = packageFilterArgs[1];
t.equal(packageFile, path.join(dir, 'baz', 'package.json'), 'package.json path is correct');
}
var packageDir = packageFilterArgs['is 1.x' ? 1 : 2]; // eslint-disable-line no-constant-condition
// eslint-disable-next-line no-constant-condition
t.equal(packageDir, path.join(dir, 'baz'), ('is 1.x' ? 'second' : 'third') + ' packageFilter argument is "dir"');
t.end();
});
+127
View File
@@ -0,0 +1,127 @@
'use strict';
var fs = require('fs');
var homedir = require('../lib/homedir');
var path = require('path');
var test = require('tape');
var mkdirp = require('mkdirp');
var rimraf = require('rimraf');
var mv = require('mv');
var copyDir = require('copy-dir');
var tmp = require('tmp');
var HOME = homedir();
var hnm = path.join(HOME, '.node_modules');
var hnl = path.join(HOME, '.node_libraries');
var resolve = require('../async');
function makeDir(t, dir, cb) {
mkdirp(dir, function (err) {
if (err) {
cb(err);
} else {
t.teardown(function cleanup() {
rimraf.sync(dir);
});
cb();
}
});
}
function makeTempDir(t, dir, cb) {
if (fs.existsSync(dir)) {
var tmpResult = tmp.dirSync();
t.teardown(tmpResult.removeCallback);
var backup = path.join(tmpResult.name, path.basename(dir));
mv(dir, backup, function (err) {
if (err) {
cb(err);
} else {
t.teardown(function () {
mv(backup, dir, cb);
});
makeDir(t, dir, cb);
}
});
} else {
makeDir(t, dir, cb);
}
}
test('homedir module paths', function (t) {
t.plan(7);
makeTempDir(t, hnm, function (err) {
t.error(err, 'no error with HNM temp dir');
if (err) {
return t.end();
}
var bazHNMDir = path.join(hnm, 'baz');
var dotMainDir = path.join(hnm, 'dot_main');
copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNMDir);
copyDir.sync(path.join(__dirname, 'resolver/dot_main'), dotMainDir);
var bazPkg = { name: 'baz', main: 'quux.js' };
var dotMainPkg = { main: 'index' };
var bazHNMmain = path.join(bazHNMDir, 'quux.js');
t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
var dotMainMain = path.join(dotMainDir, 'index.js');
t.equal(require.resolve('dot_main'), dotMainMain, 'sanity check: require.resolve finds `dot_main`');
makeTempDir(t, hnl, function (err) {
t.error(err, 'no error with HNL temp dir');
if (err) {
return t.end();
}
var bazHNLDir = path.join(hnl, 'baz');
copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNLDir);
var dotSlashMainDir = path.join(hnl, 'dot_slash_main');
var dotSlashMainMain = path.join(dotSlashMainDir, 'index.js');
var dotSlashMainPkg = { main: 'index' };
copyDir.sync(path.join(__dirname, 'resolver/dot_slash_main'), dotSlashMainDir);
t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
t.equal(require.resolve('dot_slash_main'), dotSlashMainMain, 'sanity check: require.resolve finds HNL `dot_slash_main`');
t.test('with temp dirs', function (st) {
st.plan(3);
st.test('just in `$HOME/.node_modules`', function (s2t) {
s2t.plan(3);
resolve('dot_main', function (err, res, pkg) {
s2t.error(err, 'no error resolving `dot_main`');
s2t.equal(res, dotMainMain, '`dot_main` resolves in `$HOME/.node_modules`');
s2t.deepEqual(pkg, dotMainPkg);
});
});
st.test('just in `$HOME/.node_libraries`', function (s2t) {
s2t.plan(3);
resolve('dot_slash_main', function (err, res, pkg) {
s2t.error(err, 'no error resolving `dot_slash_main`');
s2t.equal(res, dotSlashMainMain, '`dot_slash_main` resolves in `$HOME/.node_libraries`');
s2t.deepEqual(pkg, dotSlashMainPkg);
});
});
st.test('in `$HOME/.node_libraries` and `$HOME/.node_modules`', function (s2t) {
s2t.plan(3);
resolve('baz', function (err, res, pkg) {
s2t.error(err, 'no error resolving `baz`');
s2t.equal(res, bazHNMmain, '`baz` resolves in `$HOME/.node_modules` when in both');
s2t.deepEqual(pkg, bazPkg);
});
});
});
});
});
});
+114
View File
@@ -0,0 +1,114 @@
'use strict';
var fs = require('fs');
var homedir = require('../lib/homedir');
var path = require('path');
var test = require('tape');
var mkdirp = require('mkdirp');
var rimraf = require('rimraf');
var mv = require('mv');
var copyDir = require('copy-dir');
var tmp = require('tmp');
var HOME = homedir();
var hnm = path.join(HOME, '.node_modules');
var hnl = path.join(HOME, '.node_libraries');
var resolve = require('../sync');
function makeDir(t, dir, cb) {
mkdirp(dir, function (err) {
if (err) {
cb(err);
} else {
t.teardown(function cleanup() {
rimraf.sync(dir);
});
cb();
}
});
}
function makeTempDir(t, dir, cb) {
if (fs.existsSync(dir)) {
var tmpResult = tmp.dirSync();
t.teardown(tmpResult.removeCallback);
var backup = path.join(tmpResult.name, path.basename(dir));
mv(dir, backup, function (err) {
if (err) {
cb(err);
} else {
t.teardown(function () {
mv(backup, dir, cb);
});
makeDir(t, dir, cb);
}
});
} else {
makeDir(t, dir, cb);
}
}
test('homedir module paths', function (t) {
t.plan(7);
makeTempDir(t, hnm, function (err) {
t.error(err, 'no error with HNM temp dir');
if (err) {
return t.end();
}
var bazHNMDir = path.join(hnm, 'baz');
var dotMainDir = path.join(hnm, 'dot_main');
copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNMDir);
copyDir.sync(path.join(__dirname, 'resolver/dot_main'), dotMainDir);
var bazHNMmain = path.join(bazHNMDir, 'quux.js');
t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
var dotMainMain = path.join(dotMainDir, 'index.js');
t.equal(require.resolve('dot_main'), dotMainMain, 'sanity check: require.resolve finds `dot_main`');
makeTempDir(t, hnl, function (err) {
t.error(err, 'no error with HNL temp dir');
if (err) {
return t.end();
}
var bazHNLDir = path.join(hnl, 'baz');
copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNLDir);
var dotSlashMainDir = path.join(hnl, 'dot_slash_main');
var dotSlashMainMain = path.join(dotSlashMainDir, 'index.js');
copyDir.sync(path.join(__dirname, 'resolver/dot_slash_main'), dotSlashMainDir);
t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
t.equal(require.resolve('dot_slash_main'), dotSlashMainMain, 'sanity check: require.resolve finds HNL `dot_slash_main`');
t.test('with temp dirs', function (st) {
st.plan(3);
st.test('just in `$HOME/.node_modules`', function (s2t) {
s2t.plan(1);
var res = resolve('dot_main');
s2t.equal(res, dotMainMain, '`dot_main` resolves in `$HOME/.node_modules`');
});
st.test('just in `$HOME/.node_libraries`', function (s2t) {
s2t.plan(1);
var res = resolve('dot_slash_main');
s2t.equal(res, dotSlashMainMain, '`dot_slash_main` resolves in `$HOME/.node_libraries`');
});
st.test('in `$HOME/.node_libraries` and `$HOME/.node_modules`', function (s2t) {
s2t.plan(1);
var res = resolve('baz');
s2t.equal(res, bazHNMmain, '`baz` resolves in `$HOME/.node_modules` when in both');
});
});
});
});
});
+1
View File
@@ -0,0 +1 @@
packages/tests/fixtures/**
+14
View File
@@ -0,0 +1,14 @@
{
"root": true,
"extends": "@ljharb/eslint-config/node/10",
"overrides": [
{
"files": "since.js",
"rules": {
"no-console": 0,
},
},
],
}
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [ljharb]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: npm/list-exports
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
@@ -0,0 +1,78 @@
name: 'Tests: export conditions'
on: [pull_request, push]
jobs:
conditions:
name: 'exports conditions'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version:
- '17' # ↥ pattern-trailers-no-dir-slash
- '16'
- '16.9' # ⤓ with pattern trailers
- '16.8' # ↥ no pattern trailers
- '16.0' # ↥ patterns
- '15'
- '14.19' # ⤓ with pattern trailers
- '14.18' # ↥ no pattern trailers
- '14.13' # ⤓ with patterns
- '14.12' # ↥ no patterns
- '14.0' # ⤓ with conditions
- '13' # ↥ no conditions
- '13.14' # ⤓ first conditions with broken dir slash
- '13.13' # ↥ last conditions without broken dir slash
- '13.7' # ⤓ with conditions
- '13.6' # ↥ no conditions
- '13.5'
- '13.4'
- '13.3'
- '13.2' # ⤓ experimental
- '13.1' # broken
- '13.0' # broken
- '12'
- '12.20' # ⤓ with patterns
- '12.19' # ↥ no patterns
- '12.18'
- '12.17' # ⤓ with conditions
- '12.16' # ↥ no conditions
- '8'
- '6'
- '4'
- 'iojs'
- '0.12'
- '0.10'
- '0.8'
steps:
- uses: actions/checkout@v4
- uses: ljharb/actions/node/install@main
name: 'nvm install lts/* && npm install'
with:
node-version: 'lts/*'
skip-ls-check: true
- uses: ljharb/actions/node/install@main
name: 'nvm install ${{ matrix.node-version }}'
with:
node-version: ${{ matrix.node-version }}
skip-install: true
skip-ls-check: true
- run: npm run tests:conditions
exports-conditions:
name: 'exports conditions'
needs: [conditions]
runs-on: ubuntu-latest
steps:
- run: 'echo tests completed'
+25
View File
@@ -0,0 +1,25 @@
name: 'Tests: fixtures'
on: [pull_request, push]
jobs:
fixtures:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ljharb/actions/node/install@main
name: 'nvm install ${{ matrix.node-version }} && npm install'
with:
node-version: 'lts/*'
skip-ls-check: true
- name: 'list fixtures'
run: npm run fixtures:ls
working-directory: packages/tests
- name: 'verify all fixtures have `"bundleDependencies": true'
run: npm run fixtures:bundleCheck
working-directory: packages/tests
- name: 'install all fixtures in tests `package.json`'
run: npm run fixtures:install
working-directory: packages/tests
- run: git diff --quiet --exit-code
@@ -0,0 +1,42 @@
name: 'Tests: ls-engines'
on: [pull_request, push]
jobs:
engines-matrix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ljharb/actions/node/install@main
name: 'nvm install lts/* && npm install'
with:
node-version: 'lts/*'
- id: set-matrix
run: |
echo "matrix=$(npm query --no-workspaces .workspace | npx json -a location | { cat; echo; } | sort | node -pe 'JSON.stringify(fs.readFileSync(0, "utf-8").split("\n").slice(0,-1).map(x => "./"+x))')" >> $GITHUB_OUTPUT
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
engines-job:
name: 'ls-engines: package'
needs: [engines-matrix]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
package: ${{ fromJson(needs.engines-matrix.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- uses: ljharb/actions/node/install@main
name: 'nvm install lts/* && npm install'
with:
node-version: 'lts/*'
- run: npx ls-engines
working-directory: ${{ matrix.package }}
engines:
name: 'ls-engines'
needs: [engines-job]
runs-on: ubuntu-latest
steps:
- run: 'echo tests completed'
@@ -0,0 +1,29 @@
name: 'Tests: pretest/posttest'
on: [pull_request, push]
jobs:
pretest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ljharb/actions/node/install@main
name: 'nvm install ${{ matrix.node-version }} && npm install'
with:
node-version: 'lts/*'
skip-ls-check: true
- run: npm run pretest
posttest:
if: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ljharb/actions/node/install@main
name: 'nvm install ${{ matrix.node-version }} && npm install'
with:
node-version: 'lts/*'
skip-ls-check: true
- run: npm run posttest
+63
View File
@@ -0,0 +1,63 @@
name: 'Tests: node.js'
on: [pull_request, push]
jobs:
matrix:
runs-on: ubuntu-latest
outputs:
latest: ${{ steps.set-matrix.outputs.requireds }}
minors: ${{ steps.set-matrix.outputs.optionals }}
steps:
- uses: ljharb/actions/node/matrix@main
id: set-matrix
with:
preset: '^18.17.0 || >=20.5.0'
type: 'minors'
latest:
needs: [matrix]
name: 'latest minors'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.matrix.outputs.latest) }}
steps:
- uses: actions/checkout@v4
- uses: ljharb/actions/node/install@main
name: 'nvm install ${{ matrix.node-version }} && npm install'
with:
node-version: ${{ matrix.node-version }}
skip-ls-check: true
- run: npm run tests-only
- uses: codecov/codecov-action@v2
minors:
needs: [matrix, latest]
name: 'non-latest minors'
continue-on-error: true
if: ${{ !github.head_ref || !startsWith(github.head_ref, 'renovate') }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.matrix.outputs.minors) }}
steps:
- uses: actions/checkout@v4
- uses: ljharb/actions/node/install@main
name: 'nvm install ${{ matrix.node-version }} && npm install'
with:
node-version: ${{ matrix.node-version }}
skip-ls-check: true
- run: npm run tests-only
- uses: codecov/codecov-action@v2
node:
name: 'node 18+'
needs: [latest, minors]
runs-on: ubuntu-latest
steps:
- run: 'echo tests completed'
+15
View File
@@ -0,0 +1,15 @@
name: Automatic Rebase
on: [pull_request_target]
jobs:
_:
name: "Automatic Rebase"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ljharb/rebase@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,12 @@
name: Require “Allow Edits”
on: [pull_request_target]
jobs:
_:
name: "Require “Allow Edits”"
runs-on: ubuntu-latest
steps:
- uses: ljharb/require-allow-edits@main
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Jordan Harband
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+2
View File
@@ -0,0 +1,2 @@
# list-exports
Given a package name and a version number, or a path to a package.json, what specifiers does it expose?
+52
View File
@@ -0,0 +1,52 @@
{
"name": "list-exports-monorepo",
"version": "0.0.0",
"private": true,
"description": "Given a package name and a version number, or a path to a package.json, what specifiers does it expose?",
"scripts": {
"lint": "eslint . '**/bin/*'",
"pretest": "npm run lint",
"tests-only": "tape packages/tests",
"tests:conditions": "tape packages/tests/conditions",
"test": "npm run tests-only && npm run tests:conditions",
"posttest": "aud --production",
"since": "node since"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ljharb/list-exports.git"
},
"keywords": [
"exports",
"cjs",
"esm",
"module",
"commonjs",
"es",
"export",
"entrypoint",
"resolve"
],
"author": "Jordan Harband <ljharb@gmail.com>",
"funding": {
"url": "https://github.com/sponsors/ljharb"
},
"devDependencies": {
"@ljharb/eslint-config": "^21.1.0",
"aud": "^2.0.4",
"eslint": "=8.8.0",
"evalmd": "^0.0.19",
"glob-gitignore": "^1.0.14",
"has-dynamic-import": "^2.1.0",
"ls-engines": "^0.9.1",
"node-exports-info": "^1.3.0",
"semver": "^7.6.0",
"tape": "^5.7.5"
},
"engines": {
"node": "^18.17.0 || >=20.5.0"
},
"workspaces": [
"packages/*"
]
}
+21
View File
@@ -0,0 +1,21 @@
{
"root": true,
"extends": "@ljharb/eslint-config/node/10",
"rules": {
"complexity": 0,
"eqeqeq": [2, "allow-null"],
"func-style": 1,
"id-length": 0,
"max-lines": 0,
"max-lines-per-function": 0,
"max-params": 1,
"max-statements": 1,
"multiline-comment-style": 0,
"new-cap": [2, {
"capIsNewExceptions": ["GetIntrinsic"],
}],
"sort-keys": 0,
},
}
+123
View File
@@ -0,0 +1,123 @@
# list-exports <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
[![github actions][actions-image]][actions-url]
[![coverage][codecov-image]][codecov-url]
[![License][license-image]][license-url]
[![Downloads][downloads-image]][downloads-url]
[![npm badge][npm-badge-png]][package-url]
Given a path to a package.json, what specifiers does it expose?
The package export defaults an `async function`. It takes a path to a `package.json` as the only required argument.
It fulfills with an object with the following structure:
- `name` the package name
- `version`: the package version
- `engines`: the package's `engines` requirements
- `problems`: a Set of strings describing problems or validation issues encountered during exports traversal. <sub>Note that these errors *do not* necessarily interfere with the listed entry points being accessible at runtime.</sub>
- `exports`: an object with the following structure:
- `binaries`: a Map of executable program names, to the relative file path that name will execute.
- `latest`: a string describing the latest “[category][]” in the given node version range. This [category][] will be present in the following list.
- `...categories`: each [category][] that the node version range overlaps will have an object with this structure:
- `import`: a Map of import specifier, to relative file path
- `require`: a Map of require specifier, to relative file path
- `files`: a Set of relative file paths that are included in `import` and/or `require`
- `tree`: a Map. Its keys are filenames (no leading `./`), whose values are a Set of `import` or `require` specifiers that point to it; or, directory names, whose values are a Map of the same recursive structure as `tree` itself.
- `pre-exports`: this [category][] will always be present, whether it's in the above list or not, with the above structure.
In addition to the required `package.json` path, it also takes a second argument, an options object. This object supports the following properties:
- `node`: either `true`, which reads the `engines.node` field in `package.json`, or a valid semver range of node versions to target. Defaults to the current node version.
## Example
<details>
<summary>`const expected`:</summary>
```js
const expected = {
name: 'list-exports',
version: '1.1.0',
engines: { node: '^18.17.0 || >=20.5.0' },
problems: new Set(),
exports: {
binaries: {},
latest: 'pattern-trailers-no-dir-slash',
'pattern-trailers-no-dir-slash': {
import: new Map([
['.', './index.js'],
]),
require: new Map([
['.', './index.js'],
['./package.json', './package.json'],
]),
files: new Set([
'./index.js',
'./package.json',
]),
tree: new Map([
['index.js', new Set(['.'])],
['package.json', new Set(['./package.json'])],
]),
},
'pre-exports': {
import: new Map(),
require: new Map([
['.', './index.js'],
['./', './index.js'],
['./index', './index.js'],
['./index.js', './index.js'],
['./package', './package.json'],
['./package.json', './package.json'],
]),
files: new Set([
'./index.js',
'./package.json',
]),
tree: new Map([
['index.js', new Set([
'.',
'./',
'./index.js',
'./index',
])],
['package.json', new Set([
'./package.json',
'./package',
])],
]),
},
},
};
```
</details>
```js
const assert = require('assert');
const path = require('path');
const listExports = require('list-exports');
listExports('./package.json', { node: true }).then((data) => {
assert.deepEqual(data, expected);
}).catch((e) => {
console.error(e);
process.exit(1);
});
```
[package-url]: https://npmjs.org/package/list-exports
[npm-version-svg]: https://versionbadg.es/ljharb/list-exports.svg
[deps-svg]: https://david-dm.org/ljharb/list-exports.svg
[deps-url]: https://david-dm.org/ljharb/list-exports
[dev-deps-svg]: https://david-dm.org/ljharb/list-exports/dev-status.svg
[dev-deps-url]: https://david-dm.org/ljharb/list-exports#info=devDependencies
[npm-badge-png]: https://nodei.co/npm/list-exports.png?downloads=true&stars=true
[license-image]: https://img.shields.io/npm/l/list-exports.svg
[license-url]: LICENSE
[downloads-image]: https://img.shields.io/npm/dm/list-exports.svg
[downloads-url]: https://npm-stat.com/charts.html?package=list-exports
[codecov-image]: https://codecov.io/gh/ljharb/list-exports/branch/main/graphs/badge.svg
[codecov-url]: https://app.codecov.io/gh/ljharb/list-exports/
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/list-exports
[actions-url]: https://github.com/ljharb/list-exports/actions
[category]: https://github.com/inspect-js/node-exports-info#categories
+930
View File
@@ -0,0 +1,930 @@
'use strict';
/* eslint no-negated-condition: 1 */
const {
lstatSync,
existsSync,
realpathSync,
} = require('fs');
const {
basename,
dirname,
extname,
join: pathJoin,
normalize: pathNormalize,
relative: pathRelative,
sep: pathSep,
} = require('path');
const readPackageJSON = require('read-package-json');
const entries = require('object.entries');
const fromEntries = require('object.fromentries');
const flatMap = require('array.prototype.flatmap');
const flat = require('array.prototype.flat');
const filter = require('array.prototype.filter');
const some = require('array.prototype.some');
const resolve = require('resolve');
const packlist = require('npm-packlist');
const getPackageType = require('get-package-type').sync;
const inspect = require('object-inspect');
const Arborist = require('@npmcli/arborist');
const forEach = require('for-each');
const {
validRange,
intersects,
} = require('semver');
const includes = require('array-includes');
const map = require('array.prototype.map');
const reduce = require('array.prototype.reduce');
const startsWith = require('string.prototype.startswith');
const endsWith = require('string.prototype.endswith');
const GetIntrinsic = require('get-intrinsic');
const callBind = require('call-bind');
const callBound = require('call-bind/callBound');
const keys = require('object-keys');
const sortPaths = require('sort-paths');
const arrayFrom = require('array.from');
const hasOwn = require('hasown');
const validateExportsObject = require('validate-exports-object');
const getCategoriesForRange = require('node-exports-info/getCategoriesForRange');
const getConditionsForCategory = require('node-exports-info/getConditionsForCategory');
const $concat = callBound('Array.prototype.concat');
const $sort = callBound('Array.prototype.sort');
const $localeCompare = callBound('String.prototype.localeCompare');
const $replace = callBound('String.prototype.replace');
const $split = callBound('String.prototype.split');
const $all = callBind(GetIntrinsic('%Promise.all%'), Promise);
const $resolve = callBind(GetIntrinsic('%Promise.resolve%'), Promise);
function isDirectory(file) {
try {
return lstatSync(file).isDirectory();
} catch (e) {
return false;
}
}
function resolveFrom(file, basedir, extensions) {
try {
return resolve.sync(file, { basedir, extensions });
} catch (e) {
return null;
}
}
function stringSort(a, b) {
return $localeCompare(a, b);
}
function sortFiles(tree) {
return fromEntries(flatMap(entries(tree), ([k, v]) => {
if (k === 'hasDirSlash') {
return [];
}
if (k === 'files') {
return [[k, new Set(sortPaths(filter(arrayFrom(v), Boolean), '/'))]];
}
if (k === 'require' || k === 'import') {
return [[k, new Map(sortPaths(filter(arrayFrom(v), ([, vv]) => vv), ([a]) => a, '/'))]];
}
return [[k, v]];
}));
}
function getExtensions(packageType = 'commonjs') {
if (packageType !== 'commonjs' && packageType !== 'module') {
throw new TypeError(`unknown package type found: ${inspect(packageType)}`);
}
const base = filter(
keys(require.extensions),
(x) => startsWith(x, '.') && (packageType !== 'module' || x !== '.js') && x !== '.mjs',
);
const legacy = packageType === 'module' ? $concat(base, '.js') : base;
const esm = $concat(['.mjs'], packageType === 'module' ? '.js' : []);
const all = $concat([], esm, '.cjs', base);
return {
all,
base,
esm,
legacy,
};
}
function isCJS(filename, usingExports = false) {
const packageType = getPackageType(filename);
if (packageType !== 'commonjs' && packageType !== 'module') {
throw new TypeError(`unknown package type found: ${inspect(packageType)}`);
}
const { base, legacy } = getExtensions(packageType);
return includes(usingExports ? base : legacy, extname(filename));
}
function isESM(filename) {
const packageType = getPackageType(filename);
if (packageType !== 'commonjs' && packageType !== 'module') {
throw new TypeError(`unknown package type found: ${inspect(packageType)}`);
}
const { esm } = getExtensions(packageType);
return includes(esm, extname(filename));
}
async function readPackage(packageJSON) {
return new Promise((resolveP, rejectP) => {
readPackageJSON(packageJSON, (err, data) => {
if (err) {
rejectP(err);
} else {
resolveP(data);
}
});
});
}
async function serial(items, cb) {
return reduce(
items,
async (prev, item) => {
await prev;
return cb(item);
},
$resolve(),
);
}
async function asyncReduce(items, task, initial = void undefined) {
return reduce(
items,
async (prev, value) => task(await prev, value),
initial,
);
}
async function asyncForEach(items, task) {
return asyncReduce(
items,
async (prev, item) => task(item),
);
}
async function getMain(rootDir, dir, extensions, problems) {
let hasExplicitMain = false;
let main;
const fullDir = pathJoin(rootDir, dir);
const pkgJSON = pathJoin(fullDir, 'package.json');
const hasPkgJSON = existsSync(pkgJSON);
if (hasPkgJSON) {
try {
const pkg = await readPackage(pkgJSON);
hasExplicitMain = 'main' in pkg;
if (hasExplicitMain) {
if (typeof pkg.main !== 'string') {
return null;
}
main = $replace(pathNormalize(pkg.main), /^(?:\.\/)?/, './');
}
} catch (e) {
problems.add(`\`${dir}\` has a \`package.json\`, but it is invalid!`);
}
}
if (hasExplicitMain) {
const fullMain = resolveFrom(main, fullDir, extensions);
const fullMainExists = existsSync(fullMain);
if (fullMainExists) {
return `./${pathRelative(rootDir, fullMain)}`;
}
}
const indexMain = resolveFrom('./index.js', fullDir, extensions);
if (existsSync(indexMain)) {
if (hasExplicitMain) {
problems.add(`\`${dir}\` has a \`package.json\`, but its \`main\` does not exist, although \`index.js\` does.`);
}
return `./${pathRelative(rootDir, indexMain)}`;
} else if (hasExplicitMain) {
problems.add(`\`${dir}\` has a \`package.json\`, but both its \`main\` and \`index.js\` do not exist!`);
}
if (dir === '.') {
problems.add(`\`${dir}\` has a \`package.json\`, but lacks both a \`main\` and an \`index.js\`!`);
}
return null;
}
function safeSet(mapInstance, key, newVal) {
if (!mapInstance.has(key)) {
mapInstance.set(key, newVal);
}
}
async function forEachSubfile(realFile, {
dir,
options,
rootDir,
mains,
tree,
packageExports,
}, fakeFile = realFile) {
const ext = extname(realFile);
const extensionless = basename(fakeFile, ext);
const realFullFile = pathJoin(rootDir, realFile);
const canRequire = !options.useType || (
ext !== '.mjs'
&& (
getPackageType(realFullFile) !== 'module' // not type module
|| ext !== '.js' // not .js
)
);
const dirMain = mains && mains.get(dir);
const canImport = options.useType && ext !== '.json';
if (canImport) {
if (!options.skipMainDot && mains && mains.get('.') === realFile && !hasOwn(packageExports || {}, '.')) {
safeSet(tree.import, '.', realFile);
}
if (!hasOwn(packageExports || {}, fakeFile)) {
safeSet(tree.import, fakeFile, realFile);
}
}
if (canRequire) {
if (dirMain === realFile) {
safeSet(tree.require, dir, realFile);
safeSet(tree.require, `${dir}/`, realFile);
}
if (mains && mains.get('.') === realFile) {
if (!options.skipMainDot && !hasOwn(packageExports || {}, '.')) {
safeSet(tree.require, '.', realFile);
}
if (!options.skipDirSlash) {
safeSet(tree.require, './', realFile);
}
}
if (!hasOwn(packageExports || {}, fakeFile)) {
safeSet(tree.require, fakeFile, realFile);
}
if (ext !== '.cjs' && ext !== '.mjs') {
const extlessFile = `${dir}/${extensionless}`;
if (!hasOwn(packageExports || {}, extlessFile)) {
safeSet(tree.require, extlessFile, realFile);
}
}
}
if (canRequire || canImport) {
tree.files.add(realFile);
}
}
function newTree() {
return {
import: new Map(),
require: new Map(),
files: new Set(),
tree: new Map(),
hasDirSlash: null, // will be deleted
};
}
async function traverseDir(
dir,
rootDir,
filteredFiles,
mains,
packageExports,
options = {},
tree = newTree(),
) {
const subFiles = new Set();
const subDirs = new Set();
forEach(
filter(
arrayFrom(filteredFiles, (file) => `./${file}`),
(file) => startsWith(file, `${dir}/`),
),
(file) => {
const subFile = $replace(file, `${dir}/`, '');
const subFileParts = $split(subFile, pathSep);
// ignore published files inside a node_modules dir
if (!includes(subFileParts, 'node_modules')) {
if (includes(subFile, pathSep)) {
subDirs.add(subFileParts[0]);
} else {
subFiles.add(file);
}
}
},
);
const dirMain = mains.get(dir);
if (dirMain) {
const fullDirMain = pathJoin(rootDir, dir, dirMain);
const canRequire = isCJS(fullDirMain, options.useType);
const canImport = options.useType && isESM(fullDirMain);
if (canImport) {
tree.import.set(dir, dirMain);
}
if (canRequire) {
safeSet(tree.require, dir, dirMain);
const dirSlash = `${dir}/`;
safeSet(tree.require, dirSlash, dirMain);
}
}
await serial(arrayFrom(subFiles), (file) => forEachSubfile(file, {
dir,
options,
rootDir,
mains,
tree,
packageExports,
}));
// build up the tree structure, from all included files
tree.files.forEach((file) => {
const parts = $split(file, '/');
reduce(parts, (acc, part, i) => {
if (part === '.') {
return acc.tree;
}
const isLastPart = i + 1 === parts.length;
safeSet(acc, part, isLastPart ? new Set() : new Map());
return acc.get(part);
}, tree);
});
function addToTree(file, specifier) {
const parts = $split(file, '/');
reduce(parts, (acc, part, i) => {
if (part === '.') {
return acc.tree;
}
const isLastPart = i + 1 === parts.length;
if (!acc.has(part)) {
safeSet(acc, part, isLastPart ? new Set() : new Map());
}
const item = acc.get(part);
if (isLastPart) {
item.add(specifier);
}
return item;
}, tree);
}
tree.require.forEach(addToTree);
tree.import.forEach(addToTree);
await $all(arrayFrom(subDirs, (subDir) => traverseDir(
`./${pathJoin(dir, subDir)}`,
rootDir,
filteredFiles,
mains,
packageExports,
options,
tree,
)));
return sortFiles(tree);
}
function addMainString(string, packageDir, tree) {
const main = `./${pathNormalize(string)}`;
const fullMain = pathJoin(packageDir, main);
if (existsSync(fullMain)) {
const resolved = `./${pathRelative(packageDir, fullMain)}`;
if (isESM(main)) {
if (!tree.import.has(main)) {
tree.import.set('.', resolved);
tree.files.add(main);
}
} else if (isCJS(main, true)) {
if (!tree.import.has(main)) {
tree.import.set('.', resolved);
tree.files.add(main);
}
if (!tree.require.has(main)) {
tree.require.set('.', resolved);
tree.files.add(main);
}
}
}
}
function addFullPath(
packageDir,
category,
tree,
lhs,
rhs,
conditionChain,
problems,
filteredFiles,
) {
if (startsWith(rhs, './')) {
const fullPath = pathJoin(packageDir, rhs);
if (filteredFiles.has($replace(rhs, /^\.\//, '')) && existsSync(fullPath)) {
const ext = extname(fullPath);
const canRequire = ext !== '.mjs'
&& (
!isESM(fullPath) // not type module
|| ext !== '.js' // not .js
)
&& !includes(conditionChain, 'import');
const canImport = category !== 'broken'
&& ext !== '.json'
&& !includes(conditionChain, 'require');
if (canImport) {
safeSet(tree.import, lhs, rhs);
}
if (canRequire) {
safeSet(tree.require, lhs, rhs);
}
if (tree.import.get(lhs) === rhs || tree.require.get(lhs) === rhs) {
tree.files.add(rhs);
}
return true;
}
problems.add(`${lhs}”: ${rhs} does not appear to exist!`);
} else {
problems.add(`\`exports[${lhs}]\`: ${rhs} must start with \`./\``);
}
return false;
}
function hasDirSlash(category) {
return category !== 'broken-dir-slash-conditions' && category !== 'patterns' && category !== 'pattern-trailers-no-dir-slash';
}
function traverseExportsSubtree({
tree,
subtree,
problems,
packageDir,
packageExports,
mains,
dir,
lhs,
rhs,
category,
}) {
subtree.forEach((value, key) => {
if (value instanceof Set) {
// it's a file
const relativeFilePath = `./${pathJoin(dir, key)}`;
const replacedFilePath = $replace(relativeFilePath, lhs, rhs);
forEachSubfile(relativeFilePath, {
dir,
options: {
useType: true,
skipMainDot: true,
skipDirSlash: !hasDirSlash(category),
},
rootDir: packageDir,
mains,
tree,
packageExports,
}, replacedFilePath);
} else if (value instanceof Map) {
// it's a dir
traverseExportsSubtree({
tree,
subtree: value,
problems,
packageDir,
packageExports,
mains,
dir: `./${pathJoin(dir, key)}`,
lhs,
rhs,
category,
});
} else {
throw new TypeError('tree has a non-collection value!');
}
});
}
function traverseExportsSubdir({
packageDir,
packageExports,
lhs,
rhs,
problems,
tree,
legacy,
mains,
category,
}) {
const fullRHS = pathJoin(packageDir, rhs);
// traverse into rhs, mapping paths to lhs
if (!existsSync(fullRHS)) {
problems.add(`\`${lhs}\`: \`${rhs}\` does not appear to exist!`);
} else if (!isDirectory(fullRHS)) {
problems.add(`\`${lhs}\`: \`${rhs}\` is not a directory!`);
} else {
const subtree = rhs === './' ? legacy.tree : legacy.tree.get(rhs);
if (subtree) {
traverseExportsSubtree({
tree,
subtree,
problems,
packageDir,
packageExports,
mains,
dir: '.',
lhs,
rhs,
category,
});
}
}
}
async function forEachExportEntry([lhs, maybeRHS], conditionChain, {
packageDir,
packageExports,
problems,
category,
conditions,
tree,
legacy,
filteredFiles,
mains,
}) {
return asyncReduce($concat([], maybeRHS), async (prev, rhs) => {
if (await prev) {
return true;
}
if (typeof rhs === 'string') {
rhs = decodeURI(rhs); // eslint-disable-line no-param-reassign
if (endsWith(lhs, '/') && endsWith(rhs, '/')) {
if (category === 'pattern-trailers-no-dir-slash') {
return false;
}
tree.hasDirSlash = true; // eslint-disable-line no-param-reassign
traverseExportsSubdir({
packageDir,
packageExports,
lhs,
rhs,
problems,
tree,
legacy,
mains,
category,
});
return true;
}
return addFullPath(
packageDir,
category,
tree,
lhs,
rhs,
conditionChain,
problems,
filteredFiles,
);
}
const rhsResults = validateExportsObject(rhs);
rhsResults.problems.forEach((problem) => {
problems.add(problem);
});
if (rhsResults.status === 'files') {
problems.add('`./package.json`: inside a conditions object, a files object (keys starting with `.`) is invalid');
return false;
}
if (category !== 'broken') {
const validConditionEntries = filter(entries(rhs), ([x]) => conditions.has(x));
if (validConditionEntries.length === 0) {
safeSet(tree.import, lhs, false);
safeSet(tree.require, lhs, false);
return false;
}
return asyncReduce(validConditionEntries, (matchedSomething, [condition, conditionRHS]) => {
if (typeof conditionRHS === 'string') {
if (endsWith(lhs, '/') && endsWith(conditionRHS, '/')) {
return traverseExportsSubdir({
packageDir,
packageExports,
lhs,
rhs: conditionRHS,
problems,
tree,
legacy,
mains,
});
}
return addFullPath(
packageDir,
category,
tree,
lhs,
conditionRHS,
conditionChain.concat(condition),
problems,
filteredFiles,
) || matchedSomething;
}
return forEachExportEntry([lhs, conditionRHS], $concat(conditionChain, condition), {
packageDir,
packageExports,
problems,
category,
conditions,
tree,
legacy,
filteredFiles,
mains,
}) || matchedSomething;
}, false);
}
return false;
}, false);
}
async function traverseExports(category, packageDir, pkgData, filteredFiles, legacy, mains, problems) {
const tree = newTree();
function addToTree(file, specifier) {
if (file !== false) {
const parts = $split(file, '/');
reduce(parts, (acc, part, i) => {
if (part === '.') {
return acc.tree;
}
let item = acc.get(part);
if (i + 1 === parts.length) {
if (!item) {
item = new Set();
}
item.add(specifier);
} else if (!item) {
item = new Map();
}
acc.set(part, item);
return item;
}, tree);
}
}
const conditions = new Set(getConditionsForCategory(category));
if (typeof pkgData.exports === 'string') {
addMainString(pkgData.exports, packageDir, tree);
} else {
// handle array fallback for main
const exportValues = flat($concat([], pkgData.exports), Infinity);
await asyncReduce(exportValues, async (prev, value) => { // TODO: fixtures for nested arrays in "broken"
if (await prev) {
return true;
}
if (typeof value === 'string') {
addMainString(value, packageDir, tree);
return true;
}
if (value && typeof value === 'object') {
const topLevelResults = validateExportsObject(value);
topLevelResults.problems.forEach((problem) => {
problems.add(problem);
});
if (topLevelResults.status === 'empty') {
return false;
}
if (topLevelResults.status === 'conditions') {
if (category === 'broken') {
safeSet(tree.import, '.', false);
safeSet(tree.require, '.', false);
return false;
}
return forEachExportEntry(['.', value], [], {
packageDir,
packageExports: pkgData.exports,
problems,
category,
conditions,
tree,
legacy,
filteredFiles,
mains,
});
}
if (topLevelResults.status !== 'files') {
console.error({ topLevelResults });
throw new TypeError(`unknown top-level exports object type found: ${topLevelResults.status}`);
}
return asyncForEach(
entries(value),
([lhs, rhs]) => {
const matched = forEachExportEntry([lhs, rhs], [], {
packageDir,
packageExports: pkgData.exports,
problems,
category,
conditions,
tree,
legacy,
filteredFiles,
mains,
});
if (!matched) {
safeSet(tree.import, lhs, false);
safeSet(tree.require, lhs, false);
}
},
);
}
return false;
}, false);
}
tree.require.forEach(addToTree);
tree.import.forEach(addToTree);
return sortFiles(tree);
}
async function traverseMains(rootDir, filteredFiles, extensions, problems) {
// first pass: get every dir and its alleged main
const dirs = new Map(await $all(arrayFrom(
new Set(arrayFrom(filteredFiles, (file) => dirname(`./${file}`))),
async (dir) => [dir, await getMain(rootDir, dir, extensions, problems)],
)));
// second pass: any alleged main that points to a dir, remap it to an actual main
return new Map(filter(
arrayFrom(dirs, ([dir, maybeMain]) => {
const found = maybeMain && dirs.get($replace(maybeMain, /\/?$/, ''));
return [dir, found && endsWith(found, '/') ? `./${pathJoin(found, 'index.js')}` : found || maybeMain];
}),
([, x]) => x,
));
}
async function getExports(packageDir, pkgData, nodeRange, problems) {
const {
type: rootType = 'commonjs',
} = pkgData;
const { all: rootAllExtensions, base: rootBaseExtensions } = getExtensions(rootType);
const arborist = new Arborist({ path: packageDir });
const arbTree = await arborist.loadActual();
const packedFiles = await packlist(arbTree, { path: packageDir });
/* eslint function-paren-newline: 0 */
const filteredFiles = new Set(
$sort(
filter(
flatMap(
packedFiles,
(x) => {
const resolved = resolveFrom(dirname(x), packageDir, rootAllExtensions);
return [
x,
resolved && pathRelative(packageDir, resolved),
];
},
),
(x) => x && some(rootAllExtensions, (ext) => endsWith(x, ext)),
),
),
);
const mains = await traverseMains(packageDir, filteredFiles, rootBaseExtensions, problems);
const legacyP = traverseDir('.', packageDir, filteredFiles, mains, {});
const categories = getCategoriesForRange(nodeRange);
const latest = categories[0];
const binaryEntries = typeof pkgData.bin === 'string'
? [[pkgData.name, pkgData.bin]]
: entries(pkgData.bin || {});
const binaries = fromEntries(flatMap(binaryEntries, ([n, p]) => {
const resolved = resolveFrom($replace(p, /^(?:\.\/)?/, './'), packageDir, $concat('', rootAllExtensions));
if (resolved) {
const relativeBin = `./${pathRelative(packageDir, resolved)}`;
return [[n, relativeBin]];
}
return [];
}));
if (categories.length === 1 && latest === 'pre-exports') {
return {
binaries,
latest,
[latest]: await legacyP,
};
}
if (!('exports' in pkgData)) {
const [
postExports,
preExports,
] = await $all([
traverseDir('.', packageDir, filteredFiles, mains, pkgData.exports, { useType: true }),
legacyP,
]);
return {
binaries,
latest: 'conditions',
conditions: postExports,
'pre-exports': preExports,
};
}
// traverse "exports", respect "type" field, etc
const legacy = await legacyP;
const categoryExports = await $all(map(categories, async (category) => [
category,
category === 'pre-exports'
? legacy
: await traverseExports(category, packageDir, pkgData, filteredFiles, legacy, mains, problems),
]));
return {
binaries,
latest,
...fromEntries(categoryExports),
'pre-exports': legacy,
};
}
module.exports = async function listExports(packageJSON, options = {}) {
const packageJSONpath = realpathSync(packageJSON);
const packageDir = dirname(packageJSONpath);
const pkgData = await readPackage(packageJSON);
const {
name,
version,
private: isPrivate,
engines = { node: '*' },
} = pkgData;
let node = process.version;
if (options.node === true) {
({ node } = engines);
if (!validRange(node)) {
throw new RangeError('when the provided node version is `true`, this packages `engines.node` declaration must be a valid semver range');
}
} else if ('node' in options) {
if (!validRange(options.node)) {
throw new RangeError('`node` option must be `true`, or a valid semver range');
}
({ node } = options);
}
const problems = new Set();
if (!intersects(engines.node || '*', node)) {
problems.add('node' in options
? `the provided node version (${node}) does not match the packages \`engines.node\` declaration (${engines.node || '*'})`
: `the current node version (${node}) does not match the packages \`engines.node\` declaration (${engines.node || '*'})`);
}
if (isPrivate) {
return {
name,
version,
private: !!isPrivate,
problems: new Set($sort(arrayFrom(problems), stringSort)),
};
}
const exports = await getExports(packageDir, pkgData, node, problems);
return {
name,
version,
engines: {
node: '*',
...engines,
},
problems: new Set($sort(arrayFrom(problems), stringSort)),
exports,
};
};
// Map/Set has/add/get/set
@@ -0,0 +1,84 @@
{
"name": "list-exports",
"version": "1.1.0",
"description": "Given a package name and a version number, or a path to a package.json, what specifiers does it expose?",
"main": "index.js",
"exports": {
".": "./index.js",
"./package.json": "./package.json"
},
"scripts": {
"prepublish": "not-in-publish || (safe-publish-latest && cp ../../LICENSE ./)",
"lint": "eslint .",
"postlint": "evalmd README.md",
"pretest": "npm run lint",
"test": ">&2 echo tests are ran in the monorepo only",
"posttest": "aud --production"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ljharb/list-exports.git",
"directory": "./packages/list-exports"
},
"keywords": [
"exports",
"cjs",
"esm",
"module",
"commonjs",
"es",
"export",
"entrypoint",
"resolve"
],
"author": "Jordan Harband <ljharb@gmail.com>",
"funding": {
"url": "https://github.com/sponsors/ljharb"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/ljharb/list-exports/issues"
},
"homepage": "https://github.com/ljharb/list-exports#readme",
"dependencies": {
"@npmcli/arborist": "^7.3.1",
"array-includes": "^3.1.7",
"array.from": "^1.1.5",
"array.prototype.filter": "^1.0.3",
"array.prototype.flat": "^1.3.2",
"array.prototype.flatmap": "^1.3.2",
"array.prototype.map": "^1.0.6",
"array.prototype.reduce": "^1.0.6",
"array.prototype.some": "^1.1.5",
"call-bind": "^1.0.7",
"for-each": "^0.3.3",
"get-intrinsic": "^1.2.4",
"get-package-type": "^0.1.0",
"hasown": "^2.0.1",
"node-exports-info": "^1.3.0",
"npm-packlist": "^8.0.2",
"object-inspect": "^1.13.1",
"object-keys": "^1.1.1",
"object.entries": "^1.1.7",
"object.fromentries": "^2.0.7",
"read-package-json": "^7.0.0",
"resolve": "^2.0.0-next.5",
"semver": "^7.6.0",
"sort-paths": "^1.1.1",
"string.prototype.endswith": "^1.0.1",
"string.prototype.startswith": "^1.0.0",
"validate-exports-object": "^1.0.1"
},
"devDependencies": {
"@ljharb/eslint-config": "^21.1.0",
"aud": "^2.0.4",
"eslint": "=8.8.0",
"evalmd": "^0.0.19",
"in-publish": "^2.0.1",
"ls-engines": "^0.9.1",
"safe-publish-latest": "^2.0.0"
},
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"root": true,
"extends": "@ljharb/eslint-config/node/10",
"rules": {
"func-style": 1,
"function-call-argument-newline": ["off", "consistent"],
"max-lines-per-function": 0,
"sort-keys": 0,
},
}
+51
View File
@@ -0,0 +1,51 @@
# ls-exports <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
[![github actions][actions-image]][actions-url]
[![coverage][codecov-image]][codecov-url]
[![License][license-image]][license-url]
[![Downloads][downloads-image]][downloads-url]
[![npm badge][npm-badge-png]][package-url]
Given a package name and a version number, or a path to a package.json, what specifiers does it expose?
The package export defaults an `async function`. It fulfills with an object with the following keys:
- `name` the package name
- `version`: the package version
- `engines`: the package's `engines` requirements
- `binaries`: the files that are made available as executable programs
- `errors`: any validation errors encountered during parsing. <sub>Note that these errors *do not* necessarily interfere with the listed entry points being accessible at runtime.</sub>
For ESM-supporting node versions (at the time of this writing, `^12.17 || >= 13.2`):
- `require`: valid specifiers to pass into `require`
- `import`: valid specifiers to pass into `import()`, or to use in a static `import` statement
- `files`: all files on the filesystem that are directly exposed by the above entry points
- `tree`: a hierarchical object structure where each directory is represented as a key containing an object, and each file is represented as a key containing a list of the entry points that expose that file
For node versions prior to ESM support (at the time of this writing, `< 12.17 || ~13.0 || ~13.1`):
- `require (pre-exports)`: valid specifiers to pass into `require`
- `files (pre-exports)`: all files on the filesystem that are directly exposed by the above entry points
- `tree (pre-exports)`: a hierarchical object structure where each directory is represented as a key containing an object, and each file is represented as a key containing a list of the entry points that expose that file
## Example
```sh
ls-exports package resolve@1
```
[package-url]: https://npmjs.org/package/ls-exports
[npm-version-svg]: https://versionbadg.es/ljharb/list-exports.svg
[deps-svg]: https://david-dm.org/ljharb/list-exports.svg
[deps-url]: https://david-dm.org/ljharb/list-exports
[dev-deps-svg]: https://david-dm.org/ljharb/list-exports/dev-status.svg
[dev-deps-url]: https://david-dm.org/ljharb/list-exports#info=devDependencies
[npm-badge-png]: https://nodei.co/npm/ls-exports.png?downloads=true&stars=true
[license-image]: https://img.shields.io/npm/l/ls-exports.svg
[license-url]: LICENSE
[downloads-image]: https://img.shields.io/npm/dm/ls-exports.svg
[downloads-url]: https://npm-stat.com/charts.html?package=ls-exports
[codecov-image]: https://codecov.io/gh/ljharb/list-exports/branch/main/graphs/badge.svg
[codecov-url]: https://app.codecov.io/gh/ljharb/list-exports/
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/list-exports
[actions-url]: https://github.com/ljharb/list-exports/actions
[category]: https://github.com/inspect-js/node-exports-info#categories
@@ -0,0 +1,71 @@
#!/usr/bin/env node
'use strict';
const path = require('path');
const npa = require('npm-package-arg');
const colorize = require('json-colorizer');
const yargs = require('yargs');
const fromEntries = require('object.fromentries');
const arrayFrom = require('array.from');
const listExports = require('list-exports');
const exportsTable = require('../exportsTable');
const getPackageJSONPath = require('../getPackageJSONPath');
const argv = yargs
.option('json', {
describe: 'output the results as JSON',
default: false,
type: 'boolean',
})
.command(
'package <specifier>', 'list the exports for the given package (that matches the optionally given version)',
(y) => y.positional('specifier', {
coerce(arg) {
npa(arg);
return String(arg);
},
conflicts: 'path',
describe: 'package specifier with optional version, ex `foo`, `@scope/foo`, `foo@^1`, etc',
type: 'string',
}),
)
.command(
'path <path>', 'list the exports for the relative path to a directory containing a `package.json`',
(y) => y.positional('path', {
coerce(arg) {
return path.join(path.resolve(arg), 'package.json');
},
describe: `a relative path to a directory containing a \`package.json\`
Usage: $0 --path=./path/to/directory
`,
required: true,
requiresArg: true,
}),
)
.demandCommand(1, 'must specify a command: “path” or “package”')
.strict()
.help()
.parse();
const packageDirP = argv.path ? Promise.resolve(argv.path) : getPackageJSONPath(argv.specifier);
function serializer(key, value) {
if (value instanceof Set) {
return arrayFrom(value);
}
if (value instanceof Map) {
return fromEntries(arrayFrom(value));
}
return value;
}
const promise = packageDirP.then((packageDir) => (argv.json
? listExports(packageDir).then((x) => console.log(colorize(JSON.stringify(x, serializer))))
: exportsTable(packageDir, (x) => console.log(x))));
promise.catch((err) => {
console.error(err);
process.exitCode = 1;
});
@@ -0,0 +1,99 @@
'use strict';
const colors = require('colors/safe');
const fromEntries = require('object.fromentries');
const values = require('object.values');
const stripANSI = require('strip-ansi');
const walk = require('tree-walk');
const listExports = require('list-exports');
const table = require('./table');
function sumTreeLeaves(root) {
walk.postorder(root, (value, key, parent) => {
/* eslint no-param-reassign: 1 */
if (Array.isArray(parent) || parent === root) {
return;
}
if (Array.isArray(value)) {
parent[key] = value.length;
} else if (parent) {
if (typeof value !== 'number') { // TODO: remove this, once this function no longer mutates
const sum = values(value).reduce((a, b) => a + b, 0);
if (typeof sum === 'number') {
delete parent[key];
parent[`${key}/`] = sum;
}
}
}
});
}
module.exports = async function exportsTable(packageDir, log) {
const x = await listExports(packageDir);
if (x.private) {
log(`${colors.blue(x.name)} @ ${x.version}`);
log(colors.bold.red('package is private'));
return;
}
const summaryRows = [
[
`${colors.blue(x.name)} @ ${x.version}`,
colors.green('node with ESM (>= 13.1)'),
colors.green('node pre-ESM (> 13.1)'),
].map((r) => colors.bold(r)),
[
colors.red('Binaries'),
x.binaries.length || '',
x.binaries.length || '',
],
[
colors.red('CJS + ESM Export Specifiers'),
x.require.length,
x['require (pre-exports)'].length,
],
[
colors.red('ESM-only Export Specifiers'),
x.import.length,
'',
],
[
colors.red('Exposed Files'),
x.files.length,
x['files (pre-exports)'].length,
],
];
const widths = summaryRows.reduce(
(maxes, cols) => cols
.map((col) => stripANSI(String(col)).length)
.map((len, i) => Math.max(maxes[i] || 0, len)),
[],
);
const columns = fromEntries(widths.map((width, i) => [
i,
{ width, alignment: i === 0 ? 'left' : 'right' },
]));
const tableOptions = { columns };
log(table(summaryRows, tableOptions));
sumTreeLeaves(x.tree);
sumTreeLeaves(x['tree (pre-exports)']);
log(colors.bold(`Top-level ${colors.reset.magenta('files')}/${colors.bold.cyan('directories')} that contribute specifiers:`));
const treeRows = Object.keys({ ...x.tree[x.name], ...x['tree (pre-exports)'][x.name] })
.sort((a, b) => (a.endsWith('/') ? b.endsWith('/') ? a.localeCompare(b) : -1 : 1))
.map((file) => [
file.endsWith('/') ? colors.bold.cyan(file) : colors.magenta(file),
x.tree[x.name][file],
x['tree (pre-exports)'][x.name][file],
]);
log(table(treeRows, tableOptions));
if (x.errors.length > 0) {
log(colors.bold(colors.red('!! Errors:')));
log(table([x.errors.map((e) => e.replace(process.cwd(), '$PWD'))]));
}
log(colors.dim('run the same command with `--json` for full details'));
};
@@ -0,0 +1,21 @@
'use strict';
const { promisify } = require('util');
const path = require('path');
const npa = require('npm-package-arg');
const pacote = require('pacote');
const { dir } = require('tmp');
const tmpDir = promisify(dir);
module.exports = async function getPackageJSONPath(specifier) {
const { name } = npa(specifier);
const cwd = await tmpDir();
const packageDir = path.join(cwd, 'node_modules', name);
await pacote.extract(specifier, packageDir);
return path.join(packageDir, 'package.json');
};
@@ -0,0 +1,68 @@
{
"name": "ls-exports",
"version": "1.1.1",
"description": "CLI for `list-exports`: Given a package name and a version number, or a path to a package.json, what specifiers does it expose?",
"bin": "./bin/ls-exports",
"main": false,
"exports": {
"./package.json": "./package.json"
},
"scripts": {
"prepublish": "not-in-publish || (safe-publish-latest && cp ../../LICENSE ./)",
"lint": "eslint . 'bin/**'",
"pretest": "npm run lint",
"test": ">&2 echo tests are ran in the monorepo only",
"posttest": "aud --production"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ljharb/list-exports.git",
"directory": "./packages/ls-exports"
},
"keywords": [
"exports",
"cjs",
"esm",
"module",
"commonjs",
"es",
"export",
"entrypoint",
"resolve"
],
"author": "Jordan Harband <ljharb@gmail.com>",
"funding": {
"url": "https://github.com/sponsors/ljharb"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/ljharb/list-exports/issues"
},
"homepage": "https://github.com/ljharb/list-exports#readme",
"dependencies": {
"array.from": "^1.1.5",
"colors": "=1.4.0",
"json-colorizer": "^2.2.2",
"list-exports": "^1.1.0",
"npm-package-arg": "^11.0.1",
"object.fromentries": "^2.0.7",
"object.values": "^1.1.7",
"pacote": "^17.0.6",
"strip-ansi": "^6.0.1",
"table": "^6.8.1",
"tmp": "^0.2.1",
"tree-walk": "^0.4.0",
"yargs": "^17.7.2"
},
"devDependencies": {
"@ljharb/eslint-config": "^21.1.0",
"aud": "^2.0.4",
"eslint": "=8.8.0",
"in-publish": "^2.0.1",
"ls-engines": "^0.9.1",
"safe-publish-latest": "^2.0.0"
},
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
}
+13
View File
@@ -0,0 +1,13 @@
'use strict';
const {
table: makeTable,
getBorderCharacters,
} = require('table');
module.exports = function table(data, options = {}) {
return makeTable(data, {
border: getBorderCharacters('norc'),
...options,
});
};
+1
View File
@@ -0,0 +1 @@
fixtures/*/project
+32
View File
@@ -0,0 +1,32 @@
{
"root": true,
"extends": ["@ljharb/eslint-config/node/10", "@ljharb/eslint-config/tests"],
"rules": {
"complexity": 0,
"func-style": 0,
"id-length": 0,
"max-lines-per-function": 0,
"max-len": 0,
"multiline-comment-style": 0,
"no-negated-condition": 0,
},
"overrides": [
{
"files": [
"conditions.js",
"conditions-expected.js",
],
"extends": ["@ljharb", "@ljharb/eslint-config/tests"],
"rules": {
"complexity": 0,
"func-style": 0,
"id-length": 0,
"max-lines-per-function": 0,
"no-negated-condition": 0,
}
}
],
}
@@ -0,0 +1,79 @@
'use strict';
var fs = require('fs');
var path = require('path');
var semver = require('semver');
var entries = require('object.entries');
var fromEntries = require('object.fromentries');
var hasBrokenExports = semver.satisfies(process.version, '~13.0 || ~13.1', { includePrerelease: true });
var hasPackageExports = require('has-package-exports');
var hasConditions = require('has-package-exports/conditional');
var conditionsPkg = JSON.parse(String(fs.readFileSync(path.join(__dirname, './fixtures/ex-conditions/project/package.json'))));
var empty = {};
function makeResult(slug) {
return {
resolved: path.basename(path.join(__dirname, 'fixtures/ex-conditions/project/' + slug + '.js')),
result: slug
};
}
module.exports = function getExpectedConditions(resolve) {
var expected = {
'.': makeResult(hasBrokenExports ? 'fallback' : hasPackageExports ? 'default' : 'main'),
'package.json': {
resolved: path.basename(resolve('./fixtures/ex-conditions/project/package.json')),
result: empty
},
dnri: makeResult(hasPackageExports ? hasBrokenExports ? 'fallback' : 'default' : 'dnri'),
dnir: makeResult(hasPackageExports ? hasBrokenExports ? 'fallback' : 'default' : 'dnir'),
drni: makeResult(hasPackageExports ? hasBrokenExports ? 'fallback' : 'default' : 'drni'),
drin: makeResult(hasPackageExports ? hasBrokenExports ? 'fallback' : 'default' : 'drin'),
dinr: makeResult(hasPackageExports ? hasBrokenExports ? 'fallback' : 'default' : 'dinr'),
dirn: makeResult(hasPackageExports ? hasBrokenExports ? 'fallback' : 'default' : 'dirn'),
ndri: makeResult(hasPackageExports ? hasConditions ? 'node' : hasBrokenExports ? 'fallback' : 'default' : 'ndri'),
ndir: makeResult(hasPackageExports ? hasConditions ? 'node' : hasBrokenExports ? 'fallback' : 'default' : 'ndir'),
nrdi: makeResult(hasPackageExports ? hasConditions ? 'node' : hasBrokenExports ? 'fallback' : 'default' : 'nrdi'),
nrid: makeResult(hasPackageExports ? hasConditions ? 'node' : hasBrokenExports ? 'fallback' : 'default' : 'nrid'),
nidr: makeResult(hasPackageExports ? hasConditions ? 'node' : hasBrokenExports ? 'fallback' : 'default' : 'nidr'),
nird: makeResult(hasPackageExports ? hasConditions ? 'node' : hasBrokenExports ? 'fallback' : 'default' : 'nird'),
rdni: makeResult(hasPackageExports ? hasConditions ? 'require' : hasBrokenExports ? 'fallback' : 'default' : 'rdni'),
rdin: makeResult(hasPackageExports ? hasConditions ? 'require' : hasBrokenExports ? 'fallback' : 'default' : 'rdin'),
rndi: makeResult(hasPackageExports ? hasConditions ? 'require' : hasBrokenExports ? 'fallback' : 'default' : 'rndi'),
rnid: makeResult(hasPackageExports ? hasConditions ? 'require' : hasBrokenExports ? 'fallback' : 'default' : 'rnid'),
ridn: makeResult(hasPackageExports ? hasConditions ? 'require' : hasBrokenExports ? 'fallback' : 'default' : 'ridn'),
rind: makeResult(hasPackageExports ? hasConditions ? 'require' : hasBrokenExports ? 'fallback' : 'default' : 'rind'),
idnr: makeResult(hasPackageExports ? hasBrokenExports ? 'fallback' : 'default' : 'idnr'),
idrn: makeResult(hasPackageExports ? hasBrokenExports ? 'fallback' : 'default' : 'idrn'),
indr: makeResult(hasPackageExports ? hasConditions ? 'node' : hasBrokenExports ? 'fallback' : 'default' : 'indr'),
inrd: makeResult(hasPackageExports ? hasConditions ? 'node' : hasBrokenExports ? 'fallback' : 'default' : 'inrd'),
irdn: makeResult(hasPackageExports ? hasConditions ? 'require' : hasBrokenExports ? 'fallback' : 'default' : 'irdn'),
irnd: makeResult(hasPackageExports ? hasConditions ? 'require' : hasBrokenExports ? 'fallback' : 'default' : 'irnd')
};
var actual = fromEntries(entries(expected).map(function (entry) {
var result;
var resolved;
try {
var exportPath = '@fixtures/ex-conditions' + (entry[0] === '.' ? '' : '/' + entry[0]);
// eslint-disable-next-line global-require
result = entry[0] === 'package.json' ? empty : require(exportPath);
resolved = path.basename(resolve(exportPath));
} catch (e) {
result = e;
}
return [entry[0], {
resolved: resolved,
result: result
}];
}));
return {
actual: actual,
'package': conditionsPkg,
expected: expected
};
};
+63
View File
@@ -0,0 +1,63 @@
'use strict';
var test = require('tape');
var semver = require('semver');
var whyNotEqual = require('is-equal/why');
var forEach = require('for-each');
var resolve = require('resolve/sync');
var hasBrokenExports = semver.satisfies(process.version, '~13.0 || ~13.1', { includePrerelease: true });
var hasPackageExports = require('has-package-exports');
var hasConditions = require('has-package-exports/conditional');
// var hasPatterns = require('has-package-exports/pattern');
var getExpected = require('./conditions-expected');
var re = process.env.GREP && new RegExp(process.env.GREP);
test('condition ordering', { skip: re && !re.test('condition ordering') }, function (t) {
if (hasBrokenExports) {
t.ok(
semver.satisfies(process.version, '~13.0 || ~13.1', { includePrerelease: true }),
'node ~13.0 || ~13.1: the "exports" field should not work, but does, incorrectly, and only supports a string'
);
} else if (!hasPackageExports) {
t.ok(
semver.satisfies(process.version, '<12.17', { includePrerelease: true }),
'node < 12.17: no support for the "exports" field'
);
} else if (!hasConditions) {
t.ok(
semver.satisfies(process.version, '13.2 - 13.6', { includePrerelease: true }),
'node 13.2 - 13.6: supports the "exports" fields object form, but no conditions beyond "default"'
);
} else {
t.ok(
semver.satisfies(process.version, '^12.17 || ^13.7 || >= 14', { includePrerelease: true }),
'node ^12.17 || ^13.7 || >= 14: supports the "exports" field'
);
}
forEach([
['require.resolve', getExpected(require.resolve)],
['resolve', getExpected(function (x) { return resolve(x); })]
], function (entry) {
var desc = entry[0];
var results = entry[1];
t.test(desc, { todo: desc === 'resolve' || (desc === 'require.resolve' && semver.satisfies(process.version, '< 6')) }, function (st) {
st.deepEqual(
Object.keys(results.expected).map(function (e) { return e === '.' ? e : './' + e; }),
Object.keys(results['package'].exports),
'test expects proper exports'
);
st.equal(whyNotEqual(results.expected, results.actual), '', 'expected exported values match actual exported values');
st.deepEqual(results.expected, results.actual);
st.end();
});
});
t.end();
});
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More