You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
11 KiB

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