UNPKG

3.51 kBJavaScriptView Raw
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.
5const originalFetch = global.fetch;
6delete global.fetch;
7
8const { getOptions } = require('loader-utils');
9const { validate: validateOptions } = require('schema-utils');
10const { SourceMapConsumer, SourceNode } = require('source-map');
11const {
12 getIdentitySourceMap,
13 getModuleSystem,
14 getRefreshModuleRuntime,
15 normalizeOptions,
16} = require('./utils');
17const schema = require('./options.json');
18
19const RefreshRuntimePath = require
20 .resolve('react-refresh/runtime.js')
21 .replace(/\\/g, '/')
22 .replace(/'/g, "\\'");
23
24/**
25 * A simple Webpack loader to inject react-refresh HMR code into modules.
26 *
27 * [Reference for Loader API](https://webpack.js.org/api/loaders/)
28 * @this {import('webpack').loader.LoaderContext}
29 * @param {string} source The original module source code.
30 * @param {import('source-map').RawSourceMap} [inputSourceMap] The source map of the module.
31 * @param {*} [meta] The loader metadata passed in.
32 * @returns {void}
33 */
34function ReactRefreshLoader(source, inputSourceMap, meta) {
35 let options = getOptions(this);
36 validateOptions(schema, options, {
37 baseDataPath: 'options',
38 name: 'React Refresh Loader',
39 });
40
41 options = normalizeOptions(options);
42
43 const callback = this.async();
44
45 const { ModuleFilenameHelpers, Template } = this._compiler.webpack || require('webpack');
46
47 const RefreshSetupRuntimes = {
48 cjs: Template.asString(
49 `__webpack_require__.$Refresh$.runtime = require('${RefreshRuntimePath}');`
50 ),
51 esm: Template.asString([
52 `import * as __react_refresh_runtime__ from '${RefreshRuntimePath}';`,
53 `__webpack_require__.$Refresh$.runtime = __react_refresh_runtime__;`,
54 ]),
55 };
56
57 /**
58 * @this {import('webpack').loader.LoaderContext}
59 * @param {string} source
60 * @param {import('source-map').RawSourceMap} [inputSourceMap]
61 * @returns {Promise<[string, import('source-map').RawSourceMap]>}
62 */
63 async function _loader(source, inputSourceMap) {
64 const moduleSystem = await getModuleSystem.call(this, ModuleFilenameHelpers, options);
65
66 const RefreshSetupRuntime = RefreshSetupRuntimes[moduleSystem];
67 const RefreshModuleRuntime = getRefreshModuleRuntime(Template, {
68 const: options.const,
69 moduleSystem,
70 });
71
72 if (this.sourceMap) {
73 let originalSourceMap = inputSourceMap;
74 if (!originalSourceMap) {
75 originalSourceMap = getIdentitySourceMap(source, this.resourcePath);
76 }
77
78 const node = SourceNode.fromStringWithSourceMap(
79 source,
80 await new SourceMapConsumer(originalSourceMap)
81 );
82
83 node.prepend([RefreshSetupRuntime, '\n\n']);
84 node.add(['\n\n', RefreshModuleRuntime]);
85
86 const { code, map } = node.toStringWithSourceMap();
87 return [code, map.toJSON()];
88 } else {
89 return [[RefreshSetupRuntime, source, RefreshModuleRuntime].join('\n\n'), inputSourceMap];
90 }
91 }
92
93 _loader.call(this, source, inputSourceMap).then(
94 ([code, map]) => {
95 callback(null, code, map, meta);
96 },
97 (error) => {
98 callback(error);
99 }
100 );
101}
102
103module.exports = ReactRefreshLoader;
104
105// Restore the original value of the `fetch` global, if it exists
106if (originalFetch) {
107 global.fetch = originalFetch;
108}