@deneb-ui/cli 2.0.14 → 2.0.15
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/bin/index.js
CHANGED
|
@@ -199,6 +199,32 @@ function detectPages(projectDir) {
|
|
|
199
199
|
...(isContact ? { required: true } : {}),
|
|
200
200
|
});
|
|
201
201
|
}
|
|
202
|
+
|
|
203
|
+
// Check for dynamic product detail routes (e.g. products/[slug] or shop/[id])
|
|
204
|
+
const subDirPath = path.join(cDir, routeName);
|
|
205
|
+
try {
|
|
206
|
+
if (fs.existsSync(subDirPath)) {
|
|
207
|
+
const subEntries = fs.readdirSync(subDirPath, { withFileTypes: true });
|
|
208
|
+
const hasDynamicSlug = subEntries.some((se) => se.isDirectory() && se.name.startsWith('['));
|
|
209
|
+
if (hasDynamicSlug && !foundRoutes.has('/' + routeName + '/detail')) {
|
|
210
|
+
foundRoutes.add('/' + routeName + '/detail');
|
|
211
|
+
let sampleSlug = 'vanta-aero-x';
|
|
212
|
+
const siteDataPath = path.join(projectDir, 'src', 'data', 'site-data.json');
|
|
213
|
+
if (fs.existsSync(siteDataPath)) {
|
|
214
|
+
try {
|
|
215
|
+
const sd = JSON.parse(fs.readFileSync(siteDataPath, 'utf8'));
|
|
216
|
+
if (sd.content?.product?.slug) sampleSlug = sd.content.product.slug;
|
|
217
|
+
else if (sd.content?.home?.product1Slug) sampleSlug = sd.content.home.product1Slug;
|
|
218
|
+
} catch {}
|
|
219
|
+
}
|
|
220
|
+
pages.push({
|
|
221
|
+
id: 'product',
|
|
222
|
+
label: 'Product Detail',
|
|
223
|
+
route: `/${routeName}/${sampleSlug}`,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
} catch {}
|
|
202
228
|
}
|
|
203
229
|
}
|
|
204
230
|
} catch {
|
|
@@ -291,6 +317,28 @@ function getDefaultManifest(projectName, pages) {
|
|
|
291
317
|
},
|
|
292
318
|
],
|
|
293
319
|
},
|
|
320
|
+
...(pages.some((p) => p.id === 'product')
|
|
321
|
+
? [
|
|
322
|
+
{
|
|
323
|
+
id: 'product',
|
|
324
|
+
path: 'product',
|
|
325
|
+
type: 'object',
|
|
326
|
+
label: 'Product Detail Content',
|
|
327
|
+
fields: [
|
|
328
|
+
{ key: 'name', type: 'text', label: 'Product Title', required: true },
|
|
329
|
+
{ key: 'price', type: 'text', label: 'Product Price', required: true },
|
|
330
|
+
{ key: 'description', type: 'textarea', label: 'Product Description' },
|
|
331
|
+
{ key: 'badge', type: 'text', label: 'Product Badge' },
|
|
332
|
+
{ key: 'featuredImage', type: 'image', label: 'Featured Product Image' },
|
|
333
|
+
{ key: 'addToSelectionLabel', type: 'text', label: 'Add to Selection Label' },
|
|
334
|
+
{ key: 'specsTitle', type: 'text', label: 'Specifications Heading' },
|
|
335
|
+
{ key: 'shippingTitle', type: 'text', label: 'Shipping Heading' },
|
|
336
|
+
{ key: 'shippingSummary', type: 'text', label: 'Shipping Summary' },
|
|
337
|
+
{ key: 'shippingReturns', type: 'text', label: 'Shipping Returns Note' },
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
]
|
|
341
|
+
: []),
|
|
294
342
|
],
|
|
295
343
|
},
|
|
296
344
|
};
|
|
@@ -352,6 +400,24 @@ function getDefaultSiteData(projectName, pages) {
|
|
|
352
400
|
primaryCtaLabel: 'Shop Now',
|
|
353
401
|
secondaryCtaLabel: 'Learn More',
|
|
354
402
|
},
|
|
403
|
+
...(pages.some((p) => p.id === 'product')
|
|
404
|
+
? {
|
|
405
|
+
product: {
|
|
406
|
+
name: 'Signature Performance Edition',
|
|
407
|
+
price: 'LKR 32,500',
|
|
408
|
+
description: 'Lightweight performance runner with responsive foam midsole and breathable engineered mesh.',
|
|
409
|
+
badge: 'NEW',
|
|
410
|
+
featuredImage: '/products/vanta-aero-x.jpg',
|
|
411
|
+
addToSelectionLabel: 'Add to Selection',
|
|
412
|
+
specsTitle: 'Specifications',
|
|
413
|
+
shippingTitle: 'Shipping & Returns',
|
|
414
|
+
shippingSummary: 'Free Islandwide Delivery within 2-3 business days. Cash on delivery available.',
|
|
415
|
+
shippingReturns: '14-day hassle-free exchanges for unworn footwear in original condition.',
|
|
416
|
+
relatedLabel: 'You May Also Like',
|
|
417
|
+
relatedTitle: 'Related Products',
|
|
418
|
+
},
|
|
419
|
+
}
|
|
420
|
+
: {}),
|
|
355
421
|
},
|
|
356
422
|
};
|
|
357
423
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deneb-ui/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.15",
|
|
4
4
|
"description": "Official developer CLI for DENEB UI — Validating, scaffolding, testing, and packaging storefront templates. Created by Chamika Gayashan & Induranga Kawishwara.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"deneb": "bin/index.js",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "vanta-shoes-store",
|
|
3
3
|
"label": "Vanta Shoes Storefront",
|
|
4
4
|
"description": "Calibrated Vanta shoes store recipe with WhatsApp CTA, Instagram social link, product grid, and hero slides.",
|
|
5
|
-
"savedAt": "2026-09-
|
|
5
|
+
"savedAt": "2026-09-09T04:15:03.327Z",
|
|
6
6
|
"signatures": {
|
|
7
7
|
"keywords": [
|
|
8
8
|
"vanta-shoes-store",
|
|
@@ -53,7 +53,16 @@
|
|
|
53
53
|
}
|
|
54
54
|
],
|
|
55
55
|
"autoIndex": true,
|
|
56
|
-
"defaultCardPrefix": "product"
|
|
56
|
+
"defaultCardPrefix": "product",
|
|
57
|
+
"withBasePathImages": true,
|
|
58
|
+
"safeNavigationButtons": true
|
|
59
|
+
},
|
|
60
|
+
"productDetailRules": {
|
|
61
|
+
"enabled": true,
|
|
62
|
+
"sectionPath": "product",
|
|
63
|
+
"sampleRoute": "/products/vanta-aero-x",
|
|
64
|
+
"galleryWithBasePath": true,
|
|
65
|
+
"noStaticOnEditableAncestors": true
|
|
57
66
|
},
|
|
58
67
|
"actionRules": {
|
|
59
68
|
"splitActionAndLabel": true,
|
|
@@ -89,6 +98,40 @@
|
|
|
89
98
|
],
|
|
90
99
|
"targetPath": "common.footer"
|
|
91
100
|
},
|
|
101
|
+
"pages": [
|
|
102
|
+
{
|
|
103
|
+
"id": "home",
|
|
104
|
+
"label": "Home",
|
|
105
|
+
"route": "/",
|
|
106
|
+
"required": true
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"id": "about",
|
|
110
|
+
"label": "About",
|
|
111
|
+
"route": "/about"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"id": "contact",
|
|
115
|
+
"label": "Contact",
|
|
116
|
+
"route": "/contact",
|
|
117
|
+
"required": true
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"id": "products",
|
|
121
|
+
"label": "Products",
|
|
122
|
+
"route": "/products"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"id": "shop",
|
|
126
|
+
"label": "Shop",
|
|
127
|
+
"route": "/shop"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"id": "product",
|
|
131
|
+
"label": "Product Detail",
|
|
132
|
+
"route": "/products/vanta-aero-x"
|
|
133
|
+
}
|
|
134
|
+
],
|
|
92
135
|
"sections": [
|
|
93
136
|
{
|
|
94
137
|
"id": "common",
|
|
@@ -937,6 +980,7 @@
|
|
|
937
980
|
},
|
|
938
981
|
{
|
|
939
982
|
"id": "product",
|
|
983
|
+
"path": "product",
|
|
940
984
|
"label": "Product Detail Page",
|
|
941
985
|
"fields": [
|
|
942
986
|
{
|
|
@@ -1119,7 +1163,7 @@
|
|
|
1119
1163
|
"product2Name": "VANTA Flux 01",
|
|
1120
1164
|
"product2Category": "Sneakers",
|
|
1121
1165
|
"product2Price": "LKR 28,900",
|
|
1122
|
-
"product2Badge": "",
|
|
1166
|
+
"product2Badge": "Trending",
|
|
1123
1167
|
"product2Image": "/products/vanta-flux-01.jpg",
|
|
1124
1168
|
"product3Name": "VANTA Core Runner",
|
|
1125
1169
|
"product3Category": "Performance",
|
|
@@ -1129,12 +1173,12 @@
|
|
|
1129
1173
|
"product4Name": "VANTA Mono",
|
|
1130
1174
|
"product4Category": "Lifestyle",
|
|
1131
1175
|
"product4Price": "LKR 24,800",
|
|
1132
|
-
"product4Badge": "",
|
|
1176
|
+
"product4Badge": "Exclusive",
|
|
1133
1177
|
"product4Image": "/products/vanta-mono.jpg",
|
|
1134
1178
|
"product5Name": "VANTA Street 02",
|
|
1135
1179
|
"product5Category": "Sneakers",
|
|
1136
1180
|
"product5Price": "LKR 27,500",
|
|
1137
|
-
"product5Badge": "",
|
|
1181
|
+
"product5Badge": "Popular",
|
|
1138
1182
|
"product5Image": "/products/vanta-street-02.jpg",
|
|
1139
1183
|
"product6Name": "VANTA Velocity",
|
|
1140
1184
|
"product6Category": "Performance",
|
|
@@ -1144,7 +1188,7 @@
|
|
|
1144
1188
|
"product7Name": "VANTA Apex Hi",
|
|
1145
1189
|
"product7Category": "Sneakers",
|
|
1146
1190
|
"product7Price": "LKR 31,200",
|
|
1147
|
-
"product7Badge": "",
|
|
1191
|
+
"product7Badge": "Featured",
|
|
1148
1192
|
"product7Image": "/products/vanta-apex-hi.jpg",
|
|
1149
1193
|
"product8Name": "VANTA Train Pro",
|
|
1150
1194
|
"product8Category": "Training",
|
|
@@ -1159,7 +1203,7 @@
|
|
|
1159
1203
|
"product10Name": "VANTA Flux 01",
|
|
1160
1204
|
"product10Category": "Sneakers",
|
|
1161
1205
|
"product10Price": "LKR 28,900",
|
|
1162
|
-
"product10Badge": "",
|
|
1206
|
+
"product10Badge": "Trending",
|
|
1163
1207
|
"product10Image": "/products/vanta-flux-01.jpg",
|
|
1164
1208
|
"product11Name": "VANTA Core Runner",
|
|
1165
1209
|
"product11Category": "Performance",
|
|
@@ -1169,7 +1213,7 @@
|
|
|
1169
1213
|
"product12Name": "VANTA Mono",
|
|
1170
1214
|
"product12Category": "Lifestyle",
|
|
1171
1215
|
"product12Price": "LKR 24,800",
|
|
1172
|
-
"product12Badge": "",
|
|
1216
|
+
"product12Badge": "Exclusive",
|
|
1173
1217
|
"product12Image": "/products/vanta-mono.jpg"
|
|
1174
1218
|
},
|
|
1175
1219
|
"product": {
|
|
@@ -132,6 +132,15 @@ function saveRecipeFromProject(projectDir, recipeName = 'custom-storefront', opt
|
|
|
132
132
|
],
|
|
133
133
|
autoIndex: true,
|
|
134
134
|
defaultCardPrefix: 'product',
|
|
135
|
+
withBasePathImages: true,
|
|
136
|
+
safeNavigationButtons: true,
|
|
137
|
+
},
|
|
138
|
+
productDetailRules: {
|
|
139
|
+
enabled: true,
|
|
140
|
+
sectionPath: 'product',
|
|
141
|
+
sampleRoute: '/products/vanta-aero-x',
|
|
142
|
+
galleryWithBasePath: true,
|
|
143
|
+
noStaticOnEditableAncestors: true,
|
|
135
144
|
},
|
|
136
145
|
actionRules: {
|
|
137
146
|
splitActionAndLabel: true,
|
|
@@ -146,7 +155,11 @@ function saveRecipeFromProject(projectDir, recipeName = 'custom-storefront', opt
|
|
|
146
155
|
platforms: ['instagram', 'facebook', 'twitter', 'tiktok', 'youtube', 'linkedin'],
|
|
147
156
|
targetPath: 'common.footer',
|
|
148
157
|
},
|
|
149
|
-
|
|
158
|
+
pages: manifest.pages || [],
|
|
159
|
+
sections: (manifest.editorSchema?.sections || []).map((s) => ({
|
|
160
|
+
...s,
|
|
161
|
+
path: s.path || s.id,
|
|
162
|
+
})),
|
|
150
163
|
defaults: siteData.content || {},
|
|
151
164
|
};
|
|
152
165
|
|
|
@@ -579,13 +579,18 @@ function transformFileContent(filePath, pageKey, extractedData, backupDir, proje
|
|
|
579
579
|
});
|
|
580
580
|
|
|
581
581
|
// 14. Sensitive Element Guardian: Auto-annotate non-bound <a> and <img> tags with data-preview-static
|
|
582
|
-
//
|
|
583
|
-
|
|
582
|
+
// CRITICAL FIVORA RULE: Never mark an <a> as static if it encloses any children with data-preview-field-path
|
|
583
|
+
// This completely eliminates "markers cannot be on or inside data-preview-static" errors across cards and links.
|
|
584
|
+
code = code.replace(/<a(\s+[^>]*?href="[^"]*"[^>]*?)>([\s\S]*?)<\/a>/gi, (match, attrs, inner) => {
|
|
584
585
|
if (attrs.includes('data-preview-field-path') || attrs.includes('data-preview-static')) {
|
|
585
586
|
return match;
|
|
586
587
|
}
|
|
588
|
+
// If the inner content has editable field markers or is a card wrapper, NEVER mark the anchor static!
|
|
589
|
+
if (inner.includes('data-preview-field-path') || inner.includes('data-preview-item-path') || /card|product|shoe|item/i.test(attrs)) {
|
|
590
|
+
return match;
|
|
591
|
+
}
|
|
587
592
|
fileModified = true;
|
|
588
|
-
return `<a${attrs} data-preview-static="navigation-link">`;
|
|
593
|
+
return `<a${attrs} data-preview-static="navigation-link">${inner}</a>`;
|
|
589
594
|
});
|
|
590
595
|
|
|
591
596
|
code = code.replace(/<img(\s+[^>]*?src="[^"]*"[^>]*?)>/gi, (match, attrs) => {
|
|
@@ -735,13 +740,25 @@ function generateTemplateData(projectDir, projectName, detectedPages, extractedB
|
|
|
735
740
|
},
|
|
736
741
|
];
|
|
737
742
|
|
|
738
|
-
//
|
|
743
|
+
// Merge pages from detectedPages and activeRecipe.pages
|
|
744
|
+
const mergedPages = [...detectedPages];
|
|
745
|
+
if (activeRecipe && Array.isArray(activeRecipe.pages)) {
|
|
746
|
+
for (const rp of activeRecipe.pages) {
|
|
747
|
+
if (!mergedPages.some((p) => p.id === rp.id || p.route === rp.route)) {
|
|
748
|
+
mergedPages.push(rp);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// If active recipe has defined sections, seed them with guaranteed path property
|
|
739
754
|
if (activeRecipe && Array.isArray(activeRecipe.sections)) {
|
|
740
755
|
for (const sec of activeRecipe.sections) {
|
|
741
|
-
|
|
756
|
+
const clonedSec = { ...sec };
|
|
757
|
+
if (!clonedSec.path) clonedSec.path = clonedSec.id;
|
|
758
|
+
if (clonedSec.id === 'common') {
|
|
742
759
|
const commonSec = editorSections.find((s) => s.id === 'common');
|
|
743
|
-
if (commonSec && Array.isArray(
|
|
744
|
-
for (const rf of
|
|
760
|
+
if (commonSec && Array.isArray(clonedSec.fields)) {
|
|
761
|
+
for (const rf of clonedSec.fields) {
|
|
745
762
|
if (!commonSec.fields.some((f) => f.key === rf.key)) {
|
|
746
763
|
commonSec.fields.push(rf);
|
|
747
764
|
}
|
|
@@ -749,7 +766,7 @@ function generateTemplateData(projectDir, projectName, detectedPages, extractedB
|
|
|
749
766
|
}
|
|
750
767
|
continue;
|
|
751
768
|
}
|
|
752
|
-
editorSections.push(
|
|
769
|
+
editorSections.push(clonedSec);
|
|
753
770
|
}
|
|
754
771
|
}
|
|
755
772
|
|
|
@@ -796,6 +813,7 @@ function generateTemplateData(projectDir, projectName, detectedPages, extractedB
|
|
|
796
813
|
}
|
|
797
814
|
|
|
798
815
|
if (existingSection) {
|
|
816
|
+
if (!existingSection.path) existingSection.path = existingSection.id;
|
|
799
817
|
existingSection.fields.push(...newFieldDefs);
|
|
800
818
|
} else if (newFieldDefs.length > 0) {
|
|
801
819
|
editorSections.push({
|
|
@@ -808,6 +826,11 @@ function generateTemplateData(projectDir, projectName, detectedPages, extractedB
|
|
|
808
826
|
}
|
|
809
827
|
}
|
|
810
828
|
|
|
829
|
+
// Ensure all editorSections have a valid path attribute
|
|
830
|
+
for (const sec of editorSections) {
|
|
831
|
+
if (!sec.path) sec.path = sec.id;
|
|
832
|
+
}
|
|
833
|
+
|
|
811
834
|
const siteData = {
|
|
812
835
|
project: {
|
|
813
836
|
id: `${projectName}-project`,
|
|
@@ -823,11 +846,11 @@ function generateTemplateData(projectDir, projectName, detectedPages, extractedB
|
|
|
823
846
|
name: projectName,
|
|
824
847
|
engine: 'NEXT_STATIC_EXPORT',
|
|
825
848
|
structure: {
|
|
826
|
-
pages:
|
|
849
|
+
pages: mergedPages.map((p) => p.id),
|
|
827
850
|
},
|
|
828
851
|
},
|
|
829
852
|
requirements: {
|
|
830
|
-
requiredPages:
|
|
853
|
+
requiredPages: mergedPages.filter((p) => p.required).map((p) => p.id),
|
|
831
854
|
requiredFeatures: [],
|
|
832
855
|
},
|
|
833
856
|
content: content,
|
|
@@ -846,7 +869,7 @@ function generateTemplateData(projectDir, projectName, detectedPages, extractedB
|
|
|
846
869
|
installCommand: 'npm install',
|
|
847
870
|
buildCommand: 'npm run build',
|
|
848
871
|
basePathEnvVar: 'NEXT_PUBLIC_SITE_BASE_PATH',
|
|
849
|
-
pages:
|
|
872
|
+
pages: mergedPages,
|
|
850
873
|
editorSchema: {
|
|
851
874
|
version: 1,
|
|
852
875
|
sections: editorSections,
|