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.
@@ -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
+ }