mick-templates 1.1.3 → 1.1.5

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/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { spawn } from "child_process";
3
3
  import { dirname, join } from "path";
4
4
  import { fileURLToPath } from "url";
5
+ import { getUserAgent } from "package-manager-detector";
5
6
 
6
7
  // Get the directory where this script is located
7
8
  const __filename = fileURLToPath(import.meta.url);
@@ -9,8 +10,25 @@ const binDir = dirname(__filename);
9
10
  const projectRoot = join(binDir, "..");
10
11
  const cliPath = join(projectRoot, "src", "index.tsx");
11
12
 
12
- // Use npx to run tsx (it will find tsx from the package dependencies)
13
- const child = spawn("npx", ["tsx", cliPath, ...process.argv.slice(2)], {
13
+ // Detect the package manager agent
14
+ const agent = getUserAgent() ?? "npm";
15
+
16
+ let command;
17
+ let args;
18
+
19
+ if (agent === "bun") {
20
+ command = "bun";
21
+ args = [cliPath, ...process.argv.slice(2)];
22
+ } else {
23
+ // Use the local tsx from the package's node_modules
24
+ const isWin = process.platform === "win32";
25
+ const tsxBin = join(projectRoot, "node_modules", ".bin", "tsx");
26
+ command = isWin ? `${tsxBin}.cmd` : tsxBin;
27
+ args = [cliPath, ...process.argv.slice(2)];
28
+ }
29
+
30
+ // Spawn the command
31
+ const child = spawn(command, args, {
14
32
  stdio: "inherit",
15
33
  cwd: process.cwd(),
16
34
  env: process.env,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mick-templates",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "CLI tool for creating projects from templates",
5
5
  "bin": {
6
6
  "mick-templates": "bin/cli.js"
@@ -8,8 +8,7 @@
8
8
  "files": [
9
9
  "templates",
10
10
  "src",
11
- "bin",
12
- "ts-to-js-codemod.js"
11
+ "bin"
13
12
  ],
14
13
  "type": "module",
15
14
  "scripts": {
@@ -25,12 +24,11 @@
25
24
  "author": "Mick",
26
25
  "license": "MIT",
27
26
  "dependencies": {
28
- "execa": "^9.5.2",
29
27
  "glob": "^10.3.10",
30
28
  "ink": "^4.4.1",
31
29
  "ink-select-input": "^5.0.0",
32
30
  "jscodeshift": "^17.3.0",
33
- "picocolors": "^1.1.1",
31
+ "package-manager-detector": "^1.6.0",
34
32
  "react": "^18.2.0",
35
33
  "tsx": "^4.21.0"
36
34
  },
@@ -2,8 +2,7 @@ import { readFile, writeFile, rename, rm } from "fs/promises";
2
2
  import { glob } from "glob";
3
3
  import { join, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
- import { execa } from "execa";
6
- import { getPMX, detectPM } from "./pm.js";
5
+ import { execute } from "./pm.js";
7
6
 
8
7
  const TS_DEPS = [
9
8
  "typescript",
@@ -18,38 +17,27 @@ const TS_DEPS = [
18
17
  * Convert a TypeScript project to JavaScript.
19
18
  */
20
19
  export async function convertToJS(targetDir: string): Promise<void> {
21
- // Remove tsconfig.json
22
20
  await rm(join(targetDir, "tsconfig.json"), { force: true });
23
21
 
24
- // Rename vite.config.ts -> vite.config.js
25
22
  await renameIfExists(
26
23
  join(targetDir, "vite.config.ts"),
27
24
  join(targetDir, "vite.config.js")
28
25
  );
29
26
 
30
- // Rename all .tsx -> .jsx
31
27
  const tsxFiles = await glob("**/*.tsx", { cwd: targetDir });
32
28
  for (const file of tsxFiles) {
33
29
  const oldPath = join(targetDir, file);
34
- const newPath = oldPath.replace(/\.tsx$/, ".jsx");
35
- await rename(oldPath, newPath);
30
+ await rename(oldPath, oldPath.replace(/\.tsx$/, ".jsx"));
36
31
  }
37
32
 
38
- // Rename all .ts -> .js
39
33
  const tsFiles = await glob("**/*.ts", { cwd: targetDir });
40
34
  for (const file of tsFiles) {
41
35
  const oldPath = join(targetDir, file);
42
- const newPath = oldPath.replace(/\.ts$/, ".js");
43
- await rename(oldPath, newPath);
36
+ await rename(oldPath, oldPath.replace(/\.ts$/, ".js"));
44
37
  }
45
38
 
46
- // Update HTML files to reference .jsx instead of .tsx
47
39
  await updateHTMLScripts(targetDir);
48
-
49
- // Run jscodeshift on all JS/JSX files to strip types
50
40
  await runCodemod(targetDir);
51
-
52
- // Clean up package.json
53
41
  await cleanPackageJSON(targetDir);
54
42
  }
55
43
 
@@ -57,7 +45,7 @@ async function renameIfExists(from: string, to: string): Promise<void> {
57
45
  try {
58
46
  await rename(from, to);
59
47
  } catch {
60
- // File doesn't exist, ignore
48
+ // File doesn't exist
61
49
  }
62
50
  }
63
51
 
@@ -77,20 +65,11 @@ async function runCodemod(targetDir: string): Promise<void> {
77
65
  if (jsFiles.length === 0) return;
78
66
 
79
67
  const __dirname = dirname(fileURLToPath(import.meta.url));
80
- const codemodPath = join(__dirname, "..", "..", "ts-to-js-codemod.js");
81
- const pmx = getPMX(detectPM());
82
- const [cmd, ...baseArgs] = pmx.split(" ");
68
+ const codemodPath = join(__dirname, "..", "ts-to-js-codemod.mjs");
83
69
 
84
70
  for (const file of jsFiles) {
85
71
  const filePath = join(targetDir, file);
86
- await execa(
87
- cmd,
88
- [...baseArgs, "jscodeshift", "-t", codemodPath, "--parser=tsx", filePath],
89
- {
90
- cwd: targetDir,
91
- stdio: "pipe",
92
- }
93
- );
72
+ await execute(targetDir, "jscodeshift", ["-t", codemodPath, "--parser=tsx", filePath]);
94
73
  }
95
74
  }
96
75
 
@@ -99,19 +78,17 @@ async function cleanPackageJSON(targetDir: string): Promise<void> {
99
78
  try {
100
79
  const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
101
80
 
102
- // Remove TS dependencies
103
81
  for (const dep of TS_DEPS) {
104
82
  delete pkg.dependencies?.[dep];
105
83
  delete pkg.devDependencies?.[dep];
106
84
  }
107
85
 
108
- // Update build script to remove tsc
109
86
  if (pkg.scripts?.build) {
110
87
  pkg.scripts.build = pkg.scripts.build.replace("tsc -b && ", "");
111
88
  }
112
89
 
113
90
  await writeFile(pkgPath, JSON.stringify(pkg, null, 2));
114
91
  } catch {
115
- // No package.json, ignore
92
+ // No package.json
116
93
  }
117
94
  }
package/src/lib/pm.ts CHANGED
@@ -1,95 +1,49 @@
1
- import { execa } from "execa";
2
- import { dirname, join } from "path";
3
-
4
- export type PackageManager = "npm" | "yarn" | "pnpm" | "bun" | "deno";
5
-
6
- /**
7
- * Detect the package manager based on how the CLI was invoked.
8
- * Checks npm_config_user_agent first (set by npx/bunx/etc),
9
- * then falls back to checking the runtime executable.
10
- */
11
- export function detectPM(): PackageManager {
12
- const ua = process.env.npm_config_user_agent || "";
13
-
14
- if (ua.startsWith("bun/")) return "bun";
15
- if (ua.startsWith("yarn/")) return "yarn";
16
- if (ua.startsWith("pnpm/")) return "pnpm";
17
- if (ua.startsWith("deno/")) return "deno";
18
- if (ua.startsWith("npm/")) return "npm";
19
-
20
- // Fallback: check runtime executable
21
- const runtime = process.argv0.toLowerCase();
22
- if (runtime.includes("bun")) return "bun";
23
- if (runtime.includes("deno")) return "deno";
24
-
25
- return "npm";
26
- }
1
+ import { getUserAgent, resolveCommand } from "package-manager-detector";
2
+ import type { AgentName } from "package-manager-detector";
3
+ import { spawn } from "child_process";
27
4
 
28
5
  /**
29
- * Get the executable path for the package manager.
30
- * Uses the runtime path if available for better reliability.
6
+ * Get the current package manager agent.
31
7
  */
32
- function getPMExecutable(pm: PackageManager): string {
33
- const runtime = process.argv0;
34
-
35
- // If the runtime matches the PM, use it directly (handles custom paths like ~/.bun/bin/bun)
36
- if (pm === "bun" && runtime.toLowerCase().includes("bun")) return runtime;
37
- if (pm === "deno" && runtime.toLowerCase().includes("deno")) return runtime;
38
-
39
- return pm;
8
+ export function getAgent(): AgentName {
9
+ return getUserAgent() ?? "npm";
40
10
  }
41
11
 
42
12
  /**
43
- * Get the install command for a package manager.
13
+ * Run a command with the detected package manager.
44
14
  */
45
- export function getInstallCmd(pm: PackageManager): string[] {
46
- const exe = getPMExecutable(pm);
47
-
48
- switch (pm) {
49
- case "bun":
50
- return [exe, "install"];
51
- case "yarn":
52
- return [exe, "install"];
53
- case "pnpm":
54
- return [exe, "install"];
55
- case "deno":
56
- return [exe, "install"];
57
- default:
58
- return ["npm", "install"];
59
- }
15
+ function run(cmd: string, args: string[], cwd: string): Promise<void> {
16
+ return new Promise((resolve, reject) => {
17
+ spawn(cmd, args, { cwd, stdio: "inherit" })
18
+ .on("close", (code) =>
19
+ code === 0 ? resolve() : reject(new Error(`Exit ${code}`))
20
+ )
21
+ .on("error", reject);
22
+ });
60
23
  }
61
24
 
62
25
  /**
63
- * Get the package runner (npx, bunx, etc.) for a package manager.
26
+ * Install dependencies in a directory.
64
27
  */
65
- export function getPMX(pm: PackageManager): string {
66
- const runtime = process.argv0;
67
-
68
- // For bun, use bunx from the same directory as the runtime
69
- if (pm === "bun" && runtime.toLowerCase().includes("bun")) {
70
- const bunDir = dirname(runtime);
71
- return join(bunDir, "bunx");
72
- }
28
+ export async function install(cwd: string): Promise<void> {
29
+ const agent = getAgent();
30
+ const resolved = resolveCommand(agent, "install", []);
31
+ if (!resolved) throw new Error(`Cannot resolve install command for ${agent}`);
73
32
 
74
- switch (pm) {
75
- case "bun":
76
- return "bunx";
77
- case "yarn":
78
- return "yarn dlx";
79
- case "pnpm":
80
- return "pnpm dlx";
81
- case "deno":
82
- return "deno run -A npm:";
83
- default:
84
- return "npx";
85
- }
33
+ await run(resolved.command, resolved.args, cwd);
86
34
  }
87
35
 
88
36
  /**
89
- * Run the install command in a directory.
37
+ * Execute a package binary (npx/bunx/etc).
90
38
  */
91
- export async function install(cwd: string): Promise<void> {
92
- const pm = detectPM();
93
- const [cmd, ...args] = getInstallCmd(pm);
94
- await execa(cmd, args, { cwd, stdio: "inherit" });
39
+ export async function execute(
40
+ cwd: string,
41
+ pkg: string,
42
+ args: string[]
43
+ ): Promise<void> {
44
+ const agent = getAgent();
45
+ const resolved = resolveCommand(agent, "execute", [pkg, ...args]);
46
+ if (!resolved) throw new Error(`Cannot resolve execute command for ${agent}`);
47
+
48
+ await run(resolved.command, resolved.args, cwd);
95
49
  }
@@ -1,5 +1,5 @@
1
1
  // Transform TypeScript files to JavaScript
2
- module.exports = function (fileInfo, api) {
2
+ export default function (fileInfo, api) {
3
3
  const j = api.jscodeshift;
4
4
  const root = j(fileInfo.source);
5
5
 
@@ -16,4 +16,5 @@ module.exports = function (fileInfo, api) {
16
16
  });
17
17
 
18
18
  return root.toSource();
19
- };
19
+ }
20
+
@@ -21,7 +21,13 @@
21
21
  "noUnusedParameters": true,
22
22
  "erasableSyntaxOnly": true,
23
23
  "noFallthroughCasesInSwitch": true,
24
- "noUncheckedSideEffectImports": true
24
+ "noUncheckedSideEffectImports": true,
25
+
26
+ // Paths
27
+ "baseUrl": ".",
28
+ "paths": {
29
+ "@/*": ["./src/*"],
30
+ },
25
31
  },
26
- "include": ["src", "vite.config.ts"]
32
+ "include": ["src", "vite.config.ts"],
27
33
  }
@@ -1,7 +1,13 @@
1
+ import path from "node:path";
1
2
  import react from "@vitejs/plugin-react";
2
3
  import { defineConfig } from "vite";
3
4
 
4
5
  // https://vite.dev/config/
5
6
  export default defineConfig({
6
7
  plugins: [react()],
8
+ resolve: {
9
+ alias: {
10
+ "@": path.resolve(__dirname, "./src"),
11
+ },
12
+ },
7
13
  });