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
|
-
//
|
|
13
|
-
const
|
|
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
|
+
"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
|
-
"
|
|
31
|
+
"package-manager-detector": "^1.6.0",
|
|
34
32
|
"react": "^18.2.0",
|
|
35
33
|
"tsx": "^4.21.0"
|
|
36
34
|
},
|
package/src/lib/convert.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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, "..", "
|
|
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
|
|
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
|
|
92
|
+
// No package.json
|
|
116
93
|
}
|
|
117
94
|
}
|
package/src/lib/pm.ts
CHANGED
|
@@ -1,95 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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
|
|
30
|
-
* Uses the runtime path if available for better reliability.
|
|
6
|
+
* Get the current package manager agent.
|
|
31
7
|
*/
|
|
32
|
-
function
|
|
33
|
-
|
|
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
|
-
*
|
|
13
|
+
* Run a command with the detected package manager.
|
|
44
14
|
*/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
*
|
|
26
|
+
* Install dependencies in a directory.
|
|
64
27
|
*/
|
|
65
|
-
export function
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
37
|
+
* Execute a package binary (npx/bunx/etc).
|
|
90
38
|
*/
|
|
91
|
-
export async function
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
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
|
});
|