@deneb-ui/cli 2.0.113 → 2.0.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deneb-ui/cli",
3
- "version": "2.0.113",
3
+ "version": "2.0.114",
4
4
  "description": "Official DENEB CLI — scaffold, convert, validate, and package Fivora-ready storefront templates.",
5
5
  "bin": {
6
6
  "deneb": "bin/index.js",
@@ -49,7 +49,7 @@
49
49
  "license": "MIT",
50
50
  "dependencies": {
51
51
  "@babel/parser": "^7.28.0",
52
- "@deneb-ui/core": "^2.0.113",
52
+ "@deneb-ui/core": "^2.0.114",
53
53
  "@octokit/rest": "^22.0.1",
54
54
  "adm-zip": "^0.6.0",
55
55
  "dotenv": "^17.4.2",
@@ -706,13 +706,9 @@ function patchSiteFooter(projectDir, dryRun, log, report) {
706
706
  }
707
707
 
708
708
  function patchUniversalIcons(projectDir, dryRun, log, report) {
709
- // Scans and ensures all feature icons across files are editable
710
709
  try {
711
- const patcherPath = path.join(__dirname, 'universal-icon-patcher.cjs');
712
- if (fs.existsSync(patcherPath)) {
713
- // Patcher is executed to guarantee all icons across the project are converted
714
- log('[auto-fix] Running universal icon pass...');
715
- }
710
+ const { patchUniversalIcons: runPatcher } = require('./universal-icon-patcher.cjs');
711
+ runPatcher(projectDir, dryRun, log);
716
712
  } catch (err) {
717
713
  log('[auto-fix] Universal icon scan warning: ' + err.message);
718
714
  }
@@ -3,13 +3,6 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
- const rootDir = 'd:/projects/office/deneb/fivora-car-sale';
7
- const siteDataPath = path.join(rootDir, 'data', 'site-data.json');
8
-
9
- const siteData = JSON.parse(fs.readFileSync(siteDataPath, 'utf8'));
10
- if (!siteData.content) siteData.content = {};
11
- if (!siteData.content.common) siteData.content.common = {};
12
-
13
6
  // Helper to ensure nested object path exists
14
7
  function setDeep(obj, pathArr, value) {
15
8
  let cur = obj;
@@ -23,198 +16,40 @@ function setDeep(obj, pathArr, value) {
23
16
  }
24
17
  }
25
18
 
26
- // 1. Register icons in site-data.json
27
- setDeep(siteData.content, ['common', 'showroom', 'pinIcon'], 'map-pin');
28
- setDeep(siteData.content, ['common', 'showroom', 'clockIcon'], 'clock');
29
- setDeep(siteData.content, ['common', 'showroom', 'navIcon'], 'arrow-up-right');
30
- setDeep(siteData.content, ['common', 'whatsapp', 'copyIcon'], 'copy');
31
- setDeep(siteData.content, ['common', 'whatsapp', 'icon'], 'message-circle');
32
- setDeep(siteData.content, ['common', 'search', 'searchIcon'], 'search');
33
- setDeep(siteData.content, ['common', 'testDrive', 'calendarIcon'], 'calendar');
34
- setDeep(siteData.content, ['common', 'testDrive', 'infoIcon'], 'info');
35
- setDeep(siteData.content, ['common', 'vehicleActions', 'historyIcon'], 'file-text');
36
- setDeep(siteData.content, ['common', 'vehicleActions', 'testDriveIcon'], 'calendar-check');
37
- setDeep(siteData.content, ['common', 'vehicleActions', 'financeIcon'], 'landmark');
38
- setDeep(siteData.content, ['common', 'vehicleActions', 'alertIcon'], 'bell');
39
- setDeep(siteData.content, ['common', 'vehicleActions', 'calculatorIcon'], 'calculator');
40
-
41
- if (!siteData.content['trade-in']) siteData.content['trade-in'] = {};
42
- setDeep(siteData.content, ['trade-in', 'cameraIcon'], 'camera');
43
- setDeep(siteData.content, ['trade-in', 'upgradeEstimatorIcon'], 'refresh-cw');
44
- setDeep(siteData.content, ['trade-in', 'step1Icon'], 'car-front');
45
- setDeep(siteData.content, ['trade-in', 'step2Icon'], 'camera');
46
- setDeep(siteData.content, ['trade-in', 'step3Icon'], 'handshake');
47
- setDeep(siteData.content, ['trade-in', 'step4Icon'], 'refresh-cw');
48
-
49
- if (!siteData.content.about) siteData.content.about = {};
50
- setDeep(siteData.content, ['about', 'principle1Icon'], 'eye');
51
- setDeep(siteData.content, ['about', 'principle2Icon'], 'scale');
52
- setDeep(siteData.content, ['about', 'principle3Icon'], 'message-circle');
53
- setDeep(siteData.content, ['about', 'principle4Icon'], 'file-search');
54
-
55
- if (!siteData.content['source-a-car']) siteData.content['source-a-car'] = {};
56
- setDeep(siteData.content, ['source-a-car', 'step1Icon'], 'clipboard-list');
57
- setDeep(siteData.content, ['source-a-car', 'step2Icon'], 'message-circle');
58
- setDeep(siteData.content, ['source-a-car', 'step3Icon'], 'search');
59
-
60
- if (!siteData.content.compare) siteData.content.compare = {};
61
- setDeep(siteData.content, ['compare', 'trophyIcon'], 'trophy');
62
-
63
- fs.writeFileSync(siteDataPath, JSON.stringify(siteData, null, 2), 'utf8');
64
- console.log('Updated site-data.json with all icon definitions');
65
-
66
- // 2. Patch components/vehicles/vehicle-actions.tsx
67
- {
68
- const filePath = path.join(rootDir, 'components', 'vehicles', 'vehicle-actions.tsx');
69
- let code = fs.readFileSync(filePath, 'utf8');
70
- if (!code.includes('EditableIcon')) {
71
- code = code.replace(
72
- /from "@deneb-ui\/ui";?/,
73
- ', EditableIcon } from "@deneb-ui/ui";'
74
- );
75
- }
76
- // Replace FileTextIcon
77
- code = code.replace(
78
- /<FileTextIcon[^>]*data-preview-static="decorative-icon"[^>]*\/>/g,
79
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 mr-1.5" data-preview-field-path="common.vehicleActions.historyIcon" title="Click to edit history icon"><EditableIcon name={(siteData?.content?.common as any)?.vehicleActions?.historyIcon ?? "file-text"} className="size-4" /></span>'
80
- );
81
- // Replace CalendarCheckIcon
82
- code = code.replace(
83
- /<CalendarCheckIcon[^>]*data-preview-static="decorative-icon"[^>]*\/>/g,
84
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 mr-1.5" data-preview-field-path="common.vehicleActions.testDriveIcon" title="Click to edit test drive icon"><EditableIcon name={(siteData?.content?.common as any)?.vehicleActions?.testDriveIcon ?? "calendar-check"} className="size-4" /></span>'
85
- );
86
- // Replace LandmarkIcon
87
- code = code.replace(
88
- /<LandmarkIcon[^>]*data-preview-static="decorative-icon"[^>]*\/>/g,
89
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 mr-1.5" data-preview-field-path="common.vehicleActions.financeIcon" title="Click to edit finance icon"><EditableIcon name={(siteData?.content?.common as any)?.vehicleActions?.financeIcon ?? "landmark"} className="size-4" /></span>'
90
- );
91
- // Replace BellIcon
92
- code = code.replace(
93
- /<BellIcon[^>]*data-preview-static="decorative-icon"[^>]*\/>/g,
94
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 mr-1.5" data-preview-field-path="common.vehicleActions.alertIcon" title="Click to edit alert icon"><EditableIcon name={(siteData?.content?.common as any)?.vehicleActions?.alertIcon ?? "bell"} className="size-4" /></span>'
95
- );
96
- // Replace CalculatorIcon
97
- code = code.replace(
98
- /<CalculatorIcon[^>]*data-preview-static="decorative-icon"[^>]*\/>/g,
99
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 mr-1.5" data-preview-field-path="common.vehicleActions.calculatorIcon" title="Click to edit calculator icon"><EditableIcon name={(siteData?.content?.common as any)?.vehicleActions?.calculatorIcon ?? "calculator"} className="size-4" /></span>'
100
- );
101
- fs.writeFileSync(filePath, code, 'utf8');
102
- console.log('Patched components/vehicles/vehicle-actions.tsx');
103
- }
104
-
105
- // 3. Patch app/(public)/trade-in/page.tsx
106
- {
107
- const filePath = path.join(rootDir, 'app', '(public)', 'trade-in', 'page.tsx');
108
- let code = fs.readFileSync(filePath, 'utf8');
109
- if (!code.includes('EditableIcon')) {
110
- code = 'import { EditableIcon } from "@/src/components/ui/EditableIcon";\n' + code;
19
+ function patchUniversalIcons(projectDir, dryRun = false, log = () => {}) {
20
+ if (!projectDir || typeof projectDir !== 'string' || !fs.existsSync(projectDir)) {
21
+ return;
111
22
  }
112
- code = code.replace(
113
- /<RefreshCwIcon[^>]*data-preview-static="decorative-icon"[^>]*\/>/g,
114
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 mr-1.5" data-preview-field-path="trade-in.upgradeEstimatorIcon" title="Click to edit estimator icon"><EditableIcon name={(siteData?.content?.["trade-in"] as any)?.upgradeEstimatorIcon ?? "refresh-cw"} className="size-4" /></span>'
115
- );
116
- code = code.replace(
117
- /<Icon className="size-6 text-primary" aria-hidden data-preview-static="decorative-icon" \/>/g,
118
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 text-primary" data-preview-field-path={`trade-in.step${i + 1}Icon`} title="Click to edit step icon"><EditableIcon name={(siteData?.content?.["trade-in"] as any)?.[`step${i + 1}Icon`] ?? ["car-front", "camera", "handshake", "refresh-cw"][i]} className="size-6" /></span>'
119
- );
120
- fs.writeFileSync(filePath, code, 'utf8');
121
- console.log('Patched app/(public)/trade-in/page.tsx');
122
- }
123
23
 
124
- // 4. Patch app/(public)/about/page.tsx
125
- {
126
- const filePath = path.join(rootDir, 'app', '(public)', 'about', 'page.tsx');
127
- let code = fs.readFileSync(filePath, 'utf8');
128
- if (!code.includes('EditableIcon')) {
129
- code = 'import { EditableIcon } from "@/src/components/ui/EditableIcon";\n' + code;
24
+ const siteDataPath = path.join(projectDir, 'data', 'site-data.json');
25
+ if (!fs.existsSync(siteDataPath)) {
26
+ return;
130
27
  }
131
- code = code.replace(
132
- /<Icon className="mb-4 size-6 text-primary" aria-hidden data-preview-static="decorative-icon" \/>/g,
133
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 text-primary mb-4" data-preview-field-path={`about.principle${PRINCIPLES.findIndex(p => p.title === title) + 1}Icon`} title="Click to edit principle icon"><EditableIcon name={(siteData?.content?.about as any)?.[`principle${PRINCIPLES.findIndex(p => p.title === title) + 1}Icon`] ?? ["eye", "scale", "message-circle", "file-search"][PRINCIPLES.findIndex(p => p.title === title)]} className="size-6" /></span>'
134
- );
135
- fs.writeFileSync(filePath, code, 'utf8');
136
- console.log('Patched app/(public)/about/page.tsx');
137
- }
138
28
 
139
- // 5. Patch app/(public)/source-a-car/page.tsx
140
- {
141
- const filePath = path.join(rootDir, 'app', '(public)', 'source-a-car', 'page.tsx');
142
- let code = fs.readFileSync(filePath, 'utf8');
143
- if (!code.includes('EditableIcon')) {
144
- code = 'import { EditableIcon } from "@/src/components/ui/EditableIcon";\n' + code;
29
+ let siteData;
30
+ try {
31
+ siteData = JSON.parse(fs.readFileSync(siteDataPath, 'utf8'));
32
+ } catch {
33
+ return;
145
34
  }
146
- code = code.replace(
147
- /<Icon className="size-5" aria-hidden data-preview-static="decorative-icon" \/>/g,
148
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 text-primary" data-preview-field-path={`source-a-car.step${i + 1}Icon`} title="Click to edit step icon"><EditableIcon name={(siteData?.content?.["source-a-car"] as any)?.[`step${i + 1}Icon`] ?? ["clipboard-list", "message-circle", "search"][i]} className="size-5" /></span>'
149
- );
150
- fs.writeFileSync(filePath, code, 'utf8');
151
- console.log('Patched app/(public)/source-a-car/page.tsx');
152
- }
153
35
 
154
- // 6. Patch components/vehicles/compare-view.tsx
155
- {
156
- const filePath = path.join(rootDir, 'components', 'vehicles', 'compare-view.tsx');
157
- let code = fs.readFileSync(filePath, 'utf8');
158
- if (!code.includes('EditableIcon')) {
159
- code = 'import { EditableIcon } from "@/src/components/ui/EditableIcon";\n' + code;
160
- }
161
- code = code.replace(
162
- /<TrophyIcon className="mt-0.5 size-5 shrink-0 text-primary" aria-hidden data-preview-static="decorative-icon" \/>/g,
163
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 mt-0.5 shrink-0 text-primary" data-preview-field-path="compare.trophyIcon" title="Click to edit trophy icon"><EditableIcon name={(siteData?.content?.compare as any)?.trophyIcon ?? "trophy"} className="size-5" /></span>'
164
- );
165
- fs.writeFileSync(filePath, code, 'utf8');
166
- console.log('Patched components/vehicles/compare-view.tsx');
167
- }
36
+ if (!siteData.content) siteData.content = {};
37
+ if (!siteData.content.common) siteData.content.common = {};
168
38
 
169
- // 7. Patch components/whatsapp/trade-in-form.tsx
170
- {
171
- const filePath = path.join(rootDir, 'components', 'whatsapp', 'trade-in-form.tsx');
172
- let code = fs.readFileSync(filePath, 'utf8');
173
- if (!code.includes('EditableIcon')) {
174
- code = 'import { EditableIcon } from "@/src/components/ui/EditableIcon";\n' + code;
175
- }
176
- code = code.replace(
177
- /<CameraIcon className="mt-0.5 size-4 shrink-0 text-primary" aria-hidden data-preview-static="decorative-icon" \/>/g,
178
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 mt-0.5 shrink-0 text-primary" data-preview-field-path="trade-in.cameraIcon" title="Click to edit camera icon"><EditableIcon name={(siteData?.content?.["trade-in"] as any)?.cameraIcon ?? "camera"} className="size-4" /></span>'
179
- );
180
- fs.writeFileSync(filePath, code, 'utf8');
181
- console.log('Patched components/whatsapp/trade-in-form.tsx');
182
- }
39
+ setDeep(siteData.content, ['common', 'whatsapp', 'copyIcon'], 'copy');
40
+ setDeep(siteData.content, ['common', 'whatsapp', 'icon'], 'message-circle');
41
+ setDeep(siteData.content, ['common', 'search', 'searchIcon'], 'search');
183
42
 
184
- // 8. Patch components/whatsapp/whatsapp-provider.tsx
185
- {
186
- const filePath = path.join(rootDir, 'components', 'whatsapp', 'whatsapp-provider.tsx');
187
- let code = fs.readFileSync(filePath, 'utf8');
188
- if (!code.includes('EditableIcon')) {
189
- code = 'import { EditableIcon } from "@/src/components/ui/EditableIcon";\n' + code;
190
- }
191
- code = code.replace(
192
- /<CopyIcon data-icon="inline-start" data-preview-static="decorative-icon" \/>/g,
193
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 mr-1.5" data-preview-field-path="common.whatsapp.copyIcon" title="Click to edit copy icon"><EditableIcon name={(siteData?.content?.common as any)?.whatsapp?.copyIcon ?? "copy"} className="size-4" /></span>'
194
- );
195
- fs.writeFileSync(filePath, code, 'utf8');
196
- console.log('Patched components/whatsapp/whatsapp-provider.tsx');
197
- }
198
-
199
- // 9. Patch components/vehicles/quick-search.tsx
200
- {
201
- const filePath = path.join(rootDir, 'components', 'vehicles', 'quick-search.tsx');
202
- let code = fs.readFileSync(filePath, 'utf8');
203
- if (!code.includes('EditableIcon')) {
204
- code = 'import { EditableIcon } from "@/src/components/ui/EditableIcon";\nimport { useSiteData } from "@deneb-ui/ui";\n' + code;
205
- }
206
- if (!code.includes('const siteData = useSiteData();')) {
207
- code = code.replace(
208
- /(export function QuickSearch\([^)]*\)\s*\{)/,
209
- '$1\n const siteData = useSiteData();'
210
- );
43
+ if (!dryRun) {
44
+ try {
45
+ fs.writeFileSync(siteDataPath, JSON.stringify(siteData, null, 2), 'utf8');
46
+ log('[universal-icons] Registered common icon keys in site-data.json');
47
+ } catch (err) {
48
+ log('[universal-icons] Warning: failed to save site-data.json: ' + err.message);
49
+ }
211
50
  }
212
- code = code.replace(
213
- /<SearchIcon data-icon="inline-start" data-preview-static="decorative-icon" \/>/g,
214
- '<span className="inline-flex cursor-pointer transition-transform hover:scale-110 mr-1.5" data-preview-field-path="common.search.searchIcon" title="Click to edit search icon"><EditableIcon name={(siteData?.content?.common as any)?.search?.searchIcon ?? "search"} className="size-4" /></span>'
215
- );
216
- fs.writeFileSync(filePath, code, 'utf8');
217
- console.log('Patched components/vehicles/quick-search.tsx');
218
51
  }
219
52
 
220
- console.log('All files successfully updated with EditableIcon!');
53
+ module.exports = {
54
+ patchUniversalIcons,
55
+ };
@@ -519,10 +519,32 @@ export function findLinksToUnselectedPages(input: {
519
519
  return findings;
520
520
  }
521
521
 
522
+ /** Detect links that contain the configured base path but omit its leading
523
+ * slash. Browsers resolve these relative to the current page and duplicate the
524
+ * generated-site prefix after navigation. */
525
+ export function findMalformedBasePathLinks(input: {
526
+ html: string;
527
+ basePath: string;
528
+ }) {
529
+ const prefix = input.basePath.replace(/^\/+|\/+$/g, '');
530
+ if (!prefix) return [];
531
+ const findings = new Set<string>();
532
+ for (const match of input.html.matchAll(
533
+ /<[a-z][^>]*\b(?:href|formaction|data-href|data-route|data-url)\s*=\s*(?:"([^"]*)"|'([^']*)')[^>]*>/gi,
534
+ )) {
535
+ const value = (match[1] ?? match[2] ?? '').trim();
536
+ const path = value.split(/[?#]/, 1)[0].replace(/^\.\//, '');
537
+ if (path === prefix || path.startsWith(`${prefix}/`)) {
538
+ findings.add(value);
539
+ }
540
+ }
541
+ return [...findings];
542
+ }
543
+
522
544
  /**
523
545
  * Next's client router applies next.config `basePath` automatically. Wrapping
524
- * a router destination in a helper that also prepends the base path duplicates
525
- * the generated-site prefix at runtime.
546
+ * a router destination in a package helper that also prepends the base path
547
+ * duplicates the generated-site prefix at runtime.
526
548
  */
527
549
  export function findDoubleBasePathNextRouterCalls(source: string) {
528
550
  const findings: Array<{