@techninja/clearstack 0.3.12 → 0.3.14
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/README.md +12 -11
- package/bin/cli.js +3 -1
- package/docs/BUILD_LOG.md +36 -0
- package/docs/FRONTEND_IMPLEMENTATION_RULES.md +23 -0
- package/docs/JSDOC_TYPING.md +67 -0
- package/docs/QUICKSTART.md +6 -3
- package/package.json +1 -1
- package/templates/shared/docs/clearstack/BUILD_LOG.md +36 -0
- package/templates/shared/docs/clearstack/FRONTEND_IMPLEMENTATION_RULES.md +23 -0
- package/templates/shared/docs/clearstack/JSDOC_TYPING.md +67 -0
- package/templates/shared/docs/clearstack/QUICKSTART.md +6 -3
package/README.md
CHANGED
|
@@ -29,17 +29,17 @@ A project/task tracker that exercises every pattern in the spec: API-backed enti
|
|
|
29
29
|
|
|
30
30
|
## Specification
|
|
31
31
|
|
|
32
|
-
| Document
|
|
33
|
-
|
|
32
|
+
| Document | What It Covers |
|
|
33
|
+
| --------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
34
34
|
| [FRONTEND_IMPLEMENTATION_RULES.md](./docs/FRONTEND_IMPLEMENTATION_RULES.md) | Philosophy, framework choice, project structure, atomic design |
|
|
35
|
-
| [COMPONENT_PATTERNS.md](./docs/COMPONENT_PATTERNS.md)
|
|
36
|
-
| [STATE_AND_ROUTING.md](./docs/STATE_AND_ROUTING.md)
|
|
37
|
-
| [CONVENTIONS.md](./docs/CONVENTIONS.md)
|
|
38
|
-
| [SERVER_AND_DEPS.md](./docs/SERVER_AND_DEPS.md)
|
|
39
|
-
| [BACKEND_API_SPEC.md](./docs/BACKEND_API_SPEC.md)
|
|
40
|
-
| [TESTING.md](./docs/TESTING.md)
|
|
41
|
-
| [BUILD_LOG.md](./docs/BUILD_LOG.md)
|
|
42
|
-
| [QUICKSTART.md](./docs/QUICKSTART.md)
|
|
35
|
+
| [COMPONENT_PATTERNS.md](./docs/COMPONENT_PATTERNS.md) | Authoring, light DOM, styling, layout engine, JSDoc typing |
|
|
36
|
+
| [STATE_AND_ROUTING.md](./docs/STATE_AND_ROUTING.md) | Store, routing, unified app state, realtime SSE sync |
|
|
37
|
+
| [CONVENTIONS.md](./docs/CONVENTIONS.md) | Naming rules, anti-patterns |
|
|
38
|
+
| [SERVER_AND_DEPS.md](./docs/SERVER_AND_DEPS.md) | Express server, import maps, vendor dependency loading |
|
|
39
|
+
| [BACKEND_API_SPEC.md](./docs/BACKEND_API_SPEC.md) | REST CRUD, JSON Schema via HEAD, entity management |
|
|
40
|
+
| [TESTING.md](./docs/TESTING.md) | Testing philosophy, tools, patterns, phase checkpoints |
|
|
41
|
+
| [BUILD_LOG.md](./docs/BUILD_LOG.md) | How this project was built — LLM-human collaboration proof |
|
|
42
|
+
| [QUICKSTART.md](./docs/QUICKSTART.md) | Scaffolder setup, development workflow, updating, compliance |
|
|
43
43
|
|
|
44
44
|
## Using Clearstack
|
|
45
45
|
|
|
@@ -48,7 +48,8 @@ Install as a dev dependency, scaffold, and keep in sync:
|
|
|
48
48
|
```bash
|
|
49
49
|
npm install -D @techninja/clearstack # add to your project
|
|
50
50
|
npx clearstack init # scaffold (interactive)
|
|
51
|
-
npx clearstack init -y # scaffold (defaults)
|
|
51
|
+
npx clearstack init -y # scaffold (fullstack defaults)
|
|
52
|
+
npx clearstack init --static # scaffold (static, no server)
|
|
52
53
|
npm run spec # check compliance
|
|
53
54
|
npm run spec:update # sync docs + configs on upgrade
|
|
54
55
|
```
|
package/bin/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* clearstack CLI — scaffold, update, and check spec-compliant projects.
|
|
5
5
|
* Usage:
|
|
6
|
-
* clearstack init [-y] [--
|
|
6
|
+
* clearstack init [-y] [--static|--fullstack] [--port 3000]
|
|
7
7
|
* clearstack update
|
|
8
8
|
* clearstack check [code|docs|imports|lint|lint es|format|types|audit|all]
|
|
9
9
|
* clearstack → interactive menu
|
|
@@ -22,6 +22,8 @@ const flags = Object.fromEntries(
|
|
|
22
22
|
}),
|
|
23
23
|
);
|
|
24
24
|
const yes = args.includes('-y') || args.includes('--yes');
|
|
25
|
+
if (flags.static) flags.mode = 'static';
|
|
26
|
+
if (flags.fullstack) flags.mode = 'fullstack';
|
|
25
27
|
|
|
26
28
|
/** Show interactive menu. */
|
|
27
29
|
async function interactive() {
|
package/docs/BUILD_LOG.md
CHANGED
|
@@ -168,6 +168,42 @@ These are the significant corrections:
|
|
|
168
168
|
- **Actual:** Coordinates calculated from SVG rect, not accounting for pan
|
|
169
169
|
- **Fix:** Shared `canvasPos()` utility subtracts pan offset from all tools
|
|
170
170
|
|
|
171
|
+
### Enumerable models (`id: true`) break tsc
|
|
172
|
+
|
|
173
|
+
- **Expected:** `@type {import('hybrids').Model<Product>}` works with `id: true`
|
|
174
|
+
- **Actual:** tsc rejects `id: true` — it's not in the Model type definition
|
|
175
|
+
- **Fix:** Type the model as `@type {any}` with a comment explaining the cast
|
|
176
|
+
- **Documented in:** JSDOC_TYPING.md → Enumerable Models Need `@type {any}`
|
|
177
|
+
|
|
178
|
+
### Empty arrays in store models throw at runtime
|
|
179
|
+
|
|
180
|
+
- **Expected:** `items: []` on a singleton model works as an empty default
|
|
181
|
+
- **Actual:** Hybrids throws `The first item of the 'items' array must be defined`
|
|
182
|
+
- **Why:** Hybrids infers array item type from the first element at model setup
|
|
183
|
+
- **Fix:** Provide a prototype item: `items: [{ sku: '', quantity: 0 }]`
|
|
184
|
+
- **Documented in:** STATE_AND_ROUTING.md → Store Array Properties
|
|
185
|
+
|
|
186
|
+
### List store descriptors don't match array types in tsc
|
|
187
|
+
|
|
188
|
+
- **Expected:** `store([Model])` assignable to an array-typed host property
|
|
189
|
+
- **Actual:** tsc sees `EnumerableInstance`, not `any[]` — three separate casts needed
|
|
190
|
+
- **Fix:** Cast the descriptor, `store.ready()` call, and the array before `.map()`
|
|
191
|
+
- **Documented in:** JSDOC_TYPING.md → List Store Properties Need Casts
|
|
192
|
+
|
|
193
|
+
### Organisms importing pages creates circular dependencies
|
|
194
|
+
|
|
195
|
+
- **Expected:** `router.url(PageView)` works from an organism for link generation
|
|
196
|
+
- **Actual:** Page imports organism, organism imports page → circular
|
|
197
|
+
- **Fix:** Use string URLs (`/product/${sku}`) in organisms instead of `router.url()`
|
|
198
|
+
- **Documented in:** FRONTEND_IMPLEMENTATION_RULES.md → Organisms Must Not Import Pages
|
|
199
|
+
|
|
200
|
+
### `list` connector params need cast for custom filter properties
|
|
201
|
+
|
|
202
|
+
- **Expected:** `list: async ({ category }) => ...` destructures cleanly
|
|
203
|
+
- **Actual:** Param is `ModelIdentifier`, tsc rejects `.category` access
|
|
204
|
+
- **Fix:** Cast params to `any` before accessing custom properties
|
|
205
|
+
- **Documented in:** JSDOC_TYPING.md → `list` Connector Params
|
|
206
|
+
|
|
171
207
|
---
|
|
172
208
|
|
|
173
209
|
## Metrics
|
|
@@ -212,6 +212,29 @@ Pages → Templates → Organisms → Molecules → Atoms
|
|
|
212
212
|
Store / Utils
|
|
213
213
|
```
|
|
214
214
|
|
|
215
|
+
#### Organisms Must Not Import Pages
|
|
216
|
+
|
|
217
|
+
Organisms sit below pages in the import hierarchy. If an organism needs
|
|
218
|
+
to generate a URL to a page (e.g. a product grid linking to a product
|
|
219
|
+
detail page), **use a string URL, not `router.url(PageView)`**.
|
|
220
|
+
|
|
221
|
+
`router.url()` requires importing the page component, which creates a
|
|
222
|
+
circular dependency: page → organism → page. Even if the bundler or
|
|
223
|
+
browser resolves it, it makes the dependency graph untraceable.
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
// ❌ BAD — organism imports a page, circular dependency
|
|
227
|
+
import ProductDetailView from '#pages/product-detail/product-detail-view.js';
|
|
228
|
+
html`<a href="${router.url(ProductDetailView, { sku })}">View</a>`;
|
|
229
|
+
|
|
230
|
+
// ✅ GOOD — string URL, no import needed
|
|
231
|
+
html`<a href="${`/product/${sku}`}">View</a>`;
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
If the URL pattern changes, update it in one place. For complex URL
|
|
235
|
+
generation, extract a `buildUrl(view, params)` utility in `src/utils/`
|
|
236
|
+
that returns strings — no component imports.
|
|
237
|
+
|
|
215
238
|
- Atoms import **nothing** from other component tiers.
|
|
216
239
|
- Molecules import only from **atoms**.
|
|
217
240
|
- Organisms import from **molecules** and **atoms**.
|
package/docs/JSDOC_TYPING.md
CHANGED
|
@@ -76,6 +76,73 @@ function handleClick(host, event) {
|
|
|
76
76
|
}
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
## Enumerable Models Need `@type {any}`
|
|
80
|
+
|
|
81
|
+
Hybrids' TypeScript definitions don't support `id: true` on `Model<T>` —
|
|
82
|
+
`tsc` rejects it because `id` isn't in the typedef. The fix is to type
|
|
83
|
+
the entire model as `any` with a comment explaining why:
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
/** @type {any} — hybrids Model with id:true; cast to bypass tsc limitations */
|
|
87
|
+
const Product = {
|
|
88
|
+
id: true,
|
|
89
|
+
name: '',
|
|
90
|
+
// ...
|
|
91
|
+
[store.connect]: { get: ..., list: ... },
|
|
92
|
+
};
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Do **not** add `id` to the `@typedef` — it's a hybrids directive, not a
|
|
96
|
+
data field. The `@typedef` should describe the shape of the data.
|
|
97
|
+
|
|
98
|
+
## List Store Properties Need Casts
|
|
99
|
+
|
|
100
|
+
`store([Model])` returns a descriptor that `tsc` can't reconcile with
|
|
101
|
+
array-typed host properties. Both the descriptor and `store.ready()` calls
|
|
102
|
+
need `any` casts:
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
/**
|
|
106
|
+
* @typedef {Object} MyGridHost
|
|
107
|
+
* @property {any} items — list store, cast for tsc
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
/** @type {import('hybrids').Component<MyGridHost>} */
|
|
111
|
+
export default define({
|
|
112
|
+
tag: 'my-grid',
|
|
113
|
+
items: /** @type {any} */ (store([Product], { id: () => ({}) })),
|
|
114
|
+
render: {
|
|
115
|
+
value: ({ items }) => html`
|
|
116
|
+
${
|
|
117
|
+
/** @type {any} */ (store).ready(items)
|
|
118
|
+
? /** @type {any[]} */ (items).map((i) => html`<span>${i.name}</span>`)
|
|
119
|
+
: html`<p>Loading…</p>`
|
|
120
|
+
}
|
|
121
|
+
`,
|
|
122
|
+
shadow: false,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Three casts are needed:
|
|
128
|
+
|
|
129
|
+
1. `/** @type {any} */` on the `store([Model])` descriptor assignment
|
|
130
|
+
2. `/** @type {any} */ (store).ready(items)` — `store.ready()` overloads
|
|
131
|
+
reject array-typed arguments
|
|
132
|
+
3. `/** @type {any[]} */ (items)` before calling `.map()` / `.filter()`
|
|
133
|
+
|
|
134
|
+
## `list` Connector Params Are `ModelIdentifier`
|
|
135
|
+
|
|
136
|
+
The `list` connector receives a `ModelIdentifier`, not a plain object.
|
|
137
|
+
Accessing custom filter properties (like `.category`) requires a cast:
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
list: async (params) => {
|
|
141
|
+
const category = /** @type {any} */ (params)?.category;
|
|
142
|
+
// ...
|
|
143
|
+
},
|
|
144
|
+
```
|
|
145
|
+
|
|
79
146
|
## Rules
|
|
80
147
|
|
|
81
148
|
- Every exported component gets a `@typedef` for its host interface.
|
package/docs/QUICKSTART.md
CHANGED
|
@@ -22,8 +22,10 @@ pnpm add -D @techninja/clearstack
|
|
|
22
22
|
From your project root:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
npx clearstack init
|
|
26
|
-
npx clearstack init -y
|
|
25
|
+
npx clearstack init # interactive
|
|
26
|
+
npx clearstack init -y # non-interactive (fullstack defaults)
|
|
27
|
+
npx clearstack init --static # non-interactive, static mode
|
|
28
|
+
npx clearstack init -y --static # same as above
|
|
27
29
|
```
|
|
28
30
|
|
|
29
31
|
The interactive prompt asks for:
|
|
@@ -159,7 +161,8 @@ spec:code → spec:docs → lint → format → typecheck → test
|
|
|
159
161
|
| Task | Command |
|
|
160
162
|
| --------------------- | -------------------------------------- |
|
|
161
163
|
| Install Clearstack | `npm install -D @techninja/clearstack` |
|
|
162
|
-
| Scaffold
|
|
164
|
+
| Scaffold (fullstack) | `npx clearstack init` |
|
|
165
|
+
| Scaffold (static) | `npx clearstack init --static` |
|
|
163
166
|
| Install dependencies | `npm install` |
|
|
164
167
|
| Start dev server | `npm run dev` |
|
|
165
168
|
| Lint + format | `npm run lint:fix && npm run format` |
|
package/package.json
CHANGED
|
@@ -168,6 +168,42 @@ These are the significant corrections:
|
|
|
168
168
|
- **Actual:** Coordinates calculated from SVG rect, not accounting for pan
|
|
169
169
|
- **Fix:** Shared `canvasPos()` utility subtracts pan offset from all tools
|
|
170
170
|
|
|
171
|
+
### Enumerable models (`id: true`) break tsc
|
|
172
|
+
|
|
173
|
+
- **Expected:** `@type {import('hybrids').Model<Product>}` works with `id: true`
|
|
174
|
+
- **Actual:** tsc rejects `id: true` — it's not in the Model type definition
|
|
175
|
+
- **Fix:** Type the model as `@type {any}` with a comment explaining the cast
|
|
176
|
+
- **Documented in:** JSDOC_TYPING.md → Enumerable Models Need `@type {any}`
|
|
177
|
+
|
|
178
|
+
### Empty arrays in store models throw at runtime
|
|
179
|
+
|
|
180
|
+
- **Expected:** `items: []` on a singleton model works as an empty default
|
|
181
|
+
- **Actual:** Hybrids throws `The first item of the 'items' array must be defined`
|
|
182
|
+
- **Why:** Hybrids infers array item type from the first element at model setup
|
|
183
|
+
- **Fix:** Provide a prototype item: `items: [{ sku: '', quantity: 0 }]`
|
|
184
|
+
- **Documented in:** STATE_AND_ROUTING.md → Store Array Properties
|
|
185
|
+
|
|
186
|
+
### List store descriptors don't match array types in tsc
|
|
187
|
+
|
|
188
|
+
- **Expected:** `store([Model])` assignable to an array-typed host property
|
|
189
|
+
- **Actual:** tsc sees `EnumerableInstance`, not `any[]` — three separate casts needed
|
|
190
|
+
- **Fix:** Cast the descriptor, `store.ready()` call, and the array before `.map()`
|
|
191
|
+
- **Documented in:** JSDOC_TYPING.md → List Store Properties Need Casts
|
|
192
|
+
|
|
193
|
+
### Organisms importing pages creates circular dependencies
|
|
194
|
+
|
|
195
|
+
- **Expected:** `router.url(PageView)` works from an organism for link generation
|
|
196
|
+
- **Actual:** Page imports organism, organism imports page → circular
|
|
197
|
+
- **Fix:** Use string URLs (`/product/${sku}`) in organisms instead of `router.url()`
|
|
198
|
+
- **Documented in:** FRONTEND_IMPLEMENTATION_RULES.md → Organisms Must Not Import Pages
|
|
199
|
+
|
|
200
|
+
### `list` connector params need cast for custom filter properties
|
|
201
|
+
|
|
202
|
+
- **Expected:** `list: async ({ category }) => ...` destructures cleanly
|
|
203
|
+
- **Actual:** Param is `ModelIdentifier`, tsc rejects `.category` access
|
|
204
|
+
- **Fix:** Cast params to `any` before accessing custom properties
|
|
205
|
+
- **Documented in:** JSDOC_TYPING.md → `list` Connector Params
|
|
206
|
+
|
|
171
207
|
---
|
|
172
208
|
|
|
173
209
|
## Metrics
|
|
@@ -212,6 +212,29 @@ Pages → Templates → Organisms → Molecules → Atoms
|
|
|
212
212
|
Store / Utils
|
|
213
213
|
```
|
|
214
214
|
|
|
215
|
+
#### Organisms Must Not Import Pages
|
|
216
|
+
|
|
217
|
+
Organisms sit below pages in the import hierarchy. If an organism needs
|
|
218
|
+
to generate a URL to a page (e.g. a product grid linking to a product
|
|
219
|
+
detail page), **use a string URL, not `router.url(PageView)`**.
|
|
220
|
+
|
|
221
|
+
`router.url()` requires importing the page component, which creates a
|
|
222
|
+
circular dependency: page → organism → page. Even if the bundler or
|
|
223
|
+
browser resolves it, it makes the dependency graph untraceable.
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
// ❌ BAD — organism imports a page, circular dependency
|
|
227
|
+
import ProductDetailView from '#pages/product-detail/product-detail-view.js';
|
|
228
|
+
html`<a href="${router.url(ProductDetailView, { sku })}">View</a>`;
|
|
229
|
+
|
|
230
|
+
// ✅ GOOD — string URL, no import needed
|
|
231
|
+
html`<a href="${`/product/${sku}`}">View</a>`;
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
If the URL pattern changes, update it in one place. For complex URL
|
|
235
|
+
generation, extract a `buildUrl(view, params)` utility in `src/utils/`
|
|
236
|
+
that returns strings — no component imports.
|
|
237
|
+
|
|
215
238
|
- Atoms import **nothing** from other component tiers.
|
|
216
239
|
- Molecules import only from **atoms**.
|
|
217
240
|
- Organisms import from **molecules** and **atoms**.
|
|
@@ -76,6 +76,73 @@ function handleClick(host, event) {
|
|
|
76
76
|
}
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
## Enumerable Models Need `@type {any}`
|
|
80
|
+
|
|
81
|
+
Hybrids' TypeScript definitions don't support `id: true` on `Model<T>` —
|
|
82
|
+
`tsc` rejects it because `id` isn't in the typedef. The fix is to type
|
|
83
|
+
the entire model as `any` with a comment explaining why:
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
/** @type {any} — hybrids Model with id:true; cast to bypass tsc limitations */
|
|
87
|
+
const Product = {
|
|
88
|
+
id: true,
|
|
89
|
+
name: '',
|
|
90
|
+
// ...
|
|
91
|
+
[store.connect]: { get: ..., list: ... },
|
|
92
|
+
};
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Do **not** add `id` to the `@typedef` — it's a hybrids directive, not a
|
|
96
|
+
data field. The `@typedef` should describe the shape of the data.
|
|
97
|
+
|
|
98
|
+
## List Store Properties Need Casts
|
|
99
|
+
|
|
100
|
+
`store([Model])` returns a descriptor that `tsc` can't reconcile with
|
|
101
|
+
array-typed host properties. Both the descriptor and `store.ready()` calls
|
|
102
|
+
need `any` casts:
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
/**
|
|
106
|
+
* @typedef {Object} MyGridHost
|
|
107
|
+
* @property {any} items — list store, cast for tsc
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
/** @type {import('hybrids').Component<MyGridHost>} */
|
|
111
|
+
export default define({
|
|
112
|
+
tag: 'my-grid',
|
|
113
|
+
items: /** @type {any} */ (store([Product], { id: () => ({}) })),
|
|
114
|
+
render: {
|
|
115
|
+
value: ({ items }) => html`
|
|
116
|
+
${
|
|
117
|
+
/** @type {any} */ (store).ready(items)
|
|
118
|
+
? /** @type {any[]} */ (items).map((i) => html`<span>${i.name}</span>`)
|
|
119
|
+
: html`<p>Loading…</p>`
|
|
120
|
+
}
|
|
121
|
+
`,
|
|
122
|
+
shadow: false,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Three casts are needed:
|
|
128
|
+
|
|
129
|
+
1. `/** @type {any} */` on the `store([Model])` descriptor assignment
|
|
130
|
+
2. `/** @type {any} */ (store).ready(items)` — `store.ready()` overloads
|
|
131
|
+
reject array-typed arguments
|
|
132
|
+
3. `/** @type {any[]} */ (items)` before calling `.map()` / `.filter()`
|
|
133
|
+
|
|
134
|
+
## `list` Connector Params Are `ModelIdentifier`
|
|
135
|
+
|
|
136
|
+
The `list` connector receives a `ModelIdentifier`, not a plain object.
|
|
137
|
+
Accessing custom filter properties (like `.category`) requires a cast:
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
list: async (params) => {
|
|
141
|
+
const category = /** @type {any} */ (params)?.category;
|
|
142
|
+
// ...
|
|
143
|
+
},
|
|
144
|
+
```
|
|
145
|
+
|
|
79
146
|
## Rules
|
|
80
147
|
|
|
81
148
|
- Every exported component gets a `@typedef` for its host interface.
|
|
@@ -22,8 +22,10 @@ pnpm add -D @techninja/clearstack
|
|
|
22
22
|
From your project root:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
npx clearstack init
|
|
26
|
-
npx clearstack init -y
|
|
25
|
+
npx clearstack init # interactive
|
|
26
|
+
npx clearstack init -y # non-interactive (fullstack defaults)
|
|
27
|
+
npx clearstack init --static # non-interactive, static mode
|
|
28
|
+
npx clearstack init -y --static # same as above
|
|
27
29
|
```
|
|
28
30
|
|
|
29
31
|
The interactive prompt asks for:
|
|
@@ -159,7 +161,8 @@ spec:code → spec:docs → lint → format → typecheck → test
|
|
|
159
161
|
| Task | Command |
|
|
160
162
|
| --------------------- | -------------------------------------- |
|
|
161
163
|
| Install Clearstack | `npm install -D @techninja/clearstack` |
|
|
162
|
-
| Scaffold
|
|
164
|
+
| Scaffold (fullstack) | `npx clearstack init` |
|
|
165
|
+
| Scaffold (static) | `npx clearstack init --static` |
|
|
163
166
|
| Install dependencies | `npm install` |
|
|
164
167
|
| Start dev server | `npm run dev` |
|
|
165
168
|
| Lint + format | `npm run lint:fix && npm run format` |
|