handoff-app 2.0.0-6 → 2.0.0-7

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.
Files changed (174) hide show
  1. package/README.md +129 -41
  2. package/UPGRADE.md +198 -0
  3. package/config/config.template.js +1 -4
  4. package/config/config.template.ts +1 -2
  5. package/dist/app-builder/build.js +2 -4
  6. package/dist/app-builder/config-diff/strategies/pattern.js +3 -3
  7. package/dist/app-builder/watchers/component.js +5 -5
  8. package/dist/app-builder/watchers/utils.d.ts +2 -3
  9. package/dist/app-builder/watchers.d.ts +8 -15
  10. package/dist/app-builder/watchers.js +53 -54
  11. package/dist/cache/build-cache.js +18 -22
  12. package/dist/catalog/define.d.ts +6 -0
  13. package/dist/catalog/define.js +72 -0
  14. package/dist/catalog/implementation.d.ts +27 -0
  15. package/dist/catalog/implementation.js +133 -0
  16. package/dist/catalog/index.d.ts +6 -0
  17. package/dist/catalog/index.js +22 -0
  18. package/dist/catalog/normalize.d.ts +16 -0
  19. package/dist/catalog/normalize.js +115 -0
  20. package/dist/catalog/previews.d.ts +18 -0
  21. package/dist/catalog/previews.js +106 -0
  22. package/dist/catalog/renderers.d.ts +43 -0
  23. package/dist/catalog/renderers.js +62 -0
  24. package/dist/catalog/types.d.ts +63 -0
  25. package/dist/cli/make.d.ts +0 -5
  26. package/dist/cli/make.js +22 -67
  27. package/dist/cli/scaffold.js +25 -33
  28. package/dist/commands/checkout/index.d.ts +7 -4
  29. package/dist/commands/checkout/index.js +9 -6
  30. package/dist/commands/compat/push-args.d.ts +2 -2
  31. package/dist/commands/index.js +0 -2
  32. package/dist/commands/publish/index.d.ts +9 -6
  33. package/dist/commands/publish/index.js +11 -8
  34. package/dist/commands/pull/index.js +1 -1
  35. package/dist/commands/push/index.js +1 -1
  36. package/dist/commands/utils.d.ts +15 -4
  37. package/dist/commands/utils.js +19 -6
  38. package/dist/config/catalog-include.d.ts +43 -0
  39. package/dist/config/catalog-include.js +203 -0
  40. package/dist/config/index.d.ts +2 -2
  41. package/dist/config/index.js +5 -5
  42. package/dist/config/loader.js +9 -1
  43. package/dist/config/runtime.d.ts +5 -6
  44. package/dist/config/runtime.js +154 -154
  45. package/dist/handlebars.d.ts +9 -0
  46. package/dist/handlebars.js +16 -0
  47. package/dist/index.d.ts +15 -4
  48. package/dist/index.js +29 -15
  49. package/dist/pattern.d.ts +7 -0
  50. package/dist/pattern.js +13 -0
  51. package/dist/react.d.ts +43 -0
  52. package/dist/react.js +11 -0
  53. package/dist/registry/asset-storage/define.d.ts +1 -1
  54. package/dist/registry/asset-storage/define.js +1 -1
  55. package/dist/registry/checkout/index.d.ts +9 -0
  56. package/dist/registry/checkout/index.js +335 -149
  57. package/dist/registry/db/schema.d.ts +34 -0
  58. package/dist/registry/db/schema.js +2 -0
  59. package/dist/registry/publish/index.d.ts +13 -2
  60. package/dist/registry/publish/index.js +109 -25
  61. package/dist/registry/publish/package.js +14 -1
  62. package/dist/registry/selection.d.ts +20 -0
  63. package/dist/registry/selection.js +37 -1
  64. package/dist/store/filesystem.d.ts +1 -1
  65. package/dist/store/filesystem.js +7 -7
  66. package/dist/store/registry.js +24 -3
  67. package/dist/transformers/plugins/csf-render.js +4 -2
  68. package/dist/transformers/plugins/ssr-render.js +14 -5
  69. package/dist/transformers/preview/component/api.d.ts +8 -0
  70. package/dist/transformers/preview/component/api.js +11 -15
  71. package/dist/transformers/preview/component/artifacts.d.ts +1 -1
  72. package/dist/transformers/preview/component/artifacts.js +11 -7
  73. package/dist/transformers/preview/component/builder.js +15 -18
  74. package/dist/transformers/preview/component/css.d.ts +2 -0
  75. package/dist/transformers/preview/component/css.js +8 -11
  76. package/dist/transformers/preview/component/html.js +18 -16
  77. package/dist/transformers/preview/component/javascript.js +2 -4
  78. package/dist/transformers/preview/pattern/builder.js +31 -18
  79. package/dist/transformers/preview/types.d.ts +15 -1
  80. package/dist/types/config.d.ts +29 -11
  81. package/docs/api.md +3 -0
  82. package/docs/cli.md +1 -1
  83. package/drizzle/0006_add_component_source_format.sql +3 -0
  84. package/drizzle/meta/0006_snapshot.json +1941 -0
  85. package/drizzle/meta/_journal.json +8 -1
  86. package/package.json +34 -1
  87. package/src/app/components/Component/Preview.tsx +36 -50
  88. package/src/app/components/Markdown/CodeHighlight.tsx +73 -70
  89. package/src/app/components/context/PreviewContext.tsx +5 -5
  90. package/src/app/components/util/index.ts +7 -15
  91. package/src/app/lib/mcp/shape.ts +6 -5
  92. package/src/app/lib/registry-api/transfer.ts +7 -5
  93. package/src/app-builder/build.ts +3 -6
  94. package/src/app-builder/config-diff/strategies/pattern.ts +1 -1
  95. package/src/app-builder/watchers/component.ts +3 -3
  96. package/src/app-builder/watchers/utils.ts +2 -3
  97. package/src/app-builder/watchers.ts +46 -50
  98. package/src/cache/build-cache.ts +11 -14
  99. package/src/catalog/README.md +92 -0
  100. package/src/catalog/define.ts +79 -0
  101. package/src/catalog/implementation.ts +145 -0
  102. package/src/catalog/index.ts +31 -0
  103. package/src/catalog/normalize.ts +143 -0
  104. package/src/catalog/previews.ts +111 -0
  105. package/src/catalog/renderers.ts +94 -0
  106. package/src/catalog/types.ts +74 -0
  107. package/src/cli/README.md +1 -1
  108. package/src/cli/make.ts +20 -72
  109. package/src/cli/scaffold.ts +25 -31
  110. package/src/commands/README.md +1 -1
  111. package/src/commands/checkout/index.ts +17 -7
  112. package/src/commands/compat/push-args.ts +2 -2
  113. package/src/commands/index.ts +0 -2
  114. package/src/commands/publish/index.ts +19 -9
  115. package/src/commands/pull/index.ts +2 -2
  116. package/src/commands/push/index.ts +2 -2
  117. package/src/commands/utils.ts +22 -7
  118. package/src/config/README.md +2 -1
  119. package/src/config/catalog-include.ts +227 -0
  120. package/src/config/index.ts +3 -3
  121. package/src/config/loader.ts +12 -0
  122. package/src/config/runtime.ts +186 -191
  123. package/src/handlebars.ts +18 -0
  124. package/src/index.ts +39 -26
  125. package/src/pattern.ts +14 -0
  126. package/src/react.ts +52 -0
  127. package/src/registry/asset-storage/define.ts +1 -1
  128. package/src/registry/checkout/index.ts +416 -152
  129. package/src/registry/db/schema.ts +2 -0
  130. package/src/registry/publish/index.ts +123 -26
  131. package/src/registry/publish/package.ts +14 -1
  132. package/src/registry/selection.ts +44 -0
  133. package/src/store/filesystem.ts +4 -4
  134. package/src/store/registry.ts +13 -2
  135. package/src/transformers/plugins/csf-render.ts +4 -2
  136. package/src/transformers/plugins/ssr-render.ts +15 -5
  137. package/src/transformers/preview/component/api.ts +13 -1
  138. package/src/transformers/preview/component/artifacts.ts +8 -4
  139. package/src/transformers/preview/component/builder.ts +17 -4
  140. package/src/transformers/preview/component/css.ts +6 -10
  141. package/src/transformers/preview/component/html.ts +30 -19
  142. package/src/transformers/preview/component/javascript.ts +1 -3
  143. package/src/transformers/preview/pattern/builder.ts +33 -20
  144. package/src/transformers/preview/types.ts +15 -1
  145. package/src/types/config.ts +31 -11
  146. package/templates/default/components/button/button.react.handoff.js.tpl +19 -23
  147. package/templates/default/components/button/button.react.handoff.ts.tpl +22 -22
  148. package/templates/default/handoff.config.js.tpl +5 -15
  149. package/templates/default/handoff.config.ts.tpl +5 -15
  150. package/templates/default/handoff.config.with-examples.js.tpl +6 -13
  151. package/templates/default/handoff.config.with-examples.ts.tpl +6 -13
  152. package/config/templates/component/template.json +0 -49
  153. package/config/templates/template.html +0 -7
  154. package/dist/commands/make/template.d.ts +0 -8
  155. package/dist/commands/make/template.js +0 -43
  156. package/dist/config/entries.d.ts +0 -47
  157. package/dist/config/entries.js +0 -162
  158. package/dist/config/normalizers/declaration.d.ts +0 -11
  159. package/dist/config/normalizers/declaration.js +0 -122
  160. package/dist/config/normalizers/pattern.d.ts +0 -8
  161. package/dist/config/normalizers/pattern.js +0 -41
  162. package/dist/declarations/helpers.d.ts +0 -6
  163. package/dist/declarations/helpers.js +0 -19
  164. package/dist/declarations/index.d.ts +0 -2
  165. package/dist/declarations/index.js +0 -9
  166. package/dist/declarations/types.d.ts +0 -60
  167. package/src/commands/make/template.ts +0 -46
  168. package/src/config/entries.ts +0 -175
  169. package/src/config/normalizers/declaration.ts +0 -179
  170. package/src/config/normalizers/pattern.ts +0 -59
  171. package/src/declarations/helpers.ts +0 -36
  172. package/src/declarations/index.ts +0 -19
  173. package/src/declarations/types.ts +0 -70
  174. /package/dist/{declarations → catalog}/types.js +0 -0
