@vlandoss/run-run 0.0.21 → 0.0.22-git-06df4ba.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/bin.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env bun
2
+ import { dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
2
4
  import { main } from "./src/main";
3
5
 
4
6
  main({
5
- binDir: __dirname,
7
+ binDir: dirname(fileURLToPath(import.meta.url)),
6
8
  });
package/dist/bin.mjs ADDED
@@ -0,0 +1,600 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ import path, { dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { colorIsSupported, colorize, createPkgService, createShellService, cwd, getVersion, palette, run } from "@vlandoss/clibuddy";
6
+ import { Option, createCommand } from "commander";
7
+ import fs from "node:fs";
8
+ import os from "node:os";
9
+ import { lilconfig } from "lilconfig";
10
+ import { createLoggy } from "@vlandoss/loggy";
11
+ import { glob } from "glob";
12
+ import { rimraf } from "rimraf";
13
+ import isCI from "is-ci";
14
+ import memoize from "memoize";
15
+ //#region \0rolldown/runtime.js
16
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
17
+ //#endregion
18
+ //#region src/services/logger.ts
19
+ const logger = createLoggy({ namespace: "run-run" });
20
+ //#endregion
21
+ //#region src/services/config.ts
22
+ const DEFAULT_CONFIG = { future: { oxc: false } };
23
+ var ConfigService = class {
24
+ #searcher;
25
+ constructor() {
26
+ this.#searcher = lilconfig("run-run", {
27
+ searchPlaces: ["run-run.config.ts"],
28
+ cache: true,
29
+ stopDir: os.homedir(),
30
+ loaders: { ".ts": (filepath) => import(filepath).then((mod) => mod.default) }
31
+ });
32
+ }
33
+ async load() {
34
+ const debug = logger.subdebug("load-config");
35
+ const searchResult = await this.#searcher.search();
36
+ if (!searchResult || searchResult?.isEmpty) {
37
+ debug("loaded default config: %O", DEFAULT_CONFIG);
38
+ return {
39
+ config: DEFAULT_CONFIG,
40
+ meta: {
41
+ isDefault: true,
42
+ filepath: void 0
43
+ }
44
+ };
45
+ }
46
+ const config = searchResult.config;
47
+ debug("loaded config: %O", config);
48
+ debug("config file: %s", searchResult.filepath);
49
+ return {
50
+ config,
51
+ meta: {
52
+ isDefault: false,
53
+ filepath: searchResult.filepath
54
+ }
55
+ };
56
+ }
57
+ };
58
+ //#endregion
59
+ //#region src/services/ctx.ts
60
+ async function createContext(binDir) {
61
+ const debug = logger.subdebug("create-context");
62
+ const binPath = fs.realpathSync(binDir);
63
+ debug("bin path:", binPath);
64
+ debug("process cwd:", process.cwd());
65
+ const [appPkg, binPkg] = await Promise.all([createPkgService(cwd), createPkgService(binPath)]);
66
+ if (!binPkg) throw new Error("Could not find bin package.json");
67
+ if (!appPkg) throw new Error("Could not find app package.json");
68
+ debug("app pkg info: %O", appPkg.info());
69
+ debug("bin pkg info: %O", binPkg.info());
70
+ const shell = createShellService({
71
+ localBaseBinPath: [binDir],
72
+ stdio: "inherit"
73
+ });
74
+ debug("shell service options: %O", shell.options);
75
+ return {
76
+ appPkg,
77
+ binPkg,
78
+ shell,
79
+ config: await new ConfigService().load()
80
+ };
81
+ }
82
+ //#endregion
83
+ //#region src/program/ui.ts
84
+ const CREDITS_TEXT = `\nAcknowledgment:
85
+ - kcd-scripts: for main inspiration
86
+ ${palette.link("https://github.com/kentcdodds/kcd-scripts")}
87
+
88
+ - peruvian news: in honor to Run Run
89
+ ${palette.link("https://es.wikipedia.org/wiki/Run_Run")}`;
90
+ const tsdownColor = colorize("#FF7E18");
91
+ const biomeColor = colorize("#61A5FA");
92
+ const oxlintColor = colorize("#32F3E9");
93
+ const oxfmtColor = colorize("#32F3E9");
94
+ const tscColor = colorize("#3178C6");
95
+ const rimrafColor = colorize("#7C7270");
96
+ const runRunColor = colorize("FC7A1E");
97
+ const TOOL_LABELS = {
98
+ TSDOWN: tsdownColor("tsdown"),
99
+ BIOME: biomeColor("biome"),
100
+ OXLINT: oxlintColor("oxlint"),
101
+ OXFMT: oxfmtColor("oxfmt"),
102
+ TSC: tscColor("tsc"),
103
+ RIMRAF: rimrafColor("rimraf"),
104
+ RUN_RUN: runRunColor("run-run")
105
+ };
106
+ function getBannerText(version) {
107
+ const uiLogo = `🦊 ${palette.bold("R")} ${palette.bold("U")} ${palette.bold("N")} - ${palette.bold("R")} ${palette.bold("U")} ${palette.bold("N")}`;
108
+ const vlandLogo = `${palette.link(palette.vland("https://variable.land"))} 👊`;
109
+ const title = `${uiLogo} ${palette.muted(`v${version}`)}`;
110
+ const subtitle = `${palette.italic(palette.muted("The CLI toolbox for"))} ${vlandLogo}`;
111
+ if (!colorIsSupported()) return `${title}\n${subtitle}\n`;
112
+ const FOX_COLORS = {
113
+ BLACK: colorize("#39393A"),
114
+ ORANGE: runRunColor,
115
+ WHITE: colorize("#FFFFFF")
116
+ };
117
+ const _ = " ";
118
+ const B = FOX_COLORS.BLACK("██");
119
+ const O = FOX_COLORS.ORANGE("██");
120
+ const W = FOX_COLORS.WHITE("██");
121
+ const lines = [
122
+ [
123
+ _,
124
+ B,
125
+ _,
126
+ _,
127
+ _,
128
+ _,
129
+ _,
130
+ B,
131
+ _
132
+ ],
133
+ [
134
+ _,
135
+ O,
136
+ W,
137
+ _,
138
+ _,
139
+ _,
140
+ W,
141
+ O,
142
+ _
143
+ ],
144
+ [
145
+ _,
146
+ O,
147
+ W,
148
+ O,
149
+ _,
150
+ O,
151
+ W,
152
+ O,
153
+ _
154
+ ],
155
+ [
156
+ B,
157
+ O,
158
+ O,
159
+ O,
160
+ O,
161
+ O,
162
+ O,
163
+ O,
164
+ B
165
+ ],
166
+ [
167
+ O,
168
+ O,
169
+ O,
170
+ O,
171
+ O,
172
+ O,
173
+ O,
174
+ O,
175
+ O
176
+ ],
177
+ [
178
+ W,
179
+ O,
180
+ B,
181
+ O,
182
+ O,
183
+ O,
184
+ B,
185
+ O,
186
+ W
187
+ ],
188
+ [
189
+ _,
190
+ W,
191
+ W,
192
+ O,
193
+ O,
194
+ O,
195
+ W,
196
+ W,
197
+ _
198
+ ],
199
+ [
200
+ _,
201
+ _,
202
+ W,
203
+ W,
204
+ B,
205
+ W,
206
+ W,
207
+ _,
208
+ _
209
+ ],
210
+ [
211
+ _,
212
+ _,
213
+ _,
214
+ W,
215
+ W,
216
+ W,
217
+ _,
218
+ _,
219
+ _
220
+ ]
221
+ ].map((row) => row.join(""));
222
+ lines[3] += ` ${title}`;
223
+ lines[4] += ` ${subtitle}`;
224
+ return `${lines.join("\n")}\n`;
225
+ }
226
+ //#endregion
227
+ //#region src/program/commands/build-lib.ts
228
+ function createBuildLibCommand(ctx) {
229
+ return createCommand("build:lib").summary(`build a ts library 🏗️ (${TOOL_LABELS.TSDOWN})`).description("Compiles TypeScript code into JavaScript and generates type declaration files, making it ready for distribution.").action(async function buildAction() {
230
+ await ctx.shell.$`tsdown`;
231
+ }).addHelpText("afterAll", `\nUnder the hood, this command uses the ${TOOL_LABELS.TSDOWN} CLI to build the project.`);
232
+ }
233
+ //#endregion
234
+ //#region src/program/commands/clean.ts
235
+ function createCleanCommand() {
236
+ return createCommand("clean").summary(`delete dirty files 🗑️ (${TOOL_LABELS.RIMRAF})`).description("Deletes generated files and folders such as 'dist', 'node_modules', and lock files to ensure a clean state.").option("--only-dist", "delete 'dist' folders only").option("--dry-run", "outputs the paths that would be deleted").action(async function cleanCommandAction(options) {
237
+ async function run(paths, globOptions) {
238
+ if (options.dryRun) {
239
+ const toDelete = await glob(paths, globOptions);
240
+ logger.info("Paths that would be deleted: %O", toDelete);
241
+ return;
242
+ }
243
+ logger.start("Clean started");
244
+ await rimraf(paths, { glob: globOptions });
245
+ logger.success("Clean completed");
246
+ }
247
+ const BUILD_PATHS = ["**/dist"];
248
+ const ALL_DIRTY_PATHS = [
249
+ "**/.turbo",
250
+ "**/node_modules",
251
+ "pnpm-lock.yaml",
252
+ "bun.lock",
253
+ ...BUILD_PATHS
254
+ ];
255
+ if (options.onlyDist) await run(BUILD_PATHS, {
256
+ cwd,
257
+ ignore: ["**/node_modules/**"]
258
+ });
259
+ else await run(ALL_DIRTY_PATHS, { cwd });
260
+ }).addHelpText("afterAll", `\nUnder the hood, this command uses ${TOOL_LABELS.RIMRAF} to delete dirty folders or files.`);
261
+ }
262
+ //#endregion
263
+ //#region src/program/commands/config.ts
264
+ function createConfigCommand(ctx) {
265
+ return createCommand("config").summary("display the current config 🛠️").description("Displays the current configuration settings, including their source file path if available.").action(async function configAction() {
266
+ const { config, meta } = ctx.config;
267
+ console.log(palette.muted("Config:"));
268
+ console.log(config);
269
+ console.log(palette.muted(`Loaded from ${meta.filepath ? palette.link(meta.filepath) : "n/a"}`));
270
+ });
271
+ }
272
+ //#endregion
273
+ //#region src/utils/gracefullBinDir.ts
274
+ function gracefullBinDir(binPathResolver) {
275
+ try {
276
+ const binPath = binPathResolver();
277
+ return fs.statSync(binPath).isDirectory() ? binPath : path.dirname(binPath);
278
+ } catch (error) {
279
+ logger.error("Error getting bin directory:", error);
280
+ process.exit(1);
281
+ }
282
+ }
283
+ //#endregion
284
+ //#region src/services/tool.ts
285
+ var ToolService = class {
286
+ #shellService;
287
+ #bin;
288
+ #ui;
289
+ constructor({ bin, ui, shellService }) {
290
+ this.#bin = bin;
291
+ this.#ui = ui ?? bin;
292
+ this.#shellService = shellService;
293
+ }
294
+ exec(args) {
295
+ const $ = this.#shell();
296
+ return this.#run($, args);
297
+ }
298
+ #shell = memoize((cwd) => {
299
+ const { getBinDir } = this;
300
+ return this.#shellService.child({
301
+ cwd,
302
+ ...getBinDir && { preferLocal: gracefullBinDir(() => getBinDir()) }
303
+ }).$;
304
+ });
305
+ #run(shell, args) {
306
+ return shell`${this.#bin} ${typeof args === "string" ? args : args.join(" ")}`;
307
+ }
308
+ get bin() {
309
+ return this.#bin;
310
+ }
311
+ get ui() {
312
+ return this.#ui;
313
+ }
314
+ };
315
+ //#endregion
316
+ //#region src/services/biome.ts
317
+ var BiomeService = class extends ToolService {
318
+ constructor(shellService) {
319
+ super({
320
+ bin: "biome",
321
+ ui: TOOL_LABELS.BIOME,
322
+ shellService
323
+ });
324
+ }
325
+ getBinDir() {
326
+ return __require.resolve("@biomejs/biome/bin/biome");
327
+ }
328
+ async format(options) {
329
+ const commonOptions = "format --colors=force --no-errors-on-unmatched";
330
+ if (options.fix) await this.exec(`${commonOptions} --fix`);
331
+ else await this.exec(`${commonOptions}`);
332
+ }
333
+ async lint(options) {
334
+ const commonOptions = "check --colors=force --no-errors-on-unmatched --formatter-enabled=false";
335
+ if (options.fix) await this.exec(`${commonOptions} --fix --unsafe`);
336
+ else await this.exec(`${commonOptions}`);
337
+ }
338
+ async check(options) {
339
+ const commonOptions = (cmd = "check") => `${cmd} --colors=force --no-errors-on-unmatched`;
340
+ if (options.fix) await this.exec(`${commonOptions()} --fix`);
341
+ else if (options.fixStaged) await this.exec(`${commonOptions()} --fix --staged`);
342
+ else await this.exec(`${commonOptions(isCI ? "ci" : "check")}`);
343
+ }
344
+ };
345
+ //#endregion
346
+ //#region src/services/oxfmt.ts
347
+ var OxfmtService = class extends ToolService {
348
+ constructor(shellService) {
349
+ super({
350
+ bin: "oxfmt",
351
+ ui: TOOL_LABELS.OXFMT,
352
+ shellService
353
+ });
354
+ }
355
+ getBinDir() {
356
+ return __require.resolve("oxfmt/bin/oxfmt");
357
+ }
358
+ async format(options) {
359
+ const commonOptions = "--no-error-on-unmatched-pattern";
360
+ if (options.fix) await this.exec(`${commonOptions} --fix`);
361
+ else await this.exec(`${commonOptions} --check`);
362
+ }
363
+ };
364
+ //#endregion
365
+ //#region src/program/commands/format.ts
366
+ function getToolService$2(ctx) {
367
+ const { config } = ctx.config;
368
+ if (config.future?.oxc) return new OxfmtService(ctx.shell);
369
+ return new BiomeService(ctx.shell);
370
+ }
371
+ function createFormatCommand(ctx) {
372
+ const toolService = getToolService$2(ctx);
373
+ return createCommand("format").summary(`check & fix format errors 🎨 (${toolService.ui})`).description("Checks the code for formatting issues and optionally fixes them, ensuring it adheres to the defined style standards.").option("--fix", "format all the code").action(async function formatAction(options) {
374
+ await toolService.format(options);
375
+ }).addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolService.ui} CLI to format the code.`);
376
+ }
377
+ //#endregion
378
+ //#region src/program/commands/jscheck.ts
379
+ function createJsCheckCommand(ctx) {
380
+ const checkerService = new BiomeService(ctx.shell);
381
+ return createCommand("jsc").alias("jscheck").alias("check").summary(`check format and lint 🔍 (${checkerService.ui})`).description("Checks the code for formatting and linting issues, ensuring it adheres to the defined style and quality standards.").option("--fix", "try to fix issues automatically").option("--fix-staged", "try to fix staged files only").action(async function checkAction(options) {
382
+ await checkerService.check(options);
383
+ }).addHelpText("afterAll", `\nUnder the hood, this command uses the ${checkerService.ui} CLI to check the code.`);
384
+ }
385
+ //#endregion
386
+ //#region src/services/oxlint.ts
387
+ var OxlintService = class extends ToolService {
388
+ constructor(shellService) {
389
+ super({
390
+ bin: "oxlint",
391
+ ui: TOOL_LABELS.OXLINT,
392
+ shellService
393
+ });
394
+ }
395
+ getBinDir() {
396
+ return __require.resolve("oxlint/bin/oxlint");
397
+ }
398
+ async lint(options) {
399
+ const commonOptions = "--report-unused-disable-directives";
400
+ if (options.fix) await this.exec(`${commonOptions} --fix`);
401
+ else await this.exec(`${commonOptions} --check`);
402
+ }
403
+ };
404
+ //#endregion
405
+ //#region src/program/commands/lint.ts
406
+ function getToolService$1(ctx) {
407
+ const { config } = ctx.config;
408
+ if (config.future?.oxc) return new OxlintService(ctx.shell);
409
+ return new BiomeService(ctx.shell);
410
+ }
411
+ function createLintCommand(ctx) {
412
+ const toolService = getToolService$1(ctx);
413
+ return createCommand("lint").summary(`check & fix lint errors 🔍 (${toolService.ui})`).description("Checks the code for linting issues and optionally fixes them, ensuring it adheres to the defined quality standards.").option("-c, --check", "check if the code is valid", true).option("--fix", "try to fix all the code").action(async function lintAction(options) {
414
+ await toolService.lint(options);
415
+ }).addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolService.ui} CLI to lint the code.`);
416
+ }
417
+ //#endregion
418
+ //#region src/program/commands/pkgs.ts
419
+ const decorators = ["turbo"];
420
+ function createPkgsCommand(ctx) {
421
+ return createCommand("pkgs").summary("list affected packages 📦").description("Given a list of files, returns the list of affected packages. Useful to run commands only on affected packages.").addOption(new Option("--files <files...>", "list of files to check")).addOption(new Option("--decorator [type]", "type of decorator to use").choices(decorators)).action(async function pkgsAction({ files, decorator }, cmd) {
422
+ const { appPkg } = ctx;
423
+ if (!appPkg.isMonorepo()) {
424
+ const cmdName = cmd.parent?.args[0] ?? cmd.args[0] ?? cmd.name();
425
+ logger.error(`The \`${cmdName}\` command can only be run in a monorepo.`);
426
+ return process.exit(1);
427
+ }
428
+ const projects = await appPkg.getWorkspaceProjects();
429
+ const getRelativeRootDir = memoize((rootDir) => {
430
+ const appDir = appPkg.dirPath;
431
+ if (!path.isAbsolute(rootDir)) return rootDir;
432
+ return path.relative(appDir, rootDir);
433
+ });
434
+ function getPackageForFile(filePath) {
435
+ for (const project of projects) {
436
+ const relativeRootDir = getRelativeRootDir(project.rootDir);
437
+ if (filePath.startsWith(relativeRootDir)) return project;
438
+ }
439
+ return null;
440
+ }
441
+ const uniquePkgsNames = /* @__PURE__ */ new Set();
442
+ files.forEach((file) => {
443
+ const pkg = getPackageForFile(file);
444
+ if (pkg?.manifest.name) uniquePkgsNames.add(pkg.manifest.name);
445
+ });
446
+ if (!uniquePkgsNames.size) return;
447
+ const pkgsNames = Array.from(uniquePkgsNames);
448
+ if (decorator === "turbo") console.log(...pkgsNames.map((name) => `--filter=...${name}`));
449
+ else console.log(...pkgsNames);
450
+ });
451
+ }
452
+ //#endregion
453
+ //#region src/program/commands/run.ts
454
+ function createRunCommand(ctx) {
455
+ return createCommand("run").argument("<cmds...>", "commands to execute concurrently (e.g. 'check tsc')").action(async function runRunAction(cmds) {
456
+ const { $ } = ctx.shell;
457
+ const commands = cmds.map((cmd) => $`rr ${cmd}`);
458
+ await Promise.all(commands);
459
+ });
460
+ }
461
+ //#endregion
462
+ //#region src/program/commands/test-static.ts
463
+ function createTestStaticCommand(ctx) {
464
+ return createCommand("test:static").summary(`run static tests 🔬 (${TOOL_LABELS.RUN_RUN})`).description("Runs static tests, including linting, formatting checks, and TypeScript type checking, to ensure code quality and correctness without executing the code.").action(async function testStaticAction() {
465
+ await ctx.shell.$`rr jscheck tscheck`;
466
+ });
467
+ }
468
+ //#endregion
469
+ //#region src/services/tsdown.ts
470
+ var TsdownService = class extends ToolService {
471
+ constructor(shellService) {
472
+ super({
473
+ bin: "tsdown",
474
+ ui: TOOL_LABELS.TSDOWN,
475
+ shellService
476
+ });
477
+ }
478
+ };
479
+ //#endregion
480
+ //#region src/program/commands/tools.ts
481
+ function getToolService(bin, shell) {
482
+ switch (bin) {
483
+ case "biome": return new BiomeService(shell);
484
+ case "oxfmt": return new OxfmtService(shell);
485
+ case "oxlint": return new OxlintService(shell);
486
+ case "tsdown": return new TsdownService(shell);
487
+ default: throw new Error(`Unknown tool: ${bin}`);
488
+ }
489
+ }
490
+ function createToolCommand(bin, shell) {
491
+ const tool = getToolService(bin, shell);
492
+ return createCommand(tool.bin).helpCommand(false).helpOption(false).allowExcessArguments(true).allowUnknownOption(true).action(async (_, { args }) => {
493
+ await tool.exec(args);
494
+ });
495
+ }
496
+ function createToolsCommand(ctx) {
497
+ return createCommand("tools").description("expose the internal tools 🛠️").addCommand(createToolCommand("biome", ctx.shell)).addCommand(createToolCommand("oxfmt", ctx.shell)).addCommand(createToolCommand("oxlint", ctx.shell)).addCommand(createToolCommand("tsdown", ctx.shell));
498
+ }
499
+ //#endregion
500
+ //#region src/program/commands/tscheck.ts
501
+ const getPreScript = (scripts) => scripts?.pretsc ?? scripts?.pretypecheck;
502
+ async function typecheckAt({ dir, scripts, log, shell, run }) {
503
+ let shellAt;
504
+ log.debug(`checking types at ${dir}`);
505
+ if (cwd === dir) shellAt = shell;
506
+ else {
507
+ log.debug(`Changing directory to ${dir} for typecheck`);
508
+ shellAt = shell.at(dir);
509
+ }
510
+ try {
511
+ const preScript = getPreScript(scripts);
512
+ if (preScript) {
513
+ log.start(`Running pre-script: ${preScript}`);
514
+ await shellAt.$`${preScript}`;
515
+ log.success("Pre-script completed");
516
+ }
517
+ log.start("Type checking started");
518
+ await run(shellAt);
519
+ log.success("Typecheck completed");
520
+ } catch (error) {
521
+ log.error("Typecheck failed");
522
+ throw error;
523
+ }
524
+ }
525
+ function createTsCheckCommand(ctx) {
526
+ const { appPkg, shell, config: { config } } = ctx;
527
+ const toolUi = config.future?.oxc ? TOOL_LABELS.OXLINT : TOOL_LABELS.TSC;
528
+ return createCommand("tsc").alias("tscheck").summary(`check typescript errors 🧩 (${toolUi})`).description("Checks the TypeScript code for type errors, ensuring that the code adheres to the defined type constraints and helps catch potential issues before runtime.").addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolUi} CLI to check the code.`).action(async function typecheckAction() {
529
+ const isTsProject = (dir) => appPkg.hasFile("tsconfig.json", dir);
530
+ const runTypecheck = async (shell) => {
531
+ if (config.future?.oxc) await new OxlintService(shell).exec(`--type-aware --type-check --report-unused-disable-directives`);
532
+ else await shell.$`tsc --noEmit`;
533
+ };
534
+ if (!appPkg.isMonorepo()) {
535
+ if (!isTsProject(appPkg.dirPath)) {
536
+ logger.info("No tsconfig.json found, skipping typecheck");
537
+ return;
538
+ }
539
+ await typecheckAt({
540
+ shell,
541
+ run: runTypecheck,
542
+ dir: appPkg.dirPath,
543
+ scripts: appPkg.packageJson.scripts,
544
+ log: logger
545
+ });
546
+ return;
547
+ }
548
+ const tsProjects = (await appPkg.getWorkspaceProjects()).filter((project) => isTsProject(project.rootDir));
549
+ if (!tsProjects.length) {
550
+ logger.warn("No ts projects found in the monorepo, skipping typecheck");
551
+ return;
552
+ }
553
+ await Promise.all(tsProjects.map((p) => typecheckAt({
554
+ shell,
555
+ run: runTypecheck,
556
+ dir: p.rootDir,
557
+ scripts: p.manifest.scripts,
558
+ log: logger.child({
559
+ tag: p.manifest.name,
560
+ namespace: "typecheck"
561
+ })
562
+ })));
563
+ });
564
+ }
565
+ //#endregion
566
+ //#region src/program/index.ts
567
+ async function createProgram(options) {
568
+ const ctx = await createContext(options.binDir);
569
+ const version = getVersion(ctx.binPkg);
570
+ return {
571
+ program: createCommand("rr").alias("run-run").usage("[options] <command...>").helpCommand(false).version(version, "-v, --version").addHelpText("before", getBannerText(version)).addHelpText("after", CREDITS_TEXT).addCommand(createBuildLibCommand(ctx)).addCommand(createJsCheckCommand(ctx)).addCommand(createTsCheckCommand(ctx)).addCommand(createLintCommand(ctx)).addCommand(createFormatCommand(ctx)).addCommand(createTestStaticCommand(ctx)).addCommand(createCleanCommand()).addCommand(createPkgsCommand(ctx)).addCommand(createConfigCommand(ctx)).addCommand(createRunCommand(ctx), { hidden: true }).addCommand(createToolsCommand(ctx), { hidden: true }),
572
+ ctx
573
+ };
574
+ }
575
+ //#endregion
576
+ //#region src/program/parse-args.ts
577
+ const debug = logger.subdebug("parseArgs");
578
+ function parseArgs(argv = process.argv) {
579
+ const args = argv.slice(2);
580
+ const allArgsAreValidCommands = args.every((arg) => !arg.startsWith("-")) && args.length > 1 && args[0] !== "tools";
581
+ debug("args %O", args);
582
+ if (allArgsAreValidCommands) {
583
+ debug("multiple commands detected, adding 'run' command");
584
+ return ["run", ...args];
585
+ }
586
+ return args;
587
+ }
588
+ //#endregion
589
+ //#region src/main.ts
590
+ async function main(options) {
591
+ await run(async () => {
592
+ const { program } = await createProgram(options);
593
+ await program.parseAsync(parseArgs(), { from: "user" });
594
+ }, logger);
595
+ }
596
+ //#endregion
597
+ //#region bin.ts
598
+ main({ binDir: dirname(fileURLToPath(import.meta.url)) });
599
+ //#endregion
600
+ export {};
package/dist/config.mjs CHANGED
@@ -2,6 +2,5 @@
2
2
  function defineConfig(config) {
3
3
  return config;
4
4
  }
5
-
6
5
  //#endregion
7
- export { defineConfig };
6
+ export { defineConfig };
@@ -1,3 +1,2 @@
1
- export * from "tsdown"
2
-
3
- export { };
1
+ export * from "tsdown";
2
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vlandoss/run-run",
3
- "version": "0.0.21",
3
+ "version": "0.0.22-git-06df4ba.0",
4
4
  "description": "The CLI toolbox to fullstack common scripts in Variable Land",
5
5
  "homepage": "https://github.com/variableland/dx/tree/main/packages/run-run#readme",
6
6
  "bugs": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "git+https://github.com/variableland/dx.git"
11
+ "url": "git+https://github.com/variableland/dx.git",
12
+ "directory": "packages/run-run"
12
13
  },
13
14
  "license": "MIT",
14
15
  "author": "rcrd <rcrd@variable.land>",
@@ -19,19 +20,17 @@
19
20
  },
20
21
  "exports": {
21
22
  "./config": {
22
- "bun": "./src/lib/config.ts",
23
23
  "types": "./dist/config.d.mts",
24
24
  "default": "./dist/config.mjs"
25
25
  },
26
26
  "./tools/*": {
27
- "bun": "./src/lib/tools/*.ts",
28
27
  "types": "./dist/tools/*.d.mts",
29
28
  "default": "./dist/tools/*.mjs"
30
29
  }
31
30
  },
32
31
  "bin": {
33
- "rr": "./bin.ts",
34
- "run-run": "./bin.ts",
32
+ "rr": "./dist/bin.mjs",
33
+ "run-run": "./dist/bin.mjs",
35
34
  "biome": "./tools/biome",
36
35
  "oxfmt": "./tools/oxfmt",
37
36
  "oxlint": "./tools/oxlint"
@@ -40,6 +39,8 @@
40
39
  "bin.ts",
41
40
  "dist",
42
41
  "src",
42
+ "!src/**/__tests__",
43
+ "!src/**/*.test.*",
43
44
  "tools",
44
45
  "tsconfig.json"
45
46
  ],
@@ -54,17 +55,21 @@
54
55
  "oxlint": "1.50.0",
55
56
  "oxlint-tsgolint": "0.15.0",
56
57
  "rimraf": "6.1.3",
57
- "tsdown": "0.21.0-beta.2",
58
+ "tsdown": "0.21.10",
58
59
  "typescript": "5.9.3",
59
- "@vlandoss/clibuddy": "0.0.10",
60
- "@vlandoss/loggy": "0.0.7"
60
+ "@vlandoss/loggy": "0.0.8-git-06df4ba.0",
61
+ "@vlandoss/clibuddy": "0.0.11-git-06df4ba.0"
61
62
  },
62
63
  "publishConfig": {
63
64
  "access": "public"
64
65
  },
65
66
  "engines": {
67
+ "node": ">=20.0.0",
66
68
  "bun": ">=1.0.0"
67
69
  },
70
+ "devDependencies": {
71
+ "@vlandoss/tsdown-config": "^0.0.2-git-06df4ba.0"
72
+ },
68
73
  "scripts": {
69
74
  "build": "tsdown",
70
75
  "test": "bun test",
package/tools/biome CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env bash
2
- bunx rr tools biome "$@"
2
+ rr tools biome "$@"
package/tools/oxfmt CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env bash
2
- bunx rr tools oxfmt "$@"
2
+ rr tools oxfmt "$@"
package/tools/oxlint CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env bash
2
- bunx rr tools oxlint "$@"
2
+ rr tools oxlint "$@"
package/tools/tsdown CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env bash
2
- bunx rr tools tsdown "$@"
2
+ rr tools tsdown "$@"
@@ -1,189 +0,0 @@
1
- // Bun Snapshot v1, https://bun.sh/docs/test/snapshots
2
-
3
- exports[`should match all root commands: root-command---help 1`] = `
4
- "🦊 R U N - R U N v0.0.0-test
5
- The CLI toolbox for https://variable.land 👊
6
-
7
- Usage: rr|run-run [options] <command...>
8
-
9
- Options:
10
- -v, --version output the version number
11
- -h, --help display help for command
12
-
13
- Commands:
14
- build:lib build a ts library 🏗️ (tsdown)
15
- jsc|jscheck [options] check format and lint 🔍 (biome)
16
- tsc|tscheck check typescript errors 🧩 (tsc)
17
- lint [options] check & fix lint errors 🔍 (biome)
18
- format [options] check & fix format errors 🎨 (biome)
19
- test:static run static tests 🔬 (run-run)
20
- clean [options] delete dirty files 🗑️ (rimraf)
21
- pkgs [options] list affected packages 📦
22
- config display the current config 🛠️
23
-
24
- Acknowledgment:
25
- - kcd-scripts: for main inspiration
26
- https://github.com/kentcdodds/kcd-scripts
27
-
28
- - peruvian news: in honor to Run Run
29
- https://es.wikipedia.org/wiki/Run_Run
30
- "
31
- `;
32
-
33
- exports[`should match all root commands: root-command---version 1`] = `
34
- "0.0.0-test
35
- "
36
- `;
37
-
38
- exports[`should match all root commands: root-command--v 1`] = `
39
- "0.0.0-test
40
- "
41
- `;
42
-
43
- exports[`should match help messages for all commands: help-command-build:lib 1`] = `
44
- "Usage: rr build:lib [options]
45
-
46
- Compiles TypeScript code into JavaScript and generates type declaration files,
47
- making it ready for distribution.
48
-
49
- Options:
50
- -h, --help display help for command
51
-
52
- Under the hood, this command uses the tsdown CLI to build the project.
53
- "
54
- `;
55
-
56
- exports[`should match help messages for all commands: help-command-jsc 1`] = `
57
- "Usage: rr jsc|jscheck [options]
58
-
59
- Checks the code for formatting and linting issues, ensuring it adheres to the
60
- defined style and quality standards.
61
-
62
- Options:
63
- --fix try to fix issues automatically
64
- --fix-staged try to fix staged files only
65
- -h, --help display help for command
66
-
67
- Under the hood, this command uses the biome CLI to check the code.
68
- "
69
- `;
70
-
71
- exports[`should match help messages for all commands: help-command-tsc 1`] = `
72
- "Usage: rr tsc|tscheck [options]
73
-
74
- Checks the TypeScript code for type errors, ensuring that the code adheres to
75
- the defined type constraints and helps catch potential issues before runtime.
76
-
77
- Options:
78
- -h, --help display help for command
79
-
80
- Under the hood, this command uses the tsc CLI to check the code.
81
- "
82
- `;
83
-
84
- exports[`should match help messages for all commands: help-command-lint 1`] = `
85
- "Usage: rr lint [options]
86
-
87
- Checks the code for linting issues and optionally fixes them, ensuring it
88
- adheres to the defined quality standards.
89
-
90
- Options:
91
- -c, --check check if the code is valid (default: true)
92
- --fix try to fix all the code
93
- -h, --help display help for command
94
-
95
- Under the hood, this command uses the biome CLI to lint the code.
96
- "
97
- `;
98
-
99
- exports[`should match help messages for all commands: help-command-format 1`] = `
100
- "Usage: rr format [options]
101
-
102
- Checks the code for formatting issues and optionally fixes them, ensuring it
103
- adheres to the defined style standards.
104
-
105
- Options:
106
- --fix format all the code
107
- -h, --help display help for command
108
-
109
- Under the hood, this command uses the biome CLI to format the code.
110
- "
111
- `;
112
-
113
- exports[`should match help messages for all commands: help-command-test:static 1`] = `
114
- "Usage: rr test:static [options]
115
-
116
- Runs static tests, including linting, formatting checks, and TypeScript type
117
- checking, to ensure code quality and correctness without executing the code.
118
-
119
- Options:
120
- -h, --help display help for command
121
- "
122
- `;
123
-
124
- exports[`should match help messages for all commands: help-command-clean 1`] = `
125
- "Usage: rr clean [options]
126
-
127
- Deletes generated files and folders such as 'dist', 'node_modules', and lock
128
- files to ensure a clean state.
129
-
130
- Options:
131
- --only-dist delete 'dist' folders only
132
- --dry-run outputs the paths that would be deleted
133
- -h, --help display help for command
134
-
135
- Under the hood, this command uses rimraf to delete dirty folders or files.
136
- "
137
- `;
138
-
139
- exports[`should match help messages for all commands: help-command-pkgs 1`] = `
140
- "Usage: rr pkgs [options]
141
-
142
- Given a list of files, returns the list of affected packages. Useful to run
143
- commands only on affected packages.
144
-
145
- Options:
146
- --files <files...> list of files to check
147
- --decorator [type] type of decorator to use (choices: "turbo")
148
- -h, --help display help for command
149
- "
150
- `;
151
-
152
- exports[`should match help messages for all commands: help-command-config 1`] = `
153
- "Usage: rr config [options]
154
-
155
- Displays the current configuration settings, including their source file path if
156
- available.
157
-
158
- Options:
159
- -h, --help display help for command
160
- "
161
- `;
162
-
163
- exports[`should match help messages for all commands: help-command-run 1`] = `
164
- "Usage: rr run [options] <cmds...>
165
-
166
- Arguments:
167
- cmds commands to execute concurrently (e.g. 'check tsc')
168
-
169
- Options:
170
- -h, --help display help for command
171
- "
172
- `;
173
-
174
- exports[`should match help messages for all commands: help-command-tools 1`] = `
175
- "Usage: rr tools [options] [command]
176
-
177
- expose the internal tools 🛠️
178
-
179
- Options:
180
- -h, --help display help for command
181
-
182
- Commands:
183
- biome
184
- oxfmt
185
- oxlint
186
- tsdown
187
- help [command] display help for command
188
- "
189
- `;
@@ -1,38 +0,0 @@
1
- import { afterEach, expect, test } from "bun:test";
2
- import { createTestProgram, execCli, mocked } from "#test/helpers.ts";
3
-
4
- const { program, ctx } = await createTestProgram();
5
- const $ = ctx.shell.$;
6
-
7
- const rootCommands = ["--help", "--version", "-v"];
8
-
9
- afterEach(() => {
10
- mocked($).mockClear();
11
- });
12
-
13
- test("should match all root commands", async () => {
14
- const results = await Promise.all(
15
- rootCommands.map(async (cmd) => {
16
- const { stdout } = await execCli(cmd);
17
- return { cmd, output: stdout };
18
- }),
19
- );
20
-
21
- for (const { cmd, output } of results) {
22
- expect(output).toMatchSnapshot(`root-command-${cmd}`);
23
- }
24
- });
25
-
26
- test("should match help messages for all commands", async () => {
27
- const results = await Promise.all(
28
- program.commands.map(async (command) => {
29
- const cmd = command.name();
30
- const { stdout } = await execCli(`${cmd} --help`);
31
- return { cmd, output: stdout };
32
- }),
33
- );
34
-
35
- for (const { cmd, output } of results) {
36
- expect(output).toMatchSnapshot(`help-command-${cmd}`);
37
- }
38
- });