@feeef.dev/cli 0.2.2 → 0.2.3
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 +22 -5
- package/dist/bin.js +127 -16
- package/dist/bin.js.map +1 -1
- package/dist/index.js +127 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scaffolds/blank/.cursor/rules/feeef-theme.mdc +9 -7
- package/scaffolds/blank/.cursor/rules/theme-source.mdc +24 -17
- package/scaffolds/blank/.cursor/skills/feeef-theme/SKILL.md +21 -9
- package/scaffolds/blank/.cursor/skills/feeef-theme/reference.md +35 -30
- package/scaffolds/blank/AGENTS.md +7 -4
- package/scaffolds/blank/README.md +1 -1
- package/scaffolds/blank/docs/03-CUSTOM-COMPONENTS.md +68 -39
- package/scaffolds/blank/docs/04-WORKFLOW.md +7 -4
- package/scaffolds/blank/docs/05-ANTI-PATTERNS.md +7 -6
- package/scaffolds/blank/docs/07-SHARED-COMPONENTS.md +45 -77
- package/scaffolds/blank/docs/08-ORDER-FORM.md +1 -1
- package/scaffolds/blank/docs/10-SCHEMA-LIBRARY.md +21 -69
- package/scaffolds/blank/docs/11-PAGES-CHECKOUT-THANKS-PRODUCT.md +37 -22
- package/scaffolds/blank/docs/16-AUTHORING-TS-IMPORTS.md +38 -14
- package/scaffolds/blank/package-lock.json +4 -1918
- package/vendor/template-kit/lib/build.mjs +1 -1
- package/vendor/template-kit/lib/component-meta.mjs +92 -8
- package/vendor/template-kit/lib/unpack.mjs +295 -135
- package/vendor/template-kit/lib/validate.mjs +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@feeef.dev/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Feeef developer CLI — templates, auth, and cloud tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"commander": "^12.1.0",
|
|
52
52
|
"dotenv": "^16.4.7",
|
|
53
53
|
"esbuild": "^0.25.0",
|
|
54
|
-
"feeef": "^0.12.
|
|
54
|
+
"feeef": "^0.12.2",
|
|
55
55
|
"picocolors": "^1.1.1",
|
|
56
56
|
"react": "^19.2.7",
|
|
57
57
|
"react-dom": "^19.2.7"
|
|
@@ -13,22 +13,24 @@ Before editing theme source, read:
|
|
|
13
13
|
|
|
14
14
|
## Non-negotiables
|
|
15
15
|
|
|
16
|
+
- **Flat TSX for leaf customs** — `pages|shared|library/.../<name>.tsx` with `export const meta` + `function App()`.
|
|
17
|
+
- Folder form (`<name>/<name>.tsx` + `slots/`) **only** when slots/children are required.
|
|
18
|
+
- Never author new customs as `component.json` + `component.jsx`.
|
|
19
|
+
- Never invent deep nested shell trees when a flat sibling stack + `$ref` works.
|
|
16
20
|
- `code`, `propsSchema`, `slotsSchema`, `slots`, `slotsLayout` are **top-level** — never under `props`.
|
|
17
|
-
- Custom components expose `function App() {}`. Author `.jsx`/`.tsx`; build bundles imports (see `docs/16-AUTHORING-TS-IMPORTS.md`).
|
|
18
21
|
- Prefer `React.useState` / `React.useEffect` in custom code.
|
|
19
|
-
- Styles: `hsl(var(--primary))` etc. — not raw decoration ints.
|
|
22
|
+
- Styles: `hsl(var(--primary))` etc. — not raw decoration ints (`docs/12-DARK-LIGHT-THEME.md`).
|
|
20
23
|
- Navigation: `Link` / `RouterNav` / `useRouter`.
|
|
21
|
-
- Repeated UI → `shared/components/<id
|
|
22
|
-
- Order / COD → `docs/08-ORDER-FORM.md
|
|
23
|
-
- Checkout / thanks / product → `docs/11-PAGES-CHECKOUT-THANKS-PRODUCT.md
|
|
24
|
-
- Dark / light → `docs/12-DARK-LIGHT-THEME.md`.
|
|
24
|
+
- Repeated UI → `shared/components/<id>.tsx` + `$ref` (`docs/07-SHARED-COMPONENTS.md`).
|
|
25
|
+
- Order / COD → `docs/08-ORDER-FORM.md`.
|
|
26
|
+
- Checkout / thanks / product → `docs/11-PAGES-CHECKOUT-THANKS-PRODUCT.md`.
|
|
25
27
|
- Multilang → `docs/13-TEMPLATE-I18N.md`.
|
|
26
28
|
- Product lists → `docs/14-FILTERATOR.md`.
|
|
27
|
-
- Never `useEffect([cartCtx])` that resets shipping `state` to null — bootstrap once; preserve address.
|
|
28
29
|
|
|
29
30
|
## After edits
|
|
30
31
|
|
|
31
32
|
```bash
|
|
32
33
|
npm run build # or npm run check
|
|
34
|
+
npm run typecheck
|
|
33
35
|
npm run dev # hard-refresh browser
|
|
34
36
|
```
|
|
@@ -8,25 +8,32 @@ alwaysApply: false
|
|
|
8
8
|
|
|
9
9
|
You are editing **theme source**, not a hand-maintained `dist/data.json`.
|
|
10
10
|
|
|
11
|
-
## Structure
|
|
11
|
+
## Structure (required)
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- Leaf file
|
|
16
|
-
- Folder layout (`hero/component.tsx` + `slots/` / `children/`) only when nesting or colocated helpers are needed
|
|
13
|
+
- **Flat TSX is mandatory for leaf customs:** `pages/<page>/components/<name>.tsx`
|
|
14
|
+
- Same for shared/library leaves: `shared/components/<id>.tsx`, `library/components/<id>.tsx`
|
|
15
|
+
- Leaf file = `export const meta` (+ `propsSchema`) + `function App() { … }`
|
|
17
16
|
- Type props: `type Props = FeeefLivePropsOf<typeof propsSchema>` then `const p = props as Props`
|
|
18
|
-
- `satisfies FeeefComponentMeta<typeof propsSchema>` keeps `meta.props` aligned
|
|
19
|
-
-
|
|
17
|
+
- `satisfies FeeefComponentMeta<typeof propsSchema>` keeps `meta.props` aligned
|
|
18
|
+
- **Folder only** when you need `slots/` / `children/` / colocated modules (`<name>/<name>.tsx`)
|
|
19
|
+
- `$ref` placements are leaf `.json` stubs (not duplicated JSX)
|
|
20
|
+
- Prefer a **flat sibling stack** on the page; use a shell folder only when slots are necessary
|
|
21
|
+
- Built-ins (`cart_order_form`, `product_body`, …) are leaf `.json` with `"type": "…"`
|
|
22
|
+
|
|
23
|
+
## Forbidden
|
|
24
|
+
|
|
25
|
+
1. `component.json` + `component.jsx` pairs for new customs
|
|
26
|
+
2. Both `hero.tsx` and `hero/` for the same id
|
|
27
|
+
3. Mixing `pages/<page>/components/` and `pages/<page>/sections/` on one page
|
|
28
|
+
4. Inventing deep nested `*-shell/slots/**` trees when flat siblings + `$ref` work
|
|
29
|
+
5. Putting JSX inside `.json` meta / `$ref` files
|
|
30
|
+
6. Duplicating header/footer JSX — use `shared/components/<id>.tsx` + `$ref`
|
|
20
31
|
|
|
21
32
|
## Rules
|
|
22
33
|
|
|
23
|
-
1.
|
|
24
|
-
2.
|
|
25
|
-
3.
|
|
26
|
-
4.
|
|
27
|
-
5.
|
|
28
|
-
6.
|
|
29
|
-
7. Read `docs/05-ANTI-PATTERNS.md` before inventing patterns.
|
|
30
|
-
8. If UI exists on another page, use `shared/components/<id>.tsx` — do not duplicate.
|
|
31
|
-
9. Do not mix `pages/<page>/components/` and `pages/<page>/sections/` on the same page.
|
|
32
|
-
10. Prefer `feeef template add page|component` / `remove` over hand-creating trees (keeps manifest + starter meta in sync).
|
|
34
|
+
1. Set `"order"` so sibling order is deterministic.
|
|
35
|
+
2. Slot folder names must match `slotsSchema` keys.
|
|
36
|
+
3. After edits: `npm run build` (or rely on `npm run dev` watch).
|
|
37
|
+
4. Read `docs/03-CUSTOM-COMPONENTS.md` before writing JSX.
|
|
38
|
+
5. Read `docs/05-ANTI-PATTERNS.md` before inventing patterns.
|
|
39
|
+
6. Prefer `feeef template add page|component` / `remove` over hand-creating trees.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: feeef-theme
|
|
3
3
|
description: >-
|
|
4
|
-
Author Feeef theme packages (pages/shared/library, custom
|
|
4
|
+
Author Feeef theme packages (pages/shared/library, flat TSX custom components,
|
|
5
5
|
TemplateData build, order forms, i18n, filterator, dark/light). Use when editing
|
|
6
|
-
this theme's
|
|
6
|
+
this theme's *.tsx, locales, or publishing with feeef.
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Feeef Theme Skill
|
|
@@ -13,30 +13,42 @@ description: >-
|
|
|
13
13
|
1. `AGENTS.md`
|
|
14
14
|
2. `docs/00-INDEX.md`
|
|
15
15
|
3. Task-specific doc from the cheat-sheet
|
|
16
|
-
4. Skim `docs/05-ANTI-PATTERNS.md` before writing JSON/
|
|
16
|
+
4. Skim `docs/05-ANTI-PATTERNS.md` before writing JSON/TSX
|
|
17
|
+
|
|
18
|
+
## Authoring contract (required)
|
|
19
|
+
|
|
20
|
+
- **Flat TSX leaves:** `pages/<page>/components/<name>.tsx`
|
|
21
|
+
- `export const meta` (+ `propsSchema`) + `function App()`
|
|
22
|
+
- `type Props = FeeefLivePropsOf<typeof propsSchema>`; `const p = props as Props`
|
|
23
|
+
- `satisfies FeeefComponentMeta<typeof propsSchema>`
|
|
24
|
+
- **Folder only** for `slots/` / `children/` / colocated helpers
|
|
25
|
+
- Shared leaves: `shared/components/<id>.tsx` + placement `{ "$ref": "shared.<id>" }` as `.json`
|
|
26
|
+
- Prefer flat sibling stacks; do **not** invent nested `*-shell/slots/**` when siblings work
|
|
27
|
+
- Scaffold with `feeef template add page|component` — do not invent trees by hand
|
|
17
28
|
|
|
18
29
|
## Workflows
|
|
19
30
|
|
|
20
31
|
### Edit a component
|
|
21
32
|
|
|
22
|
-
1. Open the
|
|
23
|
-
2. Edit `
|
|
33
|
+
1. Open the flat `.tsx` (or `…/<name>.tsx` if it has slots)
|
|
34
|
+
2. Edit `meta` + `App` — no parallel `component.json` for customs
|
|
24
35
|
3. `npm run build` && hard-refresh `npm run dev`
|
|
25
36
|
|
|
26
37
|
### Shared chrome
|
|
27
38
|
|
|
28
|
-
1. Canonical
|
|
39
|
+
1. Canonical leaf: `shared/components/<id>.tsx`
|
|
29
40
|
2. Placements: `{ "$ref": "shared.<id>", "instanceId": "…", "props": { } }`
|
|
30
41
|
3. See `docs/07-SHARED-COMPONENTS.md`
|
|
31
42
|
|
|
32
43
|
### Order / COD
|
|
33
44
|
|
|
34
45
|
1. Read `docs/08-ORDER-FORM.md` first
|
|
35
|
-
2. `
|
|
46
|
+
2. Prefer native `product_order_form` / `cart_order_form` `.json` leaves
|
|
47
|
+
3. Custom forms: `POST /api/orders/create` only; draft + pending; geo; anti-abuse
|
|
36
48
|
|
|
37
49
|
### Special pages
|
|
38
50
|
|
|
39
|
-
`docs/11-PAGES-CHECKOUT-THANKS-PRODUCT.md` —
|
|
51
|
+
`docs/11-PAGES-CHECKOUT-THANKS-PRODUCT.md` — `main` via flat `components/`; slots when needed.
|
|
40
52
|
|
|
41
53
|
### Lists / PLP
|
|
42
54
|
|
|
@@ -50,7 +62,7 @@ description: >-
|
|
|
50
62
|
|
|
51
63
|
- Top-level custom fields — never under `props`
|
|
52
64
|
- `function App()` in published `code`
|
|
53
|
-
- CSS vars for brand colors
|
|
65
|
+
- CSS vars for brand colors (`hsl(var(--…))`)
|
|
54
66
|
- Stable `instanceId`
|
|
55
67
|
- Repeated UI → `shared/` + `$ref`
|
|
56
68
|
- Orders → create API + draft/pending contract
|
|
@@ -6,65 +6,70 @@
|
|
|
6
6
|
|---|---|
|
|
7
7
|
| `build` | source → `dist/data.json` |
|
|
8
8
|
| `check` | validate |
|
|
9
|
-
| `
|
|
9
|
+
| `typecheck` | `tsc --noEmit` |
|
|
10
|
+
| `test` | smoke custom `App`s |
|
|
11
|
+
| `dev` | remote draft preview |
|
|
10
12
|
| `publish` | upload (+ optional `--apply`) |
|
|
11
13
|
|
|
12
|
-
##
|
|
14
|
+
## Flat TSX leaf (required)
|
|
13
15
|
|
|
14
|
-
```
|
|
15
|
-
{
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
"instanceId": "custom_example",
|
|
19
|
-
"title": "Example",
|
|
20
|
-
"propsSchema": {
|
|
21
|
-
"heading": { "type": "string" }
|
|
22
|
-
},
|
|
23
|
-
"props": { "heading": "Hello" },
|
|
24
|
-
"slotsSchema": {},
|
|
25
|
-
"slots": {}
|
|
26
|
-
}
|
|
27
|
-
```
|
|
16
|
+
```tsx
|
|
17
|
+
const propsSchema = {
|
|
18
|
+
heading: { type: "string", name: "Heading" },
|
|
19
|
+
} as const;
|
|
28
20
|
|
|
29
|
-
|
|
21
|
+
export const meta = {
|
|
22
|
+
type: "custom",
|
|
23
|
+
title: "Hero",
|
|
24
|
+
order: 0,
|
|
25
|
+
propsSchema,
|
|
26
|
+
props: { heading: "" },
|
|
27
|
+
} as const satisfies FeeefComponentMeta<typeof propsSchema>;
|
|
28
|
+
|
|
29
|
+
type Props = FeeefLivePropsOf<typeof propsSchema>;
|
|
30
30
|
|
|
31
|
-
```jsx
|
|
32
31
|
function App() {
|
|
33
|
-
const
|
|
34
|
-
const store = storeCtx?.store;
|
|
32
|
+
const p = props as Props;
|
|
35
33
|
return (
|
|
36
34
|
<section style={{ color: "hsl(var(--foreground))" }}>
|
|
37
|
-
<h1>{
|
|
38
|
-
{props.slots?.body}
|
|
35
|
+
<h1>{p.heading || t("home.heroHeading")}</h1>
|
|
39
36
|
</section>
|
|
40
37
|
);
|
|
41
38
|
}
|
|
42
39
|
```
|
|
43
40
|
|
|
41
|
+
Paths:
|
|
42
|
+
|
|
43
|
+
- `pages/<page>/components/<name>.tsx`
|
|
44
|
+
- `shared/components/<id>.tsx`
|
|
45
|
+
- `library/components/<id>.tsx`
|
|
46
|
+
|
|
47
|
+
Folder (`<name>/<name>.tsx` + `slots/`) **only** when nesting is required.
|
|
48
|
+
|
|
44
49
|
## Shared placement
|
|
45
50
|
|
|
46
51
|
```json
|
|
47
52
|
{
|
|
48
53
|
"order": 0,
|
|
49
|
-
"$ref": "shared.
|
|
50
|
-
"instanceId": "
|
|
54
|
+
"$ref": "shared.header",
|
|
55
|
+
"instanceId": "home_header",
|
|
51
56
|
"props": {}
|
|
52
57
|
}
|
|
53
58
|
```
|
|
54
59
|
|
|
55
60
|
## Order form (quick)
|
|
56
61
|
|
|
57
|
-
- `
|
|
58
|
-
-
|
|
59
|
-
- See `docs/08-ORDER-FORM.md`
|
|
62
|
+
- Prefer native `cart_order_form` / `product_order_form` `.json`
|
|
63
|
+
- Custom: `POST /api/orders/create` only — `docs/08-ORDER-FORM.md`
|
|
60
64
|
|
|
61
65
|
## Pages
|
|
62
66
|
|
|
63
67
|
| Page | Path | Rule |
|
|
64
68
|
|---|---|---|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
69
|
+
| home / products / contact | flat `components/` stack | `$ref` chrome as siblings |
|
|
70
|
+
| product | `/products/:id` | shell folder + slots if needed |
|
|
71
|
+
| checkout | `/checkout` | `cart_order_form` leaf |
|
|
72
|
+
| thank_you | `/thanks` | body + chrome |
|
|
68
73
|
|
|
69
74
|
## Colors / i18n / lists
|
|
70
75
|
|
|
@@ -8,9 +8,10 @@ editor, the storefront preview, and published stores. Follow this file exactly.
|
|
|
8
8
|
1. **One JSON contract** — `TemplateData` (built into `dist/data.json`) is what Feeef stores and renders. `dist/schema.json` is the editor schema.
|
|
9
9
|
2. **Source vs dist** — edit `schema.ts`, `pages/`, `shared/`, `library/`, `locales/`, `props.json`; run `npm run build`. Do not hand-edit large `dist/*` as the source of truth.
|
|
10
10
|
3. **Template schema is yours** — `schema.ts` (`satisfies FeeefThemeSchema`) controls pages/layouts/root `propsSchema`. Build merges Lithium built-ins into `dist/schema.json` for publish.
|
|
11
|
-
4. **`type: "custom"` is react-live** — published `code` is a **string** evaluated in a sandbox. It must expose top-level `function App() { … }`. Author
|
|
11
|
+
4. **`type: "custom"` is react-live** — published `code` is a **string** evaluated in a sandbox. It must expose top-level `function App() { … }`. **Author flat TSX:** `pages/<page>/components/<name>.tsx` with `export const meta` (include `propsSchema`) + `App`. Type props via `FeeefLivePropsOf<typeof propsSchema>` and `satisfies FeeefComponentMeta<typeof propsSchema>`. Local/`npm` imports are **bundled at build time** (see `docs/16-AUTHORING-TS-IMPORTS.md`). Use a folder (`<name>/<name>.tsx`) **only** when you need `slots/` / `children/` or colocated modules. **Do not** author new customs as `component.json` + `component.jsx`.
|
|
12
12
|
5. **`code` / `propsSchema` / `slotsSchema` / `slots` / `slotsLayout` are TOP-LEVEL** on the component object — never nest them under `props`.
|
|
13
13
|
6. **Planes stay separate** — schema (editable fields) ≠ merchant content (`props`) ≠ renderer.
|
|
14
|
+
7. **Prefer flat sibling stacks** — `header.json` (`$ref`) + `hero.tsx` + `footer.json` under `pages/<page>/components/`. Do **not** invent deep nested `*-shell/slots/**` trees when siblings work.
|
|
14
15
|
|
|
15
16
|
## Before you change anything
|
|
16
17
|
|
|
@@ -36,7 +37,9 @@ editor, the storefront preview, and published stores. Follow this file exactly.
|
|
|
36
37
|
| Keep `instanceId` stable when editing existing components | Selection + React keys |
|
|
37
38
|
| `children` only for `grid` / `flex` / `container` | Other types use `slots` |
|
|
38
39
|
| Empty section **keys** optional; published JSON still has `sections` object | Flat disk → `sections.main`; legacy multi-section keys still work |
|
|
39
|
-
| UI on 2+ pages → `shared/components
|
|
40
|
+
| UI on 2+ pages → `shared/components/<id>.tsx` + `$ref` | No duplicated chrome JSX |
|
|
41
|
+
| Flat TSX leaves for customs — no `component.json`+`jsx` pairs | Meta in `export const meta` |
|
|
42
|
+
| No hand-built nested shell trees when flat stack works | Siblings under `components/` |
|
|
40
43
|
| Custom order forms → `docs/08-ORDER-FORM.md` | Draft, geo, create API, anti-abuse |
|
|
41
44
|
| Never re-bootstrap shipping on every `cartCtx` change | Wipes state/city |
|
|
42
45
|
| Add to cart = `addCurrentItemToCart` (or `cart.add`) | Not `updateCurrentItem({})` theater |
|
|
@@ -60,7 +63,7 @@ dist/data.json # TemplateData
|
|
|
60
63
|
dist/schema.json # merged editor schema (not hand-edited)
|
|
61
64
|
```
|
|
62
65
|
|
|
63
|
-
Prefer a
|
|
66
|
+
Prefer a **flat sibling stack** under `pages/<page>/components/` (builds to `sections.main`). Use a shell folder + slots only when nesting is required (typical for product/checkout). Do not mix flat `components/` and `sections/` on one page.
|
|
64
67
|
|
|
65
68
|
## Local commands
|
|
66
69
|
|
|
@@ -85,7 +88,7 @@ npm run publish -- --apply
|
|
|
85
88
|
|
|
86
89
|
**Preview:** `npm run dev` pushes `metadata.templatePreview` and opens `?preview=…`. Customers keep live `templateData`. Exit via banner or `/?preview=0`. Go live only with `publish --apply`.
|
|
87
90
|
|
|
88
|
-
**AI / agents:** use `feeef template add page|component` (and `remove`) instead of inventing folder trees by hand. New pages are flat `components
|
|
91
|
+
**AI / agents:** use `feeef template add page|component` (and `remove`) instead of inventing folder trees by hand. New pages are flat `components/*.tsx` → published `sections.main`. Shared chrome = flat `shared/components/<id>.tsx` + `$ref` `.json` placements. Do **not** mix flat `components/` with `sections/` on the same page. Do **not** invent deep `*-shell/slots/**` nests when a flat stack works.
|
|
89
92
|
|
|
90
93
|
Editor: `tsconfig.json` + `types/feeef-live-scope.d.ts` + `types/feeef-template-schema.d.ts` explain react-live globals and `FeeefThemeSchema` (template schema type) to VS Code / Cursor.
|
|
91
94
|
|
|
@@ -8,7 +8,7 @@ npm install -g @feeef.dev/cli
|
|
|
8
8
|
|
|
9
9
|
**AI / Cursor:** read [`AGENTS.md`](./AGENTS.md) and [`docs/00-INDEX.md`](./docs/00-INDEX.md) first. Rules and skills live under [`.cursor/`](./.cursor/).
|
|
10
10
|
|
|
11
|
-
Author components under `pages/<page>/components/<name>.tsx` (`export const meta` + `function App()`). Build maps them to published `sections.main`.
|
|
11
|
+
Author components under `pages/<page>/components/<name>.tsx` (`export const meta` + `function App()`). **Flat TSX is required for leaf customs.** Build maps them to published `sections.main`. Use a folder only for `slots/` / `children/`. Shared chrome: `shared/components/<id>.tsx` + `$ref` `.json` placements. Local `import`s are bundled at `npm run build` into react-live `code` (`react` stays provided by the storefront).
|
|
12
12
|
|
|
13
13
|
Editor: `tsconfig.json` + `types/feeef-live-scope.d.ts` teach VS Code / Cursor about live-scope globals (`props`, `t`, `useStore`, …). Run `npm install` once after init.
|
|
14
14
|
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
> Theme package copy — paths assume this theme folder is the project root.
|
|
4
4
|
|
|
5
|
-
Full API: `
|
|
6
|
-
Architecture: `docs/03-CUSTOM-COMPONENTS.md`.
|
|
5
|
+
Full API and authoring contract for `type: "custom"` components.
|
|
7
6
|
|
|
8
7
|
## What a custom component is
|
|
9
8
|
|
|
10
|
-
A `TemplateDataComponent` with:
|
|
9
|
+
A `TemplateDataComponent` with top-level:
|
|
11
10
|
|
|
12
11
|
```json
|
|
13
12
|
{
|
|
@@ -23,29 +22,68 @@ A `TemplateDataComponent` with:
|
|
|
23
22
|
}
|
|
24
23
|
```
|
|
25
24
|
|
|
26
|
-
`code` is executed by `CustomLive`
|
|
25
|
+
`code` is executed by `CustomLive` via **react-live**. You author **TypeScript/TSX on disk**; `npm run build` (template-kit) emits the `code` string. There is **no browser bundler** at runtime: `react` / `react-dom` stay in the live scope.
|
|
27
26
|
|
|
28
|
-
## Required
|
|
27
|
+
## Required authoring shape (flat TSX)
|
|
28
|
+
|
|
29
|
+
**Prefer a single flat file** under the page (or shared/library):
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
pages/home/components/hero.tsx
|
|
33
|
+
shared/components/header.tsx
|
|
34
|
+
library/components/promo-banner.tsx
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
const propsSchema = {
|
|
39
|
+
heading: { type: "string", name: "Heading" },
|
|
40
|
+
} as const;
|
|
41
|
+
|
|
42
|
+
export const meta = {
|
|
43
|
+
type: "custom",
|
|
44
|
+
title: "Hero",
|
|
45
|
+
order: 0,
|
|
46
|
+
propsSchema,
|
|
47
|
+
props: { heading: "" },
|
|
48
|
+
} as const satisfies FeeefComponentMeta<typeof propsSchema>;
|
|
49
|
+
|
|
50
|
+
type Props = FeeefLivePropsOf<typeof propsSchema>;
|
|
29
51
|
|
|
30
|
-
```jsx
|
|
31
52
|
function App() {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
return <div>…</div>;
|
|
53
|
+
const p = props as Props;
|
|
54
|
+
return <h1>{p.heading || t("home.heroHeading")}</h1>;
|
|
35
55
|
}
|
|
36
56
|
```
|
|
37
57
|
|
|
38
|
-
|
|
58
|
+
Rules:
|
|
59
|
+
|
|
60
|
+
- Entry must be **`function App()`** (published `code` must expose it).
|
|
39
61
|
- Prefer **`React.useState` / `React.useEffect` / …** — merchant preview may not inject bare hooks.
|
|
40
|
-
-
|
|
62
|
+
- **Do not** author a parallel `component.json` for customs — meta lives in `export const meta`.
|
|
63
|
+
- **Do not** put `import` / `export` / `require` in the *published* live string (build strips / bundles). Authoring **may** use local/`npm` imports — see [16-AUTHORING-TS-IMPORTS.md](./16-AUTHORING-TS-IMPORTS.md).
|
|
64
|
+
- Type props with `FeeefLivePropsOf<typeof propsSchema>`; cast `const p = props as Props`.
|
|
65
|
+
|
|
66
|
+
### When to use a folder
|
|
41
67
|
|
|
42
|
-
|
|
68
|
+
Use a folder **only** when you need `slots/`, `children/`, or colocated modules:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
pages/product/components/product-shell/
|
|
72
|
+
product-shell.tsx # export const meta + App (includes slotsSchema)
|
|
73
|
+
slots/header/header.json # $ref or leaf
|
|
74
|
+
slots/body/gallery.tsx
|
|
75
|
+
slots/form/order.json # built-in type
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Never create both `hero.tsx` and `hero/` for the same id.
|
|
79
|
+
|
|
80
|
+
## Injected scope (no imports needed at runtime)
|
|
43
81
|
|
|
44
82
|
| Symbol | Use |
|
|
45
83
|
|---|---|
|
|
46
84
|
| `React` | Hooks, Fragment |
|
|
47
|
-
| `props` | Instance props |
|
|
48
|
-
| `props.slots` | Pre-rendered slot children
|
|
85
|
+
| `props` | Instance props — cast with `FeeefLivePropsOf` |
|
|
86
|
+
| `props.slots` | Pre-rendered slot children |
|
|
49
87
|
| `SlotContextBridge` | Provide context to slot children |
|
|
50
88
|
| `useSlotContext(key)` | Read parent slot context |
|
|
51
89
|
| `useStore()` | `{ store }` or null — **null-check** |
|
|
@@ -54,12 +92,12 @@ function App() {
|
|
|
54
92
|
| `useFeeef` | SDK instance or null |
|
|
55
93
|
| `useTheme()` | next-themes |
|
|
56
94
|
| `useTemplate()` | read-only template state |
|
|
57
|
-
| `t(key, params?)` | Theme locales
|
|
95
|
+
| `t(key, params?)` | Theme locales — [13-TEMPLATE-I18N.md](./13-TEMPLATE-I18N.md) |
|
|
58
96
|
| `useFeeefT()` / `useFeeefLocale()` | Theme i18n hooks |
|
|
59
97
|
| `useRouter` / `usePathname` / `useSearchParams` | next/navigation |
|
|
60
|
-
| `Link` | next/link — prefer over `<a>` |
|
|
98
|
+
| `Link` | next/link — prefer over `<a>` for in-app nav |
|
|
61
99
|
| `RouterNav` | clickable block with stopPropagation for nested cards |
|
|
62
|
-
| `SlotsLayout` | Renders `props.slotsLayout`
|
|
100
|
+
| `SlotsLayout` | Renders `props.slotsLayout` with `props.slots` |
|
|
63
101
|
| `dartColorToCssColor` | convert Flutter color ints from **props** |
|
|
64
102
|
|
|
65
103
|
For product markdown details use registry **`product_body`** (reads `product.body`), not CustomLive HTML injection.
|
|
@@ -70,27 +108,19 @@ For product markdown details use registry **`product_body`** (reads `product.bod
|
|
|
70
108
|
- Corners: `var(--corners-card)`, `var(--corners-buttons-large)`, …
|
|
71
109
|
- **Do not** assign `store.decoration.primary` directly (Flutter AARRGGBB int).
|
|
72
110
|
- Prefer store fields for chrome: `store.logoUrl`, `store.name`, `store.contacts`, …
|
|
73
|
-
- **Dark / light:** ride native tokens (`:root` / `.dark`)
|
|
74
|
-
- Toggle: `const { resolvedTheme, setTheme } = useTheme();`
|
|
111
|
+
- **Dark / light:** ride native tokens (`:root` / `.dark`) — [12-DARK-LIGHT-THEME.md](./12-DARK-LIGHT-THEME.md).
|
|
75
112
|
|
|
76
113
|
## Slots in JSX
|
|
77
114
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<SlotContextBridge value={{ shell: { isOpen, setIsOpen } }}>
|
|
82
|
-
{props.slots?.sections}
|
|
83
|
-
</SlotContextBridge>
|
|
115
|
+
```tsx
|
|
116
|
+
{props.slots?.header}
|
|
117
|
+
<main>{props.slots?.body}</main>
|
|
84
118
|
{props.slots?.footer}
|
|
85
119
|
```
|
|
86
120
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
```jsx
|
|
90
|
-
const shell = useSlotContext("shell");
|
|
91
|
-
```
|
|
121
|
+
Or prefer `<SlotsLayout />` when `slotsLayout` is set.
|
|
92
122
|
|
|
93
|
-
**Never** use slot context for navigation
|
|
123
|
+
**Never** use slot context for navigation. Use `Link` / `RouterNav` / `useRouter()`.
|
|
94
124
|
|
|
95
125
|
## Product media
|
|
96
126
|
|
|
@@ -99,18 +129,17 @@ const shell = useSlotContext("shell");
|
|
|
99
129
|
|
|
100
130
|
## Cart
|
|
101
131
|
|
|
102
|
-
```
|
|
132
|
+
```tsx
|
|
103
133
|
const cartCtx = useFeeefCart();
|
|
104
134
|
if (!cartCtx) return null;
|
|
105
135
|
cartCtx.cart.add({ product, quantity: 1, variantPath: "Color/Red" });
|
|
106
136
|
```
|
|
107
137
|
|
|
108
|
-
## Kit authoring
|
|
109
|
-
|
|
110
|
-
In `…/my-comp/`:
|
|
138
|
+
## Kit authoring checklist
|
|
111
139
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
- `
|
|
140
|
+
1. Prefer `feeef template add component <page> <name>` → flat `<name>.tsx`
|
|
141
|
+
2. Put chrome reused on 2+ pages in `shared/components/<id>.tsx` + `$ref` placements ([07](./07-SHARED-COMPONENTS.md))
|
|
142
|
+
3. Built-ins (order forms, `product_body`) stay as leaf `.json` with `type: "…"`
|
|
143
|
+
4. `npm run build` then `npm run dev`
|
|
115
144
|
|
|
116
|
-
See [04-WORKFLOW.md](./04-WORKFLOW.md).
|
|
145
|
+
See [04-WORKFLOW.md](./04-WORKFLOW.md) / [04-WORKFLOW.md](./04-WORKFLOW.md).
|
|
@@ -63,7 +63,7 @@ pages/<page>/components/ # preferred (→ published sections.main)
|
|
|
63
63
|
<name>.tsx # leaf: export const meta + App
|
|
64
64
|
<name>.json # leaf built-in / no code
|
|
65
65
|
<name>/ # only when slots/ or children/ needed
|
|
66
|
-
|
|
66
|
+
<name>.tsx
|
|
67
67
|
slots/… | children/…
|
|
68
68
|
pages/<page>/sections/<section>/components/ # legacy multi-section (still supported)
|
|
69
69
|
shared/components/<id>.tsx
|
|
@@ -98,9 +98,9 @@ Types: `types/feeef-template-schema.d.ts`. Fallback: `schema.json` (same shape).
|
|
|
98
98
|
|
|
99
99
|
## Custom components
|
|
100
100
|
|
|
101
|
-
-
|
|
102
|
-
- Use a folder only when you need `slots/` or `children/` (or colocated helpers)
|
|
103
|
-
-
|
|
101
|
+
- **Required:** page-level `components/hero.tsx` — `export const meta` + `function App() { … }`
|
|
102
|
+
- Use a folder **only** when you need `slots/` or `children/` (or colocated helpers)
|
|
103
|
+
- Prefer flat sibling stacks (`header.json` `$ref` + section `.tsx` + `footer.json`); shell folders only when slots are necessary
|
|
104
104
|
- Type-safe props from schema:
|
|
105
105
|
|
|
106
106
|
```tsx
|
|
@@ -124,6 +124,7 @@ function App() {
|
|
|
124
124
|
|
|
125
125
|
- Build strips `meta` and bundles into react-live `code` (see [16-AUTHORING-TS-IMPORTS.md](./16-AUTHORING-TS-IMPORTS.md))
|
|
126
126
|
- `react` stays provided by the storefront — do not bundle a second React
|
|
127
|
+
- **Do not** author new customs as `component.json` + `component.jsx`
|
|
127
128
|
|
|
128
129
|
## Do not
|
|
129
130
|
|
|
@@ -132,3 +133,5 @@ function App() {
|
|
|
132
133
|
- Call `ff.orders.send` from custom checkout (use `POST /api/orders/create` — [08](./08-ORDER-FORM.md))
|
|
133
134
|
- Expect `npm run dev` to update what customers see (it does not)
|
|
134
135
|
- Clone the Lithium storefront just to preview (remote draft is the default)
|
|
136
|
+
- Invent deep nested `*-shell/slots/**` trees when a flat `components/` stack works
|
|
137
|
+
- Create both `name.tsx` and `name/` for the same id
|
|
@@ -35,7 +35,7 @@ High-signal mistakes AIs and humans make in this system.
|
|
|
35
35
|
| `<a href="/p/x">` for in-app nav | `<Link href="/p/x">` or `RouterNav` |
|
|
36
36
|
| `useSlotContext('x').routerNav.push` | Real `useRouter` / `Link` / `RouterNav` |
|
|
37
37
|
| Assuming `useStore()` non-null | Always null-check |
|
|
38
|
-
| TypeScript / JSX types in published `code` | Author `
|
|
38
|
+
| TypeScript / JSX types in published `code` | Author `<name>.tsx` (or `<name>/<name>.tsx` with slots); build strips types into `code` |
|
|
39
39
|
| Assuming raw `import` works inside react-live without build | Use theme `npm run build` — imports are bundled into `code` |
|
|
40
40
|
| Bundling a second `react` into `code` | Mark react as external (build does this); use live scope |
|
|
41
41
|
|
|
@@ -46,12 +46,13 @@ High-signal mistakes AIs and humans make in this system.
|
|
|
46
46
|
| Hand-edit `dist/data.json` as primary workflow | Edit `pages/` / `shared/`, then `npm run build` |
|
|
47
47
|
| Use Node APIs inside component source | Use `utils/get-template.ts` |
|
|
48
48
|
| Arbitrary filesystem preview paths | Only `feeef template dev` / CLI override |
|
|
49
|
-
| Treating `npm run dev` as “go live” | Draft only — use `publish --apply` for customers |
|
|
50
|
-
| Cloning Lithium storefront just to preview | Default is remote draft (`?preview=`) |
|
|
51
49
|
| Expect Shopify reference dumps to render | They are not Feeef themes |
|
|
52
|
-
| Skip `npm run check` after edits | Rebuild and refresh
|
|
50
|
+
| Skip `npm run check` after edits | Rebuild and hard-refresh preview |
|
|
53
51
|
| Commit `dist/` as source | Source is the folder tree |
|
|
54
|
-
| Duplicate the same footer/navbar JSX on every page | Promote to `shared/components/<id
|
|
52
|
+
| Duplicate the same footer/navbar JSX on every page | Promote to `shared/components/<id>.tsx` + `$ref` |
|
|
53
|
+
| Author customs as `component.json` + `component.jsx` | Flat `<name>.tsx` with `export const meta` + `App` |
|
|
54
|
+
| Hand-build nested `*-shell/slots/**` when a flat stack works | Flat `pages/<page>/components/*` siblings + `$ref` |
|
|
55
|
+
| Both `hero.tsx` and `hero/` for the same id | Keep **one** entry (folder only if slots/children needed) |
|
|
55
56
|
| Copy dawn-order-form as “complete” checkout | Follow `docs/08-ORDER-FORM.md` + native `order-form.tsx` |
|
|
56
57
|
| `ff.orders.send` from custom JSX | `fetch("/api/orders/create")` |
|
|
57
58
|
| Free-text wilaya/city without cascade/indices | Legacy 1-based indices or new state codes + city names |
|
|
@@ -66,7 +67,7 @@ High-signal mistakes AIs and humans make in this system.
|
|
|
66
67
|
|
|
67
68
|
## Registry / built-ins
|
|
68
69
|
|
|
69
|
-
Prefer built-in types when they fit; otherwise `type: "custom"` with `
|
|
70
|
+
Prefer built-in types when they fit; otherwise `type: "custom"` with flat `.tsx` (`export const meta` + `App`).
|
|
70
71
|
|
|
71
72
|
## AI generation
|
|
72
73
|
|