create-stator 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tony Sullivan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/index.js ADDED
@@ -0,0 +1,54 @@
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.
4
+ import { cp, readdir, readFile, rename, stat, writeFile } from 'node:fs/promises'
5
+ import { basename, join, resolve } from 'node:path'
6
+ import { fileURLToPath } from 'node:url'
7
+
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)
13
+ }
14
+
15
+ const dest = resolve(process.cwd(), target)
16
+ try {
17
+ const existing = await readdir(dest)
18
+ if (existing.length > 0) {
19
+ console.error(`create-stator: ${target} exists and is not empty — refusing to overwrite.`)
20
+ process.exit(1)
21
+ }
22
+ } catch {
23
+ // does not exist — fine
24
+ }
25
+
26
+ const templateDir = fileURLToPath(new URL('./template', import.meta.url))
27
+ await cp(templateDir, dest, { recursive: true })
28
+
29
+ // npm strips dotfiles from published packages; ship as _gitignore and rename.
30
+ try {
31
+ await stat(join(dest, '_gitignore'))
32
+ await rename(join(dest, '_gitignore'), join(dest, '.gitignore'))
33
+ } catch {
34
+ // already named .gitignore (running from the repo)
35
+ }
36
+
37
+ const name = basename(dest)
38
+ .toLowerCase()
39
+ .replace(/[^a-z0-9-]/g, '-')
40
+ const pkgPath = join(dest, 'package.json')
41
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf8'))
42
+ pkg.name = name
43
+ await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`)
44
+
45
+ console.log(`
46
+ Scaffolded ${name} into ${dest}
47
+
48
+ Next steps:
49
+ cd ${target}
50
+ pnpm install
51
+ pnpm dev # http://localhost:3000
52
+
53
+ Other scripts: pnpm typecheck · pnpm build · pnpm start
54
+ `)
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "create-stator",
3
+ "version": "1.0.0",
4
+ "description": "Scaffold a new Stator app: pnpm create stator my-app",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "create-stator": "./index.js"
9
+ },
10
+ "files": [
11
+ "index.js",
12
+ "template"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/statorjs/stator.git",
17
+ "directory": "packages/create-stator"
18
+ },
19
+ "homepage": "https://github.com/statorjs/stator#readme",
20
+ "engines": {
21
+ "node": ">=20"
22
+ }
23
+ }
@@ -0,0 +1,3 @@
1
+ node_modules/
2
+ dist/
3
+ .stator/
@@ -0,0 +1,7 @@
1
+ import { dirname, resolve } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { buildApp } from '@statorjs/stator/build'
4
+
5
+ const here = dirname(fileURLToPath(import.meta.url))
6
+ const result = await buildApp({ root: here, outDir: resolve(here, 'dist') })
7
+ console.log(`stator: built ${result.compiled} components → ${result.outDir}`)
@@ -0,0 +1,27 @@
1
+ import { defineMachine } from '@statorjs/stator/server'
2
+
3
+ type Events = { type: 'INCREMENT' } | { type: 'RESET' }
4
+
5
+ export default defineMachine({
6
+ name: 'CounterMachine',
7
+ lifecycle: 'session',
8
+ events: {} as Events,
9
+ context: { count: 0 },
10
+ initial: 'idle',
11
+ states: {
12
+ idle: {
13
+ on: {
14
+ INCREMENT: (ctx) => {
15
+ ctx.count += 1
16
+ },
17
+ RESET: (ctx) => {
18
+ ctx.count = 0
19
+ },
20
+ },
21
+ },
22
+ },
23
+ selectors: {
24
+ count: (ctx) => ctx.count,
25
+ label: (ctx) => `count is ${ctx.count}`,
26
+ },
27
+ })
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "my-stator-app",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "sync": "tsx sync.ts",
8
+ "typecheck": "tsx sync.ts && tsc --noEmit",
9
+ "dev": "tsx watch server.ts",
10
+ "build": "tsx build.ts",
11
+ "start": "tsx start.ts"
12
+ },
13
+ "dependencies": {
14
+ "@statorjs/stator": "^1.0.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.7.0",
18
+ "tsx": "^4.19.0",
19
+ "typescript": "^5.6.0",
20
+ "vite": "^6.0.0",
21
+ "pino-pretty": "^13.1.3"
22
+ }
23
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ import Counter from '../machines/counter.ts'
3
+ import Layout from '../templates/layout.stator'
4
+
5
+ const [counter] = Stator.reads([Counter])
6
+ ---
7
+ <Layout>
8
+ <section class="counter">
9
+ <h1>Hello, Stator</h1>
10
+ <p class="count">{read(counter, (c) => c.label)}</p>
11
+ <button on:click={() => counter.send({ type: 'INCREMENT' })}>+1</button>
12
+ <button on:click={() => counter.send({ type: 'RESET' })}>reset</button>
13
+ </section>
14
+ </Layout>
@@ -0,0 +1,19 @@
1
+ import { dirname, resolve } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { createDevApp } from '@statorjs/stator/dev'
4
+
5
+ const here = dirname(fileURLToPath(import.meta.url))
6
+ const port = Number(process.env.PORT ?? 3000)
7
+
8
+ // Dev server: Vite compiles `.stator` on the way in, with live reload and the
9
+ // inspector toolbar. Production runs `pnpm build && pnpm start` instead.
10
+ // Sessions default to the in-memory store — swap in RedisStore (from
11
+ // '@statorjs/stator/server') to survive restarts.
12
+ const app = await createDevApp({
13
+ root: here,
14
+ machinesDir: resolve(here, 'machines'),
15
+ routesDir: resolve(here, 'routes'),
16
+ staticDir: resolve(here, 'static'),
17
+ })
18
+
19
+ await app.listen(port)
@@ -0,0 +1,25 @@
1
+ import { stat } from 'node:fs/promises'
2
+ import { dirname, resolve } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { loadProductionHead } from '@statorjs/stator/build'
5
+ import { createApp, logger } from '@statorjs/stator/server'
6
+
7
+ const here = dirname(fileURLToPath(import.meta.url))
8
+ const dist = resolve(here, 'dist')
9
+ const port = Number(process.env.PORT ?? 3000)
10
+
11
+ try {
12
+ await stat(resolve(dist, 'routes'))
13
+ } catch {
14
+ logger.error({}, 'no dist/ found — run `pnpm build` first')
15
+ process.exit(1)
16
+ }
17
+
18
+ const app = await createApp({
19
+ machinesDir: resolve(dist, 'machines'),
20
+ routesDir: resolve(dist, 'routes'),
21
+ staticDir: resolve(dist, 'static'),
22
+ headExtras: await loadProductionHead(dist),
23
+ })
24
+
25
+ await app.listen(port)
@@ -0,0 +1,5 @@
1
+ :root { font-family: system-ui, sans-serif; color-scheme: light dark; }
2
+ body { margin: 0; display: grid; place-items: center; min-height: 100vh; }
3
+ .counter { text-align: center; }
4
+ .count { font-size: 1.5rem; font-variant-numeric: tabular-nums; }
5
+ button { font: inherit; padding: 0.4rem 1rem; margin: 0 0.25rem; cursor: pointer; }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Ambient type for `.stator` single-file components, for this app's TypeScript
3
+ * project. (Stator apps include one of these, like Vite apps include env.d.ts.)
4
+ */
5
+ declare module '*.stator' {
6
+ import type { HtmlFragment } from '@statorjs/stator/template'
7
+
8
+ // biome-ignore lint/suspicious/noExplicitAny: the wildcard fallback must admit every component's props — `Record<string, unknown>` would reject interfaces without index signatures
9
+ const component: (props?: any) => HtmlFragment
10
+ export default component
11
+ }
@@ -0,0 +1,7 @@
1
+ import { dirname } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { syncTypes } from '@statorjs/stator/build'
4
+
5
+ const here = dirname(fileURLToPath(import.meta.url))
6
+ const { written } = await syncTypes(here)
7
+ console.log(`stator: wrote ${written} .stator.d.ts files`)
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>stator app</title>
7
+ <link rel="stylesheet" href="/static/app.css" />
8
+ </head>
9
+ <body>
10
+ <main><children /></main>
11
+ </body>
12
+ </html>
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": ["ES2022", "DOM"],
7
+ "strict": true,
8
+ "noUncheckedIndexedAccess": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "isolatedModules": true,
12
+ "allowImportingTsExtensions": true,
13
+ "noEmit": true,
14
+ "rootDirs": [".", ".stator/types"]
15
+ },
16
+ "include": ["**/*.ts"],
17
+ "exclude": ["node_modules", "static", "dist"]
18
+ }