index.js 8.5 KB

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