package/README.md CHANGED
@@ -66,11 +66,16 @@ my-handoff-project/
66
66
  └─ out/ # build output; gitignored
67
67
  ```
68
68
 
69
- ## Components and patterns
69
+ ## Catalog items
70
70
 
71
- An implementation and a `*.handoff.ts` declaration are required for a
72
- TypeScript React component. Stable identity, documentation metadata, source
73
- entries, and previews are supplied by the declaration.
71
+ Every documented UI entry is a catalog item. Each item declares either an implementation or a composition of other items.
72
+ An implementation names its renderer. `defineCatalogItem` comes from the module for that renderer.
73
+ The declaration supplies stable identity, documentation metadata, source entries, and previews.
74
+
75
+ ### React
76
+
77
+ Handoff finds the implementation file from its import, so you do not need to repeat the path.
78
+ Previews are named exports. `Preview<typeof item>` gives them the implementation's argument type.
74
79
 
75
80
  ```tsx
76
81
  // components/example/Component.tsx
@@ -83,61 +88,136 @@ export default function Component({ label }: ComponentProps) {
83
88
  }
84
89
  ```
85
90
 
86
- ```ts
87
- // components/example/component.handoff.ts
88
- import { defineReactComponent } from 'handoff-app';
91
+ ```tsx
92
+ // components/example/example.handoff.ts
93
+ import { defineCatalogItem, type Preview } from 'handoff-app/react';
89
94
  import Component from './Component';
90
95
 
91
- export default defineReactComponent(Component, {
96
+ const item = defineCatalogItem({
92
97
  id: 'component-id',
93
98
  name: 'Component name',
94
99
  description: 'Usage guidance for the component.',
95
100
  group: 'Group name',
96
- entries: {
97
- component: './Component.tsx',
98
- },
99
- previews: {
100
- default: {
101
- title: 'Default',
102
- args: { label: 'Example' },
103
- },
104
- },
101
+ implementation: Component,
102
+ });
103
+
104
+ export default item;
105
+
106
+ type ComponentPreview = Preview<typeof item>;
107
+
108
+ export const Default = {
109
+ args: { label: 'Example' },
110
+ } satisfies ComponentPreview;
111
+ ```
112
+
113
+ The export name is the preview name. Use `name` to set a different display title.
114
+
115
+ ### Handlebars
116
+
117
+ The implementation is the path to the template. Arguments are `Record<string, unknown>` by default;
118
+ an explicit argument type is supplied through `defineCatalogItem<BadgeArgs>`.
119
+
120
+ ```ts
121
+ // components/badge/badge.handoff.ts
122
+ import { defineCatalogItem, type Preview } from 'handoff-app/handlebars';
123
+
124
+ const item = defineCatalogItem({
125
+ id: 'badge',
126
+ name: 'Badge',
127
+ implementation: './Badge.hbs',
105
128
  });
129
+
130
+ export default item;
131
+
132
+ type BadgePreview = Preview<typeof item>;
133
+
134
+ export const Primary = {
135
+ args: { variant: 'primary', children: 'New' },
136
+ } satisfies BadgePreview;
106
137
  ```
