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.
Files changed (2) hide show
  1. package/dist/cli.js +296 -16
  2. 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 configuration files
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 config files (default)
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
- EXAMPLES:
65
- npx ts-builds # Initialize project
66
- npx ts-builds info # List bundled packages
67
- npx ts-builds cleanup # Remove redundant deps
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
- DESCRIPTION:
70
- This package bundles all ESLint, Prettier, Vitest, and TypeScript
71
- tooling as dependencies. You only need to install:
142
+ CONFIGURATION:
143
+ Create ts-builds.config.json in your project root:
72
144
 
73
- - ts-builds (this package)
74
- - tsdown (peer dependency for building)
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
- Run 'npx ts-builds info' to see the full list
77
- of bundled packages.
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
- switch (process.argv[2] || "init") {
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.0",
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": "./dist/cli.js"
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
- "format": "prettier --write .",
83
- "format:check": "prettier --check .",
84
- "build": "rimraf dist && tsdown",
85
- "test": "vitest run",
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
- "validate": "pnpm format && pnpm build && pnpm test"
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
+ }