ts-builds 1.1.0 ā 1.2.1
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.js +296 -16
- package/package.json +18 -10
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,72 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
2
3
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { createInterface } from "node:readline";
|
|
5
6
|
|
|
6
7
|
//#region src/cli.ts
|
|
7
8
|
const targetDir = process.cwd();
|
|
9
|
+
const defaultChains = { validate: [
|
|
10
|
+
"format",
|
|
11
|
+
"lint",
|
|
12
|
+
"typecheck",
|
|
13
|
+
"test",
|
|
14
|
+
"build"
|
|
15
|
+
] };
|
|
16
|
+
function loadConfig() {
|
|
17
|
+
const configPath = join(targetDir, "ts-builds.config.json");
|
|
18
|
+
let userConfig = {};
|
|
19
|
+
if (existsSync(configPath)) try {
|
|
20
|
+
userConfig = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
21
|
+
} catch {
|
|
22
|
+
console.error("Warning: Failed to parse ts-builds.config.json, using defaults");
|
|
23
|
+
}
|
|
24
|
+
const commands = {};
|
|
25
|
+
if (userConfig.commands) for (const [name, cmd] of Object.entries(userConfig.commands)) commands[name] = typeof cmd === "string" ? { run: cmd } : cmd;
|
|
26
|
+
const chains = { ...defaultChains };
|
|
27
|
+
if (userConfig.validateChain) chains.validate = userConfig.validateChain;
|
|
28
|
+
if (userConfig.chains) Object.assign(chains, userConfig.chains);
|
|
29
|
+
return {
|
|
30
|
+
srcDir: userConfig.srcDir ?? "./src",
|
|
31
|
+
testDir: userConfig.testDir ?? "./test",
|
|
32
|
+
commands,
|
|
33
|
+
chains
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function runCommand(command$1, args, options = {}) {
|
|
37
|
+
const cwd = options.cwd ? join(targetDir, options.cwd) : targetDir;
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
const child = spawn(command$1, args, {
|
|
40
|
+
cwd,
|
|
41
|
+
stdio: "inherit",
|
|
42
|
+
shell: true
|
|
43
|
+
});
|
|
44
|
+
child.on("close", (code) => {
|
|
45
|
+
resolve(code ?? 1);
|
|
46
|
+
});
|
|
47
|
+
child.on("error", (err) => {
|
|
48
|
+
console.error(`Failed to run ${command$1}: ${err.message}`);
|
|
49
|
+
resolve(1);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function runShellCommand(shellCmd, options = {}) {
|
|
54
|
+
const cwd = options.cwd ? join(targetDir, options.cwd) : targetDir;
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
const child = spawn(shellCmd, {
|
|
57
|
+
cwd,
|
|
58
|
+
stdio: "inherit",
|
|
59
|
+
shell: true
|
|
60
|
+
});
|
|
61
|
+
child.on("close", (code) => {
|
|
62
|
+
resolve(code ?? 1);
|
|
63
|
+
});
|
|
64
|
+
child.on("error", (err) => {
|
|
65
|
+
console.error(`Failed to run: ${err.message}`);
|
|
66
|
+
resolve(1);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
|
8
70
|
const bundledPackages = [
|
|
9
71
|
"@eslint/eslintrc",
|
|
10
72
|
"@eslint/js",
|
|
@@ -50,31 +112,72 @@ function ensureNpmrcHoistPatterns() {
|
|
|
50
112
|
}
|
|
51
113
|
function showHelp() {
|
|
52
114
|
console.log(`
|
|
53
|
-
ts-builds - Shared TypeScript
|
|
115
|
+
ts-builds - Shared TypeScript build tooling
|
|
54
116
|
|
|
55
117
|
USAGE:
|
|
56
118
|
npx ts-builds [command]
|
|
57
119
|
|
|
58
|
-
COMMANDS:
|
|
59
|
-
init Initialize project with
|
|
120
|
+
SETUP COMMANDS:
|
|
121
|
+
init Initialize project with .npmrc hoist patterns (default)
|
|
122
|
+
config Create ts-builds.config.json (use --force to overwrite)
|
|
60
123
|
info Show bundled packages you don't need to install
|
|
61
124
|
cleanup Remove redundant dependencies from package.json
|
|
62
125
|
help Show this help message
|
|
63
126
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
127
|
+
SCRIPT COMMANDS:
|
|
128
|
+
validate Run full validation chain (configurable)
|
|
129
|
+
format Format code with Prettier (--write)
|
|
130
|
+
format:check Check formatting without writing
|
|
131
|
+
lint Lint and fix with ESLint
|
|
132
|
+
lint:check Check lint without fixing
|
|
133
|
+
typecheck Run TypeScript type checking (tsc --noEmit)
|
|
134
|
+
test Run tests once (vitest run)
|
|
135
|
+
test:watch Run tests in watch mode
|
|
136
|
+
test:coverage Run tests with coverage
|
|
137
|
+
test:ui Launch Vitest UI
|
|
138
|
+
build Production build (rimraf dist && tsdown)
|
|
139
|
+
build:watch Watch mode build
|
|
140
|
+
dev Alias for build:watch
|
|
68
141
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
tooling as dependencies. You only need to install:
|
|
142
|
+
CONFIGURATION:
|
|
143
|
+
Create ts-builds.config.json in your project root:
|
|
72
144
|
|
|
73
|
-
|
|
74
|
-
|
|
145
|
+
Basic:
|
|
146
|
+
{
|
|
147
|
+
"srcDir": "./src",
|
|
148
|
+
"validateChain": ["format", "lint", "typecheck", "test", "build"]
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
Advanced (monorepo with custom commands):
|
|
152
|
+
{
|
|
153
|
+
"srcDir": "./src",
|
|
154
|
+
"commands": {
|
|
155
|
+
"docs:validate": "pnpm docs:build && pnpm docs:check",
|
|
156
|
+
"landing:validate": { "run": "pnpm validate", "cwd": "./landing" }
|
|
157
|
+
},
|
|
158
|
+
"chains": {
|
|
159
|
+
"validate": ["validate:core", "validate:landing"],
|
|
160
|
+
"validate:core": ["format", "lint", "compile", "test", "docs:validate", "build"],
|
|
161
|
+
"validate:landing": ["landing:validate"]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
75
164
|
|
|
76
|
-
|
|
77
|
-
|
|
165
|
+
USAGE IN PACKAGE.JSON:
|
|
166
|
+
{
|
|
167
|
+
"scripts": {
|
|
168
|
+
"validate": "ts-builds validate",
|
|
169
|
+
"validate:core": "ts-builds validate:core",
|
|
170
|
+
"format": "ts-builds format",
|
|
171
|
+
"lint": "ts-builds lint",
|
|
172
|
+
"test": "ts-builds test",
|
|
173
|
+
"build": "ts-builds build"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
EXAMPLES:
|
|
178
|
+
npx ts-builds validate # Run default validation chain
|
|
179
|
+
npx ts-builds validate:core # Run named chain
|
|
180
|
+
npx ts-builds lint # Run single command
|
|
78
181
|
`);
|
|
79
182
|
}
|
|
80
183
|
function showInfo() {
|
|
@@ -151,10 +254,133 @@ function init() {
|
|
|
151
254
|
ensureNpmrcHoistPatterns();
|
|
152
255
|
console.log("\nDone! Your project is configured to hoist CLI binaries from peer dependencies.");
|
|
153
256
|
console.log("\nNext steps:");
|
|
257
|
+
console.log(" - Run 'npx ts-builds config' to create a config file");
|
|
154
258
|
console.log(" - Run 'npx ts-builds info' to see bundled packages");
|
|
155
259
|
console.log(" - Run 'npx ts-builds cleanup' to remove redundant deps");
|
|
156
260
|
}
|
|
157
|
-
|
|
261
|
+
function createConfig(force = false) {
|
|
262
|
+
const configPath = join(targetDir, "ts-builds.config.json");
|
|
263
|
+
if (existsSync(configPath) && !force) {
|
|
264
|
+
console.log("ts-builds.config.json already exists.");
|
|
265
|
+
console.log("Use 'ts-builds config --force' to overwrite.");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
writeFileSync(configPath, JSON.stringify({
|
|
269
|
+
srcDir: "./src",
|
|
270
|
+
validateChain: [
|
|
271
|
+
"format",
|
|
272
|
+
"lint",
|
|
273
|
+
"typecheck",
|
|
274
|
+
"test",
|
|
275
|
+
"build"
|
|
276
|
+
]
|
|
277
|
+
}, null, 2) + "\n");
|
|
278
|
+
console.log("ā Created ts-builds.config.json");
|
|
279
|
+
console.log(`
|
|
280
|
+
Configuration options:
|
|
281
|
+
srcDir Source directory for linting (default: "./src")
|
|
282
|
+
testDir Test directory (default: "./test")
|
|
283
|
+
validateChain Commands to run for validate (default shown above)
|
|
284
|
+
commands Custom commands: { "name": "shell command" }
|
|
285
|
+
chains Named chains: { "validate:fast": ["format", "lint"] }
|
|
286
|
+
|
|
287
|
+
Example with custom commands:
|
|
288
|
+
{
|
|
289
|
+
"srcDir": "./src",
|
|
290
|
+
"commands": {
|
|
291
|
+
"docs": "pnpm docs:build",
|
|
292
|
+
"subproject": { "run": "pnpm validate", "cwd": "./packages/sub" }
|
|
293
|
+
},
|
|
294
|
+
"chains": {
|
|
295
|
+
"validate": ["format", "lint", "test", "build"],
|
|
296
|
+
"validate:full": ["format", "lint", "typecheck", "test", "docs", "build"]
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
`);
|
|
300
|
+
}
|
|
301
|
+
async function runFormat(check = false) {
|
|
302
|
+
return runCommand("prettier", check ? ["--check", "."] : ["--write", "."]);
|
|
303
|
+
}
|
|
304
|
+
async function runLint(check = false) {
|
|
305
|
+
const config = loadConfig();
|
|
306
|
+
return runCommand("eslint", check ? [config.srcDir] : ["--fix", config.srcDir]);
|
|
307
|
+
}
|
|
308
|
+
async function runTypecheck() {
|
|
309
|
+
return runCommand("tsc", ["--noEmit"]);
|
|
310
|
+
}
|
|
311
|
+
async function runTest(mode = "run") {
|
|
312
|
+
switch (mode) {
|
|
313
|
+
case "watch": return runCommand("vitest", []);
|
|
314
|
+
case "coverage": return runCommand("vitest", ["run", "--coverage"]);
|
|
315
|
+
case "ui": return runCommand("vitest", ["--ui"]);
|
|
316
|
+
default: return runCommand("vitest", ["run"]);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
async function runBuild(watch = false) {
|
|
320
|
+
if (watch) return runCommand("tsdown", ["--watch"]);
|
|
321
|
+
const cleanCode = await runCommand("rimraf", ["dist"]);
|
|
322
|
+
if (cleanCode !== 0) return cleanCode;
|
|
323
|
+
return runCommand("cross-env", ["NODE_ENV=production", "tsdown"]);
|
|
324
|
+
}
|
|
325
|
+
function getBuiltinCommands(config) {
|
|
326
|
+
return {
|
|
327
|
+
format: { run: "prettier --write ." },
|
|
328
|
+
"format:check": { run: "prettier --check ." },
|
|
329
|
+
lint: { run: `eslint --fix ${config.srcDir}` },
|
|
330
|
+
"lint:check": { run: `eslint ${config.srcDir}` },
|
|
331
|
+
typecheck: { run: "tsc --noEmit" },
|
|
332
|
+
"ts-types": { run: "tsc --noEmit" },
|
|
333
|
+
test: { run: "vitest run" },
|
|
334
|
+
"test:watch": { run: "vitest" },
|
|
335
|
+
"test:coverage": { run: "vitest run --coverage" },
|
|
336
|
+
"test:ui": { run: "vitest --ui" },
|
|
337
|
+
build: { run: "rimraf dist && cross-env NODE_ENV=production tsdown" },
|
|
338
|
+
"build:watch": { run: "tsdown --watch" },
|
|
339
|
+
dev: { run: "tsdown --watch" },
|
|
340
|
+
compile: { run: "tsc" }
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
async function runChain(chainName, config, visited = /* @__PURE__ */ new Set()) {
|
|
344
|
+
if (visited.has(chainName)) {
|
|
345
|
+
console.error(`Circular chain reference detected: ${chainName}`);
|
|
346
|
+
return 1;
|
|
347
|
+
}
|
|
348
|
+
visited.add(chainName);
|
|
349
|
+
const chain = config.chains[chainName];
|
|
350
|
+
if (!chain) {
|
|
351
|
+
console.error(`Unknown chain: ${chainName}`);
|
|
352
|
+
return 1;
|
|
353
|
+
}
|
|
354
|
+
const builtins = getBuiltinCommands(config);
|
|
355
|
+
console.log(`\nš Running chain: ${chainName} [${chain.join(" ā ")}]`);
|
|
356
|
+
for (const step of chain) {
|
|
357
|
+
if (config.chains[step]) {
|
|
358
|
+
const code$1 = await runChain(step, config, visited);
|
|
359
|
+
if (code$1 !== 0) return code$1;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const cmdDef = config.commands[step] ?? builtins[step];
|
|
363
|
+
if (!cmdDef) {
|
|
364
|
+
console.error(`Unknown command or chain: ${step}`);
|
|
365
|
+
return 1;
|
|
366
|
+
}
|
|
367
|
+
const cwdLabel = cmdDef.cwd ? ` (in ${cmdDef.cwd})` : "";
|
|
368
|
+
console.log(`\nā¶ Running ${step}...${cwdLabel}`);
|
|
369
|
+
const code = await runShellCommand(cmdDef.run, { cwd: cmdDef.cwd });
|
|
370
|
+
if (code !== 0) {
|
|
371
|
+
console.error(`\nā ${step} failed with exit code ${code}`);
|
|
372
|
+
return code;
|
|
373
|
+
}
|
|
374
|
+
console.log(`ā ${step} complete`);
|
|
375
|
+
}
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
async function runValidate(chainName = "validate") {
|
|
379
|
+
return runChain(chainName, loadConfig());
|
|
380
|
+
}
|
|
381
|
+
const command = process.argv[2] || "init";
|
|
382
|
+
const subCommand = process.argv[3];
|
|
383
|
+
switch (command) {
|
|
158
384
|
case "help":
|
|
159
385
|
case "--help":
|
|
160
386
|
case "-h":
|
|
@@ -168,10 +394,64 @@ switch (process.argv[2] || "init") {
|
|
|
168
394
|
case "--cleanup":
|
|
169
395
|
await cleanup();
|
|
170
396
|
break;
|
|
397
|
+
case "format":
|
|
398
|
+
process.exit(await runFormat(subCommand === "check"));
|
|
399
|
+
break;
|
|
400
|
+
case "format:check":
|
|
401
|
+
process.exit(await runFormat(true));
|
|
402
|
+
break;
|
|
403
|
+
case "lint":
|
|
404
|
+
process.exit(await runLint(subCommand === "check"));
|
|
405
|
+
break;
|
|
406
|
+
case "lint:check":
|
|
407
|
+
process.exit(await runLint(true));
|
|
408
|
+
break;
|
|
409
|
+
case "typecheck":
|
|
410
|
+
case "ts-types":
|
|
411
|
+
process.exit(await runTypecheck());
|
|
412
|
+
break;
|
|
413
|
+
case "test":
|
|
414
|
+
process.exit(await runTest(subCommand));
|
|
415
|
+
break;
|
|
416
|
+
case "test:watch":
|
|
417
|
+
process.exit(await runTest("watch"));
|
|
418
|
+
break;
|
|
419
|
+
case "test:coverage":
|
|
420
|
+
process.exit(await runTest("coverage"));
|
|
421
|
+
break;
|
|
422
|
+
case "test:ui":
|
|
423
|
+
process.exit(await runTest("ui"));
|
|
424
|
+
break;
|
|
425
|
+
case "build":
|
|
426
|
+
process.exit(await runBuild(subCommand === "watch"));
|
|
427
|
+
break;
|
|
428
|
+
case "build:watch":
|
|
429
|
+
case "dev":
|
|
430
|
+
process.exit(await runBuild(true));
|
|
431
|
+
break;
|
|
432
|
+
case "validate":
|
|
433
|
+
process.exit(await runValidate());
|
|
434
|
+
break;
|
|
171
435
|
case "init":
|
|
172
|
-
default:
|
|
173
436
|
init();
|
|
174
437
|
break;
|
|
438
|
+
case "config":
|
|
439
|
+
createConfig(process.argv.includes("--force") || process.argv.includes("-f"));
|
|
440
|
+
break;
|
|
441
|
+
default: {
|
|
442
|
+
const config = loadConfig();
|
|
443
|
+
if (config.chains[command]) process.exit(await runValidate(command));
|
|
444
|
+
else if (config.commands[command]) {
|
|
445
|
+
const cmdDef = config.commands[command];
|
|
446
|
+
const code = await runShellCommand(cmdDef.run, { cwd: cmdDef.cwd });
|
|
447
|
+
process.exit(code);
|
|
448
|
+
} else {
|
|
449
|
+
console.error(`Unknown command: ${command}`);
|
|
450
|
+
console.log("Run 'ts-builds help' for usage information.");
|
|
451
|
+
process.exit(1);
|
|
452
|
+
}
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
175
455
|
}
|
|
176
456
|
|
|
177
457
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-builds",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Shared TypeScript configuration files for library templates. Provides standardized ESLint, Prettier, Vitest, TypeScript, and build configs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
"homepage": "https://github.com/jordanburke/ts-builds",
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
21
|
-
"url": "https://github.com/jordanburke/ts-builds"
|
|
21
|
+
"url": "git+https://github.com/jordanburke/ts-builds.git"
|
|
22
22
|
},
|
|
23
23
|
"main": "./prettier-config.js",
|
|
24
24
|
"bin": {
|
|
25
|
-
"ts-builds": "
|
|
25
|
+
"ts-builds": "dist/cli.js"
|
|
26
26
|
},
|
|
27
27
|
"exports": {
|
|
28
28
|
".": "./prettier-config.js",
|
|
@@ -79,11 +79,19 @@
|
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
81
|
"scripts": {
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
82
|
+
"bootstrap": "rimraf dist && tsdown",
|
|
83
|
+
"validate": "node dist/cli.js validate",
|
|
84
|
+
"validate:bootstrap": "pnpm bootstrap && pnpm validate",
|
|
85
|
+
"format": "node dist/cli.js format",
|
|
86
|
+
"format:check": "node dist/cli.js format:check",
|
|
87
|
+
"lint": "node dist/cli.js lint",
|
|
88
|
+
"lint:check": "node dist/cli.js lint:check",
|
|
89
|
+
"typecheck": "node dist/cli.js typecheck",
|
|
90
|
+
"test": "node dist/cli.js test",
|
|
86
91
|
"test:watch": "vitest",
|
|
87
|
-
"
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
"build": "node dist/cli.js build",
|
|
93
|
+
"dev": "node dist/cli.js dev",
|
|
94
|
+
"prepublishOnly": "pnpm validate:bootstrap"
|
|
95
|
+
},
|
|
96
|
+
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a"
|
|
97
|
+
}
|