@deneb-ui/cli 2.0.54 → 2.0.56
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 +7 -0
- package/package.json +5 -2
- package/src/arc/fivora-contract.cjs +10 -0
- package/src/arc/manifest.cjs +78 -1
- package/src/arc/transformer.cjs +67 -0
- package/src/platform/platform-contract.json +39 -0
- package/src/recipes/mobile-shop.json +2651 -0
- package/src/tools/deneb-doctor.cjs +210 -63
- package/src/tools/deneb-template-validator.cjs +6 -6
- package/src/tools/template-preview-focus-bridge.cjs +1 -1
|
@@ -133,6 +133,8 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
133
133
|
};
|
|
134
134
|
|
|
135
135
|
function addCheck(suiteName, type, title, detail, meta = {}) {
|
|
136
|
+
const code = meta.code || 'DNB-GEN-001';
|
|
137
|
+
const action = meta.action || detail || '';
|
|
136
138
|
if (type === 'pass') reportData.passed++;
|
|
137
139
|
else if (type === 'warn') reportData.warnings++;
|
|
138
140
|
else if (type === 'err') reportData.errors++;
|
|
@@ -146,17 +148,19 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
146
148
|
suite = { name: suiteName, checks: [] };
|
|
147
149
|
reportData.suites.push(suite);
|
|
148
150
|
}
|
|
149
|
-
|
|
151
|
+
const checkObj = { code, type, title, detail, action, ...meta };
|
|
152
|
+
suite.checks.push(checkObj);
|
|
150
153
|
|
|
151
154
|
if (!isJson) {
|
|
155
|
+
const codeTag = `\x1b[36m[${code}]\x1b[0m `;
|
|
152
156
|
if (type === 'pass') {
|
|
153
|
-
console.log(` \x1b[32m✔\x1b[0m \x1b[1m${title}\x1b[0m${detail ? ` \x1b[90m(${detail})\x1b[0m` : ''}`);
|
|
157
|
+
console.log(` \x1b[32m✔\x1b[0m ${codeTag}\x1b[1m${title}\x1b[0m${detail ? ` \x1b[90m(${detail})\x1b[0m` : ''}`);
|
|
154
158
|
} else if (type === 'fixed') {
|
|
155
|
-
console.log(` \x1b[35m⚡ FIXED:\x1b[0m \x1b[1m${title}\x1b[0m${detail ? ` \x1b[32m- ${detail}\x1b[0m` : ''}`);
|
|
159
|
+
console.log(` \x1b[35m⚡ FIXED:\x1b[0m ${codeTag}\x1b[1m${title}\x1b[0m${detail ? ` \x1b[32m- ${detail}\x1b[0m` : ''}`);
|
|
156
160
|
} else if (type === 'warn') {
|
|
157
|
-
console.log(` \x1b[33m⚠\x1b[0m \x1b[33m${title}\x1b[0m${detail ? ` \x1b[90m- ${detail}\x1b[0m` : ''}`);
|
|
161
|
+
console.log(` \x1b[33m⚠\x1b[0m ${codeTag}\x1b[33m${title}\x1b[0m${detail ? ` \x1b[90m- ${detail}\x1b[0m` : ''}`);
|
|
158
162
|
} else {
|
|
159
|
-
console.log(` \x1b[31m✖\x1b[0m \x1b[31m${title}\x1b[0m${detail ? ` \x1b[90m- ${detail}\x1b[0m` : ''}`);
|
|
163
|
+
console.log(` \x1b[31m✖\x1b[0m ${codeTag}\x1b[31m${title}\x1b[0m${detail ? ` \x1b[90m- ${detail}\x1b[0m` : ''}`);
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
166
|
}
|
|
@@ -178,17 +182,17 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
178
182
|
const nodeVersion = process.version;
|
|
179
183
|
const majorNode = parseInt(nodeVersion.replace(/^v/, '').split('.')[0], 10);
|
|
180
184
|
if (majorNode >= 18) {
|
|
181
|
-
addCheck(suite1, 'pass', 'Node.js Runtime', `${nodeVersion} (Supported)`);
|
|
185
|
+
addCheck(suite1, 'pass', 'Node.js Runtime', `${nodeVersion} (Supported)`, { code: 'DNB-SYS-001' }) //, `${nodeVersion} (Supported)`);
|
|
182
186
|
} else {
|
|
183
|
-
addCheck(suite1, 'err', 'Node.js Runtime', `${nodeVersion} (Requires Node.js >= 18.0.0)`);
|
|
187
|
+
addCheck(suite1, 'err', 'Node.js Runtime', `${nodeVersion} (Requires Node.js >= 18.0.0)`, { code: 'DNB-SYS-001' }) //, `${nodeVersion} (Requires Node.js >= 18.0.0)`);
|
|
184
188
|
}
|
|
185
189
|
|
|
186
190
|
const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
187
191
|
const npmCheck = spawnSync(npmBin, ['--version'], { encoding: 'utf-8', shell: process.platform === 'win32' });
|
|
188
192
|
if (!npmCheck.error && npmCheck.status === 0) {
|
|
189
|
-
addCheck(suite1, 'pass', 'Package Manager', `npm v${npmCheck.stdout.trim()}`);
|
|
193
|
+
addCheck(suite1, 'pass', 'Package Manager', `npm v${npmCheck.stdout.trim()}`, { code: 'DNB-SYS-002' }) //, `npm v${npmCheck.stdout.trim()}`);
|
|
190
194
|
} else {
|
|
191
|
-
addCheck(suite1, 'warn', 'Package Manager', 'npm not found in system PATH');
|
|
195
|
+
addCheck(suite1, 'warn', 'Package Manager', 'npm not found in system PATH', { code: 'DNB-SYS-002' }) //, 'npm not found in system PATH');
|
|
192
196
|
}
|
|
193
197
|
|
|
194
198
|
// =========================================================================
|
|
@@ -202,26 +206,26 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
202
206
|
if (fs.existsSync(pkgPath)) {
|
|
203
207
|
try {
|
|
204
208
|
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
205
|
-
addCheck(suite2, 'pass', 'package.json', `Found "${pkg.name || 'unnamed'}"`);
|
|
209
|
+
addCheck(suite2, 'pass', 'package.json', `Found "${pkg.name || 'unnamed'}"`, { code: 'DNB-PKG-001' }) //, `Found "${pkg.name || 'unnamed'}"`);
|
|
206
210
|
|
|
207
211
|
const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
208
212
|
|
|
209
213
|
if (allDeps['next']) {
|
|
210
|
-
addCheck(suite2, 'pass', 'Next.js Framework', allDeps['next']);
|
|
214
|
+
addCheck(suite2, 'pass', 'Next.js Framework', allDeps['next'], { code: 'DNB-PKG-002' }) //, allDeps['next']);
|
|
211
215
|
} else {
|
|
212
|
-
addCheck(suite2, 'err', 'Next.js Framework', 'next dependency missing in package.json');
|
|
216
|
+
addCheck(suite2, 'err', 'Next.js Framework', 'next dependency missing in package.json', { code: 'DNB-PKG-002' }) //, 'next dependency missing in package.json');
|
|
213
217
|
}
|
|
214
218
|
|
|
215
219
|
if (allDeps['@deneb-ui/ui'] || allDeps['@deneb/ui']) {
|
|
216
|
-
addCheck(suite2, 'pass', '@deneb-ui/ui Library', allDeps['@deneb-ui/ui'] || allDeps['@deneb/ui']);
|
|
220
|
+
addCheck(suite2, 'pass', '@deneb-ui/ui Library', allDeps['@deneb-ui/ui'] || allDeps['@deneb/ui'], { code: 'DNB-PKG-003' }) //, allDeps['@deneb-ui/ui'] || allDeps['@deneb/ui']);
|
|
217
221
|
} else {
|
|
218
|
-
addCheck(suite2, 'warn', '@deneb-ui/ui Library', 'Not installed (run "npm i @deneb-ui/ui")');
|
|
222
|
+
addCheck(suite2, 'warn', '@deneb-ui/ui Library', 'Not installed (run "npm i @deneb-ui/ui")', { code: 'DNB-PKG-003' }) //, 'Not installed (run "npm i @deneb-ui/ui")');
|
|
219
223
|
}
|
|
220
224
|
|
|
221
225
|
if (allDeps['@deneb-ui/cli']) {
|
|
222
|
-
addCheck(suite2, 'pass', '@deneb-ui/cli Tooling', allDeps['@deneb-ui/cli']);
|
|
226
|
+
addCheck(suite2, 'pass', '@deneb-ui/cli Tooling', allDeps['@deneb-ui/cli'], { code: 'DNB-PKG-004' }) //, allDeps['@deneb-ui/cli']);
|
|
223
227
|
} else {
|
|
224
|
-
addCheck(suite2, 'warn', '@deneb-ui/cli Tooling', 'Recommended for local CLI scripts');
|
|
228
|
+
addCheck(suite2, 'warn', '@deneb-ui/cli Tooling', 'Recommended for local CLI scripts', { code: 'DNB-PKG-004' }) //, 'Recommended for local CLI scripts');
|
|
225
229
|
}
|
|
226
230
|
|
|
227
231
|
// Check required scripts
|
|
@@ -230,16 +234,57 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
230
234
|
const missingScripts = requiredScripts.filter((s) => !pkg.scripts[s]);
|
|
231
235
|
|
|
232
236
|
if (missingScripts.length === 0) {
|
|
233
|
-
addCheck(suite2, 'pass', 'DENEB Package Scripts', 'lab, validate, zip, validate-and-zip verified');
|
|
237
|
+
addCheck(suite2, 'pass', 'DENEB Package Scripts', 'lab, validate, zip, validate-and-zip verified', { code: 'DNB-PKG-005' }) //, 'lab, validate, zip, validate-and-zip verified');
|
|
234
238
|
} else if (shouldFix) {
|
|
235
239
|
pkg.scripts['lab'] = pkg.scripts['lab'] || 'deneb lab .';
|
|
236
240
|
pkg.scripts['validate'] = pkg.scripts['validate'] || 'deneb validate .';
|
|
237
241
|
pkg.scripts['zip'] = pkg.scripts['zip'] || 'deneb zip .';
|
|
238
242
|
pkg.scripts['validate-and-zip'] = pkg.scripts['validate-and-zip'] || 'deneb validate-and-zip .';
|
|
239
243
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
240
|
-
addCheck(suite2, 'fixed', 'DENEB Package Scripts', `Injected missing scripts: ${missingScripts.join(', ')}`);
|
|
244
|
+
addCheck(suite2, 'fixed', 'DENEB Package Scripts', `Injected missing scripts: ${missingScripts.join(', ')}`, { code: 'DNB-PKG-005' }) //, `Injected missing scripts: ${missingScripts.join(', ')}`);
|
|
241
245
|
} else {
|
|
242
|
-
addCheck(suite2, 'warn', 'DENEB Package Scripts', `Missing scripts: ${missingScripts.join(', ')} (Run with --fix to repair)`);
|
|
246
|
+
addCheck(suite2, 'warn', 'DENEB Package Scripts', `Missing scripts: ${missingScripts.join(', ')} (Run with --fix to repair)`, { code: 'DNB-PKG-005' }) //, `Missing scripts: ${missingScripts.join(', ')} (Run with --fix to repair)`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Check Node 20 LTS platform engine compatibility
|
|
250
|
+
const lockPath = path.join(targetDir, 'package-lock.json');
|
|
251
|
+
let hasNode22EngineConflict = false;
|
|
252
|
+
const checkNode20 = (e) => {
|
|
253
|
+
if (!e || e === '*' || e === 'latest') return true;
|
|
254
|
+
return e.split('||').map(n => n.trim()).some(n => {
|
|
255
|
+
let r = n.match(/>=\s*(\d+)/);
|
|
256
|
+
if (r) return parseInt(r[1], 10) <= 20;
|
|
257
|
+
let i = n.match(/\^\s*(\d+)/);
|
|
258
|
+
if (i) return parseInt(i[1], 10) <= 20;
|
|
259
|
+
let s = n.match(/~(\d+)/);
|
|
260
|
+
if (s) return parseInt(s[1], 10) <= 20;
|
|
261
|
+
let o = n.match(/^\s*(\d+)/);
|
|
262
|
+
if (o) return parseInt(o[1], 10) <= 20;
|
|
263
|
+
return false;
|
|
264
|
+
});
|
|
265
|
+
};
|
|
266
|
+
if (fs.existsSync(lockPath)) {
|
|
267
|
+
try {
|
|
268
|
+
const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
|
|
269
|
+
for (const [pkgKey, pkgVal] of Object.entries(lock.packages || {})) {
|
|
270
|
+
const engineNode = pkgVal?.engines?.node;
|
|
271
|
+
if (engineNode && typeof engineNode === 'string' && !checkNode20(engineNode)) {
|
|
272
|
+
hasNode22EngineConflict = true;
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} catch {}
|
|
277
|
+
}
|
|
278
|
+
if (!hasNode22EngineConflict) {
|
|
279
|
+
addCheck(suite2, 'pass', 'Platform Engine Compatibility', 'All dependencies compatible with Fivora Node 20 LTS runtime', { code: 'DNB-ENG-001' }) //, 'All dependencies compatible with Fivora Node 20 LTS runtime');
|
|
280
|
+
} else if (shouldFix) {
|
|
281
|
+
pkg.overrides = pkg.overrides || {};
|
|
282
|
+
pkg.overrides['content-type'] = '2.1.0';
|
|
283
|
+
pkg.overrides['@octokit/request'] = { 'content-type': '2.1.0' };
|
|
284
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
285
|
+
addCheck(suite2, 'fixed', 'Platform Engine Compatibility', 'Injected Node 20 overrides for content-type: 2.1.0', { code: 'DNB-ENG-001' }) //, 'Injected Node 20 overrides for content-type: 2.1.0');
|
|
286
|
+
} else {
|
|
287
|
+
addCheck(suite2, 'err', 'Platform Engine Compatibility', 'Detected packages requiring Node >=22. Run "deneb doctor --fix" to inject Node 20 overrides.', { code: 'DNB-ENG-001' }) //, 'Detected packages requiring Node >=22. Run "deneb doctor --fix" to inject Node 20 overrides.');
|
|
243
288
|
}
|
|
244
289
|
} catch (e) {
|
|
245
290
|
addCheck(suite2, 'err', 'package.json Syntax', e.message);
|
|
@@ -263,21 +308,21 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
263
308
|
const hasUnoptimized = /unoptimized\s*:\s*true/.test(content);
|
|
264
309
|
|
|
265
310
|
if (hasExport) {
|
|
266
|
-
addCheck(suite3, 'pass', 'Next.js Static Export', `output: 'export' verified in ${path.basename(nextConfigPath)}`);
|
|
311
|
+
addCheck(suite3, 'pass', 'Next.js Static Export', `output: 'export' verified in ${path.basename(nextConfigPath)}`, { code: 'DNB-EXP-001' }) //, `output: 'export' verified in ${path.basename(nextConfigPath)}`);
|
|
267
312
|
} else if (shouldFix) {
|
|
268
313
|
if (content.includes('nextConfig')) {
|
|
269
314
|
content = content.replace(/(const\s+nextConfig\s*=\s*{)/, `$1\n output: 'export',`);
|
|
270
315
|
fs.writeFileSync(nextConfigPath, content, 'utf8');
|
|
271
|
-
addCheck(suite3, 'fixed', 'Next.js Static Export', `Added output: 'export' to ${path.basename(nextConfigPath)}`);
|
|
316
|
+
addCheck(suite3, 'fixed', 'Next.js Static Export', `Added output: 'export' to ${path.basename(nextConfigPath)}`, { code: 'DNB-EXP-001' }) //, `Added output: 'export' to ${path.basename(nextConfigPath)}`);
|
|
272
317
|
} else {
|
|
273
|
-
addCheck(suite3, 'err', 'Next.js Static Export', `Missing output: 'export' in ${path.basename(nextConfigPath)}`);
|
|
318
|
+
addCheck(suite3, 'err', 'Next.js Static Export', `Missing output: 'export' in ${path.basename(nextConfigPath)} (Required by Fivora)`, { code: 'DNB-EXP-001' }) //, `Missing output: 'export' in ${path.basename(nextConfigPath)}`);
|
|
274
319
|
}
|
|
275
320
|
} else {
|
|
276
321
|
addCheck(suite3, 'err', 'Next.js Static Export', `Missing output: 'export' in ${path.basename(nextConfigPath)} (Required by Fivora)`);
|
|
277
322
|
}
|
|
278
323
|
|
|
279
324
|
if (hasUnoptimized) {
|
|
280
|
-
addCheck(suite3, 'pass', 'Image Optimization Preflight', 'images.unoptimized = true verified');
|
|
325
|
+
addCheck(suite3, 'pass', 'Image Optimization Preflight', 'images.unoptimized = true verified', { code: 'DNB-IMG-001' }) //, 'images.unoptimized = true verified');
|
|
281
326
|
} else if (shouldFix) {
|
|
282
327
|
if (content.includes('images:')) {
|
|
283
328
|
content = content.replace(/images:\s*{/, `images: { unoptimized: true, `);
|
|
@@ -285,9 +330,9 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
285
330
|
content = content.replace(/(const\s+nextConfig\s*=\s*{)/, `$1\n images: { unoptimized: true },`);
|
|
286
331
|
}
|
|
287
332
|
fs.writeFileSync(nextConfigPath, content, 'utf8');
|
|
288
|
-
addCheck(suite3, 'fixed', 'Image Optimization Preflight', `Added images.unoptimized = true to ${path.basename(nextConfigPath)}`);
|
|
333
|
+
addCheck(suite3, 'fixed', 'Image Optimization Preflight', `Added images.unoptimized = true to ${path.basename(nextConfigPath)}`, { code: 'DNB-IMG-001' }) //, `Added images.unoptimized = true to ${path.basename(nextConfigPath)}`);
|
|
289
334
|
} else {
|
|
290
|
-
addCheck(suite3, 'warn', 'Image Optimization Preflight', 'Missing images.unoptimized = true (Next.js Image export requires unoptimized: true)');
|
|
335
|
+
addCheck(suite3, 'warn', 'Image Optimization Preflight', 'Missing images.unoptimized = true (Next.js Image export requires unoptimized: true)', { code: 'DNB-IMG-001' }) //, 'Missing images.unoptimized = true (Next.js Image export requires unoptimized: true)');
|
|
291
336
|
}
|
|
292
337
|
} else {
|
|
293
338
|
addCheck(suite3, 'err', 'Next.js Config', 'No next.config.ts, next.config.mjs, or next.config.js found');
|
|
@@ -305,21 +350,21 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
305
350
|
if (fs.existsSync(manifestPath)) {
|
|
306
351
|
try {
|
|
307
352
|
manifestData = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
308
|
-
addCheck(suite4, 'pass', 'fivora-template.json', `Valid JSON (strict=${manifestData.strict !== false})`);
|
|
353
|
+
addCheck(suite4, 'pass', 'fivora-template.json', `Valid JSON (strict=${manifestData.strict !== false})`, { code: 'DNB-MNF-001' }) //, `Valid JSON (strict=${manifestData.strict !== false})`);
|
|
309
354
|
|
|
310
355
|
if (manifestData.version === 2 || manifestData.version === '2') {
|
|
311
|
-
addCheck(suite4, 'pass', 'Manifest Contract Version', 'Version 2 (Current standard)');
|
|
356
|
+
addCheck(suite4, 'pass', 'Manifest Contract Version', 'Version 2 (Current standard)', { code: 'DNB-MNF-002' }) //, 'Version 2 (Current standard)');
|
|
312
357
|
} else {
|
|
313
|
-
addCheck(suite4, 'warn', 'Manifest Contract Version', `Version ${manifestData.version} detected (Recommend version 2)`);
|
|
358
|
+
addCheck(suite4, 'warn', 'Manifest Contract Version', `Version ${manifestData.version} detected (Recommend version 2)`, { code: 'DNB-MNF-002' }) //, `Version ${manifestData.version} detected (Recommend version 2)`);
|
|
314
359
|
}
|
|
315
360
|
|
|
316
361
|
// Check home route
|
|
317
362
|
const pages = Array.isArray(manifestData.pages) ? manifestData.pages : [];
|
|
318
363
|
const hasHome = pages.some((p) => p.route === '/' || p.slug === '/' || p.path === '/' || p.id === 'home');
|
|
319
364
|
if (hasHome) {
|
|
320
|
-
addCheck(suite4, 'pass', 'Home Route Entry', 'Home page ("/") declared in manifest');
|
|
365
|
+
addCheck(suite4, 'pass', 'Home Route Entry', 'Home page ("/") declared in manifest', { code: 'DNB-MNF-003' }) //, 'Home page ("/") declared in manifest');
|
|
321
366
|
} else {
|
|
322
|
-
addCheck(suite4, 'err', 'Home Route Entry', 'Manifest pages array missing root route: "/"');
|
|
367
|
+
addCheck(suite4, 'err', 'Home Route Entry', 'Manifest pages array missing root route: "/"', { code: 'DNB-MNF-003' }) //, 'Manifest pages array missing root route: "/"');
|
|
323
368
|
}
|
|
324
369
|
|
|
325
370
|
// Route coherence check: verify declared manifest routes exist on filesystem
|
|
@@ -344,9 +389,9 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
344
389
|
}
|
|
345
390
|
|
|
346
391
|
if (missingDiskRoutes.length === 0) {
|
|
347
|
-
addCheck(suite4, 'pass', 'Route Coherence', `All ${pages.length} declared routes verified against filesystem`);
|
|
392
|
+
addCheck(suite4, 'pass', 'Route Coherence', `All ${pages.length} declared routes verified against filesystem`, { code: 'DNB-RTE-001' }) //, `All ${pages.length} declared routes verified against filesystem`);
|
|
348
393
|
} else {
|
|
349
|
-
addCheck(suite4, 'warn', 'Route Coherence', `Declared routes missing corresponding files on disk: ${missingDiskRoutes.join(', ')}`);
|
|
394
|
+
addCheck(suite4, 'warn', 'Route Coherence', `Declared routes missing corresponding files on disk: ${missingDiskRoutes.join(', ')}`, { code: 'DNB-RTE-001' }) //, `Declared routes missing corresponding files on disk: ${missingDiskRoutes.join(', ')}`);
|
|
350
395
|
}
|
|
351
396
|
} catch (e) {
|
|
352
397
|
addCheck(suite4, 'err', 'fivora-template.json Syntax', e.message);
|
|
@@ -366,7 +411,7 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
366
411
|
if (fs.existsSync(siteDataPath)) {
|
|
367
412
|
try {
|
|
368
413
|
siteData = JSON.parse(fs.readFileSync(siteDataPath, 'utf-8'));
|
|
369
|
-
addCheck(suite5, 'pass', 'site-data.json', 'src/data/site-data.json exists & valid');
|
|
414
|
+
addCheck(suite5, 'pass', 'site-data.json', 'src/data/site-data.json exists & valid', { code: 'DNB-SYN-001' }) //, 'src/data/site-data.json exists & valid');
|
|
370
415
|
} catch (e) {
|
|
371
416
|
addCheck(suite5, 'err', 'site-data.json Syntax', e.message);
|
|
372
417
|
}
|
|
@@ -408,15 +453,30 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
408
453
|
}
|
|
409
454
|
}
|
|
410
455
|
|
|
411
|
-
// 2. Action URL vs Visible Text Collision
|
|
412
|
-
//
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
456
|
+
// 2. Action URL vs Visible Text Collision (DNB-ACT-004)
|
|
457
|
+
// Only flag if element carries an action URL and has child text WITHOUT a dedicated child label marker
|
|
458
|
+
const actionCollisions = code.matchAll(/<(a|button)(\s+[^>]*data-preview-field-path="[^"]*(?:Url|Link|Action)"[^>]*)>([\s\S]*?)<\/\1>/gi);
|
|
459
|
+
let fileNeedsActionFix = false;
|
|
460
|
+
let newCodeAction = code;
|
|
461
|
+
for (const ac of actionCollisions) {
|
|
462
|
+
const innerContent = ac[3].trim();
|
|
463
|
+
// If innerContent has its own data-preview-field-path, it is decoupled!
|
|
464
|
+
if (innerContent.length > 0 && !/\bdata-preview-field-path\s*=/.test(innerContent)) {
|
|
465
|
+
const textOnly = innerContent.replace(/<[^>]*>/g, '').trim();
|
|
466
|
+
if (textOnly.length > 1) {
|
|
467
|
+
actionTextCollisions++;
|
|
468
|
+
if (shouldFix) {
|
|
469
|
+
const originalOpening = `<${ac[1]}${ac[2]}>`;
|
|
470
|
+
const cleanOpening = originalOpening.replace(/\s*data-preview-field-path="[^"]*"/g, '');
|
|
471
|
+
newCodeAction = newCodeAction.replace(originalOpening, cleanOpening);
|
|
472
|
+
fileNeedsActionFix = true;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
418
475
|
}
|
|
419
476
|
}
|
|
477
|
+
if (shouldFix && fileNeedsActionFix) {
|
|
478
|
+
fs.writeFileSync(file, newCodeAction, 'utf8');
|
|
479
|
+
}
|
|
420
480
|
|
|
421
481
|
// 3. Static Ancestor Collision
|
|
422
482
|
// Detect element with data-preview-static wrapping element with data-preview-field-path
|
|
@@ -475,16 +535,16 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
475
535
|
}
|
|
476
536
|
}
|
|
477
537
|
|
|
478
|
-
addCheck(suite5, 'pass', 'Field Path Scan', `${foundFieldPaths.size} visual editing field markers scanned across ${sourceFiles.length} files`);
|
|
538
|
+
addCheck(suite5, 'pass', 'Field Path Scan', `${foundFieldPaths.size} visual editing field markers scanned across ${sourceFiles.length} files`, { code: 'DNB-AST-001' }) //, `${foundFieldPaths.size} visual editing field markers scanned across ${sourceFiles.length} files`);
|
|
479
539
|
|
|
480
540
|
if (orphanPaths.length === 0) {
|
|
481
|
-
addCheck(suite5, 'pass', 'Content Synchronization', 'All source field paths exist in site-data.json');
|
|
541
|
+
addCheck(suite5, 'pass', 'Content Synchronization', 'All source field paths exist in site-data.json', { code: 'DNB-SYN-002' }) //, 'All source field paths exist in site-data.json');
|
|
482
542
|
} else {
|
|
483
|
-
addCheck(suite5, 'warn', 'Content Synchronization', `${orphanPaths.length} field paths not found in site-data.json (e.g. ${orphanPaths[0].fieldPath})`);
|
|
543
|
+
addCheck(suite5, 'warn', 'Content Synchronization', `${orphanPaths.length} field paths not found in site-data.json`, { code: 'DNB-SYN-002' }) //, `${orphanPaths.length} field paths not found in site-data.json (e.g. ${orphanPaths[0].fieldPath})`);
|
|
484
544
|
}
|
|
485
545
|
|
|
486
546
|
if (missingInSchema.length === 0) {
|
|
487
|
-
addCheck(suite5, 'pass', 'Schema Synchronization', 'All source field paths declared in fivora-template.json editorSchema');
|
|
547
|
+
addCheck(suite5, 'pass', 'Schema Synchronization', 'All source field paths declared in fivora-template.json editorSchema', { code: 'DNB-SCH-001' }) //, 'All source field paths declared in fivora-template.json editorSchema');
|
|
488
548
|
} else if (shouldFix && manifestData) {
|
|
489
549
|
// Auto-fix missing schema entries
|
|
490
550
|
let fixedSchemaFields = 0;
|
|
@@ -513,49 +573,114 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
513
573
|
}
|
|
514
574
|
|
|
515
575
|
fs.writeFileSync(manifestPath, JSON.stringify(manifestData, null, 2) + '\n', 'utf8');
|
|
516
|
-
addCheck(suite5, 'fixed', 'Schema Synchronization', `Added ${fixedSchemaFields} missing field definitions to editorSchema`);
|
|
576
|
+
addCheck(suite5, 'fixed', 'Schema Synchronization', `Added ${fixedSchemaFields} missing field definitions to editorSchema`, { code: 'DNB-SCH-001' }) //, `Added ${fixedSchemaFields} missing field definitions to editorSchema`);
|
|
517
577
|
} else {
|
|
518
|
-
addCheck(suite5, 'warn', 'Schema Synchronization', `${missingInSchema.length} field paths missing in fivora-template.json (Run with --fix to register automatically)`);
|
|
578
|
+
addCheck(suite5, 'warn', 'Schema Synchronization', `${missingInSchema.length} field paths missing in fivora-template.json (Run with --fix to register automatically)`, { code: 'DNB-SCH-001' }) //, `${missingInSchema.length} field paths missing in fivora-template.json (Run with --fix to register automatically)`);
|
|
519
579
|
}
|
|
520
580
|
|
|
521
581
|
if (actionTextCollisions === 0) {
|
|
522
|
-
addCheck(suite5, 'pass', 'Action vs Text Contracts', 'Zero URL vs text label collisions detected on interactive links');
|
|
582
|
+
addCheck(suite5, 'pass', 'Action vs Text Contracts', 'Zero URL vs text label collisions detected on interactive links', { code: 'DNB-ACT-004' }) //, 'Zero URL vs text label collisions detected on interactive links');
|
|
583
|
+
} else if (shouldFix) {
|
|
584
|
+
addCheck(suite5, 'fixed', 'Action vs Text Contracts', `Stripped ${actionTextCollisions} action URL marker(s) from container elements to protect child labels`, { code: 'DNB-ACT-004' }) //, `Stripped ${actionTextCollisions} action URL marker(s) from container elements to protect child labels`);
|
|
523
585
|
} else {
|
|
524
|
-
addCheck(suite5, 'warn', 'Action vs Text Contracts', `${actionTextCollisions} potential action URL/label conflict(s) (Split URL marker on <a> and text on <span>)`);
|
|
586
|
+
addCheck(suite5, 'warn', 'Action vs Text Contracts', `${actionTextCollisions} potential action URL/label conflict(s) (Split URL marker on <a>/<button> and text on <span>)`, { code: 'DNB-ACT-004' }) //, `${actionTextCollisions} potential action URL/label conflict(s) (Split URL marker on <a>/<button> and text on <span>)`);
|
|
525
587
|
}
|
|
526
588
|
|
|
527
589
|
if (staticAncestorCollisions === 0) {
|
|
528
|
-
addCheck(suite5, 'pass', 'Ancestor Delegation', 'Zero static ancestor collisions (clickable visual focus intact)');
|
|
590
|
+
addCheck(suite5, 'pass', 'Ancestor Delegation', 'Zero static ancestor collisions (clickable visual focus intact)', { code: 'DNB-ANC-001' }) //, 'Zero static ancestor collisions (clickable visual focus intact)');
|
|
529
591
|
} else if (shouldFix) {
|
|
530
|
-
addCheck(suite5, 'fixed', 'Ancestor Delegation', `Stripped ${staticAncestorCollisions} static ancestor attribute(s) that shadowed editable children`);
|
|
592
|
+
addCheck(suite5, 'fixed', 'Ancestor Delegation', `Stripped ${staticAncestorCollisions} static ancestor attribute(s) that shadowed editable children`, { code: 'DNB-ANC-001' }) //, `Stripped ${staticAncestorCollisions} static ancestor attribute(s) that shadowed editable children`);
|
|
531
593
|
} else {
|
|
532
|
-
addCheck(suite5, 'err', 'Ancestor Delegation', `${staticAncestorCollisions} static ancestor wrapper(s) covering editable children (Run with --fix to strip automatically)`);
|
|
594
|
+
addCheck(suite5, 'err', 'Ancestor Delegation', `${staticAncestorCollisions} static ancestor wrapper(s) covering editable children (Run with --fix to strip automatically)`, { code: 'DNB-ANC-001' }) //, `${staticAncestorCollisions} static ancestor wrapper(s) covering editable children (Run with --fix to strip automatically)`);
|
|
533
595
|
}
|
|
534
596
|
|
|
535
597
|
if (broadStaticContainers.length === 0) {
|
|
536
|
-
addCheck(suite5, 'pass', 'Granular Static Markup', 'Zero broad layout containers (div/nav/section) marked static');
|
|
598
|
+
addCheck(suite5, 'pass', 'Granular Static Markup', 'Zero broad layout containers (div/nav/section) marked static', { code: 'DNB-STC-006' }) //, 'Zero broad layout containers (div/nav/section) marked static');
|
|
537
599
|
} else if (shouldFix) {
|
|
538
|
-
addCheck(suite5, 'fixed', 'Granular Static Markup', `Stripped data-preview-static from ${broadStaticContainers.length} broad container(s)`);
|
|
600
|
+
addCheck(suite5, 'fixed', 'Granular Static Markup', `Stripped data-preview-static from ${broadStaticContainers.length} broad container(s)`, { code: 'DNB-STC-006' }) //, `Stripped data-preview-static from ${broadStaticContainers.length} broad container(s)`);
|
|
539
601
|
} else {
|
|
540
|
-
addCheck(suite5, 'warn', 'Granular Static Markup', `${broadStaticContainers.length} broad container(s) marked with data-preview-static (Fivora requires marking only smallest leaf elements)`);
|
|
602
|
+
addCheck(suite5, 'warn', 'Granular Static Markup', `${broadStaticContainers.length} broad container(s) marked with data-preview-static (Fivora requires marking only smallest leaf elements)`, { code: 'DNB-STC-006' }) //, `${broadStaticContainers.length} broad container(s) marked with data-preview-static (Fivora requires marking only smallest leaf elements)`);
|
|
541
603
|
}
|
|
542
604
|
|
|
543
605
|
if (dynamicVariableMarkers.length === 0) {
|
|
544
|
-
addCheck(suite5, 'pass', 'Literal Marker Standard', 'All data-preview-field-path annotations use literal strings or JSX templates');
|
|
606
|
+
addCheck(suite5, 'pass', 'Literal Marker Standard', 'All data-preview-field-path annotations use literal strings or JSX templates', { code: 'DNB-AST-002' }) //, 'All data-preview-field-path annotations use literal strings or JSX templates');
|
|
545
607
|
} else {
|
|
546
|
-
addCheck(suite5, 'warn', 'Literal Marker Standard', `${dynamicVariableMarkers.length} dynamic variable marker(s) detected (e.g. ${dynamicVariableMarkers[0].expr})`);
|
|
608
|
+
addCheck(suite5, 'warn', 'Literal Marker Standard', `${dynamicVariableMarkers.length} dynamic variable marker(s) detected`, { code: 'DNB-AST-002' }) //, `${dynamicVariableMarkers.length} dynamic variable marker(s) detected (e.g. ${dynamicVariableMarkers[0].expr})`);
|
|
547
609
|
}
|
|
548
610
|
|
|
549
611
|
// =========================================================================
|
|
550
612
|
// SUITE 6: Multi-Niche Storefront Architecture & Security Preflight
|
|
551
613
|
// =========================================================================
|
|
614
|
+
|
|
615
|
+
// Check: Hidden contract markers & regex token collisions (DNB-HID-005)
|
|
616
|
+
let hiddenContractCount = 0;
|
|
617
|
+
let overflowHiddenCount = 0;
|
|
618
|
+
for (const file of sourceFiles) {
|
|
619
|
+
let c = fs.readFileSync(file, 'utf-8');
|
|
620
|
+
let modified = false;
|
|
621
|
+
const hiddenMatches = c.matchAll(/<([a-zA-Z0-9_-]+)\s+[^>]*(?:hidden|display:\s*['"]none['"])[^>]*data-preview-(?:field-path|list-path|item-path)[^>]*>/gi);
|
|
622
|
+
for (const hm of hiddenMatches) hiddenContractCount++;
|
|
623
|
+
|
|
624
|
+
if (c.includes('overflow-hidden') && (c.includes('data-preview-style-type="card"') || c.includes('GlassCard') || c.includes('data-preview-item-path'))) {
|
|
625
|
+
overflowHiddenCount++;
|
|
626
|
+
if (shouldFix) {
|
|
627
|
+
c = c.replace(/\boverflow-hidden\b/g, 'overflow-clip');
|
|
628
|
+
modified = true;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (/\bhidden\s+(?:md|sm|lg|xl):/.test(c) && c.includes('data-preview-')) {
|
|
632
|
+
if (shouldFix) {
|
|
633
|
+
c = c.replace(/\bhidden(\s+(?:md|sm|lg|xl):)/g, '[display:none]$1');
|
|
634
|
+
modified = true;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
if (shouldFix && modified) fs.writeFileSync(file, c, 'utf8');
|
|
638
|
+
}
|
|
639
|
+
if (hiddenContractCount === 0 && overflowHiddenCount === 0) {
|
|
640
|
+
addCheck(suite5, 'pass', 'Contract Visibility & Regex Safety', 'Zero hidden preview contract markers or token collisions', { code: 'DNB-HID-005' });
|
|
641
|
+
} else if (shouldFix) {
|
|
642
|
+
addCheck(suite5, 'fixed', 'Contract Visibility & Regex Safety', `Sanitized ${overflowHiddenCount} class token(s) to overflow-clip / [display:none]`, { code: 'DNB-HID-005' });
|
|
643
|
+
} else {
|
|
644
|
+
addCheck(suite5, 'warn', 'Contract Visibility & Regex Safety', `${hiddenContractCount} hidden marker(s) or ${overflowHiddenCount} class collision(s) detected (Run with --fix to sanitize)`, { code: 'DNB-HID-005' });
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Check: Empty-State Array & Out-of-Range Guards (DNB-ARR-003)
|
|
648
|
+
let unguardedArrayCount = 0;
|
|
649
|
+
for (const file of sourceFiles) {
|
|
650
|
+
const c = fs.readFileSync(file, 'utf-8');
|
|
651
|
+
if (c.includes('data-preview-list-path')) {
|
|
652
|
+
if (/\?\s*[A-Z0-9_]+\s*:\s*[A-Z0-9_]+/i.test(c) && !c.includes('Array.isArray')) {
|
|
653
|
+
unguardedArrayCount++;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (unguardedArrayCount === 0) {
|
|
658
|
+
addCheck(suite5, 'pass', 'Empty-State Array Guards', 'All list collections guarded against empty state ([]) out-of-range elements', { code: 'DNB-ARR-003' });
|
|
659
|
+
} else {
|
|
660
|
+
addCheck(suite5, 'warn', 'Empty-State Array Guards', `${unguardedArrayCount} list component(s) should verify Array.isArray(liveList) ? liveList : DEFAULT_LIST`, { code: 'DNB-ARR-003' });
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Check: Empty-State Singleton Persistence Guard (DNB-EMP-002)
|
|
664
|
+
let unmountedSingletonCount = 0;
|
|
665
|
+
for (const file of sourceFiles) {
|
|
666
|
+
const c = fs.readFileSync(file, 'utf-8');
|
|
667
|
+
if (/\{[^{}]{0,100}length\s*>\s*0\s*&&[^{}]{0,300}data-preview-field-path="home\.[a-zA-Z0-9]+Label"/.test(c)) {
|
|
668
|
+
if (!c.includes('length === 0')) unmountedSingletonCount++;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
if (unmountedSingletonCount === 0) {
|
|
672
|
+
addCheck(suite5, 'pass', 'Empty-State Singleton Persistence', 'Section singleton labels remain mounted during empty-state fixture tests', { code: 'DNB-EMP-002' });
|
|
673
|
+
} else {
|
|
674
|
+
addCheck(suite5, 'warn', 'Empty-State Singleton Persistence', `${unmountedSingletonCount} singleton label(s) unmount when list is empty. Provide an empty fallback container.`, { code: 'DNB-EMP-002' });
|
|
675
|
+
}
|
|
676
|
+
|
|
552
677
|
if (!isJson) console.log('\n\x1b[1m[6/6] Multi-Niche Architecture & Asset Security:\x1b[0m');
|
|
553
678
|
const suite6 = 'Niche Architecture & Security';
|
|
554
679
|
|
|
555
680
|
// Niche match analysis
|
|
556
681
|
const matchedRecipe = matchRecipeForProject(targetDir, pkg || {}, sourceFiles);
|
|
557
682
|
if (matchedRecipe) {
|
|
558
|
-
addCheck(suite6, 'pass', 'Storefront Niche Match', `${matchedRecipe.label} (${matchedRecipe.name})`);
|
|
683
|
+
addCheck(suite6, 'pass', 'Storefront Niche Match', `${matchedRecipe.label} (${matchedRecipe.name})`, { code: 'DNB-NIC-001' }) //, `${matchedRecipe.label} (${matchedRecipe.name})`);
|
|
559
684
|
|
|
560
685
|
// Audit niche-specific essential features
|
|
561
686
|
const allFileNames = sourceFiles.map((f) => path.basename(f).toLowerCase()).join(' ');
|
|
@@ -582,9 +707,9 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
582
707
|
const envFiles = ['.env', '.env.local', '.env.production', '.env.development'];
|
|
583
708
|
const foundEnv = envFiles.filter((f) => fs.existsSync(path.join(targetDir, f)));
|
|
584
709
|
if (foundEnv.length === 0) {
|
|
585
|
-
addCheck(suite6, 'pass', 'Secrets Isolation', 'No raw .env files in root directory');
|
|
710
|
+
addCheck(suite6, 'pass', 'Secrets Isolation', 'No raw .env files in root directory', { code: 'DNB-SEC-001' }) //, 'No raw .env files in root directory');
|
|
586
711
|
} else {
|
|
587
|
-
addCheck(suite6, 'warn', 'Secrets Isolation', `Active env files: ${foundEnv.join(', ')} (Will be excluded from upload ZIP)`);
|
|
712
|
+
addCheck(suite6, 'warn', 'Secrets Isolation', `Active env files: ${foundEnv.join(', ')}`, { code: 'DNB-SEC-001' }) //, `Active env files: ${foundEnv.join(', ')} (Will be excluded from upload ZIP)`);
|
|
588
713
|
}
|
|
589
714
|
|
|
590
715
|
// Storefront preview image
|
|
@@ -593,9 +718,9 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
593
718
|
fs.existsSync(path.join(targetDir, 'public', 'preview.png'));
|
|
594
719
|
|
|
595
720
|
if (previewExists) {
|
|
596
|
-
addCheck(suite6, 'pass', 'Storefront Preview Graphic', 'preview.png / thumbnail.png verified for Fivora gallery');
|
|
721
|
+
addCheck(suite6, 'pass', 'Storefront Preview Graphic', 'preview.png / thumbnail.png verified for Fivora gallery', { code: 'DNB-PRV-001' }) //, 'preview.png / thumbnail.png verified for Fivora gallery');
|
|
597
722
|
} else {
|
|
598
|
-
addCheck(suite6, 'warn', 'Storefront Preview Graphic', 'preview.png not found in root or public folder');
|
|
723
|
+
addCheck(suite6, 'warn', 'Storefront Preview Graphic', 'preview.png not found in root or public folder', { code: 'DNB-PRV-001' }) //, 'preview.png not found in root or public folder');
|
|
599
724
|
}
|
|
600
725
|
|
|
601
726
|
// Large assets audit (> 4MB)
|
|
@@ -609,9 +734,9 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
609
734
|
}
|
|
610
735
|
|
|
611
736
|
if (largeAssets.length === 0) {
|
|
612
|
-
addCheck(suite6, 'pass', 'Asset Optimization Preflight', `All ${assets.length} public asset(s) within optimal static export bounds (< 4MB)`);
|
|
737
|
+
addCheck(suite6, 'pass', 'Asset Optimization Preflight', `All ${assets.length} public asset(s) within optimal static export bounds (< 4MB)`, { code: 'DNB-AST-003' }) //, `All ${assets.length} public asset(s) within optimal static export bounds (< 4MB)`);
|
|
613
738
|
} else {
|
|
614
|
-
addCheck(suite6, 'warn', 'Asset Optimization Preflight', `${largeAssets.length} large asset(s) detected (> 4MB): ${largeAssets.map((a) => `${a.file} (${a.sizeMB}MB)`).join(', ')}`);
|
|
739
|
+
addCheck(suite6, 'warn', 'Asset Optimization Preflight', `${largeAssets.length} large asset(s) detected (> 4MB)`, { code: 'DNB-AST-003' }) //, `${largeAssets.length} large asset(s) detected (> 4MB): ${largeAssets.map((a) => `${a.file} (${a.sizeMB}MB)`).join(', ')}`);
|
|
615
740
|
}
|
|
616
741
|
|
|
617
742
|
// =========================================================================
|
|
@@ -626,6 +751,28 @@ function runDoctor(targetDirInput = '.', options = {}) {
|
|
|
626
751
|
if (isJson) {
|
|
627
752
|
console.log(JSON.stringify(reportData, null, 2));
|
|
628
753
|
} else {
|
|
754
|
+
if (reportData.errors > 0 || reportData.warnings > 0 || reportData.fixedCount > 0) {
|
|
755
|
+
console.log('\n \x1b[1m\x1b[36mSTANDARDIZED DEVELOPER ACTION MATRIX:\x1b[0m');
|
|
756
|
+
console.log(' \x1b[90m┌──────────────┬──────────┬─────────────────────────────────────┬──────────────────────────────────────────────────────┐\x1b[0m');
|
|
757
|
+
console.log(' \x1b[90m│\x1b[0m \x1b[1mCode\x1b[0m \x1b[90m│\x1b[0m \x1b[1mSeverity\x1b[0m \x1b[90m│\x1b[0m \x1b[1mDiagnostic Title\x1b[0m \x1b[90m│\x1b[0m \x1b[1mActionable Remediation / Standard\x1b[0m \x1b[90m│\x1b[0m');
|
|
758
|
+
console.log(' \x1b[90m├──────────────┼──────────┼─────────────────────────────────────┼──────────────────────────────────────────────────────┤\x1b[0m');
|
|
759
|
+
|
|
760
|
+
const actionChecks = reportData.suites
|
|
761
|
+
.flatMap(s => s.checks)
|
|
762
|
+
.filter(c => c.type === 'err' || c.type === 'warn' || c.type === 'fixed');
|
|
763
|
+
|
|
764
|
+
for (const item of actionChecks) {
|
|
765
|
+
const sevColor = item.type === 'err' ? '\x1b[31mBLOCKING\x1b[0m' : item.type === 'warn' ? '\x1b[33mADVISORY\x1b[0m' : '\x1b[32mREPAIRED\x1b[0m';
|
|
766
|
+
const rawSev = item.type === 'err' ? 'BLOCKING' : item.type === 'warn' ? 'ADVISORY' : 'REPAIRED';
|
|
767
|
+
const codePad = item.code.padEnd(12);
|
|
768
|
+
const titlePad = item.title.slice(0, 35).padEnd(35);
|
|
769
|
+
const actionText = (item.action || item.detail || 'Follow Fivora v2 Visual Editing Standard').slice(0, 52).padEnd(52);
|
|
770
|
+
console.log(` \x1b[90m│\x1b[0m \x1b[36m${codePad}\x1b[0m \x1b[90m│\x1b[0m ${sevColor}${' '.repeat(8 - rawSev.length)} \x1b[90m│\x1b[0m ${titlePad} \x1b[90m│\x1b[0m \x1b[90m${actionText}\x1b[0m \x1b[90m│\x1b[0m`);
|
|
771
|
+
}
|
|
772
|
+
console.log(' \x1b[90m└──────────────┴──────────┴─────────────────────────────────────┴──────────────────────────────────────────────────────┘\x1b[0m');
|
|
773
|
+
console.log(' \x1b[90mRun \x1b[32mdeneb doctor --fix\x1b[90m to automatically remediate auto-repairable items.\x1b[0m\n');
|
|
774
|
+
}
|
|
775
|
+
|
|
629
776
|
console.log('\n' + createBox([
|
|
630
777
|
'\x1b[1mDOCTOR DIAGNOSTIC SUMMARY\x1b[0m',
|
|
631
778
|
`\x1b[32m✔ Passed:\x1b[0m ${reportData.passed}`,
|