mick-templates 1.1.6 → 1.1.7
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/README.md +37 -3
- package/bin/cli.js +4 -4
- package/package.json +3 -2
- package/src/app.tsx +6 -6
- package/src/components/{LanguageSelect.tsx → language-select.tsx} +2 -6
- package/src/components/{OverwritePrompt.tsx → overwrite-prompt.tsx} +0 -2
- package/src/components/{Status.tsx → status.tsx} +0 -2
- package/src/index.tsx +21 -10
- package/src/lib/constants.ts +11 -0
- package/src/lib/convert.ts +9 -4
- package/src/lib/pm.ts +23 -14
- package/src/lib/template.ts +13 -4
- package/src/ts-to-js-codemod.mjs +1 -2
package/README.md
CHANGED
|
@@ -1,9 +1,43 @@
|
|
|
1
1
|
# mick-templates
|
|
2
2
|
|
|
3
|
-
a minimal, clean, and robust cli
|
|
3
|
+
a minimal, clean, and robust cli for creating projects from templates. built with bun, typescript, and ink.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
# install globally
|
|
9
|
+
npm install -g mick-templates
|
|
10
|
+
# or
|
|
11
|
+
yarn global add mick-templates
|
|
12
|
+
# or
|
|
13
|
+
pnpm add -g mick-templates
|
|
14
|
+
# or
|
|
15
|
+
bun add -g mick-templates
|
|
9
16
|
```
|
|
17
|
+
|
|
18
|
+
## usage
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
mick-templates <template> [name] [options]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### options
|
|
25
|
+
|
|
26
|
+
- `--lang <js|ts>`: choose language (default: typescript)
|
|
27
|
+
- `--skip-install`: skip automatic dependency installation
|
|
28
|
+
|
|
29
|
+
### examples
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# create a new vite project
|
|
33
|
+
mick-templates vite my-app
|
|
34
|
+
|
|
35
|
+
# create with javascript
|
|
36
|
+
mick-templates vite my-app --lang js
|
|
37
|
+
|
|
38
|
+
# skip dependency installation
|
|
39
|
+
mick-templates vite my-app --skip-install
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## license
|
|
43
|
+
MIT License
|
package/bin/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from "child_process";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { getUserAgent } from "package-manager-detector";
|
|
7
7
|
|
|
8
8
|
// Get the directory where this script is located
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mick-templates",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.7",
|
|
4
|
+
"description": "cli tool for creating projects from templates",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mick-templates": "bin/cli.js"
|
|
7
7
|
},
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"tsx": "^4.21.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
+
"@biomejs/biome": "2.3.11",
|
|
36
37
|
"@types/bun": "latest",
|
|
37
38
|
"@types/react": "^18.2.0",
|
|
38
39
|
"typescript": "^5.3.0",
|
package/src/app.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { LanguageSelect } from "./components/
|
|
4
|
-
import { OverwritePrompt } from "./components/
|
|
5
|
-
import { Status } from "./components/
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { LanguageSelect } from "./components/language-select.js";
|
|
4
|
+
import { OverwritePrompt } from "./components/overwrite-prompt.js";
|
|
5
|
+
import { Status } from "./components/status.js";
|
|
6
6
|
import { createProject, type Language } from "./lib/template.js";
|
|
7
7
|
|
|
8
8
|
type Step = "language" | "overwrite" | "processing" | "done" | "error";
|
|
@@ -95,7 +95,7 @@ export function App({
|
|
|
95
95
|
if (!overwrite) {
|
|
96
96
|
process.exit(0);
|
|
97
97
|
}
|
|
98
|
-
runCreate(language
|
|
98
|
+
runCreate(language as Language, true);
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
if (step === "language") {
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { Box, Text } from "ink";
|
|
3
2
|
import SelectInput from "ink-select-input";
|
|
3
|
+
import { LANGUAGE_OPTIONS } from "../lib/constants.js";
|
|
4
4
|
import type { Language } from "../lib/template.js";
|
|
5
5
|
|
|
6
6
|
interface Props {
|
|
7
7
|
onSelect: (lang: Language) => void;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
const items =
|
|
11
|
-
{ label: "TypeScript (recommended)", value: "ts" as Language },
|
|
12
|
-
{ label: "JavaScript", value: "js" as Language },
|
|
13
|
-
];
|
|
10
|
+
const items = LANGUAGE_OPTIONS;
|
|
14
11
|
|
|
15
12
|
export function LanguageSelect({ onSelect }: Props) {
|
|
16
13
|
return (
|
|
@@ -20,4 +17,3 @@ export function LanguageSelect({ onSelect }: Props) {
|
|
|
20
17
|
</Box>
|
|
21
18
|
);
|
|
22
19
|
}
|
|
23
|
-
|
package/src/index.tsx
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
|
|
3
|
-
import {
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Box, render, Text } from "ink";
|
|
4
4
|
import { App } from "./app.js";
|
|
5
|
-
import {
|
|
5
|
+
import { LANGUAGE_OPTIONS } from "./lib/constants.js";
|
|
6
|
+
import { type Language, listTemplates } from "./lib/template.js";
|
|
6
7
|
|
|
7
8
|
const args = process.argv.slice(2);
|
|
8
9
|
|
|
@@ -14,9 +15,11 @@ const skipInstall = args.includes("--skip-install");
|
|
|
14
15
|
const langFlag = args.find((arg) => arg.startsWith("--lang="))?.split("=")[1];
|
|
15
16
|
|
|
16
17
|
// Validate language flag
|
|
17
|
-
const validLangs =
|
|
18
|
+
const validLangs: string[] = LANGUAGE_OPTIONS.map((o) => o.value);
|
|
18
19
|
if (langFlag && !validLangs.includes(langFlag)) {
|
|
19
|
-
console.error(
|
|
20
|
+
console.error(
|
|
21
|
+
`Error: Invalid language "${langFlag}". Use "${validLangs.join('" or "')}".`
|
|
22
|
+
);
|
|
20
23
|
process.exit(1);
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -27,14 +30,23 @@ if (!template) {
|
|
|
27
30
|
render(
|
|
28
31
|
<Box flexDirection="column" padding={1}>
|
|
29
32
|
<Text color="red" bold>
|
|
30
|
-
Usage: mick-templates {"<template>"} [name] [--lang
|
|
33
|
+
Usage: mick-templates {"<template>"} [name] [--lang=$
|
|
34
|
+
{validLangs.join("|")}] [--skip-install]
|
|
31
35
|
</Text>
|
|
32
36
|
<Box marginTop={1} flexDirection="column">
|
|
33
37
|
<Text bold>Available templates:</Text>
|
|
34
|
-
<Box
|
|
38
|
+
<Box
|
|
39
|
+
borderStyle="single"
|
|
40
|
+
borderColor="gray"
|
|
41
|
+
marginTop={1}
|
|
42
|
+
paddingX={1}
|
|
43
|
+
flexDirection="column"
|
|
44
|
+
>
|
|
35
45
|
{templates.map((t) => (
|
|
36
46
|
<Box key={t.name} flexDirection="row">
|
|
37
|
-
<Text color="cyan" bold>
|
|
47
|
+
<Text color="cyan" bold>
|
|
48
|
+
{t.name}
|
|
49
|
+
</Text>
|
|
38
50
|
{t.description && <Text color="gray"> - {t.description}</Text>}
|
|
39
51
|
</Box>
|
|
40
52
|
))}
|
|
@@ -54,4 +66,3 @@ render(
|
|
|
54
66
|
skipInstall={skipInstall}
|
|
55
67
|
/>
|
|
56
68
|
);
|
|
57
|
-
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Language = "ts" | "js";
|
|
2
|
+
|
|
3
|
+
export interface LanguageOption {
|
|
4
|
+
value: Language;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const LANGUAGE_OPTIONS: LanguageOption[] = [
|
|
9
|
+
{ value: "ts", label: "TypeScript (recommended)" },
|
|
10
|
+
{ value: "js", label: "JavaScript" },
|
|
11
|
+
];
|
package/src/lib/convert.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { readFile,
|
|
1
|
+
import { readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
2
4
|
import { glob } from "glob";
|
|
3
|
-
import { join, dirname } from "path";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
5
|
import { execute } from "./pm.js";
|
|
6
6
|
|
|
7
7
|
const TS_DEPS = [
|
|
@@ -69,7 +69,12 @@ async function runCodemod(targetDir: string): Promise<void> {
|
|
|
69
69
|
|
|
70
70
|
for (const file of jsFiles) {
|
|
71
71
|
const filePath = join(targetDir, file);
|
|
72
|
-
await execute(targetDir, "jscodeshift", [
|
|
72
|
+
await execute(targetDir, "jscodeshift", [
|
|
73
|
+
"-t",
|
|
74
|
+
codemodPath,
|
|
75
|
+
"--parser=tsx",
|
|
76
|
+
filePath,
|
|
77
|
+
]);
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
80
|
|
package/src/lib/pm.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
2
5
|
import type { AgentName } from "package-manager-detector";
|
|
3
|
-
import {
|
|
4
|
-
import { createRequire } from "module";
|
|
5
|
-
import { join, dirname } from "path";
|
|
6
|
-
import { readFile } from "fs/promises";
|
|
6
|
+
import { getUserAgent, resolveCommand } from "package-manager-detector";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Get the current package manager agent.
|
|
@@ -28,10 +28,11 @@ function run(cmd: string, args: string[], cwd: string): Promise<void> {
|
|
|
28
28
|
/**
|
|
29
29
|
* Install dependencies in a directory.
|
|
30
30
|
*/
|
|
31
|
-
export async function install(cwd: string): Promise<void> {
|
|
32
|
-
const
|
|
33
|
-
const resolved = resolveCommand(
|
|
34
|
-
if (!resolved)
|
|
31
|
+
export async function install(cwd: string, agent?: AgentName): Promise<void> {
|
|
32
|
+
const resolvedAgent = agent ?? getAgent();
|
|
33
|
+
const resolved = resolveCommand(resolvedAgent, "install", []);
|
|
34
|
+
if (!resolved)
|
|
35
|
+
throw new Error(`Cannot resolve install command for ${resolvedAgent}`);
|
|
35
36
|
|
|
36
37
|
await run(resolved.command, resolved.args, cwd);
|
|
37
38
|
}
|
|
@@ -57,14 +58,22 @@ export async function execute(
|
|
|
57
58
|
const pkgJsonPath = require.resolve(`${pkg}/package.json`);
|
|
58
59
|
const pkgJson = JSON.parse(await readFile(pkgJsonPath, "utf-8")) as Record<
|
|
59
60
|
string,
|
|
60
|
-
|
|
61
|
+
unknown
|
|
61
62
|
>;
|
|
62
63
|
|
|
63
64
|
let binPath: string | undefined;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
const bin = pkgJson.bin;
|
|
66
|
+
if (typeof bin === "string") {
|
|
67
|
+
binPath = join(dirname(pkgJsonPath), bin);
|
|
68
|
+
} else if (
|
|
69
|
+
bin &&
|
|
70
|
+
typeof bin === "object" &&
|
|
71
|
+
typeof (bin as Record<string, unknown>)[pkg] === "string"
|
|
72
|
+
) {
|
|
73
|
+
binPath = join(
|
|
74
|
+
dirname(pkgJsonPath),
|
|
75
|
+
(bin as Record<string, string>)[pkg]
|
|
76
|
+
);
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
if (binPath) {
|
package/src/lib/template.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
cp,
|
|
3
|
+
mkdir,
|
|
4
|
+
readdir,
|
|
5
|
+
readFile,
|
|
6
|
+
rm,
|
|
7
|
+
stat,
|
|
8
|
+
writeFile,
|
|
9
|
+
} from "node:fs/promises";
|
|
10
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import type { Language } from "./constants.js";
|
|
4
13
|
import { convertToJS } from "./convert.js";
|
|
5
14
|
import { install } from "./pm.js";
|
|
6
15
|
|
|
7
16
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
17
|
const TEMPLATES_DIR = join(__dirname, "..", "..", "templates");
|
|
9
18
|
|
|
10
|
-
export type Language
|
|
19
|
+
export type { Language };
|
|
11
20
|
|
|
12
21
|
export interface CreateOptions {
|
|
13
22
|
template: string;
|
package/src/ts-to-js-codemod.mjs
CHANGED
|
@@ -10,11 +10,10 @@ export default function (fileInfo, api) {
|
|
|
10
10
|
|
|
11
11
|
// Update import declarations from .tsx to .jsx
|
|
12
12
|
root.find(j.ImportDeclaration).forEach((path) => {
|
|
13
|
-
if (path.node.source.value
|
|
13
|
+
if (path.node.source.value?.endsWith(".tsx")) {
|
|
14
14
|
path.node.source.value = path.node.source.value.replace(/\.tsx$/, ".jsx");
|
|
15
15
|
}
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
return root.toSource();
|
|
19
19
|
}
|
|
20
|
-
|