107
138
 
108
- Components are referenced by stable ID in patterns. A named preview can be
109
- selected and its arguments can be overridden by each reference. Additional
110
- references can be added as required by the composition.
139
+ ### CSF
140
+
141
+ CSF is a source format. A story file uses the renderer from the module that declares it. A React item references an existing CSF file with `fromCSF`.
142
+ The CSF file stays unchanged and defines its previews through named exports. Each story's `args` merge with and override `meta.args`.
143
+ Handoff preserves story names, `argTypes`, and `render` functions.
144
+ It finds the React component through `meta.component`, then documents and publishes it with the story file.
111
145
 
112
146
  ```ts
113
- // patterns/example/pattern.handoff.ts
114
- import { definePattern } from 'handoff-app';
147
+ // components/card/card.handoff.ts
148
+ import { defineCatalogItem, fromCSF } from 'handoff-app/react';
115
149
 
116
- export default definePattern({
150
+ export default defineCatalogItem({
151
+ id: 'card',
152
+ name: 'Card',
153
+ implementation: fromCSF('./Card.stories.tsx'),
154
+ });
155
+ ```
156
+
157
+ `Meta` and `StoryObj` are exported from `handoff-app/react`, so a story file type-checks without a
158
+ Storybook dependency. A project that already has Storybook keeps using its own types.
159
+
160
+ ```tsx
161
+ // components/card/Card.stories.tsx
162
+ import { type Meta, type StoryObj } from 'handoff-app/react';
163
+ import Card from './Card';
164
+
165
+ const meta = { component: Card } satisfies Meta<typeof Card>;
166
+ export default meta;
167
+
168
+ export const Primary: StoryObj<typeof meta> = {
169
+ args: { title: 'Example' },
170
+ };
171
+ ```
172
+
173
+ A CSF preview is rendered on the server and is not hydrated. A direct React implementation is
174
+ hydrated in the browser.
175
+
176
+ ### Compositions
177
+
178
+ A composition references other items by stable id. A named preview can be selected and its arguments
179
+ can be overridden by each reference. A composition needs no renderer, so it is declared with
180
+ `defineCatalogItem` from `handoff-app/pattern`.
181
+
182
+ ```ts
183
+ // patterns/example/example.handoff.ts
184
+ import { defineCatalogItem } from 'handoff-app/pattern';
185
+
186
+ export default defineCatalogItem({
117
187
  id: 'pattern-id',
118
188
  name: 'Pattern name',
119
189
  description: 'Purpose and usage of the composition.',
120
190
  group: 'Group name',
121
- components: [
122
- {
123
- id: 'component-id',
124
- preview: 'default',
125
- args: { label: 'Pattern example' },
126
- },
191
+ composition: [
192
+ { ref: 'component-id', preview: 'Default' },
193
+ { ref: 'badge', args: { children: 'Send' } },
127
194
  ],
128
195
  });
129
196
  ```
