injectRefreshLoader.js 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. const path = require('path');
  2. /**
  3. * @callback MatchObject
  4. * @param {string} [str]
  5. * @returns {boolean}
  6. */
  7. /**
  8. * @typedef {Object} InjectLoaderOptions
  9. * @property {MatchObject} match A function to include/exclude files to be processed.
  10. * @property {import('../../loader/types').ReactRefreshLoaderOptions} [options] Options passed to the loader.
  11. */
  12. const resolvedLoader = require.resolve('../../loader');
  13. const reactRefreshPath = path.dirname(require.resolve('react-refresh'));
  14. const refreshUtilsPath = path.join(__dirname, '../runtime/RefreshUtils');
  15. /**
  16. * Injects refresh loader to all JavaScript-like and user-specified files.
  17. * @param {*} moduleData Module factory creation data.
  18. * @param {InjectLoaderOptions} injectOptions Options to alter how the loader is injected.
  19. * @returns {*} The injected module factory creation data.
  20. */
  21. function injectRefreshLoader(moduleData, injectOptions) {
  22. const { match, options } = injectOptions;
  23. // Include and exclude user-specified files
  24. if (!match(moduleData.matchResource || moduleData.resource)) return moduleData;
  25. // Include and exclude dynamically generated modules from other loaders
  26. if (moduleData.matchResource && !match(moduleData.request)) return moduleData;
  27. // Exclude files referenced as assets
  28. if (moduleData.type.includes('asset')) return moduleData;
  29. // Check to prevent double injection
  30. if (moduleData.loaders.find(({ loader }) => loader === resolvedLoader)) return moduleData;
  31. // Skip react-refresh and the plugin's runtime utils to prevent self-referencing -
  32. // this is useful when using the plugin as a direct dependency,
  33. // or when node_modules are specified to be processed.
  34. if (
  35. moduleData.resource.includes(reactRefreshPath) ||
  36. moduleData.resource.includes(refreshUtilsPath)
  37. ) {
  38. return moduleData;
  39. }
  40. // As we inject runtime code for each module,
  41. // it is important to run the injected loader after everything.
  42. // This way we can ensure that all code-processing have been done,
  43. // and we won't risk breaking tools like Flow or ESLint.
  44. moduleData.loaders.unshift({
  45. loader: resolvedLoader,
  46. options,
  47. });
  48. return moduleData;
  49. }
  50. module.exports = injectRefreshLoader;