create-stator 1.0.0 → 1.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.js CHANGED
@@ -1,29 +1,82 @@
1
1
  #!/usr/bin/env node
2
- // Minimal Stator scaffolder: copies the embedded template into a new
3
- // directory and stamps the project name. No prompts, no network.
2
+ // Stator scaffolder. Interactive (clack) when run bare; every prompt has a
3
+ // flag so CI and scripts stay prompt-free:
4
+ // pnpm create stator → prompts for directory + template
5
+ // pnpm create stator my-app --template minimal → no prompts
4
6
  import { cp, readdir, readFile, rename, stat, writeFile } from 'node:fs/promises'
5
7
  import { basename, join, resolve } from 'node:path'
6
8
  import { fileURLToPath } from 'node:url'
9
+ import { parseArgs } from 'node:util'
10
+ import * as p from '@clack/prompts'
7
11
 
8
- const target = process.argv[2]
9
- if (!target) {
10
- console.log('Usage: pnpm create stator <directory>')
11
- console.log(' npm create stator@latest <directory>')
12
- process.exit(1)
12
+ const TEMPLATES = [
13
+ {
14
+ value: 'minimal',
15
+ label: 'Minimal',
16
+ hint: 'one machine, one page — the smallest real app',
17
+ },
18
+ ]
19
+
20
+ const { values: flags, positionals } = parseArgs({
21
+ allowPositionals: true,
22
+ options: {
23
+ template: { type: 'string', short: 't' },
24
+ help: { type: 'boolean', short: 'h' },
25
+ },
26
+ })
27
+
28
+ if (flags.help) {
29
+ console.log(
30
+ `Usage: pnpm create stator [directory] [--template ${TEMPLATES.map((t) => t.value).join('|')}]`,
31
+ )
32
+ process.exit(0)
13
33
  }
14
34
 
35
+ p.intro('create-stator')
36
+
37
+ // --- target directory ---
38
+ let target = positionals[0]
39
+ if (!target) {
40
+ const answer = await p.text({
41
+ message: 'Where should the project live?',
42
+ placeholder: './my-stator-app',
43
+ validate: (v) => (v.trim() === '' ? 'A directory is required.' : undefined),
44
+ })
45
+ bailIfCancelled(answer)
46
+ target = answer
47
+ }
15
48
  const dest = resolve(process.cwd(), target)
49
+
16
50
  try {
17
51
  const existing = await readdir(dest)
18
52
  if (existing.length > 0) {
19
- console.error(`create-stator: ${target} exists and is not empty — refusing to overwrite.`)
53
+ p.cancel(`${target} exists and is not empty — refusing to overwrite.`)
20
54
  process.exit(1)
21
55
  }
22
56
  } catch {
23
57
  // does not exist — fine
24
58
  }
25
59
 
26
- const templateDir = fileURLToPath(new URL('./template', import.meta.url))
60
+ // --- template ---
61
+ let template = flags.template
62
+ if (template && !TEMPLATES.some((t) => t.value === template)) {
63
+ p.cancel(`Unknown template "${template}". Available: ${TEMPLATES.map((t) => t.value).join(', ')}`)
64
+ process.exit(1)
65
+ }
66
+ if (!template) {
67
+ if (TEMPLATES.length === 1) {
68
+ template = TEMPLATES[0].value
69
+ } else {
70
+ const answer = await p.select({ message: 'Which template?', options: TEMPLATES })
71
+ bailIfCancelled(answer)
72
+ template = answer
73
+ }
74
+ }
75
+
76
+ // --- scaffold ---
77
+ const s = p.spinner()
78
+ s.start('Copying template')
79
+ const templateDir = fileURLToPath(new URL(`./templates/${template}`, import.meta.url))
27
80
  await cp(templateDir, dest, { recursive: true })
28
81
 
29
82
  // npm strips dotfiles from published packages; ship as _gitignore and rename.
@@ -41,14 +94,14 @@ const pkgPath = join(dest, 'package.json')
41
94
  const pkg = JSON.parse(await readFile(pkgPath, 'utf8'))
42
95
  pkg.name = name
43
96
  await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`)
97
+ s.stop(`Scaffolded ${name} (${template})`)
44
98
 
45
- console.log(`
46
- Scaffolded ${name} into ${dest}
47
-
48
- Next steps:
49
- cd ${target}
50
- pnpm install
51
- pnpm dev # http://localhost:3000
99
+ p.note([`cd ${target}`, 'pnpm install', 'pnpm dev'].join('\n'), 'Next steps')
100
+ p.outro('Docs: https://docs.statorjs.dev · Demo: https://demo.statorjs.dev')
52
101
 
53
- Other scripts: pnpm typecheck · pnpm build · pnpm start
54
- `)
102
+ function bailIfCancelled(v) {
103
+ if (p.isCancel(v)) {
104
+ p.cancel('Cancelled.')
105
+ process.exit(0)
106
+ }
107
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-stator",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Scaffold a new Stator app: pnpm create stator my-app",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "files": [
11
11
  "index.js",
12
- "template"
12
+ "templates"
13
13
  ],
14
14
  "repository": {
15
15
  "type": "git",
@@ -19,5 +19,8 @@
19
19
  "homepage": "https://github.com/statorjs/stator#readme",
20
20
  "engines": {
21
21
  "node": ">=20"
22
+ },
23
+ "dependencies": {
24
+ "@clack/prompts": "^1.7.0"
22
25
  }
23
26
  }
@@ -0,0 +1,31 @@
1
+ # A Stator app
2
+
3
+ ```sh
4
+ pnpm install
5
+ pnpm dev # dev server with live reload + the wire inspector
6
+ pnpm typecheck # sync generated component types, then tsc
7
+ pnpm build # production build into dist/
8
+ pnpm start # serve the production build
9
+ ```
10
+
11
+ ## The mental model
12
+
13
+ State lives **on the server**, in machines. Pages declare what they read;
14
+ when an event changes a machine, the server diffs and sends small JSON
15
+ patches to the browser. There is no client renderer and no API layer.
16
+
17
+ - `machines/counter.ts` — a session-scoped machine: typed events, guarded
18
+ transitions, selectors. Session state survives a page reload because the
19
+ server owns it.
20
+ - `routes/index.stator` — a page: frontmatter declares `Stator.reads`, the
21
+ template calls `read(machine, selector)` wherever live values render.
22
+ - `templates/layout.stator` — a component with a `<children />` slot.
23
+
24
+ Try it: run `pnpm dev`, click **+1**, then reload the page — the count
25
+ persists. Open the inspector (bottom of the page) and click again to watch
26
+ the patch arrive on the wire.
27
+
28
+ ## Learn more
29
+
30
+ - Docs: https://docs.statorjs.dev — start with the tutorial
31
+ - Reference app: https://demo.statorjs.dev ([source](https://github.com/statorjs/stator/tree/main/apps/store))
@@ -11,7 +11,7 @@
11
11
  "start": "tsx start.ts"
12
12
  },
13
13
  "dependencies": {
14
- "@statorjs/stator": "^1.0.0"
14
+ "@statorjs/stator": "^1.1.0"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@types/node": "^22.7.0",
@@ -3,7 +3,11 @@
3
3
  "target": "ES2022",
4
4
  "module": "ESNext",
5
5
  "moduleResolution": "Bundler",
6
- "lib": ["ES2022", "DOM"],
6
+ "lib": [
7
+ "ES2022",
8
+ "DOM",
9
+ "DOM.Iterable"
10
+ ],
7
11
  "strict": true,
8
12
  "noUncheckedIndexedAccess": true,
9
13
  "esModuleInterop": true,
@@ -11,8 +15,19 @@
11
15
  "isolatedModules": true,
12
16
  "allowImportingTsExtensions": true,
13
17
  "noEmit": true,
14
- "rootDirs": [".", ".stator/types"]
18
+ "rootDirs": [
19
+ ".",
20
+ ".stator/types"
21
+ ],
22
+ "jsx": "preserve"
15
23
  },
16
- "include": ["**/*.ts"],
17
- "exclude": ["node_modules", "static", "dist"]
24
+ "include": [
25
+ "**/*.ts",
26
+ "**/*.stator"
27
+ ],
28
+ "exclude": [
29
+ "node_modules",
30
+ "static",
31
+ "dist"
32
+ ]
18
33
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes