Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for the exports package.json attribute #224

Open
wants to merge 4 commits into
base: 1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ block_comment_end = */
[*.yml]
indent_size = 1

[package.json]
[{package.json,.gitmodules}]
indent_style = tab

[CHANGELOG.md]
Expand All @@ -27,7 +27,7 @@ indent_size = 2
[{*.json,Makefile}]
max_line_length = off

[test/{dotdot,resolver,module_dir,multirepo,node_path,pathfilter,precedence}/**/*]
[test/{dotdot,exports,list-exports,resolver,module_dir,multirepo,node_path,pathfilter,precedence}/**/*]
indent_style = off
indent_size = off
max_line_length = off
Expand Down
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
test/list-exports/
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"object-curly-newline": 0,
"operator-linebreak": [2, "before"],
"sort-keys": 0,
"eqeqeq": [2, "always", {"null": "ignore"}]
},
"overrides": [
{
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ yarn.lock

# symlinked file used in tests
test/resolver/symlinked/_/node_modules/package

# submodule
test/list-exports
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "test/list-exports"]
path = test/list-exports
url = https://github.com/ljharb/list-exports.git
branch = main
117 changes: 101 additions & 16 deletions lib/async.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* eslint-disable max-lines */
var fs = require('fs');
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 resolveExports = require('./resolve-imports-exports');

var realpathFS = fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath;

Expand Down Expand Up @@ -77,6 +79,12 @@ module.exports = function resolve(x, options, callback) {
var basedir = opts.basedir || path.dirname(caller());
var parent = opts.filename || basedir;

if (opts.exportsField == null) {
opts.exportsField = { level: 'ignore' };
} else if (typeof opts.exportsField === 'string') {
opts.exportsField = { level: opts.exportsField };
}

opts.paths = opts.paths || [];

// ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory
Expand Down Expand Up @@ -265,35 +273,112 @@ module.exports = function resolve(x, options, callback) {
});
}

function processDirs(cb, dirs) {
function loadManifestInDir(dir, cb) {
maybeRealpath(realpath, dir, opts, function (err, pkgdir) {
if (err) return cb(null);

var pkgfile = path.join(pkgdir, 'package.json');
isFile(pkgfile, function (err, ex) {
// on err, ex is false
if (!ex) return cb(null);

readFile(pkgfile, function (err, body) {
if (err) cb(err);
try { var pkg = JSON.parse(body); } catch (jsonErr) {}

if (pkg && opts.packageFilter) {
pkg = opts.packageFilter(pkg, pkgfile, dir);
}
cb(pkg);
});
});
});
}

function processDirs(cb, dirs, subpath) {
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);
if (opts.exportsField.level !== 'ignore' && endsWithSubpath(dir, subpath)) {
var pkgDir = dir.slice(0, dir.length - subpath.length);
loadManifestInDir(pkgDir, onmanifestWithExports);
} else {
onmanifest(false);
}

function onfile(err, m, pkg) {
if (err) return cb(err);
if (m) return cb(null, m, pkg);
loadAsDirectory(dir, opts.package, ondir);
function onmanifestWithExports(pkg) {
if (!pkg || pkg.exports == null) {
return onmanifest(false);
}

var resolvedExport;
try {
resolvedExport = resolveExports(opts.exportsField, pkgDir, parent, subpath, pkg.exports);
} catch (resolveErr) {
return cb(resolveErr);
}

if (resolvedExport.exact) {
isFile(resolvedExport.resolved, function (err, ex) {
if (ex) {
cb(null, resolvedExport.resolved, pkg);
} else {
cb(null, undefined);
}
});
} else {
dir = resolvedExport.resolved;
onmanifest(true);
}
}

function ondir(err, n, pkg) {
if (err) return cb(err);
if (n) return cb(null, n, pkg);
processDirs(cb, dirs.slice(1));
function onmanifest(stop) {
isDirectory(path.dirname(dir), isdir);

function isdir(err, isdir) {
if (err) return cb(err);
if (!isdir) return next();
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);
next();
}

function next() {
if (stop) {
cb(null, undefined);
} else {
processDirs(cb, dirs.slice(1), subpath);
}
}
}
}

function loadNodeModules(x, start, cb) {
var subpathIndex = x.charAt(0) === '@' ? x.indexOf('/', x.indexOf('/') + 1) : x.indexOf('/');
var subpath = subpathIndex === -1 ? '' : x.slice(subpathIndex);

var thunk = function () { return getPackageCandidates(x, start, opts); };

processDirs(
cb,
packageIterator ? packageIterator(x, start, thunk, opts) : thunk()
packageIterator ? packageIterator(x, start, thunk, opts) : thunk(),
subpath
);
}

function endsWithSubpath(dir, subpath) {
var endOfDir = dir.slice(dir.length - subpath.length);

return endOfDir === subpath || endOfDir.replace(/\\/g, '/') === subpath;
}
};
Loading