130
197
 
131
- Component and pattern directories are registered in the project config:
198
+ An item declares `implementation` or `composition`, never both. Terms such as atom, element, and
199
+ block stay optional classification metadata on `type` and `categories`.
200
+
201
+ The `id` addresses the item everywhere: its artifacts, its documentation URL, and its publish and
202
+ checkout commands. Components and patterns share one namespace, so each id must be unique across
203
+ both.
204
+
205
+ If two declarations claim one id, Handoff keeps the first and skips the second. The warning names
206
+ both files. `publish` refuses to run until each id is unique.
207
+
208
+
209
+ ### Registration
210
+
211
+ Catalog directories are registered in one place. A path is either an item directory or a collection
212
+ directory whose subdirectories are each treated as an item.
132
213
 
133
214
  ```ts
134
215
  // handoff.config.ts
135
216
  import { defineConfig } from 'handoff-app';
136
217
 
137
218
  export default defineConfig({
138
- entries: {
139
- components: ['components/example'],
140
- patterns: ['patterns'],
219
+ catalog: {
220
+ include: ['components', 'patterns'],
141
221
  },
142
222
  runtime: {
143
223
  workspace: {
@@ -147,6 +227,11 @@ export default defineConfig({
147
227
  });
148
228
  ```
149
229
 
