@kidd-cli/cli 0.1.2 → 0.1.3

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.
@@ -1,12 +1,11 @@
1
- import { n as renderTemplate, t as writeFiles } from "../../write-DDGnajpV.mjs";
2
- import { t as detectProject } from "../../detect-CSSt9GdX.mjs";
1
+ import { n as isKebabCase, r as renderTemplate, t as writeFiles } from "../../write-dfZMTbtn.mjs";
2
+ import { t as detectProject } from "../../detect-t9mL3oTg.mjs";
3
3
  import { command } from "@kidd-cli/core";
4
4
  import { join } from "node:path";
5
5
  import { loadConfig } from "@kidd-cli/config/loader";
6
6
  import { z } from "zod";
7
7
 
8
8
  //#region src/commands/add/command.ts
9
- const KEBAB_CASE_CHARS_RE = /^[a-z][\da-z-]*$/;
10
9
  const addCommandCommand = command({
11
10
  args: z.object({
12
11
  args: z.boolean().describe("Include args schema").optional(),
@@ -18,8 +17,7 @@ const addCommandCommand = command({
18
17
  const [detectError, project] = await detectProject(process.cwd());
19
18
  if (detectError) return ctx.fail(detectError.message);
20
19
  if (!project) return ctx.fail("Not in a kidd project. Run `kidd init` first.");
21
- const [configError, configResult] = await loadConfig({ cwd: project.rootDir });
22
- if (configError) {}
20
+ const [, configResult] = await loadConfig({ cwd: project.rootDir });
23
21
  const commandName = await resolveCommandName(ctx);
24
22
  const commandDescription = await resolveDescription(ctx);
25
23
  const includeArgs = await resolveIncludeArgs(ctx);
@@ -50,24 +48,11 @@ const addCommandCommand = command({
50
48
  return ctx.fail(writeError.message);
51
49
  }
52
50
  ctx.spinner.stop("Command created!");
53
- result.written.map((file) => ctx.output.raw(` created ${file}`));
54
- result.skipped.map((file) => ctx.output.raw(` skipped ${file} (already exists)`));
51
+ const summary = [...result.written.map((file) => ` created ${file}`), ...result.skipped.map((file) => ` skipped ${file} (already exists)`)].join("\n");
52
+ if (summary.length > 0) ctx.output.raw(summary);
55
53
  }
56
54
  });
57
55
  /**
58
- * Check whether a string is valid kebab-case.
59
- *
60
- * @param value - The string to validate.
61
- * @returns True when the string is kebab-case.
62
- * @private
63
- */
64
- function isKebabCase(value) {
65
- if (!KEBAB_CASE_CHARS_RE.test(value)) return false;
66
- if (value.endsWith("-")) return false;
67
- if (value.includes("--")) return false;
68
- return true;
69
- }
70
- /**
71
56
  * Resolve the command name from args or prompt.
72
57
  *
73
58
  * @param ctx - Command context.
@@ -110,7 +95,7 @@ async function resolveDescription(ctx) {
110
95
  * @private
111
96
  */
112
97
  async function resolveIncludeArgs(ctx) {
113
- if (ctx.args.args !== void 0 && ctx.args.args !== null) return ctx.args.args;
98
+ if (ctx.args.args !== void 0) return ctx.args.args;
114
99
  return ctx.prompts.confirm({
115
100
  initialValue: true,
116
101
  message: "Include args schema?"
@@ -1,11 +1,10 @@
1
- import { n as renderTemplate, t as writeFiles } from "../../write-DDGnajpV.mjs";
2
- import { t as detectProject } from "../../detect-CSSt9GdX.mjs";
1
+ import { n as isKebabCase, r as renderTemplate, t as writeFiles } from "../../write-dfZMTbtn.mjs";
2
+ import { t as detectProject } from "../../detect-t9mL3oTg.mjs";
3
3
  import { command } from "@kidd-cli/core";
4
4
  import { join } from "node:path";
5
5
  import { z } from "zod";
6
6
 
7
7
  //#region src/commands/add/middleware.ts
8
- const KEBAB_CASE_CHARS_RE = /^[a-z][\da-z-]*$/;
9
8
  const addMiddlewareCommand = command({
10
9
  args: z.object({
11
10
  description: z.string().describe("Middleware description").optional(),
@@ -44,24 +43,11 @@ const addMiddlewareCommand = command({
44
43
  return ctx.fail(writeError.message);
45
44
  }
46
45
  ctx.spinner.stop("Middleware created!");
47
- result.written.map((file) => ctx.output.raw(` created ${file}`));
48
- result.skipped.map((file) => ctx.output.raw(` skipped ${file} (already exists)`));
46
+ const summary = [...result.written.map((file) => ` created ${file}`), ...result.skipped.map((file) => ` skipped ${file} (already exists)`)].join("\n");
47
+ if (summary.length > 0) ctx.output.raw(summary);
49
48
  }
50
49
  });
51
50
  /**
52
- * Check whether a string is valid kebab-case.
53
- *
54
- * @param value - The string to validate.
55
- * @returns True when the string is kebab-case.
56
- * @private
57
- */
58
- function isKebabCase(value) {
59
- if (!KEBAB_CASE_CHARS_RE.test(value)) return false;
60
- if (value.endsWith("-")) return false;
61
- if (value.includes("--")) return false;
62
- return true;
63
- }
64
- /**
65
51
  * Resolve the middleware name from args or prompt.
66
52
  *
67
53
  * @param ctx - Command context.
@@ -1,3 +1,4 @@
1
+ import { t as extractConfig } from "../config-helpers-QvhfBI9b.mjs";
1
2
  import { command } from "@kidd-cli/core";
2
3
  import { relative } from "node:path";
3
4
  import { build, compile, resolveTargetLabel } from "@kidd-cli/bundler";
@@ -21,9 +22,8 @@ const buildCommand = command({
21
22
  description: "Build a kidd CLI project for production",
22
23
  handler: async (ctx) => {
23
24
  const cwd = process.cwd();
24
- const [configError, configResult] = await loadConfig({ cwd });
25
+ const [, configResult] = await loadConfig({ cwd });
25
26
  const config = extractConfig(configResult);
26
- if (configError) {}
27
27
  ctx.spinner.start("Bundling with tsdown...");
28
28
  const [buildError, buildOutput] = await build({
29
29
  config,
@@ -73,17 +73,6 @@ const buildCommand = command({
73
73
  }
74
74
  });
75
75
  /**
76
- * Extract a KiddConfig from a load result, falling back to empty defaults.
77
- *
78
- * @private
79
- * @param result - The result from loadConfig, or null when loading failed.
80
- * @returns The loaded config or an empty object (all KiddConfig fields are optional).
81
- */
82
- function extractConfig(result) {
83
- if (result) return result.config;
84
- return {};
85
- }
86
- /**
87
76
  * Determine whether compilation should run based on CLI flags and config.
88
77
  *
89
78
  * Resolution order:
@@ -1,3 +1,4 @@
1
+ import { t as extractConfig } from "../config-helpers-QvhfBI9b.mjs";
1
2
  import { autoload, command } from "@kidd-cli/core";
2
3
  import { join } from "node:path";
3
4
  import { loadConfig } from "@kidd-cli/config/loader";
@@ -15,10 +16,8 @@ const commandsCommand = command({
15
16
  description: "Display the command tree for a kidd CLI project",
16
17
  handler: async (ctx) => {
17
18
  const cwd = process.cwd();
18
- const [configError, configResult] = await loadConfig({ cwd });
19
- const config = extractConfig(configResult);
20
- if (configError) {}
21
- const commandsDir = join(cwd, config.commands ?? "commands");
19
+ const [, configResult] = await loadConfig({ cwd });
20
+ const commandsDir = join(cwd, extractConfig(configResult).commands ?? "commands");
22
21
  if (!existsSync(commandsDir)) ctx.fail(`Commands directory not found: ${commandsDir}`);
23
22
  ctx.spinner.start("Scanning commands...");
24
23
  const tree = await buildTree(await autoload({ dir: commandsDir }));
@@ -31,17 +30,6 @@ const commandsCommand = command({
31
30
  }
32
31
  });
33
32
  /**
34
- * Extract a KiddConfig from a load result, falling back to empty defaults.
35
- *
36
- * @private
37
- * @param result - The result from loadConfig, or null when loading failed.
38
- * @returns The loaded config or an empty object (all KiddConfig fields are optional).
39
- */
40
- function extractConfig(result) {
41
- if (result) return result.config;
42
- return {};
43
- }
44
- /**
45
33
  * Resolve a command's subcommands field, which may be a Promise, a map, or undefined.
46
34
  *
47
35
  * @private
@@ -1,3 +1,4 @@
1
+ import { t as extractConfig } from "../config-helpers-QvhfBI9b.mjs";
1
2
  import { command } from "@kidd-cli/core";
2
3
  import { watch } from "@kidd-cli/bundler";
3
4
  import { loadConfig } from "@kidd-cli/config/loader";
@@ -13,9 +14,8 @@ const devCommand = command({
13
14
  description: "Start a kidd CLI project in development mode",
14
15
  handler: async (ctx) => {
15
16
  const cwd = process.cwd();
16
- const [configError, configResult] = await loadConfig({ cwd });
17
+ const [, configResult] = await loadConfig({ cwd });
17
18
  const config = extractConfig(configResult);
18
- if (configError) {}
19
19
  ctx.spinner.start("Starting dev server...");
20
20
  const [watchError] = await watch({
21
21
  config,
@@ -29,17 +29,6 @@ const devCommand = command({
29
29
  }
30
30
  });
31
31
  /**
32
- * Extract a KiddConfig from a load result, falling back to empty defaults.
33
- *
34
- * @private
35
- * @param result - The result from loadConfig, or null when loading failed.
36
- * @returns The loaded config or an empty object (all KiddConfig fields are optional).
37
- */
38
- function extractConfig(result) {
39
- if (result) return result.config;
40
- return {};
41
- }
42
- /**
43
32
  * Create an onSuccess callback that tracks first-build state.
44
33
  *
45
34
  * On the first invocation the spinner is stopped and a "watching" message is
@@ -4,8 +4,9 @@ import { readManifest } from "@kidd-cli/utils/manifest";
4
4
  import { loadConfig } from "@kidd-cli/config/loader";
5
5
  import { z } from "zod";
6
6
  import pc from "picocolors";
7
- import { access, mkdir, readFile, writeFile } from "node:fs/promises";
7
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
8
8
  import { attemptAsync, err, ok } from "@kidd-cli/utils/fp";
9
+ import { fileExists } from "@kidd-cli/utils/fs";
9
10
  import { jsonParse, jsonStringify } from "@kidd-cli/utils/json";
10
11
 
11
12
  //#region src/lib/checks.ts
@@ -440,17 +441,6 @@ async function readRawPackageJson(cwd) {
440
441
  return ok(data);
441
442
  }
442
443
  /**
443
- * Check whether a path exists on disk.
444
- *
445
- * @private
446
- * @param filePath - The path to check.
447
- * @returns True when the path is accessible, false otherwise.
448
- */
449
- async function fileExists(filePath) {
450
- const [accessError] = await attemptAsync(() => access(filePath));
451
- return accessError === null;
452
- }
453
- /**
454
444
  * Extract a KiddConfig from a load result, returning null when absent.
455
445
  *
456
446
  * @private
@@ -618,24 +608,23 @@ async function applyFixes(results, context) {
618
608
  * @param fixResults - The fix results (empty when --fix was not used).
619
609
  */
620
610
  function displayResults(ctx, results, fixResults) {
621
- results.map((result) => formatResultLine(ctx, result, fixResults));
611
+ const output = results.map((result) => formatResultLine(result, fixResults)).join("");
612
+ if (output.length > 0) ctx.output.raw(output);
622
613
  }
623
614
  /**
624
- * Format and display a single check result line with optional hint.
615
+ * Format a single check result line with optional hint.
625
616
  *
626
617
  * @private
627
- * @param ctx - The command context.
628
- * @param result - The check result to display.
618
+ * @param result - The check result to format.
629
619
  * @param fixResults - The fix results to check for applied fixes.
620
+ * @returns The formatted result line string.
630
621
  */
631
- function formatResultLine(ctx, result, fixResults) {
622
+ function formatResultLine(result, fixResults) {
632
623
  const appliedFix = fixResults.find((f) => f.name === result.name && f.fixed);
633
- if (appliedFix) {
634
- ctx.output.raw(` ${formatDisplayStatus("fix")} ${result.name} - ${appliedFix.message}\n`);
635
- return;
636
- }
637
- ctx.output.raw(` ${formatDisplayStatus(result.status)} ${result.name} - ${result.message}\n`);
638
- if (result.hint && result.status !== "pass") ctx.output.raw(` ${pc.dim(`→ ${result.hint}`)}\n`);
624
+ if (appliedFix) return ` ${formatDisplayStatus("fix")} ${result.name} - ${appliedFix.message}\n`;
625
+ const line = ` ${formatDisplayStatus(result.status)} ${result.name} - ${result.message}\n`;
626
+ if (result.hint && result.status !== "pass") return `${line} ${pc.dim(`→ ${result.hint}`)}\n`;
627
+ return line;
639
628
  }
640
629
  /**
641
630
  * Format a display status with color.
@@ -1,4 +1,4 @@
1
- import { n as renderTemplate, t as writeFiles } from "../write-DDGnajpV.mjs";
1
+ import { n as isKebabCase, r as renderTemplate, t as writeFiles } from "../write-dfZMTbtn.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import { command } from "@kidd-cli/core";
4
4
  import { dirname, join } from "node:path";
@@ -7,7 +7,6 @@ import { z } from "zod";
7
7
  import { attempt } from "@kidd-cli/utils/fp";
8
8
 
9
9
  //#region src/commands/init.ts
10
- const KEBAB_CASE_CHARS_RE = /^[a-z][\da-z-]*$/;
11
10
  const initCommand = command({
12
11
  args: z.object({
13
12
  description: z.string().describe("Project description").optional(),
@@ -59,19 +58,6 @@ const initCommand = command({
59
58
  }
60
59
  });
61
60
  /**
62
- * Check whether a string is valid kebab-case.
63
- *
64
- * @param value - The string to validate.
65
- * @returns True when the string is kebab-case.
66
- * @private
67
- */
68
- function isKebabCase(value) {
69
- if (!KEBAB_CASE_CHARS_RE.test(value)) return false;
70
- if (value.endsWith("-")) return false;
71
- if (value.includes("--")) return false;
72
- return true;
73
- }
74
- /**
75
61
  * Resolve the project name from args or prompt.
76
62
  *
77
63
  * @param ctx - Command context.
@@ -141,7 +127,7 @@ async function resolvePackageManager(ctx) {
141
127
  * @private
142
128
  */
143
129
  async function resolveIncludeExample(ctx) {
144
- if (ctx.args.example !== void 0 && ctx.args.example !== null) return ctx.args.example;
130
+ if (ctx.args.example !== void 0) return ctx.args.example;
145
131
  return ctx.prompts.confirm({
146
132
  initialValue: true,
147
133
  message: "Include example command?"
@@ -0,0 +1,14 @@
1
+ //#region src/lib/config-helpers.ts
2
+ /**
3
+ * Extract a KiddConfig from a load result, falling back to empty defaults.
4
+ *
5
+ * @param result - The result from loadConfig, or null when loading failed.
6
+ * @returns The loaded config or an empty object (all KiddConfig fields are optional).
7
+ */
8
+ function extractConfig(result) {
9
+ if (result) return result.config;
10
+ return {};
11
+ }
12
+
13
+ //#endregion
14
+ export { extractConfig as t };
@@ -1,6 +1,7 @@
1
1
  import { join } from "node:path";
2
- import { access, readFile } from "node:fs/promises";
2
+ import { readFile } from "node:fs/promises";
3
3
  import { attemptAsync, ok, toErrorMessage } from "@kidd-cli/utils/fp";
4
+ import { fileExists } from "@kidd-cli/utils/fs";
4
5
 
5
6
  //#region src/lib/detect.ts
6
7
  /**
@@ -57,17 +58,6 @@ async function readPackageJson(filePath) {
57
58
  }, null];
58
59
  }
59
60
  }
60
- /**
61
- * Check whether a path exists on disk.
62
- *
63
- * @param filePath - The path to check.
64
- * @returns True when the path is accessible, false otherwise.
65
- * @private
66
- */
67
- async function fileExists(filePath) {
68
- const [err] = await attemptAsync(() => access(filePath));
69
- return err === null;
70
- }
71
61
 
72
62
  //#endregion
73
63
  export { detectProject as t };
@@ -1,6 +1,7 @@
1
1
  import { dirname, join, relative } from "node:path";
2
- import { access, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
3
- import { attemptAsync, ok, toErrorMessage } from "@kidd-cli/utils/fp";
2
+ import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
3
+ import { ok, toErrorMessage } from "@kidd-cli/utils/fp";
4
+ import { fileExists } from "@kidd-cli/utils/fs";
4
5
  import { Liquid } from "liquidjs";
5
6
 
6
7
  //#region src/lib/render.ts
@@ -92,6 +93,22 @@ function isGenerateError(value) {
92
93
  return "type" in value && "message" in value;
93
94
  }
94
95
 
96
+ //#endregion
97
+ //#region src/lib/validate.ts
98
+ const KEBAB_CASE_CHARS_RE = /^[a-z][\da-z-]*$/;
99
+ /**
100
+ * Check whether a string is valid kebab-case.
101
+ *
102
+ * @param value - The string to validate.
103
+ * @returns True when the string is kebab-case.
104
+ */
105
+ function isKebabCase(value) {
106
+ if (!KEBAB_CASE_CHARS_RE.test(value)) return false;
107
+ if (value.endsWith("-")) return false;
108
+ if (value.includes("--")) return false;
109
+ return true;
110
+ }
111
+
95
112
  //#endregion
96
113
  //#region src/lib/write.ts
97
114
  /**
@@ -105,18 +122,13 @@ function isGenerateError(value) {
105
122
  * @returns An async Result with counts of written/skipped files or a GenerateError.
106
123
  */
107
124
  async function writeFiles(params) {
108
- const written = [];
109
- const skipped = [];
110
125
  const results = await Promise.all(params.files.map((file) => writeSingleFile(file, params.outputDir, params.overwrite)));
111
126
  const firstError = results.find((r) => r[0] !== null);
112
127
  if (firstError) return [firstError[0], null];
113
128
  const validStatuses = results.filter((r) => r[1] !== null);
114
- const writtenPaths = validStatuses.filter(([, status]) => status.action === "written").map(([, status]) => status.path);
115
- const skippedPaths = validStatuses.filter(([, status]) => status.action === "skipped").map(([, status]) => status.path);
116
- writtenPaths.map((p) => written.push(p));
117
- skippedPaths.map((p) => skipped.push(p));
129
+ const written = validStatuses.filter(([, status]) => status.action === "written").map(([, status]) => status.path);
118
130
  return ok({
119
- skipped,
131
+ skipped: validStatuses.filter(([, status]) => status.action === "skipped").map(([, status]) => status.path),
120
132
  written
121
133
  });
122
134
  }
@@ -150,17 +162,6 @@ async function writeSingleFile(file, outputDir, overwrite) {
150
162
  }, null];
151
163
  }
152
164
  }
153
- /**
154
- * Check whether a path exists on disk.
155
- *
156
- * @param filePath - The path to check.
157
- * @returns True when the path is accessible, false otherwise.
158
- * @private
159
- */
160
- async function fileExists(filePath) {
161
- const [err] = await attemptAsync(() => access(filePath));
162
- return err === null;
163
- }
164
165
 
165
166
  //#endregion
166
- export { renderTemplate as n, writeFiles as t };
167
+ export { isKebabCase as n, renderTemplate as r, writeFiles as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kidd-cli/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "DX companion CLI for the kidd framework",
5
5
  "keywords": [
6
6
  "cli",
@@ -23,13 +23,13 @@
23
23
  "type": "module",
24
24
  "dependencies": {
25
25
  "fs-extra": "^11.3.3",
26
- "liquidjs": "^10.24.0",
26
+ "liquidjs": "^10.25.0",
27
27
  "picocolors": "^1.1.1",
28
28
  "zod": "^4.3.6",
29
- "@kidd-cli/bundler": "0.1.1",
30
- "@kidd-cli/config": "0.1.1",
31
- "@kidd-cli/core": "0.1.2",
32
- "@kidd-cli/utils": "0.1.1"
29
+ "@kidd-cli/bundler": "0.1.2",
30
+ "@kidd-cli/config": "0.1.2",
31
+ "@kidd-cli/core": "0.2.0",
32
+ "@kidd-cli/utils": "0.1.2"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/fs-extra": "^11.0.4",
@@ -43,6 +43,7 @@
43
43
  "lint": "oxlint --ignore-pattern node_modules",
44
44
  "lint:fix": "oxlint --fix --ignore-pattern node_modules",
45
45
  "test": "vitest run",
46
+ "test:coverage": "vitest run --coverage",
46
47
  "test:watch": "vitest"
47
48
  }
48
49
  }