canicode 0.6.4 → 0.7.1
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 +22 -269
- package/dist/cli/index.js +68 -59
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +18 -20
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +62 -53
- package/dist/mcp/server.js.map +1 -1
- package/package.json +2 -2
package/dist/mcp/server.js
CHANGED
|
@@ -41,7 +41,7 @@ var SEVERITY_LABELS = {
|
|
|
41
41
|
suggestion: "Suggestion"
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
// src/contracts/rule.ts
|
|
44
|
+
// src/core/contracts/rule.ts
|
|
45
45
|
z.object({
|
|
46
46
|
id: z.string(),
|
|
47
47
|
name: z.string(),
|
|
@@ -62,7 +62,7 @@ function supportsDepthWeight(category) {
|
|
|
62
62
|
return DEPTH_WEIGHT_CATEGORIES.includes(category);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
// src/rules/rule-config.ts
|
|
65
|
+
// src/core/rules/rule-config.ts
|
|
66
66
|
var RULE_CONFIGS = {
|
|
67
67
|
// ============================================
|
|
68
68
|
// Layout (11 rules)
|
|
@@ -357,7 +357,7 @@ function getRuleOption(ruleId, optionKey, defaultValue) {
|
|
|
357
357
|
return value ?? defaultValue;
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
// src/rules/rule-registry.ts
|
|
360
|
+
// src/core/rules/rule-registry.ts
|
|
361
361
|
var RuleRegistry = class {
|
|
362
362
|
rules = /* @__PURE__ */ new Map();
|
|
363
363
|
/**
|
|
@@ -418,7 +418,7 @@ function defineRule(rule) {
|
|
|
418
418
|
return rule;
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
-
// src/core/rule-engine.ts
|
|
421
|
+
// src/core/engine/rule-engine.ts
|
|
422
422
|
function calculateMaxDepth(node, currentDepth = 0) {
|
|
423
423
|
if (!node.children || node.children.length === 0) {
|
|
424
424
|
return currentDepth;
|
|
@@ -591,7 +591,7 @@ function analyzeFile(file, options) {
|
|
|
591
591
|
return engine.analyze(file);
|
|
592
592
|
}
|
|
593
593
|
|
|
594
|
-
// src/adapters/figma-client.ts
|
|
594
|
+
// src/core/adapters/figma-client.ts
|
|
595
595
|
var FIGMA_API_BASE = "https://api.figma.com/v1";
|
|
596
596
|
var FigmaClient = class _FigmaClient {
|
|
597
597
|
token;
|
|
@@ -699,7 +699,7 @@ var FigmaClientError = class extends Error {
|
|
|
699
699
|
}
|
|
700
700
|
};
|
|
701
701
|
|
|
702
|
-
// src/adapters/figma-transformer.ts
|
|
702
|
+
// src/core/adapters/figma-transformer.ts
|
|
703
703
|
function transformFigmaResponse(fileKey, response) {
|
|
704
704
|
return {
|
|
705
705
|
fileKey,
|
|
@@ -818,7 +818,7 @@ function transformStyles(styles) {
|
|
|
818
818
|
return result;
|
|
819
819
|
}
|
|
820
820
|
|
|
821
|
-
// src/adapters/figma-file-loader.ts
|
|
821
|
+
// src/core/adapters/figma-file-loader.ts
|
|
822
822
|
async function loadFigmaFileFromJson(filePath) {
|
|
823
823
|
const content = await readFile(filePath, "utf-8");
|
|
824
824
|
const data = JSON.parse(content);
|
|
@@ -916,7 +916,7 @@ function getDeviceId() {
|
|
|
916
916
|
return id;
|
|
917
917
|
}
|
|
918
918
|
|
|
919
|
-
// src/core/loader.ts
|
|
919
|
+
// src/core/engine/loader.ts
|
|
920
920
|
function isFigmaUrl(input) {
|
|
921
921
|
return input.includes("figma.com/");
|
|
922
922
|
}
|
|
@@ -959,7 +959,7 @@ async function loadFromApi(fileKey, nodeId, token) {
|
|
|
959
959
|
};
|
|
960
960
|
}
|
|
961
961
|
|
|
962
|
-
// src/adapters/figma-mcp-adapter.ts
|
|
962
|
+
// src/core/adapters/figma-mcp-adapter.ts
|
|
963
963
|
var TAG_TYPE_MAP = {
|
|
964
964
|
canvas: "CANVAS",
|
|
965
965
|
frame: "FRAME",
|
|
@@ -1090,7 +1090,7 @@ function parseMcpMetadataXml(xml, fileKey, fileName) {
|
|
|
1090
1090
|
};
|
|
1091
1091
|
}
|
|
1092
1092
|
|
|
1093
|
-
// src/core/design-data-parser.ts
|
|
1093
|
+
// src/core/engine/design-data-parser.ts
|
|
1094
1094
|
function parseDesignData(data, fileKey, fileName) {
|
|
1095
1095
|
const trimmed = data.trim();
|
|
1096
1096
|
if (trimmed.startsWith("<")) {
|
|
@@ -1108,7 +1108,7 @@ function parseDesignData(data, fileKey, fileName) {
|
|
|
1108
1108
|
);
|
|
1109
1109
|
}
|
|
1110
1110
|
|
|
1111
|
-
// src/core/scoring.ts
|
|
1111
|
+
// src/core/engine/scoring.ts
|
|
1112
1112
|
var SEVERITY_DENSITY_WEIGHT = {
|
|
1113
1113
|
blocking: 3,
|
|
1114
1114
|
risk: 2,
|
|
@@ -1271,7 +1271,7 @@ function formatScoreSummary(report) {
|
|
|
1271
1271
|
return lines.join("\n");
|
|
1272
1272
|
}
|
|
1273
1273
|
|
|
1274
|
-
// src/
|
|
1274
|
+
// src/core/ui-constants.ts
|
|
1275
1275
|
var GAUGE_R = 54;
|
|
1276
1276
|
var GAUGE_C = Math.round(2 * Math.PI * GAUGE_R);
|
|
1277
1277
|
var CATEGORY_DESCRIPTIONS = {
|
|
@@ -1282,12 +1282,31 @@ var CATEGORY_DESCRIPTIONS = {
|
|
|
1282
1282
|
"ai-readability": "Structure clarity for AI code generation, z-index, empty frames",
|
|
1283
1283
|
"handoff-risk": "Hardcoded values, text truncation, image placeholders, dev status"
|
|
1284
1284
|
};
|
|
1285
|
-
var SEVERITY_ORDER = [
|
|
1285
|
+
var SEVERITY_ORDER = [
|
|
1286
|
+
"blocking",
|
|
1287
|
+
"risk",
|
|
1288
|
+
"missing-info",
|
|
1289
|
+
"suggestion"
|
|
1290
|
+
];
|
|
1291
|
+
|
|
1292
|
+
// src/core/ui-helpers.ts
|
|
1286
1293
|
function gaugeColor(pct) {
|
|
1287
1294
|
if (pct >= 75) return "#22c55e";
|
|
1288
1295
|
if (pct >= 50) return "#f59e0b";
|
|
1289
1296
|
return "#ef4444";
|
|
1290
1297
|
}
|
|
1298
|
+
function escapeHtml(text) {
|
|
1299
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1300
|
+
}
|
|
1301
|
+
function severityDot(sev) {
|
|
1302
|
+
const map = {
|
|
1303
|
+
blocking: "bg-red-500",
|
|
1304
|
+
risk: "bg-amber-500",
|
|
1305
|
+
"missing-info": "bg-zinc-400",
|
|
1306
|
+
suggestion: "bg-green-500"
|
|
1307
|
+
};
|
|
1308
|
+
return map[sev];
|
|
1309
|
+
}
|
|
1291
1310
|
function severityBadge(sev) {
|
|
1292
1311
|
const map = {
|
|
1293
1312
|
blocking: "bg-red-500/10 text-red-600 border-red-500/20",
|
|
@@ -1302,15 +1321,24 @@ function scoreBadgeStyle(pct) {
|
|
|
1302
1321
|
if (pct >= 50) return "bg-amber-500/10 text-amber-700 border-amber-500/20";
|
|
1303
1322
|
return "bg-red-500/10 text-red-700 border-red-500/20";
|
|
1304
1323
|
}
|
|
1305
|
-
function
|
|
1306
|
-
const
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
"
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1324
|
+
function renderGaugeSvg(pct, size, strokeW, grade) {
|
|
1325
|
+
const offset = GAUGE_C * (1 - pct / 100);
|
|
1326
|
+
const color = gaugeColor(pct);
|
|
1327
|
+
if (grade) {
|
|
1328
|
+
return `<svg width="${size}" height="${size}" viewBox="0 0 120 120" class="gauge-svg block">
|
|
1329
|
+
<circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke-width="${strokeW}" stroke="#e4e4e7" class="stroke-border" />
|
|
1330
|
+
<circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke="${color}" stroke-width="${strokeW}" stroke-linecap="round" stroke-dasharray="${GAUGE_C}" stroke-dashoffset="${offset}" transform="rotate(-90 60 60)" class="gauge-fill" />
|
|
1331
|
+
<text x="60" y="60" text-anchor="middle" dominant-baseline="central" fill="currentColor" font-size="48" font-weight="700" font-family="Inter,-apple-system,sans-serif" class="font-sans">${escapeHtml(grade)}</text>
|
|
1332
|
+
</svg>`;
|
|
1333
|
+
}
|
|
1334
|
+
return `<svg width="${size}" height="${size}" viewBox="0 0 120 120" class="gauge-svg block">
|
|
1335
|
+
<circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke-width="${strokeW}" stroke="#e4e4e7" class="stroke-border" />
|
|
1336
|
+
<circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke="${color}" stroke-width="${strokeW}" stroke-linecap="round" stroke-dasharray="${GAUGE_C}" stroke-dashoffset="${offset}" transform="rotate(-90 60 60)" class="gauge-fill" />
|
|
1337
|
+
<text x="60" y="62" text-anchor="middle" dominant-baseline="central" fill="currentColor" font-size="28" font-weight="700" font-family="Inter,-apple-system,sans-serif" class="font-sans">${pct}</text>
|
|
1338
|
+
</svg>`;
|
|
1313
1339
|
}
|
|
1340
|
+
|
|
1341
|
+
// src/core/report-html/index.ts
|
|
1314
1342
|
function generateHtmlReport(file, result, scores, options) {
|
|
1315
1343
|
const screenshotMap = new Map(
|
|
1316
1344
|
(options?.nodeScreenshots ?? []).map((ns) => [ns.nodeId, ns])
|
|
@@ -1467,23 +1495,6 @@ ${figmaToken ? ` <script>
|
|
|
1467
1495
|
</body>
|
|
1468
1496
|
</html>`;
|
|
1469
1497
|
}
|
|
1470
|
-
function renderGaugeSvg(pct, size, strokeW, grade) {
|
|
1471
|
-
const offset = GAUGE_C * (1 - pct / 100);
|
|
1472
|
-
const color = gaugeColor(pct);
|
|
1473
|
-
if (grade) {
|
|
1474
|
-
return `<svg width="${size}" height="${size}" viewBox="0 0 120 120" class="block">
|
|
1475
|
-
<circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke-width="${strokeW}" class="stroke-border" />
|
|
1476
|
-
<circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke="${color}" stroke-width="${strokeW}" stroke-linecap="round" stroke-dasharray="${GAUGE_C}" stroke-dashoffset="${offset}" transform="rotate(-90 60 60)" class="gauge-fill" />
|
|
1477
|
-
<text x="60" y="60" text-anchor="middle" dominant-baseline="central" fill="currentColor" font-size="52" font-weight="700" class="font-sans">${esc(grade)}</text>
|
|
1478
|
-
</svg>`;
|
|
1479
|
-
}
|
|
1480
|
-
const fontSize = 32;
|
|
1481
|
-
return `<svg width="${size}" height="${size}" viewBox="0 0 120 120" class="block">
|
|
1482
|
-
<circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke-width="${strokeW}" class="stroke-border" />
|
|
1483
|
-
<circle cx="60" cy="60" r="${GAUGE_R}" fill="none" stroke="${color}" stroke-width="${strokeW}" stroke-linecap="round" stroke-dasharray="${GAUGE_C}" stroke-dashoffset="${offset}" transform="rotate(-90 60 60)" class="gauge-fill" />
|
|
1484
|
-
<text x="60" y="62" text-anchor="middle" dominant-baseline="central" fill="currentColor" font-size="${fontSize}" font-weight="700" class="font-sans">${pct}</text>
|
|
1485
|
-
</svg>`;
|
|
1486
|
-
}
|
|
1487
1498
|
function renderSummaryDot(dotClass, count, label) {
|
|
1488
1499
|
return `<div class="flex items-center gap-2">
|
|
1489
1500
|
<span class="w-2.5 h-2.5 rounded-full ${dotClass}"></span>
|
|
@@ -1595,9 +1606,7 @@ function groupIssuesByCategory(issues) {
|
|
|
1595
1606
|
for (const issue of issues) grouped.get(issue.rule.definition.category).push(issue);
|
|
1596
1607
|
return grouped;
|
|
1597
1608
|
}
|
|
1598
|
-
|
|
1599
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1600
|
-
}
|
|
1609
|
+
var esc = escapeHtml;
|
|
1601
1610
|
var RuleOverrideSchema = z.object({
|
|
1602
1611
|
score: z.number().int().max(0).optional(),
|
|
1603
1612
|
severity: SeveritySchema.optional(),
|
|
@@ -1700,7 +1709,7 @@ var CustomRuleSchema = z.object({
|
|
|
1700
1709
|
});
|
|
1701
1710
|
var CustomRulesFileSchema = z.array(CustomRuleSchema);
|
|
1702
1711
|
|
|
1703
|
-
// src/rules/custom/custom-rule-loader.ts
|
|
1712
|
+
// src/core/rules/custom/custom-rule-loader.ts
|
|
1704
1713
|
async function loadCustomRules(filePath) {
|
|
1705
1714
|
const absPath = resolve(filePath);
|
|
1706
1715
|
const raw = await readFile(absPath, "utf-8");
|
|
@@ -1778,7 +1787,7 @@ function createPatternCheck(cr) {
|
|
|
1778
1787
|
};
|
|
1779
1788
|
}
|
|
1780
1789
|
|
|
1781
|
-
// src/monitoring/events.ts
|
|
1790
|
+
// src/core/monitoring/events.ts
|
|
1782
1791
|
var EVENT_PREFIX = "cic_";
|
|
1783
1792
|
var EVENTS = {
|
|
1784
1793
|
// Analysis
|
|
@@ -1796,7 +1805,7 @@ var EVENTS = {
|
|
|
1796
1805
|
CLI_INIT: `${EVENT_PREFIX}cli_init`
|
|
1797
1806
|
};
|
|
1798
1807
|
|
|
1799
|
-
// src/monitoring/capture.ts
|
|
1808
|
+
// src/core/monitoring/capture.ts
|
|
1800
1809
|
var monitoringEnabled = false;
|
|
1801
1810
|
var posthogApiKey;
|
|
1802
1811
|
var sentryDsn;
|
|
@@ -1901,7 +1910,7 @@ function shutdownCapture() {
|
|
|
1901
1910
|
commonProps = {};
|
|
1902
1911
|
}
|
|
1903
1912
|
|
|
1904
|
-
// src/monitoring/index.ts
|
|
1913
|
+
// src/core/monitoring/index.ts
|
|
1905
1914
|
function initMonitoring(config2) {
|
|
1906
1915
|
initCapture(config2);
|
|
1907
1916
|
}
|
|
@@ -1924,17 +1933,17 @@ function shutdownMonitoring() {
|
|
|
1924
1933
|
}
|
|
1925
1934
|
}
|
|
1926
1935
|
|
|
1927
|
-
// src/monitoring/keys.ts
|
|
1936
|
+
// src/core/monitoring/keys.ts
|
|
1928
1937
|
var POSTHOG_API_KEY = "phc_rBFeG140KqJLpUnlpYDEFgdMM6JozZeqQsf9twXf5Dq" ;
|
|
1929
1938
|
var SENTRY_DSN = "https://80a836a8300b25f17ef5bbf23afb5b3a@o4511080656207872.ingest.us.sentry.io/4511080661319680" ;
|
|
1930
1939
|
|
|
1931
|
-
// src/rules/excluded-names.ts
|
|
1940
|
+
// src/core/rules/excluded-names.ts
|
|
1932
1941
|
var EXCLUDED_NAME_PATTERN = /(badge|close|dismiss|overlay|float|fab|dot|indicator|corner|decoration|tag|status|notification|icon|ico|image|asset|filter|dim|dimmed|bg|background|logo|avatar|divider|separator|nav|navigation|gnb|header|footer|sidebar|toolbar|modal|dialog|popup|toast|tooltip|dropdown|menu|sticky|spinner|loader|cursor|cta|chatbot|thumb|thumbnail|tabbar|tab-bar|statusbar|status-bar)/i;
|
|
1933
1942
|
function isExcludedName(name) {
|
|
1934
1943
|
return EXCLUDED_NAME_PATTERN.test(name);
|
|
1935
1944
|
}
|
|
1936
1945
|
|
|
1937
|
-
// src/rules/layout/index.ts
|
|
1946
|
+
// src/core/rules/layout/index.ts
|
|
1938
1947
|
function isContainerNode(node) {
|
|
1939
1948
|
return node.type === "FRAME" || node.type === "GROUP" || node.type === "COMPONENT";
|
|
1940
1949
|
}
|
|
@@ -2221,7 +2230,7 @@ defineRule({
|
|
|
2221
2230
|
check: inconsistentSiblingLayoutDirectionCheck
|
|
2222
2231
|
});
|
|
2223
2232
|
|
|
2224
|
-
// src/rules/token/index.ts
|
|
2233
|
+
// src/core/rules/token/index.ts
|
|
2225
2234
|
function hasStyleReference(node, styleType) {
|
|
2226
2235
|
return node.styles !== void 0 && styleType in node.styles;
|
|
2227
2236
|
}
|
|
@@ -2425,7 +2434,7 @@ defineRule({
|
|
|
2425
2434
|
check: multipleFillColorsCheck
|
|
2426
2435
|
});
|
|
2427
2436
|
|
|
2428
|
-
// src/rules/component/index.ts
|
|
2437
|
+
// src/core/rules/component/index.ts
|
|
2429
2438
|
function isComponentInstance(node) {
|
|
2430
2439
|
return node.type === "INSTANCE";
|
|
2431
2440
|
}
|
|
@@ -2599,7 +2608,7 @@ defineRule({
|
|
|
2599
2608
|
check: singleUseComponentCheck
|
|
2600
2609
|
});
|
|
2601
2610
|
|
|
2602
|
-
// src/rules/naming/index.ts
|
|
2611
|
+
// src/core/rules/naming/index.ts
|
|
2603
2612
|
var DEFAULT_NAME_PATTERNS = [
|
|
2604
2613
|
/^Frame\s*\d*$/i,
|
|
2605
2614
|
/^Group\s*\d*$/i,
|
|
@@ -2784,7 +2793,7 @@ defineRule({
|
|
|
2784
2793
|
check: tooLongNameCheck
|
|
2785
2794
|
});
|
|
2786
2795
|
|
|
2787
|
-
// src/rules/ai-readability/index.ts
|
|
2796
|
+
// src/core/rules/ai-readability/index.ts
|
|
2788
2797
|
function hasAutoLayout2(node) {
|
|
2789
2798
|
return node.layoutMode !== void 0 && node.layoutMode !== "NONE";
|
|
2790
2799
|
}
|
|
@@ -2961,7 +2970,7 @@ defineRule({
|
|
|
2961
2970
|
check: emptyFrameCheck
|
|
2962
2971
|
});
|
|
2963
2972
|
|
|
2964
|
-
// src/rules/handoff-risk/index.ts
|
|
2973
|
+
// src/core/rules/handoff-risk/index.ts
|
|
2965
2974
|
function hasAutoLayout3(node) {
|
|
2966
2975
|
return node.layoutMode !== void 0 && node.layoutMode !== "NONE";
|
|
2967
2976
|
}
|