230
+ ### Upgrade from version 1.x.x to version 2.x.x
231
+
232
+ See the [version 1.x.x to version 2.x.x migration guide](UPGRADE.md#version-1xx-to-version-2xx)
233
+ for breaking changes, migration steps, and upgrade verification.
234
+
150
235
  Custom documentation pages are Markdown files under `pages/`. Their relative
151
236
  paths become their routes and registry IDs.
152
237
 
@@ -340,20 +425,23 @@ npm run publish -- all
340
425
  A single kind is published on its own:
341
426
 
342
427
  ```bash
343
- npm run publish -- components
344
- npm run publish -- patterns
428
+ npm run publish -- catalog
345
429
  npm run publish -- pages
346
430
  npm run publish -- tokens
347
431
  npm run publish -- assets
348
432
  ```
349
433
 
434
+ `catalog` covers every catalog item. An item is stored as a component or as a
435
+ pattern, and its declaration decides which. The command names items and never a
436
+ kind.
437
+
350
438
  Publishing tokens or assets runs the Figma data pipeline before upload, so the
351
439
  documented Figma credentials must be available.
352
440
 
353
441
  One or more IDs can be appended to narrow a publish to those entities:
354
442
 
355
443
  ```bash
356
- npm run publish -- components component-id another-id
444
+ npm run publish -- catalog item-id another-id
357
445
  ```
358
446
 
359
447
  `--dry-run` reports what would be uploaded and contacts no registry at all, so
@@ -363,16 +451,16 @@ the existing output, and the two combine to leave the workspace untouched:
363
451
 
364
452
  ```bash
365
453
  npm run publish -- all --dry-run
366
- npm run publish -- components --no-build
454
+ npm run publish -- catalog --no-build
367
455
  ```
368
456
 
369
457
  `checkout` takes the same `all`, multi-ID, and `--dry-run` forms. A dry-run
370
458
  checkout reads from the registry, lists the files it would create or overwrite,
371
459
  and writes nothing.
372
460
 
373
- After the registry is reloaded, the published components, patterns, and
374
- foundations should be visible. Published database records are read by registry
375
- pages; the local workspace is never read directly.
461
+ After the registry is reloaded, the published catalog items and foundations are
462
+ visible. Published database records are read by registry pages; the local
463
+ workspace is never read directly.
376
464
 
377
465
  For CI, a registry connection and user-issued token are configured:
378
466
 
@@ -473,7 +561,7 @@ both.
473
561
  | `npm run build -- [--target static\|registry]` | The static site or standalone registry bundle is built |
474
562
  | `npm run db:migrate` | Registry database migrations are applied |
475
563
  | `npm run validate` | Configured components are validated |
476
- | `npm run publish -- <kind\|all> [id...]` | Components, patterns, pages, tokens, or assets are published |
564
+ | `npm run publish -- <kind\|all> [id...]` | Catalog items, pages, tokens, or assets are published |
477
565
  | `npm run checkout -- <kind\|all> [id...]` | Published content is pulled into a workspace |
478
566
  | `npm run login -- --url <url>` | The CLI is authorized through the registry device flow |
479
567
  | `npm run logout -- [--url <url>]` | A saved CLI credential is revoked and removed |
package/UPGRADE.md ADDED
@@ -0,0 +1,198 @@
1
+ # Upgrade Handoff
2
+
3
+ This document contains migration steps for each major version upgrade.
4
+
5
+ ## Version 1.x.x to version 2.x.x
6
+
7
+ This guide describes how to upgrade a Handoff project from version 1.x.x to version 2.x.x.
8
+ Each feature section identifies the affected projects and the required changes.
9
+
10
+ ### Before you start
11
+
12
+ 1. Verify that the project builds with its current Handoff version.
13
+ 2. Save the project files, package version, and lockfile in version control.
14
+ 3. Install the version 2.x.x package that you plan to use.
15
+ 4. Apply the migration sections that affect your project.
16
+
17
+ ### Breaking changes
18
+
19
+ | Change | Affected projects |
20
+ | --- | --- |
21
+ | [Catalog items](#catalog-items) | Projects with component or pattern declarations |
22
+
23
+ <!-- Add future breaking changes as feature sections before "Verify the upgrade". Add each section to this table. -->
24
+
25
+ ### Catalog items
26
+
27
+ #### Affected projects
28
+
29
+ This change affects projects that use the old component or pattern registration API.
30
+ It also affects projects that use the deprecated API from an earlier version 2.x.x prerelease.
31
+ Version 2.x.x requires `defineCatalogItem` for implementations and compositions.
32
+
33
+ #### Register catalog directories
34
+
35
+ Move component and pattern directories from `entries` to `catalog.include`.
36
+
37
+ Version 1.x.x:
38
+
39
+ ```js
40
+ entries: { components: ['components'], patterns: ['patterns'], scss: 'styles/main.scss' }
41
+ ```
42
+
43
+ Version 2.x.x:
44
+
45
+ ```js
46
+ catalog: { include: ['components', 'patterns'] },
47
+ entries: { scss: 'styles/main.scss' }
48
+ ```
49
+
50
+ Each path can name one item directory or a collection of item directories. Global `entries.js` and `entries.scss` still work.
51
+
52
+ #### Convert declaration files
53
+
54
+ Use `<name>.handoff.ts`, `<name>.handoff.js`, or `<name>.handoff.cjs`.
55
+ Replace legacy filenames such as `button.json` or `button.js`.
56
+
57
+ Version 2.x.x does not support JSON item declarations, including `.handoff.json`.
58
+ JSON project config (`handoff.config.json`) remains supported.
59
+
60
+ If `runtime.workspace.declarationFormat` is `json`, change it to `ts`, `js`, or `cjs`. The default remains `ts`.
61
+
62
+ #### Replace declaration helpers
63
+
64
+ | Removed API | Replacement |
65
+ | --- | --- |
66
+ | `defineReactComponent(Component, config)` | `defineCatalogItem({ ...config, implementation: Component })` from `handoff-app/react` |
67
+ | `defineHandlebarsComponent(config)` | `defineCatalogItem({ ...config, implementation: './Button.hbs' })` from `handoff-app/handlebars` |
68
+ | `defineCsfComponent(config)` | `defineCatalogItem({ ...config, implementation: fromCSF('./Button.stories.tsx') })` from `handoff-app/react` |
69
+ | `defineComponent(config)` | `defineCatalogItem({ ...config, implementation: './Button.tsx' })` from the module for its renderer |
70
+ | `definePattern({ components })` | `defineCatalogItem({ composition })` from `handoff-app/pattern` |
71
+
72
+ Remove the old `renderer`, `entries.component`, `entries.template`, and `entries.story` fields.
73
+ The implementation supplies the renderer and source file.
74
+ Keep the supporting `entries.js`, `entries.scss`, `entries.schema`, and `entries.templates` paths that your item uses.
75
+ Rename the metadata fields:
76
+
77
+ | Old field | Version 2.x.x field |
78
+ | --- | --- |
79
+ | `title` | `name` |
80
+ | `should_do` | `shouldDo` |
81
+ | `should_not_do` | `shouldNotDo` |
82
+
83
+ ##### React
84
+
85
+ ```ts
86
+ import { defineCatalogItem, type Preview } from 'handoff-app/react';
87
+ import Button from './Button';
88
+
89
+ const item = defineCatalogItem({
90
+ name: 'Button',
91
+ implementation: Button,
92
+ entries: { scss: './button.scss' },
93
+ });
94
+ export default item;
95
+ export const Primary = { args: { children: 'Save' } } satisfies Preview<typeof item>;
96
+ ```
97
+
98
+ Imported components retain their prop types. A file path also works: `implementation: './Button.tsx'`.
99
+ Keep implementation files inside the item directory to publish them.
100
+
101
+ ##### Handlebars and CommonJS
102
+
103
+ ```js
104
+ const { defineCatalogItem } = require('handoff-app/handlebars');
105
+
106
+ exports.default = defineCatalogItem({
107
+ name: 'Badge',
108
+ implementation: './Badge.hbs',
109
+ });
110
+ exports.Primary = { args: { children: 'New' } };
111
+ ```
112
+
113
+ Use this form in `.handoff.js` or `.handoff.cjs`. JavaScript declarations can also use `import` and `export`.
114
+
115
+ ##### CSF
116
+
117
+ ```ts
118
+ import { defineCatalogItem, fromCSF } from 'handoff-app/react';
119
+
120
+ export default defineCatalogItem({
121
+ name: 'Button',
122
+ implementation: fromCSF('./Button.stories.tsx'),
123
+ });
124
+ ```
125
+
126
+ CSF is a React source format. The story file supplies the previews.
127
+ The filename does not select CSF automatically.
128
+
129
+ ##### Compositions
130
+
131
+ ```ts
132
+ import { defineCatalogItem } from 'handoff-app/pattern';
133
+
134
+ export default defineCatalogItem({
135
+ name: 'Toolbar',
136
+ composition: [{ ref: 'button', preview: 'Primary', args: { children: 'Save' } }],
137
+ });
138
+ ```
139
+
140
+ Rename `components` to `composition`.
141
+ Rename each member's `id` to `ref`.
142
+ Keep item IDs stable.
143
+
144
+ #### Export previews
145
+
146
+ Remove the inline `previews` map.
147
+ Export each preview beside the default catalog item.
148
+ Rename preview `title` to `name`.
149
+ Rename `values` to `args`:
150
+
151
+ ```ts
152
+ export const Primary = { name: 'Primary action', args: { children: 'Save' } };
153
+ ```
154
+
155
+ If a preview key is a valid export name, preserve its spelling and capitalization.
156
+ If a key is not a valid export name, rename it.
157
+ Update composition references to each renamed preview.
158
+
159
+ Previews appear in source export order.
160
+
161
+ For CommonJS declarations, use `exports.default` for the item.
162
+ Use a named export such as `exports.Primary` for each preview.
163
+
164
+ #### Replace declaration types
165
+
166
+ Replace imports of the removed types:
167
+
168
+ | Removed type | Replacement |
169
+ | --- | --- |
170
+ | `Component` | `CatalogItem` |
171
+ | `DeclarationPreview` | `CatalogPreview` or `Preview<typeof item>` |
172
+ | `PatternComponentRef` | `CompositionRef` |
173
+ | `ReactDeclarationConfig`, `HandlebarsDeclarationConfig`, `CsfDeclarationConfig` | The catalog helper's input type |
174
+ | `GenericDeclarationConfig`, `GenericPatternDeclarationConfig` | The catalog helper's input type, or `CatalogItemMeta` for shared metadata |
175
+
176
+ `RendererKind` comes from the catalog API and contains `react` and `handlebars`.
177
+ CSF uses `renderer: 'react'` with `format: 'csf'` inside `implementation`.
178
+
179
+ #### Registry projects
180
+
181
+ Checkout writes only catalog declarations.
182
+ Registry records require explicit renderer and source-format metadata where applicable.
183
+
184
+ ### Verify the upgrade
185
+
186
+ After you apply the required migrations, verify the project:
187
+
188
+ 1. Build the project with version 2.x.x.
189
+ 2. Verify that all expected catalog items appear in the documentation.
190
+ 3. Verify the preview order, arguments, and composition references.
191
+ 4. Start the workspace with file watchers.
192
+ 5. Edit a declaration to verify that its previews update.
193
+
194
+ If the project uses a registry, verify the published items:
195
+
196
+ 1. Publish the migrated items.
197
+ 2. Verify that checkout produces valid declarations.
198
+ 3. Rebuild the checked-out items.
@@ -54,13 +54,10 @@ const config = {
54
54
  ports: { app: 3000, websocket: 3001 },
55
55
  },
56
56
  // These are the entry points used to compile the target application
57
+ catalog: { include: ['./components'] },
57
58
  entries: {
58
59
  scss: './sass/main.scss',
59
60
  js: './js/main.js',
60
- components: ['./components'],
61
- // Uncomment to enable pattern documentation pages.
62
- // Patterns compose component previews into single-page views.
63
- // patterns: ['./patterns'],
64
61
  },
65
62
  // These are the asset zip file download links
66
63
  assetsZipLinks: {
@@ -34,11 +34,10 @@ export default defineConfig({
34
34
  },
35
35
  ports: { app: 3000, websocket: 3001 },
36
36
  },
37
+ catalog: { include: ['./components'] },
37
38
  entries: {
38
39
  scss: './sass/main.scss',
39
40
  js: './js/main.js',
40
- components: ['./components'],
41
- // patterns: ['./patterns'],
42
41
  },
43
42
  assetsZipLinks: {
44
43
  icons: null,
@@ -695,14 +695,12 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
695
695
  pendingHandlers: new Map(),
696
696
  runtimeComponentsWatcher: null,
697
697
  runtimeConfigurationWatcher: null,
698
- componentDirectoriesWatcher: null,
699
- patternDirectoriesWatcher: null,
698
+ catalogDirectoriesWatcher: null,
700
699
  };
701
700
  (0, watchers_1.watchPublicDirectory)(handoff, wss, state, chokidarConfig);
702
701
  (0, watchers_1.watchRuntimeComponents)(handoff, state, (0, watchers_1.getRuntimeComponentsPathsToWatch)(handoff));
703
702
  (0, watchers_1.watchRuntimeConfiguration)(handoff, state);
704
- (0, watchers_1.watchComponentDirectories)(handoff, state, chokidarConfig);
705
- (0, watchers_1.watchPatternDirectories)(handoff, state, chokidarConfig);
703
+ (0, watchers_1.watchCatalogDirectories)(handoff, state, chokidarConfig);
706
704
  (0, watchers_1.watchGlobalEntries)(handoff, state, chokidarConfig);
707
705
  (0, watchers_1.watchPages)(handoff, chokidarConfig);
708
706
  });
@@ -43,12 +43,12 @@ const snapshot_1 = require("../snapshot");
43
43
  * components. Outer key is component id, inner key is synthetic preview key.
44
44
  */
45
45
  const snapshotPatternSynthetics = (handoff, patternId) => {
46
- var _a, _b, _c, _d;
47
- const components = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.components) !== null && _c !== void 0 ? _c : {};
46
+ var _a, _b, _c;
47
+ const components = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.components) !== null && _b !== void 0 ? _b : {};
48
48
  const prefix = `__pattern_${patternId}_`;
