loadLoader.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { pathToFileURL } = require("url");
  7. const memoize = require("../util/memoize");
  8. const getLoaderLoadingError = memoize(() =>
  9. require("../errors/LoaderLoadingError")
  10. );
  11. /** @import { LoaderObject } from "./LoaderRunner" */
  12. /**
  13. * Lazily-compiled dynamic `import()`. Built on first use so this file still
  14. * parses on Node versions without native `import()` (<13.2), then reused.
  15. * @type {((url: URL) => Promise<EXPECTED_ANY>) | undefined}
  16. */
  17. let importModule;
  18. /**
  19. * @param {LoaderObject} loader the loader to load into
  20. * @param {EXPECTED_ANY} module the loaded module
  21. * @param {(err?: Error) => void} callback callback
  22. * @returns {void}
  23. */
  24. function handleResult(loader, module, callback) {
  25. if (typeof module !== "function" && typeof module !== "object") {
  26. return callback(
  27. new (getLoaderLoadingError())(
  28. `Module '${loader.path}' is not a loader (export function or es6 module)`
  29. )
  30. );
  31. }
  32. loader.normal = typeof module === "function" ? module : module.default;
  33. loader.pitch = module.pitch;
  34. loader.raw = module.raw;
  35. if (
  36. typeof loader.normal !== "function" &&
  37. typeof loader.pitch !== "function"
  38. ) {
  39. return callback(
  40. new (getLoaderLoadingError())(
  41. `Module '${loader.path}' is not a loader (must have normal or pitch function)`
  42. )
  43. );
  44. }
  45. callback();
  46. }
  47. /**
  48. * @param {LoaderObject} loader the loader to load
  49. * @param {(err?: Error) => void} callback callback
  50. * @returns {void}
  51. */
  52. function loadLoader(loader, callback) {
  53. if (loader.type === "module") {
  54. try {
  55. if (importModule === undefined) {
  56. // `eval` defers parsing `import()` to runtime, so this file stays
  57. // loadable on Node versions that can't parse it (<13.2).
  58. // eslint-disable-next-line no-eval
  59. const dynamicImport = eval("(url) => import(url)");
  60. importModule = /** @type {(url: URL) => Promise<EXPECTED_ANY>} */ (
  61. dynamicImport
  62. );
  63. }
  64. importModule(pathToFileURL(loader.path)).then(
  65. (/** @type {EXPECTED_ANY} */ module) => {
  66. handleResult(loader, module, callback);
  67. },
  68. callback
  69. );
  70. } catch (err) {
  71. callback(/** @type {Error} */ (err));
  72. }
  73. return;
  74. }
  75. let loadedModule;
  76. try {
  77. loadedModule = require(loader.path);
  78. } catch (err) {
  79. // node can choke on require when the FD limit is hit; defer to recover.
  80. if (
  81. err instanceof Error &&
  82. /** @type {NodeJS.ErrnoException} */ (err).code === "EMFILE"
  83. ) {
  84. setImmediate(loadLoader, loader, callback);
  85. return;
  86. }
  87. return callback(/** @type {Error} */ (err));
  88. }
  89. return handleResult(loader, loadedModule, callback);
  90. }
  91. module.exports = loadLoader;