hudson 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/.hudson/hudson.yml +60 -0
- package/README.md +163 -0
- package/example/.hudson/hudson.yml +60 -0
- package/example/src/index.ts +1 -0
- package/example/tsconfig.json +21 -0
- package/package.json +32 -0
- package/src/commands/build.ts +187 -0
- package/src/commands/configure.ts +85 -0
- package/src/commands/dev.ts +114 -0
- package/src/commands/info.ts +78 -0
- package/src/commands/init.ts +80 -0
- package/src/commands/run.ts +114 -0
- package/src/commands/workspaces.ts +49 -0
- package/src/core/config.ts +214 -0
- package/src/core/runner.ts +108 -0
- package/src/core/workspace.ts +65 -0
- package/src/index.ts +138 -0
- package/src/types/gradient-string.d.ts +4 -0
- package/src/utils/display.ts +66 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#####################################################################
|
|
2
|
+
#
|
|
3
|
+
# ██╗ ██╗██╗ ██╗██████╗ ███████╗ ██████╗ ███╗ ██╗
|
|
4
|
+
# ██║ ██║██║ ██║██╔══██╗██╔════╝██╔═══██╗████╗ ██║
|
|
5
|
+
# ███████║██║ ██║██║ ██║███████╗██║ ██║██╔██╗ ██║
|
|
6
|
+
# ██╔══██║██║ ██║██║ ██║╚════██║██║ ██║██║╚██╗██║
|
|
7
|
+
# ██║ ██║╚██████╔╝██████╔╝███████║╚██████╔╝██║ ╚████║
|
|
8
|
+
# ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═══╝
|
|
9
|
+
#
|
|
10
|
+
# Hudson Build System — v1.0.0
|
|
11
|
+
# The blazing-fast TypeScript build system
|
|
12
|
+
#
|
|
13
|
+
# ─────────────────────────────────────────────────────
|
|
14
|
+
# Configure via CLI (without editing this file):
|
|
15
|
+
#
|
|
16
|
+
# hudson configure <key> <value>
|
|
17
|
+
#
|
|
18
|
+
# Examples:
|
|
19
|
+
# hudson configure build.outDir dist
|
|
20
|
+
# hudson configure output.verbose true
|
|
21
|
+
# hudson configure packageManager pnpm
|
|
22
|
+
#
|
|
23
|
+
# ─────────────────────────────────────────────────────
|
|
24
|
+
# Docs: https://github.com/hudson-build/hudson
|
|
25
|
+
#
|
|
26
|
+
#####################################################################
|
|
27
|
+
|
|
28
|
+
project:
|
|
29
|
+
name: hudson
|
|
30
|
+
version: 1.0.0
|
|
31
|
+
description: A Hudson-managed project
|
|
32
|
+
build:
|
|
33
|
+
outDir: dist
|
|
34
|
+
tsconfig: tsconfig.json
|
|
35
|
+
sourcemap: true
|
|
36
|
+
minify: false
|
|
37
|
+
target: ES2022
|
|
38
|
+
clean: true
|
|
39
|
+
workspaces:
|
|
40
|
+
enabled: false
|
|
41
|
+
packages:
|
|
42
|
+
- packages/*
|
|
43
|
+
parallel: true
|
|
44
|
+
maxConcurrency: 4
|
|
45
|
+
dev:
|
|
46
|
+
watch: true
|
|
47
|
+
open: false
|
|
48
|
+
scripts:
|
|
49
|
+
build: tsc
|
|
50
|
+
dev: tsc --watch
|
|
51
|
+
test: node --test
|
|
52
|
+
lint: eslint .
|
|
53
|
+
clean: rm -rf dist
|
|
54
|
+
hooks: {}
|
|
55
|
+
packageManager: pnpm
|
|
56
|
+
output:
|
|
57
|
+
verbose: false
|
|
58
|
+
timestamps: true
|
|
59
|
+
color: true
|
|
60
|
+
banner: true
|
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# 🎣 Hudson
|
|
2
|
+
|
|
3
|
+
**Hudson** is a blazing-fast, beginner-friendly TypeScript build system with first-class workspace support.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
██╗ ██╗██╗ ██╗██████╗ ███████╗ ██████╗ ███╗ ██╗
|
|
7
|
+
██║ ██║██║ ██║██╔══██╗██╔════╝██╔═══██╗████╗ ██║
|
|
8
|
+
███████║██║ ██║██║ ██║███████╗██║ ██║██╔██╗ ██║
|
|
9
|
+
██╔══██║██║ ██║██║ ██║╚════██║██║ ██║██║╚██╗██║
|
|
10
|
+
██║ ██║╚██████╔╝██████╔╝███████║╚██████╔╝██║ ╚████║
|
|
11
|
+
╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═══╝
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- ⚡ **Blazing fast** — parallel workspace builds, smart caching
|
|
17
|
+
- 🏗️ **Workspace support** — monorepo-ready with topological sort
|
|
18
|
+
- ⚙️ **Fully configurable** — YAML config with CLI configuration
|
|
19
|
+
- 👀 **Dev mode** — file watching with instant rebuilds
|
|
20
|
+
- 🎨 **Beautiful output** — modern, colorful, informative terminal output
|
|
21
|
+
- 📦 **PNPM/Yarn first** — built for modern package managers
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add -g hudson
|
|
27
|
+
# or
|
|
28
|
+
yarn global add hudson
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Initialize Hudson in your project
|
|
35
|
+
hudson init
|
|
36
|
+
|
|
37
|
+
# Build your project
|
|
38
|
+
hudson build
|
|
39
|
+
|
|
40
|
+
# Start dev mode
|
|
41
|
+
hudson dev
|
|
42
|
+
|
|
43
|
+
# Run a script
|
|
44
|
+
hudson run test
|
|
45
|
+
|
|
46
|
+
# Configure a setting
|
|
47
|
+
hudson configure build.outDir dist
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
Hudson uses `.hudson/hudson.yml` for configuration. You can edit it directly or use the CLI:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# List all config values
|
|
56
|
+
hudson configure --list
|
|
57
|
+
|
|
58
|
+
# Set a value
|
|
59
|
+
hudson configure build.outDir lib
|
|
60
|
+
hudson configure output.verbose true
|
|
61
|
+
hudson configure workspaces.enabled true
|
|
62
|
+
hudson configure packageManager pnpm
|
|
63
|
+
|
|
64
|
+
# Get a value
|
|
65
|
+
hudson configure --get build.outDir
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Config File
|
|
69
|
+
|
|
70
|
+
Located at `.hudson/hudson.yml`:
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
project:
|
|
74
|
+
name: my-project
|
|
75
|
+
version: 1.0.0
|
|
76
|
+
|
|
77
|
+
build:
|
|
78
|
+
outDir: dist
|
|
79
|
+
tsconfig: tsconfig.json
|
|
80
|
+
sourcemap: true
|
|
81
|
+
clean: true
|
|
82
|
+
|
|
83
|
+
workspaces:
|
|
84
|
+
enabled: false
|
|
85
|
+
packages:
|
|
86
|
+
- packages/*
|
|
87
|
+
parallel: true
|
|
88
|
+
maxConcurrency: 4
|
|
89
|
+
|
|
90
|
+
scripts:
|
|
91
|
+
build: tsc
|
|
92
|
+
dev: tsc --watch
|
|
93
|
+
test: node --test
|
|
94
|
+
lint: eslint .
|
|
95
|
+
clean: rm -rf dist
|
|
96
|
+
|
|
97
|
+
hooks:
|
|
98
|
+
preBuild: ""
|
|
99
|
+
postBuild: ""
|
|
100
|
+
|
|
101
|
+
packageManager: pnpm
|
|
102
|
+
|
|
103
|
+
output:
|
|
104
|
+
verbose: false
|
|
105
|
+
timestamps: true
|
|
106
|
+
color: true
|
|
107
|
+
banner: true
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Commands
|
|
111
|
+
|
|
112
|
+
| Command | Description |
|
|
113
|
+
|---------|-------------|
|
|
114
|
+
| `hudson init` | Initialize Hudson in the current directory |
|
|
115
|
+
| `hudson build` | Build the project |
|
|
116
|
+
| `hudson build --watch` | Build and watch for changes |
|
|
117
|
+
| `hudson build -w <name>` | Build a specific workspace |
|
|
118
|
+
| `hudson dev` | Start dev mode with file watching |
|
|
119
|
+
| `hudson run <script>` | Run a script from config |
|
|
120
|
+
| `hudson run <script> --all` | Run script in all workspaces |
|
|
121
|
+
| `hudson configure <key> <value>` | Set a config value |
|
|
122
|
+
| `hudson configure --list` | List all config values |
|
|
123
|
+
| `hudson info` | Show project info |
|
|
124
|
+
| `hudson ws list` | List all workspaces |
|
|
125
|
+
|
|
126
|
+
## Workspaces
|
|
127
|
+
|
|
128
|
+
Hudson supports monorepo workspaces with automatic dependency detection and topological sorting.
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
workspaces:
|
|
132
|
+
enabled: true
|
|
133
|
+
packages:
|
|
134
|
+
- packages/*
|
|
135
|
+
- apps/*
|
|
136
|
+
parallel: true
|
|
137
|
+
maxConcurrency: 4
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Build all workspaces in dependency order
|
|
142
|
+
hudson build
|
|
143
|
+
|
|
144
|
+
# Build a specific workspace
|
|
145
|
+
hudson build -w my-package
|
|
146
|
+
|
|
147
|
+
# Run a script in all workspaces
|
|
148
|
+
hudson run test --all
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Hooks
|
|
152
|
+
|
|
153
|
+
Run custom commands before or after builds:
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
hooks:
|
|
157
|
+
preBuild: pnpm lint
|
|
158
|
+
postBuild: pnpm test
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
MIT
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#####################################################################
|
|
2
|
+
#
|
|
3
|
+
# ██╗ ██╗██╗ ██╗██████╗ ███████╗ ██████╗ ███╗ ██╗
|
|
4
|
+
# ██║ ██║██║ ██║██╔══██╗██╔════╝██╔═══██╗████╗ ██║
|
|
5
|
+
# ███████║██║ ██║██║ ██║███████╗██║ ██║██╔██╗ ██║
|
|
6
|
+
# ██╔══██║██║ ██║██║ ██║╚════██║██║ ██║██║╚██╗██║
|
|
7
|
+
# ██║ ██║╚██████╔╝██████╔╝███████║╚██████╔╝██║ ╚████║
|
|
8
|
+
# ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═══╝
|
|
9
|
+
#
|
|
10
|
+
# Hudson Build System — v1.0.0
|
|
11
|
+
# The blazing-fast TypeScript build system
|
|
12
|
+
#
|
|
13
|
+
# ─────────────────────────────────────────────────────
|
|
14
|
+
# Configure via CLI (without editing this file):
|
|
15
|
+
#
|
|
16
|
+
# hudson configure <key> <value>
|
|
17
|
+
#
|
|
18
|
+
# Examples:
|
|
19
|
+
# hudson configure build.outDir dist
|
|
20
|
+
# hudson configure output.verbose true
|
|
21
|
+
# hudson configure packageManager pnpm
|
|
22
|
+
#
|
|
23
|
+
# ─────────────────────────────────────────────────────
|
|
24
|
+
# Docs: https://github.com/hudson-build/hudson
|
|
25
|
+
#
|
|
26
|
+
#####################################################################
|
|
27
|
+
|
|
28
|
+
project:
|
|
29
|
+
name: example
|
|
30
|
+
version: 1.0.0
|
|
31
|
+
description: A Hudson-managed project
|
|
32
|
+
build:
|
|
33
|
+
outDir: dist
|
|
34
|
+
tsconfig: tsconfig.json
|
|
35
|
+
sourcemap: true
|
|
36
|
+
minify: false
|
|
37
|
+
target: ES2022
|
|
38
|
+
clean: true
|
|
39
|
+
workspaces:
|
|
40
|
+
enabled: false
|
|
41
|
+
packages:
|
|
42
|
+
"0": packages/*
|
|
43
|
+
parallel: true
|
|
44
|
+
maxConcurrency: 4
|
|
45
|
+
dev:
|
|
46
|
+
watch: true
|
|
47
|
+
open: false
|
|
48
|
+
scripts:
|
|
49
|
+
build: tsc
|
|
50
|
+
dev: tsc --watch
|
|
51
|
+
test: node --test
|
|
52
|
+
lint: eslint .
|
|
53
|
+
clean: rm -rf dist
|
|
54
|
+
hooks: {}
|
|
55
|
+
packageManager: pnpm
|
|
56
|
+
output:
|
|
57
|
+
verbose: false
|
|
58
|
+
timestamps: true
|
|
59
|
+
color: true
|
|
60
|
+
banner: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.log("hey!")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"allowImportingTsExtensions": false,
|
|
16
|
+
"noUnusedLocals": false,
|
|
17
|
+
"noUnusedParameters": false
|
|
18
|
+
},
|
|
19
|
+
"include": ["src/**/*"],
|
|
20
|
+
"exclude": ["node_modules", "dist"]
|
|
21
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hudson",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A blazing fast TypeScript build system",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"hudson": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsc --watch",
|
|
12
|
+
"start": "node dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"chalk": "^5.3.0",
|
|
16
|
+
"chokidar": "^3.5.3",
|
|
17
|
+
"commander": "^11.1.0",
|
|
18
|
+
"execa": "^8.0.1",
|
|
19
|
+
"fast-glob": "^3.3.2",
|
|
20
|
+
"js-yaml": "^4.1.0",
|
|
21
|
+
"ora": "^7.0.1",
|
|
22
|
+
"boxen": "^7.1.1",
|
|
23
|
+
"figures": "^6.0.1",
|
|
24
|
+
"gradient-string": "^2.0.2",
|
|
25
|
+
"ansi-escapes": "^6.2.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/js-yaml": "^4.0.9",
|
|
29
|
+
"@types/node": "^20.10.0",
|
|
30
|
+
"typescript": "^5.3.2"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { loadConfig, HudsonConfig } from "../core/config.js";
|
|
6
|
+
import { runCommand, runHook } from "../core/runner.js";
|
|
7
|
+
import { discoverWorkspaces, topologicalSort, Workspace } from "../core/workspace.js";
|
|
8
|
+
import { log, formatDuration, symbols, BANNER } from "../utils/display.js";
|
|
9
|
+
|
|
10
|
+
interface BuildOptions {
|
|
11
|
+
workspace?: string;
|
|
12
|
+
verbose?: boolean;
|
|
13
|
+
clean?: boolean;
|
|
14
|
+
watch?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function buildCommand(options: BuildOptions = {}): Promise<void> {
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
const config = loadConfig(cwd);
|
|
20
|
+
const verbose = options.verbose ?? config.output.verbose;
|
|
21
|
+
|
|
22
|
+
if (config.output.banner) {
|
|
23
|
+
console.log(BANNER());
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
log.section("Build");
|
|
27
|
+
|
|
28
|
+
const start = Date.now();
|
|
29
|
+
|
|
30
|
+
// Pre-build hook
|
|
31
|
+
if (config.hooks.preBuild) {
|
|
32
|
+
const ok = await runHook("preBuild", config, cwd);
|
|
33
|
+
if (!ok) {
|
|
34
|
+
log.error("Pre-build hook failed");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Clean output directory
|
|
40
|
+
const shouldClean = options.clean ?? config.build.clean;
|
|
41
|
+
if (shouldClean) {
|
|
42
|
+
const outDir = path.join(cwd, config.build.outDir);
|
|
43
|
+
if (fs.existsSync(outDir)) {
|
|
44
|
+
const spinner = ora({ text: chalk.dim("Cleaning output directory..."), prefixText: " " }).start();
|
|
45
|
+
fs.rmSync(outDir, { recursive: true, force: true });
|
|
46
|
+
spinner.succeed(chalk.dim("Cleaned " + chalk.cyan(config.build.outDir)));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (config.workspaces.enabled && !options.workspace) {
|
|
51
|
+
await buildWorkspaces(config, cwd, verbose);
|
|
52
|
+
} else if (options.workspace) {
|
|
53
|
+
const workspaces = await discoverWorkspaces(config, cwd);
|
|
54
|
+
const ws = workspaces.find((w) => w.name === options.workspace);
|
|
55
|
+
if (!ws) {
|
|
56
|
+
log.error(`Workspace '${options.workspace}' not found`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
await buildSingleWorkspace(ws, config, verbose);
|
|
60
|
+
} else {
|
|
61
|
+
await buildProject(config, cwd, verbose, options.watch);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Post-build hook
|
|
65
|
+
if (config.hooks.postBuild) {
|
|
66
|
+
await runHook("postBuild", config, cwd);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const duration = Date.now() - start;
|
|
70
|
+
log.blank();
|
|
71
|
+
log.success(
|
|
72
|
+
`Build complete ${chalk.dim("in")} ${chalk.cyan(formatDuration(duration))}`
|
|
73
|
+
);
|
|
74
|
+
log.blank();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function buildProject(
|
|
78
|
+
config: HudsonConfig,
|
|
79
|
+
cwd: string,
|
|
80
|
+
verbose: boolean,
|
|
81
|
+
watch = false
|
|
82
|
+
): Promise<void> {
|
|
83
|
+
const tsconfig = path.join(cwd, config.build.tsconfig);
|
|
84
|
+
|
|
85
|
+
if (!fs.existsSync(tsconfig)) {
|
|
86
|
+
log.error(`tsconfig not found: ${config.build.tsconfig}`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const args = ["-p", config.build.tsconfig];
|
|
91
|
+
if (watch) args.push("--watch");
|
|
92
|
+
if (config.build.sourcemap) args.push("--sourceMap");
|
|
93
|
+
|
|
94
|
+
const spinner = ora({
|
|
95
|
+
text: watch ? chalk.dim("Watching for changes...") : chalk.dim("Compiling TypeScript..."),
|
|
96
|
+
prefixText: " ",
|
|
97
|
+
}).start();
|
|
98
|
+
|
|
99
|
+
const result = await runCommand("tsc", args, { cwd, verbose });
|
|
100
|
+
|
|
101
|
+
if (result.exitCode === 0) {
|
|
102
|
+
spinner.succeed(chalk.white("TypeScript compiled") + chalk.dim(` ${formatDuration(result.duration)}`));
|
|
103
|
+
if (verbose && result.stdout) {
|
|
104
|
+
result.stdout.split("\n").filter(Boolean).forEach((l) => log.dim(l));
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
spinner.fail(chalk.red("TypeScript compilation failed"));
|
|
108
|
+
if (result.stderr) {
|
|
109
|
+
log.blank();
|
|
110
|
+
result.stderr.split("\n").filter(Boolean).forEach((l) => {
|
|
111
|
+
console.log(` ${chalk.red(l)}`);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function buildWorkspaces(
|
|
119
|
+
config: HudsonConfig,
|
|
120
|
+
cwd: string,
|
|
121
|
+
verbose: boolean
|
|
122
|
+
): Promise<void> {
|
|
123
|
+
const workspaces = await discoverWorkspaces(config, cwd);
|
|
124
|
+
|
|
125
|
+
if (workspaces.length === 0) {
|
|
126
|
+
log.warn("No workspaces found. Check your workspaces.packages config.");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const sorted = topologicalSort(workspaces);
|
|
131
|
+
log.step(`Found ${chalk.cyan(sorted.length)} workspace${sorted.length !== 1 ? "s" : ""}`);
|
|
132
|
+
log.blank();
|
|
133
|
+
|
|
134
|
+
if (config.workspaces.parallel) {
|
|
135
|
+
const chunks: Workspace[][] = [];
|
|
136
|
+
const batchSize = config.workspaces.maxConcurrency;
|
|
137
|
+
|
|
138
|
+
for (let i = 0; i < sorted.length; i += batchSize) {
|
|
139
|
+
chunks.push(sorted.slice(i, i + batchSize));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for (const chunk of chunks) {
|
|
143
|
+
await Promise.all(chunk.map((ws) => buildSingleWorkspace(ws, config, verbose)));
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
for (const ws of sorted) {
|
|
147
|
+
await buildSingleWorkspace(ws, config, verbose);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function buildSingleWorkspace(
|
|
153
|
+
ws: Workspace,
|
|
154
|
+
config: HudsonConfig,
|
|
155
|
+
verbose: boolean
|
|
156
|
+
): Promise<void> {
|
|
157
|
+
const buildScript = ws.scripts["build"];
|
|
158
|
+
|
|
159
|
+
if (!buildScript) {
|
|
160
|
+
log.warn(` ${chalk.cyan(ws.name)} has no build script, skipping`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const spinner = ora({
|
|
165
|
+
text: chalk.dim(`Building ${chalk.cyan(ws.name)}...`),
|
|
166
|
+
prefixText: " ",
|
|
167
|
+
}).start();
|
|
168
|
+
|
|
169
|
+
const parts = buildScript.split(" ");
|
|
170
|
+
const result = await runCommand(parts[0], parts.slice(1), {
|
|
171
|
+
cwd: ws.path,
|
|
172
|
+
verbose,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (result.exitCode === 0) {
|
|
176
|
+
spinner.succeed(
|
|
177
|
+
`${chalk.cyan(ws.name)} ${chalk.dim("built")} ${chalk.dim(formatDuration(result.duration))}`
|
|
178
|
+
);
|
|
179
|
+
} else {
|
|
180
|
+
spinner.fail(`${chalk.red(ws.name)} ${chalk.red("failed")}`);
|
|
181
|
+
if (result.stderr) {
|
|
182
|
+
result.stderr.split("\n").filter(Boolean).forEach((l) => {
|
|
183
|
+
console.log(` ${chalk.dim(l)}`);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { loadConfig, setConfigValue, getConfigValue, saveConfig } from "../core/config.js";
|
|
3
|
+
import { log } from "../utils/display.js";
|
|
4
|
+
|
|
5
|
+
export async function configureCommand(
|
|
6
|
+
key?: string,
|
|
7
|
+
value?: string,
|
|
8
|
+
options: { list?: boolean; reset?: boolean; get?: string } = {}
|
|
9
|
+
): Promise<void> {
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
|
|
12
|
+
if (options.list) {
|
|
13
|
+
await listConfig(cwd);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (options.reset) {
|
|
18
|
+
const { DEFAULT_CONFIG } = await import("../core/config.js");
|
|
19
|
+
saveConfig(DEFAULT_CONFIG, cwd);
|
|
20
|
+
log.success("Config reset to defaults");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (options.get) {
|
|
25
|
+
const val = getConfigValue(options.get, cwd);
|
|
26
|
+
if (val === undefined) {
|
|
27
|
+
log.error(`Config key '${options.get}' not found`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
console.log(` ${chalk.cyan(options.get)} ${chalk.dim("=")} ${chalk.white(String(val))}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!key || value === undefined) {
|
|
35
|
+
log.error("Usage: hudson configure <key> <value>");
|
|
36
|
+
log.dim("Example: hudson configure build.outDir dist");
|
|
37
|
+
log.dim(" hudson configure output.verbose true");
|
|
38
|
+
log.dim(" hudson configure packageManager pnpm");
|
|
39
|
+
log.blank();
|
|
40
|
+
log.step(`List all keys: ${chalk.cyan("hudson configure --list")}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setConfigValue(key, value, cwd);
|
|
45
|
+
log.success(`Set ${chalk.cyan(key)} ${chalk.dim("→")} ${chalk.white(value)}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function listConfig(cwd: string): Promise<void> {
|
|
49
|
+
const config = loadConfig(cwd);
|
|
50
|
+
|
|
51
|
+
log.section("Configuration");
|
|
52
|
+
printObject(config, "", 0);
|
|
53
|
+
log.blank();
|
|
54
|
+
log.dim(`Config file: ${chalk.cyan(".hudson/hudson.yml")}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function printObject(obj: unknown, prefix: string, depth: number): void {
|
|
58
|
+
if (typeof obj !== "object" || obj === null) return;
|
|
59
|
+
|
|
60
|
+
const indent = " ".repeat(depth + 1);
|
|
61
|
+
|
|
62
|
+
for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
|
|
63
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
64
|
+
|
|
65
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
66
|
+
console.log(`${indent}${chalk.dim(key + ":")}`);
|
|
67
|
+
printObject(value, fullKey, depth + 1);
|
|
68
|
+
} else if (Array.isArray(value)) {
|
|
69
|
+
console.log(
|
|
70
|
+
`${indent}${chalk.hex("#4FC3F7")(fullKey)} ${chalk.dim("=")} ${chalk.yellow(JSON.stringify(value))}`
|
|
71
|
+
);
|
|
72
|
+
} else {
|
|
73
|
+
const displayValue =
|
|
74
|
+
typeof value === "boolean"
|
|
75
|
+
? value
|
|
76
|
+
? chalk.green(String(value))
|
|
77
|
+
: chalk.red(String(value))
|
|
78
|
+
: chalk.white(String(value));
|
|
79
|
+
|
|
80
|
+
console.log(
|
|
81
|
+
`${indent}${chalk.hex("#4FC3F7")(fullKey)} ${chalk.dim("=")} ${displayValue}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|