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 CHANGED
@@ -1,9 +1,43 @@
1
1
  # mick-templates
2
2
 
3
- a minimal, clean, and robust cli to create projects from templates.
3
+ a minimal, clean, and robust cli for creating projects from templates. built with bun, typescript, and ink.
4
4
 
5
- ## Usage
5
+ ## installation
6
6
 
7
7
  ```bash
8
- bun start <template> [name]
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 { dirname, join } from "path";
4
- import { fileURLToPath } from "url";
5
- import { createRequire } from "module";
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.6",
4
- "description": "CLI tool for creating projects from templates",
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 React, { useState, useEffect, useCallback } from "react";
2
- import { readdirSync, existsSync } from "fs";
3
- import { LanguageSelect } from "./components/LanguageSelect.js";
4
- import { OverwritePrompt } from "./components/OverwritePrompt.js";
5
- import { Status } from "./components/Status.js";
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!, true);
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
-
@@ -1,4 +1,3 @@
1
- import React from "react";
2
1
  import { Box, Text } from "ink";
3
2
  import SelectInput from "ink-select-input";
4
3
 
@@ -19,4 +18,3 @@ export function OverwritePrompt({ onSelect }: Props) {
19
18
  </Box>
20
19
  );
21
20
  }
22
-
@@ -1,4 +1,3 @@
1
- import React from "react";
2
1
  import { Text } from "ink";
3
2
 
4
3
  interface Props {
@@ -18,4 +17,3 @@ export function Status({ status, targetDir, error }: Props) {
18
17
 
19
18
  return <Text color="yellow">Creating project...</Text>;
20
19
  }
21
-
package/src/index.tsx CHANGED
@@ -1,8 +1,9 @@
1
- #!/usr/bin/env node
2
- import React from "react";
3
- import { render, Box, Text } from "ink";
1
+ #!/usr/bin/env bun
2
+
3
+ import { Box, render, Text } from "ink";
4
4
  import { App } from "./app.js";
5
- import { listTemplates, type Language } from "./lib/template.js";
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 = ["js", "ts"];
18
+ const validLangs: string[] = LANGUAGE_OPTIONS.map((o) => o.value);
18
19
  if (langFlag && !validLangs.includes(langFlag)) {
19
- console.error(`Error: Invalid language "${langFlag}". Use "js" or "ts".`);
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=js|ts] [--skip-install]
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 borderStyle="single" borderColor="gray" marginTop={1} paddingX={1} flexDirection="column">
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>{t.name}</Text>
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
+ ];
@@ -1,7 +1,7 @@
1
- import { readFile, writeFile, rename, rm } from "fs/promises";
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", ["-t", codemodPath, "--parser=tsx", filePath]);
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 { getUserAgent, resolveCommand } from "package-manager-detector";
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 { spawn } from "child_process";
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 agent = getAgent();
33
- const resolved = resolveCommand(agent, "install", []);
34
- if (!resolved) throw new Error(`Cannot resolve install command for ${agent}`);
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
- any
61
+ unknown
61
62
  >;
62
63
 
63
64
  let binPath: string | undefined;
64
- if (typeof pkgJson.bin === "string") {
65
- binPath = join(dirname(pkgJsonPath), pkgJson.bin);
66
- } else if (pkgJson.bin && pkgJson.bin[pkg]) {
67
- binPath = join(dirname(pkgJsonPath), pkgJson.bin[pkg]);
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) {
@@ -1,13 +1,22 @@
1
- import { cp, readdir, rm, readFile, writeFile, mkdir, stat } from "fs/promises";
2
- import { join, resolve, basename, dirname } from "path";
3
- import { fileURLToPath } from "url";
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 = "ts" | "js";
19
+ export type { Language };
11
20
 
12
21
  export interface CreateOptions {
13
22
  template: string;
@@ -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 && path.node.source.value.endsWith(".tsx")) {
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
-