@sudajs/cli 0.9.0 → 0.9.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudajs/cli",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "suda": "./bin/suda.js"
@@ -32,7 +32,7 @@
32
32
  "react": "^19.2.7",
33
33
  "react-dom": "^19.2.7",
34
34
  "zod": "^3.24.1",
35
- "@sudajs/theme-engine": "2.3.0"
35
+ "@sudajs/theme-engine": "2.3.1"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@tailwindcss/postcss": "^4.3.0",
@@ -2,57 +2,81 @@
2
2
 
3
3
  This directory is a standalone Vite + React + Tailwind Suda theme generated by `suda theme init`.
4
4
 
5
- ## Contract
5
+ Your job in a theme project is to create a complete, publishable theme with theme-specific components, realistic starter pages, scoped styling, assets, and AI metadata. Do not build pages by assembling generic engine components; those are reserved for end users who need supplemental editing blocks after the theme is installed.
6
+
7
+ ## Theme contract
6
8
 
7
9
  - Keep `renderMode: "ssr"` in `src/manifest.ts`.
8
10
  - Keep source entries at `src/index.tsx`, `src/runtime.client.ts`, and `src/styles.css`.
11
+ - Export a complete `ThemeModule` from `src/index.tsx`: `manifest`, `pageConfig`, `layoutConfig`, `defaultLayout`, and `starterPages`.
12
+ - Keep `pageConfig` focused on page content sections. Keep `layoutConfig` focused on shared site chrome such as root, header, page outlet, and footer.
9
13
  - Do not edit generated files under `dist/`; run `pnpm build` to regenerate them.
10
14
  - React, React DOM, Puck, and `@sudajs/theme-engine` are host-provided peers. Do not bundle private copies into the theme runtime.
15
+ - Keep persisted props JSON-serializable. Do not store functions, React nodes, class instances, database ids for media, or environment-specific absolute filesystem paths.
16
+
17
+ ## Component strategy
18
+
19
+ - Create theme-specific components and sections first. Every page component should express this theme's brand, industry, visual rhythm, and content model.
20
+ - Do not register `createEngineComponents()`, `createBaseBlocks()`, or `createContainers()` in `pageConfig.components` when creating a theme. Generic engine components are fallback editing tools for users, not the foundation of a theme template.
21
+ - Prefer purpose-built sections such as hero, services, product highlights, feature comparisons, gallery, process, pricing, testimonial, FAQ, article list, contact, and CTA when the theme needs those patterns.
22
+ - Keep section APIs semantic. Expose props that describe content and editor decisions, not implementation details.
23
+ - Keep `fields`, `defaultProps`, and `render` in sync. Every editor field should have a sensible default, and render functions should tolerate omitted optional values.
24
+ - Put editor controls in props when they change rendering, for example `showLogo`, `mediaPosition`, `tone`, or `columns`; pair dependent controls with `visibleIf` rather than hiding logic only in JSX.
25
+
26
+ ## Layout and slot rules
27
+
28
+ - Page-level sections and components must not declare native Puck `slot` fields. Do not write `fields: { content: { type: "slot" } }` in page components.
29
+ - Native Puck slots are reserved for Suda-provided layout/container primitives and theme layout components. The normal template pattern is the one in `src/layout.tsx`: `rootConfig` renders `children`, `PageOutlet` renders `getPageSlot(puck?.metadata)`, and `Header` / `Footer` surround that outlet in `defaultLayout`.
30
+ - Do not add arbitrary nested page composition through native slots. It makes AI-generated pages unpredictable and hard to validate.
31
+ - If a page section needs controlled nested content, use Suda `blockSlots`, not a hand-written Puck slot field. `blockSlots` lets the theme define exactly which local block kinds are allowed inside that section.
32
+ - Do not use legacy DropZone or `zones` patterns.
11
33
 
12
- ## Styling
34
+ ## Styling and assets
13
35
 
14
36
  - Tailwind v4 starts in `src/styles.css` with `@import "tailwindcss";` and `@source "./**/*.{ts,tsx}";`.
15
- - Theme-local assets live in top-level `assets/`; the CLI copies them to `dist/assets/` during build. Use `themeAsset("assets/...")` instead of importing images from React code.
37
+ - Theme-local assets live in top-level `assets/`; the CLI copies them to `dist/assets/` during build.
38
+ - Use `themeAsset("assets/...")` for theme-bundled assets in default props and starter pages. Do not import images from React code.
39
+ - Use `resolveAsset(puck?.metadata, value)` from `@sudajs/theme-engine/runtime` before rendering user-selected media fields.
40
+ - Scope theme CSS with the generated theme key classes. Avoid global resets that could affect the host editor or other themes.
16
41
  - Preview screenshots are required at `assets/preview/desktop.png`, `assets/preview/tablet.png`, and `assets/preview/mobile.png`; run `suda theme capture` to generate them.
