resolver_sync.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. var path = require('path');
  2. var fs = require('fs');
  3. var test = require('tape');
  4. var resolve = require('../');
  5. var sync = require('../sync');
  6. var requireResolveSupportsPaths = require.resolve.length > 1
  7. && (/^12\.[012]\./).test(process.version); // broken in v12.0-12.2, see https://github.com/nodejs/node/issues/27794
  8. test('`./sync` entry point', function (t) {
  9. t.equal(resolve.sync, sync, '`./sync` entry point is the same as `.sync` on `main`');
  10. t.end();
  11. });
  12. test('foo', function (t) {
  13. var dir = path.join(__dirname, 'resolver');
  14. t.equal(
  15. resolve.sync('./foo', { basedir: dir }),
  16. path.join(dir, 'foo.js'),
  17. './foo'
  18. );
  19. if (requireResolveSupportsPaths) {
  20. t.equal(
  21. resolve.sync('./foo', { basedir: dir }),
  22. require.resolve('./foo', { paths: [dir] }),
  23. './foo: resolve.sync === require.resolve'
  24. );
  25. }
  26. t.equal(
  27. resolve.sync('./foo.js', { basedir: dir }),
  28. path.join(dir, 'foo.js'),
  29. './foo.js'
  30. );
  31. if (requireResolveSupportsPaths) {
  32. t.equal(
  33. resolve.sync('./foo.js', { basedir: dir }),
  34. require.resolve('./foo.js', { paths: [dir] }),
  35. './foo.js: resolve.sync === require.resolve'
  36. );
  37. }
  38. t.equal(
  39. resolve.sync('./foo.js', { basedir: dir, filename: path.join(dir, 'bar.js') }),
  40. path.join(dir, 'foo.js')
  41. );
  42. t.throws(function () {
  43. resolve.sync('foo', { basedir: dir });
  44. });
  45. // Test that filename is reported as the "from" value when passed.
  46. t.throws(
  47. function () {
  48. resolve.sync('foo', { basedir: dir, filename: path.join(dir, 'bar.js') });
  49. },
  50. {
  51. name: 'Error',
  52. message: "Cannot find module 'foo' from '" + path.join(dir, 'bar.js') + "'"
  53. }
  54. );
  55. t.end();
  56. });
  57. test('bar', function (t) {
  58. var dir = path.join(__dirname, 'resolver');
  59. var basedir = path.join(dir, 'bar');
  60. t.equal(
  61. resolve.sync('foo', { basedir: basedir }),
  62. path.join(dir, 'bar/node_modules/foo/index.js'),
  63. 'foo in bar'
  64. );
  65. if (requireResolveSupportsPaths) {
  66. t.equal(
  67. resolve.sync('foo', { basedir: basedir }),
  68. require.resolve('foo', { paths: [basedir] }),
  69. 'foo in bar: resolve.sync === require.resolve'
  70. );
  71. }
  72. t.end();
  73. });
  74. test('baz', function (t) {
  75. var dir = path.join(__dirname, 'resolver');
  76. t.equal(
  77. resolve.sync('./baz', { basedir: dir }),
  78. path.join(dir, 'baz/quux.js'),
  79. './baz'
  80. );
  81. if (requireResolveSupportsPaths) {
  82. t.equal(
  83. resolve.sync('./baz', { basedir: dir }),
  84. require.resolve('./baz', { paths: [dir] }),
  85. './baz: resolve.sync === require.resolve'
  86. );
  87. }
  88. t.end();
  89. });
  90. test('biz', function (t) {
  91. var dir = path.join(__dirname, 'resolver/biz/node_modules');
  92. t.equal(
  93. resolve.sync('./grux', { basedir: dir }),
  94. path.join(dir, 'grux/index.js')
  95. );
  96. if (requireResolveSupportsPaths) {
  97. t.equal(
  98. resolve.sync('./grux', { basedir: dir }),
  99. require.resolve('./grux', { paths: [dir] }),
  100. './grux: resolve.sync === require.resolve'
  101. );
  102. }
  103. var tivDir = path.join(dir, 'grux');
  104. t.equal(
  105. resolve.sync('tiv', { basedir: tivDir }),
  106. path.join(dir, 'tiv/index.js')
  107. );
  108. if (requireResolveSupportsPaths) {
  109. t.equal(
  110. resolve.sync('tiv', { basedir: tivDir }),
  111. require.resolve('tiv', { paths: [tivDir] }),
  112. 'tiv: resolve.sync === require.resolve'
  113. );
  114. }
  115. var gruxDir = path.join(dir, 'tiv');
  116. t.equal(
  117. resolve.sync('grux', { basedir: gruxDir }),
  118. path.join(dir, 'grux/index.js')
  119. );
  120. if (requireResolveSupportsPaths) {
  121. t.equal(
  122. resolve.sync('grux', { basedir: gruxDir }),
  123. require.resolve('grux', { paths: [gruxDir] }),
  124. 'grux: resolve.sync === require.resolve'
  125. );
  126. }
  127. t.end();
  128. });
  129. test('normalize', function (t) {
  130. var dir = path.join(__dirname, 'resolver/biz/node_modules/grux');
  131. t.equal(
  132. resolve.sync('../grux', { basedir: dir }),
  133. path.join(dir, 'index.js')
  134. );
  135. if (requireResolveSupportsPaths) {
  136. t.equal(
  137. resolve.sync('../grux', { basedir: dir }),
  138. require.resolve('../grux', { paths: [dir] }),
  139. '../grux: resolve.sync === require.resolve'
  140. );
  141. }
  142. t.end();
  143. });
  144. test('cup', function (t) {
  145. var dir = path.join(__dirname, 'resolver');
  146. t.equal(
  147. resolve.sync('./cup', {
  148. basedir: dir,
  149. extensions: ['.js', '.coffee']
  150. }),
  151. path.join(dir, 'cup.coffee'),
  152. './cup -> ./cup.coffee'
  153. );
  154. t.equal(
  155. resolve.sync('./cup.coffee', { basedir: dir }),
  156. path.join(dir, 'cup.coffee'),
  157. './cup.coffee'
  158. );
  159. t.throws(function () {
  160. resolve.sync('./cup', {
  161. basedir: dir,
  162. extensions: ['.js']
  163. });
  164. });
  165. if (requireResolveSupportsPaths) {
  166. t.equal(
  167. resolve.sync('./cup.coffee', { basedir: dir, extensions: ['.js', '.coffee'] }),
  168. require.resolve('./cup.coffee', { paths: [dir] }),
  169. './cup.coffee: resolve.sync === require.resolve'
  170. );
  171. }
  172. t.end();
  173. });
  174. test('mug', function (t) {
  175. var dir = path.join(__dirname, 'resolver');
  176. t.equal(
  177. resolve.sync('./mug', { basedir: dir }),
  178. path.join(dir, 'mug.js'),
  179. './mug -> ./mug.js'
  180. );
  181. if (requireResolveSupportsPaths) {
  182. t.equal(
  183. resolve.sync('./mug', { basedir: dir }),
  184. require.resolve('./mug', { paths: [dir] }),
  185. './mug: resolve.sync === require.resolve'
  186. );
  187. }
  188. t.equal(
  189. resolve.sync('./mug', {
  190. basedir: dir,
  191. extensions: ['.coffee', '.js']
  192. }),
  193. path.join(dir, 'mug.coffee'),
  194. './mug -> ./mug.coffee'
  195. );
  196. t.equal(
  197. resolve.sync('./mug', {
  198. basedir: dir,
  199. extensions: ['.js', '.coffee']
  200. }),
  201. path.join(dir, 'mug.js'),
  202. './mug -> ./mug.js'
  203. );
  204. t.end();
  205. });
  206. test('other path', function (t) {
  207. var resolverDir = path.join(__dirname, 'resolver');
  208. var dir = path.join(resolverDir, 'bar');
  209. var otherDir = path.join(resolverDir, 'other_path');
  210. t.equal(
  211. resolve.sync('root', {
  212. basedir: dir,
  213. paths: [otherDir]
  214. }),
  215. path.join(resolverDir, 'other_path/root.js')
  216. );
  217. t.equal(
  218. resolve.sync('lib/other-lib', {
  219. basedir: dir,
  220. paths: [otherDir]
  221. }),
  222. path.join(resolverDir, 'other_path/lib/other-lib.js')
  223. );
  224. t.throws(function () {
  225. resolve.sync('root', { basedir: dir });
  226. });
  227. t.throws(function () {
  228. resolve.sync('zzz', {
  229. basedir: dir,
  230. paths: [otherDir]
  231. });
  232. });
  233. t.end();
  234. });
  235. test('path iterator', function (t) {
  236. var resolverDir = path.join(__dirname, 'resolver');
  237. var exactIterator = function (x, start, getPackageCandidates, opts) {
  238. return [path.join(resolverDir, x)];
  239. };
  240. t.equal(
  241. resolve.sync('baz', { packageIterator: exactIterator }),
  242. path.join(resolverDir, 'baz/quux.js')
  243. );
  244. t.end();
  245. });
  246. test('incorrect main', function (t) {
  247. var resolverDir = path.join(__dirname, 'resolver');
  248. var dir = path.join(resolverDir, 'incorrect_main');
  249. t.equal(
  250. resolve.sync('./incorrect_main', { basedir: resolverDir }),
  251. path.join(dir, 'index.js')
  252. );
  253. if (requireResolveSupportsPaths) {
  254. t.equal(
  255. resolve.sync('./incorrect_main', { basedir: resolverDir }),
  256. require.resolve('./incorrect_main', { paths: [resolverDir] }),
  257. './incorrect_main: resolve.sync === require.resolve'
  258. );
  259. }
  260. t.end();
  261. });
  262. var stubStatSync = function stubStatSync(fn) {
  263. var statSync = fs.statSync;
  264. try {
  265. fs.statSync = function () {
  266. throw new EvalError('Unknown Error');
  267. };
  268. return fn();
  269. } finally {
  270. fs.statSync = statSync;
  271. }
  272. };
  273. test('#79 - re-throw non ENOENT errors from stat', function (t) {
  274. var dir = path.join(__dirname, 'resolver');
  275. stubStatSync(function () {
  276. t.throws(function () {
  277. resolve.sync('foo', { basedir: dir });
  278. }, /Unknown Error/);
  279. });
  280. t.end();
  281. });
  282. test('#52 - incorrectly resolves module-paths like "./someFolder/" when there is a file of the same name', function (t) {
  283. var dir = path.join(__dirname, 'resolver');
  284. var basedir = path.join(dir, 'same_names');
  285. t.equal(
  286. resolve.sync('./foo', { basedir: basedir }),
  287. path.join(dir, 'same_names/foo.js')
  288. );
  289. if (requireResolveSupportsPaths) {
  290. t.equal(
  291. resolve.sync('./foo', { basedir: basedir }),
  292. require.resolve('./foo', { paths: [basedir] }),
  293. './foo: resolve.sync === require.resolve'
  294. );
  295. }
  296. t.equal(
  297. resolve.sync('./foo/', { basedir: basedir }),
  298. path.join(dir, 'same_names/foo/index.js')
  299. );
  300. if (requireResolveSupportsPaths) {
  301. t.equal(
  302. resolve.sync('./foo/', { basedir: basedir }),
  303. require.resolve('./foo/', { paths: [basedir] }),
  304. './foo/: resolve.sync === require.resolve'
  305. );
  306. }
  307. t.end();
  308. });
  309. test('#211 - incorrectly resolves module-paths like "." when from inside a folder with a sibling file of the same name', function (t) {
  310. var dir = path.join(__dirname, 'resolver');
  311. var basedir = path.join(dir, 'same_names/foo');
  312. t.equal(
  313. resolve.sync('./', { basedir: basedir }),
  314. path.join(dir, 'same_names/foo/index.js'),
  315. './'
  316. );
  317. if (requireResolveSupportsPaths) {
  318. t.equal(
  319. resolve.sync('./', { basedir: basedir }),
  320. require.resolve('./', { paths: [basedir] }),
  321. './: resolve.sync === require.resolve'
  322. );
  323. }
  324. t.equal(
  325. resolve.sync('.', { basedir: basedir }),
  326. path.join(dir, 'same_names/foo/index.js'),
  327. '.'
  328. );
  329. if (requireResolveSupportsPaths) {
  330. t.equal(
  331. resolve.sync('.', { basedir: basedir }),
  332. require.resolve('.', { paths: [basedir] }),
  333. '.: resolve.sync === require.resolve',
  334. { todo: true }
  335. );
  336. }
  337. t.end();
  338. });
  339. test('sync: #121 - treating an existing file as a dir when no basedir', function (t) {
  340. var testFile = path.basename(__filename);
  341. t.test('sanity check', function (st) {
  342. st.equal(
  343. resolve.sync('./' + testFile),
  344. __filename,
  345. 'sanity check'
  346. );
  347. st.equal(
  348. resolve.sync('./' + testFile),
  349. require.resolve('./' + testFile),
  350. 'sanity check: resolve.sync === require.resolve'
  351. );
  352. st.end();
  353. });
  354. t.test('with a fake directory', function (st) {
  355. function run() { return resolve.sync('./' + testFile + '/blah'); }
  356. st.throws(run, 'throws an error');
  357. try {
  358. run();
  359. } catch (e) {
  360. st.equal(e.code, 'MODULE_NOT_FOUND', 'error code matches require.resolve');
  361. st.equal(
  362. e.message,
  363. 'Cannot find module \'./' + testFile + '/blah\' from \'' + __dirname + '\'',
  364. 'can not find nonexistent module'
  365. );
  366. }
  367. st.end();
  368. });
  369. t.end();
  370. });
  371. test('sync dot main', function (t) {
  372. var start = new Date();
  373. t.equal(
  374. resolve.sync('./resolver/dot_main'),
  375. path.join(__dirname, 'resolver/dot_main/index.js'),
  376. './resolver/dot_main'
  377. );
  378. t.equal(
  379. resolve.sync('./resolver/dot_main'),
  380. require.resolve('./resolver/dot_main'),
  381. './resolver/dot_main: resolve.sync === require.resolve'
  382. );
  383. t.ok(new Date() - start < 50, 'resolve.sync timedout');
  384. t.end();
  385. });
  386. test('sync dot slash main', function (t) {
  387. var start = new Date();
  388. t.equal(
  389. resolve.sync('./resolver/dot_slash_main'),
  390. path.join(__dirname, 'resolver/dot_slash_main/index.js')
  391. );
  392. t.equal(
  393. resolve.sync('./resolver/dot_slash_main'),
  394. require.resolve('./resolver/dot_slash_main'),
  395. './resolver/dot_slash_main: resolve.sync === require.resolve'
  396. );
  397. t.ok(new Date() - start < 50, 'resolve.sync timedout');
  398. t.end();
  399. });
  400. test('not a directory', function (t) {
  401. var path = './foo';
  402. try {
  403. resolve.sync(path, { basedir: __filename });
  404. t.fail();
  405. } catch (err) {
  406. t.ok(err, 'a non-directory errors');
  407. t.equal(err && err.message, 'Cannot find module \'' + path + "' from '" + __filename + "'");
  408. t.equal(err && err.code, 'MODULE_NOT_FOUND');
  409. }
  410. t.end();
  411. });
  412. test('non-string "main" field in package.json', function (t) {
  413. var dir = path.join(__dirname, 'resolver');
  414. try {
  415. var result = resolve.sync('./invalid_main', { basedir: dir });
  416. t.equal(result, undefined, 'result should not exist');
  417. t.fail('should not get here');
  418. } catch (err) {
  419. t.ok(err, 'errors on non-string main');
  420. t.equal(err.message, 'package “invalid_main” `main` must be a string');
  421. t.equal(err.code, 'INVALID_PACKAGE_MAIN');
  422. }
  423. t.end();
  424. });
  425. test('non-string "main" field in package.json', function (t) {
  426. var dir = path.join(__dirname, 'resolver');
  427. try {
  428. var result = resolve.sync('./invalid_main', { basedir: dir });
  429. t.equal(result, undefined, 'result should not exist');
  430. t.fail('should not get here');
  431. } catch (err) {
  432. t.ok(err, 'errors on non-string main');
  433. t.equal(err.message, 'package “invalid_main” `main` must be a string');
  434. t.equal(err.code, 'INVALID_PACKAGE_MAIN');
  435. }
  436. t.end();
  437. });
  438. test('browser field in package.json', function (t) {
  439. var dir = path.join(__dirname, 'resolver');
  440. var res = resolve.sync('./browser_field', {
  441. basedir: dir,
  442. packageFilter: function packageFilter(pkg) {
  443. if (pkg.browser) {
  444. pkg.main = pkg.browser; // eslint-disable-line no-param-reassign
  445. delete pkg.browser; // eslint-disable-line no-param-reassign
  446. }
  447. return pkg;
  448. }
  449. });
  450. t.equal(res, path.join(dir, 'browser_field', 'b.js'));
  451. t.end();
  452. });
  453. test('absolute paths', function (t) {
  454. var extensionless = __filename.slice(0, -path.extname(__filename).length);
  455. t.equal(
  456. resolve.sync(__filename),
  457. __filename,
  458. 'absolute path to this file resolves'
  459. );
  460. t.equal(
  461. resolve.sync(__filename),
  462. require.resolve(__filename),
  463. 'absolute path to this file: resolve.sync === require.resolve'
  464. );
  465. t.equal(
  466. resolve.sync(extensionless),
  467. __filename,
  468. 'extensionless absolute path to this file resolves'
  469. );
  470. t.equal(
  471. resolve.sync(__filename),
  472. require.resolve(__filename),
  473. 'absolute path to this file: resolve.sync === require.resolve'
  474. );
  475. t.equal(
  476. resolve.sync(__filename, { basedir: process.cwd() }),
  477. __filename,
  478. 'absolute path to this file with a basedir resolves'
  479. );
  480. if (requireResolveSupportsPaths) {
  481. t.equal(
  482. resolve.sync(__filename, { basedir: process.cwd() }),
  483. require.resolve(__filename, { paths: [process.cwd()] }),
  484. 'absolute path to this file + basedir: resolve.sync === require.resolve'
  485. );
  486. }
  487. t.equal(
  488. resolve.sync(extensionless, { basedir: process.cwd() }),
  489. __filename,
  490. 'extensionless absolute path to this file with a basedir resolves'
  491. );
  492. if (requireResolveSupportsPaths) {
  493. t.equal(
  494. resolve.sync(extensionless, { basedir: process.cwd() }),
  495. require.resolve(extensionless, { paths: [process.cwd()] }),
  496. 'extensionless absolute path to this file + basedir: resolve.sync === require.resolve'
  497. );
  498. }
  499. t.end();
  500. });
  501. test('malformed package.json', function (t) {
  502. t.plan(5 + (requireResolveSupportsPaths ? 1 : 0));
  503. var basedir = path.join(__dirname, 'resolver/malformed_package_json');
  504. var expected = path.join(basedir, 'index.js');
  505. t.equal(
  506. resolve.sync('./index.js', { basedir: basedir }),
  507. expected,
  508. 'malformed package.json is silently ignored'
  509. );
  510. if (requireResolveSupportsPaths) {
  511. t.equal(
  512. resolve.sync('./index.js', { basedir: basedir }),
  513. require.resolve('./index.js', { paths: [basedir] }),
  514. 'malformed package.json: resolve.sync === require.resolve'
  515. );
  516. }
  517. var res1 = resolve.sync(
  518. './index.js',
  519. {
  520. basedir: basedir,
  521. packageFilter: function (pkg, pkgfile, dir) {
  522. t.fail('should not reach here');
  523. }
  524. }
  525. );
  526. t.equal(
  527. res1,
  528. expected,
  529. 'with packageFilter: malformed package.json is silently ignored'
  530. );
  531. var res2 = resolve.sync(
  532. './index.js',
  533. {
  534. basedir: basedir,
  535. readPackageSync: function (readFileSync, pkgfile) {
  536. t.equal(pkgfile, path.join(basedir, 'package.json'), 'readPackageSync: `pkgfile` is package.json path');
  537. var result = String(readFileSync(pkgfile));
  538. try {
  539. return JSON.parse(result);
  540. } catch (e) {
  541. t.ok(e instanceof SyntaxError, 'readPackageSync: malformed package.json parses as a syntax error');
  542. }
  543. }
  544. }
  545. );
  546. t.equal(
  547. res2,
  548. expected,
  549. 'with readPackageSync: malformed package.json is silently ignored'
  550. );
  551. });