@mbsi/mkcmd 0.2.3 → 0.2.4

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/CHANGELOG.md CHANGED
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.0] - 2026-1-1
11
+
12
+ ### Added
13
+
14
+ - `WatchConfig` interface in `src/config.ts` for defining watch targets
15
+ - Support for both `"dir"` and `"file"` types in watch configurations
16
+ - `scaffold-rest.ts` function for handling arbitrary watch targets
17
+ - `src/data/index.ts` for centralized data exports
18
+
19
+ ### Changed
20
+
21
+ - `stringifier.ts` now reads from `config.watchConfigs` instead of hardcoded paths
22
+ - `stringifier.ts` supports watching individual files and multiple directories
23
+ - `parseFolder` renamed and refactored to support both directory and file processing
24
+ - `stringifier.ts` currently only supports typescript and md files - other file types may need special file handling.
25
+ - `README.md` to match current export structure.
26
+
27
+ ### Fixed
28
+ The following are marked under 0.2.4 in git, but are released in 0.3.0+
29
+ - `src/functions/scaffold-core.ts` - using `cwd` to handle folder creation, matching the relative pathing intimated by the clack prompt masking.
30
+ - `src/functions/scaffold-project.ts` - same as above
31
+ - `src/README.md` - finished styling directory markup
32
+
10
33
  ## [0.2.3] - 2025-12-30
11
34
 
12
35
  ### Fixed
package/README.md CHANGED
@@ -11,7 +11,7 @@ A CLI tool for scaffolding new CLI projects with sensible defaults. Create comma
11
11
  - **Extensible** - Easy to add your own commands
12
12
 
13
13
  > [!WARNING]
14
- > While you can *run* the program in without Bun, the source code itself **depends on Bun** for development and building.
14
+ > While you can *run* the program in without Bun, the source code itself **depends on Bun** for development and building
15
15
 
16
16
  ## Run remotely
17
17
 
@@ -52,7 +52,8 @@ my-cli/
52
52
  │ │ ├── log.ts # Logging helpers (single/multi info/warn/err, title)
53
53
  │ │ └── helpers/
54
54
  │ │ ├── file-builder.ts # Indentation-aware file builder
55
- │ │ └── file-utils.ts # Path and file writing utilities
55
+ │ │ ├── file-utils.ts # Path and file writing utilities
56
+ │ │ └── stringifier.ts # Dynamic template code generation
56
57
  │ ├── commands/
57
58
  │ │ └── index.ts # Command registration hook
58
59
  │ └── config.ts # Project configuration
