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