tutuca 0.9.44 → 0.9.45
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/dist/tutuca-cli.js +3764 -81
- package/dist/tutuca-dev.js +3851 -109
- package/dist/tutuca-dev.min.js +4 -3
- package/dist/tutuca-extra.js +4 -0
- package/dist/tutuca-extra.min.js +1 -1
- package/dist/tutuca.js +4 -0
- package/dist/tutuca.min.js +1 -1
- package/package.json +3 -2
- package/skill/tutuca/advanced.md +3 -4
- package/skill/tutuca/cli.md +78 -7
- package/skill/tutuca/core.md +19 -6
- package/skill/SKILL.md +0 -46
- package/skill/advanced.md +0 -146
- package/skill/cli.md +0 -117
- package/skill/core.md +0 -766
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tutuca",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.45",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Zero-dependency SPA framework with immutable state and virtual DOM",
|
|
6
6
|
"main": "./dist/tutuca.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"fix": "bunx @biomejs/biome check --write src test/*.js",
|
|
34
34
|
"tutuca": "bun tools/tutuca.js",
|
|
35
35
|
"stresstest": "bun scripts/stresstest.js",
|
|
36
|
-
"smoke-test": "bun tools/tutuca.js ./test/todo.js lint && bun tools/tutuca.js ./test/todo.js render && bun tools/tutuca.js ./test/json.js lint && bun tools/tutuca.js ./test/json.js render"
|
|
36
|
+
"smoke-test": "bun tools/tutuca.js ./test/todo.js lint && bun tools/tutuca.js ./test/todo.js render && bun tools/tutuca.js ./test/todo.js test && bun tools/tutuca.js ./test/json.js lint && bun tools/tutuca.js ./test/json.js render"
|
|
37
37
|
},
|
|
38
38
|
"sideEffects": false,
|
|
39
39
|
"files": [
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"skill"
|
|
48
48
|
],
|
|
49
49
|
"dependencies": {
|
|
50
|
+
"chai": "^6.2.2",
|
|
50
51
|
"jsdom": "^28.0.0 || ^29.0.0"
|
|
51
52
|
},
|
|
52
53
|
"peerDependencies": {
|
package/skill/tutuca/advanced.md
CHANGED
|
@@ -145,7 +145,6 @@ collects the `class=` and `:class=` literals, hands them to a `compile`
|
|
|
145
145
|
function (any margaui-compatible signature), and returns CSS text. Pair
|
|
146
146
|
with `injectCss(scopeName, css)` to install the result before `start()`.
|
|
147
147
|
|
|
148
|
-
If a margaui skill is
|
|
149
|
-
|
|
150
|
-
class
|
|
151
|
-
class strings, which is what the `compile` step expects.
|
|
148
|
+
If a margaui skill is available, load it alongside this one when
|
|
149
|
+
authoring class lists — it lists the available components and their
|
|
150
|
+
canonical class strings, which is what the `compile` step expects.
|
package/skill/tutuca/cli.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Tutuca CLI Reference
|
|
2
2
|
|
|
3
|
-
The `tutuca` CLI inspects, documents, lints, and renders any
|
|
4
|
-
follows the *Conventional Module Exports* shape (see
|
|
5
|
-
this file when you need command/flag/exit-code
|
|
6
|
-
a lint code out of `lint` output. Otherwise
|
|
7
|
-
`core.md` (run `lint`, then `
|
|
8
|
-
enough.
|
|
3
|
+
The `tutuca` CLI inspects, documents, lints, tests, and renders any
|
|
4
|
+
module that follows the *Conventional Module Exports* shape (see
|
|
5
|
+
`core.md`). Reach this file when you need command/flag/exit-code
|
|
6
|
+
details, or when reading a lint code out of `lint` output. Otherwise
|
|
7
|
+
the post-edit recipe in `core.md` (run `lint`, then `test` for
|
|
8
|
+
behavior changes, then `render --title "<your example>"`) is enough.
|
|
9
9
|
|
|
10
10
|
## Install / invoke
|
|
11
11
|
|
|
@@ -36,6 +36,7 @@ prints a one-liner. `tutuca` ↔ `tutuca -h` prints overview.
|
|
|
36
36
|
| `docs [name]` | Generate API docs (methods, input handlers, fields with auto-generated accessors) — all or one |
|
|
37
37
|
| `lint [name]` | Run the linter; exits **2** on any error-level finding |
|
|
38
38
|
| `render [name]` | Render examples to HTML in a headless DOM. Filter by component name or `--title`/`--view`. Exits **3** on render crash |
|
|
39
|
+
| `test [name]` | Run tests defined by `getTests({ describe, test, expect })`. Filter by component name, `--grep <pattern>`, or `--bail`. Exits **4** on any failure |
|
|
39
40
|
| `help [cmd]` | Show usage; the only command that does **not** need a module path |
|
|
40
41
|
|
|
41
42
|
## Global flags
|
|
@@ -61,6 +62,7 @@ prints a one-liner. `tutuca` ↔ `tutuca -h` prints overview.
|
|
|
61
62
|
| `1` | usage error (bad args, missing module, bad module shape) |
|
|
62
63
|
| `2` | lint findings at error level |
|
|
63
64
|
| `3` | render crash |
|
|
65
|
+
| `4` | one or more tests failed |
|
|
64
66
|
|
|
65
67
|
## Examples
|
|
66
68
|
|
|
@@ -77,8 +79,77 @@ tutuca ./src/components.js render Button --title "Disabled state"
|
|
|
77
79
|
tutuca ./src/components.js lint
|
|
78
80
|
tutuca ./src/components.js render --title "Disabled state"
|
|
79
81
|
tutuca ./src/components.js render --title "Disabled state" --pretty
|
|
82
|
+
|
|
83
|
+
# Component-behavior verification: run the suite for one component, or
|
|
84
|
+
# narrow further with --grep. Add tests next to the component (the
|
|
85
|
+
# getTests() export) when the change isn't observable from render alone.
|
|
86
|
+
tutuca ./src/components.js test Counter
|
|
87
|
+
tutuca ./src/components.js test Counter --grep "inc()"
|
|
88
|
+
tutuca ./src/components.js test --bail
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## `test` — running component tests
|
|
92
|
+
|
|
93
|
+
Use `test` after edits that change attributes, instance methods, input
|
|
94
|
+
handlers, or static factories — anything observable from JS rather than
|
|
95
|
+
from rendered HTML. The module opts in by exporting
|
|
96
|
+
`getTests({ describe, test, expect })`:
|
|
97
|
+
|
|
98
|
+
- `describe(Component, fn)` tags the suite with `Component.name` so
|
|
99
|
+
the positional `[name]` filter can pick it.
|
|
100
|
+
- `describe(title, fn)` is untagged; reachable only via `--grep`.
|
|
101
|
+
- `describe(title, { component }, fn)` tags an explicit title with a
|
|
102
|
+
custom component name.
|
|
103
|
+
- `test(title, fn)` — `fn` may be async; assertions use the injected
|
|
104
|
+
chai `expect`.
|
|
105
|
+
|
|
106
|
+
Filters:
|
|
107
|
+
|
|
108
|
+
- `[name]` — only tests whose tagged `componentName` equals `<name>`.
|
|
109
|
+
- `--grep <p>` — substring match against the full path
|
|
110
|
+
(e.g. `"Counter > inc() > works on a negative counter"`).
|
|
111
|
+
- `--bail` — stop on first failure; remaining tests reported as `skip`.
|
|
112
|
+
|
|
113
|
+
Default format is `cli` (a tree with ✓/✗/○ and per-test durations);
|
|
114
|
+
`-f md` and `-f json` work too.
|
|
115
|
+
|
|
116
|
+
A worked `getTests()` export covering methods, input handlers (called
|
|
117
|
+
via `Comp.input.x.call(inst)`), and immutability:
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
export function getTests({ describe, test, expect }) {
|
|
121
|
+
describe(Counter, () => {
|
|
122
|
+
describe("inc()", () => { // method
|
|
123
|
+
test("returns a Counter with count + 1", () => {
|
|
124
|
+
const next = Counter.make().inc();
|
|
125
|
+
expect(next).to.be.instanceOf(Counter.Class);
|
|
126
|
+
expect(next.count).to.equal(1);
|
|
127
|
+
});
|
|
128
|
+
test("does not mutate the original instance", () => {
|
|
129
|
+
const c = Counter.make({ count: 7 });
|
|
130
|
+
c.inc();
|
|
131
|
+
expect(c.count).to.equal(7); // immutability
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("dec()", () => { // input handler
|
|
136
|
+
test("returns a Counter with count - 1", () => {
|
|
137
|
+
const next = Counter.input.dec.call(Counter.make());
|
|
138
|
+
expect(next.count).to.equal(-1);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("inc and dec round-trip", () => { // untagged path
|
|
143
|
+
expect(Counter.input.dec.call(Counter.make().inc()).count).to.equal(0);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
80
147
|
```
|
|
81
148
|
|
|
149
|
+
`describe(Counter, fn)` auto-tags the suite path with `Counter.name`, so
|
|
150
|
+
`tutuca <module> test Counter` picks it up. Untagged `test(...)` at the
|
|
151
|
+
top of a tagged `describe` inherits the tag.
|
|
152
|
+
|
|
82
153
|
## Install skill assets
|
|
83
154
|
|
|
84
155
|
`tutuca install-skill` copies bundled Claude Code skill files into
|
|
@@ -100,7 +171,7 @@ tutuca install-skill --all --force # overwrite existing files
|
|
|
100
171
|
|
|
101
172
|
## Linter Rules
|
|
102
173
|
|
|
103
|
-
Codes emitted by `lint`.
|
|
174
|
+
Codes emitted by `lint`.
|
|
104
175
|
|
|
105
176
|
### Field references
|
|
106
177
|
|
package/skill/tutuca/core.md
CHANGED
|
@@ -15,8 +15,8 @@ the `tutuca` CLI.
|
|
|
15
15
|
|
|
16
16
|
## Verifying changes
|
|
17
17
|
|
|
18
|
-
After editing a Tutuca module, run
|
|
19
|
-
done:
|
|
18
|
+
After editing a Tutuca module, run these checks before declaring the
|
|
19
|
+
edit done:
|
|
20
20
|
|
|
21
21
|
1. **Lint the module** — catches undefined fields/handlers/macros/events
|
|
22
22
|
(all the `*_NOT_DEFINED` / `*_NOT_REFERENCED` codes):
|
|
@@ -26,7 +26,21 @@ done:
|
|
|
26
26
|
Exits `2` on any error-level finding. Pass a component name to scope
|
|
27
27
|
it: `tutuca <module-path> lint Button`.
|
|
28
28
|
|
|
29
|
-
2. **
|
|
29
|
+
2. **Test component behavior** — when the edit changes attributes,
|
|
30
|
+
instance methods, input handlers, or static factories (anything
|
|
31
|
+
observable from JS, not just the rendered HTML), run the test
|
|
32
|
+
suite. The module opts in by exporting
|
|
33
|
+
`getTests({ describe, test, expect })`:
|
|
34
|
+
|
|
35
|
+
tutuca <module-path> test
|
|
36
|
+
tutuca <module-path> test Counter # one component
|
|
37
|
+
tutuca <module-path> test --grep "inc()" # one path
|
|
38
|
+
|
|
39
|
+
Exits `4` on any failure. Skip this step when the change is purely
|
|
40
|
+
templates/styling — `render` already covers that. Full reference,
|
|
41
|
+
including a worked `getTests()` export, in [cli.md](./cli.md).
|
|
42
|
+
|
|
43
|
+
3. **Render the example(s) that exercise the feature you changed** —
|
|
30
44
|
confirms the component actually mounts in a headless DOM with the new
|
|
31
45
|
behavior. Pick the example whose `title` matches the feature, or
|
|
32
46
|
filter by component:
|
|
@@ -775,9 +789,8 @@ import {
|
|
|
775
789
|
```
|
|
776
790
|
|
|
777
791
|
Because every `immutable` export is reachable through `tutuca`, if an
|
|
778
|
-
`immutable-js` skill is
|
|
779
|
-
|
|
780
|
-
applies directly to the values you'll be reading and writing.
|
|
792
|
+
`immutable-js` skill is available, load it alongside this one — its
|
|
793
|
+
guidance applies directly to the values you'll be reading and writing.
|
|
781
794
|
|
|
782
795
|
## Conventional Module Exports
|
|
783
796
|
|
package/skill/SKILL.md
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: tutuca
|
|
3
|
-
description: Authoring or reviewing tutuca components, html`` views, macros, or running the `tutuca` CLI. Covers field types, @-directives, bubble/receive/response handlers, and the post-edit `tutuca <module> lint` + `tutuca <module> render --title …` verification recipe.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Tutuca
|
|
7
|
-
|
|
8
|
-
Tutuca is an immutable-state SPA framework: components have typed
|
|
9
|
-
`fields`, auto-generated mutators (`setX`, `pushInX`, …), HTML-template
|
|
10
|
-
`view`s with `@`-prefixed directives, and `bubble` / `receive` /
|
|
11
|
-
`response` handlers for orchestration.
|
|
12
|
-
|
|
13
|
-
## Verifying changes
|
|
14
|
-
|
|
15
|
-
After editing a tutuca module, run two checks before declaring the edit
|
|
16
|
-
done:
|
|
17
|
-
|
|
18
|
-
1. **Lint** — catches undefined fields/handlers/macros/events. Exits
|
|
19
|
-
`2` on any error-level finding.
|
|
20
|
-
|
|
21
|
-
```sh
|
|
22
|
-
tutuca <module-path> lint
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
2. **Render the example that exercises the feature you changed** —
|
|
26
|
-
confirms the component mounts in a headless DOM with the new
|
|
27
|
-
behavior. Exits `3` on render crash.
|
|
28
|
-
|
|
29
|
-
```sh
|
|
30
|
-
tutuca <module-path> render --title "<example title>"
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
If no example covers the feature, add one to `getExamples()` first —
|
|
34
|
-
that's how the feature becomes verifiable.
|
|
35
|
-
|
|
36
|
-
## Routing
|
|
37
|
-
|
|
38
|
-
| Task | File |
|
|
39
|
-
| ---------------------------------------------------------------------------------------------- | ------------------------------- |
|
|
40
|
-
| Authoring `component({...})`, `html\`...\`` views, macros, fields, events, lists, styles | [core.md](./core.md) |
|
|
41
|
-
| CLI commands, flags, exit codes, full linter rule list | [cli.md](./cli.md) |
|
|
42
|
-
| Drag & drop, dynamic bindings (`*x`), pseudo-`x`, custom seq types, Tailwind/MargaUI | [advanced.md](./advanced.md) |
|
|
43
|
-
|
|
44
|
-
Read `core.md` first. Reach for `cli.md` or `advanced.md` only when the
|
|
45
|
-
task touches them — both files are referenced inline from `core.md` so
|
|
46
|
-
you'll be pointed there when relevant.
|
package/skill/advanced.md
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
# Tutuca — Advanced Topics
|
|
2
|
-
|
|
3
|
-
Reach this file only when the task touches drag & drop, context-style
|
|
4
|
-
"dynamic bindings", pseudo-`x` (the `<x>`-stripping workaround inside
|
|
5
|
-
`<select>`/`<table>`/`<tr>`), registering a custom seq type, or
|
|
6
|
-
compiling Tailwind / MargaUI classes. For everything else, `core.md`
|
|
7
|
-
is the right place.
|
|
8
|
-
|
|
9
|
-
## Drag and Drop
|
|
10
|
-
|
|
11
|
-
```html
|
|
12
|
-
<div
|
|
13
|
-
@each=".items"
|
|
14
|
-
draggable="true"
|
|
15
|
-
data-dragtype="my-item"
|
|
16
|
-
data-droptarget="my-item"
|
|
17
|
-
@on.drop="onDrop @key dragInfo event"
|
|
18
|
-
></div>
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
```js
|
|
22
|
-
input: {
|
|
23
|
-
onDrop(targetKey, dragInfo, e) {
|
|
24
|
-
const sourceKey = dragInfo.lookupBind("key"); // any bind from source render
|
|
25
|
-
return this.setItems(this.items.moveKeyBeforeKey(sourceKey, targetKey));
|
|
26
|
-
},
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Tutuca auto-manages two attrs during a drag — style them with CSS:
|
|
31
|
-
|
|
32
|
-
```css
|
|
33
|
-
[data-dragging="1"] {
|
|
34
|
-
opacity: 0.5;
|
|
35
|
-
}
|
|
36
|
-
[data-draggingover="my-item"] {
|
|
37
|
-
outline: 1px dashed;
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Touch is wired up too (drag fires after a small move threshold).
|
|
42
|
-
|
|
43
|
-
## Dynamic Bindings
|
|
44
|
-
|
|
45
|
-
For passing values "context-style" through nested components without prop
|
|
46
|
-
drilling. Define on the producer; alias on consumers; resolve as `*name`.
|
|
47
|
-
|
|
48
|
-
```js
|
|
49
|
-
const Theme = component({
|
|
50
|
-
name: "Theme",
|
|
51
|
-
fields: { color: "blue" },
|
|
52
|
-
dynamic: { color: ".color" },
|
|
53
|
-
on: {
|
|
54
|
-
stackEnter() {
|
|
55
|
-
return ["color"];
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
const Child = component({
|
|
60
|
-
dynamic: { color: { for: "Theme.color", default: "'gray'" } },
|
|
61
|
-
view: html`<p :style="color: {*color}"></p>`,
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
`on.stackEnter()` is required only on the **producer** (the component
|
|
66
|
-
declaring `dynamic: { name: ".field" }` to expose a value). It returns
|
|
67
|
-
the list of dynamic-binding names this component pushes onto the stack
|
|
68
|
-
when entering its render. Consumers (which only alias via
|
|
69
|
-
`{ for: "Producer.name", default: ... }`) don't need it.
|
|
70
|
-
|
|
71
|
-
## Pseudo-`x` (`@x`)
|
|
72
|
-
|
|
73
|
-
Tutuca's special operations (`render`, `render-it`, `render-each`, `text`,
|
|
74
|
-
`show`, `hide`, `slot`) live on the `<x>` tag. That works almost
|
|
75
|
-
everywhere, but the browser's HTML parser refuses to keep `<x>` (or any
|
|
76
|
-
unknown tag) as a child of certain elements: `<select>` only allows
|
|
77
|
-
`<option>` / `<optgroup>`, `<table>` only allows `<thead>` / `<tbody>` /
|
|
78
|
-
`<tr>`, `<tr>` only allows `<th>` / `<td>`, etc. Drop `<x render-each>`
|
|
79
|
-
inside one of those and the parser silently strips it.
|
|
80
|
-
|
|
81
|
-
The escape hatch: prefix the **first** attribute on a *legal* tag with
|
|
82
|
-
`@x`. Tutuca treats that tag as if it were `<x>` and reads the next
|
|
83
|
-
attribute as the special op.
|
|
84
|
-
|
|
85
|
-
```html
|
|
86
|
-
<!-- ❌ <x> stripped by the HTML parser inside <select> -->
|
|
87
|
-
<select>
|
|
88
|
-
<x render-each=".items" as="option"></x>
|
|
89
|
-
</select>
|
|
90
|
-
|
|
91
|
-
<!-- ✅ pseudo-x: <option @x render-each=".items" as="option"> -->
|
|
92
|
-
<select>
|
|
93
|
-
<option @x render-each=".items" as="option"></option>
|
|
94
|
-
</select>
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
Notes:
|
|
98
|
-
|
|
99
|
-
- `@x` must be the **first** attribute; the special op (`render-each`,
|
|
100
|
-
`render`, `text`, `show`, ...) is the second.
|
|
101
|
-
- The host tag (here `<option>`) is otherwise ignored — only the special
|
|
102
|
-
op runs. Tutuca produces the rendered children directly.
|
|
103
|
-
- Same trick works inside `<tr>`, `<table>`, `<colgroup>`, `<dl>`,
|
|
104
|
-
`<details>`, or anywhere else the parser would discard `<x>`.
|
|
105
|
-
|
|
106
|
-
## Registering a custom seq type
|
|
107
|
-
|
|
108
|
-
To make `@each` recognize your own collection class, install a
|
|
109
|
-
`SEQ_INFO` walker on its prototype:
|
|
110
|
-
|
|
111
|
-
```js
|
|
112
|
-
import { SEQ_INFO } from "tutuca";
|
|
113
|
-
|
|
114
|
-
class MyClass {
|
|
115
|
-
// ...
|
|
116
|
-
}
|
|
117
|
-
MyClass.prototype[SEQ_INFO] = (seq, visit) => {
|
|
118
|
-
for (const [k, v] of seq.entries()) visit(k, v, "data-sk");
|
|
119
|
-
};
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
`SEQ_INFO` is `Symbol.for("tutuca.seqInfo")`, so the same identity
|
|
123
|
-
is shared across module graphs (source vs. bundled tutuca). The
|
|
124
|
-
renderer reads `seq[SEQ_INFO]` directly (no `.constructor` lookup),
|
|
125
|
-
which is why the walker goes on the prototype, not as a static.
|
|
126
|
-
|
|
127
|
-
The third arg to `visit` is the data attribute used for stable-key
|
|
128
|
-
diffing (typically `"data-sk"` for "sequence key").
|
|
129
|
-
|
|
130
|
-
## Tailwind / MargaUI Class Compilation (extra build)
|
|
131
|
-
|
|
132
|
-
```js
|
|
133
|
-
import { compileClassesToStyleText, injectCss, tutuca } from "tutuca/extra";
|
|
134
|
-
import { compile } from "https://esm.sh/margaui";
|
|
135
|
-
|
|
136
|
-
const app = tutuca("#app");
|
|
137
|
-
app.registerComponents([Comp]);
|
|
138
|
-
const css = await compileClassesToStyleText(app, compile);
|
|
139
|
-
injectCss("myapp", css);
|
|
140
|
-
app.start();
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
`compileClassesToStyleText` walks every registered component's templates,
|
|
144
|
-
collects the `class=` and `:class=` literals, hands them to a `compile`
|
|
145
|
-
function (any margaui-compatible signature), and returns CSS text. Pair
|
|
146
|
-
with `injectCss(scopeName, css)` to install the result before `start()`.
|
package/skill/cli.md
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
# Tutuca CLI Reference
|
|
2
|
-
|
|
3
|
-
The `tutuca` CLI inspects, documents, lints, and renders any module that
|
|
4
|
-
follows the *Conventional Module Exports* shape (see `core.md`). Reach
|
|
5
|
-
this file when you need command/flag/exit-code details, or when reading
|
|
6
|
-
a lint code out of `lint` output. Otherwise the post-edit recipe in
|
|
7
|
-
`core.md` (run `lint`, then `render --title "<your example>"`) is
|
|
8
|
-
enough.
|
|
9
|
-
|
|
10
|
-
## Install / invoke
|
|
11
|
-
|
|
12
|
-
```sh
|
|
13
|
-
# project local
|
|
14
|
-
npm i --save-dev tutuca
|
|
15
|
-
npx tutuca <module-path> <command> [name] [flags]
|
|
16
|
-
|
|
17
|
-
# global
|
|
18
|
-
npm i -g tutuca
|
|
19
|
-
tutuca <module-path> <command> [name] [flags]
|
|
20
|
-
|
|
21
|
-
# from a checkout of this repo
|
|
22
|
-
bun tools/tutuca.js <module-path> <command> [name] [flags]
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
The module path comes **first**, the command second, an optional component
|
|
26
|
-
name third. `tutuca help` prints the full reference; `tutuca help <command>`
|
|
27
|
-
prints a one-liner. `tutuca` ↔ `tutuca -h` prints overview.
|
|
28
|
-
|
|
29
|
-
## Commands
|
|
30
|
-
|
|
31
|
-
| Command | Purpose |
|
|
32
|
-
| --------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
33
|
-
| `info` | Summarize which `getX()` exports are present and counts |
|
|
34
|
-
| `list` | List components with their views and fields (name + type) |
|
|
35
|
-
| `examples` | Print `getExamples()` content (title, items, per section) |
|
|
36
|
-
| `docs [name]` | Generate API docs (methods, input handlers, fields with auto-generated accessors) — all or one |
|
|
37
|
-
| `lint [name]` | Run the linter; exits **2** on any error-level finding |
|
|
38
|
-
| `render [name]` | Render examples to HTML in a headless DOM. Filter by component name or `--title`/`--view`. Exits **3** on render crash |
|
|
39
|
-
| `help [cmd]` | Show usage; the only command that does **not** need a module path |
|
|
40
|
-
|
|
41
|
-
## Global flags
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
-f, --format <cli|md|json|html> output format
|
|
45
|
-
defaults: info/list/examples/lint → cli
|
|
46
|
-
docs/render → md
|
|
47
|
-
html only valid for render
|
|
48
|
-
json works for every command
|
|
49
|
-
-o, --output <file> write to file instead of stdout
|
|
50
|
-
--pretty pretty-print HTML (md/html) via prettier;
|
|
51
|
-
JSON formatter uses indent 2
|
|
52
|
-
--module <path> alternative to first-positional module path
|
|
53
|
-
-h, --help show help (overview, or for one command)
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Exit codes
|
|
57
|
-
|
|
58
|
-
| Code | Meaning |
|
|
59
|
-
| ---- | -------------------------------------------------------- |
|
|
60
|
-
| `0` | success |
|
|
61
|
-
| `1` | usage error (bad args, missing module, bad module shape) |
|
|
62
|
-
| `2` | lint findings at error level |
|
|
63
|
-
| `3` | render crash |
|
|
64
|
-
|
|
65
|
-
## Examples
|
|
66
|
-
|
|
67
|
-
```sh
|
|
68
|
-
tutuca ./src/components.js info # quick overview
|
|
69
|
-
tutuca ./src/components.js list # components, views, fields
|
|
70
|
-
tutuca ./src/components.js docs Button -f json # one component, JSON
|
|
71
|
-
tutuca ./src/components.js render -f html --pretty -o out/examples.html
|
|
72
|
-
tutuca ./src/components.js render Button --title "Disabled state"
|
|
73
|
-
|
|
74
|
-
# Post-edit verification: lint, then render the example for the feature
|
|
75
|
-
# you just changed (add the example first if none covers it). Add
|
|
76
|
-
# --pretty when you need to read the HTML to verify structure.
|
|
77
|
-
tutuca ./src/components.js lint
|
|
78
|
-
tutuca ./src/components.js render --title "Disabled state"
|
|
79
|
-
tutuca ./src/components.js render --title "Disabled state" --pretty
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Linter Rules
|
|
83
|
-
|
|
84
|
-
Codes emitted by `lint`. Source: `tools/core/lint-check.js`.
|
|
85
|
-
|
|
86
|
-
### Field references
|
|
87
|
-
|
|
88
|
-
- `FIELD_VAL_NOT_DEFINED` — `.field` not declared in `fields`.
|
|
89
|
-
|
|
90
|
-
### Input-handler / method confusion
|
|
91
|
-
|
|
92
|
-
- `INPUT_HANDLER_NOT_IMPLEMENTED` — bare handler name not in `input`.
|
|
93
|
-
- `INPUT_HANDLER_NOT_REFERENCED` — `input` entry never used.
|
|
94
|
-
- `INPUT_HANDLER_METHOD_NOT_IMPLEMENTED` — `.handler` not in `methods`.
|
|
95
|
-
- `INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD` — bare name resolves to `methods` (use `.name`).
|
|
96
|
-
- `INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER` — `.name` resolves to `input` (drop the dot).
|
|
97
|
-
|
|
98
|
-
### Iteration helpers (`alter`)
|
|
99
|
-
|
|
100
|
-
- `ALT_HANDLER_NOT_DEFINED` — `@when` / `@enrich-with` / `@loop-with` name not in `alter`.
|
|
101
|
-
- `ALT_HANDLER_NOT_REFERENCED` — `alter` entry never used.
|
|
102
|
-
|
|
103
|
-
### Templates / events
|
|
104
|
-
|
|
105
|
-
- `RENDER_IT_OUTSIDE_OF_LOOP` — `<x render-it>` outside `@each` / `render-each`.
|
|
106
|
-
- `UNKNOWN_EVENT_MODIFIER` — `+mod` not in the recognized modifier set.
|
|
107
|
-
- `UNKNOWN_HANDLER_ARG_NAME` — handler arg name not a built-in / declared component.
|
|
108
|
-
- `DUPLICATE_ATTR_DEFINITION` — same attr set by literal + `:attr` + `@if.attr` on one element.
|
|
109
|
-
- `UNKNOWN_DIRECTIVE` — `@name` directive not recognized (typo or unsupported).
|
|
110
|
-
- `UNKNOWN_X_OP` — first attribute on `<x>` (or pseudo-`@x`) is not a known op.
|
|
111
|
-
- `UNKNOWN_X_ATTR` — extra attribute on `<x op>` not consumed by the op and not a known wrapper (`show`/`hide`).
|
|
112
|
-
|
|
113
|
-
### Names registered with the app
|
|
114
|
-
|
|
115
|
-
- `UNKNOWN_REQUEST_NAME` — `!name` not registered.
|
|
116
|
-
- `UNKNOWN_COMPONENT_NAME` — component type not registered.
|
|
117
|
-
- `UNKNOWN_MACRO_ARG` — macro attr not declared in defaults.
|