tutuca 0.9.84 → 0.9.85

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.
@@ -15994,7 +15994,7 @@ import { existsSync, readFileSync } from "node:fs";
15994
15994
  import { dirname, resolve } from "node:path";
15995
15995
  import { fileURLToPath } from "node:url";
15996
15996
  var describe = "Print a machine-readable schema (commands, flags, exit codes) as JSON.";
15997
- var SCHEMA_VERSION = 2;
15997
+ var SCHEMA_VERSION = 3;
15998
15998
  var GLOBAL_FLAGS = [
15999
15999
  {
16000
16000
  name: "json",
@@ -16126,6 +16126,45 @@ var NO_MODULE_COMMANDS_META = {
16126
16126
  ],
16127
16127
  positionals: []
16128
16128
  },
16129
+ storybook: {
16130
+ describe: "Serve a live storybook for the project, auto-discovering co-located *.dev.js modules.",
16131
+ needsModule: false,
16132
+ flags: [
16133
+ {
16134
+ name: "port",
16135
+ type: "string",
16136
+ description: "Preferred port (default 4321; falls back to a free port)."
16137
+ },
16138
+ {
16139
+ name: "out",
16140
+ type: "string",
16141
+ description: "Write a static index.html + bootstrap (CDN import map) instead of serving."
16142
+ },
16143
+ {
16144
+ name: "no-margaui",
16145
+ type: "boolean",
16146
+ description: "Render unstyled (skip margaui)."
16147
+ },
16148
+ {
16149
+ name: "no-check",
16150
+ type: "boolean",
16151
+ description: "Skip the in-browser check(app)."
16152
+ },
16153
+ {
16154
+ name: "no-tests",
16155
+ type: "boolean",
16156
+ description: "Skip running the modules' getTests() before serving."
16157
+ },
16158
+ { name: "help", short: "h", type: "boolean" }
16159
+ ],
16160
+ positionals: [
16161
+ {
16162
+ name: "dir",
16163
+ required: false,
16164
+ description: "Project root to scan and serve (default: cwd)."
16165
+ }
16166
+ ]
16167
+ },
16129
16168
  "agent-context": {
16130
16169
  describe: "Print this schema as JSON.",
16131
16170
  needsModule: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tutuca",
3
- "version": "0.9.84",
3
+ "version": "0.9.85",
4
4
  "type": "module",
5
5
  "description": "Zero-dependency SPA framework with immutable state and virtual DOM",
6
6
  "main": "./dist/tutuca.js",
@@ -40,6 +40,7 @@ Use `--module=<path>` if the path conflicts with positional parsing.
40
40
  | `lint <module> [name]` | Run the linter; exits **2** on any error-level finding |
41
41
  | `render <module> [name]` | Render examples to HTML in a headless DOM. Filter by component name or `--title`/`--view`. Exits **3** on render crash |
42
42
  | `test <module> [name]` | Run tests defined by `getTests({ describe, test, expect })`. Filter by component name, `--grep <pattern>`, or `--bail`. Exits **4** on any failure |
43
+ | `storybook [dir]` | Serve a live storybook for the project, auto-discovering co-located `*.dev.js` modules. Flags: `--port`, `--out`, `--no-margaui`, `--no-check`, `--no-tests`. No module path needed |
43
44
  | `help [cmd]` | Show usage. No module path needed |
44
45
  | `feedback [message]` | Append a feedback note (positional or stdin) to `~/.tutuca/feedback.jsonl`. No module path needed |
45
46
  | `install-skill [name]` | Copy a bundled skill (`tutuca`, `margaui`, `immutable-js`, or `--all`) into `.claude/skills/`. No module path needed |
@@ -187,6 +188,61 @@ export function getTests({ describe, test, expect }) {
187
188
  `tutuca test <module> Counter` picks it up. Untagged `test(...)` at the
188
189
  top of a tagged `describe` inherits the tag.
189
190
 
191
+ ## storybook — live component catalog
192
+
193
+ `tutuca storybook [dir]` serves a browser storybook for a project with no
194
+ setup. It recursively discovers co-located `*.dev.js` modules (see the
195
+ `.dev.js` convention below), mounts them via the shipped `tutuca/storybook`
196
+ library, and serves an ephemeral page — no config, no HTML to write.
197
+
198
+ ```sh
199
+ tutuca storybook # scan + serve the current directory
200
+ tutuca storybook ./packages/ui # scan + serve another directory
201
+ tutuca storybook --port 4321 # preferred port (falls back to a free one if taken)
202
+ tutuca storybook --out ./_site # write a static index.html + bootstrap instead of serving
203
+ tutuca storybook --no-tests # skip the pre-serve getTests() run
204
+ tutuca storybook --no-margaui # render unstyled (skip margaui)
205
+ tutuca storybook --no-check # skip the in-browser check(app)
206
+ ```
207
+
208
+ It is **batteries-included by default**: before serving it runs each module's
209
+ `getTests()` in the terminal, the page wires margaui styling, and the browser
210
+ runs `check(app)`. Each is individually disablable with the `--no-*` flags.
211
+
212
+ How tutuca itself is resolved (convention over configuration): a local
213
+ `node_modules/tutuca` install if present, else the CLI's own `dist`, else the
214
+ version-pinned CDN. All tutuca specifiers resolve to a single runtime, which
215
+ component scope/identity requires. `--out` always pins the CDN so the static
216
+ artifact is portable (host it from the project root so `/*.dev.js` paths resolve).
217
+
218
+ ### The `.dev.js` convention
219
+
220
+ A `*.dev.js` file is a **dev-only module**: it holds stories
221
+ (`getComponents()` + `getExamples()`), tests (`getTests()`), and any other
222
+ development-time helpers for nearby components, and is **never shipped to
223
+ production or the UI**. The `.dev.js` suffix is the contract — your app imports
224
+ its real components directly and never a `.dev.js`, and a production build glob
225
+ can exclude `**/*.dev.js`. Because they follow the full module convention, the
226
+ same files are valid targets for `tutuca test`/`lint`/`render` too.
227
+
228
+ ```js
229
+ // counter.dev.js — lives next to counter.js
230
+ import { component, html } from "tutuca";
231
+ import { Counter } from "./counter.js";
232
+
233
+ export function getComponents() {
234
+ return [Counter];
235
+ }
236
+ export function getExamples() {
237
+ return { title: "Counter", items: [{ title: "Basic", value: Counter.make({}) }] };
238
+ }
239
+ export function getTests({ describe, test, expect }) {
240
+ describe(Counter, () => {
241
+ test("starts at zero", () => expect(Counter.make({}).count).toBe(0));
242
+ });
243
+ }
244
+ ```
245
+
190
246
  ## Install skill assets
191
247
 
192
248
  `tutuca install-skill` copies bundled Claude Code skill files into
@@ -988,6 +988,12 @@ export, alias it instead of teaching tools a new name:
988
988
  export { allMyComponents as getComponents } from "./app.js";
989
989
  ```
990
990
 
991
+ Put these exports in a co-located **`*.dev.js`** file (a dev-only module
992
+ holding stories + tests, never shipped) and `tutuca storybook` auto-discovers
993
+ and renders them with no setup — see [cli.md](./cli.md). The same shape is
994
+ consumed by the shipped `tutuca/storybook` library (`mountStorybook`,
995
+ `buildStorybook`) if you want to embed a storybook in your own page.
996
+
991
997
  ## See also
992
998
 
993
999
  - [component-design.md](./component-design.md) — design judgment for shaping a