addEntries.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. 'use strict';
  2. const webpack = require('webpack');
  3. const createDomain = require('./createDomain');
  4. /**
  5. * A Entry, it can be of type string or string[] or Object<string | string[],string>
  6. * @typedef {(string[] | string | Object<string | string[],string>)} Entry
  7. */
  8. /**
  9. * Add entries Method
  10. * @param {?Object} config - Webpack config
  11. * @param {?Object} options - Dev-Server options
  12. * @param {?Object} server
  13. * @returns {void}
  14. */
  15. function addEntries(config, options, server) {
  16. if (options.inline !== false) {
  17. // we're stubbing the app in this method as it's static and doesn't require
  18. // a server to be supplied. createDomain requires an app with the
  19. // address() signature.
  20. const app = server || {
  21. address() {
  22. return { port: options.port };
  23. },
  24. };
  25. /** @type {string} */
  26. const domain = createDomain(options, app);
  27. /** @type {string} */
  28. const sockHost = options.sockHost ? `&sockHost=${options.sockHost}` : '';
  29. /** @type {string} */
  30. const sockPath = options.sockPath ? `&sockPath=${options.sockPath}` : '';
  31. /** @type {string} */
  32. const sockPort = options.sockPort ? `&sockPort=${options.sockPort}` : '';
  33. /** @type {string} */
  34. const clientEntry = `${require.resolve(
  35. '../../client/'
  36. )}?${domain}${sockHost}${sockPath}${sockPort}`;
  37. /** @type {(string[] | string)} */
  38. let hotEntry;
  39. if (options.hotOnly) {
  40. hotEntry = require.resolve('webpack/hot/only-dev-server');
  41. } else if (options.hot) {
  42. hotEntry = require.resolve('webpack/hot/dev-server');
  43. }
  44. /**
  45. * prependEntry Method
  46. * @param {Entry} originalEntry
  47. * @param {Entry} additionalEntries
  48. * @returns {Entry}
  49. */
  50. const prependEntry = (originalEntry, additionalEntries) => {
  51. if (typeof originalEntry === 'function') {
  52. return () =>
  53. Promise.resolve(originalEntry()).then((entry) =>
  54. prependEntry(entry, additionalEntries)
  55. );
  56. }
  57. if (typeof originalEntry === 'object' && !Array.isArray(originalEntry)) {
  58. /** @type {Object<string,string>} */
  59. const clone = {};
  60. Object.keys(originalEntry).forEach((key) => {
  61. // entry[key] should be a string here
  62. const entryDescription = originalEntry[key];
  63. if (typeof entryDescription === 'object' && entryDescription.import) {
  64. clone[key] = Object.assign({}, entryDescription, {
  65. import: prependEntry(entryDescription.import, additionalEntries),
  66. });
  67. } else {
  68. clone[key] = prependEntry(entryDescription, additionalEntries);
  69. }
  70. });
  71. return clone;
  72. }
  73. // in this case, entry is a string or an array.
  74. // make sure that we do not add duplicates.
  75. /** @type {Entry} */
  76. const entriesClone = additionalEntries.slice(0);
  77. [].concat(originalEntry).forEach((newEntry) => {
  78. if (!entriesClone.includes(newEntry)) {
  79. entriesClone.push(newEntry);
  80. }
  81. });
  82. return entriesClone;
  83. };
  84. /**
  85. *
  86. * Description of the option for checkInject method
  87. * @typedef {Function} checkInjectOptionsParam
  88. * @param {Object} _config - compilerConfig
  89. * @return {Boolean}
  90. */
  91. /**
  92. *
  93. * @param {Boolean | checkInjectOptionsParam} option - inject(Hot|Client) it is Boolean | fn => Boolean
  94. * @param {Object} _config
  95. * @param {Boolean} defaultValue
  96. * @return {Boolean}
  97. */
  98. // eslint-disable-next-line no-shadow
  99. const checkInject = (option, _config, defaultValue) => {
  100. if (typeof option === 'boolean') return option;
  101. if (typeof option === 'function') return option(_config);
  102. return defaultValue;
  103. };
  104. // eslint-disable-next-line no-shadow
  105. [].concat(config).forEach((config) => {
  106. /** @type {Boolean} */
  107. const webTarget = [
  108. 'web',
  109. 'webworker',
  110. 'electron-renderer',
  111. 'node-webkit',
  112. undefined, // eslint-disable-line
  113. null,
  114. ].includes(config.target);
  115. /** @type {Entry} */
  116. const additionalEntries = checkInject(
  117. options.injectClient,
  118. config,
  119. webTarget
  120. )
  121. ? [clientEntry]
  122. : [];
  123. if (hotEntry && checkInject(options.injectHot, config, true)) {
  124. additionalEntries.push(hotEntry);
  125. }
  126. config.entry = prependEntry(config.entry || './src', additionalEntries);
  127. if (options.hot || options.hotOnly) {
  128. config.plugins = config.plugins || [];
  129. if (
  130. !config.plugins.find(
  131. // Check for the name rather than the constructor reference in case
  132. // there are multiple copies of webpack installed
  133. (plugin) => plugin.constructor.name === 'HotModuleReplacementPlugin'
  134. )
  135. ) {
  136. config.plugins.push(new webpack.HotModuleReplacementPlugin());
  137. }
  138. }
  139. });
  140. }
  141. }
  142. module.exports = addEntries;