create-stator 1.2.0 → 1.4.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 +102 -3
  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,29 +27,81 @@ 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
+ },
45
+ {
46
+ value: 'weather',
47
+ label: 'Weather',
48
+ hint: 'live forecasts over SSE, entry-effect data loading, a canvas island',
49
+ },
29
50
  ]
30
51
 
31
52
  /** First-party templates resolve into the monorepo's examples/. */
32
53
  const FIRST_PARTY = 'gh:statorjs/stator/examples'
33
54
  /** Scaffolded apps get a real semver for the framework (the examples
34
55
  * themselves use workspace linking in-repo). Bumped with releases. */
35
- const STATOR_RANGE = '^1.1.0'
56
+ const STATOR_RANGE = '^1.4.0'
36
57
 
37
58
  const { values: flags, positionals } = parseArgs({
38
59
  allowPositionals: true,
39
60
  options: {
40
61
  template: { type: 'string', short: 't' },
41
62
  ref: { type: 'string' },
63
+ install: { type: 'boolean' },
64
+ 'no-install': { type: 'boolean' },
65
+ git: { type: 'boolean' },
66
+ 'no-git': { type: 'boolean' },
67
+ yes: { type: 'boolean', short: 'y' },
42
68
  help: { type: 'boolean', short: 'h' },
43
69
  },
44
70
  })
45
71
 
72
+ /** The package manager that LAUNCHED us (`pnpm create stator` → pnpm), via
73
+ * the standard npm_config_user_agent header. Defaults to npm. */
74
+ function detectPackageManager() {
75
+ const agent = process.env.npm_config_user_agent ?? ''
76
+ if (agent.startsWith('pnpm')) return 'pnpm'
77
+ if (agent.startsWith('yarn')) return 'yarn'
78
+ if (agent.startsWith('bun')) return 'bun'
79
+ return 'npm'
80
+ }
81
+ const pm = detectPackageManager()
82
+ const runCmd = (script) => (pm === 'npm' ? `npm run ${script}` : `${pm} ${script}`)
83
+
84
+ /** Resolve a yes/no choice: explicit flag > --yes > prompt (TTY) > safe
85
+ * default for non-interactive runs. */
86
+ async function decide({ yesFlag, noFlag, message, fallback }) {
87
+ if (yesFlag) return true
88
+ if (noFlag) return false
89
+ if (flags.yes) return true
90
+ if (!process.stdout.isTTY) return fallback
91
+ const answer = await p.confirm({ message, initialValue: true })
92
+ bailIfCancelled(answer)
93
+ return answer
94
+ }
95
+
46
96
  if (flags.help) {
47
97
  console.log(
48
98
  `Usage: pnpm create stator [directory] [--template ${TEMPLATES.map((t) => t.value).join('|')}]`,
49
99
  )
50
100
  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')
101
+ console.log(' --ref <branch|tag> fetch first-party templates from a specific ref')
102
+ console.log(' --install/--no-install install dependencies after scaffolding')
103
+ console.log(' --git/--no-git initialize a git repository')
104
+ console.log(' -y, --yes accept all defaults (install + git)')
52
105
  process.exit(0)
53
106
  }
54
107
 
@@ -128,7 +181,53 @@ try {
128
181
  s.stop(`Scaffolded ${name} (${template}) — no package.json to stamp`)
129
182
  }
130
183
 
131
- p.note([`cd ${target}`, 'pnpm install', 'pnpm dev'].join('\n'), 'Next steps')
184
+ // --- install? ---
185
+ const wantInstall = await decide({
186
+ yesFlag: flags.install,
187
+ noFlag: flags['no-install'],
188
+ message: `Install dependencies with ${pm}?`,
189
+ fallback: false,
190
+ })
191
+ let installed = false
192
+ if (wantInstall) {
193
+ const si = p.spinner()
194
+ si.start(`Installing with ${pm}`)
195
+ installed = await new Promise((done) => {
196
+ const child = spawn(pm, ['install'], { cwd: dest, stdio: 'ignore', shell: true })
197
+ child.on('close', (code) => done(code === 0))
198
+ child.on('error', () => done(false))
199
+ })
200
+ si.stop(installed ? 'Dependencies installed' : `${pm} install failed — run it yourself`)
201
+ }
202
+
203
+ // --- git? ---
204
+ const wantGit = await decide({
205
+ yesFlag: flags.git,
206
+ noFlag: flags['no-git'],
207
+ message: 'Initialize a git repository?',
208
+ fallback: false,
209
+ })
210
+ if (wantGit) {
211
+ const hasGit = spawnSync('git', ['--version'], { stdio: 'ignore' }).status === 0
212
+ if (hasGit) {
213
+ spawnSync('git', ['init', '-q'], { cwd: dest, stdio: 'ignore' })
214
+ spawnSync('git', ['add', '-A'], { cwd: dest, stdio: 'ignore' })
215
+ // May no-op if user.name/email are unset — an initialized repo without
216
+ // a first commit is still a win.
217
+ spawnSync('git', ['commit', '-q', '-m', 'Initial commit from create-stator'], {
218
+ cwd: dest,
219
+ stdio: 'ignore',
220
+ })
221
+ p.log.success('Initialized a git repository')
222
+ } else {
223
+ p.log.warn('git not found — skipped repository setup')
224
+ }
225
+ }
226
+
227
+ p.note(
228
+ [`cd ${target}`, ...(installed ? [] : [`${pm} install`]), runCmd('dev')].join('\n'),
229
+ 'Next steps',
230
+ )
132
231
  p.outro('Docs: https://docs.statorjs.dev · Demo: https://demo.statorjs.dev')
133
232
 
134
233
  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.4.0",
4
4
  "description": "Scaffold a new Stator app: pnpm create stator my-app",
5
5
  "license": "MIT",
6
6
  "type": "module",