@jay-framework/jay-stack-cli 0.11.0 → 0.13.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.
@@ -0,0 +1,510 @@
1
+ # Jay Framework Vendors
2
+
3
+ This directory contains vendor implementations that convert design tool documents (Figma, Sketch, Adobe XD, etc.) into Jay HTML format.
4
+
5
+ ## Overview
6
+
7
+ Vendors are **contributed by the community** as part of the Jay Framework open-source project. Each vendor implements a simple interface to transform their native document format into Jay HTML.
8
+
9
+ ## Directory Structure
10
+
11
+ ```
12
+ vendors/
13
+ ├── types.ts # Vendor interface definition
14
+ ├── registry.ts # Vendor registration system
15
+ ├── index.ts # Public API
16
+ └── figma/ # Figma vendor implementation
17
+ └── index.ts
18
+ ```
19
+
20
+ ## How It Works
21
+
22
+ 1. **Vendor Registration**: Vendors are statically imported in `registry.ts` and registered at server startup
23
+ 2. **Export Flow**: When an editor plugin calls `export()`, the server:
24
+ - Saves the vendor document as `page.<vendorId>.json`
25
+ - Looks up the vendor by ID
26
+ - Calls `vendor.convertToJayHtml()` to generate Jay HTML
27
+ - Saves the result to `page.jay-html`
28
+
29
+ ## Vendor Interface
30
+
31
+ Every vendor must implement this simple interface:
32
+
33
+ ```typescript
34
+ interface Vendor<TVendorDoc = any> {
35
+ vendorId: string;
36
+
37
+ convertToJayHtml(vendorDoc: TVendorDoc, pageUrl: string): Promise<string>;
38
+ }
39
+ ```
40
+
41
+ ### Parameters
42
+
43
+ - **`vendorDoc`**: The vendor's native document format (e.g., Figma SectionNode)
44
+ - **`pageUrl`**: The page route (e.g., `/home`, `/products`)
45
+
46
+ ### Returns
47
+
48
+ A Promise that resolves to a **Jay HTML string**.
49
+
50
+ ## Contributing a New Vendor
51
+
52
+ ### Overview
53
+
54
+ When adding a new vendor, you need to:
55
+
56
+ 1. Define the vendor document type in editor-protocol (single source of truth)
57
+ 2. Implement the vendor converter (imports the type)
58
+ 3. Register the vendor
59
+
60
+ ### Step 1: Define Vendor Document Type
61
+
62
+ **Important**: The vendor document type is defined **ONCE** in the editor-protocol package. This is the single source of truth that both plugins and vendors import from.
63
+
64
+ #### A. Define in Editor Protocol
65
+
66
+ Edit `/packages/jay-stack/editor-protocol/lib/vendor-documents.ts`:
67
+
68
+ ```typescript
69
+ /**
70
+ * YourVendor Document Type
71
+ *
72
+ * The structure that YourVendor plugins must send when exporting.
73
+ * This is the single source of truth - both plugins and the vendor
74
+ * implementation import from here.
75
+ */
76
+ export type YourVendorDocument = {
77
+ // Your vendor's serializable document structure
78
+ id: string;
79
+ name: string;
80
+ nodes: YourVendorNode[];
81
+ };
82
+
83
+ export type YourVendorNode = {
84
+ id: string;
85
+ type: string;
86
+ // ... node properties
87
+ };
88
+ ```
89
+
90
+ This allows **both plugin developers AND the vendor implementation** to import:
91
+
92
+ ```typescript
93
+ import { YourVendorDocument } from '@jay-framework/editor-protocol';
94
+ ```
95
+
96
+ ### Step 2: Implement the Vendor
97
+
98
+ Create `your-vendor-id/index.ts` and **import** the type:
99
+
100
+ ```typescript
101
+ import { Vendor } from '../types';
102
+ import type { YourVendorDocument } from '@jay-framework/editor-protocol';
103
+
104
+ /**
105
+ * YourVendor Implementation
106
+ *
107
+ * Imports YourVendorDocument from @jay-framework/editor-protocol,
108
+ * which is the single source of truth.
109
+ */
110
+
111
+ export const yourVendorVendor: Vendor<YourVendorDocument> = {
112
+ vendorId: 'your-vendor-id',
113
+
114
+ async convertToJayHtml(vendorDoc: YourVendorDocument, pageUrl: string): Promise<string> {
115
+ // Parse vendor document
116
+ const elements = parseVendorDocument(vendorDoc);
117
+
118
+ // Generate Jay HTML
119
+ const jayHtml = generateJayHtml(elements);
120
+
121
+ return jayHtml;
122
+ },
123
+ };
124
+
125
+ function parseVendorDocument(doc: YourVendorDocument) {
126
+ // Your parsing logic
127
+ return [];
128
+ }
129
+
130
+ function generateJayHtml(elements: any[]): string {
131
+ // Your Jay HTML generation logic
132
+ return '<section>...</section>';
133
+ }
134
+ ```
135
+
136
+ **Key Points:**
137
+
138
+ - ✅ **Import the type** from `@jay-framework/editor-protocol`
139
+ - ✅ **No duplicate definitions** - there's only one source of truth
140
+ - ✅ **Use `import type`** for type-only imports (better for tree-shaking)
141
+ vendorId: 'your-vendor-id',
142
+ async convertToJayHtml(
143
+ vendorDoc: YourVendorDocument,
144
+ pageUrl: string,
145
+ ): Promise<string> {
146
+ // Parse vendor document
147
+ const elements = parseVendorDocument(vendorDoc);
148
+
149
+ // Generate Jay HTML
150
+ const jayHtml = generateJayHtml(elements);
151
+
152
+ return jayHtml;
153
+ },
154
+
155
+ };
156
+
157
+ function parseVendorDocument(doc: YourVendorDocument) {
158
+ // Your parsing logic
159
+ return [];
160
+ }
161
+
162
+ function generateJayHtml(elements: any[]): string {
163
+ // Your Jay HTML generation logic
164
+ return '<section>...</section>';
165
+ }
166
+
167
+ ````
168
+
169
+ ### Step 3: Create Vendor README
170
+
171
+ Create `your-vendor-id/README.md`:
172
+
173
+ ```markdown
174
+ # YourVendor Vendor
175
+
176
+ Converts YourVendor documents to Jay HTML.
177
+
178
+ ## For Plugin Developers
179
+
180
+ Import the document type from editor protocol:
181
+
182
+ \`\`\`typescript
183
+ import { YourVendorDocument } from '@jay-framework/editor-protocol';
184
+
185
+ const vendorDoc: YourVendorDocument = {
186
+ id: 'doc-123',
187
+ name: 'My Page',
188
+ nodes: [...]
189
+ };
190
+
191
+ await editorProtocol.export({
192
+ vendorId: 'your-vendor-id',
193
+ pageUrl: '/home',
194
+ vendorDoc
195
+ });
196
+ \`\`\`
197
+
198
+ ## Document Structure
199
+
200
+ See the type definition in `@jay-framework/editor-protocol/lib/vendor-documents.ts`.
201
+ ````
202
+
203
+ ### Step 4: Register Your Vendor
204
+
205
+ Edit `registry.ts`:
206
+
207
+ ```typescript
208
+ // Add import
209
+ import { yourVendorVendor } from './your-vendor-id';
210
+
211
+ // Add to registry Map
212
+ const vendorRegistry = new Map<string, Vendor>([
213
+ [figmaVendor.vendorId, figmaVendor],
214
+ [yourVendorVendor.vendorId, yourVendorVendor], // Add your vendor here
215
+ ]);
216
+ ```
217
+
218
+ ### Step 5: Test Your Vendor
219
+
220
+ 1. Build the package:
221
+
222
+ ```bash
223
+ cd /Users/noamsi/projects/jay/packages/jay-stack/stack-cli
224
+ npm run build
225
+ ```
226
+
227
+ 2. Start a dev server:
228
+
229
+ ```bash
230
+ jay dev
231
+ ```
232
+
233
+ 3. Look for your vendor in the startup logs:
234
+
235
+ ```
236
+ 📦 Initializing vendors...
237
+ ✅ Registered vendor: figma
238
+ ✅ Registered vendor: your-vendor-id
239
+ ```
240
+
241
+ 4. Test export from your editor plugin
242
+
243
+ ## Example: Figma Vendor
244
+
245
+ See `figma/index.ts` for a complete example implementation.
246
+
247
+ ```typescript
248
+ export const figmaVendor: Vendor<FigmaDocument> = {
249
+ vendorId: 'figma',
250
+
251
+ async convertToJayHtml(vendorDoc, pageUrl) {
252
+ console.log(`Converting Figma document for page: ${pageUrl}`);
253
+
254
+ // Parse Figma document
255
+ const elements = parseFigmaNodes(vendorDoc);
256
+
257
+ // Generate Jay HTML
258
+ const jayHtml = generateJayHtmlFromElements(elements);
259
+
260
+ return jayHtml;
261
+ },
262
+ };
263
+ ```
264
+
265
+ ## Best Practices
266
+
267
+ ### 1. Single Source of Truth
268
+
269
+ **The vendor document type is defined ONCE** in `editor-protocol/lib/vendor-documents.ts`. Both plugins and vendors import from there:
270
+
271
+ ```typescript
272
+ // ✅ CORRECT - Import from editor-protocol (single source of truth)
273
+ import type { MyVendorDocument } from '@jay-framework/editor-protocol';
274
+
275
+ export const myVendor: Vendor<MyVendorDocument> = {
276
+ // Implementation uses imported type
277
+ };
278
+
279
+ // ❌ WRONG - Don't redefine the type locally
280
+ export type MyVendorDocument = {
281
+ /* ... */
282
+ }; // DON'T DO THIS
283
+ ```
284
+
285
+ **Why?**
286
+
287
+ - **No duplication** - type is defined once
288
+ - **Always in sync** - impossible to have mismatches
289
+ - **Single update point** - change in one place updates everywhere
290
+ - **Follows DRY principle** - Don't Repeat Yourself
291
+
292
+ ### 2. Use `type` for Vendor Documents, Not `interface`
293
+
294
+ In the editor-protocol, use `type` instead of `interface` for vendor documents:
295
+
296
+ ```typescript
297
+ // ✅ CORRECT - Use 'type' for serializable data
298
+ export type MyVendorDocument = {
299
+ name: string;
300
+ nodes: MyVendorNode[];
301
+ };
302
+
303
+ // ❌ WRONG - Don't use 'interface' for data sent over network
304
+ interface MyVendorDocument {
305
+ name: string;
306
+ nodes: MyVendorNode[];
307
+ }
308
+ ```
309
+
310
+ **Why?**
311
+
312
+ - `type` is more explicit about being a data structure
313
+ - `type` makes it clear this is serializable data, not an API contract
314
+ - Follows TypeScript best practices for data transfer objects (DTOs)
315
+
316
+ ### 3. Use `import type` for Type-Only Imports
317
+
318
+ When importing vendor document types, use `import type`:
319
+
320
+ ```typescript
321
+ // ✅ CORRECT - Type-only import (better for tree-shaking)
322
+ import type { MyVendorDocument } from '@jay-framework/editor-protocol';
323
+
324
+ // ⚠️ OK but not optimal - Regular import
325
+ import { MyVendorDocument } from '@jay-framework/editor-protocol';
326
+ ```
327
+
328
+ ### 4. Type Safety
329
+
330
+ } catch (error) {
331
+ throw new Error(`Failed to generate Jay HTML: ${error.message}`);
332
+ }
333
+
334
+ }
335
+
336
+ ````
337
+
338
+ ### 3. Logging
339
+
340
+ Add console logs to help with debugging:
341
+
342
+ ```typescript
343
+ async convertToJayHtml(vendorDoc, pageUrl) {
344
+ console.log(`Converting ${this.vendorId} document for ${pageUrl}`);
345
+ console.log(`Document has ${vendorDoc.nodes.length} nodes`);
346
+
347
+ const jayHtml = generateJayHtml(vendorDoc);
348
+
349
+ console.log(`Generated ${jayHtml.length} characters of Jay HTML`);
350
+ return jayHtml;
351
+ }
352
+ ````
353
+
354
+ ### 4. Semantic HTML
355
+
356
+ Generate proper semantic HTML:
357
+
358
+ ```typescript
359
+ function generateJayHtml(doc: MyVendorDoc): string {
360
+ // Use semantic tags
361
+ let html = '<section>\n';
362
+
363
+ for (const node of doc.nodes) {
364
+ if (node.type === 'heading') {
365
+ html += ` <h1>${node.text}</h1>\n`;
366
+ } else if (node.type === 'paragraph') {
367
+ html += ` <p>${node.text}</p>\n`;
368
+ }
369
+ }
370
+
371
+ html += '</section>';
372
+ return html;
373
+ }
374
+ ```
375
+
376
+ ### 5. Handle Edge Cases
377
+
378
+ Account for various document structures:
379
+
380
+ ```typescript
381
+ async convertToJayHtml(vendorDoc, pageUrl) {
382
+ // Handle empty document
383
+ if (!vendorDoc.nodes || vendorDoc.nodes.length === 0) {
384
+ return '<section><p>Empty page</p></section>';
385
+ }
386
+
387
+ // Handle unsupported features gracefully
388
+ const supportedNodes = vendorDoc.nodes.filter(node =>
389
+ ['text', 'frame', 'image'].includes(node.type)
390
+ );
391
+
392
+ return generateJayHtml(supportedNodes);
393
+ }
394
+ ```
395
+
396
+ ## Testing
397
+
398
+ ### Unit Tests
399
+
400
+ Create unit tests for your vendor:
401
+
402
+ ```typescript
403
+ import { yourVendor } from './your-vendor-id';
404
+
405
+ describe('YourVendor', () => {
406
+ it('should convert basic document', async () => {
407
+ const doc = { nodes: [{ type: 'text', text: 'Hello' }] };
408
+ const html = await yourVendor.convertToJayHtml(doc, '/test');
409
+
410
+ expect(html).toContain('Hello');
411
+ });
412
+
413
+ it('should handle empty document', async () => {
414
+ const doc = { nodes: [] };
415
+ const html = await yourVendor.convertToJayHtml(doc, '/test');
416
+
417
+ expect(html).toBeTruthy();
418
+ });
419
+ });
420
+ ```
421
+
422
+ ### Integration Tests
423
+
424
+ Test the full export flow:
425
+
426
+ 1. Start dev server
427
+ 2. Call export API from plugin
428
+ 3. Verify `page.jay-html` is created
429
+ 4. Verify HTML is valid
430
+
431
+ ## Architecture
432
+
433
+ ```
434
+ ┌─────────────────┐
435
+ │ Editor Plugin │
436
+ │ (Figma/etc.) │
437
+ └────────┬────────┘
438
+ │ export({ vendorId, vendorDoc, pageUrl })
439
+
440
+ ┌─────────────────────────┐
441
+ │ Editor Protocol │
442
+ └────────┬────────────────┘
443
+
444
+ ┌─────────────────────────┐
445
+ │ onExport Handler │
446
+ │ 1. Save JSON │
447
+ │ 2. Lookup vendor │
448
+ │ 3. Call convertToJayHtml
449
+ └────────┬────────────────┘
450
+
451
+ ┌─────────────────────────┐
452
+ │ Vendor Registry │
453
+ │ getVendor(vendorId) │
454
+ └────────┬────────────────┘
455
+
456
+ ┌─────────────────────────┐
457
+ │ Your Vendor │
458
+ │ convertToJayHtml() │
459
+ │ Returns: string │
460
+ └────────┬────────────────┘
461
+
462
+ ┌─────────────────────────┐
463
+ │ Save to File System │
464
+ │ page.jay-html │
465
+ └─────────────────────────┘
466
+ ```
467
+
468
+ ## FAQ
469
+
470
+ ### Q: Where should vendor code live?
471
+
472
+ **A:** In the Jay Framework repository under `packages/jay-stack/stack-cli/lib/vendors/`. Vendors contribute their implementations as part of the open-source project.
473
+
474
+ ### Q: Can vendors have dependencies?
475
+
476
+ **A:** Yes, add them to `packages/jay-stack/stack-cli/package.json`.
477
+
478
+ ### Q: How do I handle vendor-specific types?
479
+
480
+ **A:** Import types from your vendor's SDK (e.g., `@figma/plugin-typings`) or define your own interfaces.
481
+
482
+ ### Q: What if conversion fails?
483
+
484
+ **A:** Throw an error with a descriptive message. The framework will catch it and return it in the export response.
485
+
486
+ ### Q: Can I generate multiple files?
487
+
488
+ **A:** The current interface returns a single HTML string that gets saved to `page.jay-html`. If you need to generate additional files (like contracts), please open an issue to discuss the requirements.
489
+
490
+ ### Q: How do I debug my vendor?
491
+
492
+ **A:** Add `console.log()` statements. They will appear in the dev server console when export is called.
493
+
494
+ ## Contributing
495
+
496
+ 1. Fork the Jay Framework repository
497
+ 2. Create your vendor implementation
498
+ 3. Add tests
499
+ 4. Submit a pull request
500
+ 5. Maintainers will review and merge
501
+
502
+ ## Resources
503
+
504
+ - [Jay Framework Documentation](https://github.com/jay-framework/jay)
505
+ - [Editor Protocol Specification](../../editor-protocol/README.md)
506
+ - [Example: Figma Vendor](./figma/index.ts)
507
+
508
+ ## License
509
+
510
+ Same as Jay Framework - check the root LICENSE file.