@deneb-ui/cli 2.0.22 → 2.0.24

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.
Files changed (52) hide show
  1. package/README.md +62 -114
  2. package/bin/index.js +55 -12
  3. package/package.json +25 -5
  4. package/src/arc/__fixtures__/next-app-basic/package.json +10 -0
  5. package/src/arc/__fixtures__/next-app-basic/src/app/globals.css +3 -0
  6. package/src/arc/__fixtures__/next-app-basic/src/app/layout.tsx +9 -0
  7. package/src/arc/__fixtures__/next-app-basic/src/app/page.tsx +11 -0
  8. package/src/arc/__fixtures__/next-app-basic/src/components/Header.tsx +13 -0
  9. package/src/arc/__fixtures__/next-app-basic/src/components/Hero.tsx +12 -0
  10. package/src/arc/__fixtures__/next-app-basic/src/components/PromoBanner.tsx +10 -0
  11. package/src/arc/__fixtures__/next-app-basic/tsconfig.json +12 -0
  12. package/src/arc/__fixtures__/next-app-storefront/components.json +14 -0
  13. package/src/arc/__fixtures__/next-app-storefront/package.json +22 -0
  14. package/src/arc/__fixtures__/next-app-storefront/src/app/about/page.tsx +13 -0
  15. package/src/arc/__fixtures__/next-app-storefront/src/app/globals.css +5 -0
  16. package/src/arc/__fixtures__/next-app-storefront/src/app/layout.tsx +19 -0
  17. package/src/arc/__fixtures__/next-app-storefront/src/app/page.tsx +13 -0
  18. package/src/arc/__fixtures__/next-app-storefront/src/components/Features.tsx +31 -0
  19. package/src/arc/__fixtures__/next-app-storefront/src/components/Hero.tsx +45 -0
  20. package/src/arc/__fixtures__/next-app-storefront/src/components/ProductGrid.tsx +49 -0
  21. package/src/arc/__fixtures__/next-app-storefront/src/components/SiteFooter.tsx +22 -0
  22. package/src/arc/__fixtures__/next-app-storefront/src/components/SiteHeader.tsx +21 -0
  23. package/src/arc/__fixtures__/next-app-storefront/src/components/ui/button.tsx +36 -0
  24. package/src/arc/__fixtures__/next-app-storefront/tsconfig.json +15 -0
  25. package/src/arc/__fixtures__/next-pages-basic/package.json +10 -0
  26. package/src/arc/__fixtures__/next-pages-basic/pages/_app.jsx +5 -0
  27. package/src/arc/__fixtures__/next-pages-basic/pages/contact.jsx +9 -0
  28. package/src/arc/__fixtures__/next-pages-basic/pages/index.jsx +11 -0
  29. package/src/arc/__fixtures__/next-pages-basic/styles/globals.css +9 -0
  30. package/src/arc/__tests__/arc.test.cjs +498 -0
  31. package/src/arc/adapters.cjs +184 -0
  32. package/src/arc/ast.cjs +339 -0
  33. package/src/arc/field-paths.cjs +165 -0
  34. package/src/arc/fivora-contract.cjs +521 -0
  35. package/src/arc/font-plan.cjs +65 -0
  36. package/src/arc/fs-utils.cjs +170 -0
  37. package/src/arc/index.cjs +627 -0
  38. package/src/arc/learning.cjs +211 -0
  39. package/src/arc/manifest.cjs +438 -0
  40. package/src/arc/next-config.cjs +279 -0
  41. package/src/arc/planner.cjs +252 -0
  42. package/src/arc/printer.cjs +183 -0
  43. package/src/arc/recipes-v2.cjs +49 -0
  44. package/src/arc/scanner.cjs +613 -0
  45. package/src/arc/semantic.cjs +651 -0
  46. package/src/arc/style-candidates.cjs +72 -0
  47. package/src/arc/transformer.cjs +677 -0
  48. package/src/arc/validator.cjs +173 -0
  49. package/src/arc/version.cjs +22 -0
  50. package/src/common/style-validation.ts +30 -0
  51. package/src/tools/deneb-fonts.cjs +114 -0
  52. package/src/tools/template-preview-focus-bridge.cjs +3 -3
