create-stator 1.2.0 → 1.3.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.
Files changed (2) hide show
  1. package/index.js +96 -2
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -9,6 +9,7 @@
9
9
  // pnpm create stator → prompts
10
10
  // pnpm create stator my-app --template todomvc → no prompts
11
11
  // pnpm create stator my-app -t github:you/your-template
12
+ import { spawn, spawnSync } from 'node:child_process'
12
13
  import { readdir, readFile, writeFile } from 'node:fs/promises'
13
14
  import { basename, join, resolve } from 'node:path'
14
15
  import { parseArgs } from 'node:util'
@@ -26,6 +27,21 @@ const TEMPLATES = [
26
27
  label: 'TodoMVC',
27
28
  hint: 'the classic, with server-owned todos and zero-JS editing',
28
29
  },
30
+ {
31
+ value: 'desksmith',
32
+ label: 'Desksmith',
33
+ hint: "the tutorial's finished app — catalog, cart, checkout, theme island",
34
+ },
35
+ {
36
+ value: 'live-poll',
37
+ label: 'Live poll',
38
+ hint: 'shared app-machine state, pushed to every visitor over SSE',
39
+ },
40
+ {
41
+ value: 'with-auth',
42
+ label: 'With auth',
43
+ hint: 'accounts, guarded login, roles, session rotation (Node 24+)',
44
+ },
29
45
  ]
30
46
 
31
47
  /** First-party templates resolve into the monorepo's examples/. */
@@ -39,16 +55,48 @@ const { values: flags, positionals } = parseArgs({
39
55
  options: {
40
56
  template: { type: 'string', short: 't' },
41
57
  ref: { type: 'string' },
58
+ install: { type: 'boolean' },
59
+ 'no-install': { type: 'boolean' },
60
+ git: { type: 'boolean' },
61
+ 'no-git': { type: 'boolean' },
62
+ yes: { type: 'boolean', short: 'y' },
42
63
  help: { type: 'boolean', short: 'h' },
43
64
  },
44
65
  })
45
66
 
67
+ /** The package manager that LAUNCHED us (`pnpm create stator` → pnpm), via
68
+ * the standard npm_config_user_agent header. Defaults to npm. */
69
+ function detectPackageManager() {
70
+ const agent = process.env.npm_config_user_agent ?? ''
71
+ if (agent.startsWith('pnpm')) return 'pnpm'
72
+ if (agent.startsWith('yarn')) return 'yarn'
73
+ if (agent.startsWith('bun')) return 'bun'
74
+ return 'npm'
75
+ }
76
+ const pm = detectPackageManager()
77
+ const runCmd = (script) => (pm === 'npm' ? `npm run ${script}` : `${pm} ${script}`)
78
+
79
+ /** Resolve a yes/no choice: explicit flag > --yes > prompt (TTY) > safe
80
+ * default for non-interactive runs. */
81
+ async function decide({ yesFlag, noFlag, message, fallback }) {
82
+ if (yesFlag) return true
83
+ if (noFlag) return false
84
+ if (flags.yes) return true
85
+ if (!process.stdout.isTTY) return fallback
86
+ const answer = await p.confirm({ message, initialValue: true })
87
+ bailIfCancelled(answer)
88
+ return answer
89
+ }
90
+
46
91
  if (flags.help) {
47
92
  console.log(
48
93
  `Usage: pnpm create stator [directory] [--template ${TEMPLATES.map((t) => t.value).join('|')}]`,
49
94
  )
50
95
  console.log(' pnpm create stator my-app --template github:user/repo/path')
51
- console.log(' --ref <branch|tag> fetch first-party templates from a specific ref')
96
+ console.log(' --ref <branch|tag> fetch first-party templates from a specific ref')
97
+ console.log(' --install/--no-install install dependencies after scaffolding')
98
+ console.log(' --git/--no-git initialize a git repository')
99
+ console.log(' -y, --yes accept all defaults (install + git)')
52
100
  process.exit(0)
53
101
  }
54
102
 
@@ -128,7 +176,53 @@ try {
128
176
  s.stop(`Scaffolded ${name} (${template}) — no package.json to stamp`)
129
177
  }
130
178
 
131
- p.note([`cd ${target}`, 'pnpm install', 'pnpm dev'].join('\n'), 'Next steps')
179
+ // --- install? ---
180
+ const wantInstall = await decide({
181
+ yesFlag: flags.install,
182
+ noFlag: flags['no-install'],
183
+ message: `Install dependencies with ${pm}?`,
184
+ fallback: false,
185
+ })
186
+ let installed = false
187
+ if (wantInstall) {
188
+ const si = p.spinner()
189
+ si.start(`Installing with ${pm}`)
190
+ installed = await new Promise((done) => {
191
+ const child = spawn(pm, ['install'], { cwd: dest, stdio: 'ignore', shell: true })
192
+ child.on('close', (code) => done(code === 0))
193
+ child.on('error', () => done(false))
194
+ })
195
+ si.stop(installed ? 'Dependencies installed' : `${pm} install failed — run it yourself`)
196
+ }
197
+
198
+ // --- git? ---
199
+ const wantGit = await decide({
200
+ yesFlag: flags.git,
201
+ noFlag: flags['no-git'],
202
+ message: 'Initialize a git repository?',
203
+ fallback: false,
204
+ })
205
+ if (wantGit) {
206
+ const hasGit = spawnSync('git', ['--version'], { stdio: 'ignore' }).status === 0
207
+ if (hasGit) {
208
+ spawnSync('git', ['init', '-q'], { cwd: dest, stdio: 'ignore' })
209
+ spawnSync('git', ['add', '-A'], { cwd: dest, stdio: 'ignore' })
210
+ // May no-op if user.name/email are unset — an initialized repo without
211
+ // a first commit is still a win.
212
+ spawnSync('git', ['commit', '-q', '-m', 'Initial commit from create-stator'], {
213
+ cwd: dest,
214
+ stdio: 'ignore',
215
+ })
216
+ p.log.success('Initialized a git repository')
217
+ } else {
218
+ p.log.warn('git not found — skipped repository setup')
219
+ }
220
+ }
221
+
222
+ p.note(
223
+ [`cd ${target}`, ...(installed ? [] : [`${pm} install`]), runCmd('dev')].join('\n'),
224
+ 'Next steps',
225
+ )
132
226
  p.outro('Docs: https://docs.statorjs.dev · Demo: https://demo.statorjs.dev')
133
227
 
134
228
  function bailIfCancelled(v) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-stator",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Scaffold a new Stator app: pnpm create stator my-app",
5
5
  "license": "MIT",
6
6
  "type": "module",