cmssy-cli 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +7 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +67 -8
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +124 -70
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +178 -22
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +80 -71
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts +2 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +225 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/dev-ui/app.js +496 -0
- package/dist/dev-ui/index.html +443 -0
- package/dist/types/block-config.d.ts +40 -0
- package/dist/types/block-config.d.ts.map +1 -0
- package/dist/types/block-config.js +3 -0
- package/dist/types/block-config.js.map +1 -0
- package/dist/utils/block-config.d.ts +10 -0
- package/dist/utils/block-config.d.ts.map +1 -0
- package/dist/utils/block-config.js +191 -0
- package/dist/utils/block-config.js.map +1 -0
- package/dist/utils/field-schema.d.ts +10 -0
- package/dist/utils/field-schema.d.ts.map +1 -0
- package/dist/utils/field-schema.js +155 -0
- package/dist/utils/field-schema.js.map +1 -0
- package/dist/utils/type-generator.d.ts +3 -0
- package/dist/utils/type-generator.d.ts.map +1 -0
- package/dist/utils/type-generator.js +67 -0
- package/dist/utils/type-generator.js.map +1 -0
- package/package.json +8 -3
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Cmssy Dev Server</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
16
|
+
background: #f5f5f5;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
height: 100vh;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.container {
|
|
22
|
+
display: grid;
|
|
23
|
+
grid-template-columns: 280px 1fr 400px;
|
|
24
|
+
height: 100vh;
|
|
25
|
+
gap: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Left Panel - Blocks List */
|
|
29
|
+
.blocks-panel {
|
|
30
|
+
background: #ffffff;
|
|
31
|
+
border-right: 1px solid #e0e0e0;
|
|
32
|
+
overflow-y: auto;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.blocks-header {
|
|
36
|
+
padding: 20px;
|
|
37
|
+
border-bottom: 1px solid #e0e0e0;
|
|
38
|
+
background: #fafafa;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.blocks-header h1 {
|
|
42
|
+
font-size: 18px;
|
|
43
|
+
font-weight: 600;
|
|
44
|
+
color: #1a1a1a;
|
|
45
|
+
margin-bottom: 4px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.blocks-header p {
|
|
49
|
+
font-size: 13px;
|
|
50
|
+
color: #666;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.blocks-list {
|
|
54
|
+
padding: 12px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.block-item {
|
|
58
|
+
padding: 12px 16px;
|
|
59
|
+
margin-bottom: 4px;
|
|
60
|
+
border-radius: 8px;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
transition: all 0.2s;
|
|
63
|
+
border: 1px solid transparent;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.block-item:hover {
|
|
67
|
+
background: #f5f5f5;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.block-item.active {
|
|
71
|
+
background: #667eea;
|
|
72
|
+
color: white;
|
|
73
|
+
border-color: #667eea;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.block-item-name {
|
|
77
|
+
font-size: 14px;
|
|
78
|
+
font-weight: 500;
|
|
79
|
+
margin-bottom: 2px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.block-item-type {
|
|
83
|
+
font-size: 12px;
|
|
84
|
+
opacity: 0.7;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Center Panel - Preview */
|
|
88
|
+
.preview-panel {
|
|
89
|
+
background: #fafafa;
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-direction: column;
|
|
92
|
+
overflow: hidden;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.preview-header {
|
|
96
|
+
padding: 16px 24px;
|
|
97
|
+
background: white;
|
|
98
|
+
border-bottom: 1px solid #e0e0e0;
|
|
99
|
+
display: flex;
|
|
100
|
+
justify-content: space-between;
|
|
101
|
+
align-items: center;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.preview-title {
|
|
105
|
+
font-size: 16px;
|
|
106
|
+
font-weight: 600;
|
|
107
|
+
color: #1a1a1a;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.preview-badge {
|
|
111
|
+
font-size: 12px;
|
|
112
|
+
padding: 4px 12px;
|
|
113
|
+
background: #e8f5e9;
|
|
114
|
+
color: #2e7d32;
|
|
115
|
+
border-radius: 12px;
|
|
116
|
+
font-weight: 500;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.preview-content {
|
|
120
|
+
flex: 1;
|
|
121
|
+
padding: 24px;
|
|
122
|
+
overflow: auto;
|
|
123
|
+
display: flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
justify-content: center;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.preview-iframe-wrapper {
|
|
129
|
+
width: 100%;
|
|
130
|
+
height: 100%;
|
|
131
|
+
background: white;
|
|
132
|
+
border-radius: 12px;
|
|
133
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
134
|
+
overflow: hidden;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.preview-iframe {
|
|
138
|
+
width: 100%;
|
|
139
|
+
height: 100%;
|
|
140
|
+
border: none;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.preview-empty {
|
|
144
|
+
text-align: center;
|
|
145
|
+
color: #999;
|
|
146
|
+
padding: 60px 20px;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.preview-empty-icon {
|
|
150
|
+
font-size: 48px;
|
|
151
|
+
margin-bottom: 16px;
|
|
152
|
+
opacity: 0.3;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* Right Panel - Editor Drawer */
|
|
156
|
+
.editor-panel {
|
|
157
|
+
background: #ffffff;
|
|
158
|
+
border-left: 1px solid #e0e0e0;
|
|
159
|
+
display: flex;
|
|
160
|
+
flex-direction: column;
|
|
161
|
+
overflow: hidden;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.editor-header {
|
|
165
|
+
padding: 20px;
|
|
166
|
+
border-bottom: 1px solid #e0e0e0;
|
|
167
|
+
background: #fafafa;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.editor-header h2 {
|
|
171
|
+
font-size: 16px;
|
|
172
|
+
font-weight: 600;
|
|
173
|
+
color: #1a1a1a;
|
|
174
|
+
margin-bottom: 4px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.editor-header p {
|
|
178
|
+
font-size: 13px;
|
|
179
|
+
color: #666;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.editor-content {
|
|
183
|
+
flex: 1;
|
|
184
|
+
overflow-y: auto;
|
|
185
|
+
padding: 20px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.editor-empty {
|
|
189
|
+
text-align: center;
|
|
190
|
+
color: #999;
|
|
191
|
+
padding: 40px 20px;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* Form Fields */
|
|
195
|
+
.field-group {
|
|
196
|
+
margin-bottom: 24px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.field-label {
|
|
200
|
+
display: block;
|
|
201
|
+
font-size: 13px;
|
|
202
|
+
font-weight: 500;
|
|
203
|
+
color: #333;
|
|
204
|
+
margin-bottom: 8px;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.field-required {
|
|
208
|
+
color: #e53935;
|
|
209
|
+
margin-left: 2px;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.field-help {
|
|
213
|
+
font-size: 12px;
|
|
214
|
+
color: #666;
|
|
215
|
+
margin-top: 4px;
|
|
216
|
+
font-weight: 400;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.field-input {
|
|
220
|
+
width: 100%;
|
|
221
|
+
padding: 10px 12px;
|
|
222
|
+
border: 1px solid #ddd;
|
|
223
|
+
border-radius: 6px;
|
|
224
|
+
font-size: 14px;
|
|
225
|
+
font-family: inherit;
|
|
226
|
+
transition: all 0.2s;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.field-input:focus {
|
|
230
|
+
outline: none;
|
|
231
|
+
border-color: #667eea;
|
|
232
|
+
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.field-textarea {
|
|
236
|
+
min-height: 80px;
|
|
237
|
+
resize: vertical;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.field-select {
|
|
241
|
+
cursor: pointer;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.field-checkbox {
|
|
245
|
+
width: auto;
|
|
246
|
+
margin-right: 8px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* Repeater Fields */
|
|
250
|
+
.repeater-items {
|
|
251
|
+
border: 1px solid #e0e0e0;
|
|
252
|
+
border-radius: 8px;
|
|
253
|
+
padding: 12px;
|
|
254
|
+
background: #fafafa;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.repeater-item {
|
|
258
|
+
background: white;
|
|
259
|
+
border: 1px solid #e0e0e0;
|
|
260
|
+
border-radius: 6px;
|
|
261
|
+
padding: 16px;
|
|
262
|
+
margin-bottom: 12px;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.repeater-item:last-child {
|
|
266
|
+
margin-bottom: 0;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.repeater-item-header {
|
|
270
|
+
display: flex;
|
|
271
|
+
justify-content: space-between;
|
|
272
|
+
align-items: center;
|
|
273
|
+
margin-bottom: 12px;
|
|
274
|
+
padding-bottom: 12px;
|
|
275
|
+
border-bottom: 1px solid #f0f0f0;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.repeater-item-title {
|
|
279
|
+
font-size: 13px;
|
|
280
|
+
font-weight: 500;
|
|
281
|
+
color: #666;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.repeater-item-remove {
|
|
285
|
+
background: #ffebee;
|
|
286
|
+
color: #c62828;
|
|
287
|
+
border: none;
|
|
288
|
+
padding: 4px 12px;
|
|
289
|
+
border-radius: 4px;
|
|
290
|
+
font-size: 12px;
|
|
291
|
+
cursor: pointer;
|
|
292
|
+
font-weight: 500;
|
|
293
|
+
transition: all 0.2s;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.repeater-item-remove:hover {
|
|
297
|
+
background: #ffcdd2;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.repeater-add {
|
|
301
|
+
width: 100%;
|
|
302
|
+
padding: 10px;
|
|
303
|
+
background: white;
|
|
304
|
+
border: 1px dashed #667eea;
|
|
305
|
+
color: #667eea;
|
|
306
|
+
border-radius: 6px;
|
|
307
|
+
font-size: 13px;
|
|
308
|
+
font-weight: 500;
|
|
309
|
+
cursor: pointer;
|
|
310
|
+
transition: all 0.2s;
|
|
311
|
+
margin-top: 8px;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.repeater-add:hover {
|
|
315
|
+
background: #f5f7ff;
|
|
316
|
+
border-style: solid;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* Loading State */
|
|
320
|
+
.loading {
|
|
321
|
+
display: flex;
|
|
322
|
+
align-items: center;
|
|
323
|
+
justify-content: center;
|
|
324
|
+
padding: 40px;
|
|
325
|
+
color: #999;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.spinner {
|
|
329
|
+
width: 20px;
|
|
330
|
+
height: 20px;
|
|
331
|
+
border: 2px solid #f3f3f3;
|
|
332
|
+
border-top: 2px solid #667eea;
|
|
333
|
+
border-radius: 50%;
|
|
334
|
+
animation: spin 1s linear infinite;
|
|
335
|
+
margin-right: 12px;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
@keyframes spin {
|
|
339
|
+
0% { transform: rotate(0deg); }
|
|
340
|
+
100% { transform: rotate(360deg); }
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/* Media Field */
|
|
344
|
+
.media-field {
|
|
345
|
+
display: flex;
|
|
346
|
+
gap: 12px;
|
|
347
|
+
align-items: flex-start;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.media-preview {
|
|
351
|
+
width: 80px;
|
|
352
|
+
height: 80px;
|
|
353
|
+
border: 1px solid #ddd;
|
|
354
|
+
border-radius: 6px;
|
|
355
|
+
overflow: hidden;
|
|
356
|
+
background: #fafafa;
|
|
357
|
+
display: flex;
|
|
358
|
+
align-items: center;
|
|
359
|
+
justify-content: center;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.media-preview img {
|
|
363
|
+
width: 100%;
|
|
364
|
+
height: 100%;
|
|
365
|
+
object-fit: cover;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.media-placeholder {
|
|
369
|
+
color: #999;
|
|
370
|
+
font-size: 12px;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.media-input-group {
|
|
374
|
+
flex: 1;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* Color Field */
|
|
378
|
+
.color-field {
|
|
379
|
+
display: flex;
|
|
380
|
+
gap: 12px;
|
|
381
|
+
align-items: center;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.color-preview {
|
|
385
|
+
width: 40px;
|
|
386
|
+
height: 40px;
|
|
387
|
+
border: 1px solid #ddd;
|
|
388
|
+
border-radius: 6px;
|
|
389
|
+
cursor: pointer;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.color-input {
|
|
393
|
+
flex: 1;
|
|
394
|
+
}
|
|
395
|
+
</style>
|
|
396
|
+
</head>
|
|
397
|
+
<body>
|
|
398
|
+
<div class="container">
|
|
399
|
+
<!-- Left Panel: Blocks List -->
|
|
400
|
+
<div class="blocks-panel">
|
|
401
|
+
<div class="blocks-header">
|
|
402
|
+
<h1>📦 Blocks</h1>
|
|
403
|
+
<p id="blocks-count">Loading...</p>
|
|
404
|
+
</div>
|
|
405
|
+
<div class="blocks-list" id="blocks-list">
|
|
406
|
+
<div class="loading">
|
|
407
|
+
<div class="spinner"></div>
|
|
408
|
+
<span>Loading blocks...</span>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
|
|
413
|
+
<!-- Center Panel: Preview -->
|
|
414
|
+
<div class="preview-panel">
|
|
415
|
+
<div class="preview-header">
|
|
416
|
+
<div class="preview-title" id="preview-title">Preview</div>
|
|
417
|
+
<div class="preview-badge" id="preview-status">Ready</div>
|
|
418
|
+
</div>
|
|
419
|
+
<div class="preview-content" id="preview-content">
|
|
420
|
+
<div class="preview-empty">
|
|
421
|
+
<div class="preview-empty-icon">👈</div>
|
|
422
|
+
<p>Select a block to preview</p>
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
</div>
|
|
426
|
+
|
|
427
|
+
<!-- Right Panel: Editor -->
|
|
428
|
+
<div class="editor-panel">
|
|
429
|
+
<div class="editor-header">
|
|
430
|
+
<h2>⚙️ Properties</h2>
|
|
431
|
+
<p id="editor-subtitle">No block selected</p>
|
|
432
|
+
</div>
|
|
433
|
+
<div class="editor-content" id="editor-content">
|
|
434
|
+
<div class="editor-empty">
|
|
435
|
+
Select a block to edit its properties
|
|
436
|
+
</div>
|
|
437
|
+
</div>
|
|
438
|
+
</div>
|
|
439
|
+
</div>
|
|
440
|
+
|
|
441
|
+
<script src="/dev-ui/app.js"></script>
|
|
442
|
+
</body>
|
|
443
|
+
</html>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type FieldType = "singleLine" | "multiLine" | "richText" | "text" | "string" | "number" | "boolean" | "date" | "media" | "link" | "select" | "color" | "repeater";
|
|
2
|
+
export interface BaseFieldConfig {
|
|
3
|
+
type: FieldType;
|
|
4
|
+
label: string;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
defaultValue?: any;
|
|
8
|
+
helpText?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SelectFieldConfig extends BaseFieldConfig {
|
|
11
|
+
type: "select";
|
|
12
|
+
options: Array<{
|
|
13
|
+
label: string;
|
|
14
|
+
value: string;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
export interface RepeaterFieldConfig extends BaseFieldConfig {
|
|
18
|
+
type: "repeater";
|
|
19
|
+
minItems?: number;
|
|
20
|
+
maxItems?: number;
|
|
21
|
+
schema: Record<string, FieldConfig>;
|
|
22
|
+
}
|
|
23
|
+
export type FieldConfig = BaseFieldConfig | SelectFieldConfig | RepeaterFieldConfig;
|
|
24
|
+
export interface BlockConfig {
|
|
25
|
+
name: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
longDescription?: string;
|
|
28
|
+
category: string;
|
|
29
|
+
tags?: string[];
|
|
30
|
+
schema: Record<string, FieldConfig>;
|
|
31
|
+
pricing?: {
|
|
32
|
+
licenseType: "free" | "paid";
|
|
33
|
+
priceCents?: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface TemplateConfig extends Omit<BlockConfig, "category"> {
|
|
37
|
+
category?: string;
|
|
38
|
+
}
|
|
39
|
+
export type ResourceConfig = BlockConfig | TemplateConfig;
|
|
40
|
+
//# sourceMappingURL=block-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-config.d.ts","sourceRoot":"","sources":["../../src/types/block-config.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GACjB,YAAY,GACZ,WAAW,GACX,UAAU,GACV,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,MAAM,GACN,OAAO,GACP,MAAM,GACN,QAAQ,GACR,OAAO,GACP,UAAU,CAAC;AAEf,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACrC;AAED,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,iBAAiB,GACjB,mBAAmB,CAAC;AAExB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE;QACR,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-config.js","sourceRoot":"","sources":["../../src/types/block-config.ts"],"names":[],"mappings":"AAAA,8CAA8C"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BlockConfig, TemplateConfig, ResourceConfig, FieldConfig } from "../types/block-config.js";
|
|
2
|
+
export declare function defineBlock(config: BlockConfig): BlockConfig;
|
|
3
|
+
export declare function defineTemplate(config: TemplateConfig): TemplateConfig;
|
|
4
|
+
export declare function loadBlockConfig(blockPath: string): Promise<ResourceConfig | null>;
|
|
5
|
+
export declare function validateSchema(schema: Record<string, FieldConfig>, blockPath: string): Promise<{
|
|
6
|
+
valid: boolean;
|
|
7
|
+
errors: string[];
|
|
8
|
+
}>;
|
|
9
|
+
export declare function generatePackageJsonMetadata(config: ResourceConfig, packageType: "block" | "template"): any;
|
|
10
|
+
//# sourceMappingURL=block-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-config.d.ts","sourceRoot":"","sources":["../../src/utils/block-config.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,WAAW,EACX,cAAc,EACd,cAAc,EACd,WAAW,EAGZ,MAAM,0BAA0B,CAAC;AAIlC,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAE5D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAErE;AAGD,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA4EhC;AAGD,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACnC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAqF/C;AAGD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,OAAO,GAAG,UAAU,GAChC,GAAG,CAkBL"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import { getFieldTypes, isValidFieldType } from "./field-schema.js";
|
|
6
|
+
// Helper function for type-safe config authoring
|
|
7
|
+
export function defineBlock(config) {
|
|
8
|
+
return config;
|
|
9
|
+
}
|
|
10
|
+
export function defineTemplate(config) {
|
|
11
|
+
return config;
|
|
12
|
+
}
|
|
13
|
+
// Load block.config.ts dynamically
|
|
14
|
+
export async function loadBlockConfig(blockPath) {
|
|
15
|
+
const configPath = path.join(blockPath, "block.config.ts");
|
|
16
|
+
if (!fs.existsSync(configPath)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
// Find tsx binary - try multiple locations
|
|
21
|
+
const cliPath = path.dirname(path.dirname(new URL(import.meta.url).pathname));
|
|
22
|
+
// Possible locations for tsx binary
|
|
23
|
+
const possibleTsxPaths = [
|
|
24
|
+
path.join(cliPath, "node_modules", ".bin", "tsx"),
|
|
25
|
+
path.join(cliPath, "..", "..", "node_modules", ".bin", "tsx"), // If symlinked
|
|
26
|
+
path.join(process.cwd(), "node_modules", ".bin", "tsx"), // Project's node_modules
|
|
27
|
+
];
|
|
28
|
+
let tsxBinary = possibleTsxPaths.find(p => fs.existsSync(p));
|
|
29
|
+
// If not found, use npx as fallback
|
|
30
|
+
if (!tsxBinary) {
|
|
31
|
+
tsxBinary = "npx -y tsx"; // Use npx with -y to auto-install if needed
|
|
32
|
+
}
|
|
33
|
+
const cacheDir = path.join(process.cwd(), ".cmssy", "cache");
|
|
34
|
+
fs.ensureDirSync(cacheDir);
|
|
35
|
+
// Create a mock cmssy-cli/config module in cache
|
|
36
|
+
const mockConfigPath = path.join(cacheDir, "cmssy-cli-config.mjs");
|
|
37
|
+
const mockConfig = `export const defineBlock = (config) => config;\nexport const defineTemplate = (config) => config;`;
|
|
38
|
+
fs.writeFileSync(mockConfigPath, mockConfig);
|
|
39
|
+
// Read original config and replace import path to point to mock
|
|
40
|
+
const configContent = fs.readFileSync(configPath, "utf-8");
|
|
41
|
+
const modifiedConfig = configContent.replace(/from\s+['"]cmssy-cli\/config['"]/g, `from '${mockConfigPath.replace(/\\/g, '/')}'`);
|
|
42
|
+
// Write modified config to temp file
|
|
43
|
+
const tempConfigPath = path.join(cacheDir, `temp-${path.basename(configPath)}`);
|
|
44
|
+
fs.writeFileSync(tempConfigPath, modifiedConfig);
|
|
45
|
+
// Execute with tsx - use --eval to import and output
|
|
46
|
+
const evalCode = `import cfg from '${tempConfigPath.replace(/\\/g, '/')}'; console.log(JSON.stringify(cfg.default || cfg));`;
|
|
47
|
+
// Build command - handle both direct binary path and npx
|
|
48
|
+
const command = tsxBinary.includes("npx")
|
|
49
|
+
? `${tsxBinary} --eval "${evalCode}"`
|
|
50
|
+
: `"${tsxBinary}" --eval "${evalCode}"`;
|
|
51
|
+
const output = execSync(command, {
|
|
52
|
+
encoding: "utf-8",
|
|
53
|
+
cwd: process.cwd(),
|
|
54
|
+
env: {
|
|
55
|
+
...process.env,
|
|
56
|
+
NODE_ENV: "development",
|
|
57
|
+
},
|
|
58
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
59
|
+
});
|
|
60
|
+
// Clean up
|
|
61
|
+
fs.removeSync(tempConfigPath);
|
|
62
|
+
fs.removeSync(mockConfigPath);
|
|
63
|
+
// Parse JSON output
|
|
64
|
+
const lines = output.trim().split("\n");
|
|
65
|
+
const jsonLine = lines[lines.length - 1];
|
|
66
|
+
const config = JSON.parse(jsonLine);
|
|
67
|
+
return config;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
throw new Error(`Failed to load block.config.ts at ${configPath}: ${error.message}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Validate schema against backend field types
|
|
74
|
+
export async function validateSchema(schema, blockPath) {
|
|
75
|
+
const errors = [];
|
|
76
|
+
const fieldTypes = await getFieldTypes();
|
|
77
|
+
function validateField(key, field, parentPath = "") {
|
|
78
|
+
const fullPath = parentPath ? `${parentPath}.${key}` : key;
|
|
79
|
+
// Check if field type is valid
|
|
80
|
+
if (!isValidFieldType(field.type, fieldTypes)) {
|
|
81
|
+
errors.push(`Invalid field type "${field.type}" for field "${fullPath}". Valid types: ${fieldTypes.map((ft) => ft.type).join(", ")}`);
|
|
82
|
+
}
|
|
83
|
+
// Validate repeater nested schema
|
|
84
|
+
if (field.type === "repeater") {
|
|
85
|
+
const repeaterField = field;
|
|
86
|
+
if (!repeaterField.schema || typeof repeaterField.schema !== "object") {
|
|
87
|
+
errors.push(`Repeater field "${fullPath}" must have a "schema" property`);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// Recursively validate nested schema
|
|
91
|
+
Object.entries(repeaterField.schema).forEach(([nestedKey, nestedField]) => {
|
|
92
|
+
validateField(nestedKey, nestedField, fullPath);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// Validate minItems/maxItems
|
|
96
|
+
if (repeaterField.minItems !== undefined && repeaterField.minItems < 0) {
|
|
97
|
+
errors.push(`Repeater field "${fullPath}" has invalid minItems (must be >= 0)`);
|
|
98
|
+
}
|
|
99
|
+
if (repeaterField.maxItems !== undefined && repeaterField.maxItems < 1) {
|
|
100
|
+
errors.push(`Repeater field "${fullPath}" has invalid maxItems (must be >= 1)`);
|
|
101
|
+
}
|
|
102
|
+
if (repeaterField.minItems &&
|
|
103
|
+
repeaterField.maxItems &&
|
|
104
|
+
repeaterField.minItems > repeaterField.maxItems) {
|
|
105
|
+
errors.push(`Repeater field "${fullPath}" has minItems > maxItems`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Validate select options
|
|
109
|
+
if (field.type === "select") {
|
|
110
|
+
const selectField = field;
|
|
111
|
+
if (!selectField.options ||
|
|
112
|
+
!Array.isArray(selectField.options) ||
|
|
113
|
+
selectField.options.length === 0) {
|
|
114
|
+
errors.push(`Select field "${fullPath}" must have at least one option`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Warn about required fields with default values
|
|
118
|
+
if (field.required && field.defaultValue !== undefined) {
|
|
119
|
+
console.warn(chalk.yellow(`Warning: Field "${fullPath}" is required but has a defaultValue. The defaultValue will be ignored.`));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
Object.entries(schema).forEach(([key, field]) => {
|
|
123
|
+
validateField(key, field);
|
|
124
|
+
});
|
|
125
|
+
return { valid: errors.length === 0, errors };
|
|
126
|
+
}
|
|
127
|
+
// Generate package.json cmssy section from block.config.ts
|
|
128
|
+
export function generatePackageJsonMetadata(config, packageType) {
|
|
129
|
+
// Convert schema to legacy schemaFields format
|
|
130
|
+
const schemaFields = convertSchemaToLegacyFormat(config.schema);
|
|
131
|
+
// Extract default content from schema
|
|
132
|
+
const defaultContent = extractDefaultContent(config.schema);
|
|
133
|
+
return {
|
|
134
|
+
packageType,
|
|
135
|
+
displayName: config.name,
|
|
136
|
+
description: config.description,
|
|
137
|
+
longDescription: config.longDescription,
|
|
138
|
+
category: config.category || (packageType === "template" ? "pages" : "other"),
|
|
139
|
+
tags: config.tags || [],
|
|
140
|
+
pricing: config.pricing || { licenseType: "free" },
|
|
141
|
+
schemaFields,
|
|
142
|
+
defaultContent,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function convertSchemaToLegacyFormat(schema) {
|
|
146
|
+
const fields = [];
|
|
147
|
+
function convertField(key, field) {
|
|
148
|
+
const baseField = {
|
|
149
|
+
key,
|
|
150
|
+
type: field.type,
|
|
151
|
+
label: field.label,
|
|
152
|
+
required: field.required || false,
|
|
153
|
+
};
|
|
154
|
+
if (field.placeholder) {
|
|
155
|
+
baseField.placeholder = field.placeholder;
|
|
156
|
+
}
|
|
157
|
+
if (field.type === "select") {
|
|
158
|
+
const selectField = field;
|
|
159
|
+
baseField.options = selectField.options;
|
|
160
|
+
}
|
|
161
|
+
if (field.type === "repeater") {
|
|
162
|
+
const repeaterField = field;
|
|
163
|
+
const nestedFields = convertSchemaToLegacyFormat(repeaterField.schema);
|
|
164
|
+
baseField.minItems = repeaterField.minItems;
|
|
165
|
+
baseField.maxItems = repeaterField.maxItems;
|
|
166
|
+
baseField.itemSchema = {
|
|
167
|
+
type: "object",
|
|
168
|
+
fields: nestedFields,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return baseField;
|
|
172
|
+
}
|
|
173
|
+
Object.entries(schema).forEach(([key, field]) => {
|
|
174
|
+
fields.push(convertField(key, field));
|
|
175
|
+
});
|
|
176
|
+
return fields;
|
|
177
|
+
}
|
|
178
|
+
function extractDefaultContent(schema) {
|
|
179
|
+
const content = {};
|
|
180
|
+
Object.entries(schema).forEach(([key, field]) => {
|
|
181
|
+
if (field.defaultValue !== undefined) {
|
|
182
|
+
content[key] = field.defaultValue;
|
|
183
|
+
}
|
|
184
|
+
else if (field.type === "repeater") {
|
|
185
|
+
// Repeaters default to empty array
|
|
186
|
+
content[key] = [];
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
return content;
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=block-config.js.map
|