@deneb-ui/cli 2.0.28 → 2.0.30
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 +4 -4
- package/package.json +2 -2
- package/src/arc/__tests__/arc.test.cjs +23 -0
- package/src/arc/fivora-contract.cjs +1 -0
- package/src/arc/index.cjs +14 -1
- package/src/arc/transformer.cjs +57 -0
- package/src/tools/local-template-lab.cjs +44 -931
- package/src/tools/template-preview-focus-bridge.cjs +2 -2
package/bin/index.js
CHANGED
|
@@ -732,12 +732,12 @@ function initProject(targetInput, options = {}) {
|
|
|
732
732
|
// 6. Install DENEB packages if missing
|
|
733
733
|
const skipInstall = process.argv.includes('--skip-install');
|
|
734
734
|
const hasUi = Boolean(
|
|
735
|
-
(pkg.dependencies && (pkg.dependencies['@deneb-ui/ui'] || pkg.dependencies['@deneb/ui']
|
|
736
|
-
(pkg.devDependencies && (pkg.devDependencies['@deneb-ui/ui'] || pkg.devDependencies['@deneb/ui']
|
|
735
|
+
(pkg.dependencies && (pkg.dependencies['@deneb-ui/ui'] || pkg.dependencies['@deneb/ui'])) ||
|
|
736
|
+
(pkg.devDependencies && (pkg.devDependencies['@deneb-ui/ui'] || pkg.devDependencies['@deneb/ui']))
|
|
737
737
|
);
|
|
738
738
|
const hasCli = Boolean(
|
|
739
|
-
(pkg.devDependencies && (pkg.devDependencies['@deneb-ui/cli'] || pkg.devDependencies['@
|
|
740
|
-
(pkg.dependencies && (pkg.dependencies['@deneb-ui/cli'] || pkg.dependencies['@
|
|
739
|
+
(pkg.devDependencies && (pkg.devDependencies['@deneb-ui/cli'] || pkg.devDependencies['@deneb/cli'])) ||
|
|
740
|
+
(pkg.dependencies && (pkg.dependencies['@deneb-ui/cli'] || pkg.dependencies['@deneb/cli']))
|
|
741
741
|
);
|
|
742
742
|
|
|
743
743
|
if (!skipInstall && (!hasUi || !hasCli)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deneb-ui/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.30",
|
|
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.
|
|
52
|
+
"@deneb-ui/core": "^2.0.30",
|
|
53
53
|
"adm-zip": "^0.6.0",
|
|
54
54
|
"recast": "^0.23.11"
|
|
55
55
|
},
|
|
@@ -496,3 +496,26 @@ test('schema sections only claim a pageKey when reachability proves it', () => {
|
|
|
496
496
|
assert.equal(sections.find((s) => s.id === 'home').pageKey, 'home');
|
|
497
497
|
assert.equal(sections.find((s) => s.id === 'about').pageKey, 'about');
|
|
498
498
|
});
|
|
499
|
+
|
|
500
|
+
test('conflicting data-preview-static is stripped when element has editable markers', () => {
|
|
501
|
+
const dir = copyOf(STOREFRONT_FIXTURE);
|
|
502
|
+
// Introduce a conflicting static marker on an element with an editable marker
|
|
503
|
+
const heroPath = path.join(dir, 'src', 'components', 'Hero.tsx');
|
|
504
|
+
let heroCode = fs.readFileSync(heroPath, 'utf8');
|
|
505
|
+
heroCode = heroCode.replace(
|
|
506
|
+
'<p className="mt-4',
|
|
507
|
+
'<p data-preview-static="legacy static description" className="mt-4'
|
|
508
|
+
);
|
|
509
|
+
fs.writeFileSync(heroPath, heroCode, 'utf8');
|
|
510
|
+
|
|
511
|
+
silence(() => runDenebArc(dir, 'acme-store', { telemetry: 'off' }));
|
|
512
|
+
const updatedHero = fs.readFileSync(heroPath, 'utf8');
|
|
513
|
+
|
|
514
|
+
// data-preview-static must be stripped since the paragraph has data-preview-field-path
|
|
515
|
+
assert.ok(!updatedHero.includes('data-preview-static="legacy static description"'));
|
|
516
|
+
assert.ok(updatedHero.includes('data-preview-field-path'));
|
|
517
|
+
|
|
518
|
+
const { errors } = auditFivora(dir);
|
|
519
|
+
const placementErrors = errors.filter((e) => e.includes('cannot share an element with data-preview-static'));
|
|
520
|
+
assert.equal(placementErrors.length, 0);
|
|
521
|
+
});
|
|
@@ -490,6 +490,7 @@ function findUncoveredVisibleText(code, filePath) {
|
|
|
490
490
|
// A `<` preceded by an identifier character is a TypeScript generic
|
|
491
491
|
// (forwardRef<HTMLButtonElement, Props>), never a JSX element.
|
|
492
492
|
if (/[\w$)\]]/.test(source[offset - 1] || '')) continue;
|
|
493
|
+
if (tag.toLowerCase() === 'option') continue;
|
|
493
494
|
if (!text || text.length <= 2 || !/\p{L}/u.test(text)) continue;
|
|
494
495
|
if (!/^[\p{L}\p{N}"'(¡¿#$€£]/u.test(text)) continue;
|
|
495
496
|
if (/^(?:true|false|null|undefined)$/i.test(text)) continue;
|
package/src/arc/index.cjs
CHANGED
|
@@ -17,7 +17,7 @@ const { walkFiles, isJsxFile, rel, copyFilePreserve, writeJson, readJsonSafe, fi
|
|
|
17
17
|
const { scanProject, buildDependencyGraph, inferOwnerScope } = require('./scanner.cjs');
|
|
18
18
|
const { analyzeFile, collectDesignSnapshot } = require('./semantic.cjs');
|
|
19
19
|
const { planTransformations } = require('./planner.cjs');
|
|
20
|
-
const { applyFilePlan, instrumentLayoutSource, instrumentPageKey, resolveSiteDataSpecifier, ensureJsonModule } = require('./transformer.cjs');
|
|
20
|
+
const { applyFilePlan, instrumentLayoutSource, instrumentPageKey, resolveSiteDataSpecifier, ensureJsonModule, sanitizeContradictoryMarkersInSource } = require('./transformer.cjs');
|
|
21
21
|
const { parseSource } = require('./ast.cjs');
|
|
22
22
|
const { buildSiteDataAndManifest, writeDataBank, loadExistingData, countSchemaFields } = require('./manifest.cjs');
|
|
23
23
|
const { validateAstFiles, validateContracts, designPreservationScore, coverageMetrics } = require('./validator.cjs');
|
|
@@ -355,6 +355,19 @@ function runDenebArc(projectDir, projectName, options = {}) {
|
|
|
355
355
|
}
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
// Remove any conflicting data-preview-static from elements carrying editable markers
|
|
359
|
+
for (const relativeFile of profile.jsxFiles || []) {
|
|
360
|
+
const abs = path.join(projectDir, relativeFile);
|
|
361
|
+
if (!fs.existsSync(abs)) continue;
|
|
362
|
+
const original = fs.readFileSync(abs, 'utf8');
|
|
363
|
+
const sanitized = sanitizeContradictoryMarkersInSource(original, relativeFile);
|
|
364
|
+
if (sanitized.updated && sanitized.code !== original) {
|
|
365
|
+
backupFile(projectDir, backupDir, abs);
|
|
366
|
+
fs.writeFileSync(abs, sanitized.code, 'utf8');
|
|
367
|
+
if (!changedFiles.includes(relativeFile)) changedFiles.push(relativeFile);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
358
371
|
if (profile.framework === 'nextjs') {
|
|
359
372
|
const before = findNextConfig(projectDir);
|
|
360
373
|
if (before) backupFile(projectDir, backupDir, before.abs);
|
package/src/arc/transformer.cjs
CHANGED
|
@@ -48,7 +48,16 @@ function replaceAttrValue(node, attrName, expression) {
|
|
|
48
48
|
attr.value = b.jsxExpressionContainer(expression);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function stripStaticAttribute(node) {
|
|
52
|
+
if (node && node.openingElement && Array.isArray(node.openingElement.attributes)) {
|
|
53
|
+
node.openingElement.attributes = node.openingElement.attributes.filter(
|
|
54
|
+
(attr) => !(attr.type === 'JSXAttribute' && attr.name && attr.name.name === 'data-preview-static')
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
function ensurePreviewPath(node, fieldPath) {
|
|
60
|
+
stripStaticAttribute(node);
|
|
52
61
|
if (hasJsxAttribute(node, 'data-preview-field-path')) return;
|
|
53
62
|
node.openingElement.attributes.push(jsxPreviewAttr(fieldPath));
|
|
54
63
|
}
|
|
@@ -505,6 +514,9 @@ function applyFilePlan(filePlan, profile) {
|
|
|
505
514
|
ensureDefaultImport(ast, siteDataImport, 'siteData');
|
|
506
515
|
}
|
|
507
516
|
|
|
517
|
+
// Sanitize any conflicting data-preview-static on elements with editable markers
|
|
518
|
+
sanitizeContradictoryMarkers(ast);
|
|
519
|
+
|
|
508
520
|
// Page keys are stamped in a separate route-driven pass so App Router and
|
|
509
521
|
// Pages Router projects are handled by the same logic.
|
|
510
522
|
const code = printSource(ast, filePlan.originalCode);
|
|
@@ -692,6 +704,49 @@ function ensureJsonModule(tsconfig) {
|
|
|
692
704
|
};
|
|
693
705
|
}
|
|
694
706
|
|
|
707
|
+
function sanitizeContradictoryMarkers(ast) {
|
|
708
|
+
let cleaned = 0;
|
|
709
|
+
recast.types.visit(ast, {
|
|
710
|
+
visitJSXOpeningElement(pathNode) {
|
|
711
|
+
const attrs = pathNode.node.attributes || [];
|
|
712
|
+
const hasEditable = attrs.some(
|
|
713
|
+
(a) => a.type === 'JSXAttribute' && a.name && (
|
|
714
|
+
a.name.name === 'data-preview-field-path' ||
|
|
715
|
+
a.name.name === 'data-preview-list-path' ||
|
|
716
|
+
a.name.name === 'data-preview-item-path'
|
|
717
|
+
)
|
|
718
|
+
);
|
|
719
|
+
const hasStatic = attrs.some(
|
|
720
|
+
(a) => a.type === 'JSXAttribute' && a.name && a.name.name === 'data-preview-static'
|
|
721
|
+
);
|
|
722
|
+
if (hasEditable && hasStatic) {
|
|
723
|
+
pathNode.node.attributes = attrs.filter(
|
|
724
|
+
(a) => !(a.type === 'JSXAttribute' && a.name && a.name.name === 'data-preview-static')
|
|
725
|
+
);
|
|
726
|
+
cleaned++;
|
|
727
|
+
}
|
|
728
|
+
this.traverse(pathNode);
|
|
729
|
+
},
|
|
730
|
+
});
|
|
731
|
+
return cleaned;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function sanitizeContradictoryMarkersInSource(code, relativeFile) {
|
|
735
|
+
if (!code.includes('data-preview-static')) return { code, updated: false };
|
|
736
|
+
if (!code.includes('data-preview-field-path') && !code.includes('data-preview-list-path') && !code.includes('data-preview-item-path')) {
|
|
737
|
+
return { code, updated: false };
|
|
738
|
+
}
|
|
739
|
+
let ast;
|
|
740
|
+
try {
|
|
741
|
+
ast = parseSource(code, relativeFile);
|
|
742
|
+
} catch {
|
|
743
|
+
return { code, updated: false };
|
|
744
|
+
}
|
|
745
|
+
const count = sanitizeContradictoryMarkers(ast);
|
|
746
|
+
if (count === 0) return { code, updated: false };
|
|
747
|
+
return { code: printSource(ast, code), updated: true, count };
|
|
748
|
+
}
|
|
749
|
+
|
|
695
750
|
module.exports = {
|
|
696
751
|
applyFilePlan,
|
|
697
752
|
instrumentLayoutSource,
|
|
@@ -699,4 +754,6 @@ module.exports = {
|
|
|
699
754
|
resolveSiteDataSpecifier,
|
|
700
755
|
ensureJsonModule,
|
|
701
756
|
inferPageKey,
|
|
757
|
+
sanitizeContradictoryMarkers,
|
|
758
|
+
sanitizeContradictoryMarkersInSource,
|
|
702
759
|
};
|