17
- - Customize `vite.config.ts`, `postcss.config.mjs`, and `src/styles.css` as needed.
42
+ - Customize `vite.config.ts`, `postcss.config.mjs`, and `src/styles.css` only when the theme needs it.
18
43
 
19
- ## Commands
44
+ ## Field authoring rules
20
45
 
21
- ```bash
22
- pnpm install
23
- pnpm lint
24
- pnpm typecheck
25
- pnpm format
26
- pnpm dev # Vite-powered local preview
27
- pnpm build # Vite build + Suda artifact finalize + AI metadata check
28
- pnpm validate
29
- suda theme check
30
- ```
46
+ Use the most specific field type available. A generic `text` field may pass typecheck, but it is wrong when a semantic Suda field exists.
47
+
48
+ - Always use `url` for links and routes, including anchors, `mailto:` links, and external URLs. Do not use `text` for href-like props.
49
+ - Use `image` for images, `video` for videos, and `media` only when the same prop intentionally accepts mixed media.
50
+ - Use `color` for color values, `icon` for icon names, `font` for font selections, `range` for bounded numbers, `spacing` for spacing tokens, and `menu` for navigation items.
51
+ - Use `select` or `radio` for fixed choices. Keep option values stable, short, and serializable.
52
+ - Use `array` for repeatable items and `object` for grouped settings. Keep nested field names aligned with the prop shape and `defaultProps`.
53
+ - Use `textarea` for multi-sentence copy. Use `text` only for short labels, headings, slugs, names, or plain strings that are not URLs, media, colors, icons, fonts, or menus.
54
+ - For card grids, feature lists, pricing tables, logo walls, gallery grids, and any multi-column component, expose a `columns` prop with a `range` or `select` field and a sensible default.
55
+ - Use `visibleIf: ({ props }, { fields }) => boolean` for synchronous field visibility that depends on sibling props or resolved fields. Keep it pure and fast.
56
+ - Use Puck `resolveFields` only for heavier dynamic field changes that cannot be expressed with `visibleIf`.
57
+ - Add field-level `ai.instructions`, `ai.required`, `ai.exclude`, `ai.stream`, or `ai.bind` when a prop needs generation guidance beyond its label and type. Use `ai.stream: false` for atomic values such as URLs that should not stream partially.
31
58
 
32
59
  ## AI metadata
33
60
 
34
- Every component and layout component should declare useful `ai.instructions`. Missing component-level instructions fail `suda theme check` and block publish.
61
+ Every page component and layout component must declare useful `ai.instructions`. Missing component-level instructions fail `suda theme check` and block publish.
35
62
 
36
- Use `visibleIf: ({ props }, { fields }) => boolean` for synchronous field visibility that depends on sibling props or resolved fields. Keep it pure and fast; use Puck `resolveFields` directly for heavier dynamic field changes. AI and MCP schemas expose `visibleIf` fields as dynamic visibility so agents know the editor surface can change with props.
63
+ Good component instructions explain:
37
64
 
38
- ## Field authoring rules
65
+ - Purpose: what content or business role the component serves.
66
+ - Use when / avoid when: how AI should choose this component instead of a similar one.
67
+ - Placement: where it normally belongs in a page.
68
+ - Frequency: whether it should appear once, multiple times, or only near another section.
69
+ - Composition: required neighboring content or local blocks, when relevant.
39
70
 
40
- - Prefer Suda field helpers over generic text fields: use `url` for links and routes, `image` for images, `video` for videos, `media` for mixed media, `color` for colors, `icon` for icon names, `font` for font selections, `range` for bounded numbers, `spacing` for spacing tokens, and `menu` for navigation items.
41
- - Use `select` or `radio` when authors must choose from a fixed set of values. Keep option values stable and serializable.
42
- - Use `array` for repeatable items and `object` for grouped settings. Keep nested field names aligned with the prop shape and `defaultProps`.
43
- - For card grids, feature lists, pricing tables, logo walls, gallery grids, and any multi-column component, expose a `columns` prop with a `range` or `select` field and a sensible default.
44
- - Use `textarea` for multi-sentence copy and `text` only for short labels, headings, slugs, or plain strings that are not URLs/media/color/icon/font values.
45
- - Keep `fields`, `defaultProps`, and `render` in sync: every editor field should have a sensible default and render should tolerate omitted optional values.
46
- - Put editor-only controls in props when they change rendering, for example `showLogo`; pair dependent controls with `visibleIf` rather than hiding logic only in JSX.
47
- - Keep persisted props JSON-serializable. Do not store functions, React nodes, class instances, database ids for media, or environment-specific absolute filesystem paths.
48
- - Add optional field-level `ai.instructions`, `ai.required`, or `ai.exclude` when a prop needs generation guidance beyond its label and type.
71
+ Use `ai.exclude: true` only for structural or editor-only components, and still provide instructions explaining why AI must not generate them. `PageOutlet` is the standard example.
49
72
 
