@tzwzx/codesweep 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tzwzx
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # codesweep
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@tzwzx/codesweep.svg)](https://www.npmjs.com/package/@tzwzx/codesweep)
4
+ [![license](https://img.shields.io/npm/l/@tzwzx/codesweep.svg)](./LICENSE)
5
+
6
+ Run lint, format, typecheck, test, and other code quality commands in one go. Configure each project with a YAML file.
7
+
8
+ ## Requirements
9
+
10
+ - Node.js >= 22 (or Bun >= 1)
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ # npm
16
+ npm install @tzwzx/codesweep
17
+
18
+ # bun
19
+ bun add @tzwzx/codesweep
20
+
21
+ # pnpm
22
+ pnpm add @tzwzx/codesweep
23
+ ```
24
+
25
+ After install, the `codesweep` binary is available via `npx @tzwzx/codesweep`, `bunx @tzwzx/codesweep`, or `./node_modules/.bin/codesweep`.
26
+
27
+ ## CLI usage
28
+
29
+ ```bash
30
+ codesweep <mode> [options]
31
+ ```
32
+
33
+ - **mode** โ€” Any mode defined in `codesweep.yml` (for example `check` or `fix`).
34
+ - **`--config` / `-c <path>`** โ€” Path to the config file (default: `./codesweep.yml`).
35
+ - **`--help` / `-h`** โ€” Show help.
36
+
37
+ Examples:
38
+
39
+ ```bash
40
+ codesweep check
41
+ codesweep fix
42
+ codesweep check --config ./packages/app/codesweep.yml
43
+ ```
44
+
45
+ Exit code `0` on success, `1` on failure. Elapsed time is printed when the run finishes.
46
+
47
+ ## Library usage
48
+
49
+ ```ts
50
+ import { codesweep } from "@tzwzx/codesweep";
51
+
52
+ await codesweep("check");
53
+ await codesweep("fix", "./custom.yml");
54
+ ```
55
+
56
+ You can also import helpers such as `loadConfig`, `runParallel`, and `runSequential` from the same package.
57
+
58
+ ## Config: `codesweep.yml`
59
+
60
+ Define one or more **modes**. Each mode is an ordered list of **stages**. Each stage is either `parallel` or `sequential`, and holds a non-empty array of shell command strings.
61
+
62
+ Example:
63
+
64
+ ```yaml
65
+ check:
66
+ - parallel:
67
+ - bun lint
68
+ - bun tsc
69
+ - bun test
70
+
71
+ fix:
72
+ - sequential:
73
+ - bun fix
74
+ - parallel:
75
+ - bun tsc
76
+ - bun test
77
+ ```
78
+
79
+ ### Behavior
80
+
81
+ - **Stages** run one after another (stage 2 starts after stage 1 finishes).
82
+ - **Within a stage**, `parallel` runs commands concurrently; `sequential` runs them in order.
83
+ - **Fail-through**: If one command in a stage fails, other commands in that stage still run; the stage then fails as a whole.
84
+
85
+ ### Validation rules
86
+
87
+ - The config must be a non-empty object.
88
+ - Each modeโ€™s value must be an array of stages.
89
+ - Each stage must have exactly one of `parallel` or `sequential` (not both).
90
+ - Command lists must be non-empty arrays of non-empty strings.
91
+
92
+ ## Development
93
+
94
+ ```bash
95
+ bun install
96
+ bun run build # outputs to dist/
97
+ bun run dev # tsc --watch
98
+ ```
99
+
100
+ ## License
101
+
102
+ MIT โ€” see [LICENSE](LICENSE).
package/dist/cli.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * codesweep CLI - Run code quality checks in one command
4
+ *
5
+ * Usage:
6
+ * codesweep <mode> [--config path]
7
+ *
8
+ * Examples:
9
+ * codesweep check # Run quality checks
10
+ * codesweep fix # Auto-fix then check
11
+ * codesweep check --config ./custom.yml # Use custom config file
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG"}
package/dist/cli.js ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * codesweep CLI - Run code quality checks in one command
4
+ *
5
+ * Usage:
6
+ * codesweep <mode> [--config path]
7
+ *
8
+ * Examples:
9
+ * codesweep check # Run quality checks
10
+ * codesweep fix # Auto-fix then check
11
+ * codesweep check --config ./custom.yml # Use custom config file
12
+ */
13
+ import process from "node:process";
14
+ import { parseArgs } from "node:util";
15
+ import { codesweep } from "./index.js";
16
+ const HELP_TEXT = `
17
+ codesweep - Run code quality checks in one command ๐Ÿงน
18
+
19
+ Usage:
20
+ codesweep <mode> [options]
21
+
22
+ Modes:
23
+ Any mode defined in your codesweep.yml (e.g. check, fix, ...)
24
+
25
+ Options:
26
+ --config, -c <path> Path to config file (default: ./codesweep.yml)
27
+ --help, -h Show help
28
+ `;
29
+ const main = async () => {
30
+ let values;
31
+ let positionals;
32
+ try {
33
+ ({ values, positionals } = parseArgs({
34
+ allowPositionals: true,
35
+ args: process.argv.slice(2),
36
+ options: {
37
+ config: { short: "c", type: "string" },
38
+ help: { short: "h", type: "boolean" },
39
+ },
40
+ }));
41
+ }
42
+ catch (error) {
43
+ console.error(error instanceof Error ? error.message : String(error));
44
+ process.exit(1);
45
+ }
46
+ if (values.help) {
47
+ console.log(HELP_TEXT);
48
+ process.exit(0);
49
+ }
50
+ const [mode] = positionals;
51
+ if (!mode) {
52
+ console.log(HELP_TEXT);
53
+ process.exit(1);
54
+ }
55
+ try {
56
+ await codesweep(mode, values.config);
57
+ process.exit(0);
58
+ }
59
+ catch (error) {
60
+ if (error instanceof Error) {
61
+ console.error(error.message);
62
+ }
63
+ process.exit(1);
64
+ }
65
+ };
66
+ // Guard against any unexpected rejection escaping main() so the process
67
+ // always exits with a non-zero code instead of an unhandled rejection.
68
+ try {
69
+ await main();
70
+ }
71
+ catch (error) {
72
+ console.error(error instanceof Error ? error.message : String(error));
73
+ process.exit(1);
74
+ }
75
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG;AAEH,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,SAAS,GAAG;;;;;;;;;;;;CAYjB,CAAC;AAEF,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;IACrC,IAAI,MAA2C,CAAC;IAChD,IAAI,WAAqB,CAAC;IAE1B,IAAI,CAAC;QACH,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;YACnC,gBAAgB,EAAE,IAAI;YACtB,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YAC3B,OAAO,EAAE;gBACP,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACtC,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE;aACtC;SACF,CAAC,CAAC,CAAC;IACN,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC;IAE3B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC;AAEF,wEAAwE;AACxE,uEAAuE;AACvE,IAAI,CAAC;IACH,MAAM,IAAI,EAAE,CAAC;AACf,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ParallelStage, SequentialStage, Stage, CodesweepConfig } from "./types.js";
2
+ export declare const isParallelStage: (stage: Stage) => stage is ParallelStage;
3
+ export declare const isSequentialStage: (stage: Stage) => stage is SequentialStage;
4
+ export declare const loadConfig: (configPath?: string) => CodesweepConfig;
5
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIzF,eAAO,MAAM,eAAe,GAAI,OAAO,KAAK,KAAG,KAAK,IAAI,aAAoC,CAAC;AAE7F,eAAO,MAAM,iBAAiB,GAAI,OAAO,KAAK,KAAG,KAAK,IAAI,eAAwC,CAAC;AA2DnG,eAAO,MAAM,UAAU,GAAI,aAAa,MAAM,KAAG,eAchD,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,64 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { parse } from "yaml";
4
+ const DEFAULT_CONFIG_FILENAME = "codesweep.yml";
5
+ export const isParallelStage = (stage) => "parallel" in stage;
6
+ export const isSequentialStage = (stage) => "sequential" in stage;
7
+ const validateStage = (stage, modeName, index) => {
8
+ if (typeof stage !== "object" || stage === null) {
9
+ throw new Error(`${modeName}[${index}]: Stage must be an object`);
10
+ }
11
+ const record = stage;
12
+ const hasParallel = "parallel" in record;
13
+ const hasSequential = "sequential" in record;
14
+ if (!hasParallel && !hasSequential) {
15
+ throw new Error(`${modeName}[${index}]: Stage must have a "parallel" or "sequential" key`);
16
+ }
17
+ if (hasParallel && hasSequential) {
18
+ throw new Error(`${modeName}[${index}]: Stage cannot have both "parallel" and "sequential"`);
19
+ }
20
+ const commands = hasParallel ? record.parallel : record.sequential;
21
+ if (!Array.isArray(commands) || commands.length === 0) {
22
+ throw new Error(`${modeName}[${index}]: Command list must be a non-empty array`);
23
+ }
24
+ for (const command of commands) {
25
+ if (typeof command !== "string" || command.trim() === "") {
26
+ throw new Error(`${modeName}[${index}]: Each command must be a non-empty string`);
27
+ }
28
+ }
29
+ };
30
+ const validateConfig = (config) => {
31
+ if (typeof config !== "object" || config === null || Array.isArray(config)) {
32
+ throw new Error("Config file must be an object");
33
+ }
34
+ const record = config;
35
+ const modes = Object.keys(record);
36
+ if (modes.length === 0) {
37
+ throw new Error("Config must define at least one mode");
38
+ }
39
+ for (const mode of modes) {
40
+ const stages = record[mode];
41
+ if (!Array.isArray(stages)) {
42
+ throw new TypeError(`"${mode}": Stage list must be an array`);
43
+ }
44
+ for (const [index, stage] of stages.entries()) {
45
+ validateStage(stage, mode, index);
46
+ }
47
+ }
48
+ return record;
49
+ };
50
+ export const loadConfig = (configPath) => {
51
+ const resolvedPath = configPath
52
+ ? path.resolve(configPath)
53
+ : path.resolve(process.cwd(), DEFAULT_CONFIG_FILENAME);
54
+ let content;
55
+ try {
56
+ content = readFileSync(resolvedPath, "utf-8");
57
+ }
58
+ catch {
59
+ throw new Error(`Config file not found: ${resolvedPath}`);
60
+ }
61
+ const parsed = parse(content);
62
+ return validateConfig(parsed);
63
+ };
64
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Mode } from "./types.js";
2
+ export type { CodesweepConfig, Stage, ParallelStage, SequentialStage, Mode } from "./types.js";
3
+ export { isParallelStage, isSequentialStage, loadConfig } from "./config.js";
4
+ export { runParallel, runSequential } from "./runner.js";
5
+ /**
6
+ * Load the config file, then run the pipeline for the given mode.
7
+ *
8
+ * Stages run one after another; within a stage, commands run in parallel or
9
+ * sequentially as configured. Resolves when every command succeeds.
10
+ *
11
+ * @param mode - Mode name to run (must be defined in the config file).
12
+ * @param configPath - Path to the config file (default: `./codesweep.yml`).
13
+ * @throws {Error} If the mode is undefined, or any command in the pipeline fails.
14
+ */
15
+ export declare const codesweep: (mode: Mode, configPath?: string) => Promise<void>;
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAS,IAAI,EAAE,MAAM,YAAY,CAAC;AAE9C,YAAY,EAAE,eAAe,EAAE,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAkBzD;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,GAAU,MAAM,IAAI,EAAE,aAAa,MAAM,KAAG,OAAO,CAAC,IAAI,CAsB7E,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,45 @@
1
+ import { performance } from "node:perf_hooks";
2
+ import { isParallelStage, loadConfig } from "./config.js";
3
+ import { runParallel, runSequential } from "./runner.js";
4
+ export { isParallelStage, isSequentialStage, loadConfig } from "./config.js";
5
+ export { runParallel, runSequential } from "./runner.js";
6
+ const MILLISECONDS_PER_SECOND = 1000;
7
+ const formatDuration = (startTime) => ((performance.now() - startTime) / MILLISECONDS_PER_SECOND).toFixed(2);
8
+ const runStage = (stage) => isParallelStage(stage) ? runParallel(stage.parallel) : runSequential(stage.sequential);
9
+ const runPipeline = async (stages) => {
10
+ for (const stage of stages) {
11
+ // Stages run serially by design ("serial between stages"); parallelizing would break the contract.
12
+ // oxlint-disable-next-line no-await-in-loop
13
+ await runStage(stage);
14
+ }
15
+ };
16
+ /**
17
+ * Load the config file, then run the pipeline for the given mode.
18
+ *
19
+ * Stages run one after another; within a stage, commands run in parallel or
20
+ * sequentially as configured. Resolves when every command succeeds.
21
+ *
22
+ * @param mode - Mode name to run (must be defined in the config file).
23
+ * @param configPath - Path to the config file (default: `./codesweep.yml`).
24
+ * @throws {Error} If the mode is undefined, or any command in the pipeline fails.
25
+ */
26
+ export const codesweep = async (mode, configPath) => {
27
+ const config = loadConfig(configPath);
28
+ const stages = config[mode];
29
+ if (!stages) {
30
+ const availableModes = Object.keys(config).join(", ");
31
+ throw new Error(`Mode "${mode}" is not defined in the config file (available: ${availableModes})`);
32
+ }
33
+ const startTime = performance.now();
34
+ try {
35
+ await runPipeline(stages);
36
+ const duration = formatDuration(startTime);
37
+ console.log(`\nโœ… codesweep ${mode} passed (${duration}s)\n`);
38
+ }
39
+ catch (error) {
40
+ const duration = formatDuration(startTime);
41
+ console.error(`\nโŒ codesweep ${mode} failed (${duration}s)\n`);
42
+ throw error;
43
+ }
44
+ };
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIzD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAErC,MAAM,cAAc,GAAG,CAAC,SAAiB,EAAU,EAAE,CACnD,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,uBAAuB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAEzE,MAAM,QAAQ,GAAG,CAAC,KAAY,EAAiB,EAAE,CAC/C,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAEzF,MAAM,WAAW,GAAG,KAAK,EAAE,MAAwB,EAAiB,EAAE;IACpE,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,mGAAmG;QACnG,4CAA4C;QAC5C,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAAE,IAAU,EAAE,UAAmB,EAAiB,EAAE;IAChF,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAE5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,IAAI,KAAK,CACb,SAAS,IAAI,mDAAmD,cAAc,GAAG,CAClF,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAEpC,IAAI,CAAC;QACH,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QAC1B,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,YAAY,QAAQ,MAAM,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;QAC3C,OAAO,CAAC,KAAK,CAAC,iBAAiB,IAAI,YAAY,QAAQ,MAAM,CAAC,CAAC;QAC/D,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Run commands sequentially (continues on failure).
3
+ * All commands run even if one fails; errors are collected and thrown at the end.
4
+ */
5
+ export declare const runSequential: (commands: readonly string[]) => Promise<void>;
6
+ /**
7
+ * Run commands in parallel (continues on failure).
8
+ * All commands run even if one fails; the error is thrown after all complete.
9
+ */
10
+ export declare const runParallel: (commands: readonly string[]) => Promise<void>;
11
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAqBA;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAU,UAAU,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,IAAI,CAqB7E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAU,UAAU,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,IAAI,CAe3E,CAAC"}
package/dist/runner.js ADDED
@@ -0,0 +1,61 @@
1
+ import { exec } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import concurrently from "concurrently";
4
+ const execPromise = promisify(exec);
5
+ const printCommandOutput = (stdout, stderr) => {
6
+ if (stdout) {
7
+ console.log(stdout);
8
+ }
9
+ if (stderr) {
10
+ console.error(stderr);
11
+ }
12
+ };
13
+ const runSingleCommand = async (command) => {
14
+ const { stdout, stderr } = await execPromise(command);
15
+ printCommandOutput(stdout, stderr);
16
+ };
17
+ /**
18
+ * Run commands sequentially (continues on failure).
19
+ * All commands run even if one fails; errors are collected and thrown at the end.
20
+ */
21
+ export const runSequential = async (commands) => {
22
+ const errors = [];
23
+ for (const command of commands) {
24
+ try {
25
+ // Commands run one-by-one by design (sequential stage); parallelizing would defeat the purpose.
26
+ // oxlint-disable-next-line no-await-in-loop
27
+ await runSingleCommand(command);
28
+ }
29
+ catch (error) {
30
+ const { stdout, stderr } = error;
31
+ const message = error instanceof Error ? error.message : String(error);
32
+ console.error(`โŒ Command failed: ${command}`);
33
+ printCommandOutput(stdout, stderr);
34
+ errors.push({ command, message });
35
+ }
36
+ }
37
+ if (errors.length > 0) {
38
+ const errorMessages = errors.map((e) => `${e.command}: ${e.message}`).join("\n");
39
+ throw new Error(`Sequential execution failed:\n${errorMessages}`);
40
+ }
41
+ };
42
+ /**
43
+ * Run commands in parallel (continues on failure).
44
+ * All commands run even if one fails; the error is thrown after all complete.
45
+ */
46
+ export const runParallel = async (commands) => {
47
+ const commandObjects = commands.map((command) => ({ command }));
48
+ const { result } = concurrently(commandObjects, {
49
+ group: true,
50
+ killOthersOn: [],
51
+ prefix: "none",
52
+ });
53
+ try {
54
+ await result;
55
+ }
56
+ catch (error) {
57
+ const message = error instanceof Error ? error.message : "Unknown error";
58
+ throw new Error(`Parallel execution failed: ${message}`, { cause: error });
59
+ }
60
+ };
61
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,YAAY,MAAM,cAAc,CAAC;AAExC,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAEpC,MAAM,kBAAkB,GAAG,CAAC,MAAe,EAAE,MAAe,EAAQ,EAAE;IACpE,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;IACD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,KAAK,EAAE,OAAe,EAAiB,EAAE;IAChE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IACtD,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACrC,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,QAA2B,EAAiB,EAAE;IAChF,MAAM,MAAM,GAA2C,EAAE,CAAC;IAE1D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,gGAAgG;YAChG,4CAA4C;YAC5C,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAA6C,CAAC;YACzE,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO,CAAC,KAAK,CAAC,qBAAqB,OAAO,EAAE,CAAC,CAAC;YAC9C,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjF,MAAM,IAAI,KAAK,CAAC,iCAAiC,aAAa,EAAE,CAAC,CAAC;IACpE,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,QAA2B,EAAiB,EAAE;IAC9E,MAAM,cAAc,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAEhE,MAAM,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,cAAc,EAAE;QAC9C,KAAK,EAAE,IAAI;QACX,YAAY,EAAE,EAAE;QAChB,MAAM,EAAE,MAAM;KACf,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,MAAM,IAAI,KAAK,CAAC,8BAA8B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /** Parallel execution stage */
2
+ export interface ParallelStage {
3
+ parallel: string[];
4
+ }
5
+ /** Sequential execution stage */
6
+ export interface SequentialStage {
7
+ sequential: string[];
8
+ }
9
+ /** Execution stage (parallel or sequential) */
10
+ export type Stage = ParallelStage | SequentialStage;
11
+ /** Mode name (any key defined in config) */
12
+ export type Mode = string;
13
+ /** Pipeline configuration (key = mode name, value = stages) */
14
+ export type CodesweepConfig = Record<string, Stage[]>;
15
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,iCAAiC;AACjC,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,+CAA+C;AAC/C,MAAM,MAAM,KAAK,GAAG,aAAa,GAAG,eAAe,CAAC;AAEpD,4CAA4C;AAC5C,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC;AAE1B,+DAA+D;AAC/D,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@tzwzx/codesweep",
3
+ "version": "1.0.0",
4
+ "description": "Run code quality checks in one command ๐Ÿงน",
5
+ "keywords": [
6
+ "cli",
7
+ "code-quality",
8
+ "formatter",
9
+ "linter",
10
+ "orchestrator",
11
+ "test-runner",
12
+ "typecheck"
13
+ ],
14
+ "homepage": "https://github.com/tzwzx/codesweep#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/tzwzx/codesweep/issues"
17
+ },
18
+ "license": "MIT",
19
+ "author": "tzwzx",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/tzwzx/codesweep.git"
23
+ },
24
+ "bin": {
25
+ "codesweep": "dist/cli.js"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "README.md",
30
+ "LICENSE"
31
+ ],
32
+ "type": "module",
33
+ "main": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "import": "./dist/index.js"
39
+ }
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc",
46
+ "dev": "tsc --watch",
47
+ "lint": "oxfmt --check . && oxlint .",
48
+ "fix": "oxfmt --write . && oxlint --fix .",
49
+ "tsc": "tsc --noEmit",
50
+ "knip": "knip",
51
+ "syncpack": "syncpack lint",
52
+ "test": "bun test",
53
+ "codesweep:check": "node dist/cli.js check",
54
+ "codesweep:fix": "node dist/cli.js fix",
55
+ "prepublishOnly": "bun run build"
56
+ },
57
+ "dependencies": {
58
+ "concurrently": "10.0.3",
59
+ "yaml": "2.9.0"
60
+ },
61
+ "devDependencies": {
62
+ "@types/node": "26.0.1",
63
+ "knip": "6.23.0",
64
+ "oxfmt": "0.57.0",
65
+ "oxlint": "1.72.0",
66
+ "syncpack": "15.3.2",
67
+ "typescript": "6.0.3",
68
+ "ultracite": "7.8.3"
69
+ },
70
+ "engines": {
71
+ "bun": ">=1",
72
+ "node": ">=22"
73
+ }
74
+ }