Spaces:
Sleeping
Sleeping
// 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').LoaderContext<import('./types').ReactRefreshLoaderOptions>} | |
* @param {string} source | |
* @param {import('source-map').RawSourceMap} [inputSourceMap] | |
* @returns {Promise<[string, import('source-map').RawSourceMap]>} | |
*/ | |
async function _loader(source, inputSourceMap) { | |
/** @type {'esm' | 'cjs'} */ | |
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; | |
} | |