index.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. // This is a patch for mozilla/source-map#349 -
  2. // internally, it uses the existence of the `fetch` global to toggle browser behaviours.
  3. // That check, however, will break when `fetch` polyfills are used for SSR setups.
  4. // We "reset" the polyfill here to ensure it won't interfere with source-map generation.
  5. const originalFetch = global.fetch;
  6. delete global.fetch;
  7. const { getOptions } = require('loader-utils');
  8. const { validate: validateOptions } = require('schema-utils');
  9. const { SourceMapConsumer, SourceNode } = require('source-map');
  10. const {
  11. getIdentitySourceMap,
  12. getModuleSystem,
  13. getRefreshModuleRuntime,
  14. normalizeOptions,
  15. } = require('./utils');
  16. const schema = require('./options.json');
  17. const RefreshRuntimePath = require
  18. .resolve('react-refresh')
  19. .replace(/\\/g, '/')
  20. .replace(/'/g, "\\'");
  21. /**
  22. * A simple Webpack loader to inject react-refresh HMR code into modules.
  23. *
  24. * [Reference for Loader API](https://webpack.js.org/api/loaders/)
  25. * @this {import('webpack').LoaderContext<import('./types').ReactRefreshLoaderOptions>}
  26. * @param {string} source The original module source code.
  27. * @param {import('source-map').RawSourceMap} [inputSourceMap] The source map of the module.
  28. * @param {*} [meta] The loader metadata passed in.
  29. * @returns {void}
  30. */
  31. function ReactRefreshLoader(source, inputSourceMap, meta) {
  32. let options = getOptions(this);
  33. validateOptions(schema, options, {
  34. baseDataPath: 'options',
  35. name: 'React Refresh Loader',
  36. });
  37. options = normalizeOptions(options);
  38. const callback = this.async();
  39. const { ModuleFilenameHelpers, Template } = this._compiler.webpack || require('webpack');
  40. const RefreshSetupRuntimes = {
  41. cjs: Template.asString(
  42. `__webpack_require__.$Refresh$.runtime = require('${RefreshRuntimePath}');`
  43. ),
  44. esm: Template.asString([
  45. `import * as __react_refresh_runtime__ from '${RefreshRuntimePath}';`,
  46. `__webpack_require__.$Refresh$.runtime = __react_refresh_runtime__;`,
  47. ]),
  48. };
  49. /**
  50. * @this {import('webpack').LoaderContext<import('./types').ReactRefreshLoaderOptions>}
  51. * @param {string} source
  52. * @param {import('source-map').RawSourceMap} [inputSourceMap]
  53. * @returns {Promise<[string, import('source-map').RawSourceMap]>}
  54. */
  55. async function _loader(source, inputSourceMap) {
  56. /** @type {'esm' | 'cjs'} */
  57. let moduleSystem = 'cjs';
  58. // Only try to resolve the module system if the environment is known to support ES syntax
  59. if (this.environment != null) {
  60. moduleSystem = await getModuleSystem.call(this, ModuleFilenameHelpers, options);
  61. }
  62. const RefreshSetupRuntime = RefreshSetupRuntimes[moduleSystem];
  63. const RefreshModuleRuntime = getRefreshModuleRuntime(Template, {
  64. const: options.const,
  65. moduleSystem,
  66. });
  67. if (this.sourceMap) {
  68. let originalSourceMap = inputSourceMap;
  69. if (!originalSourceMap) {
  70. originalSourceMap = getIdentitySourceMap(source, this.resourcePath);
  71. }
  72. return SourceMapConsumer.with(originalSourceMap, undefined, (consumer) => {
  73. const node = SourceNode.fromStringWithSourceMap(source, consumer);
  74. node.prepend([RefreshSetupRuntime, '\n\n']);
  75. node.add(['\n\n', RefreshModuleRuntime]);
  76. const { code, map } = node.toStringWithSourceMap();
  77. return [code, map.toJSON()];
  78. });
  79. } else {
  80. return [[RefreshSetupRuntime, source, RefreshModuleRuntime].join('\n\n'), inputSourceMap];
  81. }
  82. }
  83. _loader.call(this, source, inputSourceMap).then(
  84. ([code, map]) => {
  85. callback(null, code, map, meta);
  86. },
  87. (error) => {
  88. callback(error);
  89. }
  90. );
  91. }
  92. module.exports = ReactRefreshLoader;
  93. // Restore the original value of the `fetch` global, if it exists
  94. if (originalFetch) {
  95. global.fetch = originalFetch;
  96. }