49
49
  const result = new Map();
50
50
  for (const [componentId, component] of Object.entries(components)) {
51
- const previews = (_d = component.internalPatternPreviews) !== null && _d !== void 0 ? _d : {};
51
+ const previews = (_c = component.internalPatternPreviews) !== null && _c !== void 0 ? _c : {};
52
52
  const entries = new Map();
53
53
  for (const [key, preview] of Object.entries(previews)) {
54
54
  if (key.startsWith(prefix)) {
@@ -23,11 +23,11 @@ const mapEntryTypeToSegment = (type) => {
23
23
  };
24
24
  exports.mapEntryTypeToSegment = mapEntryTypeToSegment;
25
25
  const resolveComponentIdForChangedFile = (handoff, changedFilePath) => {
26
- var _a, _b, _c, _d;
26
+ var _a, _b, _c;
27
27
  const normalizedChangedPath = (0, path_2.normalizePathForCompare)(changedFilePath);
28
- const runtimeComponents = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.components) !== null && _c !== void 0 ? _c : {};
28
+ const runtimeComponents = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.components) !== null && _b !== void 0 ? _b : {};
29
29
  for (const [componentId, componentDef] of Object.entries(runtimeComponents)) {
30
- const entries = (_d = componentDef.entries) !== null && _d !== void 0 ? _d : {};
30
+ const entries = (_c = componentDef.entries) !== null && _c !== void 0 ? _c : {};
31
31
  for (const entryPath of Object.values(entries)) {
32
32
  if (!entryPath)
33
33
  continue;
@@ -49,8 +49,8 @@ exports.resolveComponentIdForChangedFile = resolveComponentIdForChangedFile;
49
49
  const getRuntimeComponentsPathsToWatch = (handoff) => {
50
50
  var _a, _b, _c;
51
51
  const result = new Map();
52
- for (const runtimeComponentId of Object.keys((_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries.components) !== null && _b !== void 0 ? _b : {})) {
53
- const runtimeComponent = handoff.runtimeConfig.entries.components[runtimeComponentId];
52
+ for (const runtimeComponentId of Object.keys((_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.components) !== null && _b !== void 0 ? _b : {})) {
53
+ const runtimeComponent = handoff.runtimeConfig.entities.components[runtimeComponentId];
54
54
  for (const [runtimeComponentEntryType, runtimeComponentEntryPath] of Object.entries((_c = runtimeComponent.entries) !== null && _c !== void 0 ? _c : {})) {
55
55
  const normalizedComponentEntryPath = runtimeComponentEntryPath;
56
56
  if (fs_extra_1.default.existsSync(normalizedComponentEntryPath)) {
@@ -4,9 +4,8 @@ export interface WatcherState {
4
4
  pendingHandlers: Map<string, () => Promise<void>>;
5
5
  runtimeComponentsWatcher: chokidar.FSWatcher | null;
6
6
  runtimeConfigurationWatcher: chokidar.FSWatcher | null;
7
- componentDirectoriesWatcher: chokidar.FSWatcher | null;
8
- /** Watches entries.patterns parent directories for newly created pattern subdirectories. */
9
- patternDirectoriesWatcher: chokidar.FSWatcher | null;
7
+ /** Watches every registered catalog directory for newly created item subdirectories. */
8
+ catalogDirectoriesWatcher: chokidar.FSWatcher | null;
10
9
  }
11
10
  /**
12
11
  * Schedules a handler for execution with mutual exclusion and per-source
@@ -36,19 +36,12 @@ export declare const watchRuntimeComponents: (handoff: Handoff, state: WatcherSt
36
36
  */
37
37
  export declare const watchRuntimeConfiguration: (handoff: Handoff, state: WatcherState) => void;
38
38
  /**
39
- * Watches the parent component directories from config.entries.components for
40
- * new components being added. When a new config file appears in a new
41
- * subdirectory, reloads the runtime config, restarts the component and
42
- * configuration watchers, then builds the new component and any patterns that
43
- * reference it.
44
- */
45
- export declare const watchComponentDirectories: (handoff: Handoff, state: WatcherState, chokidarConfig: chokidar.WatchOptions) => void;
46
- /**
47
- * Watches the parent pattern directories from config.entries.patterns for new
48
- * patterns being added. The rebuild sequence intentionally differs from
49
- * watchComponentDirectories because the dependency arrow is reversed: a new
50
- * pattern references existing components, so we rebuild only the preview
51
- * segment of those components (to produce the __pattern_* HTML files that
52
- * injectPatternPreviews registered) before composing the pattern itself.
39
+ * Watches `catalog.include` directories for a new item. A single watcher covers implementations
40
+ * and compositions; the declaration determines which pipeline builds the item.
41
+ *
42
+ * The rebuild differs by kind, and the dependency arrow is reversed between them. A new item with
43
+ * an implementation builds itself, then the compositions that reference it. A new composition
44
+ * rebuilds the preview segment of the items it references, so the synthetic `__pattern_*` HTML
45
+ * exists, and only then composes itself.
53
46
  */
54
- export declare const watchPatternDirectories: (handoff: Handoff, state: WatcherState, chokidarConfig: chokidar.WatchOptions) => void;
47
+ export declare const watchCatalogDirectories: (handoff: Handoff, state: WatcherState, chokidarConfig: chokidar.WatchOptions) => void;