@augeo/smelt 1.2.2 → 1.3.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/.github/workflows/verify.yml +23 -10
- package/AGENTS.md +7 -3
- package/README.md +123 -23
- package/dist/cli.mjs +84 -39
- package/dist/schema.d.mts +8 -6
- package/dist/schema.mjs +6 -4
- package/docs/build-spec.md +105 -7
- package/docs/js-modules-plan.md +278 -0
- package/example/blocks/built--components--card.liquid +34 -0
- package/example/snippets/built--components--card.liquid +1 -1
- package/example/src/components/card/block/card.schema.ts +14 -0
- package/example/src/components/card/card.liquid +1 -1
- package/lib/build/build.test.ts +207 -0
- package/lib/build/build.ts +92 -18
- package/lib/resolver.test.ts +91 -4
- package/lib/resolver.ts +121 -36
- package/lib/schema.ts +8 -10
- package/package.json +1 -1
- package/docs/library-conversion-plan.md +0 -419
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
status: draft
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Converting Smelt to a Library + CLI
|
|
6
|
-
|
|
7
|
-
Plan for turning this repo from "a Shopify theme that happens to have a custom
|
|
8
|
-
build script" into "an npm package that other Shopify themes install and use."
|
|
9
|
-
Companion to [`build-spec.md`](./build-spec.md) — the build spec describes the
|
|
10
|
-
compiler's behavior; this doc describes the path from where we are to publishing
|
|
11
|
-
it as a tool.
|
|
12
|
-
|
|
13
|
-
## Vision
|
|
14
|
-
|
|
15
|
-
Today, Smelt _is_ a theme. After this work:
|
|
16
|
-
|
|
17
|
-
- Smelt is published as **`@augeo/smelt`** (`npm i -D @augeo/smelt`), matching
|
|
18
|
-
the scope used by
|
|
19
|
-
[`@augeo/assay`](https://www.npmjs.com/package/@augeo/assay).
|
|
20
|
-
- It exposes a **CLI** (`npx smelt build`, `npx smelt dev`).
|
|
21
|
-
- It ships a **baseline component layer** that consumers can use as-is.
|
|
22
|
-
- Consumer themes have their own `src/` that **layers on top** of Smelt's
|
|
23
|
-
baseline — files in the consumer layer shadow matching baseline files.
|
|
24
|
-
- This repo keeps a working `example/` consumer theme that exercises the package
|
|
25
|
-
end-to-end (mirrors how [`assay`](https://github.com/seanhealy/assay) is
|
|
26
|
-
organized).
|
|
27
|
-
|
|
28
|
-
The build engine's _behavior_ doesn't change much. The walker, render rewriter,
|
|
29
|
-
esbuild bundle, CSS/JS inlining, banner, and indent handling all transfer almost
|
|
30
|
-
as-is. The new pieces are **layer resolution**, **CLI packaging**, and the
|
|
31
|
-
**inside-out reorganization** of this repo.
|
|
32
|
-
|
|
33
|
-
## Design Decisions
|
|
34
|
-
|
|
35
|
-
All five confirmed during planning. Listed here so the rationale lives next to
|
|
36
|
-
the decision and is easy to revisit if a future constraint forces a rethink.
|
|
37
|
-
|
|
38
|
-
### 1. Shadowing is per-file, not per-component
|
|
39
|
-
|
|
40
|
-
If a consumer provides only `button.css`, they get Smelt's `button.liquid` +
|
|
41
|
-
`button.ts` with their CSS swapped in. Each of `<name>.{liquid,ts,css,test.ts}`
|
|
42
|
-
resolves independently across layers.
|
|
43
|
-
|
|
44
|
-
- **Why:** maximally flexible; lets consumers tweak just styling without forking
|
|
45
|
-
the logic.
|
|
46
|
-
- **Cost:** harder to reason about than "you own the whole component." Need
|
|
47
|
-
clear error messages when a shadow drifts (e.g. consumer's CSS references a
|
|
48
|
-
class Smelt's liquid no longer renders).
|
|
49
|
-
|
|
50
|
-
### 2. `@/` and `./` resolve against the **merged** layer tree
|
|
51
|
-
|
|
52
|
-
When Smelt's `tabs.liquid` does `{% render '@/components/icon' %}`, it resolves
|
|
53
|
-
through the layered tree — so a consumer's icon shadow is picked up. Same for
|
|
54
|
-
esbuild's resolution of `@/utilities/...` imports.
|
|
55
|
-
|
|
56
|
-
- **Why:** without this, layering is just "drop in alternates" rather than true
|
|
57
|
-
overrides — the baseline can't be visibly customized through its
|
|
58
|
-
collaborators.
|
|
59
|
-
- **Cost:** consumers can break Smelt internals by shadowing a utility
|
|
60
|
-
carelessly. Mitigation: discipline + clear documentation about which surface
|
|
61
|
-
is considered stable (same model as Gatsby component shadowing or Material UI
|
|
62
|
-
theme overrides).
|
|
63
|
-
|
|
64
|
-
### 3. We ship **source**, not built artifacts
|
|
65
|
-
|
|
66
|
-
The published package contains raw `.liquid` / `.ts` / `.css` for the baseline
|
|
67
|
-
layer. The consumer's `smelt build` is what compiles them.
|
|
68
|
-
|
|
69
|
-
- **Why:** required for layering to work at the source level. If we shipped
|
|
70
|
-
pre-built `built--*.liquid`, consumers couldn't shadow a baseline component's
|
|
71
|
-
CSS in isolation.
|
|
72
|
-
- **Cost:** consumer build time scales with Smelt's component count. Acceptable
|
|
73
|
-
for now; revisit if pain.
|
|
74
|
-
|
|
75
|
-
### 4. One published package, not split
|
|
76
|
-
|
|
77
|
-
`@augeo/smelt` ships the CLI **and** the baseline components together. No
|
|
78
|
-
`@augeo/smelt-cli` + `@augeo/smelt-baseline` split yet.
|
|
79
|
-
|
|
80
|
-
- **Why:** split is cheap to do later if a reason emerges (e.g. alternate
|
|
81
|
-
baselines, plugin authors who want just the CLI). No reason today.
|
|
82
|
-
- **Cost:** baseline component changes force a new version of the CLI package
|
|
83
|
-
too. Acceptable until baseline surface grows.
|
|
84
|
-
|
|
85
|
-
### 5. Optional `smelt.config.ts`, minimal surface
|
|
86
|
-
|
|
87
|
-
A config file exists but is optional. For v1 it has exactly one field: `layers`.
|
|
88
|
-
Everything else stays convention until proven necessary.
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
// smelt.config.ts
|
|
92
|
-
import { defineConfig } from "@augeo/smelt";
|
|
93
|
-
|
|
94
|
-
export default defineConfig({
|
|
95
|
-
layers: ["./src", "@augeo/smelt"],
|
|
96
|
-
});
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
If the file doesn't exist, the resolver defaults to `["./src", "@augeo/smelt"]`
|
|
100
|
-
— first wins. Output dirs, build options, etc. stay hardcoded for now.
|
|
101
|
-
|
|
102
|
-
- **Why:** consumers will eventually want custom layer ordering (multiple
|
|
103
|
-
baselines, brand overrides, plugin layers). The config makes this expressible
|
|
104
|
-
without arg-parsing gymnastics. Limiting v1 to `layers` alone keeps the door
|
|
105
|
-
open without committing to a sprawling surface.
|
|
106
|
-
- **Cost:** another file format to maintain — small. `defineConfig` is a pure
|
|
107
|
-
type-identity helper (same pattern as Vite, Vitest, etc.).
|
|
108
|
-
- **Future:** add `output`, `extensions`, build options when a real consumer
|
|
109
|
-
needs them.
|
|
110
|
-
|
|
111
|
-
## CLI Surface
|
|
112
|
-
|
|
113
|
-
| Command | What it does |
|
|
114
|
-
| ------------- | ------------------------------------------------------------------ |
|
|
115
|
-
| `smelt build` | One-shot build of all layers → consumer's flat output directories |
|
|
116
|
-
| `smelt dev` | Watch mode: rebuild affected components on source file change |
|
|
117
|
-
| `smelt init` | (Future) Scaffold a new consumer theme repo with sensible defaults |
|
|
118
|
-
|
|
119
|
-
**`smelt dev` is build-watch only** — it rebuilds affected components on source
|
|
120
|
-
change and exits on Ctrl-C. Consumer composes it with the Shopify CLI in their
|
|
121
|
-
own `package.json`:
|
|
122
|
-
|
|
123
|
-
```jsonc
|
|
124
|
-
{
|
|
125
|
-
"scripts": {
|
|
126
|
-
"dev": "concurrently 'smelt dev' 'shopify theme dev'",
|
|
127
|
-
},
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Rationale: keeps Smelt from having a runtime dependency on Shopify CLI being
|
|
132
|
-
installed, and matches how Vite / Next / etc handle their `dev` command (they
|
|
133
|
-
don't try to run framework-adjacent servers for you).
|
|
134
|
-
|
|
135
|
-
## Architecture Changes
|
|
136
|
-
|
|
137
|
-
Most of the engine survives intact. What changes:
|
|
138
|
-
|
|
139
|
-
| Area | Before | After conversion |
|
|
140
|
-
| ---------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------- |
|
|
141
|
-
| Entry point | `src/scripts/build.ts` runs immediately | `lib/cli.ts` defines commands via [citty](https://github.com/unjs/citty), calls library API |
|
|
142
|
-
| Roots | Hardcoded `ROOT = resolve(".")`, `SRC = …` | `resolveContext()` figures out consumer cwd + layer paths |
|
|
143
|
-
| Component walker | Walks one tree | Walks N layer trees, per-file merge by priority |
|
|
144
|
-
| Render rewriter | Looks up in single component map | Looks up in merged component map |
|
|
145
|
-
| esbuild | `tsconfig` points at this repo's tsconfig | Uses consumer's tsconfig if present; alias plugin resolves `@/` per layer |
|
|
146
|
-
| Output dirs | Hardcoded `sections/`, etc. | Resolved relative to consumer cwd |
|
|
147
|
-
| Watch mode | Doesn't exist | New: incremental rebuild on file change in any layer |
|
|
148
|
-
| Dev mode | `npm run dev` = shopify CLI only | `smelt dev` = build watcher; consumer composes with `shopify theme dev` |
|
|
149
|
-
| Banner | `GENERATED FROM src/...` | `GENERATED FROM <layer-tagged source path>` |
|
|
150
|
-
|
|
151
|
-
## Phased Execution Plan
|
|
152
|
-
|
|
153
|
-
Each phase is a coherent chunk that can be verified, reviewed, and merged
|
|
154
|
-
independently. We don't need to do them all in one shot — they're sized so the
|
|
155
|
-
repo stays useful between phases.
|
|
156
|
-
|
|
157
|
-
### Phase 1 — Inside-out reorganization (minimal engine touchups)
|
|
158
|
-
|
|
159
|
-
Move all theme content into `example/` and reframe the root as a future library,
|
|
160
|
-
without touching the engine architecturally. The build script gets ~4 lines of
|
|
161
|
-
hardcoded path updates so it keeps working against the new layout — proper
|
|
162
|
-
context-driven refactor happens in Phase 2. After this phase, `example/` is the
|
|
163
|
-
test bed for everything that follows (mirroring how assay uses its own
|
|
164
|
-
`example/`).
|
|
165
|
-
|
|
166
|
-
Deliverables:
|
|
167
|
-
|
|
168
|
-
- Create directories: `example/`, `lib/` (placeholder for Phase 2),
|
|
169
|
-
`components/` (placeholder for Phase 3), `dist/` (gitignored)
|
|
170
|
-
- Move theme content into `example/`:
|
|
171
|
-
- Output dirs: `sections/`, `snippets/`, `blocks/`
|
|
172
|
-
- Theme files: `layout/`, `templates/`, `config/`, `locales/`, `assets/`
|
|
173
|
-
- Theme config: `.theme-check.yml`, `.shopifyignore`
|
|
174
|
-
- Test config: `vitest.config.ts`
|
|
175
|
-
- Move demo component: `src/components/button/` →
|
|
176
|
-
`example/src/components/button/`
|
|
177
|
-
- Patch `src/scripts/build.ts`: change `SRC` to point at `example/src`, prefix
|
|
178
|
-
output dir constants with `example/`. ~4 line change; engine is still
|
|
179
|
-
hardcoded but works against the new layout.
|
|
180
|
-
- Update root `package.json`:
|
|
181
|
-
- Strip theme scripts (`dev`, `push`, `pull`, `theme:check`, `theme:fix`)
|
|
182
|
-
- Keep `build` (still at root for now; outputs into `example/`)
|
|
183
|
-
- Create `example/package.json`:
|
|
184
|
-
- Theme scripts (cwd=`example/`): `dev`, `push`, `pull`, `theme:check`,
|
|
185
|
-
`theme:fix`
|
|
186
|
-
- `build`: `tsx ../src/scripts/build.ts` (interim — Phase 2 makes this clean)
|
|
187
|
-
- `test`: vitest against `example/`'s built output
|
|
188
|
-
- Update `.gitattributes` linguist-generated patterns to use the `example/`
|
|
189
|
-
prefix
|
|
190
|
-
- Update CI workflow: lint/typecheck at root, then `cd example` for build,
|
|
191
|
-
tests, theme-check, and the `git status` diff guard
|
|
192
|
-
- Update docs:
|
|
193
|
-
- `AGENTS.md` — restructure to reflect the library-root + consumer-example
|
|
194
|
-
layout. Note that `example/` is the integration test bed.
|
|
195
|
-
- `docs/build-spec.md` — reframe the worked example as "in the consumer's
|
|
196
|
-
repo." Pipeline behavior description unchanged.
|
|
197
|
-
- `README.md` — minor update to reflect theme commands now live in `example/`.
|
|
198
|
-
|
|
199
|
-
Verification: `npm run build` at root produces files in `example/`; from
|
|
200
|
-
`example/`: `npm test && npm run theme:check` succeeds and the built output
|
|
201
|
-
matches today's root-as-theme output (after the move).
|
|
202
|
-
|
|
203
|
-
### Phase 2 — Engine refactor + citty CLI + `file:..` link
|
|
204
|
-
|
|
205
|
-
Pull the build script out of `src/scripts/` and into `lib/` as a properly
|
|
206
|
-
context-driven library, exposed through a citty CLI compiled to a real bin via
|
|
207
|
-
tsdown. After this phase, the engine no longer assumes anything about "where the
|
|
208
|
-
consumer's theme lives" — it accepts a `BuildContext`, **and `example/` consumes
|
|
209
|
-
it like a real npm consumer** via `"@augeo/smelt": "file:.."`. This catches bin
|
|
210
|
-
/ shebang / entry-point bugs early without waiting for full packaging in
|
|
211
|
-
Phase 4.
|
|
212
|
-
|
|
213
|
-
**Types policy:** no dedicated `types.ts` file. Define types alongside the code
|
|
214
|
-
that primarily uses them — if a second module needs the same type, it imports
|
|
215
|
-
from the defining module. Keeps things easy to mutate; revisit only if genuine
|
|
216
|
-
ambiguity emerges.
|
|
217
|
-
|
|
218
|
-
**Bundler policy:** tsdown owns _library packaging_ (the CLI bin, and later the
|
|
219
|
-
programmatic exports + d.ts). esbuild stays for _per-component runtime bundling_
|
|
220
|
-
inside the build engine. Two bundlers, two distinct concerns; not a swap.
|
|
221
|
-
|
|
222
|
-
Deliverables:
|
|
223
|
-
|
|
224
|
-
- `lib/build.ts` — exports `async function build(context: BuildContext)`.
|
|
225
|
-
`BuildContext` lives in this file.
|
|
226
|
-
- `lib/cli.ts` — defines commands via [citty](https://github.com/unjs/citty),
|
|
227
|
-
dispatches to `build` (only command for now). Picked over cac/Cliffy/oclif for
|
|
228
|
-
deep TS inference, lightweight footprint, and active maintenance in the UnJS
|
|
229
|
-
ecosystem.
|
|
230
|
-
- `tsdown.config.ts` — bundles `lib/cli.ts` → `dist/cli.mjs` with a
|
|
231
|
-
`#!/usr/bin/env node` shebang. (Grows in Phase 4 to add `dist/index.js` +
|
|
232
|
-
d.ts + exports map.)
|
|
233
|
-
- Delete `src/scripts/` entirely (no shim — its one caller, `example/`'s `build`
|
|
234
|
-
script, gets repointed via the bin)
|
|
235
|
-
- Root `package.json` (mirrors assay's script conventions):
|
|
236
|
-
- Add `bin.smelt` → `./dist/cli.mjs`
|
|
237
|
-
- `build` script: `tsdown` (this package's artifact = `dist/cli.mjs`)
|
|
238
|
-
- `build:watch` script: `tsdown --watch` (iteration ergonomic)
|
|
239
|
-
- `verify` script: `lint:fix && typecheck` — fast root quality gate
|
|
240
|
-
- `test` script: `npm run build && npm test --prefix example` — integration
|
|
241
|
-
chain
|
|
242
|
-
- `prepare` script: `npm run build` (keeps `npm install` ergonomic; needed
|
|
243
|
-
because the example's bin resolution depends on `dist/cli.mjs` existing)
|
|
244
|
-
- `prepublishOnly` script: `npm run build` (publish-time safety)
|
|
245
|
-
- Add `citty` to `dependencies`; add `tsdown` to `devDependencies`
|
|
246
|
-
- `example/package.json` (intentionally consumer-shaped):
|
|
247
|
-
- `devDependencies`: `"@augeo/smelt": "file:.."`
|
|
248
|
-
- `build` script: `smelt build` (resolves through the linked bin)
|
|
249
|
-
- `test` script: `typecheck → build → vitest → theme:check` — example's full
|
|
250
|
-
check, called by root's `npm test`
|
|
251
|
-
- Run `npm install` in `example/` once to set up the symlink
|
|
252
|
-
- Update `tsconfig.json` `include` from `src/**/*.ts` → `lib/**/*.ts`
|
|
253
|
-
- Update doc references (`AGENTS.md`, `docs/build-spec.md`) to the new
|
|
254
|
-
`lib/build.ts` path
|
|
255
|
-
- `npm run verify` at root and `npm run verify` in `example/` both pass; diff
|
|
256
|
-
check confirms no output drift
|
|
257
|
-
|
|
258
|
-
Verification: from `example/`, `npx smelt build` (via `npm run build`) produces
|
|
259
|
-
byte-identical output to Phase 1 (modulo any intentional banner reformat).
|
|
260
|
-
Resolution flows `example/node_modules/@augeo/smelt` → symlink → repo root, so
|
|
261
|
-
edits to `lib/cli.ts` rebuild `dist/cli.mjs` on next example build and are
|
|
262
|
-
picked up automatically.
|
|
263
|
-
|
|
264
|
-
### Phase 3 — Layer resolution
|
|
265
|
-
|
|
266
|
-
Introduce the `Layer` concept and per-file merging. Build engine accepts an
|
|
267
|
-
ordered list of layer paths; walker merges; render rewriter and esbuild resolver
|
|
268
|
-
consult the merged tree. With `example/` in place since Phase 1, this is
|
|
269
|
-
exercised against a real consumer setup rather than synthetic scaffolding.
|
|
270
|
-
|
|
271
|
-
Deliverables:
|
|
272
|
-
|
|
273
|
-
- `lib/resolver.ts` — walks N layers, returns merged component list. Each
|
|
274
|
-
`ResolvedComponent` has `liquid`/`ts`/`css`/`test` slots that independently
|
|
275
|
-
track which layer they came from. New types (`Layer`, `ComponentSource`,
|
|
276
|
-
`ResolvedComponent`, `ComponentType`) live here; `lib/build/build.ts` imports
|
|
277
|
-
them.
|
|
278
|
-
- `BuildContext` drops `cwd` and gains `layers: Layer[]`. Output dirs are always
|
|
279
|
-
relative (`sections/`, `snippets/`, `blocks/`), resolved against the first
|
|
280
|
-
layer (the consumer root).
|
|
281
|
-
- `lib/build/build.ts` walker rewritten to consume the merged list from the
|
|
282
|
-
resolver (no more local `walkSrc`).
|
|
283
|
-
- `lib/build/command.ts` uses `import.meta.url` + `fileURLToPath` to locate the
|
|
284
|
-
published package's root (the second layer); the consumer's layer is
|
|
285
|
-
`process.cwd()`. Default layers: `[<consumer>, "@augeo/smelt"]`.
|
|
286
|
-
- Promote the button into the root `src/components/button/` baseline so there's
|
|
287
|
-
something to shadow. Drop a `button.css` shadow into
|
|
288
|
-
`example/src/components/button/` to demonstrate partial override.
|
|
289
|
-
- esbuild plugin for `@/...` resolution against the merged layer tree —
|
|
290
|
-
**deferred until a real component uses `@/...` TS imports**. The demo button
|
|
291
|
-
doesn't, so this is YAGNI for now.
|
|
292
|
-
- Root `vitest.config.ts` (matches assay's pattern) picks up
|
|
293
|
-
`lib/**/*.test.ts` + `src/**/*.test.ts`. Root `test` script chains
|
|
294
|
-
`vitest run` between the lib build and example's tests.
|
|
295
|
-
- `lib/resolver.test.ts` — colocated unit tests covering baseline-only, partial
|
|
296
|
-
CSS shadow, full consumer shadow, first-wins ordering, disjoint layers, and
|
|
297
|
-
dead-code (consumer slot with no liquid anchor) scenarios via `mkdtemp`
|
|
298
|
-
fixtures.
|
|
299
|
-
- `src/components/button/button.test.ts` — assay-style functional test (renders,
|
|
300
|
-
accepts `label` arg, applies CSS class). **Deferred** until the baseline has a
|
|
301
|
-
built target vitest can render against; the integration test in `example/`
|
|
302
|
-
already verifies the button compiles end-to-end.
|
|
303
|
-
|
|
304
|
-
Verification: in `example/`, the built button uses the consumer's CSS
|
|
305
|
-
(`background: #f3c724`, pill-shaped) while the `liquid` and `ts` slots come from
|
|
306
|
-
baseline (`@augeo/smelt/src/components/button/...`, visible in the banner).
|
|
307
|
-
|
|
308
|
-
### Phase 4 — Packaging
|
|
309
|
-
|
|
310
|
-
Make the library a real publishable npm package. The CLI bin already comes out
|
|
311
|
-
of tsdown (set up in Phase 2); this phase grows the same tsdown config to also
|
|
312
|
-
emit a programmatic entry point and adds the `package.json` metadata an npm
|
|
313
|
-
consumer needs.
|
|
314
|
-
|
|
315
|
-
Deliverables:
|
|
316
|
-
|
|
317
|
-
- Grow `tsdown.config.ts` to add a second entry: `lib/build.ts` →
|
|
318
|
-
`dist/index.js` with `.d.ts`
|
|
319
|
-
- `package.json` updates:
|
|
320
|
-
- `exports` map for `dist/index.js` (programmatic use) and the bin
|
|
321
|
-
- `files: ["dist", "components"]`
|
|
322
|
-
- `peerDependencies`: nothing yet (we bundle esbuild)
|
|
323
|
-
- `dependencies`: esbuild, citty (already moved out of devDeps in Phase 2 for
|
|
324
|
-
citty; confirm esbuild placement)
|
|
325
|
-
- `prepublishOnly` script that runs `npm run lib:build` to ensure `dist/` is
|
|
326
|
-
fresh
|
|
327
|
-
- `npm pack` round-trip: install the tarball into `example/` (instead of
|
|
328
|
-
`file:..`) and confirm it still builds. Catches missing-`files`-entry and
|
|
329
|
-
shebang-permission bugs that `file:..` masks.
|
|
330
|
-
|
|
331
|
-
Verification: `npm pack` produces a tarball;
|
|
332
|
-
`cd example && npm install ../smelt-0.0.0.tgz && npx smelt build` works
|
|
333
|
-
end-to-end against the packed artifact (not just `file:..`). After verification,
|
|
334
|
-
restore the `file:..` link so day-to-day dev keeps working live.
|
|
335
|
-
|
|
336
|
-
### Phase 5 — CI and verify chain
|
|
337
|
-
|
|
338
|
-
CI now verifies two things: the library compiles cleanly, and the example
|
|
339
|
-
consumer builds correctly with no drift.
|
|
340
|
-
|
|
341
|
-
Deliverables:
|
|
342
|
-
|
|
343
|
-
- Workflow `.github/workflows/verify.yml`:
|
|
344
|
-
- lint, typecheck, library unit tests, library build (`tsdown`)
|
|
345
|
-
- then `cd example`, `npm install`, `npx smelt build`, vitest
|
|
346
|
-
- finally `git diff --exit-code` to catch stale built files in
|
|
347
|
-
`example/sections`, `example/snippets`, `example/blocks`
|
|
348
|
-
- Root `npm run verify` mirrors CI: lint:fix → typecheck → test → build → cd
|
|
349
|
-
example && verify
|
|
350
|
-
- `.claude/settings.json` adjusted for the new script set
|
|
351
|
-
|
|
352
|
-
Verification: open a draft PR with a deliberately stale built file in
|
|
353
|
-
`example/`; CI should fail on the diff check.
|
|
354
|
-
|
|
355
|
-
### Phase 6 — First publish + first real consumer
|
|
356
|
-
|
|
357
|
-
Go from "works locally" to "is the tool we use."
|
|
358
|
-
|
|
359
|
-
Deliverables:
|
|
360
|
-
|
|
361
|
-
- Publish `@augeo/smelt` 0.1.0
|
|
362
|
-
- Pick one of the Augeo themes (or coverlet, if we're feeling ambitious) to
|
|
363
|
-
migrate as the first real consumer
|
|
364
|
-
- Document the migration path in `docs/`
|
|
365
|
-
|
|
366
|
-
Verification: real-world consumer's CI is green using the published package.
|
|
367
|
-
|
|
368
|
-
## Documentation Updates
|
|
369
|
-
|
|
370
|
-
Done as part of the relevant phase, listed here for visibility:
|
|
371
|
-
|
|
372
|
-
- `AGENTS.md` (Phase 1) — restructure to reflect the library-root +
|
|
373
|
-
consumer-example layout. Will get a second pass in Phase 4 to add a section
|
|
374
|
-
pointing consumers at the README / `docs/consumer-guide.md`.
|
|
375
|
-
- `README.md` (Phase 4) — rewrite as "what Smelt is, how to install, how to
|
|
376
|
-
use." Move the current command table to `example/README.md`.
|
|
377
|
-
- `docs/build-spec.md` (Phase 1) — reframe the worked example as "in the
|
|
378
|
-
consumer's repo, where Smelt was installed." The pipeline behavior description
|
|
379
|
-
is unchanged.
|
|
380
|
-
- `docs/consumer-guide.md` (Phase 4, new) — install, write components, shadow
|
|
381
|
-
baseline, run dev/build/test. The new user-facing doc.
|
|
382
|
-
- `docs/library-architecture.md` (Phase 3, new, optional) — internal notes on
|
|
383
|
-
layer resolution and the engine API. Lives next to `build-spec.md`.
|
|
384
|
-
|
|
385
|
-
## Open Questions / Risks
|
|
386
|
-
|
|
387
|
-
- **Shadowing footguns.** A consumer shadowing a utility used by multiple
|
|
388
|
-
baseline components creates a non-local change. Error messages need to make
|
|
389
|
-
this obvious. May need a `smelt explain` command long-term (shows resolution
|
|
390
|
-
for a given component).
|
|
391
|
-
- **Tests-against-built-output composes with layers how?** Today vitest runs
|
|
392
|
-
against `./sections`, `./snippets`, `./blocks` — those contain built output
|
|
393
|
-
from BOTH baseline + consumer post-merge. So tests work the same way. But:
|
|
394
|
-
should consumers be able to write tests for baseline components without
|
|
395
|
-
copy-pasting them? Probably yes; defer until use case appears.
|
|
396
|
-
- **What happens when a consumer references a baseline component via
|
|
397
|
-
`@/components/button` but doesn't shadow it?** Resolver finds it in the
|
|
398
|
-
baseline layer, build emits `snippets/built--components--button.liquid` into
|
|
399
|
-
the consumer's output dirs. ✓ same naming as if consumer had written it.
|
|
400
|
-
Banner notes the file came from `smelt/components/button/`.
|
|
401
|
-
|
|
402
|
-
## Out of Scope (For This Conversion)
|
|
403
|
-
|
|
404
|
-
Things we explicitly defer beyond the scope of this conversion:
|
|
405
|
-
|
|
406
|
-
- **Plugin system.** No third-party extension points yet. Layers cover the
|
|
407
|
-
customization story for v1.
|
|
408
|
-
- **Multiple baseline packages.** v1 has exactly one baseline (the library's own
|
|
409
|
-
`components/`). Consumers can't mix `smelt-baseline-a` + `smelt-baseline-b`.
|
|
410
|
-
Adding that is a layered version of layering and can wait.
|
|
411
|
-
- **`props.ts` runtime validation.** Already deferred in `build-spec.md`. Still
|
|
412
|
-
deferred here.
|
|
413
|
-
- **Workspace conversion.** Single package is the right call until a reason
|
|
414
|
-
emerges to split. Triggers documented in [conversation history / future ADR].
|
|
415
|
-
- **Component documentation site.** Out of scope; baseline components are
|
|
416
|
-
documented via their own colocated comments + a future `docs/components.md`
|
|
417
|
-
catalog.
|
|
418
|
-
- **Migration tooling for existing themes.** v1 consumer migration is manual.
|
|
419
|
-
Tooling (e.g. `smelt migrate-from-dawn`) is way out of scope.
|