You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

108 lines
3.5 KiB

2 years ago
// This is a patch for mozilla/source-map#349 -
// internally, it uses the existence of the `fetch` global to toggle browser behaviours.
// That check, however, will break when `fetch` polyfills are used for SSR setups.
// We "reset" the polyfill here to ensure it won't interfere with source-map generation.
const originalFetch = global.fetch;
delete global.fetch;
const { getOptions } = require('loader-utils');
const { validate: validateOptions } = require('schema-utils');
const { SourceMapConsumer, SourceNode } = require('source-map');
const {
getIdentitySourceMap,
getModuleSystem,
getRefreshModuleRuntime,
normalizeOptions,
} = require('./utils');
const schema = require('./options.json');
const RefreshRuntimePath = require
.resolve('react-refresh')
.replace(/\\/g, '/')
.replace(/'/g, "\\'");
/**
* A simple Webpack loader to inject react-refresh HMR code into modules.
*
* [Reference for Loader API](https://webpack.js.org/api/loaders/)
* @this {import('webpack').LoaderContext<import('./types').ReactRefreshLoaderOptions>}
* @param {string} source The original module source code.
* @param {import('source-map').RawSourceMap} [inputSourceMap] The source map of the module.
* @param {*} [meta] The loader metadata passed in.
* @returns {void}
*/
function ReactRefreshLoader(source, inputSourceMap, meta) {
let options = getOptions(this);
validateOptions(schema, options, {
baseDataPath: 'options',
name: 'React Refresh Loader',
});
options = normalizeOptions(options);
const callback = this.async();
const { ModuleFilenameHelpers, Template } = this._compiler.webpack || require('webpack');
const RefreshSetupRuntimes = {
cjs: Template.asString(
`__webpack_require__.$Refresh$.runtime = require('${RefreshRuntimePath}');`
),
esm: Template.asString([
`import * as __react_refresh_runtime__ from '${RefreshRuntimePath}';`,
`__webpack_require__.$Refresh$.runtime = __react_refresh_runtime__;`,
]),
};
/**
* @this {import('webpack').loader.LoaderContext}
* @param {string} source
* @param {import('source-map').RawSourceMap} [inputSourceMap]
* @returns {Promise<[string, import('source-map').RawSourceMap]>}
*/
async function _loader(source, inputSourceMap) {
const moduleSystem = await getModuleSystem.call(this, ModuleFilenameHelpers, options);
const RefreshSetupRuntime = RefreshSetupRuntimes[moduleSystem];
const RefreshModuleRuntime = getRefreshModuleRuntime(Template, {
const: options.const,
moduleSystem,
});
if (this.sourceMap) {
let originalSourceMap = inputSourceMap;
if (!originalSourceMap) {
originalSourceMap = getIdentitySourceMap(source, this.resourcePath);
}
return SourceMapConsumer.with(originalSourceMap, undefined, (consumer) => {
const node = SourceNode.fromStringWithSourceMap(source, consumer);
node.prepend([RefreshSetupRuntime, '\n\n']);
node.add(['\n\n', RefreshModuleRuntime]);
const { code, map } = node.toStringWithSourceMap();
return [code, map.toJSON()];
});
} else {
return [[RefreshSetupRuntime, source, RefreshModuleRuntime].join('\n\n'), inputSourceMap];
}
}
_loader.call(this, source, inputSourceMap).then(
([code, map]) => {
callback(null, code, map, meta);
},
(error) => {
callback(error);
}
);
}
module.exports = ReactRefreshLoader;
// Restore the original value of the `fetch` global, if it exists
if (originalFetch) {
global.fetch = originalFetch;
}