| 1 | const path = require('path');
|
|---|
| 2 |
|
|---|
| 3 | /**
|
|---|
| 4 | * @callback MatchObject
|
|---|
| 5 | * @param {string} [str]
|
|---|
| 6 | * @returns {boolean}
|
|---|
| 7 | */
|
|---|
| 8 |
|
|---|
| 9 | /**
|
|---|
| 10 | * @typedef {Object} InjectLoaderOptions
|
|---|
| 11 | * @property {MatchObject} match A function to include/exclude files to be processed.
|
|---|
| 12 | * @property {import('../../loader/types').ReactRefreshLoaderOptions} [options] Options passed to the loader.
|
|---|
| 13 | */
|
|---|
| 14 |
|
|---|
| 15 | const resolvedLoader = require.resolve('../../loader');
|
|---|
| 16 | const reactRefreshPath = path.dirname(require.resolve('react-refresh'));
|
|---|
| 17 | const refreshUtilsPath = path.join(__dirname, '../runtime/RefreshUtils');
|
|---|
| 18 |
|
|---|
| 19 | /**
|
|---|
| 20 | * Injects refresh loader to all JavaScript-like and user-specified files.
|
|---|
| 21 | * @param {*} moduleData Module factory creation data.
|
|---|
| 22 | * @param {InjectLoaderOptions} injectOptions Options to alter how the loader is injected.
|
|---|
| 23 | * @returns {*} The injected module factory creation data.
|
|---|
| 24 | */
|
|---|
| 25 | function injectRefreshLoader(moduleData, injectOptions) {
|
|---|
| 26 | const { match, options } = injectOptions;
|
|---|
| 27 |
|
|---|
| 28 | // Include and exclude user-specified files
|
|---|
| 29 | if (!match(moduleData.matchResource || moduleData.resource)) return moduleData;
|
|---|
| 30 | // Include and exclude dynamically generated modules from other loaders
|
|---|
| 31 | if (moduleData.matchResource && !match(moduleData.request)) return moduleData;
|
|---|
| 32 | // Exclude files referenced as assets
|
|---|
| 33 | if (moduleData.type.includes('asset')) return moduleData;
|
|---|
| 34 | // Check to prevent double injection
|
|---|
| 35 | if (moduleData.loaders.find(({ loader }) => loader === resolvedLoader)) return moduleData;
|
|---|
| 36 | // Skip react-refresh and the plugin's runtime utils to prevent self-referencing -
|
|---|
| 37 | // this is useful when using the plugin as a direct dependency,
|
|---|
| 38 | // or when node_modules are specified to be processed.
|
|---|
| 39 | if (
|
|---|
| 40 | moduleData.resource.includes(reactRefreshPath) ||
|
|---|
| 41 | moduleData.resource.includes(refreshUtilsPath)
|
|---|
| 42 | ) {
|
|---|
| 43 | return moduleData;
|
|---|
| 44 | }
|
|---|
| 45 |
|
|---|
| 46 | // As we inject runtime code for each module,
|
|---|
| 47 | // it is important to run the injected loader after everything.
|
|---|
| 48 | // This way we can ensure that all code-processing have been done,
|
|---|
| 49 | // and we won't risk breaking tools like Flow or ESLint.
|
|---|
| 50 | moduleData.loaders.unshift({
|
|---|
| 51 | loader: resolvedLoader,
|
|---|
| 52 | options,
|
|---|
| 53 | });
|
|---|
| 54 |
|
|---|
| 55 | return moduleData;
|
|---|
| 56 | }
|
|---|
| 57 |
|
|---|
| 58 | module.exports = injectRefreshLoader;
|
|---|