Skip to content

Commit

Permalink
[New] add default support for paths to include `$HOME/.node_{module…
Browse files Browse the repository at this point in the history
…s,libraries}`

Note, this is a rarely used feature that should be aggressively avoided, but it‘s important to minimize gaps between node and this package.

Fixes #163
  • Loading branch information
ljharb committed Jan 21, 2022
1 parent 89377ad commit 0d5a7bb
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 3 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"func-style": 0,
"global-require": 1,
"id-length": [2, { "min": 1, "max": 30 }],
"max-lines": [2, 350],
"max-lines-per-function": 1,
"max-nested-callbacks": 0,
"max-params": 0,
Expand Down
11 changes: 10 additions & 1 deletion lib/async.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var fs = require('fs');
var getHomedir = require('./homedir');
var path = require('path');
var caller = require('./caller');
var nodeModulesPaths = require('./node-modules-paths');
Expand All @@ -7,6 +8,14 @@ var isCore = require('is-core-module');

var realpathFS = 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) {
Expand Down Expand Up @@ -98,7 +107,7 @@ module.exports = function resolve(x, options, callback) {
var basedir = opts.basedir || path.dirname(caller());
var parent = opts.filename || basedir;

opts.paths = opts.paths || [];
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);
Expand Down
24 changes: 24 additions & 0 deletions lib/homedir.js
Original file line number Diff line number Diff line change
@@ -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));
}

return home || null;
};
11 changes: 10 additions & 1 deletion lib/sync.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
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 = 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 });
Expand Down Expand Up @@ -78,7 +87,7 @@ module.exports = function resolveSync(x, options) {
var basedir = opts.basedir || path.dirname(caller());
var parent = opts.filename || basedir;

opts.paths = opts.paths || [];
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);
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,18 @@
"@ljharb/eslint-config": "^20.2.0",
"array.prototype.map": "^1.0.4",
"aud": "^2.0.0",
"copy-dir": "^1.3.0",
"eclint": "^2.8.1",
"eslint": "^8.7.0",
"in-publish": "^2.0.1",
"mkdirp": "^0.5.5",
"mv": "^2.1.1",
"object-keys": "^1.1.1",
"rimraf": "^2.7.1",
"safe-publish-latest": "^2.0.0",
"tap": "0.4.13",
"tape": "^5.4.1"
"tape": "^5.4.1",
"tmp": "^0.0.31"
},
"license": "MIT",
"author": {
Expand Down
127 changes: 127 additions & 0 deletions test/home_paths.js
Original file line number Diff line number Diff line change
@@ -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 changes: 114 additions & 0 deletions test/home_paths_sync.js
Original file line number Diff line number Diff line change
@@ -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');
});
});
});
});
});

0 comments on commit 0d5a7bb

Please sign in to comment.