index.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const WEBPACK_PACKAGE = process.env.WEBPACK_PACKAGE || "webpack";
  4. const WEBPACK_DEV_SERVER_PACKAGE = process.env.WEBPACK_DEV_SERVER_PACKAGE || "webpack-dev-server";
  5. class ServeCommand {
  6. async apply(cli) {
  7. const loadDevServerOptions = () => {
  8. const devServer = require(WEBPACK_DEV_SERVER_PACKAGE);
  9. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  10. const options = cli.webpack.cli.getArguments(devServer.schema);
  11. // New options format
  12. // { flag1: {}, flag2: {} }
  13. return Object.keys(options).map((key) => {
  14. options[key].name = key;
  15. return options[key];
  16. });
  17. };
  18. await cli.makeCommand({
  19. name: "serve [entries...]",
  20. alias: ["server", "s"],
  21. description: "Run the webpack dev server and watch for source file changes while serving.",
  22. usage: "[entries...] [options]",
  23. pkg: "@webpack-cli/serve",
  24. dependencies: [WEBPACK_PACKAGE, WEBPACK_DEV_SERVER_PACKAGE],
  25. }, async () => {
  26. let devServerFlags = [];
  27. cli.webpack = await cli.loadWebpack();
  28. try {
  29. devServerFlags = loadDevServerOptions();
  30. }
  31. catch (error) {
  32. cli.logger.error(`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${error}`);
  33. process.exit(2);
  34. }
  35. const builtInOptions = cli.getBuiltInOptions();
  36. return [...builtInOptions, ...devServerFlags];
  37. },
  38. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  39. async (entries, options) => {
  40. const builtInOptions = cli.getBuiltInOptions();
  41. let devServerFlags = [];
  42. try {
  43. devServerFlags = loadDevServerOptions();
  44. }
  45. catch (_err) {
  46. // Nothing, to prevent future updates
  47. }
  48. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  49. const webpackCLIOptions = {};
  50. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  51. const devServerCLIOptions = {};
  52. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  53. const processors = [];
  54. for (const optionName in options) {
  55. const kebabedOption = cli.toKebabCase(optionName);
  56. const isBuiltInOption = builtInOptions.find(
  57. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  58. (builtInOption) => builtInOption.name === kebabedOption);
  59. if (isBuiltInOption) {
  60. webpackCLIOptions[optionName] = options[optionName];
  61. }
  62. else {
  63. const needToProcess = devServerFlags.find(
  64. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  65. (devServerOption) => devServerOption.name === kebabedOption && devServerOption.processor);
  66. if (needToProcess) {
  67. processors.push(needToProcess.processor);
  68. }
  69. devServerCLIOptions[optionName] = options[optionName];
  70. }
  71. }
  72. for (const processor of processors) {
  73. processor(devServerCLIOptions);
  74. }
  75. if (entries.length > 0) {
  76. webpackCLIOptions.entry = [...entries, ...(webpackCLIOptions.entry || [])];
  77. }
  78. webpackCLIOptions.argv = Object.assign(Object.assign({}, options), { env: Object.assign({ WEBPACK_SERVE: true }, options.env) });
  79. webpackCLIOptions.isWatchingLikeCommand = true;
  80. const compiler = await cli.createCompiler(webpackCLIOptions);
  81. if (!compiler) {
  82. return;
  83. }
  84. const servers = [];
  85. if (cli.needWatchStdin(compiler)) {
  86. process.stdin.on("end", () => {
  87. Promise.all(servers.map((server) => {
  88. return server.stop();
  89. })).then(() => {
  90. process.exit(0);
  91. });
  92. });
  93. process.stdin.resume();
  94. }
  95. const DevServer = require(WEBPACK_DEV_SERVER_PACKAGE);
  96. try {
  97. // eslint-disable-next-line @typescript-eslint/no-unused-expressions
  98. require(`${WEBPACK_DEV_SERVER_PACKAGE}/package.json`).version;
  99. }
  100. catch (err) {
  101. cli.logger.error(`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${err}`);
  102. process.exit(2);
  103. }
  104. const compilers = cli.isMultipleCompiler(compiler) ? compiler.compilers : [compiler];
  105. const possibleCompilers = compilers.filter((compiler) => compiler.options.devServer);
  106. const compilersForDevServer = possibleCompilers.length > 0 ? possibleCompilers : [compilers[0]];
  107. const usedPorts = [];
  108. for (const compilerForDevServer of compilersForDevServer) {
  109. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  110. // @ts-ignore
  111. if (compilerForDevServer.options.devServer === false) {
  112. continue;
  113. }
  114. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  115. const args = devServerFlags.reduce((accumulator, flag) => {
  116. accumulator[flag.name] = flag;
  117. return accumulator;
  118. }, {});
  119. const values = Object.keys(devServerCLIOptions).reduce(
  120. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  121. (accumulator, name) => {
  122. const kebabName = cli.toKebabCase(name);
  123. if (args[kebabName]) {
  124. accumulator[kebabName] = options[name];
  125. }
  126. return accumulator;
  127. }, {});
  128. const result = Object.assign({}, (compilerForDevServer.options.devServer || {}));
  129. const problems = (cli.webpack.cli && typeof cli.webpack.cli.processArguments === "function"
  130. ? cli.webpack.cli
  131. : DevServer.cli).processArguments(args, result, values);
  132. if (problems) {
  133. const groupBy = (xs, key) => {
  134. return xs.reduce((rv, x) => {
  135. (rv[x[key]] = rv[x[key]] || []).push(x);
  136. return rv;
  137. }, {});
  138. };
  139. const problemsByPath = groupBy(problems, "path");
  140. for (const path in problemsByPath) {
  141. const problems = problemsByPath[path];
  142. for (const problem of problems) {
  143. cli.logger.error(`${cli.capitalizeFirstLetter(problem.type.replace(/-/g, " "))}${problem.value ? ` '${problem.value}'` : ""} for the '--${problem.argument}' option${problem.index ? ` by index '${problem.index}'` : ""}`);
  144. if (problem.expected) {
  145. cli.logger.error(`Expected: '${problem.expected}'`);
  146. }
  147. }
  148. }
  149. process.exit(2);
  150. }
  151. const devServerOptions = result;
  152. if (devServerOptions.port) {
  153. const portNumber = Number(devServerOptions.port);
  154. if (usedPorts.find((port) => portNumber === port)) {
  155. throw new Error("Unique ports must be specified for each devServer option in your webpack configuration. Alternatively, run only 1 devServer config using the --config-name flag to specify your desired config.");
  156. }
  157. usedPorts.push(portNumber);
  158. }
  159. try {
  160. const server = new DevServer(devServerOptions, compiler);
  161. await server.start();
  162. servers.push(server);
  163. }
  164. catch (error) {
  165. if (cli.isValidationError(error)) {
  166. cli.logger.error(error.message);
  167. }
  168. else {
  169. cli.logger.error(error);
  170. }
  171. process.exit(2);
  172. }
  173. }
  174. if (servers.length === 0) {
  175. cli.logger.error("No dev server configurations to run");
  176. process.exit(2);
  177. }
  178. });
  179. }
  180. }
  181. exports.default = ServeCommand;