create-murasaki 0.0.7 → 0.1.0

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/index.mjs CHANGED
@@ -6,8 +6,8 @@ import { cp, mkdir, readFile, writeFile, stat } from 'node:fs/promises'
6
6
  import { existsSync } from 'node:fs'
7
7
  import { dirname, join, resolve } from 'node:path'
8
8
  import { fileURLToPath } from 'node:url'
9
- import { createInterface } from 'node:readline/promises'
10
9
  import { spawnSync } from 'node:child_process'
10
+ import { text, select, isCancel, cancel, intro, outro } from '@clack/prompts'
11
11
 
12
12
  // ── ANSI truecolor (Oomurasaki palette) ────────────────────────────────
13
13
  const BRIGHT = '\x1b[38;2;168;85;247m'
@@ -132,21 +132,39 @@ function runInstall(targetDir, pm) {
132
132
  return result.status === 0
133
133
  }
134
134
 
135
+ function exitIfCancel(value) {
136
+ if (isCancel(value)) {
137
+ cancel('Cancelled.')
138
+ process.exit(0)
139
+ }
140
+ return value
141
+ }
142
+
135
143
  async function promptForName() {
136
- const rl = createInterface({ input: process.stdin, output: process.stdout })
137
- const answer = await rl.question(` ${c(DEEP)}?${c(RESET)} ${c(BOLD)}Project name${c(RESET)} ${c(DIM)}(my-app):${c(RESET)} `)
138
- rl.close()
139
- return answer.trim() || 'my-app'
144
+ const value = await text({
145
+ message: 'Project name',
146
+ placeholder: 'my-app',
147
+ defaultValue: 'my-app',
148
+ validate(v) {
149
+ const t = (v || '').trim()
150
+ if (!t) return // empty → use defaultValue
151
+ if (!isValidPackageName(t)) return 'Use lowercase letters, digits, dot, hyphen, underscore. Start with a letter or digit.'
152
+ },
153
+ })
154
+ return exitIfCancel(value)
140
155
  }
141
156
 
142
157
  async function promptForLinter() {
143
- const rl = createInterface({ input: process.stdin, output: process.stdout })
144
- const answer = await rl.question(` ${c(DEEP)}?${c(RESET)} ${c(BOLD)}Linter${c(RESET)} ${c(DIM)}(biome / eslint / none) [biome]:${c(RESET)} `)
145
- rl.close()
146
- const v = answer.trim().toLowerCase()
147
- if (v === 'eslint' || v === 'e') return 'eslint'
148
- if (v === 'none' || v === 'n') return 'none'
149
- return 'biome' // default
158
+ const value = await select({
159
+ message: 'Which linter would you like to use?',
160
+ options: [
161
+ { value: 'biome', label: 'Biome', hint: 'fast, single tool, recommended' },
162
+ { value: 'eslint', label: 'ESLint', hint: 'classic, huge ecosystem' },
163
+ { value: 'none', label: 'None', hint: 'add your own later' },
164
+ ],
165
+ initialValue: 'biome',
166
+ })
167
+ return exitIfCancel(value)
150
168
  }
151
169
 
152
170
  // ── Linter installers ─────────────────────────────────────────────────
@@ -311,7 +329,7 @@ async function scaffold(projectName, linter) {
311
329
  // ── Main ──────────────────────────────────────────────────────────────
312
330
  const banner = renderBanner()
313
331
  process.stdout.write('\n' + banner + '\n\n')
314
- process.stdout.write(` ${c(DIM)}desktop apps for Next.js developers${c(RESET)}\n`)
332
+ process.stdout.write(` ${c(DIM)}desktop apps for Next.js developers${c(RESET)}\n\n`)
315
333
 
316
334
  // ── Parse args ────────────────────────────────────────────────────────
317
335
  const argName = process.argv[2] && !process.argv[2].startsWith('--') ? process.argv[2] : null
@@ -324,8 +342,10 @@ const argLinter = (() => {
324
342
  return null
325
343
  })()
326
344
 
327
- const projectName = argName || (await promptForName())
345
+ intro('🦋 create-murasaki')
346
+ const projectName = argName || (await promptForName())
328
347
  const linter = argLinter || (await promptForLinter())
348
+ outro('Setting things up...')
329
349
 
330
350
  try {
331
351
  await scaffold(projectName, linter)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-murasaki",
3
- "version": "0.0.7",
3
+ "version": "0.1.0",
4
4
  "description": "Scaffolder for Murasaki apps. Run with `npm create murasaki@latest`.",
5
5
  "keywords": [
6
6
  "murasaki",
@@ -29,5 +29,8 @@
29
29
  "templates",
30
30
  "README.md",
31
31
  "LICENSE"
32
- ]
32
+ ],
33
+ "dependencies": {
34
+ "@clack/prompts": "^1.6.0"
35
+ }
33
36
  }
@@ -7,12 +7,6 @@
7
7
  "dev": "murasaki dev"
8
8
  },
9
9
  "dependencies": {
10
- "murasaki": "^0.0.3",
11
- "react": "^19.2.7",
12
- "react-dom": "^19.2.7"
13
- },
14
- "devDependencies": {
15
- "@types/react": "^19.2.17",
16
- "@types/react-dom": "^19.2.3"
10
+ "murasaki": "^0.1.0"
17
11
  }
18
12
  }
@@ -1,7 +1,7 @@
1
1
  // src/layout.tsx — wraps your app. Edit me to change the global shell.
2
2
  // Global styles live in src/globals.css (auto-injected by murasaki).
3
3
 
4
- import type { ReactNode } from 'react'
4
+ import type { Child } from 'murasaki/jsx'
5
5
  import type { Metadata } from 'murasaki'
6
6
 
7
7
  export const metadata: Metadata = {
@@ -13,7 +13,7 @@ export const metadata: Metadata = {
13
13
  },
14
14
  }
15
15
 
16
- export default function Layout({ children }: { children: ReactNode }) {
16
+ export default function Layout({ children }: { children?: Child }) {
17
17
  return (
18
18
  <html lang="en">
19
19
  <head>
@@ -4,6 +4,7 @@
4
4
  "module": "ESNext",
5
5
  "moduleResolution": "Bundler",
6
6
  "jsx": "react-jsx",
7
+ "jsxImportSource": "murasaki",
7
8
  "strict": true,
8
9
  "esModuleInterop": true,
9
10
  "allowSyntheticDefaultImports": true,