50
73
  ## Block slot authoring rules
51
74
 
52
- Prefer `blockSlots` when a section owns nested content that benefits from independent editor selection and configuration, such as hero actions, pricing cards, feature rows, stats, or timeline items. This is usually more editor-friendly than modeling those items as plain `array` / `object` fields because each local block can be selected and configured on its own in the visual editor. Keep ordinary section props in `fields`; `fields` is optional and does not need to list every prop.
75
+ Use `blockSlots` when a section owns controlled local content that benefits from independent editor selection and configuration, such as hero actions, pricing cards, feature rows, stats, timeline items, or contact methods. Keep ordinary section props in `fields`.
53
76
 
54
- - Type the slot prop as Puck `Slot` and declare `blockSlots` with the same prop key. Non-slot props should stay in `fields` or nested `array` / `object` fields.
77
+ - Type the slot prop as Puck `Slot` and declare `blockSlots` with the same prop key. This is the Suda block-slot API; do not create a native `fields.<key>.type = "slot"` yourself.
55
78
  - Define local block kinds under `blockSlots.<slotName>.blocks`. Local blocks support normal Suda fields, `label`, `defaultProps`, `render`, `metadata`, `inline`, `permissions`, and optional `ai`.
79
+ - Keep local blocks specific to the owning section. They are not reusable global page components.
56
80
  - Do not add nested `blockSlots` or `cms` to local blocks. Block slots are one level deep in the theme authoring API.
57
81
  - Do not put the slot prop in the host component's `defaultProps`; use `defaultBlocks` on the slot instead.
58
82
  - Do not hand-write `id` in local block `defaultProps`, `defaultBlocks[].props`, or starter page nested block props. The editor/runtime owns block ids.
@@ -71,7 +95,8 @@ type HeroProps = {
71
95
  export const Hero: SudaComponentConfig<HeroProps> = {
72
96
  label: "Hero",
73
97
  ai: {
74
- instructions: "Primary page introduction with editable call-to-action blocks.",
98
+ instructions:
99
+ "Primary page introduction for landing pages. Use once near the top with one or two local action blocks.",
75
100
  },
76
101
  fields: {
77
102
  title: { type: "text", label: "Title" },
@@ -108,7 +133,7 @@ export const Hero: SudaComponentConfig<HeroProps> = {
108
133
  };
109
134
  ```
110
135
 
111
- Starter page data should use the public host component type and a standard slot array on the slot prop. Use local block `kind` values as nested `type` values.
136
+ Starter page data should use the public host component type and a standard array on the block-slot prop. Use local block `kind` values as nested `type` values.
112
137
 
113
138
  ```ts
114
139
  {
@@ -128,3 +153,35 @@ Starter page data should use the public host component type and a standard slot
128
153
  },
129
154
  }
130
155
  ```
156
+
157
+ ## Starter pages
158
+
159
+ - Provide realistic starter pages in `src/templates.ts`; do not ship placeholder-only pages.
160
+ - Every starter page is Puck `Data` with `root: { props: {} }` and `content: [...]`.
161
+ - Each top-level component instance in starter page `content` must include a stable `props.id`.
162
+ - Use only components registered in `pageConfig.components` for top-level page content.
163
+ - Match starter page content to the theme's intended audience and category. The home page should show the theme's best composition, not just every component in order.
164
+ - Use `themeAsset("assets/...")` for bundled starter media.
165
+
166
+ ## CMS-aware themes
167
+
168
+ - If the theme supports posts or tags, declare `content.views` on the exported `ThemeModule`.
169
+ - Add one starter page with matching `cmsView` for every declared CMS view.
170
+ - Declare component-level `cms` capabilities only on components that can render that CMS source.
171
+ - Use `cmsBinding` in starter page props when a component should receive CMS data at render time.
172
+ - Keep static fallback props for CMS components so the page still renders meaningfully when CMS payloads are absent.
173
+
174
+ ## Commands
175
+
176
+ ```bash
177
+ pnpm install
178
+ pnpm lint
179
+ pnpm typecheck
180
+ pnpm format
181
+ pnpm dev # Vite-powered local preview
182
+ pnpm build # Vite build + Suda artifact finalize + AI metadata check
183
+ pnpm validate
184
+ suda theme check
185
+ ```
186
+
187
+ Before publishing or handing off a finished theme, run `pnpm typecheck`, `pnpm lint`, `pnpm build`, and `pnpm validate`. Use `suda theme check` when iterating on AI metadata.