@@ -0,0 +1,183 @@
1
+ 'use strict';
2
+
3
+ const { ARC_NAME, ARC_FULL_NAME, ARC_VERSION } = require('./version.cjs');
4
+
5
+ const C = {
6
+ reset: '\x1b[0m',
7
+ bold: '\x1b[1m',
8
+ dim: '\x1b[90m',
9
+ green: '\x1b[32m',
10
+ yellow: '\x1b[33m',
11
+ cyan: '\x1b[36m',
12
+ red: '\x1b[31m',
13
+ white: '\x1b[37m',
14
+ };
15
+
16
+ function ok(msg) {
17
+ console.log(` ${C.green}✓${C.reset} ${msg}`);
18
+ }
19
+
20
+ function warn(msg) {
21
+ console.log(` ${C.yellow}⚠${C.reset} ${msg}`);
22
+ }
23
+
24
+ function info(msg) {
25
+ console.log(` ${C.cyan}•${C.reset} ${msg}`);
26
+ }
27
+
28
+ function heading(title) {
29
+ console.log(`\n${C.bold}${title}${C.reset}\n`);
30
+ }
31
+
32
+ function printBanner(mode) {
33
+ const suffix = mode === 'dry-run' ? ' (dry run)' : mode === 'explain' ? ' (explain)' : '';
34
+ console.log(`\n${C.cyan}▲${C.reset} ${C.bold}${ARC_NAME}${C.reset}${suffix}`);
35
+ console.log(`${C.dim}${ARC_FULL_NAME} v${ARC_VERSION}${C.reset}\n`);
36
+ }
37
+
38
+ function printProfile(profile) {
39
+ heading('Analyzing project...');
40
+ const fw = profile.framework === 'nextjs'
41
+ ? `Next.js${profile.frameworkVersion ? ' ' + profile.frameworkVersion : ''}`
42
+ : profile.framework;
43
+ ok(`${fw} detected`);
44
+ ok(`${profile.language === 'javascript' ? 'JavaScript' : profile.language === 'typescript' ? 'TypeScript' : 'Mixed JS/TS'} detected`);
45
+ if (profile.cssSystems.includes('tailwind-v4')) ok('Tailwind CSS v4 detected');
46
+ else if (profile.cssSystems.some((s) => s.startsWith('tailwind'))) ok('Tailwind CSS detected');
47
+ if (profile.shadcn) ok('shadcn/ui detected');
48
+ if (profile.heroui) ok('HeroUI detected');
49
+ if (profile.reactBits) ok('React Bits detected');
50
+ if (profile.router === 'next-app') ok('App Router detected');
51
+ if (profile.router === 'next-pages') ok('Pages Router detected');
52
+ if (profile.router === 'react-router') ok('React Router detected');
53
+ }
54
+
55
+ function printScan(profile, graph, candidateCount, actionCount) {
56
+ heading('Scanning architecture...');
57
+ ok(`${profile.routes.length} routes`);
58
+ ok(`${profile.components.length} components`);
59
+ ok(`${candidateCount} content candidates`);
60
+ ok(`${actionCount} interactive actions`);
61
+ if (graph.sharedFiles?.length) info(`${graph.sharedFiles.length} shared components reused across routes`);
62
+ }
63
+
64
+ function printPlan(plan) {
65
+ heading('Planning editable contracts...');
66
+ ok(`${plan.stats.auto} high-confidence transformations`);
67
+ if (plan.stats.validate) info(`${plan.stats.validate} transformations queued with extra validation`);
68
+ const skippedDynamic = plan.skipped.filter((s) => /dynamic|api/.test(s.reason || '')).length;
69
+ const skippedLow = plan.skipped.filter((s) => (s.confidence || 1) < 0.6).length;
70
+ if (plan.stats.styleBinds) ok(`${plan.stats.styleBinds} Fivora style-bind contracts`);
71
+ if (skippedDynamic) ok(`${skippedDynamic} existing dynamic values preserved`);
72
+ if (skippedLow) warn(`${skippedLow} low-confidence candidates skipped`);
73
+ }
74
+
75
+ function printApply(result) {
76
+ heading('Applying Deneb contracts...');
77
+ ok(`${result.filesUpdated} files updated`);
78
+ if (result.layoutUpdated) ok('Root layout instrumented with SiteDataProvider');
79
+ }
80
+
81
+ function printValidation(validation, coverage, design) {
82
+ heading('Validating...');
83
+ if (validation.syntaxPassed) ok('AST');
84
+ else warn('AST validation reported parse issues');
85
+ if (validation.contractPassed) ok('editable contracts');
86
+ else warn('editable contract issues detected');
87
+ ok('manifest');
88
+ if (validation.idempotencyPassed !== false) ok('idempotency');
89
+ console.log('');
90
+ console.log(` Editable coverage: ${coverage.editableCoverage}%`);
91
+ console.log(` Design preservation: ${design.score}%`);
92
+ }
93
+
94
+ function printExplain(plan) {
95
+ heading('Explain');
96
+ for (const file of plan.files) {
97
+ if (!file.transformations.length) continue;
98
+ console.log(` ${C.bold}${file.file}${C.reset}`);
99
+ for (const t of file.transformations) {
100
+ const target = t.stylePath || t.field || t.urlField || t.listField || '';
101
+ console.log(` - ${t.operation} ${target}`);
102
+ const boost = t.explain?.fingerprintBoost
103
+ ? ` fingerprint: ${t.explain.fingerprintState}+${t.explain.fingerprintBoost}`
104
+ : '';
105
+ console.log(` ${C.dim}why: ${t.reason} confidence: ${t.confidence} recipe: ${t.recipeId || 'none'}${boost}${C.reset}`);
106
+ }
107
+ }
108
+ }
109
+
110
+ function printDryRun(profile, plan) {
111
+ heading('Dry run (no files modified)');
112
+ console.log(` Technology: ${profile.framework} / ${profile.language} / ${(profile.cssSystems || []).join(', ') || 'css'}`);
113
+ console.log(` Routes: ${profile.routes.map((r) => r.route).join(', ')}`);
114
+ console.log(` Components scanned: ${profile.components.length}`);
115
+ console.log(` Planned transformations: ${plan.stats.planned}`);
116
+ console.log(` Files affected: ${plan.stats.filesAffected}`);
117
+ if (plan.skipped.length) {
118
+ console.log(` Risks:`);
119
+ for (const skip of plan.skipped.slice(0, 8)) {
120
+ console.log(` - ${skip.file}: ${skip.reason} (${skip.confidence})`);
121
+ }
122
+ }
123
+ }
124
+
125
+ function printError(file, reason, confidence) {
126
+ console.log(`\n${C.red}Deneb could not safely determine the transformation in:${C.reset}\n`);
127
+ console.log(` ${file}\n`);
128
+ console.log(`Reason:\n ${reason}\n`);
129
+ if (confidence != null) console.log(`Confidence: ${confidence}\n`);
130
+ console.log('Action:\n Component left unchanged.\n');
131
+ console.log('No source code was damaged.\n');
132
+ }
133
+
134
+ function printSuccess() {
135
+ console.log(`\n${C.green}${C.bold}Deneb ARC completed successfully.${C.reset}\n`);
136
+ }
137
+
138
+ function printUncoveredText(findings = []) {
139
+ if (!findings.length) return;
140
+ warn(`${findings.length} visible text node(s) still uncovered — run deneb validate . before packaging`);
141
+ for (const item of findings.slice(0, 12)) {
142
+ const loc = item.filePath ? `${item.filePath}${item.line ? `:${item.line}` : ''}` : '';
143
+ const snippet = String(item.text || '').slice(0, 80);
144
+ console.log(` ${C.dim}${loc} <${item.tag}> ${snippet}${C.reset}`);
145
+ }
146
+ if (findings.length > 12) {
147
+ console.log(` ${C.dim}... ${findings.length - 12} more in .deneb/report.json${C.reset}`);
148
+ }
149
+ }
150
+
151
+ function printDeveloperNextSteps() {
152
+ heading('Next steps for Fivora');
153
+ console.log(` ${C.cyan}1.${C.reset} npm run lab ${C.dim}preview visual editing locally${C.reset}`);
154
+ console.log(` ${C.cyan}2.${C.reset} deneb fonts install . ${C.dim}self-host Google Fonts used by this site${C.reset}`);
155
+ console.log(` ${C.cyan}3.${C.reset} npm run validate ${C.dim}same contract Fivora ingest uses${C.reset}`);
156
+ console.log(` ${C.cyan}4.${C.reset} npm run validate-and-zip ${C.dim}upload-ready ZIP${C.reset}`);
157
+ console.log(` ${C.dim}Style edits: Fivora sends FIVORA_PREVIEW_STYLE_PATCH to data-preview-style-target nodes.${C.reset}`);
158
+ console.log(` ${C.dim}Re-run with --explain to see why each field/style was bound.${C.reset}\n`);
159
+ }
160
+
161
+ function printRollback(reason) {
162
+ console.log(`\n${C.yellow}Deneb ARC rolled back the conversion.${C.reset}`);
163
+ console.log(`${C.dim}${reason}${C.reset}\n`);
164
+ }
165
+
166
+ module.exports = {
167
+ printBanner,
168
+ printProfile,
169
+ printScan,
170
+ printPlan,
171
+ printApply,
172
+ printValidation,
173
+ printExplain,
174
+ printDryRun,
175
+ printError,
176
+ printSuccess,
177
+ printUncoveredText,
178
+ printDeveloperNextSteps,
179
+ printRollback,
180
+ ok,
181
+ warn,
182
+ info,
183
+ };
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ const { loadAllRecipes, matchRecipeForProject, getRecipeByName } = require('../tools/recipe-engine.cjs');
4
+
5
+ function matchRecipeV2(projectDir, profile, sourceFiles, recipeName) {
6
+ if (recipeName) {
7
+ const explicit = getRecipeByName(recipeName, projectDir);
8
+ if (explicit) return { recipe: normalizeRecipe(explicit), source: 'explicit' };
9
+ }
10
+ const matched = matchRecipeForProject(projectDir, profile.pkg || {}, sourceFiles);
11
+ if (matched) return { recipe: normalizeRecipe(matched), source: 'signature' };
12
+ return { recipe: null, source: null };
13
+ }
14
+
15
+ function normalizeRecipe(recipe) {
16
+ if (!recipe) return null;
17
+ return {
18
+ id: recipe.name || recipe.id,
19
+ name: recipe.name || recipe.id,
20
+ label: recipe.label || recipe.name,
21
+ version: recipe.version || '1.0.0',
22
+ fingerprint: recipe.signatures || {},
23
+ prerequisites: [],
24
+ detectors: recipe.signatures?.keywords || [],
25
+ transforms: [],
26
+ validators: [],
27
+ confidenceThreshold: 0.85,
28
+ successfulApplications: recipe.successfulApplications || 0,
29
+ failedApplications: recipe.failedApplications || 0,
30
+ actionRules: recipe.actionRules,
31
+ socialRules: recipe.socialRules,
32
+ gridRules: recipe.gridRules,
33
+ sections: recipe.sections,
34
+ pages: recipe.pages,
35
+ defaults: recipe.defaults,
36
+ signatures: recipe.signatures,
37
+ raw: recipe,
38
+ };
39
+ }
40
+
41
+ function listRecipes(projectDir) {
42
+ return loadAllRecipes(projectDir).map(normalizeRecipe);
43
+ }
44
+
45
+ module.exports = {
46
+ matchRecipeV2,
47
+ normalizeRecipe,
48
+ listRecipes,
49
+ };