Skip to content

Commit 0d5a7bb

Browse files
committed
[New] add default support for paths to include $HOME/.node_{modules,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
1 parent 89377ad commit 0d5a7bb

7 files changed

+292
-3
lines changed

.eslintrc

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"func-style": 0,
1515
"global-require": 1,
1616
"id-length": [2, { "min": 1, "max": 30 }],
17+
"max-lines": [2, 350],
1718
"max-lines-per-function": 1,
1819
"max-nested-callbacks": 0,
1920
"max-params": 0,

lib/async.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var fs = require('fs');
2+
var getHomedir = require('./homedir');
23
var path = require('path');
34
var caller = require('./caller');
45
var nodeModulesPaths = require('./node-modules-paths');
@@ -7,6 +8,14 @@ var isCore = require('is-core-module');
78

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

11+
var homedir = getHomedir();
12+
var defaultPaths = function () {
13+
return [
14+
path.join(homedir, '.node_modules'),
15+
path.join(homedir, '.node_libraries')
16+
];
17+
};
18+
1019
var defaultIsFile = function isFile(file, cb) {
1120
fs.stat(file, function (err, stat) {
1221
if (!err) {
@@ -98,7 +107,7 @@ module.exports = function resolve(x, options, callback) {
98107
var basedir = opts.basedir || path.dirname(caller());
99108
var parent = opts.filename || basedir;
100109

101-
opts.paths = opts.paths || [];
110+
opts.paths = opts.paths || defaultPaths();
102111

103112
// ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory
104113
var absoluteStart = path.resolve(basedir);

lib/homedir.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
3+
var os = require('os');
4+
5+
// adapted from https://github.com/sindresorhus/os-homedir/blob/11e089f4754db38bb535e5a8416320c4446e8cfd/index.js
6+
7+
module.exports = os.homedir || function homedir() {
8+
var home = process.env.HOME;
9+
var user = process.env.LOGNAME || process.env.USER || process.env.LNAME || process.env.USERNAME;
10+
11+
if (process.platform === 'win32') {
12+
return process.env.USERPROFILE || process.env.HOMEDRIVE + process.env.HOMEPATH || home || null;
13+
}
14+
15+
if (process.platform === 'darwin') {
16+
return home || (user ? '/Users/' + user : null);
17+
}
18+
19+
if (process.platform === 'linux') {
20+
return home || (process.getuid() === 0 ? '/root' : (user ? '/home/' + user : null));
21+
}
22+
23+
return home || null;
24+
};

lib/sync.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
var isCore = require('is-core-module');
22
var fs = require('fs');
33
var path = require('path');
4+
var getHomedir = require('./homedir');
45
var caller = require('./caller');
56
var nodeModulesPaths = require('./node-modules-paths');
67
var normalizeOptions = require('./normalize-options');
78

89
var realpathFS = fs.realpathSync && typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync;
910

11+
var homedir = getHomedir();
12+
var defaultPaths = function () {
13+
return [
14+
path.join(homedir, '.node_modules'),
15+
path.join(homedir, '.node_libraries')
16+
];
17+
};
18+
1019
var defaultIsFile = function isFile(file) {
1120
try {
1221
var stat = fs.statSync(file, { throwIfNoEntry: false });
@@ -78,7 +87,7 @@ module.exports = function resolveSync(x, options) {
7887
var basedir = opts.basedir || path.dirname(caller());
7988
var parent = opts.filename || basedir;
8089

81-
opts.paths = opts.paths || [];
90+
opts.paths = opts.paths || defaultPaths();
8291

8392
// ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory
8493
var absoluteStart = maybeRealpathSync(realpathSync, path.resolve(basedir), opts);

package.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,18 @@
4444
"@ljharb/eslint-config": "^20.2.0",
4545
"array.prototype.map": "^1.0.4",
4646
"aud": "^2.0.0",
47+
"copy-dir": "^1.3.0",
4748
"eclint": "^2.8.1",
4849
"eslint": "^8.7.0",
4950
"in-publish": "^2.0.1",
51+
"mkdirp": "^0.5.5",
52+
"mv": "^2.1.1",
5053
"object-keys": "^1.1.1",
54+
"rimraf": "^2.7.1",
5155
"safe-publish-latest": "^2.0.0",
5256
"tap": "0.4.13",
53-
"tape": "^5.4.1"
57+
"tape": "^5.4.1",
58+
"tmp": "^0.0.31"
5459
},
5560
"license": "MIT",
5661
"author": {

test/home_paths.js

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
'use strict';
2+
3+
var fs = require('fs');
4+
var homedir = require('../lib/homedir');
5+
var path = require('path');
6+
7+
var test = require('tape');
8+
var mkdirp = require('mkdirp');
9+
var rimraf = require('rimraf');
10+
var mv = require('mv');
11+
var copyDir = require('copy-dir');
12+
var tmp = require('tmp');
13+
14+
var HOME = homedir();
15+
16+
var hnm = path.join(HOME, '.node_modules');
17+
var hnl = path.join(HOME, '.node_libraries');
18+
19+
var resolve = require('../async');
20+
21+
function makeDir(t, dir, cb) {
22+
mkdirp(dir, function (err) {
23+
if (err) {
24+
cb(err);
25+
} else {
26+
t.teardown(function cleanup() {
27+
rimraf.sync(dir);
28+
});
29+
cb();
30+
}
31+
});
32+
}
33+
34+
function makeTempDir(t, dir, cb) {
35+
if (fs.existsSync(dir)) {
36+
var tmpResult = tmp.dirSync();
37+
t.teardown(tmpResult.removeCallback);
38+
var backup = path.join(tmpResult.name, path.basename(dir));
39+
mv(dir, backup, function (err) {
40+
if (err) {
41+
cb(err);
42+
} else {
43+
t.teardown(function () {
44+
mv(backup, dir, cb);
45+
});
46+
makeDir(t, dir, cb);
47+
}
48+
});
49+
} else {
50+
makeDir(t, dir, cb);
51+
}
52+
}
53+
54+
test('homedir module paths', function (t) {
55+
t.plan(7);
56+
57+
makeTempDir(t, hnm, function (err) {
58+
t.error(err, 'no error with HNM temp dir');
59+
if (err) {
60+
return t.end();
61+
}
62+
63+
var bazHNMDir = path.join(hnm, 'baz');
64+
var dotMainDir = path.join(hnm, 'dot_main');
65+
copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNMDir);
66+
copyDir.sync(path.join(__dirname, 'resolver/dot_main'), dotMainDir);
67+
68+
var bazPkg = { name: 'baz', main: 'quux.js' };
69+
var dotMainPkg = { main: 'index' };
70+
71+
var bazHNMmain = path.join(bazHNMDir, 'quux.js');
72+
t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
73+
var dotMainMain = path.join(dotMainDir, 'index.js');
74+
t.equal(require.resolve('dot_main'), dotMainMain, 'sanity check: require.resolve finds `dot_main`');
75+
76+
makeTempDir(t, hnl, function (err) {
77+
t.error(err, 'no error with HNL temp dir');
78+
if (err) {
79+
return t.end();
80+
}
81+
var bazHNLDir = path.join(hnl, 'baz');
82+
copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNLDir);
83+
84+
var dotSlashMainDir = path.join(hnl, 'dot_slash_main');
85+
var dotSlashMainMain = path.join(dotSlashMainDir, 'index.js');
86+
var dotSlashMainPkg = { main: 'index' };
87+
copyDir.sync(path.join(__dirname, 'resolver/dot_slash_main'), dotSlashMainDir);
88+
89+
t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
90+
t.equal(require.resolve('dot_slash_main'), dotSlashMainMain, 'sanity check: require.resolve finds HNL `dot_slash_main`');
91+
92+
t.test('with temp dirs', function (st) {
93+
st.plan(3);
94+
95+
st.test('just in `$HOME/.node_modules`', function (s2t) {
96+
s2t.plan(3);
97+
98+
resolve('dot_main', function (err, res, pkg) {
99+
s2t.error(err, 'no error resolving `dot_main`');
100+
s2t.equal(res, dotMainMain, '`dot_main` resolves in `$HOME/.node_modules`');
101+
s2t.deepEqual(pkg, dotMainPkg);
102+
});
103+
});
104+
105+
st.test('just in `$HOME/.node_libraries`', function (s2t) {
106+
s2t.plan(3);
107+
108+
resolve('dot_slash_main', function (err, res, pkg) {
109+
s2t.error(err, 'no error resolving `dot_slash_main`');
110+
s2t.equal(res, dotSlashMainMain, '`dot_slash_main` resolves in `$HOME/.node_libraries`');
111+
s2t.deepEqual(pkg, dotSlashMainPkg);
112+
});
113+
});
114+
115+
st.test('in `$HOME/.node_libraries` and `$HOME/.node_modules`', function (s2t) {
116+
s2t.plan(3);
117+
118+
resolve('baz', function (err, res, pkg) {
119+
s2t.error(err, 'no error resolving `baz`');
120+
s2t.equal(res, bazHNMmain, '`baz` resolves in `$HOME/.node_modules` when in both');
121+
s2t.deepEqual(pkg, bazPkg);
122+
});
123+
});
124+
});
125+
});
126+
});
127+
});

test/home_paths_sync.js

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
'use strict';
2+
3+
var fs = require('fs');
4+
var homedir = require('../lib/homedir');
5+
var path = require('path');
6+
7+
var test = require('tape');
8+
var mkdirp = require('mkdirp');
9+
var rimraf = require('rimraf');
10+
var mv = require('mv');
11+
var copyDir = require('copy-dir');
12+
var tmp = require('tmp');
13+
14+
var HOME = homedir();
15+
16+
var hnm = path.join(HOME, '.node_modules');
17+
var hnl = path.join(HOME, '.node_libraries');
18+
19+
var resolve = require('../sync');
20+
21+
function makeDir(t, dir, cb) {
22+
mkdirp(dir, function (err) {
23+
if (err) {
24+
cb(err);
25+
} else {
26+
t.teardown(function cleanup() {
27+
rimraf.sync(dir);
28+
});
29+
cb();
30+
}
31+
});
32+
}
33+
34+
function makeTempDir(t, dir, cb) {
35+
if (fs.existsSync(dir)) {
36+
var tmpResult = tmp.dirSync();
37+
t.teardown(tmpResult.removeCallback);
38+
var backup = path.join(tmpResult.name, path.basename(dir));
39+
mv(dir, backup, function (err) {
40+
if (err) {
41+
cb(err);
42+
} else {
43+
t.teardown(function () {
44+
mv(backup, dir, cb);
45+
});
46+
makeDir(t, dir, cb);
47+
}
48+
});
49+
} else {
50+
makeDir(t, dir, cb);
51+
}
52+
}
53+
54+
test('homedir module paths', function (t) {
55+
t.plan(7);
56+
57+
makeTempDir(t, hnm, function (err) {
58+
t.error(err, 'no error with HNM temp dir');
59+
if (err) {
60+
return t.end();
61+
}
62+
63+
var bazHNMDir = path.join(hnm, 'baz');
64+
var dotMainDir = path.join(hnm, 'dot_main');
65+
copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNMDir);
66+
copyDir.sync(path.join(__dirname, 'resolver/dot_main'), dotMainDir);
67+
68+
var bazHNMmain = path.join(bazHNMDir, 'quux.js');
69+
t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
70+
var dotMainMain = path.join(dotMainDir, 'index.js');
71+
t.equal(require.resolve('dot_main'), dotMainMain, 'sanity check: require.resolve finds `dot_main`');
72+
73+
makeTempDir(t, hnl, function (err) {
74+
t.error(err, 'no error with HNL temp dir');
75+
if (err) {
76+
return t.end();
77+
}
78+
var bazHNLDir = path.join(hnl, 'baz');
79+
copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNLDir);
80+
81+
var dotSlashMainDir = path.join(hnl, 'dot_slash_main');
82+
var dotSlashMainMain = path.join(dotSlashMainDir, 'index.js');
83+
copyDir.sync(path.join(__dirname, 'resolver/dot_slash_main'), dotSlashMainDir);
84+
85+
t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
86+
t.equal(require.resolve('dot_slash_main'), dotSlashMainMain, 'sanity check: require.resolve finds HNL `dot_slash_main`');
87+
88+
t.test('with temp dirs', function (st) {
89+
st.plan(3);
90+
91+
st.test('just in `$HOME/.node_modules`', function (s2t) {
92+
s2t.plan(1);
93+
94+
var res = resolve('dot_main');
95+
s2t.equal(res, dotMainMain, '`dot_main` resolves in `$HOME/.node_modules`');
96+
});
97+
98+
st.test('just in `$HOME/.node_libraries`', function (s2t) {
99+
s2t.plan(1);
100+
101+
var res = resolve('dot_slash_main');
102+
s2t.equal(res, dotSlashMainMain, '`dot_slash_main` resolves in `$HOME/.node_libraries`');
103+
});
104+
105+
st.test('in `$HOME/.node_libraries` and `$HOME/.node_modules`', function (s2t) {
106+
s2t.plan(1);
107+
108+
var res = resolve('baz');
109+
s2t.equal(res, bazHNMmain, '`baz` resolves in `$HOME/.node_modules` when in both');
110+
});
111+
});
112+
});
113+
});
114+
});

0 commit comments

Comments
 (0)