@vizejs/vite-plugin-musea 0.212.0 → 0.214.0
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/README.md +45 -0
- package/client.d.ts +19 -0
- package/dist/gallery/assets/{MonacoEditor-B2IJhz0Y.js → MonacoEditor-CFvlW1hH.js} +2 -2
- package/dist/gallery/assets/{cssMode-Wf2sPjo8.js → cssMode-CakihE-X.js} +1 -1
- package/dist/gallery/assets/{editor.api2-BqfVxaLn.js → editor.api2-Cy8xN_Vy.js} +1 -1
- package/dist/gallery/assets/{editor.main-D0q_EY2R.js → editor.main-BLvJY5pY.js} +2 -2
- package/dist/gallery/assets/{freemarker2-B9IYORwa.js → freemarker2-8JKQlxkN.js} +1 -1
- package/dist/gallery/assets/{handlebars-CtmQAyr9.js → handlebars-DswXPT5M.js} +1 -1
- package/dist/gallery/assets/{html-DjlePv7f.js → html-CHz-ZCgY.js} +1 -1
- package/dist/gallery/assets/{htmlMode-CKqPNIWJ.js → htmlMode-DSb6gosw.js} +1 -1
- package/dist/gallery/assets/{index-DGDOzWKX.js → index-CfyRpnTg.js} +21 -21
- package/dist/gallery/assets/index-DrcH3Amz.css +1 -0
- package/dist/gallery/assets/{javascript-t8XpHDSo.js → javascript-93Vdo_Zi.js} +1 -1
- package/dist/gallery/assets/{jsonMode-C9VGEvKg.js → jsonMode-C41SvLGl.js} +1 -1
- package/dist/gallery/assets/{liquid-B1SQ490s.js → liquid-Ba4G1nCV.js} +1 -1
- package/dist/gallery/assets/{lspLanguageFeatures-BtMEaqrF.js → lspLanguageFeatures-DQpvFrJR.js} +1 -1
- package/dist/gallery/assets/{mdx-BcRzkdPF.js → mdx-kar2jls9.js} +1 -1
- package/dist/gallery/assets/{monaco.contribution-JsWDXWUd.js → monaco.contribution-D9SaY72O.js} +2 -2
- package/dist/gallery/assets/{python-DrL7J2F1.js → python-Cl3SeCHc.js} +1 -1
- package/dist/gallery/assets/{razor-DnIM76SH.js → razor-BddYKQ4w.js} +1 -1
- package/dist/gallery/assets/{tsMode-D0BSp2Hl.js → tsMode-CHpNkjIG.js} +1 -1
- package/dist/gallery/assets/{typescript-CIlbt26G.js → typescript-CiUtldbq.js} +1 -1
- package/dist/gallery/assets/{workers-BOfCFwI1.js → workers-BsJar7qW.js} +1 -1
- package/dist/gallery/assets/{xml-ChCK-qRB.js → xml-B2FG-0eD.js} +1 -1
- package/dist/gallery/assets/{yaml-j7nYrnzD.js → yaml-Dt-gwFST.js} +1 -1
- package/dist/gallery/index.html +2 -2
- package/dist/index.d.mts +5 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +405 -103
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -5
- package/dist/gallery/assets/index-BiMZ3Oo8.css +0 -1
package/dist/index.mjs
CHANGED
|
@@ -876,63 +876,70 @@ function generatePreviewHtml(art, variant, _basePath, viteBase) {
|
|
|
876
876
|
<title>${escapeHtml(art.metadata.title)} - ${escapeHtml(variant.name)}</title>
|
|
877
877
|
<script type="module" src="${base}/@vite/client"><\/script>
|
|
878
878
|
<style>
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
879
|
+
@layer musea-preview {
|
|
880
|
+
*,
|
|
881
|
+
*::before,
|
|
882
|
+
*::after {
|
|
883
|
+
box-sizing: border-box;
|
|
884
|
+
}
|
|
885
|
+
:root {
|
|
886
|
+
--musea-preview-padding: clamp(1rem, 2vw, 1.5rem);
|
|
887
|
+
}
|
|
888
|
+
html, body {
|
|
889
|
+
width: 100%;
|
|
890
|
+
height: 100%;
|
|
891
|
+
margin: 0;
|
|
892
|
+
}
|
|
893
|
+
body {
|
|
894
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
895
|
+
background: #ffffff;
|
|
896
|
+
padding: var(--musea-preview-padding);
|
|
897
|
+
overflow: auto;
|
|
898
|
+
}
|
|
899
|
+
.musea-variant {
|
|
900
|
+
width: 100%;
|
|
901
|
+
min-height: calc(100vh - (var(--musea-preview-padding) * 2));
|
|
902
|
+
}
|
|
903
|
+
.musea-error {
|
|
904
|
+
color: #dc2626;
|
|
905
|
+
background: #fef2f2;
|
|
906
|
+
border: 1px solid #fecaca;
|
|
907
|
+
border-radius: 8px;
|
|
908
|
+
padding: 1rem;
|
|
909
|
+
font-size: 0.875rem;
|
|
910
|
+
max-width: 400px;
|
|
911
|
+
}
|
|
912
|
+
.musea-error-title {
|
|
913
|
+
font-weight: 600;
|
|
914
|
+
margin-bottom: 0.5rem;
|
|
915
|
+
}
|
|
916
|
+
.musea-error pre {
|
|
917
|
+
font-family: monospace;
|
|
918
|
+
font-size: 0.75rem;
|
|
919
|
+
white-space: pre-wrap;
|
|
920
|
+
word-break: break-all;
|
|
921
|
+
margin-top: 0.5rem;
|
|
922
|
+
padding: 0.5rem;
|
|
923
|
+
background: #fff;
|
|
924
|
+
border-radius: 4px;
|
|
925
|
+
}
|
|
926
|
+
.musea-loading {
|
|
927
|
+
display: flex;
|
|
928
|
+
align-items: center;
|
|
929
|
+
gap: 0.75rem;
|
|
930
|
+
color: #6b7280;
|
|
931
|
+
font-size: 0.875rem;
|
|
932
|
+
}
|
|
933
|
+
.musea-spinner {
|
|
934
|
+
width: 20px;
|
|
935
|
+
height: 20px;
|
|
936
|
+
border: 2px solid #e5e7eb;
|
|
937
|
+
border-top-color: #3b82f6;
|
|
938
|
+
border-radius: 50%;
|
|
939
|
+
animation: musea-preview-spin 0.8s linear infinite;
|
|
940
|
+
}
|
|
934
941
|
}
|
|
935
|
-
@keyframes spin { to { transform: rotate(360deg); } }
|
|
942
|
+
@keyframes musea-preview-spin { to { transform: rotate(360deg); } }
|
|
936
943
|
|
|
937
944
|
/* Musea Addons: Checkerboard background for transparent mode */
|
|
938
945
|
.musea-bg-checkerboard {
|
|
@@ -1483,6 +1490,220 @@ function nullRecord(record) {
|
|
|
1483
1490
|
return Object.assign(Object.create(null), record);
|
|
1484
1491
|
}
|
|
1485
1492
|
//#endregion
|
|
1493
|
+
//#region src/tokens/tailwind.ts
|
|
1494
|
+
const TAILWIND_TOKEN_EXTENSIONS = new Set([
|
|
1495
|
+
".css",
|
|
1496
|
+
".pcss",
|
|
1497
|
+
".postcss"
|
|
1498
|
+
]);
|
|
1499
|
+
const VARIABLE_DECLARATION_RE = /(--[A-Za-z0-9_-]+)\s*:\s*([^;{}]+);/g;
|
|
1500
|
+
const CSS_COMMENT_RE = /\/\*[\s\S]*?\*\//g;
|
|
1501
|
+
const NAMESPACE_MAPPINGS = [
|
|
1502
|
+
{
|
|
1503
|
+
prefix: "inset-shadow",
|
|
1504
|
+
category: "shadow",
|
|
1505
|
+
pathPrefix: ["inset"],
|
|
1506
|
+
type: "shadow"
|
|
1507
|
+
},
|
|
1508
|
+
{
|
|
1509
|
+
prefix: "drop-shadow",
|
|
1510
|
+
category: "shadow",
|
|
1511
|
+
pathPrefix: ["drop"],
|
|
1512
|
+
type: "shadow"
|
|
1513
|
+
},
|
|
1514
|
+
{
|
|
1515
|
+
prefix: "font-weight",
|
|
1516
|
+
category: "typography",
|
|
1517
|
+
pathPrefix: ["fontWeight"],
|
|
1518
|
+
type: "fontWeight"
|
|
1519
|
+
},
|
|
1520
|
+
{
|
|
1521
|
+
prefix: "color",
|
|
1522
|
+
category: "color",
|
|
1523
|
+
type: "color"
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
prefix: "spacing",
|
|
1527
|
+
category: "spacing",
|
|
1528
|
+
type: "dimension"
|
|
1529
|
+
},
|
|
1530
|
+
{
|
|
1531
|
+
prefix: "font",
|
|
1532
|
+
category: "typography",
|
|
1533
|
+
pathPrefix: ["font"],
|
|
1534
|
+
type: "fontFamily"
|
|
1535
|
+
},
|
|
1536
|
+
{
|
|
1537
|
+
prefix: "text",
|
|
1538
|
+
category: "typography",
|
|
1539
|
+
pathPrefix: ["fontSize"],
|
|
1540
|
+
type: "dimension"
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
prefix: "leading",
|
|
1544
|
+
category: "typography",
|
|
1545
|
+
pathPrefix: ["lineHeight"],
|
|
1546
|
+
type: "number"
|
|
1547
|
+
},
|
|
1548
|
+
{
|
|
1549
|
+
prefix: "tracking",
|
|
1550
|
+
category: "typography",
|
|
1551
|
+
pathPrefix: ["letterSpacing"],
|
|
1552
|
+
type: "dimension"
|
|
1553
|
+
},
|
|
1554
|
+
{
|
|
1555
|
+
prefix: "radius",
|
|
1556
|
+
category: "radius",
|
|
1557
|
+
type: "dimension"
|
|
1558
|
+
},
|
|
1559
|
+
{
|
|
1560
|
+
prefix: "shadow",
|
|
1561
|
+
category: "shadow",
|
|
1562
|
+
type: "shadow"
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
prefix: "breakpoint",
|
|
1566
|
+
category: "breakpoint",
|
|
1567
|
+
type: "dimension"
|
|
1568
|
+
},
|
|
1569
|
+
{
|
|
1570
|
+
prefix: "container",
|
|
1571
|
+
category: "container",
|
|
1572
|
+
type: "dimension"
|
|
1573
|
+
},
|
|
1574
|
+
{
|
|
1575
|
+
prefix: "ease",
|
|
1576
|
+
category: "easing",
|
|
1577
|
+
type: "cubicBezier"
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
prefix: "animate",
|
|
1581
|
+
category: "animation",
|
|
1582
|
+
type: "transition"
|
|
1583
|
+
},
|
|
1584
|
+
{
|
|
1585
|
+
prefix: "blur",
|
|
1586
|
+
category: "blur",
|
|
1587
|
+
type: "dimension"
|
|
1588
|
+
},
|
|
1589
|
+
{
|
|
1590
|
+
prefix: "perspective",
|
|
1591
|
+
category: "perspective",
|
|
1592
|
+
type: "dimension"
|
|
1593
|
+
},
|
|
1594
|
+
{
|
|
1595
|
+
prefix: "aspect",
|
|
1596
|
+
category: "aspectRatio",
|
|
1597
|
+
type: "number"
|
|
1598
|
+
}
|
|
1599
|
+
];
|
|
1600
|
+
async function isTailwindTokenPath(tokensPath) {
|
|
1601
|
+
const stat = await fs.promises.stat(tokensPath).catch(() => null);
|
|
1602
|
+
if (!stat) return false;
|
|
1603
|
+
if (stat.isFile()) return TAILWIND_TOKEN_EXTENSIONS.has(path.extname(tokensPath));
|
|
1604
|
+
if (!stat.isDirectory()) return false;
|
|
1605
|
+
return (await fs.promises.readdir(tokensPath, { withFileTypes: true })).some((entry) => entry.isFile() && TAILWIND_TOKEN_EXTENSIONS.has(path.extname(entry.name)));
|
|
1606
|
+
}
|
|
1607
|
+
async function parseTailwindTokens(tokensPath) {
|
|
1608
|
+
const files = await collectTailwindTokenFiles(tokensPath);
|
|
1609
|
+
const candidates = [];
|
|
1610
|
+
for (const file of files) {
|
|
1611
|
+
const css = await fs.promises.readFile(file, "utf-8");
|
|
1612
|
+
candidates.push(...extractTailwindTokenCandidates(css));
|
|
1613
|
+
}
|
|
1614
|
+
return normalizeCategories(buildCategories(candidates));
|
|
1615
|
+
}
|
|
1616
|
+
async function collectTailwindTokenFiles(tokensPath) {
|
|
1617
|
+
if ((await fs.promises.stat(tokensPath)).isFile()) return [tokensPath];
|
|
1618
|
+
return (await fs.promises.readdir(tokensPath, { withFileTypes: true })).filter((entry) => entry.isFile() && TAILWIND_TOKEN_EXTENSIONS.has(path.extname(entry.name))).map((entry) => path.join(tokensPath, entry.name)).sort();
|
|
1619
|
+
}
|
|
1620
|
+
function extractTailwindTokenCandidates(css) {
|
|
1621
|
+
const source = css.replace(CSS_COMMENT_RE, "");
|
|
1622
|
+
const candidates = [];
|
|
1623
|
+
let match;
|
|
1624
|
+
VARIABLE_DECLARATION_RE.lastIndex = 0;
|
|
1625
|
+
while ((match = VARIABLE_DECLARATION_RE.exec(source)) !== null) {
|
|
1626
|
+
const variable = match[1];
|
|
1627
|
+
const value = match[2].trim();
|
|
1628
|
+
const mapped = mapTailwindVariable(variable);
|
|
1629
|
+
if (!mapped || !value) continue;
|
|
1630
|
+
candidates.push({
|
|
1631
|
+
variable,
|
|
1632
|
+
value,
|
|
1633
|
+
...mapped
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
return candidates;
|
|
1637
|
+
}
|
|
1638
|
+
function mapTailwindVariable(variable) {
|
|
1639
|
+
const name = variable.replace(/^--/, "");
|
|
1640
|
+
const mapping = NAMESPACE_MAPPINGS.find((candidate) => name === candidate.prefix || name.startsWith(`${candidate.prefix}-`));
|
|
1641
|
+
if (!mapping) return null;
|
|
1642
|
+
const rest = name === mapping.prefix ? "DEFAULT" : name.slice(mapping.prefix.length + 1);
|
|
1643
|
+
const pathParts = [...mapping.pathPrefix ?? [], ...tailwindNameToPath(rest)];
|
|
1644
|
+
if (pathParts.length === 0) return null;
|
|
1645
|
+
return {
|
|
1646
|
+
category: mapping.category,
|
|
1647
|
+
path: pathParts,
|
|
1648
|
+
type: mapping.type
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
function tailwindNameToPath(name) {
|
|
1652
|
+
if (!name || name === "DEFAULT") return ["DEFAULT"];
|
|
1653
|
+
const parts = name.split("-").filter(Boolean);
|
|
1654
|
+
if (parts.length <= 1) return parts;
|
|
1655
|
+
const last = parts[parts.length - 1];
|
|
1656
|
+
if (/^\d+$/.test(last)) return [...parts.slice(0, -1), last];
|
|
1657
|
+
return [parts.join("-")];
|
|
1658
|
+
}
|
|
1659
|
+
function buildCategories(candidates) {
|
|
1660
|
+
const variableToPath = /* @__PURE__ */ new Map();
|
|
1661
|
+
for (const candidate of candidates) variableToPath.set(candidate.variable, [candidate.category, ...candidate.path].join("."));
|
|
1662
|
+
const categoryMap = /* @__PURE__ */ new Map();
|
|
1663
|
+
for (const candidate of candidates) {
|
|
1664
|
+
const category = categoryMap.get(candidate.category) ?? {
|
|
1665
|
+
name: candidate.category,
|
|
1666
|
+
tokens: nullRecord({})
|
|
1667
|
+
};
|
|
1668
|
+
categoryMap.set(candidate.category, category);
|
|
1669
|
+
const valueReference = parseTailwindVariableReference(candidate.value);
|
|
1670
|
+
const referencedPath = valueReference ? variableToPath.get(valueReference) : void 0;
|
|
1671
|
+
const token = {
|
|
1672
|
+
value: referencedPath ? `{${referencedPath}}` : candidate.value,
|
|
1673
|
+
type: candidate.type,
|
|
1674
|
+
description: `Tailwind theme variable ${candidate.variable}`,
|
|
1675
|
+
attributes: { tailwindVariable: candidate.variable },
|
|
1676
|
+
$tier: referencedPath ? "semantic" : "primitive",
|
|
1677
|
+
...referencedPath ? { $reference: referencedPath } : {}
|
|
1678
|
+
};
|
|
1679
|
+
setCategoryToken(category, candidate.path, token);
|
|
1680
|
+
}
|
|
1681
|
+
return [...categoryMap.values()];
|
|
1682
|
+
}
|
|
1683
|
+
function parseTailwindVariableReference(value) {
|
|
1684
|
+
return value.match(/^var\(\s*(--[A-Za-z0-9_-]+)\s*(?:,[^)]+)?\)$/)?.[1];
|
|
1685
|
+
}
|
|
1686
|
+
function setCategoryToken(category, pathParts, token) {
|
|
1687
|
+
if (pathParts.length === 1) {
|
|
1688
|
+
category.tokens[pathParts[0]] = token;
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
let current = category;
|
|
1692
|
+
for (const part of pathParts.slice(0, -1)) {
|
|
1693
|
+
current.subcategories ??= [];
|
|
1694
|
+
let next = current.subcategories.find((subcategory) => subcategory.name === part);
|
|
1695
|
+
if (!next) {
|
|
1696
|
+
next = {
|
|
1697
|
+
name: part,
|
|
1698
|
+
tokens: nullRecord({})
|
|
1699
|
+
};
|
|
1700
|
+
current.subcategories.push(next);
|
|
1701
|
+
}
|
|
1702
|
+
current = next;
|
|
1703
|
+
}
|
|
1704
|
+
current.tokens[pathParts[pathParts.length - 1]] = token;
|
|
1705
|
+
}
|
|
1706
|
+
//#endregion
|
|
1486
1707
|
//#region src/tokens/parser.ts
|
|
1487
1708
|
/**
|
|
1488
1709
|
* Token parsing utilities for Style Dictionary integration.
|
|
@@ -1490,9 +1711,10 @@ function nullRecord(record) {
|
|
|
1490
1711
|
* Thin native binding for design token files (JSON) and directories.
|
|
1491
1712
|
*/
|
|
1492
1713
|
/**
|
|
1493
|
-
* Parse Style Dictionary tokens file or
|
|
1714
|
+
* Parse Style Dictionary tokens file/directory or Tailwind CSS theme variables.
|
|
1494
1715
|
*/
|
|
1495
1716
|
async function parseTokens(tokensPath) {
|
|
1717
|
+
if (await isTailwindTokenPath(tokensPath)) return parseTailwindTokens(tokensPath);
|
|
1496
1718
|
return normalizeCategories(tokenNative().parseDesignTokensFromPath(tokensPath));
|
|
1497
1719
|
}
|
|
1498
1720
|
//#endregion
|
|
@@ -1532,6 +1754,13 @@ function scanTokenUsage(artFiles, tokenMap) {
|
|
|
1532
1754
|
const existing = valueLookup.get(normalized);
|
|
1533
1755
|
if (existing) existing.push(tokenPath);
|
|
1534
1756
|
else valueLookup.set(normalized, [tokenPath]);
|
|
1757
|
+
const tailwindVariable = token.attributes?.tailwindVariable;
|
|
1758
|
+
if (typeof tailwindVariable === "string") {
|
|
1759
|
+
const normalizedVariable = normalizeTokenValue(`var(${tailwindVariable})`);
|
|
1760
|
+
const variableMatches = valueLookup.get(normalizedVariable);
|
|
1761
|
+
if (variableMatches) variableMatches.push(tokenPath);
|
|
1762
|
+
else valueLookup.set(normalizedVariable, [tokenPath]);
|
|
1763
|
+
}
|
|
1535
1764
|
}
|
|
1536
1765
|
const usageMap = {};
|
|
1537
1766
|
for (const [artPath, artInfo] of artFiles) {
|
|
@@ -1600,6 +1829,7 @@ function scanTokenUsage(artFiles, tokenMap) {
|
|
|
1600
1829
|
* Handles building flat token maps from categories, resolving reference chains,
|
|
1601
1830
|
* reading/writing raw token files, and validating semantic references.
|
|
1602
1831
|
*/
|
|
1832
|
+
const JSON_TOKEN_EXTENSIONS = new Set([".json", ".tokens.json"]);
|
|
1603
1833
|
const UNSAFE_TOKEN_PATH_SEGMENTS = new Set([
|
|
1604
1834
|
"__proto__",
|
|
1605
1835
|
"prototype",
|
|
@@ -1629,6 +1859,7 @@ function resolveReferences(categories, _tokenMap) {
|
|
|
1629
1859
|
* Read raw JSON token file.
|
|
1630
1860
|
*/
|
|
1631
1861
|
async function readRawTokenFile(tokensPath) {
|
|
1862
|
+
if (!isJsonTokenPath(tokensPath)) throw new Error("Token editing is only supported for JSON token files");
|
|
1632
1863
|
const content = await fs.promises.readFile(tokensPath, "utf-8");
|
|
1633
1864
|
return JSON.parse(content);
|
|
1634
1865
|
}
|
|
@@ -1636,10 +1867,14 @@ async function readRawTokenFile(tokensPath) {
|
|
|
1636
1867
|
* Write raw JSON token file atomically (write tmp, rename).
|
|
1637
1868
|
*/
|
|
1638
1869
|
async function writeRawTokenFile(tokensPath, data) {
|
|
1870
|
+
if (!isJsonTokenPath(tokensPath)) throw new Error("Token editing is only supported for JSON token files");
|
|
1639
1871
|
const tmpPath = tokensPath + ".tmp";
|
|
1640
1872
|
await fs.promises.writeFile(tmpPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
1641
1873
|
await fs.promises.rename(tmpPath, tokensPath);
|
|
1642
1874
|
}
|
|
1875
|
+
function isJsonTokenPath(tokensPath) {
|
|
1876
|
+
return JSON_TOKEN_EXTENSIONS.has(path.extname(tokensPath)) || tokensPath.endsWith(".tokens.json");
|
|
1877
|
+
}
|
|
1643
1878
|
/**
|
|
1644
1879
|
* Set a token at a dot-separated path in the raw JSON structure.
|
|
1645
1880
|
*/
|
|
@@ -1707,7 +1942,7 @@ function findDependentTokens(tokenMap, targetPath) {
|
|
|
1707
1942
|
* Generates HTML, Markdown, and JSON documentation from parsed token categories,
|
|
1708
1943
|
* and provides the main processStyleDictionary orchestrator function.
|
|
1709
1944
|
*/
|
|
1710
|
-
const SAFE_CSS_COLOR_PATTERN = /^(?:#[0-9a-fA-F]{3,8}|(?:rgb|hsl)a?\(\s*[-+.\d,%\s]+\))$/;
|
|
1945
|
+
const SAFE_CSS_COLOR_PATTERN = /^(?:#[0-9a-fA-F]{3,8}|(?:rgb|hsl)a?\(\s*[-+.\d,%\s/]+\)|(?:oklch|oklab|lab|lch)\(\s*[-+.\d,%\s/]+\))$/;
|
|
1711
1946
|
const DANGEROUS_PROTOCOL_PATTERN = /\b(javascript|vbscript):/gi;
|
|
1712
1947
|
function escapeTokenText(value) {
|
|
1713
1948
|
return escapeHtml(value).replace(DANGEROUS_PROTOCOL_PATTERN, "$1:");
|
|
@@ -1879,7 +2114,7 @@ function sendTokenMutationError(e, sendError) {
|
|
|
1879
2114
|
sendError(e.message, e.status);
|
|
1880
2115
|
return;
|
|
1881
2116
|
}
|
|
1882
|
-
if (e instanceof Error && /token path/i.test(e.message)) {
|
|
2117
|
+
if (e instanceof Error && /(token path|token editing)/i.test(e.message)) {
|
|
1883
2118
|
sendError(e.message, 400);
|
|
1884
2119
|
return;
|
|
1885
2120
|
}
|
|
@@ -2080,56 +2315,123 @@ async function handleArtPalette(ctx, match, sendJson, sendError) {
|
|
|
2080
2315
|
json: "{}",
|
|
2081
2316
|
typescript: ""
|
|
2082
2317
|
};
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
return;
|
|
2088
|
-
}
|
|
2089
|
-
try {
|
|
2090
|
-
const componentSource = await fs.promises.readFile(resolvedComponentPath, "utf-8");
|
|
2091
|
-
const analysis = binding.analyzeSfc ? binding.analyzeSfc(componentSource, { filename: resolvedComponentPath }) : analyzeSfcFallback(componentSource, { filename: resolvedComponentPath });
|
|
2092
|
-
if (analysis.props.length > 0) {
|
|
2093
|
-
palette.controls = analysis.props.map((prop) => {
|
|
2094
|
-
let control = "text";
|
|
2095
|
-
if (prop.type === "boolean") control = "boolean";
|
|
2096
|
-
else if (prop.type === "number") control = "number";
|
|
2097
|
-
else if (prop.type.includes("|") && !prop.type.includes("=>")) control = "select";
|
|
2098
|
-
const options = [];
|
|
2099
|
-
if (control === "select") {
|
|
2100
|
-
const optionMatches = prop.type.match(/"([^"]+)"/g);
|
|
2101
|
-
if (optionMatches) for (const opt of optionMatches) {
|
|
2102
|
-
const val = opt.replace(/"/g, "");
|
|
2103
|
-
options.push({
|
|
2104
|
-
label: val,
|
|
2105
|
-
value: val
|
|
2106
|
-
});
|
|
2107
|
-
}
|
|
2108
|
-
}
|
|
2109
|
-
return {
|
|
2110
|
-
name: prop.name,
|
|
2111
|
-
control,
|
|
2112
|
-
default_value: prop.default_value !== void 0 ? prop.default_value === "true" ? true : prop.default_value === "false" ? false : typeof prop.default_value === "string" && prop.default_value.startsWith("\"") ? prop.default_value.replace(/^"|"$/g, "") : prop.default_value : void 0,
|
|
2113
|
-
description: void 0,
|
|
2114
|
-
required: prop.required,
|
|
2115
|
-
options,
|
|
2116
|
-
range: void 0,
|
|
2117
|
-
group: void 0
|
|
2118
|
-
};
|
|
2119
|
-
});
|
|
2120
|
-
palette.json = JSON.stringify({
|
|
2121
|
-
title: palette.title,
|
|
2122
|
-
controls: palette.controls
|
|
2123
|
-
}, null, 2);
|
|
2124
|
-
palette.typescript = `export interface ${palette.title}Props {\n${palette.controls.map((c) => ` ${c.name}${c.required ? "" : "?"}: ${c.control === "boolean" ? "boolean" : c.control === "number" ? "number" : c.control === "select" ? c.options.map((o) => `"${String(o.value)}"`).join(" | ") : "string"};`).join("\n")}\n}\n`;
|
|
2125
|
-
}
|
|
2126
|
-
} catch {}
|
|
2318
|
+
const resolvedComponentPath = resolveComponentSourcePath(art, artPath, allowedSourceRoots(ctx.config.root, ctx.scanRoots));
|
|
2319
|
+
if (!resolvedComponentPath) {
|
|
2320
|
+
sendJson(palette);
|
|
2321
|
+
return;
|
|
2127
2322
|
}
|
|
2323
|
+
try {
|
|
2324
|
+
const componentSource = await fs.promises.readFile(resolvedComponentPath, "utf-8");
|
|
2325
|
+
const analysis = binding.analyzeSfc ? binding.analyzeSfc(componentSource, { filename: resolvedComponentPath }) : analyzeSfcFallback(componentSource, { filename: resolvedComponentPath });
|
|
2326
|
+
if (analysis.props.length > 0) mergeSfcPropsIntoPalette(palette, analysis.props);
|
|
2327
|
+
} catch {}
|
|
2128
2328
|
sendJson(palette);
|
|
2129
2329
|
} catch (e) {
|
|
2130
2330
|
sendError(e instanceof Error ? e.message : String(e));
|
|
2131
2331
|
}
|
|
2132
2332
|
}
|
|
2333
|
+
function mergeSfcPropsIntoPalette(palette, props) {
|
|
2334
|
+
const controlsByName = /* @__PURE__ */ new Map();
|
|
2335
|
+
for (const control of palette.controls) controlsByName.set(normalizePropName(control.name), control);
|
|
2336
|
+
for (const prop of props) {
|
|
2337
|
+
const control = controlsByName.get(normalizePropName(prop.name));
|
|
2338
|
+
if (control) applyPropMetadata(control, prop);
|
|
2339
|
+
else palette.controls.push(controlFromProp(prop));
|
|
2340
|
+
}
|
|
2341
|
+
palette.json = JSON.stringify({
|
|
2342
|
+
title: palette.title,
|
|
2343
|
+
controls: palette.controls
|
|
2344
|
+
}, null, 2);
|
|
2345
|
+
palette.typescript = generateTypescript(palette);
|
|
2346
|
+
}
|
|
2347
|
+
function applyPropMetadata(control, prop) {
|
|
2348
|
+
const inferred = inferControl(prop.type);
|
|
2349
|
+
control.name = prop.name;
|
|
2350
|
+
control.required = prop.required;
|
|
2351
|
+
if (control.default_value === void 0 && prop.default_value !== void 0) control.default_value = parseDefault(prop.default_value);
|
|
2352
|
+
if (inferred.control !== "text" || control.control === "text") control.control = inferred.control;
|
|
2353
|
+
if (inferred.options.length > 0) control.options = inferred.options;
|
|
2354
|
+
}
|
|
2355
|
+
function controlFromProp(prop) {
|
|
2356
|
+
const inferred = inferControl(prop.type);
|
|
2357
|
+
return {
|
|
2358
|
+
name: prop.name,
|
|
2359
|
+
control: inferred.control,
|
|
2360
|
+
default_value: prop.default_value === void 0 ? void 0 : parseDefault(prop.default_value),
|
|
2361
|
+
description: void 0,
|
|
2362
|
+
required: prop.required,
|
|
2363
|
+
options: inferred.options,
|
|
2364
|
+
range: void 0,
|
|
2365
|
+
group: void 0
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
function inferControl(type) {
|
|
2369
|
+
const normalized = type.trim();
|
|
2370
|
+
const options = literalOptions(normalized);
|
|
2371
|
+
if (options.length > 0) return {
|
|
2372
|
+
control: "select",
|
|
2373
|
+
options
|
|
2374
|
+
};
|
|
2375
|
+
if (normalized === "boolean") return {
|
|
2376
|
+
control: "boolean",
|
|
2377
|
+
options: []
|
|
2378
|
+
};
|
|
2379
|
+
if (normalized === "number") return {
|
|
2380
|
+
control: "number",
|
|
2381
|
+
options: []
|
|
2382
|
+
};
|
|
2383
|
+
if (normalized.includes("[]") || normalized.startsWith("Array<")) return {
|
|
2384
|
+
control: "array",
|
|
2385
|
+
options: []
|
|
2386
|
+
};
|
|
2387
|
+
if (normalized.startsWith("{") || normalized.startsWith("Record<")) return {
|
|
2388
|
+
control: "object",
|
|
2389
|
+
options: []
|
|
2390
|
+
};
|
|
2391
|
+
return {
|
|
2392
|
+
control: "text",
|
|
2393
|
+
options: []
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
function literalOptions(type) {
|
|
2397
|
+
if (!type.includes("|") || type.includes("=>")) return [];
|
|
2398
|
+
return type.split("|").map((part) => part.trim()).map((part) => {
|
|
2399
|
+
const stringMatch = part.match(/^["']([^"']+)["']$/);
|
|
2400
|
+
if (stringMatch) return stringMatch[1];
|
|
2401
|
+
if (part === "true") return true;
|
|
2402
|
+
if (part === "false") return false;
|
|
2403
|
+
const numberValue = Number(part);
|
|
2404
|
+
return Number.isFinite(numberValue) ? numberValue : void 0;
|
|
2405
|
+
}).filter((value) => value !== void 0).map((value) => ({
|
|
2406
|
+
label: String(value),
|
|
2407
|
+
value
|
|
2408
|
+
}));
|
|
2409
|
+
}
|
|
2410
|
+
function parseDefault(value) {
|
|
2411
|
+
if (typeof value !== "string") return value;
|
|
2412
|
+
if (value === "true") return true;
|
|
2413
|
+
if (value === "false") return false;
|
|
2414
|
+
if (/^-?\d+(?:\.\d+)?$/.test(value)) return Number(value);
|
|
2415
|
+
return value.replace(/^["']|["']$/g, "");
|
|
2416
|
+
}
|
|
2417
|
+
function normalizePropName(name) {
|
|
2418
|
+
return name.replace(/[-_]/g, "").toLowerCase();
|
|
2419
|
+
}
|
|
2420
|
+
function generateTypescript(palette) {
|
|
2421
|
+
const fields = palette.controls.map((control) => ` ${control.name}${control.required ? "" : "?"}: ${controlTsType(control)};`).join("\n");
|
|
2422
|
+
return `export interface ${pascalCase(palette.title)}Props {\n${fields}\n}\n`;
|
|
2423
|
+
}
|
|
2424
|
+
function controlTsType(control) {
|
|
2425
|
+
if (control.control === "boolean") return "boolean";
|
|
2426
|
+
if (control.control === "number" || control.control === "range") return "number";
|
|
2427
|
+
if (control.control === "array") return "unknown[]";
|
|
2428
|
+
if (control.control === "object") return "Record<string, unknown>";
|
|
2429
|
+
if (control.control === "select" && control.options.length > 0) return control.options.map((option) => JSON.stringify(option.value)).join(" | ");
|
|
2430
|
+
return "string";
|
|
2431
|
+
}
|
|
2432
|
+
function pascalCase(value) {
|
|
2433
|
+
return value.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
2434
|
+
}
|
|
2133
2435
|
//#endregion
|
|
2134
2436
|
//#region src/api-routes/handlers.ts
|
|
2135
2437
|
/**
|