index.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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 { SourceMapConsumer, SourceMapGenerator, SourceNode } = require('source-map');
  8. const { Template } = require('webpack');
  9. /**
  10. * Generates an identity source map from a source file.
  11. * @param {string} source The content of the source file.
  12. * @param {string} resourcePath The name of the source file.
  13. * @returns {import('source-map').RawSourceMap} The identity source map.
  14. */
  15. function getIdentitySourceMap(source, resourcePath) {
  16. const sourceMap = new SourceMapGenerator();
  17. sourceMap.setSourceContent(resourcePath, source);
  18. source.split('\n').forEach((line, index) => {
  19. sourceMap.addMapping({
  20. source: resourcePath,
  21. original: {
  22. line: index + 1,
  23. column: 0,
  24. },
  25. generated: {
  26. line: index + 1,
  27. column: 0,
  28. },
  29. });
  30. });
  31. return sourceMap.toJSON();
  32. }
  33. /**
  34. * Gets a runtime template from provided function.
  35. * @param {function(): void} fn A function containing the runtime template.
  36. * @returns {string} The "sanitized" runtime template.
  37. */
  38. function getTemplate(fn) {
  39. return Template.getFunctionContent(fn).trim().replace(/^ {2}/gm, '');
  40. }
  41. const RefreshSetupRuntime = getTemplate(require('./RefreshSetup.runtime')).replace(
  42. '$RefreshRuntimePath$',
  43. require.resolve('react-refresh/runtime').replace(/\\/g, '/')
  44. );
  45. const RefreshModuleRuntime = getTemplate(require('./RefreshModule.runtime'));
  46. /**
  47. * A simple Webpack loader to inject react-refresh HMR code into modules.
  48. *
  49. * [Reference for Loader API](https://webpack.js.org/api/loaders/)
  50. * @this {import('webpack').loader.LoaderContext}
  51. * @param {string} source The original module source code.
  52. * @param {import('source-map').RawSourceMap} [inputSourceMap] The source map of the module.
  53. * @param {*} [meta] The loader metadata passed in.
  54. * @returns {void}
  55. */
  56. function ReactRefreshLoader(source, inputSourceMap, meta) {
  57. const callback = this.async();
  58. /**
  59. * @this {import('webpack').loader.LoaderContext}
  60. * @param {string} source
  61. * @param {import('source-map').RawSourceMap} [inputSourceMap]
  62. * @returns {Promise<[string, import('source-map').RawSourceMap]>}
  63. */
  64. async function _loader(source, inputSourceMap) {
  65. if (this.sourceMap) {
  66. let originalSourceMap = inputSourceMap;
  67. if (!originalSourceMap) {
  68. originalSourceMap = getIdentitySourceMap(source, this.resourcePath);
  69. }
  70. const node = SourceNode.fromStringWithSourceMap(
  71. source,
  72. await new SourceMapConsumer(originalSourceMap)
  73. );
  74. node.prepend([RefreshSetupRuntime, '\n\n']);
  75. node.add(['\n\n', RefreshModuleRuntime]);
  76. const { code, map } = node.toStringWithSourceMap();
  77. return [code, map.toJSON()];
  78. } else {
  79. return [[RefreshSetupRuntime, source, RefreshModuleRuntime].join('\n\n'), inputSourceMap];
  80. }
  81. }
  82. _loader.call(this, source, inputSourceMap).then(
  83. ([code, map]) => {
  84. callback(null, code, map, meta);
  85. },
  86. (error) => {
  87. callback(error);
  88. }
  89. );
  90. }
  91. module.exports = ReactRefreshLoader;
  92. // Restore the original value of the `fetch` global, if it exists
  93. if (originalFetch) {
  94. global.fetch = originalFetch;
  95. }