@4399ywkf/core 5.1.3 → 5.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -4444,33 +4444,74 @@ import { RspackDevServer } from "@rspack/dev-server";
4444
4444
  import chalk3 from "chalk";
4445
4445
 
4446
4446
  // src/cli/port.ts
4447
+ import { execFile } from "child_process";
4447
4448
  import net from "net";
4448
- function isPortAvailable(port, host) {
4449
- return new Promise((resolve5) => {
4449
+ import { promisify } from "util";
4450
+ var execFileAsync = promisify(execFile);
4451
+ var PortInUseError = class extends Error {
4452
+ constructor(port, host) {
4453
+ super(`Port ${port} is already in use on ${host}`);
4454
+ this.port = port;
4455
+ this.host = host;
4456
+ this.name = "PortInUseError";
4457
+ }
4458
+ };
4459
+ function ensurePortAvailable(port, host) {
4460
+ return new Promise((resolve5, reject) => {
4450
4461
  const server = net.createServer();
4451
4462
  server.once("error", (err) => {
4452
4463
  if (err.code === "EADDRINUSE") {
4453
- resolve5(false);
4464
+ reject(new PortInUseError(port, host));
4454
4465
  } else {
4455
- resolve5(false);
4466
+ reject(err);
4456
4467
  }
4457
4468
  });
4458
4469
  server.once("listening", () => {
4459
- server.close(() => resolve5(true));
4470
+ server.close(() => resolve5());
4460
4471
  });
4461
4472
  server.listen(port, host);
4462
4473
  });
4463
4474
  }
4464
- async function getAvailablePort(preferredPort, host) {
4465
- const MAX_ATTEMPTS = 20;
4466
- for (let i = 0; i < MAX_ATTEMPTS; i++) {
4467
- const port = preferredPort + i;
4468
- const available = await isPortAvailable(port, host);
4469
- if (available) return port;
4470
- }
4471
- throw new Error(
4472
- `No available port found in range ${preferredPort}-${preferredPort + MAX_ATTEMPTS - 1}`
4473
- );
4475
+ async function findPortHolder(port) {
4476
+ try {
4477
+ if (process.platform === "win32") {
4478
+ const { stdout: stdout2 } = await execFileAsync("netstat", ["-ano", "-p", "tcp"], {
4479
+ timeout: 1500
4480
+ });
4481
+ const pid2 = parseWindowsNetstatPid(stdout2, port);
4482
+ return pid2 === null ? null : { pid: pid2 };
4483
+ }
4484
+ const { stdout } = await execFileAsync(
4485
+ "lsof",
4486
+ ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"],
4487
+ { timeout: 1500 }
4488
+ );
4489
+ const pid = Number.parseInt(stdout.trim().split(/\s+/)[0], 10);
4490
+ if (!Number.isInteger(pid)) return null;
4491
+ let name;
4492
+ try {
4493
+ const { stdout: comm } = await execFileAsync(
4494
+ "ps",
4495
+ ["-p", String(pid), "-o", "comm="],
4496
+ { timeout: 1500 }
4497
+ );
4498
+ name = comm.trim().split("/").pop() || void 0;
4499
+ } catch {
4500
+ }
4501
+ return { pid, name };
4502
+ } catch {
4503
+ return null;
4504
+ }
4505
+ }
4506
+ function parseWindowsNetstatPid(stdout, port) {
4507
+ for (const line of stdout.split(/\r?\n/)) {
4508
+ const parts = line.trim().split(/\s+/);
4509
+ if (parts.length >= 5 && parts[3] === "LISTENING" && parts[1]?.endsWith(`:${port}`)) {
4510
+ const pid = Number.parseInt(parts[4], 10);
4511
+ if (Number.isInteger(pid)) return pid;
4512
+ }
4513
+ }
4514
+ return null;
4474
4515
  }
4475
4516
 
4476
4517
  // src/cli/restart-watcher.ts
@@ -4535,6 +4576,22 @@ async function loadFullConfig(cwd, mode, envSnapshot, opts = {}) {
4535
4576
  loadEnv(config, cwd, mode, "development");
4536
4577
  return config;
4537
4578
  }
4579
+ async function renderPortInUseError(err) {
4580
+ const holder = await findPortHolder(err.port);
4581
+ console.error();
4582
+ console.error(chalk3.red(` \u2716 \u7AEF\u53E3 ${err.port} \u5DF2\u88AB\u5360\u7528\uFF0C\u65E0\u6CD5\u542F\u52A8 dev server`));
4583
+ if (holder) {
4584
+ const label = holder.name ? `${holder.name} ` : "";
4585
+ const killCmd = process.platform === "win32" ? `taskkill /PID ${holder.pid} /F` : `kill ${holder.pid}`;
4586
+ console.error(chalk3.gray(` \u5360\u7528\u8FDB\u7A0B\uFF1A${label}(PID ${holder.pid})`));
4587
+ console.error(chalk3.gray(` \xB7 \u7ED3\u675F\u5B83\uFF1A${killCmd}`));
4588
+ console.error(chalk3.gray(" \xB7 \u6216\u4FEE\u6539\u914D\u7F6E dev.port \u6362\u4E00\u4E2A\u7AEF\u53E3"));
4589
+ } else {
4590
+ console.error(chalk3.gray(" \xB7 \u4FEE\u6539\u914D\u7F6E dev.port \u6362\u4E00\u4E2A\u7AEF\u53E3"));
4591
+ console.error(chalk3.gray(" \xB7 \u6216\u5148\u91CA\u653E\u5360\u7528\u8BE5\u7AEF\u53E3\u7684\u8FDB\u7A0B"));
4592
+ }
4593
+ console.error();
4594
+ }
4538
4595
  async function startDevServer(cwd, config) {
4539
4596
  const pluginManager = new PluginManager(config, cwd, true);
4540
4597
  if (config.plugins && config.plugins.length > 0) {
@@ -4544,13 +4601,9 @@ async function startDevServer(cwd, config) {
4544
4601
  let rspackConfig = createRspackConfig(config, cwd, { isDev: true });
4545
4602
  rspackConfig = await pluginManager.applyRspackConfigHooks(rspackConfig);
4546
4603
  const host = config.dev.host || "127.0.0.1";
4547
- const preferredPort = config.dev.port || 3e3;
4548
- const port = await getAvailablePort(preferredPort, host);
4604
+ const port = config.dev.port || 3e3;
4605
+ await ensurePortAvailable(port, host);
4549
4606
  const pluginNames = pluginManager.getPluginNames();
4550
- if (port !== preferredPort) {
4551
- console.log(chalk3.yellow(` Port ${preferredPort} is in use, using ${port} instead.
4552
- `));
4553
- }
4554
4607
  const printer = new DevPrinter(host, port, pluginNames);
4555
4608
  printer.printBanner();
4556
4609
  printer.printBuildStart();
@@ -4668,10 +4721,17 @@ async function dev(options = {}) {
4668
4721
  await current.close();
4669
4722
  current = await startDevServer(cwd, nextConfig);
4670
4723
  } catch (error) {
4671
- console.error(
4672
- chalk3.red(" \u2716 dev server \u91CD\u542F\u5931\u8D25\uFF0C\u5F53\u524D\u670D\u52A1\u4E0D\u53EF\u7528\uFF0C\u8BF7\u4FEE\u6B63\u540E\u4FDD\u5B58\u91CD\u8BD5:")
4673
- );
4674
- console.error(error);
4724
+ if (error instanceof PortInUseError) {
4725
+ await renderPortInUseError(error);
4726
+ console.error(
4727
+ chalk3.red(" \u2716 dev server \u91CD\u542F\u5931\u8D25\uFF0C\u5F53\u524D\u670D\u52A1\u4E0D\u53EF\u7528\uFF0C\u8BF7\u4FEE\u6B63\u540E\u4FDD\u5B58\u91CD\u8BD5")
4728
+ );
4729
+ } else {
4730
+ console.error(
4731
+ chalk3.red(" \u2716 dev server \u91CD\u542F\u5931\u8D25\uFF0C\u5F53\u524D\u670D\u52A1\u4E0D\u53EF\u7528\uFF0C\u8BF7\u4FEE\u6B63\u540E\u4FDD\u5B58\u91CD\u8BD5:")
4732
+ );
4733
+ console.error(error);
4734
+ }
4675
4735
  }
4676
4736
  } while (pending);
4677
4737
  } finally {
@@ -4680,6 +4740,10 @@ async function dev(options = {}) {
4680
4740
  }
4681
4741
  });
4682
4742
  } catch (error) {
4743
+ if (error instanceof PortInUseError) {
4744
+ await renderPortInUseError(error);
4745
+ process.exit(1);
4746
+ }
4683
4747
  console.error();
4684
4748
  console.error(chalk3.red(" \u2716 Dev server failed to start"));
4685
4749
  console.error(error);