injectRefreshEntry.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. const querystring = require('querystring');
  2. const createError = require('./createError');
  3. /** @typedef {string | string[] | import('webpack').Entry} StaticEntry */
  4. /** @typedef {StaticEntry | import('webpack').EntryFunc} WebpackEntry */
  5. /**
  6. * Checks if a Webpack entry string is related to socket integrations.
  7. * @param {string} entry A Webpack entry string.
  8. * @returns {boolean} Whether the entry is related to socket integrations.
  9. */
  10. function isSocketEntry(entry) {
  11. /**
  12. * Webpack entries related to socket integrations.
  13. * They have to run before any code that sets up the error overlay.
  14. * @type {string[]}
  15. */
  16. const socketEntries = [
  17. 'webpack-dev-server/client',
  18. 'webpack-hot-middleware/client',
  19. 'webpack-plugin-serve/client',
  20. 'react-dev-utils/webpackHotDevClient',
  21. ];
  22. return socketEntries.some((socketEntry) => entry.includes(socketEntry));
  23. }
  24. /**
  25. * Injects an entry to the bundle for react-refresh.
  26. * @param {WebpackEntry} [originalEntry] A Webpack entry object.
  27. * @param {import('../types').NormalizedPluginOptions} options Configuration options for this plugin.
  28. * @returns {WebpackEntry} An injected entry object.
  29. */
  30. function injectRefreshEntry(originalEntry, options) {
  31. /** @type {Record<string, *>} */
  32. let resourceQuery = {};
  33. if (options.overlay) {
  34. options.overlay.sockHost && (resourceQuery.sockHost = options.overlay.sockHost);
  35. options.overlay.sockPath && (resourceQuery.sockPath = options.overlay.sockPath);
  36. options.overlay.sockPort && (resourceQuery.sockPort = options.overlay.sockPort);
  37. }
  38. // We don't need to URI encode the resourceQuery as it will be parsed by Webpack
  39. const queryString = querystring.stringify(resourceQuery, undefined, undefined, {
  40. /**
  41. * @param {string} string
  42. * @returns {string}
  43. */
  44. encodeURIComponent(string) {
  45. return string;
  46. },
  47. });
  48. const prependEntries = [
  49. // React-refresh runtime
  50. require.resolve('../../client/ReactRefreshEntry'),
  51. ];
  52. const overlayEntries = [
  53. // Legacy WDS SockJS integration
  54. options.overlay &&
  55. options.overlay.useLegacyWDSSockets &&
  56. require.resolve('../../client/LegacyWDSSocketEntry'),
  57. // Error overlay runtime
  58. options.overlay &&
  59. options.overlay.entry &&
  60. options.overlay.entry + (queryString && `?${queryString}`),
  61. ].filter(Boolean);
  62. // Single string entry point
  63. if (typeof originalEntry === 'string') {
  64. if (isSocketEntry(originalEntry)) {
  65. return [...prependEntries, originalEntry, ...overlayEntries];
  66. }
  67. return [...prependEntries, ...overlayEntries, originalEntry];
  68. }
  69. // Single array entry point
  70. if (Array.isArray(originalEntry)) {
  71. const socketEntryIndex = originalEntry.findIndex(isSocketEntry);
  72. let socketAndPrecedingEntries = [];
  73. if (socketEntryIndex !== -1) {
  74. socketAndPrecedingEntries = originalEntry.splice(0, socketEntryIndex + 1);
  75. }
  76. return [...prependEntries, ...socketAndPrecedingEntries, ...overlayEntries, ...originalEntry];
  77. }
  78. // Multiple entry points
  79. if (typeof originalEntry === 'object') {
  80. return Object.entries(originalEntry).reduce(
  81. (acc, [curKey, curEntry]) => ({
  82. ...acc,
  83. [curKey]:
  84. typeof curEntry === 'object' && curEntry.import
  85. ? {
  86. ...curEntry,
  87. import: injectRefreshEntry(curEntry.import, options),
  88. }
  89. : injectRefreshEntry(curEntry, options),
  90. }),
  91. {}
  92. );
  93. }
  94. // Dynamic entry points
  95. if (typeof originalEntry === 'function') {
  96. return (...args) =>
  97. Promise.resolve(originalEntry(...args)).then((resolvedEntry) =>
  98. injectRefreshEntry(resolvedEntry, options)
  99. );
  100. }
  101. throw createError('Failed to parse the Webpack `entry` object!');
  102. }
  103. module.exports = injectRefreshEntry;