1 | var fs = require('fs');
|
---|
2 | var getHomedir = require('./homedir');
|
---|
3 | var path = require('path');
|
---|
4 | var caller = require('./caller');
|
---|
5 | var nodeModulesPaths = require('./node-modules-paths');
|
---|
6 | var normalizeOptions = require('./normalize-options');
|
---|
7 | var isCore = require('is-core-module');
|
---|
8 |
|
---|
9 | var realpathFS = process.platform !== 'win32' && fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath;
|
---|
10 |
|
---|
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 |
|
---|
19 | var defaultIsFile = function isFile(file, cb) {
|
---|
20 | fs.stat(file, function (err, stat) {
|
---|
21 | if (!err) {
|
---|
22 | return cb(null, stat.isFile() || stat.isFIFO());
|
---|
23 | }
|
---|
24 | if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
|
---|
25 | return cb(err);
|
---|
26 | });
|
---|
27 | };
|
---|
28 |
|
---|
29 | var defaultIsDir = function isDirectory(dir, cb) {
|
---|
30 | fs.stat(dir, function (err, stat) {
|
---|
31 | if (!err) {
|
---|
32 | return cb(null, stat.isDirectory());
|
---|
33 | }
|
---|
34 | if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
|
---|
35 | return cb(err);
|
---|
36 | });
|
---|
37 | };
|
---|
38 |
|
---|
39 | var defaultRealpath = function realpath(x, cb) {
|
---|
40 | realpathFS(x, function (realpathErr, realPath) {
|
---|
41 | if (realpathErr && realpathErr.code !== 'ENOENT') cb(realpathErr);
|
---|
42 | else cb(null, realpathErr ? x : realPath);
|
---|
43 | });
|
---|
44 | };
|
---|
45 |
|
---|
46 | var maybeRealpath = function maybeRealpath(realpath, x, opts, cb) {
|
---|
47 | if (!opts || !opts.preserveSymlinks) {
|
---|
48 | realpath(x, cb);
|
---|
49 | } else {
|
---|
50 | cb(null, x);
|
---|
51 | }
|
---|
52 | };
|
---|
53 |
|
---|
54 | var defaultReadPackage = function defaultReadPackage(readFile, pkgfile, cb) {
|
---|
55 | readFile(pkgfile, function (readFileErr, body) {
|
---|
56 | if (readFileErr) cb(readFileErr);
|
---|
57 | else {
|
---|
58 | try {
|
---|
59 | var pkg = JSON.parse(body);
|
---|
60 | cb(null, pkg);
|
---|
61 | } catch (jsonErr) {
|
---|
62 | cb(jsonErr);
|
---|
63 | }
|
---|
64 | }
|
---|
65 | });
|
---|
66 | };
|
---|
67 |
|
---|
68 | var getPackageCandidates = function getPackageCandidates(x, start, opts) {
|
---|
69 | var dirs = nodeModulesPaths(start, opts, x);
|
---|
70 | for (var i = 0; i < dirs.length; i++) {
|
---|
71 | dirs[i] = path.join(dirs[i], x);
|
---|
72 | }
|
---|
73 | return dirs;
|
---|
74 | };
|
---|
75 |
|
---|
76 | module.exports = function resolve(x, options, callback) {
|
---|
77 | var cb = callback;
|
---|
78 | var opts = options;
|
---|
79 | if (typeof options === 'function') {
|
---|
80 | cb = opts;
|
---|
81 | opts = {};
|
---|
82 | }
|
---|
83 | if (typeof x !== 'string') {
|
---|
84 | var err = new TypeError('Path must be a string.');
|
---|
85 | return process.nextTick(function () {
|
---|
86 | cb(err);
|
---|
87 | });
|
---|
88 | }
|
---|
89 |
|
---|
90 | opts = normalizeOptions(x, opts);
|
---|
91 |
|
---|
92 | var isFile = opts.isFile || defaultIsFile;
|
---|
93 | var isDirectory = opts.isDirectory || defaultIsDir;
|
---|
94 | var readFile = opts.readFile || fs.readFile;
|
---|
95 | var realpath = opts.realpath || defaultRealpath;
|
---|
96 | var readPackage = opts.readPackage || defaultReadPackage;
|
---|
97 | if (opts.readFile && opts.readPackage) {
|
---|
98 | var conflictErr = new TypeError('`readFile` and `readPackage` are mutually exclusive.');
|
---|
99 | return process.nextTick(function () {
|
---|
100 | cb(conflictErr);
|
---|
101 | });
|
---|
102 | }
|
---|
103 | var packageIterator = opts.packageIterator;
|
---|
104 |
|
---|
105 | var extensions = opts.extensions || ['.js'];
|
---|
106 | var includeCoreModules = opts.includeCoreModules !== false;
|
---|
107 | var basedir = opts.basedir || path.dirname(caller());
|
---|
108 | var parent = opts.filename || basedir;
|
---|
109 |
|
---|
110 | opts.paths = opts.paths || defaultPaths();
|
---|
111 |
|
---|
112 | // ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory
|
---|
113 | var absoluteStart = path.resolve(basedir);
|
---|
114 |
|
---|
115 | maybeRealpath(
|
---|
116 | realpath,
|
---|
117 | absoluteStart,
|
---|
118 | opts,
|
---|
119 | function (err, realStart) {
|
---|
120 | if (err) cb(err);
|
---|
121 | else validateBasedir(realStart);
|
---|
122 | }
|
---|
123 | );
|
---|
124 |
|
---|
125 | function validateBasedir(basedir) {
|
---|
126 | if (opts.basedir) {
|
---|
127 | var dirError = new TypeError('Provided basedir "' + basedir + '" is not a directory' + (opts.preserveSymlinks ? '' : ', or a symlink to a directory'));
|
---|
128 | dirError.code = 'INVALID_BASEDIR';
|
---|
129 | isDirectory(basedir, function (err, result) {
|
---|
130 | if (err) return cb(err);
|
---|
131 | if (!result) { return cb(dirError); }
|
---|
132 | validBasedir(basedir);
|
---|
133 | });
|
---|
134 | } else {
|
---|
135 | validBasedir(basedir);
|
---|
136 | }
|
---|
137 | }
|
---|
138 |
|
---|
139 | var res;
|
---|
140 | function validBasedir(basedir) {
|
---|
141 | if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) {
|
---|
142 | res = path.resolve(basedir, x);
|
---|
143 | if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/';
|
---|
144 | if ((/\/$/).test(x) && res === basedir) {
|
---|
145 | loadAsDirectory(res, opts.package, onfile);
|
---|
146 | } else loadAsFile(res, opts.package, onfile);
|
---|
147 | } else if (includeCoreModules && isCore(x)) {
|
---|
148 | return cb(null, x);
|
---|
149 | } else loadNodeModules(x, basedir, function (err, n, pkg) {
|
---|
150 | if (err) cb(err);
|
---|
151 | else if (n) {
|
---|
152 | return maybeRealpath(realpath, n, opts, function (err, realN) {
|
---|
153 | if (err) {
|
---|
154 | cb(err);
|
---|
155 | } else {
|
---|
156 | cb(null, realN, pkg);
|
---|
157 | }
|
---|
158 | });
|
---|
159 | } else {
|
---|
160 | var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'");
|
---|
161 | moduleError.code = 'MODULE_NOT_FOUND';
|
---|
162 | cb(moduleError);
|
---|
163 | }
|
---|
164 | });
|
---|
165 | }
|
---|
166 |
|
---|
167 | function onfile(err, m, pkg) {
|
---|
168 | if (err) cb(err);
|
---|
169 | else if (m) cb(null, m, pkg);
|
---|
170 | else loadAsDirectory(res, function (err, d, pkg) {
|
---|
171 | if (err) cb(err);
|
---|
172 | else if (d) {
|
---|
173 | maybeRealpath(realpath, d, opts, function (err, realD) {
|
---|
174 | if (err) {
|
---|
175 | cb(err);
|
---|
176 | } else {
|
---|
177 | cb(null, realD, pkg);
|
---|
178 | }
|
---|
179 | });
|
---|
180 | } else {
|
---|
181 | var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'");
|
---|
182 | moduleError.code = 'MODULE_NOT_FOUND';
|
---|
183 | cb(moduleError);
|
---|
184 | }
|
---|
185 | });
|
---|
186 | }
|
---|
187 |
|
---|
188 | function loadAsFile(x, thePackage, callback) {
|
---|
189 | var loadAsFilePackage = thePackage;
|
---|
190 | var cb = callback;
|
---|
191 | if (typeof loadAsFilePackage === 'function') {
|
---|
192 | cb = loadAsFilePackage;
|
---|
193 | loadAsFilePackage = undefined;
|
---|
194 | }
|
---|
195 |
|
---|
196 | var exts = [''].concat(extensions);
|
---|
197 | load(exts, x, loadAsFilePackage);
|
---|
198 |
|
---|
199 | function load(exts, x, loadPackage) {
|
---|
200 | if (exts.length === 0) return cb(null, undefined, loadPackage);
|
---|
201 | var file = x + exts[0];
|
---|
202 |
|
---|
203 | var pkg = loadPackage;
|
---|
204 | if (pkg) onpkg(null, pkg);
|
---|
205 | else loadpkg(path.dirname(file), onpkg);
|
---|
206 |
|
---|
207 | function onpkg(err, pkg_, dir) {
|
---|
208 | pkg = pkg_;
|
---|
209 | if (err) return cb(err);
|
---|
210 | if (dir && pkg && opts.pathFilter) {
|
---|
211 | var rfile = path.relative(dir, file);
|
---|
212 | var rel = rfile.slice(0, rfile.length - exts[0].length);
|
---|
213 | var r = opts.pathFilter(pkg, x, rel);
|
---|
214 | if (r) return load(
|
---|
215 | [''].concat(extensions.slice()),
|
---|
216 | path.resolve(dir, r),
|
---|
217 | pkg
|
---|
218 | );
|
---|
219 | }
|
---|
220 | isFile(file, onex);
|
---|
221 | }
|
---|
222 | function onex(err, ex) {
|
---|
223 | if (err) return cb(err);
|
---|
224 | if (ex) return cb(null, file, pkg);
|
---|
225 | load(exts.slice(1), x, pkg);
|
---|
226 | }
|
---|
227 | }
|
---|
228 | }
|
---|
229 |
|
---|
230 | function loadpkg(dir, cb) {
|
---|
231 | if (dir === '' || dir === '/') return cb(null);
|
---|
232 | if (process.platform === 'win32' && (/^\w:[/\\]*$/).test(dir)) {
|
---|
233 | return cb(null);
|
---|
234 | }
|
---|
235 | if ((/[/\\]node_modules[/\\]*$/).test(dir)) return cb(null);
|
---|
236 |
|
---|
237 | maybeRealpath(realpath, dir, opts, function (unwrapErr, pkgdir) {
|
---|
238 | if (unwrapErr) return loadpkg(path.dirname(dir), cb);
|
---|
239 | var pkgfile = path.join(pkgdir, 'package.json');
|
---|
240 | isFile(pkgfile, function (err, ex) {
|
---|
241 | // on err, ex is false
|
---|
242 | if (!ex) return loadpkg(path.dirname(dir), cb);
|
---|
243 |
|
---|
244 | readPackage(readFile, pkgfile, function (err, pkgParam) {
|
---|
245 | if (err && !(err instanceof SyntaxError)) cb(err);
|
---|
246 |
|
---|
247 | var pkg = pkgParam;
|
---|
248 |
|
---|
249 | if (pkg && opts.packageFilter) {
|
---|
250 | pkg = opts.packageFilter(pkg, pkgfile, dir);
|
---|
251 | }
|
---|
252 | cb(null, pkg, dir);
|
---|
253 | });
|
---|
254 | });
|
---|
255 | });
|
---|
256 | }
|
---|
257 |
|
---|
258 | function loadAsDirectory(x, loadAsDirectoryPackage, callback) {
|
---|
259 | var cb = callback;
|
---|
260 | var fpkg = loadAsDirectoryPackage;
|
---|
261 | if (typeof fpkg === 'function') {
|
---|
262 | cb = fpkg;
|
---|
263 | fpkg = opts.package;
|
---|
264 | }
|
---|
265 |
|
---|
266 | maybeRealpath(realpath, x, opts, function (unwrapErr, pkgdir) {
|
---|
267 | if (unwrapErr) return loadAsDirectory(path.dirname(x), fpkg, cb);
|
---|
268 | var pkgfile = path.join(pkgdir, 'package.json');
|
---|
269 | isFile(pkgfile, function (err, ex) {
|
---|
270 | if (err) return cb(err);
|
---|
271 | if (!ex) return loadAsFile(path.join(x, 'index'), fpkg, cb);
|
---|
272 |
|
---|
273 | readPackage(readFile, pkgfile, function (err, pkgParam) {
|
---|
274 | if (err) return cb(err);
|
---|
275 |
|
---|
276 | var pkg = pkgParam;
|
---|
277 |
|
---|
278 | if (pkg && opts.packageFilter) {
|
---|
279 | pkg = opts.packageFilter(pkg, pkgfile, pkgdir);
|
---|
280 | }
|
---|
281 |
|
---|
282 | if (pkg && pkg.main) {
|
---|
283 | if (typeof pkg.main !== 'string') {
|
---|
284 | var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string');
|
---|
285 | mainError.code = 'INVALID_PACKAGE_MAIN';
|
---|
286 | return cb(mainError);
|
---|
287 | }
|
---|
288 | if (pkg.main === '.' || pkg.main === './') {
|
---|
289 | pkg.main = 'index';
|
---|
290 | }
|
---|
291 | loadAsFile(path.resolve(x, pkg.main), pkg, function (err, m, pkg) {
|
---|
292 | if (err) return cb(err);
|
---|
293 | if (m) return cb(null, m, pkg);
|
---|
294 | if (!pkg) return loadAsFile(path.join(x, 'index'), pkg, cb);
|
---|
295 |
|
---|
296 | var dir = path.resolve(x, pkg.main);
|
---|
297 | loadAsDirectory(dir, pkg, function (err, n, pkg) {
|
---|
298 | if (err) return cb(err);
|
---|
299 | if (n) return cb(null, n, pkg);
|
---|
300 | loadAsFile(path.join(x, 'index'), pkg, function (err, m, pkg) {
|
---|
301 | if (err) return cb(err);
|
---|
302 | if (m) return cb(null, m, pkg);
|
---|
303 | var incorrectMainError = new Error("Cannot find module '" + path.resolve(x, pkg.main) + "'. Please verify that the package.json has a valid \"main\" entry");
|
---|
304 | incorrectMainError.code = 'INCORRECT_PACKAGE_MAIN';
|
---|
305 | return cb(incorrectMainError);
|
---|
306 | });
|
---|
307 | });
|
---|
308 | });
|
---|
309 | return;
|
---|
310 | }
|
---|
311 |
|
---|
312 | loadAsFile(path.join(x, '/index'), pkg, cb);
|
---|
313 | });
|
---|
314 | });
|
---|
315 | });
|
---|
316 | }
|
---|
317 |
|
---|
318 | function processDirs(cb, dirs) {
|
---|
319 | if (dirs.length === 0) return cb(null, undefined);
|
---|
320 | var dir = dirs[0];
|
---|
321 |
|
---|
322 | isDirectory(path.dirname(dir), isdir);
|
---|
323 |
|
---|
324 | function isdir(err, isdir) {
|
---|
325 | if (err) return cb(err);
|
---|
326 | if (!isdir) return processDirs(cb, dirs.slice(1));
|
---|
327 | loadAsFile(dir, opts.package, onfile);
|
---|
328 | }
|
---|
329 |
|
---|
330 | function onfile(err, m, pkg) {
|
---|
331 | if (err) return cb(err);
|
---|
332 | if (m) return cb(null, m, pkg);
|
---|
333 | loadAsDirectory(dir, opts.package, ondir);
|
---|
334 | }
|
---|
335 |
|
---|
336 | function ondir(err, n, pkg) {
|
---|
337 | if (err) return cb(err);
|
---|
338 | if (n) return cb(null, n, pkg);
|
---|
339 | processDirs(cb, dirs.slice(1));
|
---|
340 | }
|
---|
341 | }
|
---|
342 | function loadNodeModules(x, start, cb) {
|
---|
343 | var thunk = function () { return getPackageCandidates(x, start, opts); };
|
---|
344 | processDirs(
|
---|
345 | cb,
|
---|
346 | packageIterator ? packageIterator(x, start, thunk, opts) : thunk()
|
---|
347 | );
|
---|
348 | }
|
---|
349 | };
|
---|