package/dist/index.js CHANGED
@@ -1586,12 +1586,21 @@ var log = {
1586
1586
  var log_default = log;
1587
1587
 
1588
1588
  // src/core/cli.ts
1589
- import { join as join2 } from "path";
1589
+ import { join as join3 } from "path";
1590
1590
 
1591
1591
  // src/config.ts
1592
+ import { join as join2 } from "path";
1592
1593
  var config = {
1593
1594
  about_text: "mkcmd is a remote node executable for scaffolding other remote node executables with sensible defaults.",
1594
- more_info_text: "See https://github.com/mackenziebowes/mkcmd for more details."
1595
+ more_info_text: "See https://github.com/mackenziebowes/mkcmd for more details.",
1596
+ watchConfigs: [
1597
+ {
1598
+ name: "core",
1599
+ type: "dir",
1600
+ sourcePath: join2(".", "src", "core"),
1601
+ outputPath: join2(".", "src", "data", "core.ts")
1602
+ }
1603
+ ]
1595
1604
  };
1596
1605
 
1597
1606
  // src/core/cli.ts
@@ -1631,7 +1640,7 @@ async function runCLI(argv = Bun.argv.slice(2)) {
1631
1640
  return;
1632
1641
  }
1633
1642
  if (["-v", "--version"].includes(name)) {
1634
- const pkgPath = join2(import.meta.dir, "..", "package.json");
1643
+ const pkgPath = join3(import.meta.dir, "..", "package.json");
1635
1644
  const pkgText = await Bun.file(pkgPath).text();
1636
1645
  const pkg = JSON.parse(pkgText);
1637
1646
  log_default.multi.info([
@@ -2125,10 +2134,10 @@ async function promptProjectDetails() {
2125
2134
 
2126
2135
  // src/core/helpers/file-utils.ts
2127
2136
  import { mkdir } from "fs/promises";
2128
- import { resolve, join as join3 } from "path";
2137
+ import { resolve, join as join4 } from "path";
2129
2138
  async function writeFileTuple([targetDir, relativePath, content]) {
2130
2139
  const fullPath = resolve(targetDir, relativePath);
2131
- await mkdir(join3(fullPath, ".."), { recursive: true });
2140
+ await mkdir(join4(fullPath, ".."), { recursive: true });
2132
2141
  await Bun.write(fullPath, content);
2133
2142
  }
2134
2143
 
@@ -2157,11 +2166,33 @@ var commands_init = () => {
2157
2166
  };
2158
2167
  var config_init = (about) => {
2159
2168
  const file = new FileBuilder;
2160
- file.addLine(`export const config = {`, 0);
2169
+ file.addLine(`import { join } from "node:path";`);
2170
+ file.addEmptyLine();
2171
+ file.addLine(`export interface WatchConfig {`);
2172
+ file.addLine(`name: string;`, 1);
2173
+ file.addLine(`type: "dir" | "file";`, 1);
2174
+ file.addLine(`sourcePath: string;`, 1);
2175
+ file.addLine(`outputPath: string;`, 1);
2176
+ file.addLine(`}`);
2177
+ file.addEmptyLine();
2178
+ file.addLine(`type Config = {`);
2179
+ file.addLine(`about_text: string;`, 1);
2180
+ file.addLine(`more_info_text: string;`, 1);
2181
+ file.addLine(`watchConfigs: WatchConfig[];`, 1);
2182
+ file.addLine(`}`);
2183
+ file.addLine(`export const config: Config = {`, 0);
2161
2184
  file.addLine(`about_text:`, 2);
2162
2185
  file.addLine(`"${about}",`, 1);
2163
2186
  file.addLine(`more_info_text:`, 2);
2164
2187
  file.addLine(`"See https://github.com/mackenziebowes/mkcmd for more details.",`, 1);
2188
+ file.addLine(`watchConfigs: [`, 1);
2189
+ file.addLine(`{`, 2);
2190
+ file.addLine(`name: "core",`, 3);
2191
+ file.addLine(`type: "dir",`, 3);
2192
+ file.addLine(`sourcePath: join(".", "src", "core"),`, 3);
2193
+ file.addLine(`outputPath: join(".", "src", "data", "core.ts"),`, 3);
2194
+ file.addLine(`},`, 2);
2195
+ file.addLine(`],`, 1);
2165
2196
  file.addLine(`};`, 0);
2166
2197
  return file.build();
2167
2198
  };
@@ -2251,10 +2282,10 @@ async function scaffoldProject(prompts) {
2251
2282
  }
2252
2283
 
2253
2284
  // src/functions/scaffold-core.ts
2254
- import { join as join4, dirname as dirname2 } from "path";
2285
+ import { join as join5, dirname as dirname2 } from "path";
2286
+ import { cwd } from "process";
2255
2287
  import { existsSync } from "fs";
2256
2288
  import { mkdir as mkdir2, writeFile } from "fs/promises";
2257
- import { cwd } from "process";
2258
2289
 
2259
2290
  // src/data/core.ts
2260
2291
  var src_core_cli_init = () => {
@@ -2485,10 +2516,10 @@ var src_core_helpers_stringifier_init = () => {
2485
2516
  fb.addLine('import { readdir, writeFile } from "node:fs/promises";');
2486
2517
  fb.addLine('import { FileBuilder } from "./file-builder";');
2487
2518
  fb.addLine('import { statSync } from "node:fs";');
2519
+ fb.addLine('import { cwd } from "node:process";');
2520
+ fb.addLine('import { config } from "../../config";');
2488
2521
  fb.addLine("");
2489
- fb.addLine("const outExport: string[] = [];");
2490
- fb.addLine("");
2491
- fb.addLine("async function generateTemplate(path: string) {");
2522
+ fb.addLine("async function generateTemplateFromTs(path: string) {");
2492
2523
  fb.addLine(' if (path.endsWith(".ts")) {');
2493
2524
  fb.addLine(" const content = await Bun.file(path).text();");
2494
2525
  fb.addLine(" const fb = new FileBuilder();");
@@ -2498,6 +2529,8 @@ var src_core_helpers_stringifier_init = () => {
2498
2529
  fb.addLine(' .split("-")');
2499
2530
  fb.addLine(' .join("_")');
2500
2531
  fb.addLine(' .split(".")[0];');
2532
+ fb.addLine(" if (!snaked_title)");
2533
+ fb.addLine(" throw new Error(`Failed to generate identifier for path: ${path}`);");
2501
2534
  fb.addLine(' fb.addLine(`const ${snaked_title + "_init"} = () => {`);');
2502
2535
  fb.addLine(" fb.addLine(`const fb = new FileBuilder();`, 1);");
2503
2536
  fb.addLine(' const lines = content.split("\\n");');
@@ -2508,42 +2541,135 @@ var src_core_helpers_stringifier_init = () => {
2508
2541
  fb.addLine(' fb.addLine("return fb.build();", 1);');
2509
2542
  fb.addLine(' fb.addLine("};");');
2510
2543
  fb.addLine(" fb.addEmptyLine();");
2511
- fb.addLine(" fb.addLine(`export const ${snaked_title} = {`);");
2544
+ fb.addLine(" fb.addLine(`const ${snaked_title} = {`);");
2512
2545
  fb.addLine(' fb.addLine(`location: "${path}",`, 1);');
2513
2546
  fb.addLine(' fb.addLine(`content: ${snaked_title + "_init"}`, 1);');
2514
2547
  fb.addLine(" fb.addLine(`};`);");
2515
2548
  fb.addLine(" const out = fb.build();");
2516
- fb.addLine(" outExport.push(`${snaked_title}`);");
2517
- fb.addLine(" return out;");
2549
+ fb.addLine(" return { name: snaked_title, content: out };");
2518
2550
  fb.addLine(" }");
2519
- fb.addLine(' return "";');
2551
+ fb.addLine(" return null;");
2552
+ fb.addLine("}");
2553
+ fb.addLine("");
2554
+ fb.addLine("async function generateTemplateFromMd(path: string) {");
2555
+ fb.addLine(' if (path.endsWith(".md")) {');
2556
+ fb.addLine(" const content = await Bun.file(path).text();");
2557
+ fb.addLine(" const fb = new FileBuilder();");
2558
+ fb.addLine(" const snaked_title = path");
2559
+ fb.addLine(' .split("/")');
2560
+ fb.addLine(' .join("_")');
2561
+ fb.addLine(' .split("-")');
2562
+ fb.addLine(' .join("_")');
2563
+ fb.addLine(' .split(".")[0];');
2564
+ fb.addLine(" if (!snaked_title)");
2565
+ fb.addLine(" throw new Error(`Failed to generate identifier for path: ${path}`);");
2566
+ fb.addLine(' fb.addLine(`const ${snaked_title + "_init"} = () => {`);');
2567
+ fb.addLine(" fb.addLine(`const fb = new FileBuilder();`, 1);");
2568
+ fb.addLine(' const lines = content.split("\\n");');
2569
+ fb.addLine(" for (const line of lines) {");
2570
+ fb.addLine(` const cleanLine = line.replaceAll("\\\\", "\\\\\\\\").replaceAll('"', '\\\\"');`);
2571
+ fb.addLine(' fb.addLine(`fb.addLine("${cleanLine}")`, 1);');
2572
+ fb.addLine(" }");
2573
+ fb.addLine(' fb.addLine("return fb.build();", 1);');
2574
+ fb.addLine(' fb.addLine("};");');
2575
+ fb.addLine(" fb.addEmptyLine();");
2576
+ fb.addLine(" fb.addLine(`const ${snaked_title} = {`);");
2577
+ fb.addLine(' fb.addLine(`location: "${path}",`, 1);');
2578
+ fb.addLine(' fb.addLine(`content: ${snaked_title + "_init"}`, 1);');
2579
+ fb.addLine(" fb.addLine(`};`);");
2580
+ fb.addLine(" const out = fb.build();");
2581
+ fb.addLine(" return { name: snaked_title, content: out };");
2582
+ fb.addLine(" }");
2583
+ fb.addLine(" return null;");
2584
+ fb.addLine("}");
2585
+ fb.addLine("");
2586
+ fb.addLine("async function generateTemplate(path: string) {");
2587
+ fb.addLine(' if (path.endsWith(".ts")) {');
2588
+ fb.addLine(" return generateTemplateFromTs(path);");
2589
+ fb.addLine(" }");
2590
+ fb.addLine(' if (path.endsWith(".md")) {');
2591
+ fb.addLine(" return generateTemplateFromMd(path);");
2592
+ fb.addLine(" }");
2593
+ fb.addLine(" return null;");
2520
2594
  fb.addLine("}");
2521
2595
  fb.addLine("");
2522
2596
  fb.addLine("async function parseFolder(path: string, fb: FileBuilder) {");
2597
+ fb.addLine(" const outExport: string[] = [];");
2523
2598
  fb.addLine(" const items = await readdir(path);");
2524
2599
  fb.addLine(" for (const item of items) {");
2525
2600
  fb.addLine(" const relPath = join(path, item);");
2526
2601
  fb.addLine(" const itemStat = statSync(relPath);");
2527
2602
  fb.addLine(" if (itemStat.isDirectory()) {");
2528
- fb.addLine(" await parseFolder(relPath, fb);");
2603
+ fb.addLine(" const nestedExports = await parseFolder(relPath, fb);");
2604
+ fb.addLine(" outExport.push(...nestedExports);");
2529
2605
  fb.addLine(" }");
2530
2606
  fb.addLine(' if (item.endsWith(".ts")) {');
2531
- fb.addLine(" fb.addLine(await generateTemplate(relPath));");
2532
- fb.addLine(" fb.addEmptyLine();");
2607
+ fb.addLine(" const template = await generateTemplate(relPath);");
2608
+ fb.addLine(" if (template) {");
2609
+ fb.addLine(" fb.addLine(template.content);");
2610
+ fb.addLine(" fb.addEmptyLine();");
2611
+ fb.addLine(" outExport.push(template.name);");
2612
+ fb.addLine(" }");
2533
2613
  fb.addLine(" }");
2534
2614
  fb.addLine(" }");
2615
+ fb.addLine(" return outExport;");
2535
2616
  fb.addLine("}");
2536
2617
  fb.addLine("");
2537
- fb.addLine("async function generateTemplates() {");
2538
- fb.addLine(' const sourceCoreDir = join(".", "src", "core");');
2618
+ fb.addLine("async function exportDataTemplates() {");
2539
2619
  fb.addLine(" const fb = new FileBuilder();");
2540
- fb.addLine(' fb.addLine(`import { FileBuilder } from "../core/helpers/file-builder";`);');
2620
+ fb.addLine(" const exportList: string[] = [];");
2621
+ fb.addLine(' const dataFiles = await readdir(join(cwd(), "src", "data"));');
2622
+ fb.addLine(" for (const file of dataFiles) {");
2623
+ fb.addLine(' if (file == "init.ts" || file == "core.ts" || file == "index.ts") {');
2624
+ fb.addLine(" continue;");
2625
+ fb.addLine(" }");
2626
+ fb.addLine(' const content = await Bun.file(join(cwd(), "src", "data", file)).text();');
2627
+ fb.addLine(' const lines = content.split("\\n");');
2628
+ fb.addLine(" const exportStatement = lines.pop();");
2629
+ fb.addLine(" if (!exportStatement) throw new Error(`Parsed file ${file} is empty`);");
2630
+ fb.addLine(' const functionName = exportStatement.split(" ")[2];');
2631
+ fb.addLine(" if (!functionName)");
2632
+ fb.addLine(" throw new Error(");
2633
+ fb.addLine(" `Parse file ${file} is malformed - missing export statement`,");
2634
+ fb.addLine(" );");
2635
+ fb.addLine(" exportList.push(functionName);");
2636
+ fb.addLine(' fb.addLine(`import { ${functionName} } from "./${file}";`);');
2637
+ fb.addLine(" }");
2541
2638
  fb.addLine(" fb.addEmptyLine();");
2542
- fb.addLine(" await parseFolder(sourceCoreDir, fb);");
2543
- fb.addLine(' fb.addLine(`const core = [${outExport.join(", ")}]`);');
2544
- fb.addLine(" fb.addLine(`export { core }`);");
2639
+ fb.addLine(' fb.addLine("type DataExport = {");');
2640
+ fb.addLine(' fb.addLine("location: string", 1);');
2641
+ fb.addLine(' fb.addLine("content: () => string", 1);');
2642
+ fb.addLine(' fb.addLine("};");');
2643
+ fb.addLine(" fb.addEmptyLine();");
2644
+ fb.addLine(' fb.addLine(`const data: DataExport[] = [${exportList.join(", ")}];`);');
2645
+ fb.addLine(' fb.addLine("export { data }");');
2545
2646
  fb.addLine(" const out = fb.build();");
2546
- fb.addLine(' await writeFile(join(".", "src", "data", "core.ts"), out, "utf8");');
2647
+ fb.addLine(' await writeFile(join(cwd(), "src", "data", "index.ts"), out, "utf8");');
2648
+ fb.addLine("}");
2649
+ fb.addLine("");
2650
+ fb.addLine("async function generateTemplates() {");
2651
+ fb.addLine(" for (const watchConfig of config.watchConfigs) {");
2652
+ fb.addLine(" const fb = new FileBuilder();");
2653
+ fb.addLine(' fb.addLine(`import { FileBuilder } from "../core/helpers/file-builder";`);');
2654
+ fb.addLine(" fb.addEmptyLine();");
2655
+ fb.addLine(' if (watchConfig.type == "dir") {');
2656
+ fb.addLine(" const exports = await parseFolder(watchConfig.sourcePath, fb);");
2657
+ fb.addLine(' fb.addLine(`const ${watchConfig.name} = [${exports.join(", ")}]`);');
2658
+ fb.addLine(" fb.addLine(`export { ${watchConfig.name} }`);");
2659
+ fb.addLine(" const out = fb.build();");
2660
+ fb.addLine(' await writeFile(watchConfig.outputPath, out, "utf8");');
2661
+ fb.addLine(" } else {");
2662
+ fb.addLine(" const template = await generateTemplate(watchConfig.sourcePath);");
2663
+ fb.addLine(" if (template) {");
2664
+ fb.addLine(" fb.addLine(template.content);");
2665
+ fb.addLine(" fb.addEmptyLine();");
2666
+ fb.addLine(" fb.addLine(`export { ${template.name} }`);");
2667
+ fb.addLine(" const out = fb.build();");
2668
+ fb.addLine(' await writeFile(watchConfig.outputPath, out, "utf8");');
2669
+ fb.addLine(" }");
2670
+ fb.addLine(" }");
2671
+ fb.addLine(" }");
2672
+ fb.addLine(" exportDataTemplates();");
2547
2673
  fb.addLine("}");
2548
2674
  fb.addLine("");
2549
2675
  fb.addLine("generateTemplates();");
@@ -2574,18 +2700,43 @@ var src_core_helpers_file_utils = {
2574
2700
  var core = [src_core_cli, src_core_log, src_core_helpers_file_builder, src_core_helpers_stringifier, src_core_helpers_file_utils];
2575
2701
 
2576
2702
  // src/functions/scaffold-core.ts
2577
- var currentDir = join4(cwd());
2578
2703
  async function scaffoldCore(targetDir) {
2579
- const combinedPath = join4(currentDir, targetDir);
2580
- console.dir(combinedPath);
2704
+ const combinedPath = join5(cwd(), targetDir);
2705
+ if (!existsSync(combinedPath)) {
2706
+ await mkdir2(combinedPath, { recursive: true });
2707
+ }
2581
2708
  for (const file of core) {
2582
2709
  const relativePath = file.location.replace(/^\.\//, "");
2583
- const parentDir = join4(combinedPath, dirname2(relativePath));
2584
- console.dir({ parentDir });
2710
+ const parentDir = join5(combinedPath, dirname2(relativePath));
2585
2711
  if (!existsSync(parentDir)) {
2586
- mkdir2(parentDir, { recursive: true });
2712
+ await mkdir2(parentDir, { recursive: true });
2713
+ }
2714
+ await writeFile(join5(combinedPath, relativePath), file.content(), "utf8");
2715
+ }
2716
+ }
2717
+
2718
+ // src/functions/scaffold-rest.ts
2719
+ import { join as join6, dirname as dirname3 } from "path";
2720
+ import { cwd as cwd2 } from "process";
2721
+ import { existsSync as existsSync2 } from "fs";
2722
+ import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
2723
+
2724
+ // src/data/index.ts
2725
+ var data = [];
2726
+
2727
+ // src/functions/scaffold-rest.ts
2728
+ async function scaffoldRest(targetDir) {
2729
+ const combinedPath = join6(cwd2(), targetDir);
2730
+ if (!existsSync2(combinedPath)) {
2731
+ await mkdir3(combinedPath, { recursive: true });
2732
+ }
2733
+ for (const file of data) {
2734
+ const relativePath = file.location.replace(/^\.\//, "");
2735
+ const parentDir = join6(combinedPath, dirname3(relativePath));
2736
+ if (!existsSync2(parentDir)) {
2737
+ await mkdir3(parentDir, { recursive: true });
2587
2738
  }
2588
- writeFile(join4(combinedPath, relativePath), file.content(), "utf8");
2739
+ await writeFile2(join6(combinedPath, relativePath), file.content(), "utf8");
2589
2740
  }
2590
2741
  }
2591
2742
 
@@ -2596,6 +2747,8 @@ async function orchestrateScaffold() {
2596
2747
  log_default.single.info("Project", "Project files created");
2597
2748
  await scaffoldCore(prompts.targetDir);
2598
2749
  log_default.single.info("Core", "Core files copied");
2750
+ await scaffoldRest(prompts.targetDir);
2751
+ log_default.single.info("Rest", "All files copied");
2599
2752
  log_default.multi.info([
2600
2753
  { t: "Success", m: "Project scaffolded successfully!" },
2601
2754
  { t: "Location", m: prompts.targetDir },
package/package.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
- "name": "@mbsi/mkcmd",
3
- "version": "0.2.3",
4
- "main": "./dist/index.js",
5
- "module": "./dist/index.js",
6
- "type": "module",
7
- "bin": {
8
- "mkcmd": "./dist/index.js"
9
- },
10
- "scripts": {
11
- "prebuild": "bun run src/core/helpers/stringifier.ts",
12
- "build": "bun build --target=bun --outfile=dist/index.js src/index.ts",
13
- "build:exe": "bun build --compile --outfile=dist/mkcmd src/index.ts",
14
- "prepack": "bun run build"
15
- },
16
- "devDependencies": {
17
- "@types/bun": "latest",
18
- "typescript": "^5"
19
- },
20
- "dependencies": {
21
- "@clack/prompts": "^0.11.0",
22
- "@types/figlet": "^1.7.0",
23
- "figlet": "^1.9.4"
24
- }
2
+ "name": "@mbsi/mkcmd",
3
+ "version": "0.2.4",
4
+ "main": "./dist/index.js",
5
+ "module": "./dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "mkcmd": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "prebuild": "bun run src/core/helpers/stringifier.ts",
12
+ "build": "bun build --target=bun --outfile=dist/index.js src/index.ts",
13
+ "build:exe": "bun build --compile --outfile=dist/mkcmd src/index.ts",
14
+ "prepack": "bun run build"
15
+ },
16
+ "devDependencies": {
17
+ "@types/bun": "latest",
18
+ "typescript": "^5"
19
+ },
20
+ "dependencies": {
21
+ "@clack/prompts": "^0.11.0",
22
+ "@types/figlet": "^1.7.0",
23
+ "figlet": "^1.9.4"
24
+ }
25
25
  }