Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

425 строки
9.3 KiB

  1. #!/usr/bin/env node
  2. 'use strict';
  3. /* eslint-disable
  4. import/order,
  5. import/no-extraneous-dependencies,
  6. global-require,
  7. no-shadow,
  8. no-console,
  9. multiline-ternary,
  10. arrow-parens,
  11. array-bracket-spacing,
  12. space-before-function-paren
  13. */
  14. const debug = require('debug')('webpack-dev-server');
  15. const fs = require('fs');
  16. const net = require('net');
  17. const path = require('path');
  18. const portfinder = require('portfinder');
  19. const importLocal = require('import-local');
  20. const yargs = require('yargs');
  21. const webpack = require('webpack');
  22. const options = require('./options');
  23. const {
  24. colors,
  25. status,
  26. version,
  27. bonjour,
  28. defaultTo
  29. } = require('./utils');
  30. const Server = require('../lib/Server');
  31. const addEntries = require('../lib/utils/addEntries');
  32. const createDomain = require('../lib/utils/createDomain');
  33. const createLogger = require('../lib/utils/createLogger');
  34. let server;
  35. const signals = [ 'SIGINT', 'SIGTERM' ];
  36. signals.forEach((signal) => {
  37. process.on(signal, () => {
  38. if (server) {
  39. server.close(() => {
  40. // eslint-disable-next-line no-process-exit
  41. process.exit();
  42. });
  43. } else {
  44. // eslint-disable-next-line no-process-exit
  45. process.exit();
  46. }
  47. });
  48. });
  49. // Prefer the local installation of webpack-dev-server
  50. if (importLocal(__filename)) {
  51. debug('Using local install of webpack-dev-server');
  52. return;
  53. }
  54. try {
  55. require.resolve('webpack-cli');
  56. } catch (err) {
  57. console.error('The CLI moved into a separate package: webpack-cli');
  58. console.error('Please install \'webpack-cli\' in addition to webpack itself to use the CLI');
  59. console.error('-> When using npm: npm i -D webpack-cli');
  60. console.error('-> When using yarn: yarn add -D webpack-cli');
  61. process.exitCode = 1;
  62. }
  63. yargs.usage(
  64. `${version()}\nUsage: https://webpack.js.org/configuration/dev-server/`
  65. );
  66. require('webpack-cli/bin/config-yargs')(yargs);
  67. // It is important that this is done after the webpack yargs config,
  68. // so it overrides webpack's version info.
  69. yargs.version(version());
  70. yargs.options(options);
  71. const argv = yargs.argv;
  72. const config = require('webpack-cli/bin/convert-argv')(yargs, argv, {
  73. outputFilename: '/bundle.js'
  74. });
  75. // Taken out of yargs because we must know if
  76. // it wasn't given by the user, in which case
  77. // we should use portfinder.
  78. const DEFAULT_PORT = 8080;
  79. function processOptions (config) {
  80. // processOptions {Promise}
  81. if (typeof config.then === 'function') {
  82. config.then(processOptions).catch((err) => {
  83. console.error(err.stack || err);
  84. // eslint-disable-next-line no-process-exit
  85. process.exit();
  86. });
  87. return;
  88. }
  89. const firstWpOpt = Array.isArray(config)
  90. ? config[0]
  91. : config;
  92. const options = config.devServer || firstWpOpt.devServer || {};
  93. if (argv.bonjour) {
  94. options.bonjour = true;
  95. }
  96. if (argv.host !== 'localhost' || !options.host) {
  97. options.host = argv.host;
  98. }
  99. if (argv['allowed-hosts']) {
  100. options.allowedHosts = argv['allowed-hosts'].split(',');
  101. }
  102. if (argv.public) {
  103. options.public = argv.public;
  104. }
  105. if (argv.socket) {
  106. options.socket = argv.socket;
  107. }
  108. if (argv.progress) {
  109. options.progress = argv.progress;
  110. }
  111. if (!options.publicPath) {
  112. // eslint-disable-next-line
  113. options.publicPath = firstWpOpt.output && firstWpOpt.output.publicPath || '';
  114. if (
  115. !/^(https?:)?\/\//.test(options.publicPath) &&
  116. options.publicPath[0] !== '/'
  117. ) {
  118. options.publicPath = `/${options.publicPath}`;
  119. }
  120. }
  121. if (!options.filename) {
  122. options.filename = firstWpOpt.output && firstWpOpt.output.filename;
  123. }
  124. if (!options.watchOptions) {
  125. options.watchOptions = firstWpOpt.watchOptions;
  126. }
  127. if (argv.stdin) {
  128. process.stdin.on('end', () => {
  129. // eslint-disable-next-line no-process-exit
  130. process.exit(0);
  131. });
  132. process.stdin.resume();
  133. }
  134. if (!options.hot) {
  135. options.hot = argv.hot;
  136. }
  137. if (!options.hotOnly) {
  138. options.hotOnly = argv['hot-only'];
  139. }
  140. if (!options.clientLogLevel) {
  141. options.clientLogLevel = argv['client-log-level'];
  142. }
  143. // eslint-disable-next-line
  144. if (options.contentBase === undefined) {
  145. if (argv['content-base']) {
  146. options.contentBase = argv['content-base'];
  147. if (Array.isArray(options.contentBase)) {
  148. options.contentBase = options.contentBase.map((p) => path.resolve(p));
  149. } else if (/^[0-9]$/.test(options.contentBase)) {
  150. options.contentBase = +options.contentBase;
  151. } else if (!/^(https?:)?\/\//.test(options.contentBase)) {
  152. options.contentBase = path.resolve(options.contentBase);
  153. }
  154. // It is possible to disable the contentBase by using
  155. // `--no-content-base`, which results in arg["content-base"] = false
  156. } else if (argv['content-base'] === false) {
  157. options.contentBase = false;
  158. }
  159. }
  160. if (argv['watch-content-base']) {
  161. options.watchContentBase = true;
  162. }
  163. if (!options.stats) {
  164. options.stats = {
  165. cached: false,
  166. cachedAssets: false
  167. };
  168. }
  169. if (
  170. typeof options.stats === 'object' &&
  171. typeof options.stats.colors === 'undefined'
  172. ) {
  173. options.stats = Object.assign(
  174. {},
  175. options.stats,
  176. { colors: argv.color }
  177. );
  178. }
  179. if (argv.lazy) {
  180. options.lazy = true;
  181. }
  182. if (!argv.info) {
  183. options.noInfo = true;
  184. }
  185. if (argv.quiet) {
  186. options.quiet = true;
  187. }
  188. if (argv.https) {
  189. options.https = true;
  190. }
  191. if (argv.cert) {
  192. options.cert = fs.readFileSync(
  193. path.resolve(argv.cert)
  194. );
  195. }
  196. if (argv.key) {
  197. options.key = fs.readFileSync(
  198. path.resolve(argv.key)
  199. );
  200. }
  201. if (argv.cacert) {
  202. options.ca = fs.readFileSync(
  203. path.resolve(argv.cacert)
  204. );
  205. }
  206. if (argv.pfx) {
  207. options.pfx = fs.readFileSync(
  208. path.resolve(argv.pfx)
  209. );
  210. }
  211. if (argv['pfx-passphrase']) {
  212. options.pfxPassphrase = argv['pfx-passphrase'];
  213. }
  214. if (argv.inline === false) {
  215. options.inline = false;
  216. }
  217. if (argv['history-api-fallback']) {
  218. options.historyApiFallback = true;
  219. }
  220. if (argv.compress) {
  221. options.compress = true;
  222. }
  223. if (argv['disable-host-check']) {
  224. options.disableHostCheck = true;
  225. }
  226. if (argv['open-page']) {
  227. options.open = true;
  228. options.openPage = argv['open-page'];
  229. }
  230. if (typeof argv.open !== 'undefined') {
  231. options.open = argv.open !== '' ? argv.open : true;
  232. }
  233. if (options.open && !options.openPage) {
  234. options.openPage = '';
  235. }
  236. if (argv.useLocalIp) {
  237. options.useLocalIp = true;
  238. }
  239. // Kind of weird, but ensures prior behavior isn't broken in cases
  240. // that wouldn't throw errors. E.g. both argv.port and options.port
  241. // were specified, but since argv.port is 8080, options.port will be
  242. // tried first instead.
  243. options.port = argv.port === DEFAULT_PORT
  244. ? defaultTo(options.port, argv.port)
  245. : defaultTo(argv.port, options.port);
  246. if (options.port != null) {
  247. startDevServer(config, options);
  248. return;
  249. }
  250. portfinder.basePort = DEFAULT_PORT;
  251. portfinder.getPort((err, port) => {
  252. if (err) {
  253. throw err;
  254. }
  255. options.port = port;
  256. startDevServer(config, options);
  257. });
  258. }
  259. function startDevServer(config, options) {
  260. const log = createLogger(options);
  261. addEntries(config, options);
  262. let compiler;
  263. try {
  264. compiler = webpack(config);
  265. } catch (err) {
  266. if (err instanceof webpack.WebpackOptionsValidationError) {
  267. log.error(colors.error(options.stats.colors, err.message));
  268. // eslint-disable-next-line no-process-exit
  269. process.exit(1);
  270. }
  271. throw err;
  272. }
  273. if (options.progress) {
  274. new webpack.ProgressPlugin({
  275. profile: argv.profile
  276. }).apply(compiler);
  277. }
  278. const suffix = (options.inline !== false || options.lazy === true ? '/' : '/webpack-dev-server/');
  279. try {
  280. server = new Server(compiler, options, log);
  281. } catch (err) {
  282. if (err.name === 'ValidationError') {
  283. log.error(colors.error(options.stats.colors, err.message));
  284. // eslint-disable-next-line no-process-exit
  285. process.exit(1);
  286. }
  287. throw err;
  288. }
  289. if (options.socket) {
  290. server.listeningApp.on('error', (e) => {
  291. if (e.code === 'EADDRINUSE') {
  292. const clientSocket = new net.Socket();
  293. clientSocket.on('error', (err) => {
  294. if (err.code === 'ECONNREFUSED') {
  295. // No other server listening on this socket so it can be safely removed
  296. fs.unlinkSync(options.socket);
  297. server.listen(options.socket, options.host, (error) => {
  298. if (error) {
  299. throw error;
  300. }
  301. });
  302. }
  303. });
  304. clientSocket.connect({ path: options.socket }, () => {
  305. throw new Error('This socket is already used');
  306. });
  307. }
  308. });
  309. server.listen(options.socket, options.host, (err) => {
  310. if (err) {
  311. throw err;
  312. }
  313. // chmod 666 (rw rw rw)
  314. const READ_WRITE = 438;
  315. fs.chmod(options.socket, READ_WRITE, (err) => {
  316. if (err) {
  317. throw err;
  318. }
  319. const uri = createDomain(options, server.listeningApp) + suffix;
  320. status(uri, options, log, argv.color);
  321. });
  322. });
  323. } else {
  324. server.listen(options.port, options.host, (err) => {
  325. if (err) {
  326. throw err;
  327. }
  328. if (options.bonjour) {
  329. bonjour(options);
  330. }
  331. const uri = createDomain(options, server.listeningApp) + suffix;
  332. status(uri, options, log, argv.color);
  333. });
  334. }
  335. }
  336. processOptions(config);