@kood/claude-code 0.3.6 → 0.3.8
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/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/.claude/agents/code-reviewer.md +124 -124
- package/templates/.claude/agents/dependency-manager.md +85 -85
- package/templates/.claude/agents/deployment-validator.md +56 -56
- package/templates/.claude/agents/git-operator.md +64 -64
- package/templates/.claude/agents/implementation-executor.md +95 -95
- package/templates/.claude/agents/ko-to-en-translator.md +74 -0
- package/templates/.claude/agents/lint-fixer.md +78 -78
- package/templates/.claude/agents/refactor-advisor.md +122 -122
- package/templates/.claude/commands/agent-creator.md +185 -185
- package/templates/.claude/commands/bug-fix.md +193 -193
- package/templates/.claude/commands/command-creator.md +54 -54
- package/templates/.claude/commands/docs-creator.md +57 -57
- package/templates/.claude/commands/docs-refactor.md +26 -26
- package/templates/.claude/commands/execute.md +12 -12
- package/templates/.claude/commands/git-all.md +32 -32
- package/templates/.claude/commands/git-session.md +42 -42
- package/templates/.claude/commands/git.md +34 -34
- package/templates/.claude/commands/lint-fix.md +138 -138
- package/templates/.claude/commands/lint-init.md +61 -61
- package/templates/.claude/commands/plan.md +260 -260
- package/templates/.claude/commands/prd.md +24 -24
- package/templates/.claude/commands/pre-deploy.md +109 -109
- package/templates/.claude/commands/refactor.md +147 -147
- package/templates/.claude/commands/version-update.md +17 -17
- package/templates/hono/CLAUDE.md +27 -27
- package/templates/hono/docs/architecture.md +24 -24
- package/templates/hono/docs/deployment/cloudflare.md +18 -18
- package/templates/hono/docs/deployment/docker.md +13 -13
- package/templates/hono/docs/deployment/index.md +19 -19
- package/templates/hono/docs/deployment/railway.md +32 -32
- package/templates/hono/docs/deployment/vercel.md +29 -29
- package/templates/hono/docs/guides/conventions.md +57 -57
- package/templates/hono/docs/guides/env-setup.md +47 -47
- package/templates/hono/docs/guides/getting-started.md +27 -27
- package/templates/hono/docs/library/hono/error-handling.md +11 -11
- package/templates/hono/docs/library/hono/index.md +4 -4
- package/templates/hono/docs/library/hono/middleware.md +18 -18
- package/templates/hono/docs/library/hono/rpc.md +7 -7
- package/templates/hono/docs/library/hono/validation.md +6 -6
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +29 -29
- package/templates/hono/docs/library/prisma/config.md +16 -16
- package/templates/hono/docs/library/prisma/index.md +32 -32
- package/templates/hono/docs/library/t3-env/index.md +22 -22
- package/templates/hono/docs/library/zod/index.md +31 -31
- package/templates/nextjs/CLAUDE.md +228 -0
- package/templates/nextjs/docs/design.md +558 -0
- package/templates/nextjs/docs/guides/conventions.md +343 -0
- package/templates/nextjs/docs/guides/getting-started.md +367 -0
- package/templates/nextjs/docs/guides/routes.md +342 -0
- package/templates/nextjs/docs/library/better-auth/index.md +541 -0
- package/templates/nextjs/docs/library/nextjs/app-router.md +269 -0
- package/templates/nextjs/docs/library/nextjs/caching.md +351 -0
- package/templates/nextjs/docs/library/nextjs/index.md +291 -0
- package/templates/nextjs/docs/library/nextjs/middleware.md +391 -0
- package/templates/nextjs/docs/library/nextjs/route-handlers.md +382 -0
- package/templates/nextjs/docs/library/nextjs/server-actions.md +366 -0
- package/templates/nextjs/docs/library/prisma/cloudflare-d1.md +76 -0
- package/templates/nextjs/docs/library/prisma/config.md +77 -0
- package/templates/nextjs/docs/library/prisma/crud.md +90 -0
- package/templates/nextjs/docs/library/prisma/index.md +73 -0
- package/templates/nextjs/docs/library/prisma/relations.md +69 -0
- package/templates/nextjs/docs/library/prisma/schema.md +98 -0
- package/templates/nextjs/docs/library/prisma/setup.md +49 -0
- package/templates/nextjs/docs/library/prisma/transactions.md +50 -0
- package/templates/nextjs/docs/library/tanstack-query/index.md +66 -0
- package/templates/nextjs/docs/library/tanstack-query/invalidation.md +54 -0
- package/templates/nextjs/docs/library/tanstack-query/optimistic-updates.md +77 -0
- package/templates/nextjs/docs/library/tanstack-query/use-mutation.md +63 -0
- package/templates/nextjs/docs/library/tanstack-query/use-query.md +70 -0
- package/templates/nextjs/docs/library/zod/complex-types.md +61 -0
- package/templates/nextjs/docs/library/zod/index.md +56 -0
- package/templates/nextjs/docs/library/zod/transforms.md +51 -0
- package/templates/nextjs/docs/library/zod/validation.md +70 -0
- package/templates/npx/CLAUDE.md +37 -37
- package/templates/npx/docs/library/commander/index.md +12 -12
- package/templates/npx/docs/library/fs-extra/index.md +9 -9
- package/templates/npx/docs/library/prompts/index.md +3 -3
- package/templates/npx/docs/references/patterns.md +12 -12
- package/templates/tanstack-start/CLAUDE.md +53 -49
- package/templates/tanstack-start/docs/architecture.md +128 -128
- package/templates/tanstack-start/docs/design.md +169 -169
- package/templates/tanstack-start/docs/guides/conventions.md +43 -43
- package/templates/tanstack-start/docs/guides/env-setup.md +35 -35
- package/templates/tanstack-start/docs/guides/getting-started.md +19 -19
- package/templates/tanstack-start/docs/guides/hooks.md +63 -35
- package/templates/tanstack-start/docs/guides/routes.md +61 -42
- package/templates/tanstack-start/docs/guides/services.md +45 -45
- package/templates/tanstack-start/docs/library/better-auth/index.md +68 -68
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +19 -19
- package/templates/tanstack-start/docs/library/prisma/config.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/crud.md +17 -17
- package/templates/tanstack-start/docs/library/prisma/index.md +17 -17
- package/templates/tanstack-start/docs/library/prisma/relations.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/schema.md +23 -23
- package/templates/tanstack-start/docs/library/prisma/setup.md +6 -6
- package/templates/tanstack-start/docs/library/prisma/transactions.md +10 -10
- package/templates/tanstack-start/docs/library/t3-env/index.md +21 -160
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +6 -6
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +19 -19
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +4 -4
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +14 -14
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +21 -21
- package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +11 -11
- package/templates/tanstack-start/docs/library/tanstack-router/index.md +18 -18
- package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +17 -17
- package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +5 -5
- package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +10 -10
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +8 -8
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +15 -15
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +6 -6
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +18 -18
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +4 -4
- package/templates/tanstack-start/docs/library/zod/complex-types.md +11 -11
- package/templates/tanstack-start/docs/library/zod/index.md +8 -8
- package/templates/tanstack-start/docs/library/zod/transforms.md +11 -11
- package/templates/tanstack-start/docs/library/zod/validation.md +9 -9
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Zod
|
|
2
|
+
|
|
3
|
+
> v4 | TypeScript Schema Validation
|
|
4
|
+
|
|
5
|
+
@complex-types.md
|
|
6
|
+
@transforms.md
|
|
7
|
+
@validation.md
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<quick_reference>
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// Basic
|
|
15
|
+
const schema = z.object({
|
|
16
|
+
email: z.email(), // v4!
|
|
17
|
+
name: z.string().min(1).trim(),
|
|
18
|
+
website: z.url().optional(), // v4!
|
|
19
|
+
age: z.number().int().positive(),
|
|
20
|
+
})
|
|
21
|
+
type Input = z.infer<typeof schema>
|
|
22
|
+
|
|
23
|
+
schema.parse(data) // throws on failure
|
|
24
|
+
schema.safeParse(data) // { success, data/error }
|
|
25
|
+
|
|
26
|
+
// TanStack Start
|
|
27
|
+
export const createUser = createServerFn({ method: 'POST' })
|
|
28
|
+
.inputValidator(schema)
|
|
29
|
+
.handler(async ({ data }) => prisma.user.create({ data }))
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
</quick_reference>
|
|
33
|
+
|
|
34
|
+
<v4_changes>
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// ✅ v4 new APIs
|
|
38
|
+
z.email() z.url() z.uuid()
|
|
39
|
+
z.iso.date() z.iso.datetime() z.iso.duration()
|
|
40
|
+
z.stringbool() // "true"/"yes"/"1" → true
|
|
41
|
+
|
|
42
|
+
// ❌ v3 deprecated
|
|
43
|
+
z.string().email() z.string().url()
|
|
44
|
+
|
|
45
|
+
// Changes
|
|
46
|
+
z.string().min(5, { error: "Too short." }) // message → error
|
|
47
|
+
z.strictObject({ name: z.string() }) // additional keys throw error
|
|
48
|
+
z.looseObject({ name: z.string() }) // additional keys pass through
|
|
49
|
+
z.string().refine(val => val.includes("@")).min(5) // refinement chaining
|
|
50
|
+
|
|
51
|
+
// Template literals
|
|
52
|
+
const css = z.templateLiteral([z.number(), z.enum(["px", "em", "rem"])])
|
|
53
|
+
// `${number}px` | `${number}em` | `${number}rem`
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
</v4_changes>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Zod - Transforms
|
|
2
|
+
|
|
3
|
+
<patterns>
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// Transform
|
|
7
|
+
const stringToLength = z.string().transform((val) => val.length)
|
|
8
|
+
stringToLength.parse('hello') // => 5
|
|
9
|
+
|
|
10
|
+
type In = z.input<typeof stringToLength> // string
|
|
11
|
+
type Out = z.output<typeof stringToLength> // number
|
|
12
|
+
|
|
13
|
+
// Pipe (validate then transform)
|
|
14
|
+
const stringToNumber = z.string()
|
|
15
|
+
.transform((val) => parseInt(val, 10))
|
|
16
|
+
.pipe(z.number().min(0).max(100))
|
|
17
|
+
|
|
18
|
+
stringToNumber.parse('50') // => 50
|
|
19
|
+
stringToNumber.parse('150') // throws
|
|
20
|
+
|
|
21
|
+
// Coerce (force conversion)
|
|
22
|
+
z.coerce.string() // any value → string
|
|
23
|
+
z.coerce.number() // any value → number
|
|
24
|
+
z.coerce.boolean() // any value → boolean
|
|
25
|
+
z.coerce.date() // any value → date
|
|
26
|
+
z.coerce.bigint() // any value → BigInt
|
|
27
|
+
|
|
28
|
+
z.coerce.number().parse("42") // => 42
|
|
29
|
+
z.coerce.date().parse("2021-01-01") // => Date
|
|
30
|
+
|
|
31
|
+
// v4: input type changed to unknown
|
|
32
|
+
type In = z.input<typeof z.coerce.string()> // unknown
|
|
33
|
+
|
|
34
|
+
// v4 environment variable boolean
|
|
35
|
+
z.stringbool() // "true"/"yes"/"1" → true, "false"/"no"/"0" → false
|
|
36
|
+
|
|
37
|
+
// Preprocess
|
|
38
|
+
const trimmed = z.preprocess(
|
|
39
|
+
(val) => typeof val === 'string' ? val.trim() : val,
|
|
40
|
+
z.string()
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
// Separate input/output types
|
|
44
|
+
const Schema = z.object({
|
|
45
|
+
createdAt: z.string().transform((str) => new Date(str)),
|
|
46
|
+
})
|
|
47
|
+
type SchemaInput = z.input<typeof Schema> // { createdAt: string }
|
|
48
|
+
type SchemaOutput = z.output<typeof Schema> // { createdAt: Date }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
</patterns>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Zod - Validation
|
|
2
|
+
|
|
3
|
+
<patterns>
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// Refinement (v4: message → error)
|
|
7
|
+
const PasswordSchema = z.string()
|
|
8
|
+
.min(8)
|
|
9
|
+
.refine((val) => /[A-Z]/.test(val), { error: 'Uppercase letter required' })
|
|
10
|
+
.refine((val) => /[0-9]/.test(val), { error: 'Number required' })
|
|
11
|
+
|
|
12
|
+
z.string().refine(val => val.includes("@")).min(5) // v4: chaining after refinement
|
|
13
|
+
|
|
14
|
+
// Async
|
|
15
|
+
const schema = z.string().refine(async (val) => val.length <= 8)
|
|
16
|
+
await schema.parseAsync('hello')
|
|
17
|
+
|
|
18
|
+
// Superrefine
|
|
19
|
+
z.object({
|
|
20
|
+
password: z.string(),
|
|
21
|
+
confirmPassword: z.string(),
|
|
22
|
+
}).superRefine((data, ctx) => {
|
|
23
|
+
if (data.password !== data.confirmPassword) {
|
|
24
|
+
ctx.addIssue({
|
|
25
|
+
code: z.ZodIssueCode.custom,
|
|
26
|
+
message: 'Passwords do not match',
|
|
27
|
+
path: ['confirmPassword'],
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
}) // v4: ctx.path is unavailable
|
|
31
|
+
|
|
32
|
+
// Custom
|
|
33
|
+
const px = z.custom<`${number}px`>((val) =>
|
|
34
|
+
typeof val === 'string' && /^\d+px$/.test(val)
|
|
35
|
+
)
|
|
36
|
+
px.parse('42px') // ✅
|
|
37
|
+
px.parse('42vw') // throws
|
|
38
|
+
|
|
39
|
+
// Error handling
|
|
40
|
+
const result = schema.safeParse(data)
|
|
41
|
+
if (!result.success) {
|
|
42
|
+
result.error.errors.forEach((err) => {
|
|
43
|
+
console.log(err.path, err.message, err.code)
|
|
44
|
+
})
|
|
45
|
+
const flat = result.error.flatten() // { fieldErrors: { email: ['Invalid email'] } }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// TanStack Start
|
|
49
|
+
export const createUser = createServerFn({ method: 'POST' })
|
|
50
|
+
.inputValidator(zodValidator(createUserSchema))
|
|
51
|
+
.handler(async ({ data }) => prisma.user.create({ data }))
|
|
52
|
+
|
|
53
|
+
// Environment variables
|
|
54
|
+
const env = z.object({
|
|
55
|
+
NODE_ENV: z.enum(['development', 'production', 'test']),
|
|
56
|
+
DATABASE_URL: z.string().url(),
|
|
57
|
+
API_SECRET: z.string().min(32),
|
|
58
|
+
}).parse(process.env)
|
|
59
|
+
|
|
60
|
+
// Middleware
|
|
61
|
+
const workspaceMiddleware = createMiddleware({ type: 'function' })
|
|
62
|
+
.inputValidator(zodValidator(z.object({ workspaceId: z.string() })))
|
|
63
|
+
.server(({ next, data }) => next())
|
|
64
|
+
|
|
65
|
+
// Zod Mini (v4)
|
|
66
|
+
import * as z from "zod/mini"
|
|
67
|
+
z.string().check(z.minLength(5), z.maxLength(10), z.trim())
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
</patterns>
|
package/templates/npx/CLAUDE.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# CLAUDE.md - NPX CLI
|
|
2
2
|
|
|
3
|
-
> Node.js CLI
|
|
3
|
+
> Node.js CLI tool development
|
|
4
4
|
|
|
5
5
|
<context>
|
|
6
6
|
|
|
7
|
-
**Purpose:** Node.js CLI
|
|
7
|
+
**Purpose:** Work instructions for Node.js CLI tool development
|
|
8
8
|
|
|
9
|
-
**Scope:**
|
|
9
|
+
**Scope:** Implement CLI packages deployable via npx
|
|
10
10
|
|
|
11
11
|
**Key Features:**
|
|
12
|
-
- Commander.js
|
|
13
|
-
-
|
|
14
|
-
-
|
|
12
|
+
- Commander.js command system
|
|
13
|
+
- Interactive prompts (prompts)
|
|
14
|
+
- File system operations (fs-extra)
|
|
15
15
|
- TypeScript + ESM
|
|
16
|
-
-
|
|
16
|
+
- Colored output (picocolors)
|
|
17
17
|
|
|
18
18
|
</context>
|
|
19
19
|
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
|
|
32
32
|
<forbidden>
|
|
33
33
|
|
|
34
|
-
|
|
|
35
|
-
|
|
36
|
-
| **Git
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
34
|
+
| Category | Prohibited Actions |
|
|
35
|
+
|----------|-------------------|
|
|
36
|
+
| **Git Commits** | AI markers (`Generated with Claude Code`, `🤖`, `Co-Authored-By:`), multi-line messages, emojis |
|
|
37
|
+
| **Console Output** | Direct `console.log` usage (use logger functions) |
|
|
38
|
+
| **File Operations** | Synchronous APIs (`fs.readFileSync`, etc.), hardcoded paths (use path.join) |
|
|
39
|
+
| **Error Handling** | Missing `process.exit()`, async operations without try-catch |
|
|
40
|
+
| **Code Search** | Bash grep/rg/find commands (use ast-grep or dedicated tools) |
|
|
41
41
|
|
|
42
42
|
</forbidden>
|
|
43
43
|
|
|
@@ -45,15 +45,15 @@
|
|
|
45
45
|
|
|
46
46
|
<required>
|
|
47
47
|
|
|
48
|
-
|
|
|
49
|
-
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
48
|
+
| Task | Required Actions |
|
|
49
|
+
|------|-----------------|
|
|
50
|
+
| **Before Starting** | Read relevant docs (Commander → commander, Files → fs-extra) |
|
|
51
|
+
| **Console Output** | Use logger functions (info/success/error/warn) |
|
|
52
|
+
| **File Operations** | Async API (`fs-extra`), `path.join` for paths |
|
|
53
|
+
| **Error Handling** | try-catch + `process.exit(1)`, clear error messages to users |
|
|
54
|
+
| **Code Search** | Use ast-grep (search functions/classes/patterns) |
|
|
55
|
+
| **Code Writing** | UTF-8 encoding, Korean comments per code block, const function declarations |
|
|
56
|
+
| **Complex Tasks** | Sequential Thinking MCP (5+ step tasks) |
|
|
57
57
|
|
|
58
58
|
</required>
|
|
59
59
|
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
|
|
62
62
|
<tech_stack>
|
|
63
63
|
|
|
64
|
-
|
|
|
65
|
-
|
|
64
|
+
| Technology | Version |
|
|
65
|
+
|-----------|---------|
|
|
66
66
|
| Node.js | >= 18 (ESM) |
|
|
67
67
|
| TypeScript | 5.x (strict) |
|
|
68
68
|
| Commander | 12.x |
|
|
@@ -78,13 +78,13 @@
|
|
|
78
78
|
<structure>
|
|
79
79
|
```
|
|
80
80
|
src/
|
|
81
|
-
├── index.ts # CLI
|
|
82
|
-
├── commands/ #
|
|
81
|
+
├── index.ts # CLI entry point
|
|
82
|
+
├── commands/ # Command modules
|
|
83
83
|
├── utils/ # logger, copy
|
|
84
|
-
└── types/ #
|
|
84
|
+
└── types/ # Types
|
|
85
85
|
|
|
86
|
-
templates/ #
|
|
87
|
-
dist/ #
|
|
86
|
+
templates/ # Copied during build
|
|
87
|
+
dist/ # Build output
|
|
88
88
|
```
|
|
89
89
|
</structure>
|
|
90
90
|
|
|
@@ -92,9 +92,9 @@ dist/ # 빌드 결과물
|
|
|
92
92
|
|
|
93
93
|
<conventions>
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
**TypeScript:** const
|
|
97
|
-
**Import
|
|
95
|
+
**File naming:** kebab-case
|
|
96
|
+
**TypeScript:** const declarations, explicit return types, interface(objects)/type(unions), any→unknown
|
|
97
|
+
**Import order:** external → internal → type
|
|
98
98
|
|
|
99
99
|
</conventions>
|
|
100
100
|
|
|
@@ -103,7 +103,7 @@ dist/ # 빌드 결과물
|
|
|
103
103
|
<quick_patterns>
|
|
104
104
|
|
|
105
105
|
```typescript
|
|
106
|
-
// CLI
|
|
106
|
+
// CLI entry point (index.ts)
|
|
107
107
|
#!/usr/bin/env node
|
|
108
108
|
import { Command } from 'commander'
|
|
109
109
|
import { logger } from './utils/logger.js'
|
|
@@ -142,7 +142,7 @@ export const logger = {
|
|
|
142
142
|
```
|
|
143
143
|
|
|
144
144
|
```typescript
|
|
145
|
-
//
|
|
145
|
+
// File copying (utils/copy.ts)
|
|
146
146
|
import path from 'node:path'
|
|
147
147
|
import { copy, pathExists } from 'fs-extra'
|
|
148
148
|
|
|
@@ -162,7 +162,7 @@ export const copyTemplate = async (
|
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
```typescript
|
|
165
|
-
//
|
|
165
|
+
// Interactive prompts
|
|
166
166
|
import prompts from 'prompts'
|
|
167
167
|
|
|
168
168
|
const response = await prompts([
|
|
@@ -192,6 +192,6 @@ const response = await prompts([
|
|
|
192
192
|
```
|
|
193
193
|
docs/
|
|
194
194
|
├── library/ # commander, fs-extra, prompts
|
|
195
|
-
└── references/ # CLI
|
|
195
|
+
└── references/ # CLI patterns, best practices
|
|
196
196
|
```
|
|
197
197
|
</docs_structure>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Commander.js
|
|
2
2
|
|
|
3
|
-
> Node.js CLI
|
|
3
|
+
> Node.js CLI framework
|
|
4
4
|
|
|
5
5
|
<patterns>
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Basic Setup
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
10
|
import { Command } from 'commander';
|
|
@@ -22,7 +22,7 @@ program.parse();
|
|
|
22
22
|
## Options
|
|
23
23
|
|
|
24
24
|
```typescript
|
|
25
|
-
//
|
|
25
|
+
// Basic
|
|
26
26
|
program
|
|
27
27
|
.option('-t, --template <name>', 'template name')
|
|
28
28
|
.option('-f, --force', 'force overwrite')
|
|
@@ -31,19 +31,19 @@ program
|
|
|
31
31
|
console.log(options.force); // boolean | undefined
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
//
|
|
34
|
+
// Required
|
|
35
35
|
program
|
|
36
36
|
.requiredOption('-c, --config <path>', 'config file path')
|
|
37
37
|
.action((options) => {
|
|
38
|
-
// options.config
|
|
38
|
+
// options.config is always present
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
//
|
|
41
|
+
// Default values
|
|
42
42
|
program
|
|
43
43
|
.option('-p, --port <number>', 'port number', '3000')
|
|
44
44
|
.option('-e, --env <name>', 'environment', 'development')
|
|
45
45
|
|
|
46
|
-
//
|
|
46
|
+
// Multiple values
|
|
47
47
|
program
|
|
48
48
|
.option('-i, --include <path...>', 'include paths')
|
|
49
49
|
.action((options) => {
|
|
@@ -58,8 +58,8 @@ program
|
|
|
58
58
|
.argument('<source>', 'source file')
|
|
59
59
|
.argument('[destination]', 'destination file')
|
|
60
60
|
.action((source, destination, options) => {
|
|
61
|
-
console.log(source); //
|
|
62
|
-
console.log(destination); //
|
|
61
|
+
console.log(source); // required
|
|
62
|
+
console.log(destination); // optional
|
|
63
63
|
});
|
|
64
64
|
```
|
|
65
65
|
|
|
@@ -71,14 +71,14 @@ program
|
|
|
71
71
|
.description('Initialize project')
|
|
72
72
|
.option('-t, --template <name>', 'template')
|
|
73
73
|
.action((options) => {
|
|
74
|
-
// init
|
|
74
|
+
// Handle init
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
program
|
|
78
78
|
.command('build')
|
|
79
79
|
.description('Build project')
|
|
80
80
|
.action(() => {
|
|
81
|
-
// build
|
|
81
|
+
// Handle build
|
|
82
82
|
});
|
|
83
83
|
```
|
|
84
84
|
|
|
@@ -95,7 +95,7 @@ program
|
|
|
95
95
|
}
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
-
await program.parseAsync(); // await
|
|
98
|
+
await program.parseAsync(); // await required
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
</patterns>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# fs-extra
|
|
2
2
|
|
|
3
|
-
> Node.js
|
|
3
|
+
> Node.js file system utilities
|
|
4
4
|
|
|
5
5
|
<patterns>
|
|
6
6
|
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
import fs from 'fs-extra';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
|
|
11
|
-
//
|
|
11
|
+
// Check existence
|
|
12
12
|
const exists = await fs.pathExists('/path/to/file');
|
|
13
13
|
|
|
14
|
-
//
|
|
14
|
+
// Create directory
|
|
15
15
|
await fs.ensureDir('/path/to/nested/dir');
|
|
16
16
|
await fs.mkdir('/path/to/dir');
|
|
17
17
|
|
|
18
|
-
//
|
|
18
|
+
// Copy
|
|
19
19
|
await fs.copy('/src/file.txt', '/dest/file.txt');
|
|
20
20
|
await fs.copy('/src/dir', '/dest/dir');
|
|
21
21
|
await fs.copy(src, dest, {
|
|
@@ -24,18 +24,18 @@ await fs.copy(src, dest, {
|
|
|
24
24
|
filter: (src) => !src.includes('node_modules'),
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
//
|
|
27
|
+
// Remove
|
|
28
28
|
await fs.remove('/path/to/dir');
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// Read directory
|
|
31
31
|
const items = await fs.readdir('/path/to/dir');
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// Check file type
|
|
34
34
|
const stat = await fs.stat(itemPath);
|
|
35
35
|
if (stat.isDirectory()) { /* ... */ }
|
|
36
36
|
if (stat.isFile()) { /* ... */ }
|
|
37
37
|
|
|
38
|
-
//
|
|
38
|
+
// Read/Write files
|
|
39
39
|
const content = await fs.readFile('/path/to/file', 'utf-8');
|
|
40
40
|
await fs.writeFile('/path/to/file', content, 'utf-8');
|
|
41
41
|
|
|
@@ -43,7 +43,7 @@ await fs.writeFile('/path/to/file', content, 'utf-8');
|
|
|
43
43
|
const data = await fs.readJson('/path/to/file.json');
|
|
44
44
|
await fs.writeJson('/path/to/file.json', data, { spaces: 2 });
|
|
45
45
|
|
|
46
|
-
//
|
|
46
|
+
// Recursive copy
|
|
47
47
|
const copyRecursive = async (src: string, dest: string) => {
|
|
48
48
|
const counter = { files: 0, directories: 0 };
|
|
49
49
|
|
|
@@ -66,7 +66,7 @@ const { secret } = await prompts({
|
|
|
66
66
|
message: 'Enter password:',
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
//
|
|
69
|
+
// Handle cancellation
|
|
70
70
|
const { template } = await prompts({ /* ... */ });
|
|
71
71
|
|
|
72
72
|
if (!template) {
|
|
@@ -74,14 +74,14 @@ if (!template) {
|
|
|
74
74
|
process.exit(0);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
//
|
|
77
|
+
// Multiple questions
|
|
78
78
|
const response = await prompts([
|
|
79
79
|
{ type: 'text', name: 'name', message: 'Project name?' },
|
|
80
80
|
{ type: 'select', name: 'template', message: 'Template?', choices: [...] },
|
|
81
81
|
{ type: 'confirm', name: 'git', message: 'Initialize git?' },
|
|
82
82
|
]);
|
|
83
83
|
|
|
84
|
-
//
|
|
84
|
+
// Conditional questions
|
|
85
85
|
const response = await prompts([
|
|
86
86
|
{ type: 'confirm', name: 'useTypescript', message: 'Use TypeScript?' },
|
|
87
87
|
{ type: (prev) => prev ? 'confirm' : null, name: 'strict', message: 'Enable strict mode?' },
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# NPX CLI Patterns
|
|
2
2
|
|
|
3
|
-
> CLI
|
|
3
|
+
> Integrated CLI implementation patterns
|
|
4
4
|
|
|
5
5
|
<patterns>
|
|
6
6
|
|
|
7
|
-
## CLI
|
|
7
|
+
## CLI Entry Point
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
10
|
#!/usr/bin/env node
|
|
@@ -35,7 +35,7 @@ program
|
|
|
35
35
|
program.parse();
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
## Logger
|
|
38
|
+
## Logger Utility
|
|
39
39
|
|
|
40
40
|
```typescript
|
|
41
41
|
import pc from 'picocolors';
|
|
@@ -49,7 +49,7 @@ export const logger = {
|
|
|
49
49
|
};
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
##
|
|
52
|
+
## Interactive Template Selection
|
|
53
53
|
|
|
54
54
|
```typescript
|
|
55
55
|
import prompts from 'prompts';
|
|
@@ -61,7 +61,7 @@ export const selectAndCopyTemplate = async (
|
|
|
61
61
|
templatesDir: string,
|
|
62
62
|
targetDir: string,
|
|
63
63
|
): Promise<void> => {
|
|
64
|
-
// 1.
|
|
64
|
+
// 1. Select template
|
|
65
65
|
const { template } = await prompts({
|
|
66
66
|
type: 'select',
|
|
67
67
|
name: 'template',
|
|
@@ -77,7 +77,7 @@ export const selectAndCopyTemplate = async (
|
|
|
77
77
|
process.exit(0);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
// 2.
|
|
80
|
+
// 2. Confirm overwrite
|
|
81
81
|
const targetExists = await fs.pathExists(targetDir);
|
|
82
82
|
if (targetExists) {
|
|
83
83
|
const { overwrite } = await prompts({
|
|
@@ -93,14 +93,14 @@ export const selectAndCopyTemplate = async (
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
// 3.
|
|
96
|
+
// 3. Copy files
|
|
97
97
|
const src = path.join(templatesDir, template);
|
|
98
98
|
await fs.copy(src, targetDir, { overwrite: true });
|
|
99
99
|
logger.success(`Template copied to ${targetDir}`);
|
|
100
100
|
};
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
## ESM __dirname
|
|
103
|
+
## ESM __dirname Handling
|
|
104
104
|
|
|
105
105
|
```typescript
|
|
106
106
|
import { fileURLToPath } from 'url';
|
|
@@ -109,11 +109,11 @@ import path from 'path';
|
|
|
109
109
|
const __filename = fileURLToPath(import.meta.url);
|
|
110
110
|
const __dirname = path.dirname(__filename);
|
|
111
111
|
|
|
112
|
-
//
|
|
112
|
+
// Templates directory path
|
|
113
113
|
const templatesDir = path.resolve(__dirname, '../templates');
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
##
|
|
116
|
+
## Recursive Copy with Counter
|
|
117
117
|
|
|
118
118
|
```typescript
|
|
119
119
|
import fs from 'fs-extra';
|
|
@@ -147,7 +147,7 @@ export const copyRecursive = async (
|
|
|
147
147
|
};
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
## package.json
|
|
150
|
+
## package.json Configuration
|
|
151
151
|
|
|
152
152
|
```json
|
|
153
153
|
{
|
|
@@ -167,7 +167,7 @@ export const copyRecursive = async (
|
|
|
167
167
|
}
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
-
## tsup
|
|
170
|
+
## tsup Configuration
|
|
171
171
|
|
|
172
172
|
```typescript
|
|
173
173
|
import { defineConfig } from 'tsup';
|