fenge 0.7.4 → 0.7.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # fenge
2
2
 
3
+ ## 0.7.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 5c5cbc9: fix: correct exit code
8
+ - Updated dependencies [db50469]
9
+ - @fenge/eslint-config@0.6.6
10
+
11
+ ## 0.7.5
12
+
13
+ ### Patch Changes
14
+
15
+ - @fenge/eslint-config@0.6.5
16
+
3
17
  ## 0.7.4
4
18
 
5
19
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fenge",
3
- "version": "0.7.4",
3
+ "version": "0.7.6",
4
4
  "description": "A CLI tool for code quality",
5
5
  "keywords": [
6
6
  "cli",
@@ -51,14 +51,14 @@
51
51
  "ora": "8.2.0",
52
52
  "prettier": "3.5.3",
53
53
  "yoctocolors": "2.1.1",
54
- "@fenge/eslint-config": "0.6.4",
54
+ "@fenge/eslint-config": "0.6.6",
55
55
  "@fenge/prettier-config": "0.3.0",
56
56
  "@fenge/tsconfig": "0.5.0",
57
- "@fenge/types": "0.3.0",
58
- "prettier-ignore": "0.3.0"
57
+ "prettier-ignore": "0.3.0",
58
+ "@fenge/types": "0.3.0"
59
59
  },
60
60
  "devDependencies": {
61
- "@types/node": "22.13.10"
61
+ "@types/node": "22.14.1"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "typescript": "^5.7.0"
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // @ts-check
3
+ import os from "node:os";
3
4
  import process from "node:process";
4
5
  import { initAction, setup } from "@fenge/tsconfig/setup";
5
6
  import { Command } from "commander";
@@ -9,6 +10,22 @@ import { install } from "../command/install.js";
9
10
  import { lint } from "../command/lint.js";
10
11
  import { uninstall } from "../command/uninstall.js";
11
12
 
13
+ /**
14
+ * @param {{code: number|null, signal: NodeJS.Signals | null}} param
15
+ * @returns {never}
16
+ */
17
+ function exit({ code, signal }) {
18
+ if (code !== null) {
19
+ process.exit(code);
20
+ } else if (signal !== null) {
21
+ process.exit(128 + os.constants.signals[signal]);
22
+ } else {
23
+ throw new Error(
24
+ "Internal Error. Code and signal should not be null at the same time.",
25
+ );
26
+ }
27
+ }
28
+
12
29
  const program = new Command().enablePositionalOptions();
13
30
 
14
31
  program
@@ -35,11 +52,14 @@ program
35
52
  )
36
53
  .argument("[paths...]", "dir or file paths to format and lint")
37
54
  .action(async (paths, options) => {
38
- let code = (await format(paths, options)) || (await lint(paths, options));
39
- if (options.fix || options.update) {
40
- code ||= await format(paths, options);
55
+ let result = await format(paths, options);
56
+ if (result.code === 0) {
57
+ result = await lint(paths, options);
58
+ if (result.code === 0) {
59
+ result = await format(paths, options);
60
+ }
41
61
  }
42
- process.exit(code);
62
+ exit(result);
43
63
  });
44
64
 
45
65
  program
@@ -58,7 +78,7 @@ program
58
78
  "print what command will be executed under the hood instead of executing",
59
79
  )
60
80
  .argument("[paths...]", "dir or file paths to lint")
61
- .action(async (paths, options) => process.exit(await lint(paths, options)));
81
+ .action(async (paths, options) => exit(await lint(paths, options)));
62
82
 
63
83
  program
64
84
  .command("format")
@@ -75,7 +95,7 @@ program
75
95
  "print what command will be executed under the hood instead of executing",
76
96
  )
77
97
  .argument("[paths...]", "dir or file paths to format")
78
- .action(async (paths, options) => process.exit(await format(paths, options)));
98
+ .action(async (paths, options) => exit(await format(paths, options)));
79
99
 
80
100
  program
81
101
  .command("install")
package/src/utils.js CHANGED
@@ -58,7 +58,7 @@ function getSpentTime(startTime) {
58
58
  /**
59
59
  * @param {string[]} command
60
60
  * @param {{topic: string, dryRun: boolean, env: Record<string, string>}} options
61
- * @returns {Promise<number>}
61
+ * @returns {Promise<{code: number|null, signal: NodeJS.Signals | null}>}
62
62
  */
63
63
  export function execAsync(command, { topic, dryRun, env }) {
64
64
  return new Promise((resolve, reject) => {
@@ -70,11 +70,14 @@ export function execAsync(command, { topic, dryRun, env }) {
70
70
  }
71
71
  if (dryRun) {
72
72
  process.stdout.write(`${colors.green(cmd)} ${args.join(" ")};\n\n`);
73
- return resolve(0);
73
+ return resolve({ code: 0, signal: null });
74
74
  }
75
75
 
76
76
  const spinner = ora(`${topic}...`).start();
77
- const cp = childProcess.spawn(cmd, args, {
77
+ /**
78
+ * @type {childProcess.ChildProcessWithoutNullStreams | undefined}
79
+ */
80
+ let cp = childProcess.spawn(cmd, args, {
78
81
  env: { FORCE_COLOR: "true", ...process.env, ...env },
79
82
  });
80
83
  let stdout = Buffer.alloc(0);
@@ -86,18 +89,13 @@ export function execAsync(command, { topic, dryRun, env }) {
86
89
  stderr = Buffer.concat([stderr, data]);
87
90
  });
88
91
  cp.on("error", (err) => {
89
- spinner.fail(
90
- `${topic} got error in ${colors.yellow(getSpentTime(startTime))}`,
91
- );
92
- process.stderr.write(err.message);
93
- resolve(getExitCode(err));
92
+ reject(err);
94
93
  });
95
94
  // Why not listen to the 'exit' event?
96
95
  // 1. The 'close' event will always emit after 'exit' was already emitted, or 'error' if the child failed to spawn.
97
96
  // 2. The 'exit' event may or may not fire after an error has occurred.
98
97
  cp.on("close", (code, signal) => {
99
- const exitCode = getExitCode({ code, signal });
100
- if (exitCode === 0) {
98
+ if (code === 0) {
101
99
  spinner.succeed(
102
100
  `${topic} succeeded in ${colors.yellow(getSpentTime(startTime))}`,
103
101
  );
@@ -108,27 +106,17 @@ export function execAsync(command, { topic, dryRun, env }) {
108
106
  }
109
107
  process.stdout.write(stdout);
110
108
  process.stderr.write(stderr);
111
- resolve(exitCode);
109
+ cp = undefined; // When the cp exited, we should clean cp to prevent memory leak.
110
+ resolve({ code, signal });
112
111
  });
113
- process.on("SIGINT", () => !cp.killed && cp.kill("SIGINT"));
114
- process.on("SIGTERM", () => !cp.killed && cp.kill("SIGTERM"));
115
- });
116
- }
117
112
 
118
- /**
119
- * @param {object} error
120
- */
121
- function getExitCode(error) {
122
- if ("signal" in error && error.signal === "SIGINT") {
123
- return 2;
124
- }
125
- if ("signal" in error && error.signal === "SIGTERM") {
126
- return 15;
127
- }
128
- if ("code" in error && typeof error.code === "number") {
129
- return error.code;
130
- }
131
- return 1;
113
+ /**
114
+ * @param {NodeJS.Signals} signal
115
+ */
116
+ const listener = (signal) => cp && !cp.killed && cp.kill(signal);
117
+ process.on("SIGINT", listener);
118
+ process.on("SIGTERM", listener);
119
+ });
132
120
  }
133
121
 
134
122
  /**