@designtools/codesurface 0.1.2 → 0.1.3
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/dist/cli.js +234 -66
- package/dist/client/assets/index-BIpQhd8p.js +150 -0
- package/dist/client/assets/index-Bbi0p5WW.js +150 -0
- package/dist/client/assets/index-CZJQ3vJi.js +150 -0
- package/dist/client/assets/index-D-bUCwC3.css +1 -0
- package/dist/client/assets/index-D3ju6tgw.css +1 -0
- package/dist/client/index.html +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1297,8 +1297,8 @@ function categorizeToken(name, value) {
|
|
|
1297
1297
|
return "other";
|
|
1298
1298
|
}
|
|
1299
1299
|
function getTokenGroup(name) {
|
|
1300
|
-
const
|
|
1301
|
-
const scaleMatch =
|
|
1300
|
+
const n4 = name.replace(/^--/, "");
|
|
1301
|
+
const scaleMatch = n4.match(/^([\w]+)-\d+$/);
|
|
1302
1302
|
if (scaleMatch) return scaleMatch[1];
|
|
1303
1303
|
const semanticPrefixes = [
|
|
1304
1304
|
"primary",
|
|
@@ -1309,19 +1309,19 @@ function getTokenGroup(name) {
|
|
|
1309
1309
|
"warning"
|
|
1310
1310
|
];
|
|
1311
1311
|
for (const prefix of semanticPrefixes) {
|
|
1312
|
-
if (
|
|
1312
|
+
if (n4 === prefix || n4.startsWith(`${prefix}-`)) return prefix;
|
|
1313
1313
|
}
|
|
1314
|
-
if (["background", "foreground", "card", "card-foreground", "popover", "popover-foreground"].includes(
|
|
1314
|
+
if (["background", "foreground", "card", "card-foreground", "popover", "popover-foreground"].includes(n4)) {
|
|
1315
1315
|
return "surface";
|
|
1316
1316
|
}
|
|
1317
|
-
if (["border", "input", "ring", "muted", "muted-foreground", "accent", "accent-foreground"].includes(
|
|
1317
|
+
if (["border", "input", "ring", "muted", "muted-foreground", "accent", "accent-foreground"].includes(n4)) {
|
|
1318
1318
|
return "utility";
|
|
1319
1319
|
}
|
|
1320
|
-
if (
|
|
1321
|
-
if (
|
|
1322
|
-
if (
|
|
1323
|
-
if (
|
|
1324
|
-
if (
|
|
1320
|
+
if (n4.startsWith("chart")) return "chart";
|
|
1321
|
+
if (n4.startsWith("sidebar")) return "sidebar";
|
|
1322
|
+
if (n4.startsWith("radius")) return "radius";
|
|
1323
|
+
if (n4.startsWith("shadow")) return "shadow";
|
|
1324
|
+
if (n4.startsWith("border-width")) return "border";
|
|
1325
1325
|
return "other";
|
|
1326
1326
|
}
|
|
1327
1327
|
function detectColorFormat(value) {
|
|
@@ -1335,6 +1335,8 @@ function detectColorFormat(value) {
|
|
|
1335
1335
|
// src/server/lib/scan-components.ts
|
|
1336
1336
|
import fs5 from "fs/promises";
|
|
1337
1337
|
import path5 from "path";
|
|
1338
|
+
import recast2 from "recast";
|
|
1339
|
+
import { namedTypes as n3 } from "ast-types";
|
|
1338
1340
|
async function scanComponents(projectRoot) {
|
|
1339
1341
|
const componentDirs = [
|
|
1340
1342
|
"components/ui",
|
|
@@ -1355,86 +1357,253 @@ async function scanComponents(projectRoot) {
|
|
|
1355
1357
|
const fullDir = path5.join(projectRoot, componentDir);
|
|
1356
1358
|
const files = await fs5.readdir(fullDir);
|
|
1357
1359
|
const tsxFiles = files.filter((f) => f.endsWith(".tsx"));
|
|
1360
|
+
const parser = await getParser();
|
|
1358
1361
|
const components = [];
|
|
1359
1362
|
for (const file of tsxFiles) {
|
|
1360
1363
|
const filePath = path5.join(componentDir, file);
|
|
1361
1364
|
const source = await fs5.readFile(path5.join(projectRoot, filePath), "utf-8");
|
|
1362
|
-
const
|
|
1363
|
-
|
|
1364
|
-
components.push(entry);
|
|
1365
|
-
}
|
|
1365
|
+
const entries = parseComponentAST(source, filePath, parser);
|
|
1366
|
+
components.push(...entries);
|
|
1366
1367
|
}
|
|
1367
1368
|
return { components };
|
|
1368
1369
|
}
|
|
1369
|
-
function
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
const
|
|
1377
|
-
|
|
1378
|
-
|
|
1370
|
+
function parseComponentAST(source, filePath, parser) {
|
|
1371
|
+
let ast;
|
|
1372
|
+
try {
|
|
1373
|
+
ast = recast2.parse(source, { parser });
|
|
1374
|
+
} catch {
|
|
1375
|
+
return [];
|
|
1376
|
+
}
|
|
1377
|
+
const cvaMap = /* @__PURE__ */ new Map();
|
|
1378
|
+
const slotToComponent = /* @__PURE__ */ new Map();
|
|
1379
|
+
const exportedNames = /* @__PURE__ */ new Set();
|
|
1380
|
+
const acceptsChildrenSet = /* @__PURE__ */ new Set();
|
|
1381
|
+
let currentComponentName = null;
|
|
1382
|
+
recast2.visit(ast, {
|
|
1383
|
+
// Find cva() calls: const fooVariants = cva("base classes", { variants: {...}, defaultVariants: {...} })
|
|
1384
|
+
visitVariableDeclaration(path17) {
|
|
1385
|
+
for (const decl of path17.node.declarations) {
|
|
1386
|
+
if (n3.VariableDeclarator.check(decl) && n3.Identifier.check(decl.id) && n3.CallExpression.check(decl.init) && isIdentifierNamed(decl.init.callee, "cva")) {
|
|
1387
|
+
const varName = decl.id.name;
|
|
1388
|
+
const args = decl.init.arguments;
|
|
1389
|
+
const baseClasses = extractStringValue(args[0]) || "";
|
|
1390
|
+
const configArg = args[1];
|
|
1391
|
+
const variants = configArg && n3.ObjectExpression.check(configArg) ? extractVariantsFromConfig(configArg) : [];
|
|
1392
|
+
cvaMap.set(varName, { baseClasses, variants });
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
this.traverse(path17);
|
|
1396
|
+
},
|
|
1397
|
+
// Find forwardRef and function components to track data-slot and currentComponentName
|
|
1398
|
+
visitCallExpression(path17) {
|
|
1399
|
+
const node = path17.node;
|
|
1400
|
+
if (isForwardRefCall(node)) {
|
|
1401
|
+
const parent = path17.parent?.node;
|
|
1402
|
+
if (n3.VariableDeclarator.check(parent) && n3.Identifier.check(parent.id)) {
|
|
1403
|
+
currentComponentName = parent.id.name;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
this.traverse(path17);
|
|
1407
|
+
},
|
|
1408
|
+
// Find data-slot JSX attributes
|
|
1409
|
+
visitJSXAttribute(path17) {
|
|
1410
|
+
const attr = path17.node;
|
|
1411
|
+
if (n3.JSXIdentifier.check(attr.name) && attr.name.name === "data-slot" && n3.StringLiteral.check(attr.value)) {
|
|
1412
|
+
const slotValue = attr.value.value;
|
|
1413
|
+
const compName = findEnclosingComponentName(path17) || currentComponentName;
|
|
1414
|
+
if (compName) {
|
|
1415
|
+
slotToComponent.set(slotValue, compName);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
this.traverse(path17);
|
|
1419
|
+
},
|
|
1420
|
+
// Detect {...props} spread in JSX — indicates component accepts children
|
|
1421
|
+
visitJSXSpreadAttribute(path17) {
|
|
1422
|
+
const expr = path17.node.argument;
|
|
1423
|
+
if (n3.Identifier.check(expr) && expr.name === "props") {
|
|
1424
|
+
const compName = findEnclosingComponentName(path17) || currentComponentName;
|
|
1425
|
+
if (compName) {
|
|
1426
|
+
acceptsChildrenSet.add(compName);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
this.traverse(path17);
|
|
1430
|
+
},
|
|
1431
|
+
// Collect named exports: export { Button, Card, ... }
|
|
1432
|
+
visitExportNamedDeclaration(path17) {
|
|
1433
|
+
const node = path17.node;
|
|
1434
|
+
if (node.specifiers) {
|
|
1435
|
+
for (const spec of node.specifiers) {
|
|
1436
|
+
if (n3.ExportSpecifier.check(spec) && n3.Identifier.check(spec.exported)) {
|
|
1437
|
+
exportedNames.add(spec.exported.name);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
if (node.declaration) {
|
|
1442
|
+
if (n3.VariableDeclaration.check(node.declaration)) {
|
|
1443
|
+
for (const decl of node.declaration.declarations) {
|
|
1444
|
+
if (n3.VariableDeclarator.check(decl) && n3.Identifier.check(decl.id)) {
|
|
1445
|
+
exportedNames.add(decl.id.name);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
} else if (n3.FunctionDeclaration.check(node.declaration) && node.declaration.id) {
|
|
1449
|
+
exportedNames.add(node.declaration.id.name);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
this.traverse(path17);
|
|
1453
|
+
},
|
|
1454
|
+
// export default function Foo
|
|
1455
|
+
visitExportDefaultDeclaration(path17) {
|
|
1456
|
+
const decl = path17.node.declaration;
|
|
1457
|
+
if (n3.FunctionDeclaration.check(decl) && decl.id) {
|
|
1458
|
+
exportedNames.add(decl.id.name);
|
|
1459
|
+
}
|
|
1460
|
+
this.traverse(path17);
|
|
1461
|
+
}
|
|
1462
|
+
});
|
|
1463
|
+
recast2.visit(ast, {
|
|
1464
|
+
visitFunctionDeclaration(path17) {
|
|
1465
|
+
const name = path17.node.id ? String(path17.node.id.name) : null;
|
|
1466
|
+
if (name) {
|
|
1467
|
+
currentComponentName = name;
|
|
1468
|
+
this.traverse(path17);
|
|
1469
|
+
currentComponentName = null;
|
|
1470
|
+
} else {
|
|
1471
|
+
this.traverse(path17);
|
|
1472
|
+
}
|
|
1473
|
+
},
|
|
1474
|
+
visitJSXAttribute(path17) {
|
|
1475
|
+
const attr = path17.node;
|
|
1476
|
+
if (n3.JSXIdentifier.check(attr.name) && attr.name.name === "data-slot" && n3.StringLiteral.check(attr.value)) {
|
|
1477
|
+
const slotValue = attr.value.value;
|
|
1478
|
+
if (!slotToComponent.has(slotValue) && currentComponentName) {
|
|
1479
|
+
slotToComponent.set(slotValue, currentComponentName);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
this.traverse(path17);
|
|
1483
|
+
}
|
|
1484
|
+
});
|
|
1485
|
+
const tokenRefs = extractTokenReferences(source);
|
|
1486
|
+
const entries = [];
|
|
1487
|
+
for (const [dataSlot, componentName] of slotToComponent) {
|
|
1488
|
+
const exportName = exportedNames.has(componentName) ? componentName : null;
|
|
1489
|
+
if (!exportName) continue;
|
|
1490
|
+
const cvaVarName = findCvaForComponent(componentName, cvaMap);
|
|
1491
|
+
const cvaData = cvaVarName ? cvaMap.get(cvaVarName) : null;
|
|
1492
|
+
const name = dataSlot.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
|
|
1493
|
+
entries.push({
|
|
1379
1494
|
name,
|
|
1380
1495
|
filePath,
|
|
1381
1496
|
dataSlot,
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1497
|
+
exportName,
|
|
1498
|
+
baseClasses: cvaData?.baseClasses || "",
|
|
1499
|
+
variants: cvaData?.variants || [],
|
|
1500
|
+
tokenReferences: tokenRefs,
|
|
1501
|
+
acceptsChildren: acceptsChildrenSet.has(componentName)
|
|
1502
|
+
});
|
|
1386
1503
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
return
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1504
|
+
return entries;
|
|
1505
|
+
}
|
|
1506
|
+
function isIdentifierNamed(node, name) {
|
|
1507
|
+
return n3.Identifier.check(node) && node.name === name;
|
|
1508
|
+
}
|
|
1509
|
+
function isObjectProperty(node) {
|
|
1510
|
+
return n3.Property.check(node) || n3.ObjectProperty.check(node);
|
|
1511
|
+
}
|
|
1512
|
+
function isForwardRefCall(node) {
|
|
1513
|
+
if (!n3.CallExpression.check(node)) return false;
|
|
1514
|
+
if (isIdentifierNamed(node.callee, "forwardRef")) return true;
|
|
1515
|
+
if (n3.MemberExpression.check(node.callee) && isIdentifierNamed(node.callee.object, "React") && isIdentifierNamed(node.callee.property, "forwardRef")) {
|
|
1516
|
+
return true;
|
|
1517
|
+
}
|
|
1518
|
+
return false;
|
|
1398
1519
|
}
|
|
1399
|
-
function
|
|
1520
|
+
function extractStringValue(node) {
|
|
1521
|
+
if (!node) return null;
|
|
1522
|
+
if (n3.StringLiteral.check(node) || n3.Literal.check(node) && typeof node.value === "string") {
|
|
1523
|
+
return String(node.value);
|
|
1524
|
+
}
|
|
1525
|
+
if (n3.TemplateLiteral.check(node) && node.expressions.length === 0 && node.quasis.length === 1) {
|
|
1526
|
+
return node.quasis[0].value.cooked || node.quasis[0].value.raw;
|
|
1527
|
+
}
|
|
1528
|
+
return null;
|
|
1529
|
+
}
|
|
1530
|
+
function extractVariantsFromConfig(configObj) {
|
|
1400
1531
|
const dimensions = [];
|
|
1401
|
-
const
|
|
1402
|
-
if (!
|
|
1403
|
-
const
|
|
1404
|
-
const
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1532
|
+
const variantsProp = findObjProperty(configObj, "variants");
|
|
1533
|
+
if (!variantsProp || !n3.ObjectExpression.check(variantsProp.value)) return dimensions;
|
|
1534
|
+
const defaultVariantsProp = findObjProperty(configObj, "defaultVariants");
|
|
1535
|
+
const defaults = {};
|
|
1536
|
+
if (defaultVariantsProp && n3.ObjectExpression.check(defaultVariantsProp.value)) {
|
|
1537
|
+
for (const prop of defaultVariantsProp.value.properties) {
|
|
1538
|
+
if (isObjectProperty(prop)) {
|
|
1539
|
+
const key = getPropertyKeyName(prop);
|
|
1540
|
+
const val = extractStringValue(prop.value);
|
|
1541
|
+
if (key && val) defaults[key] = val;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
for (const dimProp of variantsProp.value.properties) {
|
|
1546
|
+
if (!isObjectProperty(dimProp)) continue;
|
|
1547
|
+
const dimName = getPropertyKeyName(dimProp);
|
|
1548
|
+
if (!dimName || !n3.ObjectExpression.check(dimProp.value)) continue;
|
|
1409
1549
|
const options = [];
|
|
1410
1550
|
const classes = {};
|
|
1411
|
-
const
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
);
|
|
1420
|
-
let defaultVal = options[0] || "";
|
|
1421
|
-
if (defaultVariantsSection) {
|
|
1422
|
-
const defMatch = defaultVariantsSection[1].match(
|
|
1423
|
-
new RegExp(`${dimName}\\s*:\\s*["'](\\w+)["']`)
|
|
1424
|
-
);
|
|
1425
|
-
if (defMatch) defaultVal = defMatch[1];
|
|
1551
|
+
for (const optProp of dimProp.value.properties) {
|
|
1552
|
+
if (!isObjectProperty(optProp)) continue;
|
|
1553
|
+
const optName = getPropertyKeyName(optProp);
|
|
1554
|
+
const optValue = extractStringValue(optProp.value);
|
|
1555
|
+
if (optName) {
|
|
1556
|
+
options.push(optName);
|
|
1557
|
+
classes[optName] = optValue || "";
|
|
1558
|
+
}
|
|
1426
1559
|
}
|
|
1427
1560
|
if (options.length > 0) {
|
|
1428
1561
|
dimensions.push({
|
|
1429
1562
|
name: dimName,
|
|
1430
1563
|
options,
|
|
1431
|
-
default:
|
|
1564
|
+
default: defaults[dimName] || options[0],
|
|
1432
1565
|
classes
|
|
1433
1566
|
});
|
|
1434
1567
|
}
|
|
1435
1568
|
}
|
|
1436
1569
|
return dimensions;
|
|
1437
1570
|
}
|
|
1571
|
+
function findObjProperty(obj, name) {
|
|
1572
|
+
for (const prop of obj.properties) {
|
|
1573
|
+
if (isObjectProperty(prop) && getPropertyKeyName(prop) === name) {
|
|
1574
|
+
return prop;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
return null;
|
|
1578
|
+
}
|
|
1579
|
+
function getPropertyKeyName(prop) {
|
|
1580
|
+
if (n3.Identifier.check(prop.key)) return prop.key.name;
|
|
1581
|
+
if (n3.StringLiteral.check(prop.key) || n3.Literal.check(prop.key) && typeof prop.key.value === "string") {
|
|
1582
|
+
return prop.key.value;
|
|
1583
|
+
}
|
|
1584
|
+
return null;
|
|
1585
|
+
}
|
|
1586
|
+
function findEnclosingComponentName(astPath) {
|
|
1587
|
+
let current = astPath.parent;
|
|
1588
|
+
while (current) {
|
|
1589
|
+
const node = current.node;
|
|
1590
|
+
if (n3.VariableDeclarator.check(node) && n3.Identifier.check(node.id)) {
|
|
1591
|
+
return node.id.name;
|
|
1592
|
+
}
|
|
1593
|
+
if (n3.FunctionDeclaration.check(node) && node.id) {
|
|
1594
|
+
return node.id.name;
|
|
1595
|
+
}
|
|
1596
|
+
current = current.parent;
|
|
1597
|
+
}
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
function findCvaForComponent(componentName, cvaMap) {
|
|
1601
|
+
const lower = componentName.charAt(0).toLowerCase() + componentName.slice(1);
|
|
1602
|
+
const expected = `${lower}Variants`;
|
|
1603
|
+
if (cvaMap.has(expected)) return expected;
|
|
1604
|
+
if (cvaMap.size === 1) return cvaMap.keys().next().value;
|
|
1605
|
+
return null;
|
|
1606
|
+
}
|
|
1438
1607
|
function extractTokenReferences(source) {
|
|
1439
1608
|
const tokens = /* @__PURE__ */ new Set();
|
|
1440
1609
|
const classStrings = source.match(/["'`][^"'`]*["'`]/g) || [];
|
|
@@ -1443,8 +1612,7 @@ function extractTokenReferences(source) {
|
|
|
1443
1612
|
let match;
|
|
1444
1613
|
while ((match = tokenPattern.exec(str)) !== null) {
|
|
1445
1614
|
const val = match[1];
|
|
1446
|
-
if (!val.match(/^\d/) &&
|
|
1447
|
-
!["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "full", "none"].includes(val) && !val.startsWith("[")) {
|
|
1615
|
+
if (!val.match(/^\d/) && !["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "full", "none"].includes(val) && !val.startsWith("[")) {
|
|
1448
1616
|
tokens.add(val.split("/")[0]);
|
|
1449
1617
|
}
|
|
1450
1618
|
}
|