injectRefreshEntry.js 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. /** @typedef {string | string[] | import('webpack').Entry} StaticEntry */
  2. /** @typedef {StaticEntry | import('webpack').EntryFunc} WebpackEntry */
  3. const EntryParseError = new Error(
  4. [
  5. '[ReactRefreshPlugin]',
  6. 'Failed to parse the Webpack `entry` object!',
  7. 'Please ensure the `entry` option in your Webpack config is specified.',
  8. ].join(' ')
  9. );
  10. /**
  11. * Webpack entries related to socket integrations.
  12. * They have to run before any code that sets up the error overlay.
  13. * @type {string[]}
  14. */
  15. const socketEntries = [
  16. 'webpack-dev-server/client',
  17. 'webpack-hot-middleware/client',
  18. 'webpack-plugin-serve/client',
  19. 'react-dev-utils/webpackHotDevClient',
  20. ];
  21. /**
  22. * Checks if a Webpack entry string is related to socket integrations.
  23. * @param {string} entry A Webpack entry string.
  24. * @returns {boolean} Whether the entry is related to socket integrations.
  25. */
  26. function isSocketEntry(entry) {
  27. return socketEntries.some((socketEntry) => entry.includes(socketEntry));
  28. }
  29. /**
  30. * Injects an entry to the bundle for react-refresh.
  31. * @param {WebpackEntry} [originalEntry] A Webpack entry object.
  32. * @param {import('./getAdditionalEntries').AdditionalEntries} additionalEntries An object that contains the Webpack entries for prepending and the overlay feature
  33. * @returns {WebpackEntry} An injected entry object.
  34. */
  35. function injectRefreshEntry(originalEntry, additionalEntries) {
  36. const { prependEntries, overlayEntries } = additionalEntries;
  37. // Single string entry point
  38. if (typeof originalEntry === 'string') {
  39. if (isSocketEntry(originalEntry)) {
  40. return [...prependEntries, originalEntry, ...overlayEntries];
  41. }
  42. return [...prependEntries, ...overlayEntries, originalEntry];
  43. }
  44. // Single array entry point
  45. if (Array.isArray(originalEntry)) {
  46. if (originalEntry.length === 0) {
  47. throw EntryParseError;
  48. }
  49. const socketEntryIndex = originalEntry.findIndex(isSocketEntry);
  50. let socketAndPrecedingEntries = [];
  51. if (socketEntryIndex !== -1) {
  52. socketAndPrecedingEntries = originalEntry.splice(0, socketEntryIndex + 1);
  53. }
  54. return [...prependEntries, ...socketAndPrecedingEntries, ...overlayEntries, ...originalEntry];
  55. }
  56. // Multiple entry points
  57. if (typeof originalEntry === 'object') {
  58. const entries = Object.entries(originalEntry);
  59. if (entries.length === 0) {
  60. throw EntryParseError;
  61. }
  62. return entries.reduce(
  63. (acc, [curKey, curEntry]) => ({
  64. ...acc,
  65. [curKey]:
  66. typeof curEntry === 'object' && curEntry.import
  67. ? {
  68. ...curEntry,
  69. import: injectRefreshEntry(curEntry.import, additionalEntries),
  70. }
  71. : injectRefreshEntry(curEntry, additionalEntries),
  72. }),
  73. {}
  74. );
  75. }
  76. // Dynamic entry points
  77. if (typeof originalEntry === 'function') {
  78. return (...args) =>
  79. Promise.resolve(originalEntry(...args)).then((resolvedEntry) =>
  80. injectRefreshEntry(resolvedEntry, additionalEntries)
  81. );
  82. }
  83. throw EntryParseError;
  84. }
  85. module.exports = injectRefreshEntry;
  86. module.exports.socketEntries = socketEntries;