makeRefreshRuntimeModule.js 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. const { getRefreshGlobalScope } = require('../globals');
  2. const getRefreshGlobal = require('./getRefreshGlobal');
  3. /**
  4. * Makes a runtime module to intercept module execution for React Refresh.
  5. * @param {import('webpack')} webpack The Webpack exports.
  6. * @returns {ReactRefreshRuntimeModule} The runtime module class.
  7. */
  8. function makeRefreshRuntimeModule(webpack) {
  9. return class ReactRefreshRuntimeModule extends webpack.RuntimeModule {
  10. constructor() {
  11. // Second argument is the `stage` for this runtime module -
  12. // we'll use the same stage as Webpack's HMR runtime module for safety.
  13. super('react refresh', webpack.RuntimeModule.STAGE_BASIC);
  14. }
  15. /**
  16. * @returns {string} runtime code
  17. */
  18. generate() {
  19. const { runtimeTemplate } = this.compilation;
  20. const refreshGlobal = getRefreshGlobalScope(webpack.RuntimeGlobals);
  21. return webpack.Template.asString([
  22. `${webpack.RuntimeGlobals.interceptModuleExecution}.push(${runtimeTemplate.basicFunction(
  23. 'options',
  24. [
  25. `${
  26. runtimeTemplate.supportsConst() ? 'const' : 'var'
  27. } originalFactory = options.factory;`,
  28. `options.factory = function (moduleObject, moduleExports, webpackRequire) {`,
  29. webpack.Template.indent([
  30. `${refreshGlobal}.setup(options.id);`,
  31. 'try {',
  32. webpack.Template.indent(
  33. 'originalFactory.call(this, moduleObject, moduleExports, webpackRequire);'
  34. ),
  35. '} finally {',
  36. webpack.Template.indent([
  37. `if (typeof Promise !== 'undefined' && moduleObject.exports instanceof Promise) {`,
  38. webpack.Template.indent([
  39. // The exports of the module are re-assigned -
  40. // this ensures anything coming after us would wait for `cleanup` to fire.
  41. // This is particularly important because `cleanup` restores the refresh global,
  42. // maintaining consistency for mutable variables like `moduleId`.
  43. // This `.then` clause is a ponyfill of the `Promise.finally` API -
  44. // it is only part of the spec after ES2018,
  45. // but Webpack's top level await implementation support engines down to ES2015.
  46. 'options.module.exports = options.module.exports.then(',
  47. webpack.Template.indent([
  48. `${runtimeTemplate.basicFunction('result', [
  49. `${refreshGlobal}.cleanup(options.id);`,
  50. 'return result;',
  51. ])},`,
  52. runtimeTemplate.basicFunction('reason', [
  53. `${refreshGlobal}.cleanup(options.id);`,
  54. 'return Promise.reject(reason);',
  55. ]),
  56. ]),
  57. `);`,
  58. ]),
  59. '} else {',
  60. webpack.Template.indent(`${refreshGlobal}.cleanup(options.id)`),
  61. '}',
  62. ]),
  63. '}',
  64. ]),
  65. `};`,
  66. ]
  67. )})`,
  68. '',
  69. getRefreshGlobal(webpack.Template, webpack.RuntimeGlobals, runtimeTemplate),
  70. ]);
  71. }
  72. };
  73. }
  74. module.exports = makeRefreshRuntimeModule;