uilint-eslint 0.1.11 → 0.1.13
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/index.d.ts +26 -0
- package/dist/index.js +507 -9
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/index.d.ts
CHANGED
|
@@ -140,11 +140,24 @@ declare const rules: {
|
|
|
140
140
|
}], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
141
141
|
name: string;
|
|
142
142
|
};
|
|
143
|
+
"consistent-dark-mode": _typescript_eslint_utils_ts_eslint.RuleModule<"inconsistentDarkMode" | "missingDarkMode", [({
|
|
144
|
+
warnOnMissingDarkMode?: boolean;
|
|
145
|
+
} | undefined)?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
146
|
+
name: string;
|
|
147
|
+
};
|
|
143
148
|
"no-direct-store-import": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectImport", [{
|
|
144
149
|
storePattern?: string;
|
|
145
150
|
}], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
146
151
|
name: string;
|
|
147
152
|
};
|
|
153
|
+
"prefer-zustand-state-management": _typescript_eslint_utils_ts_eslint.RuleModule<"excessiveStateHooks", [({
|
|
154
|
+
maxStateHooks?: number;
|
|
155
|
+
countUseState?: boolean;
|
|
156
|
+
countUseReducer?: boolean;
|
|
157
|
+
countUseContext?: boolean;
|
|
158
|
+
} | undefined)?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
159
|
+
name: string;
|
|
160
|
+
};
|
|
148
161
|
"no-mixed-component-libraries": _typescript_eslint_utils_ts_eslint.RuleModule<"mixedLibraries" | "nonPreferredLibrary", [{
|
|
149
162
|
libraries?: ("shadcn" | "mui")[];
|
|
150
163
|
preferred?: "shadcn" | "mui";
|
|
@@ -182,11 +195,24 @@ declare const plugin: {
|
|
|
182
195
|
}], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
183
196
|
name: string;
|
|
184
197
|
};
|
|
198
|
+
"consistent-dark-mode": _typescript_eslint_utils_ts_eslint.RuleModule<"inconsistentDarkMode" | "missingDarkMode", [({
|
|
199
|
+
warnOnMissingDarkMode?: boolean;
|
|
200
|
+
} | undefined)?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
201
|
+
name: string;
|
|
202
|
+
};
|
|
185
203
|
"no-direct-store-import": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectImport", [{
|
|
186
204
|
storePattern?: string;
|
|
187
205
|
}], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
188
206
|
name: string;
|
|
189
207
|
};
|
|
208
|
+
"prefer-zustand-state-management": _typescript_eslint_utils_ts_eslint.RuleModule<"excessiveStateHooks", [({
|
|
209
|
+
maxStateHooks?: number;
|
|
210
|
+
countUseState?: boolean;
|
|
211
|
+
countUseReducer?: boolean;
|
|
212
|
+
countUseContext?: boolean;
|
|
213
|
+
} | undefined)?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
214
|
+
name: string;
|
|
215
|
+
};
|
|
190
216
|
"no-mixed-component-libraries": _typescript_eslint_utils_ts_eslint.RuleModule<"mixedLibraries" | "nonPreferredLibrary", [{
|
|
191
217
|
libraries?: ("shadcn" | "mui")[];
|
|
192
218
|
preferred?: "shadcn" | "mui";
|
package/dist/index.js
CHANGED
|
@@ -240,6 +240,239 @@ function checkSpacing(context, node, classString, scale, scaleList) {
|
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
// src/rules/consistent-dark-mode.ts
|
|
244
|
+
var COLOR_PREFIXES = [
|
|
245
|
+
"bg-",
|
|
246
|
+
"text-",
|
|
247
|
+
"border-",
|
|
248
|
+
"border-t-",
|
|
249
|
+
"border-r-",
|
|
250
|
+
"border-b-",
|
|
251
|
+
"border-l-",
|
|
252
|
+
"border-x-",
|
|
253
|
+
"border-y-",
|
|
254
|
+
"ring-",
|
|
255
|
+
"ring-offset-",
|
|
256
|
+
"divide-",
|
|
257
|
+
"outline-",
|
|
258
|
+
"shadow-",
|
|
259
|
+
"accent-",
|
|
260
|
+
"caret-",
|
|
261
|
+
"fill-",
|
|
262
|
+
"stroke-",
|
|
263
|
+
"decoration-",
|
|
264
|
+
"placeholder-",
|
|
265
|
+
"from-",
|
|
266
|
+
"via-",
|
|
267
|
+
"to-"
|
|
268
|
+
];
|
|
269
|
+
var EXEMPT_SUFFIXES = ["transparent", "inherit", "current", "auto", "none"];
|
|
270
|
+
var COLOR_NAMES = /* @__PURE__ */ new Set([
|
|
271
|
+
// Grayscale
|
|
272
|
+
"slate",
|
|
273
|
+
"gray",
|
|
274
|
+
"zinc",
|
|
275
|
+
"neutral",
|
|
276
|
+
"stone",
|
|
277
|
+
// Colors
|
|
278
|
+
"red",
|
|
279
|
+
"orange",
|
|
280
|
+
"amber",
|
|
281
|
+
"yellow",
|
|
282
|
+
"lime",
|
|
283
|
+
"green",
|
|
284
|
+
"emerald",
|
|
285
|
+
"teal",
|
|
286
|
+
"cyan",
|
|
287
|
+
"sky",
|
|
288
|
+
"blue",
|
|
289
|
+
"indigo",
|
|
290
|
+
"violet",
|
|
291
|
+
"purple",
|
|
292
|
+
"fuchsia",
|
|
293
|
+
"pink",
|
|
294
|
+
"rose",
|
|
295
|
+
// Special
|
|
296
|
+
"black",
|
|
297
|
+
"white"
|
|
298
|
+
]);
|
|
299
|
+
var COLOR_VALUE_PATTERN = /^(?:(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d{1,3}(?:\/\d+)?|black|white|inherit|current|transparent|\[.+\])$/;
|
|
300
|
+
function hasDarkVariant(className) {
|
|
301
|
+
const parts = className.split(":");
|
|
302
|
+
const variants = parts.slice(0, -1);
|
|
303
|
+
return variants.includes("dark");
|
|
304
|
+
}
|
|
305
|
+
function getBaseClass(className) {
|
|
306
|
+
const parts = className.split(":");
|
|
307
|
+
return parts[parts.length - 1] || "";
|
|
308
|
+
}
|
|
309
|
+
function getColorPrefix(baseClass) {
|
|
310
|
+
const sortedPrefixes = [...COLOR_PREFIXES].sort(
|
|
311
|
+
(a, b) => b.length - a.length
|
|
312
|
+
);
|
|
313
|
+
return sortedPrefixes.find((p) => baseClass.startsWith(p)) || null;
|
|
314
|
+
}
|
|
315
|
+
function isColorValue(baseClass, prefix) {
|
|
316
|
+
const value = baseClass.slice(prefix.length);
|
|
317
|
+
if (COLOR_VALUE_PATTERN.test(value)) {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
const firstPart = value.split("-")[0];
|
|
321
|
+
if (COLOR_NAMES.has(firstPart)) {
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
function isExempt(baseClass) {
|
|
330
|
+
return EXEMPT_SUFFIXES.some((suffix) => baseClass.endsWith(suffix));
|
|
331
|
+
}
|
|
332
|
+
var consistent_dark_mode_default = createRule({
|
|
333
|
+
name: "consistent-dark-mode",
|
|
334
|
+
meta: {
|
|
335
|
+
type: "problem",
|
|
336
|
+
docs: {
|
|
337
|
+
description: "Ensure consistent dark mode theming in Tailwind classes"
|
|
338
|
+
},
|
|
339
|
+
messages: {
|
|
340
|
+
inconsistentDarkMode: "Inconsistent dark mode: '{{unthemed}}' lack dark: variants while other color classes have them.",
|
|
341
|
+
missingDarkMode: "No dark mode theming detected. Consider adding dark: variants for color classes."
|
|
342
|
+
},
|
|
343
|
+
schema: [
|
|
344
|
+
{
|
|
345
|
+
type: "object",
|
|
346
|
+
properties: {
|
|
347
|
+
warnOnMissingDarkMode: {
|
|
348
|
+
type: "boolean",
|
|
349
|
+
description: "Whether to warn when no dark mode classes are found in a file that uses Tailwind colors"
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
additionalProperties: false
|
|
353
|
+
}
|
|
354
|
+
]
|
|
355
|
+
},
|
|
356
|
+
defaultOptions: [{ warnOnMissingDarkMode: true }],
|
|
357
|
+
create(context) {
|
|
358
|
+
const options = context.options[0] || {};
|
|
359
|
+
const warnOnMissingDarkMode = options.warnOnMissingDarkMode ?? true;
|
|
360
|
+
let fileHasColorClasses = false;
|
|
361
|
+
let fileHasDarkMode = false;
|
|
362
|
+
const reportedNodes = /* @__PURE__ */ new Set();
|
|
363
|
+
function checkClassString2(node, classString) {
|
|
364
|
+
const classes = classString.split(/\s+/).filter(Boolean);
|
|
365
|
+
if (classes.length === 0) return;
|
|
366
|
+
const prefixUsage = /* @__PURE__ */ new Map();
|
|
367
|
+
for (const cls of classes) {
|
|
368
|
+
const baseClass = getBaseClass(cls);
|
|
369
|
+
const prefix = getColorPrefix(baseClass);
|
|
370
|
+
if (!prefix) continue;
|
|
371
|
+
if (isExempt(baseClass)) continue;
|
|
372
|
+
if (!isColorValue(baseClass, prefix)) continue;
|
|
373
|
+
if (!prefixUsage.has(prefix)) {
|
|
374
|
+
prefixUsage.set(prefix, {
|
|
375
|
+
hasLight: false,
|
|
376
|
+
hasDark: false,
|
|
377
|
+
lightClasses: []
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
const usage = prefixUsage.get(prefix);
|
|
381
|
+
if (hasDarkVariant(cls)) {
|
|
382
|
+
usage.hasDark = true;
|
|
383
|
+
fileHasDarkMode = true;
|
|
384
|
+
} else {
|
|
385
|
+
usage.hasLight = true;
|
|
386
|
+
usage.lightClasses.push(cls);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (prefixUsage.size > 0) {
|
|
390
|
+
fileHasColorClasses = true;
|
|
391
|
+
}
|
|
392
|
+
const entries = Array.from(prefixUsage.entries());
|
|
393
|
+
const hasSomeDark = entries.some(([_, u]) => u.hasDark);
|
|
394
|
+
if (hasSomeDark) {
|
|
395
|
+
const unthemedEntries = entries.filter(
|
|
396
|
+
([_, usage]) => usage.hasLight && !usage.hasDark
|
|
397
|
+
);
|
|
398
|
+
if (unthemedEntries.length > 0 && !reportedNodes.has(node)) {
|
|
399
|
+
reportedNodes.add(node);
|
|
400
|
+
const unthemedClasses = unthemedEntries.flatMap(
|
|
401
|
+
([_, u]) => u.lightClasses
|
|
402
|
+
);
|
|
403
|
+
context.report({
|
|
404
|
+
node,
|
|
405
|
+
messageId: "inconsistentDarkMode",
|
|
406
|
+
data: { unthemed: unthemedClasses.join(", ") }
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
function processStringValue(node, value) {
|
|
412
|
+
checkClassString2(node, value);
|
|
413
|
+
}
|
|
414
|
+
function processTemplateLiteral(node) {
|
|
415
|
+
for (const quasi of node.quasis) {
|
|
416
|
+
checkClassString2(quasi, quasi.value.raw);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return {
|
|
420
|
+
// Check className attributes in JSX
|
|
421
|
+
JSXAttribute(node) {
|
|
422
|
+
if (node.name.type === "JSXIdentifier" && (node.name.name === "className" || node.name.name === "class")) {
|
|
423
|
+
const value = node.value;
|
|
424
|
+
if (value?.type === "Literal" && typeof value.value === "string") {
|
|
425
|
+
processStringValue(value, value.value);
|
|
426
|
+
}
|
|
427
|
+
if (value?.type === "JSXExpressionContainer") {
|
|
428
|
+
const expr = value.expression;
|
|
429
|
+
if (expr.type === "Literal" && typeof expr.value === "string") {
|
|
430
|
+
processStringValue(expr, expr.value);
|
|
431
|
+
}
|
|
432
|
+
if (expr.type === "TemplateLiteral") {
|
|
433
|
+
processTemplateLiteral(expr);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
// Check cn(), clsx(), classnames(), cva() calls
|
|
439
|
+
CallExpression(node) {
|
|
440
|
+
if (node.callee.type !== "Identifier") return;
|
|
441
|
+
const name = node.callee.name;
|
|
442
|
+
if (name === "cn" || name === "clsx" || name === "classnames" || name === "cva" || name === "twMerge") {
|
|
443
|
+
for (const arg of node.arguments) {
|
|
444
|
+
if (arg.type === "Literal" && typeof arg.value === "string") {
|
|
445
|
+
processStringValue(arg, arg.value);
|
|
446
|
+
}
|
|
447
|
+
if (arg.type === "TemplateLiteral") {
|
|
448
|
+
processTemplateLiteral(arg);
|
|
449
|
+
}
|
|
450
|
+
if (arg.type === "ArrayExpression") {
|
|
451
|
+
for (const element of arg.elements) {
|
|
452
|
+
if (element?.type === "Literal" && typeof element.value === "string") {
|
|
453
|
+
processStringValue(element, element.value);
|
|
454
|
+
}
|
|
455
|
+
if (element?.type === "TemplateLiteral") {
|
|
456
|
+
processTemplateLiteral(element);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
// At the end of the file, check if Tailwind colors are used without any dark mode
|
|
464
|
+
"Program:exit"(node) {
|
|
465
|
+
if (warnOnMissingDarkMode && fileHasColorClasses && !fileHasDarkMode) {
|
|
466
|
+
context.report({
|
|
467
|
+
node,
|
|
468
|
+
messageId: "missingDarkMode"
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
243
476
|
// src/rules/no-direct-store-import.ts
|
|
244
477
|
function patternToRegex(pattern) {
|
|
245
478
|
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
@@ -304,6 +537,184 @@ var no_direct_store_import_default = createRule({
|
|
|
304
537
|
}
|
|
305
538
|
});
|
|
306
539
|
|
|
540
|
+
// src/rules/prefer-zustand-state-management.ts
|
|
541
|
+
var STATE_HOOKS = /* @__PURE__ */ new Set(["useState", "useReducer", "useContext"]);
|
|
542
|
+
var prefer_zustand_state_management_default = createRule({
|
|
543
|
+
name: "prefer-zustand-state-management",
|
|
544
|
+
meta: {
|
|
545
|
+
type: "suggestion",
|
|
546
|
+
docs: {
|
|
547
|
+
description: "Detect excessive use of React state hooks and suggest Zustand stores"
|
|
548
|
+
},
|
|
549
|
+
messages: {
|
|
550
|
+
excessiveStateHooks: "Component '{{component}}' has {{count}} state hooks (max: {{max}}). Consider using a Zustand store for state management."
|
|
551
|
+
},
|
|
552
|
+
schema: [
|
|
553
|
+
{
|
|
554
|
+
type: "object",
|
|
555
|
+
properties: {
|
|
556
|
+
maxStateHooks: {
|
|
557
|
+
type: "number",
|
|
558
|
+
minimum: 1,
|
|
559
|
+
description: "Maximum number of state hooks before warning"
|
|
560
|
+
},
|
|
561
|
+
countUseState: {
|
|
562
|
+
type: "boolean",
|
|
563
|
+
description: "Whether to count useState calls"
|
|
564
|
+
},
|
|
565
|
+
countUseReducer: {
|
|
566
|
+
type: "boolean",
|
|
567
|
+
description: "Whether to count useReducer calls"
|
|
568
|
+
},
|
|
569
|
+
countUseContext: {
|
|
570
|
+
type: "boolean",
|
|
571
|
+
description: "Whether to count useContext calls"
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
additionalProperties: false
|
|
575
|
+
}
|
|
576
|
+
]
|
|
577
|
+
},
|
|
578
|
+
defaultOptions: [
|
|
579
|
+
{
|
|
580
|
+
maxStateHooks: 3,
|
|
581
|
+
countUseState: true,
|
|
582
|
+
countUseReducer: true,
|
|
583
|
+
countUseContext: true
|
|
584
|
+
}
|
|
585
|
+
],
|
|
586
|
+
create(context) {
|
|
587
|
+
const options = context.options[0] || {};
|
|
588
|
+
const maxStateHooks = options.maxStateHooks ?? 3;
|
|
589
|
+
const countUseState = options.countUseState ?? true;
|
|
590
|
+
const countUseReducer = options.countUseReducer ?? true;
|
|
591
|
+
const countUseContext = options.countUseContext ?? true;
|
|
592
|
+
const componentStack = [];
|
|
593
|
+
const componentFunctions = /* @__PURE__ */ new Map();
|
|
594
|
+
function isComponentName(name) {
|
|
595
|
+
if (!name) return false;
|
|
596
|
+
return /^[A-Z]/.test(name);
|
|
597
|
+
}
|
|
598
|
+
function isCustomHookName(name) {
|
|
599
|
+
if (!name) return false;
|
|
600
|
+
return /^use[A-Z]/.test(name);
|
|
601
|
+
}
|
|
602
|
+
function getFunctionName(node) {
|
|
603
|
+
if (node.type === "FunctionDeclaration" && node.id) {
|
|
604
|
+
return node.id.name;
|
|
605
|
+
}
|
|
606
|
+
const parent = node.parent;
|
|
607
|
+
if (parent?.type === "VariableDeclarator" && parent.id.type === "Identifier") {
|
|
608
|
+
return parent.id.name;
|
|
609
|
+
}
|
|
610
|
+
if (parent?.type === "CallExpression") {
|
|
611
|
+
const callParent = parent.parent;
|
|
612
|
+
if (callParent?.type === "VariableDeclarator" && callParent.id.type === "Identifier") {
|
|
613
|
+
return callParent.id.name;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
if (node.type === "FunctionExpression" && node.id) {
|
|
617
|
+
return node.id.name;
|
|
618
|
+
}
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
function shouldCountHook(hookName) {
|
|
622
|
+
switch (hookName) {
|
|
623
|
+
case "useState":
|
|
624
|
+
return countUseState;
|
|
625
|
+
case "useReducer":
|
|
626
|
+
return countUseReducer;
|
|
627
|
+
case "useContext":
|
|
628
|
+
return countUseContext;
|
|
629
|
+
default:
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function getHookName(callee) {
|
|
634
|
+
if (callee.type === "Identifier" && STATE_HOOKS.has(callee.name)) {
|
|
635
|
+
return callee.name;
|
|
636
|
+
}
|
|
637
|
+
if (callee.type === "MemberExpression" && callee.object.type === "Identifier" && callee.object.name === "React" && callee.property.type === "Identifier" && STATE_HOOKS.has(callee.property.name)) {
|
|
638
|
+
return callee.property.name;
|
|
639
|
+
}
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
function isDirectChildOfComponent(node, componentNode) {
|
|
643
|
+
let current = node.parent;
|
|
644
|
+
while (current) {
|
|
645
|
+
if (current === componentNode) {
|
|
646
|
+
return true;
|
|
647
|
+
}
|
|
648
|
+
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
current = current.parent;
|
|
652
|
+
}
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
function enterFunction(node) {
|
|
656
|
+
const name = getFunctionName(node);
|
|
657
|
+
if (isCustomHookName(name)) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (name && !isComponentName(name)) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
const componentInfo = {
|
|
664
|
+
name: name || "AnonymousComponent",
|
|
665
|
+
node,
|
|
666
|
+
hookCount: 0,
|
|
667
|
+
functionNode: node
|
|
668
|
+
};
|
|
669
|
+
componentStack.push(componentInfo);
|
|
670
|
+
componentFunctions.set(node, componentInfo);
|
|
671
|
+
}
|
|
672
|
+
function exitFunction(node) {
|
|
673
|
+
const componentInfo = componentFunctions.get(node);
|
|
674
|
+
if (!componentInfo) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
const index = componentStack.findIndex((c) => c.functionNode === node);
|
|
678
|
+
if (index !== -1) {
|
|
679
|
+
componentStack.splice(index, 1);
|
|
680
|
+
}
|
|
681
|
+
if (componentInfo.hookCount > maxStateHooks) {
|
|
682
|
+
context.report({
|
|
683
|
+
node: componentInfo.node,
|
|
684
|
+
messageId: "excessiveStateHooks",
|
|
685
|
+
data: {
|
|
686
|
+
component: componentInfo.name,
|
|
687
|
+
count: componentInfo.hookCount,
|
|
688
|
+
max: maxStateHooks
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
componentFunctions.delete(node);
|
|
693
|
+
}
|
|
694
|
+
return {
|
|
695
|
+
FunctionDeclaration: enterFunction,
|
|
696
|
+
FunctionExpression: enterFunction,
|
|
697
|
+
ArrowFunctionExpression: enterFunction,
|
|
698
|
+
"FunctionDeclaration:exit": exitFunction,
|
|
699
|
+
"FunctionExpression:exit": exitFunction,
|
|
700
|
+
"ArrowFunctionExpression:exit": exitFunction,
|
|
701
|
+
CallExpression(node) {
|
|
702
|
+
const hookName = getHookName(node.callee);
|
|
703
|
+
if (!hookName || !shouldCountHook(hookName)) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
for (let i = componentStack.length - 1; i >= 0; i--) {
|
|
707
|
+
const component = componentStack[i];
|
|
708
|
+
if (isDirectChildOfComponent(node, component.functionNode)) {
|
|
709
|
+
component.hookCount++;
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
|
|
307
718
|
// src/rules/no-mixed-component-libraries.ts
|
|
308
719
|
var LIBRARY_PATTERNS = {
|
|
309
720
|
shadcn: ["@/components/ui", "@radix-ui/", "components/ui/"],
|
|
@@ -872,14 +1283,16 @@ function getRulesByCategory(category) {
|
|
|
872
1283
|
}
|
|
873
1284
|
|
|
874
1285
|
// src/index.ts
|
|
875
|
-
var version = "0.1.0";
|
|
876
1286
|
var rules = {
|
|
877
1287
|
"no-arbitrary-tailwind": no_arbitrary_tailwind_default,
|
|
878
1288
|
"consistent-spacing": consistent_spacing_default,
|
|
1289
|
+
"consistent-dark-mode": consistent_dark_mode_default,
|
|
879
1290
|
"no-direct-store-import": no_direct_store_import_default,
|
|
1291
|
+
"prefer-zustand-state-management": prefer_zustand_state_management_default,
|
|
880
1292
|
"no-mixed-component-libraries": no_mixed_component_libraries_default,
|
|
881
|
-
semantic: semantic_default
|
|
1293
|
+
"semantic": semantic_default
|
|
882
1294
|
};
|
|
1295
|
+
var version = "0.1.0";
|
|
883
1296
|
var meta = {
|
|
884
1297
|
name: "uilint",
|
|
885
1298
|
version
|
|
@@ -904,9 +1317,49 @@ var recommendedConfig = {
|
|
|
904
1317
|
languageOptions: jsxLanguageOptions,
|
|
905
1318
|
rules: {
|
|
906
1319
|
"uilint/no-arbitrary-tailwind": "error",
|
|
907
|
-
"uilint/consistent-spacing": "warn",
|
|
908
|
-
|
|
909
|
-
|
|
1320
|
+
"uilint/consistent-spacing": ["warn", ...[
|
|
1321
|
+
{
|
|
1322
|
+
"scale": [
|
|
1323
|
+
0,
|
|
1324
|
+
1,
|
|
1325
|
+
2,
|
|
1326
|
+
3,
|
|
1327
|
+
4,
|
|
1328
|
+
5,
|
|
1329
|
+
6,
|
|
1330
|
+
8,
|
|
1331
|
+
10,
|
|
1332
|
+
12,
|
|
1333
|
+
16
|
|
1334
|
+
]
|
|
1335
|
+
}
|
|
1336
|
+
]],
|
|
1337
|
+
"uilint/consistent-dark-mode": ["error", ...[
|
|
1338
|
+
{
|
|
1339
|
+
"warnOnMissingDarkMode": true
|
|
1340
|
+
}
|
|
1341
|
+
]],
|
|
1342
|
+
"uilint/no-direct-store-import": ["warn", ...[
|
|
1343
|
+
{
|
|
1344
|
+
"storePattern": "use*Store"
|
|
1345
|
+
}
|
|
1346
|
+
]],
|
|
1347
|
+
"uilint/prefer-zustand-state-management": ["warn", ...[
|
|
1348
|
+
{
|
|
1349
|
+
"maxStateHooks": 3,
|
|
1350
|
+
"countUseState": true,
|
|
1351
|
+
"countUseReducer": true,
|
|
1352
|
+
"countUseContext": true
|
|
1353
|
+
}
|
|
1354
|
+
]],
|
|
1355
|
+
"uilint/no-mixed-component-libraries": ["error", ...[
|
|
1356
|
+
{
|
|
1357
|
+
"libraries": [
|
|
1358
|
+
"shadcn",
|
|
1359
|
+
"mui"
|
|
1360
|
+
]
|
|
1361
|
+
}
|
|
1362
|
+
]]
|
|
910
1363
|
}
|
|
911
1364
|
};
|
|
912
1365
|
var strictConfig = {
|
|
@@ -918,10 +1371,55 @@ var strictConfig = {
|
|
|
918
1371
|
languageOptions: jsxLanguageOptions,
|
|
919
1372
|
rules: {
|
|
920
1373
|
"uilint/no-arbitrary-tailwind": "error",
|
|
921
|
-
"uilint/consistent-spacing": "warn",
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1374
|
+
"uilint/consistent-spacing": ["warn", ...[
|
|
1375
|
+
{
|
|
1376
|
+
"scale": [
|
|
1377
|
+
0,
|
|
1378
|
+
1,
|
|
1379
|
+
2,
|
|
1380
|
+
3,
|
|
1381
|
+
4,
|
|
1382
|
+
5,
|
|
1383
|
+
6,
|
|
1384
|
+
8,
|
|
1385
|
+
10,
|
|
1386
|
+
12,
|
|
1387
|
+
16
|
|
1388
|
+
]
|
|
1389
|
+
}
|
|
1390
|
+
]],
|
|
1391
|
+
"uilint/consistent-dark-mode": ["error", ...[
|
|
1392
|
+
{
|
|
1393
|
+
"warnOnMissingDarkMode": true
|
|
1394
|
+
}
|
|
1395
|
+
]],
|
|
1396
|
+
"uilint/no-direct-store-import": ["warn", ...[
|
|
1397
|
+
{
|
|
1398
|
+
"storePattern": "use*Store"
|
|
1399
|
+
}
|
|
1400
|
+
]],
|
|
1401
|
+
"uilint/prefer-zustand-state-management": ["warn", ...[
|
|
1402
|
+
{
|
|
1403
|
+
"maxStateHooks": 3,
|
|
1404
|
+
"countUseState": true,
|
|
1405
|
+
"countUseReducer": true,
|
|
1406
|
+
"countUseContext": true
|
|
1407
|
+
}
|
|
1408
|
+
]],
|
|
1409
|
+
"uilint/no-mixed-component-libraries": ["error", ...[
|
|
1410
|
+
{
|
|
1411
|
+
"libraries": [
|
|
1412
|
+
"shadcn",
|
|
1413
|
+
"mui"
|
|
1414
|
+
]
|
|
1415
|
+
}
|
|
1416
|
+
]],
|
|
1417
|
+
"uilint/semantic": ["warn", ...[
|
|
1418
|
+
{
|
|
1419
|
+
"model": "qwen3-coder:30b",
|
|
1420
|
+
"styleguidePath": ".uilint/styleguide.md"
|
|
1421
|
+
}
|
|
1422
|
+
]]
|
|
925
1423
|
}
|
|
926
1424
|
};
|
|
927
1425
|
var configs = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/create-rule.ts","../src/rules/no-arbitrary-tailwind.ts","../src/rules/consistent-spacing.ts","../src/rules/no-direct-store-import.ts","../src/rules/no-mixed-component-libraries.ts","../src/rules/semantic.ts","../src/utils/cache.ts","../src/utils/styleguide-loader.ts","../src/rule-registry.ts","../src/index.ts"],"sourcesContent":["/**\n * Rule creation helper using @typescript-eslint/utils\n */\n\nimport { ESLintUtils } from \"@typescript-eslint/utils\";\n\nexport const createRule = ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/peter-suggate/uilint/blob/main/packages/uilint-eslint/docs/rules/${name}.md`\n);\n","/**\n * Rule: no-arbitrary-tailwind\n *\n * Forbids arbitrary Tailwind values like w-[123px], bg-[#fff], etc.\n */\n\nimport { createRule } from \"../utils/create-rule.js\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"noArbitraryValue\";\ntype Options = [];\n\n// Regex to match arbitrary Tailwind values: word-[anything]\nconst ARBITRARY_VALUE_REGEX = /\\b[\\w-]+-\\[[^\\]]+\\]/g;\n\nexport default createRule<Options, MessageIds>({\n name: \"no-arbitrary-tailwind\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Forbid arbitrary Tailwind values like w-[123px]\",\n },\n messages: {\n noArbitraryValue:\n \"Avoid arbitrary Tailwind value '{{value}}'. Use the spacing/color scale instead.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n return {\n // Check className attributes in JSX\n JSXAttribute(node) {\n if (\n node.name.type === \"JSXIdentifier\" &&\n (node.name.name === \"className\" || node.name.name === \"class\")\n ) {\n const value = node.value;\n\n // Handle string literal: className=\"...\"\n if (value?.type === \"Literal\" && typeof value.value === \"string\") {\n checkClassString(context, value, value.value);\n }\n\n // Handle JSX expression: className={...}\n if (value?.type === \"JSXExpressionContainer\") {\n const expr = value.expression;\n\n // Direct string: className={\"...\"}\n if (expr.type === \"Literal\" && typeof expr.value === \"string\") {\n checkClassString(context, expr, expr.value);\n }\n\n // Template literal: className={`...`}\n if (expr.type === \"TemplateLiteral\") {\n for (const quasi of expr.quasis) {\n checkClassString(context, quasi, quasi.value.raw);\n }\n }\n }\n }\n },\n\n // Check cn(), clsx(), classnames() calls\n CallExpression(node) {\n if (node.callee.type !== \"Identifier\") return;\n const name = node.callee.name;\n\n if (name === \"cn\" || name === \"clsx\" || name === \"classnames\") {\n for (const arg of node.arguments) {\n if (arg.type === \"Literal\" && typeof arg.value === \"string\") {\n checkClassString(context, arg, arg.value);\n }\n if (arg.type === \"TemplateLiteral\") {\n for (const quasi of arg.quasis) {\n checkClassString(context, quasi, quasi.value.raw);\n }\n }\n }\n }\n },\n };\n },\n});\n\nfunction checkClassString(\n context: Parameters<\n ReturnType<typeof createRule<[], \"noArbitraryValue\">>[\"create\"]\n >[0],\n node: TSESTree.Node,\n classString: string\n) {\n const matches = classString.matchAll(ARBITRARY_VALUE_REGEX);\n\n for (const match of matches) {\n // Ignore data attributes (e.g., data-[value], data-test-[something])\n if (match[0].startsWith(\"data-\")) {\n continue;\n }\n\n context.report({\n node,\n messageId: \"noArbitraryValue\",\n data: { value: match[0] },\n });\n }\n}\n","/**\n * Rule: consistent-spacing\n *\n * Enforces use of spacing scale values in gap, padding, margin utilities.\n */\n\nimport { createRule } from \"../utils/create-rule.js\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"invalidSpacing\";\ntype Options = [\n {\n scale?: number[];\n }\n];\n\n// Default Tailwind spacing scale\nconst DEFAULT_SCALE = [\n 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 24,\n 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96,\n];\n\n// Spacing utilities that take numeric values\nconst SPACING_PREFIXES = [\n \"p-\",\n \"px-\",\n \"py-\",\n \"pt-\",\n \"pr-\",\n \"pb-\",\n \"pl-\",\n \"ps-\",\n \"pe-\",\n \"m-\",\n \"mx-\",\n \"my-\",\n \"mt-\",\n \"mr-\",\n \"mb-\",\n \"ml-\",\n \"ms-\",\n \"me-\",\n \"gap-\",\n \"gap-x-\",\n \"gap-y-\",\n \"space-x-\",\n \"space-y-\",\n \"inset-\",\n \"inset-x-\",\n \"inset-y-\",\n \"top-\",\n \"right-\",\n \"bottom-\",\n \"left-\",\n \"w-\",\n \"h-\",\n \"min-w-\",\n \"min-h-\",\n \"max-w-\",\n \"max-h-\",\n \"size-\",\n];\n\n// Build regex to match spacing utilities with numeric values\nfunction buildSpacingRegex(): RegExp {\n const prefixes = SPACING_PREFIXES.map((p) => p.replace(\"-\", \"\\\\-\")).join(\"|\");\n // Match prefix followed by a number (possibly with decimal)\n return new RegExp(`\\\\b(${prefixes})(\\\\d+\\\\.?\\\\d*)\\\\b`, \"g\");\n}\n\nconst SPACING_REGEX = buildSpacingRegex();\n\nexport default createRule<Options, MessageIds>({\n name: \"consistent-spacing\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Enforce spacing scale (no magic numbers in gap/padding)\",\n },\n messages: {\n invalidSpacing:\n \"Spacing value '{{value}}' is not in the allowed scale. Use one of: {{allowed}}\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n scale: {\n type: \"array\",\n items: { type: \"number\" },\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [{ scale: DEFAULT_SCALE }],\n create(context) {\n const options = context.options[0] || {};\n const scale = new Set((options.scale || DEFAULT_SCALE).map(String));\n const scaleList = [...scale].slice(0, 10).join(\", \") + \"...\";\n\n return {\n // Check className attributes in JSX\n JSXAttribute(node) {\n if (\n node.name.type === \"JSXIdentifier\" &&\n (node.name.name === \"className\" || node.name.name === \"class\")\n ) {\n const value = node.value;\n\n if (value?.type === \"Literal\" && typeof value.value === \"string\") {\n checkSpacing(context, node, value.value, scale, scaleList);\n }\n\n if (value?.type === \"JSXExpressionContainer\") {\n const expr = value.expression;\n if (expr.type === \"Literal\" && typeof expr.value === \"string\") {\n checkSpacing(context, node, expr.value, scale, scaleList);\n }\n if (expr.type === \"TemplateLiteral\") {\n for (const quasi of expr.quasis) {\n checkSpacing(context, node, quasi.value.raw, scale, scaleList);\n }\n }\n }\n }\n },\n\n // Check cn(), clsx(), classnames() calls\n CallExpression(node) {\n if (node.callee.type !== \"Identifier\") return;\n const name = node.callee.name;\n\n if (name === \"cn\" || name === \"clsx\" || name === \"classnames\") {\n for (const arg of node.arguments) {\n if (arg.type === \"Literal\" && typeof arg.value === \"string\") {\n checkSpacing(context, arg, arg.value, scale, scaleList);\n }\n if (arg.type === \"TemplateLiteral\") {\n for (const quasi of arg.quasis) {\n checkSpacing(context, quasi, quasi.value.raw, scale, scaleList);\n }\n }\n }\n }\n },\n };\n },\n});\n\nfunction checkSpacing(\n context: Parameters<\n ReturnType<typeof createRule<Options, \"invalidSpacing\">>[\"create\"]\n >[0],\n node: TSESTree.Node,\n classString: string,\n scale: Set<string>,\n scaleList: string\n) {\n // Reset regex state\n SPACING_REGEX.lastIndex = 0;\n\n let match;\n while ((match = SPACING_REGEX.exec(classString)) !== null) {\n const [, , value] = match;\n if (value && !scale.has(value)) {\n context.report({\n node,\n messageId: \"invalidSpacing\",\n data: { value, allowed: scaleList },\n });\n }\n }\n}\n","/**\n * Rule: no-direct-store-import\n *\n * Forbids direct Zustand store imports - prefer using hooks via context.\n */\n\nimport { createRule } from \"../utils/create-rule.js\";\n\ntype MessageIds = \"noDirectImport\";\ntype Options = [\n {\n storePattern?: string;\n }\n];\n\n// Convert glob pattern to regex\nfunction patternToRegex(pattern: string): RegExp {\n const escaped = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*/g, \".*\")\n .replace(/\\?/g, \".\");\n return new RegExp(`^${escaped}$`);\n}\n\nexport default createRule<Options, MessageIds>({\n name: \"no-direct-store-import\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Forbid direct Zustand store imports (use hooks via context)\",\n },\n messages: {\n noDirectImport:\n \"Avoid importing store '{{name}}' directly. Use the store via a context hook instead.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n storePattern: {\n type: \"string\",\n description: \"Glob pattern for store names\",\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [{ storePattern: \"use*Store\" }],\n create(context) {\n const options = context.options[0] || {};\n const pattern = options.storePattern || \"use*Store\";\n const regex = patternToRegex(pattern);\n\n return {\n ImportDeclaration(node) {\n // Check if importing from a store file\n const source = node.source.value as string;\n if (!source.includes(\"store\")) return;\n\n // Check imported specifiers\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportSpecifier\") {\n const importedName =\n specifier.imported.type === \"Identifier\"\n ? specifier.imported.name\n : specifier.imported.value;\n\n if (regex.test(importedName)) {\n context.report({\n node: specifier,\n messageId: \"noDirectImport\",\n data: { name: importedName },\n });\n }\n }\n\n if (specifier.type === \"ImportDefaultSpecifier\") {\n const localName = specifier.local.name;\n if (regex.test(localName)) {\n context.report({\n node: specifier,\n messageId: \"noDirectImport\",\n data: { name: localName },\n });\n }\n }\n }\n },\n };\n },\n});\n","/**\n * Rule: no-mixed-component-libraries\n *\n * Forbids mixing shadcn/ui and MUI components in the same file.\n */\n\nimport type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../utils/create-rule.js\";\n\ntype MessageIds = \"mixedLibraries\" | \"nonPreferredLibrary\";\ntype LibraryName = \"shadcn\" | \"mui\";\ntype Options = [\n {\n libraries?: LibraryName[];\n preferred?: LibraryName;\n }\n];\n\nconst LIBRARY_PATTERNS: Record<LibraryName, string[]> = {\n shadcn: [\"@/components/ui\", \"@radix-ui/\", \"components/ui/\"],\n mui: [\"@mui/material\", \"@mui/icons-material\", \"@emotion/\"],\n};\n\nexport default createRule<Options, MessageIds>({\n name: \"no-mixed-component-libraries\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Forbid mixing component libraries in the same file\",\n },\n messages: {\n mixedLibraries:\n \"Mixing {{lib1}} and {{lib2}} components. Choose one library per file.\",\n nonPreferredLibrary:\n \"Using {{lib}} components, but {{preferred}} is the preferred library. Use {{preferred}} instead.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n libraries: {\n type: \"array\",\n items: { type: \"string\", enum: [\"shadcn\", \"mui\"] },\n },\n preferred: {\n type: \"string\",\n enum: [\"shadcn\", \"mui\"],\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [{ libraries: [\"shadcn\", \"mui\"] }],\n create(context) {\n const options = context.options[0] || {};\n const libraries = (options.libraries || [\"shadcn\", \"mui\"]) as LibraryName[];\n const preferred = options.preferred as LibraryName | undefined;\n const detected: Map<LibraryName, TSESTree.ImportDeclaration> = new Map();\n\n return {\n ImportDeclaration(node) {\n const source = node.source.value as string;\n\n for (const lib of libraries) {\n const patterns = LIBRARY_PATTERNS[lib];\n if (patterns?.some((p) => source.includes(p))) {\n if (!detected.has(lib)) {\n detected.set(lib, node);\n }\n }\n }\n },\n\n \"Program:exit\"() {\n // Check for mixing libraries (existing behavior)\n if (detected.size > 1) {\n const libs = [...detected.keys()];\n const secondLib = libs[1]!;\n const secondNode = detected.get(secondLib)!;\n\n context.report({\n node: secondNode,\n messageId: \"mixedLibraries\",\n data: { lib1: libs[0], lib2: secondLib },\n });\n return;\n }\n\n // Check for non-preferred library usage\n if (preferred && detected.size === 1) {\n const usedLib = [...detected.keys()][0];\n if (usedLib && usedLib !== preferred) {\n const node = detected.get(usedLib)!;\n context.report({\n node,\n messageId: \"nonPreferredLibrary\",\n data: { lib: usedLib, preferred },\n });\n }\n }\n },\n };\n },\n});\n","/**\n * Rule: semantic\n *\n * LLM-powered semantic UI analysis using the project's styleguide.\n * This is the only rule that reads .uilint/styleguide.md.\n */\n\nimport { existsSync, readFileSync } from \"fs\";\nimport { spawnSync } from \"child_process\";\nimport { dirname, join, relative } from \"path\";\nimport { createRule } from \"../utils/create-rule.js\";\nimport {\n getCacheEntry,\n hashContentSync,\n setCacheEntry,\n type CachedIssue,\n} from \"../utils/cache.js\";\nimport { getStyleguide } from \"../utils/styleguide-loader.js\";\nimport { UILINT_DEFAULT_OLLAMA_MODEL } from \"uilint-core\";\nimport { buildSourceScanPrompt } from \"uilint-core\";\n\ntype MessageIds = \"semanticIssue\" | \"styleguideNotFound\" | \"analysisError\";\ntype Options = [\n {\n model?: string;\n styleguidePath?: string;\n }\n];\n\nexport default createRule<Options, MessageIds>({\n name: \"semantic\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"LLM-powered semantic UI analysis using styleguide\",\n },\n messages: {\n semanticIssue: \"{{message}}\",\n styleguideNotFound:\n \"No styleguide found. Create .uilint/styleguide.md or specify styleguidePath.\",\n analysisError: \"Semantic analysis failed: {{error}}\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n model: {\n type: \"string\",\n description: \"Ollama model to use\",\n },\n styleguidePath: {\n type: \"string\",\n description: \"Path to styleguide file\",\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [{ model: UILINT_DEFAULT_OLLAMA_MODEL }],\n create(context) {\n const options = context.options[0] || {};\n const filePath = context.filename;\n const fileDir = dirname(filePath);\n\n // Get styleguide\n const { path: styleguidePath, content: styleguide } = getStyleguide(\n fileDir,\n options.styleguidePath\n );\n\n // Skip if no styleguide\n if (!styleguide) {\n console.error(\n `[uilint] Styleguide not found (styleguidePath=${String(\n options.styleguidePath ?? \"\"\n )}, startDir=${fileDir})`\n );\n\n return {\n Program(node) {\n context.report({\n node,\n messageId: \"styleguideNotFound\",\n });\n },\n };\n }\n\n // Read and hash file contents\n let fileContent: string;\n try {\n fileContent = readFileSync(filePath, \"utf-8\");\n } catch {\n console.error(`[uilint] Failed to read file ${filePath}`);\n return {\n Program(node) {\n context.report({\n node,\n messageId: \"analysisError\",\n data: { error: `Failed to read source file ${filePath}` },\n });\n },\n };\n }\n\n const fileHash = hashContentSync(fileContent);\n const styleguideHash = hashContentSync(styleguide);\n\n // Check cache\n const projectRoot = findProjectRoot(fileDir);\n const relativeFilePath = relative(projectRoot, filePath);\n const cached = getCacheEntry(\n projectRoot,\n relativeFilePath,\n fileHash,\n styleguideHash\n );\n\n const ENABLE_CACHE = false;\n if (ENABLE_CACHE && cached) {\n console.error(`[uilint] Cache hit for ${filePath}`);\n\n // Report cached issues\n return {\n Program(node) {\n for (const issue of cached.issues) {\n context.report({\n node,\n loc: { line: issue.line, column: issue.column || 0 },\n messageId: \"semanticIssue\",\n data: { message: issue.message },\n });\n }\n },\n };\n }\n\n // Cache miss: run sync analysis now (slow), cache, then report.\n ENABLE_CACHE &&\n console.error(\n `[uilint] Cache miss for ${filePath}, running semantic analysis`\n );\n\n return {\n Program(node) {\n const issues = runSemanticAnalysisSync(\n fileContent,\n styleguide,\n options.model || UILINT_DEFAULT_OLLAMA_MODEL,\n filePath\n );\n\n setCacheEntry(projectRoot, relativeFilePath, {\n fileHash,\n styleguideHash,\n issues,\n timestamp: Date.now(),\n });\n\n for (const issue of issues) {\n context.report({\n node,\n loc: { line: issue.line, column: issue.column || 0 },\n messageId: \"semanticIssue\",\n data: { message: issue.message },\n });\n }\n },\n };\n },\n});\n\n/**\n * Find project root by looking for package.json\n */\nfunction findProjectRoot(startDir: string): string {\n let dir = startDir;\n for (let i = 0; i < 20; i++) {\n if (existsSync(join(dir, \"package.json\"))) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return startDir;\n}\n\n/**\n * Run semantic analysis using Ollama (synchronously).\n *\n * Implementation detail:\n * - ESLint rules are synchronous.\n * - Blocking on a Promise (sleep-loop/Atomics) would also block Node's event loop,\n * preventing the HTTP request to Ollama from ever completing.\n * - To keep this simple & debuggable, we run the async LLM call in a child Node\n * process and synchronously wait for it to exit.\n */\nfunction runSemanticAnalysisSync(\n sourceCode: string,\n styleguide: string,\n model: string,\n filePath?: string\n): CachedIssue[] {\n const startTime = Date.now();\n const fileDisplay = filePath ? ` ${filePath}` : \"\";\n\n console.error(`[uilint] Starting semantic analysis (sync)${fileDisplay}`);\n console.error(`[uilint] Model: ${model}`);\n\n // Build prompt in-process (pure string building).\n const prompt = buildSourceScanPrompt(sourceCode, styleguide, {});\n\n // Avoid `uilint-core/node` exports *and* CJS resolution:\n // resolve the installed dependency by file URL relative to this plugin bundle.\n // When built, `import.meta.url` points at `.../uilint-eslint/dist/index.js`,\n // and the dependency lives at `.../uilint-eslint/node_modules/uilint-core/dist/node.js`.\n const coreNodeUrl = new URL(\n \"../node_modules/uilint-core/dist/node.js\",\n import.meta.url\n ).href;\n\n const childScript = `\n import * as coreNode from ${JSON.stringify(coreNodeUrl)};\n const { OllamaClient, logInfo, logWarning, createProgress, pc } = coreNode;\n const chunks = [];\n for await (const c of process.stdin) chunks.push(c);\n const input = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n const model = input.model;\n const prompt = input.prompt;\n\n const client = new OllamaClient({ model });\n const ok = await client.isAvailable();\n if (!ok) {\n logWarning(\"Ollama not available, skipping semantic analysis\");\n process.stdout.write(JSON.stringify({ issues: [] }));\n process.exit(0);\n }\n\n logInfo(\\`Ollama connected \\${pc.dim(\\`(model: \\${model})\\`)}\\`);\n const progress = createProgress(\"Analyzing with LLM...\");\n try {\n const response = await client.complete(prompt, {\n json: true,\n stream: true,\n onProgress: (latestLine) => {\n const maxLen = 60;\n const display =\n latestLine.length > maxLen\n ? latestLine.slice(0, maxLen) + \"…\"\n : latestLine;\n progress.update(\\`LLM: \\${pc.dim(display || \"...\")}\\`);\n },\n });\n progress.succeed(\"LLM complete\");\n process.stdout.write(response);\n } catch (e) {\n progress.fail(\\`LLM failed: \\${e instanceof Error ? e.message : String(e)}\\`);\n process.exit(1);\n }\n `;\n\n const child = spawnSync(\n process.execPath,\n [\"--input-type=module\", \"-e\", childScript],\n {\n input: JSON.stringify({ model, prompt }),\n encoding: \"utf8\",\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n maxBuffer: 20 * 1024 * 1024,\n }\n );\n\n const elapsed = Date.now() - startTime;\n\n if (child.error) {\n console.error(\n `[uilint] Semantic analysis failed after ${elapsed}ms: ${child.error.message}`\n );\n return [];\n }\n\n if (typeof child.status === \"number\" && child.status !== 0) {\n console.error(\n `[uilint] Semantic analysis failed after ${elapsed}ms: child exited ${child.status}`\n );\n return [];\n }\n\n const responseText = (child.stdout || \"\").trim();\n if (!responseText) {\n console.error(\n `[uilint] Semantic analysis returned empty response (${elapsed}ms)`\n );\n return [];\n }\n\n try {\n const parsed = JSON.parse(responseText) as {\n issues?: Array<{ line?: number; column?: number; message?: string }>;\n };\n\n const issues = (parsed.issues || []).map((issue) => ({\n line: issue.line || 1,\n column: issue.column,\n message: issue.message || \"Semantic issue detected\",\n ruleId: \"uilint/semantic\",\n severity: 1 as const,\n }));\n\n if (issues.length > 0) {\n console.error(`[uilint] Found ${issues.length} issue(s) (${elapsed}ms)`);\n } else {\n console.error(`[uilint] No issues found (${elapsed}ms)`);\n }\n\n return issues;\n } catch (e) {\n console.error(\n `[uilint] Semantic analysis failed to parse response after ${elapsed}ms: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n return [];\n }\n}\n","/**\n * File-hash based caching for LLM semantic rule\n *\n * Uses xxhash for fast hashing of file contents.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\n\n// Lazy-loaded xxhash\nlet xxhashInstance: Awaited<\n ReturnType<typeof import(\"xxhash-wasm\")[\"default\"]>\n> | null = null;\n\nasync function getXxhash() {\n if (!xxhashInstance) {\n const xxhash = await import(\"xxhash-wasm\");\n xxhashInstance = await xxhash.default();\n }\n return xxhashInstance;\n}\n\n/**\n * Synchronous hash using a simple djb2 algorithm (fallback when xxhash not available)\n */\nfunction djb2Hash(str: string): string {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash * 33) ^ str.charCodeAt(i);\n }\n return (hash >>> 0).toString(16);\n}\n\n/**\n * Hash content using xxhash (async) or djb2 (sync fallback)\n */\nexport async function hashContent(content: string): Promise<string> {\n try {\n const xxhash = await getXxhash();\n return xxhash.h64ToString(content);\n } catch {\n return djb2Hash(content);\n }\n}\n\n/**\n * Synchronous hash for when async is not possible\n */\nexport function hashContentSync(content: string): string {\n return djb2Hash(content);\n}\n\nexport interface CacheEntry {\n fileHash: string;\n styleguideHash: string;\n issues: CachedIssue[];\n timestamp: number;\n}\n\nexport interface CachedIssue {\n line: number;\n column?: number;\n message: string;\n ruleId: string;\n severity: 1 | 2; // 1 = warn, 2 = error\n}\n\nexport interface CacheStore {\n version: number;\n entries: Record<string, CacheEntry>;\n}\n\nconst CACHE_VERSION = 1;\nconst CACHE_FILE = \".uilint/.cache/eslint-semantic.json\";\n\n/**\n * Get the cache file path for a project\n */\nexport function getCachePath(projectRoot: string): string {\n return join(projectRoot, CACHE_FILE);\n}\n\n/**\n * Load the cache store\n */\nexport function loadCache(projectRoot: string): CacheStore {\n const cachePath = getCachePath(projectRoot);\n\n if (!existsSync(cachePath)) {\n return { version: CACHE_VERSION, entries: {} };\n }\n\n try {\n const content = readFileSync(cachePath, \"utf-8\");\n const cache = JSON.parse(content) as CacheStore;\n\n // Invalidate if version mismatch\n if (cache.version !== CACHE_VERSION) {\n return { version: CACHE_VERSION, entries: {} };\n }\n\n return cache;\n } catch {\n return { version: CACHE_VERSION, entries: {} };\n }\n}\n\n/**\n * Save the cache store\n */\nexport function saveCache(projectRoot: string, cache: CacheStore): void {\n const cachePath = getCachePath(projectRoot);\n\n try {\n const cacheDir = dirname(cachePath);\n if (!existsSync(cacheDir)) {\n mkdirSync(cacheDir, { recursive: true });\n }\n\n writeFileSync(cachePath, JSON.stringify(cache, null, 2), \"utf-8\");\n } catch {\n // Silently fail - caching is optional\n }\n}\n\n/**\n * Get cached entry for a file\n */\nexport function getCacheEntry(\n projectRoot: string,\n filePath: string,\n fileHash: string,\n styleguideHash: string\n): CacheEntry | null {\n const cache = loadCache(projectRoot);\n const entry = cache.entries[filePath];\n\n if (!entry) return null;\n\n // Check if hashes match\n if (entry.fileHash !== fileHash || entry.styleguideHash !== styleguideHash) {\n return null;\n }\n\n return entry;\n}\n\n/**\n * Set cached entry for a file\n */\nexport function setCacheEntry(\n projectRoot: string,\n filePath: string,\n entry: CacheEntry\n): void {\n const cache = loadCache(projectRoot);\n cache.entries[filePath] = entry;\n saveCache(projectRoot, cache);\n}\n\n/**\n * Clear cache for a specific file\n */\nexport function clearCacheEntry(projectRoot: string, filePath: string): void {\n const cache = loadCache(projectRoot);\n delete cache.entries[filePath];\n saveCache(projectRoot, cache);\n}\n\n/**\n * Clear entire cache\n */\nexport function clearCache(projectRoot: string): void {\n saveCache(projectRoot, { version: CACHE_VERSION, entries: {} });\n}\n","/**\n * Styleguide loader for the LLM semantic rule\n *\n * Only the semantic rule reads the styleguide - static rules use ESLint options.\n */\n\nimport { existsSync, readFileSync } from \"fs\";\nimport { dirname, isAbsolute, join, resolve } from \"path\";\n\nconst DEFAULT_STYLEGUIDE_PATHS = [\n \".uilint/styleguide.md\",\n \".uilint/styleguide.yaml\",\n \".uilint/styleguide.yml\",\n];\n\n/**\n * Find workspace root by walking up looking for pnpm-workspace.yaml, package.json, or .git\n */\nfunction findWorkspaceRoot(startDir: string): string {\n let dir = startDir;\n for (let i = 0; i < 20; i++) {\n if (\n existsSync(join(dir, \"pnpm-workspace.yaml\")) ||\n existsSync(join(dir, \".git\"))\n ) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return startDir;\n}\n\n/**\n * Find the nearest package root (directory containing package.json),\n * stopping at the workspace root.\n */\nfunction findNearestPackageRoot(\n startDir: string,\n workspaceRoot: string\n): string {\n let dir = startDir;\n for (let i = 0; i < 30; i++) {\n if (existsSync(join(dir, \"package.json\"))) return dir;\n if (dir === workspaceRoot) break;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return startDir;\n}\n\n/**\n * Find the styleguide file path\n */\nexport function findStyleguidePath(\n startDir: string,\n explicitPath?: string\n): string | null {\n // Explicit path takes precedence\n if (explicitPath) {\n if (isAbsolute(explicitPath)) {\n return existsSync(explicitPath) ? explicitPath : null;\n }\n\n // For relative explicit paths, try:\n // 1) relative to the file dir (back-compat)\n // 2) relative to the nearest package root (typical \"project root\")\n // 3) relative to workspace root (monorepo root)\n const workspaceRoot = findWorkspaceRoot(startDir);\n const packageRoot = findNearestPackageRoot(startDir, workspaceRoot);\n\n const candidates = [\n resolve(startDir, explicitPath),\n resolve(packageRoot, explicitPath),\n resolve(workspaceRoot, explicitPath),\n ];\n\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n\n return null;\n }\n\n // Check from start dir up to workspace root\n const workspaceRoot = findWorkspaceRoot(startDir);\n let dir = startDir;\n\n while (true) {\n for (const relativePath of DEFAULT_STYLEGUIDE_PATHS) {\n const fullPath = join(dir, relativePath);\n if (existsSync(fullPath)) {\n return fullPath;\n }\n }\n\n // Stop at workspace root\n if (dir === workspaceRoot) break;\n\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n return null;\n}\n\n/**\n * Load styleguide content from file\n */\nexport function loadStyleguide(\n startDir: string,\n explicitPath?: string\n): string | null {\n const path = findStyleguidePath(startDir, explicitPath);\n if (!path) return null;\n\n try {\n return readFileSync(path, \"utf-8\");\n } catch {\n return null;\n }\n}\n\n/**\n * Get styleguide path and content\n */\nexport function getStyleguide(\n startDir: string,\n explicitPath?: string\n): { path: string | null; content: string | null } {\n const path = findStyleguidePath(startDir, explicitPath);\n if (!path) return { path: null, content: null };\n\n try {\n const content = readFileSync(path, \"utf-8\");\n return { path, content };\n } catch {\n return { path, content: null };\n }\n}\n","/**\n * Rule Registry\n *\n * Central registry of all UILint ESLint rules with metadata for CLI tooling.\n * This allows the installer to auto-discover available rules and present them\n * to the user with descriptions.\n */\n\nexport interface RuleMetadata {\n /** Rule identifier (e.g., \"no-arbitrary-tailwind\") */\n id: string;\n /** Display name for CLI */\n name: string;\n /** Short description for CLI selection prompts */\n description: string;\n /** Default severity level */\n defaultSeverity: \"error\" | \"warn\" | \"off\";\n /** Default options for the rule */\n defaultOptions?: unknown[];\n /** Whether this rule requires a styleguide file */\n requiresStyleguide?: boolean;\n /** Category for grouping */\n category: \"static\" | \"semantic\";\n}\n\n/**\n * Registry of all available UILint ESLint rules\n *\n * When adding a new rule:\n * 1. Add the rule implementation to src/rules/\n * 2. Add an entry here with metadata\n * 3. Export the rule from src/index.ts\n * 4. The CLI installer will automatically discover and offer it\n */\nexport const ruleRegistry: RuleMetadata[] = [\n {\n id: \"no-arbitrary-tailwind\",\n name: \"No Arbitrary Tailwind\",\n description: \"Forbid arbitrary values like w-[123px], bg-[#fff]\",\n defaultSeverity: \"error\",\n category: \"static\",\n },\n {\n id: \"consistent-spacing\",\n name: \"Consistent Spacing\",\n description: \"Enforce spacing scale (no magic numbers in gap/padding)\",\n defaultSeverity: \"warn\",\n defaultOptions: [{ scale: [0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16] }],\n category: \"static\",\n },\n {\n id: \"consistent-dark-mode\",\n name: \"Consistent Dark Mode\",\n description:\n \"Ensure consistent dark: theming (error on mix, warn on missing)\",\n defaultSeverity: \"error\",\n defaultOptions: [{ warnOnMissingDarkMode: true }],\n category: \"static\",\n },\n {\n id: \"no-direct-store-import\",\n name: \"No Direct Store Import\",\n description: \"Forbid direct Zustand store imports (use context hooks)\",\n defaultSeverity: \"warn\",\n defaultOptions: [{ storePattern: \"use*Store\" }],\n category: \"static\",\n },\n {\n id: \"prefer-zustand-state-management\",\n name: \"Prefer Zustand State Management\",\n description:\n \"Detect excessive useState/useReducer/useContext; suggest Zustand\",\n defaultSeverity: \"warn\",\n defaultOptions: [\n {\n maxStateHooks: 3,\n countUseState: true,\n countUseReducer: true,\n countUseContext: true,\n },\n ],\n category: \"static\",\n },\n {\n id: \"no-mixed-component-libraries\",\n name: \"No Mixed Component Libraries\",\n description: \"Forbid mixing component libraries (e.g., shadcn + MUI)\",\n defaultSeverity: \"error\",\n defaultOptions: [{ libraries: [\"shadcn\", \"mui\"] }],\n category: \"static\",\n },\n {\n id: \"semantic\",\n name: \"Semantic Analysis\",\n description: \"LLM-powered semantic UI analysis using your styleguide\",\n defaultSeverity: \"warn\",\n defaultOptions: [\n { model: \"qwen3-coder:30b\", styleguidePath: \".uilint/styleguide.md\" },\n ],\n requiresStyleguide: true,\n category: \"semantic\",\n },\n];\n\n/**\n * Get rule metadata by ID\n */\nexport function getRuleMetadata(id: string): RuleMetadata | undefined {\n return ruleRegistry.find((rule) => rule.id === id);\n}\n\n/**\n * Get all rules in a category\n */\nexport function getRulesByCategory(\n category: \"static\" | \"semantic\"\n): RuleMetadata[] {\n return ruleRegistry.filter((rule) => rule.category === category);\n}\n","/**\n * UILint ESLint Plugin\n *\n * Provides ESLint rules for UI consistency checking:\n * - Static rules for common patterns (arbitrary values, spacing, component libraries)\n * - LLM-powered semantic rule that reads your styleguide\n */\n\nimport type { Linter } from \"eslint\";\nimport noArbitraryTailwind from \"./rules/no-arbitrary-tailwind.js\";\nimport consistentSpacing from \"./rules/consistent-spacing.js\";\nimport noDirectStoreImport from \"./rules/no-direct-store-import.js\";\nimport noMixedComponentLibraries from \"./rules/no-mixed-component-libraries.js\";\nimport semantic from \"./rules/semantic.js\";\n\n// Package version (injected at build time or fallback)\nconst version = \"0.1.0\";\n\n/**\n * All available rules\n */\nconst rules = {\n \"no-arbitrary-tailwind\": noArbitraryTailwind,\n \"consistent-spacing\": consistentSpacing,\n \"no-direct-store-import\": noDirectStoreImport,\n \"no-mixed-component-libraries\": noMixedComponentLibraries,\n semantic: semantic,\n};\n\n/**\n * Plugin metadata\n */\nconst meta = {\n name: \"uilint\",\n version,\n};\n\n/**\n * The ESLint plugin object\n */\nconst plugin = {\n meta,\n rules,\n};\n\n/**\n * Shared language options for all configs\n */\nconst jsxLanguageOptions: Linter.Config[\"languageOptions\"] = {\n parserOptions: {\n ecmaFeatures: {\n jsx: true,\n },\n },\n};\n\n/**\n * Recommended config - static rules only\n *\n * Usage:\n * ```js\n * import uilint from 'uilint-eslint';\n * export default [uilint.configs.recommended];\n * ```\n */\nconst recommendedConfig: Linter.Config = {\n name: \"uilint/recommended\",\n plugins: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n uilint: plugin as any,\n },\n languageOptions: jsxLanguageOptions,\n rules: {\n \"uilint/no-arbitrary-tailwind\": \"error\",\n \"uilint/consistent-spacing\": \"warn\",\n \"uilint/no-direct-store-import\": \"warn\",\n \"uilint/no-mixed-component-libraries\": \"error\",\n },\n};\n\n/**\n * Strict config - static rules + LLM semantic rule\n *\n * Usage:\n * ```js\n * import uilint from 'uilint-eslint';\n * export default [uilint.configs.strict];\n * ```\n */\nconst strictConfig: Linter.Config = {\n name: \"uilint/strict\",\n plugins: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n uilint: plugin as any,\n },\n languageOptions: jsxLanguageOptions,\n rules: {\n \"uilint/no-arbitrary-tailwind\": \"error\",\n \"uilint/consistent-spacing\": \"warn\",\n \"uilint/no-direct-store-import\": \"error\",\n \"uilint/no-mixed-component-libraries\": \"error\",\n \"uilint/semantic\": \"warn\",\n },\n};\n\n/**\n * Pre-configured configs\n */\nconst configs: Record<string, Linter.Config> = {\n recommended: recommendedConfig,\n strict: strictConfig,\n};\n\n/**\n * UILint ESLint export interface\n */\nexport interface UILintESLint {\n meta: typeof meta;\n plugin: typeof plugin;\n rules: typeof rules;\n configs: Record<string, Linter.Config>;\n}\n\n/**\n * Default export for ESLint flat config\n */\nconst uilintEslint: UILintESLint = {\n meta,\n plugin,\n rules,\n configs,\n};\n\nexport default uilintEslint;\n\n// Named exports for convenience\nexport { plugin, rules, configs, meta };\n\n// Re-export utilities for custom rule creation\nexport { createRule } from \"./utils/create-rule.js\";\nexport {\n loadStyleguide,\n findStyleguidePath,\n getStyleguide,\n} from \"./utils/styleguide-loader.js\";\nexport {\n hashContent,\n hashContentSync,\n getCacheEntry,\n setCacheEntry,\n clearCache,\n clearCacheEntry,\n loadCache,\n saveCache,\n type CacheEntry,\n type CachedIssue,\n type CacheStore,\n} from \"./utils/cache.js\";\n\n// Re-export rule registry for CLI tooling\nexport {\n ruleRegistry,\n getRuleMetadata,\n getRulesByCategory,\n type RuleMetadata,\n} from \"./rule-registry.js\";\n"],"mappings":";AAIA,SAAS,mBAAmB;AAErB,IAAM,aAAa,YAAY;AAAA,EACpC,CAAC,SACC,uFAAuF,IAAI;AAC/F;;;ACIA,IAAM,wBAAwB;AAE9B,IAAO,gCAAQ,WAAgC;AAAA,EAC7C,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,IACf;AAAA,IACA,UAAU;AAAA,MACR,kBACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,EACX;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,WAAO;AAAA;AAAA,MAEL,aAAa,MAAM;AACjB,YACE,KAAK,KAAK,SAAS,oBAClB,KAAK,KAAK,SAAS,eAAe,KAAK,KAAK,SAAS,UACtD;AACA,gBAAM,QAAQ,KAAK;AAGnB,cAAI,OAAO,SAAS,aAAa,OAAO,MAAM,UAAU,UAAU;AAChE,6BAAiB,SAAS,OAAO,MAAM,KAAK;AAAA,UAC9C;AAGA,cAAI,OAAO,SAAS,0BAA0B;AAC5C,kBAAM,OAAO,MAAM;AAGnB,gBAAI,KAAK,SAAS,aAAa,OAAO,KAAK,UAAU,UAAU;AAC7D,+BAAiB,SAAS,MAAM,KAAK,KAAK;AAAA,YAC5C;AAGA,gBAAI,KAAK,SAAS,mBAAmB;AACnC,yBAAW,SAAS,KAAK,QAAQ;AAC/B,iCAAiB,SAAS,OAAO,MAAM,MAAM,GAAG;AAAA,cAClD;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAGA,eAAe,MAAM;AACnB,YAAI,KAAK,OAAO,SAAS,aAAc;AACvC,cAAM,OAAO,KAAK,OAAO;AAEzB,YAAI,SAAS,QAAQ,SAAS,UAAU,SAAS,cAAc;AAC7D,qBAAW,OAAO,KAAK,WAAW;AAChC,gBAAI,IAAI,SAAS,aAAa,OAAO,IAAI,UAAU,UAAU;AAC3D,+BAAiB,SAAS,KAAK,IAAI,KAAK;AAAA,YAC1C;AACA,gBAAI,IAAI,SAAS,mBAAmB;AAClC,yBAAW,SAAS,IAAI,QAAQ;AAC9B,iCAAiB,SAAS,OAAO,MAAM,MAAM,GAAG;AAAA,cAClD;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAED,SAAS,iBACP,SAGA,MACA,aACA;AACA,QAAM,UAAU,YAAY,SAAS,qBAAqB;AAE1D,aAAW,SAAS,SAAS;AAE3B,QAAI,MAAM,CAAC,EAAE,WAAW,OAAO,GAAG;AAChC;AAAA,IACF;AAEA,YAAQ,OAAO;AAAA,MACb;AAAA,MACA,WAAW;AAAA,MACX,MAAM,EAAE,OAAO,MAAM,CAAC,EAAE;AAAA,IAC1B,CAAC;AAAA,EACH;AACF;;;ACzFA,IAAM,gBAAgB;AAAA,EACpB;AAAA,EAAG;AAAA,EAAK;AAAA,EAAG;AAAA,EAAK;AAAA,EAAG;AAAA,EAAK;AAAA,EAAG;AAAA,EAAK;AAAA,EAAG;AAAA,EAAG;AAAA,EAAG;AAAA,EAAG;AAAA,EAAG;AAAA,EAAG;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAC1E;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAClD;AAGA,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,SAAS,oBAA4B;AACnC,QAAM,WAAW,iBAAiB,IAAI,CAAC,MAAM,EAAE,QAAQ,KAAK,KAAK,CAAC,EAAE,KAAK,GAAG;AAE5E,SAAO,IAAI,OAAO,OAAO,QAAQ,sBAAsB,GAAG;AAC5D;AAEA,IAAM,gBAAgB,kBAAkB;AAExC,IAAO,6BAAQ,WAAgC;AAAA,EAC7C,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,IACf;AAAA,IACA,UAAU;AAAA,MACR,gBACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,YAAY;AAAA,UACV,OAAO;AAAA,YACL,MAAM;AAAA,YACN,OAAO,EAAE,MAAM,SAAS;AAAA,UAC1B;AAAA,QACF;AAAA,QACA,sBAAsB;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,EAAE,OAAO,cAAc,CAAC;AAAA,EACzC,OAAO,SAAS;AACd,UAAM,UAAU,QAAQ,QAAQ,CAAC,KAAK,CAAC;AACvC,UAAM,QAAQ,IAAI,KAAK,QAAQ,SAAS,eAAe,IAAI,MAAM,CAAC;AAClE,UAAM,YAAY,CAAC,GAAG,KAAK,EAAE,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,IAAI;AAEvD,WAAO;AAAA;AAAA,MAEL,aAAa,MAAM;AACjB,YACE,KAAK,KAAK,SAAS,oBAClB,KAAK,KAAK,SAAS,eAAe,KAAK,KAAK,SAAS,UACtD;AACA,gBAAM,QAAQ,KAAK;AAEnB,cAAI,OAAO,SAAS,aAAa,OAAO,MAAM,UAAU,UAAU;AAChE,yBAAa,SAAS,MAAM,MAAM,OAAO,OAAO,SAAS;AAAA,UAC3D;AAEA,cAAI,OAAO,SAAS,0BAA0B;AAC5C,kBAAM,OAAO,MAAM;AACnB,gBAAI,KAAK,SAAS,aAAa,OAAO,KAAK,UAAU,UAAU;AAC7D,2BAAa,SAAS,MAAM,KAAK,OAAO,OAAO,SAAS;AAAA,YAC1D;AACA,gBAAI,KAAK,SAAS,mBAAmB;AACnC,yBAAW,SAAS,KAAK,QAAQ;AAC/B,6BAAa,SAAS,MAAM,MAAM,MAAM,KAAK,OAAO,SAAS;AAAA,cAC/D;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAGA,eAAe,MAAM;AACnB,YAAI,KAAK,OAAO,SAAS,aAAc;AACvC,cAAM,OAAO,KAAK,OAAO;AAEzB,YAAI,SAAS,QAAQ,SAAS,UAAU,SAAS,cAAc;AAC7D,qBAAW,OAAO,KAAK,WAAW;AAChC,gBAAI,IAAI,SAAS,aAAa,OAAO,IAAI,UAAU,UAAU;AAC3D,2BAAa,SAAS,KAAK,IAAI,OAAO,OAAO,SAAS;AAAA,YACxD;AACA,gBAAI,IAAI,SAAS,mBAAmB;AAClC,yBAAW,SAAS,IAAI,QAAQ;AAC9B,6BAAa,SAAS,OAAO,MAAM,MAAM,KAAK,OAAO,SAAS;AAAA,cAChE;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAED,SAAS,aACP,SAGA,MACA,aACA,OACA,WACA;AAEA,gBAAc,YAAY;AAE1B,MAAI;AACJ,UAAQ,QAAQ,cAAc,KAAK,WAAW,OAAO,MAAM;AACzD,UAAM,CAAC,EAAE,EAAE,KAAK,IAAI;AACpB,QAAI,SAAS,CAAC,MAAM,IAAI,KAAK,GAAG;AAC9B,cAAQ,OAAO;AAAA,QACb;AAAA,QACA,WAAW;AAAA,QACX,MAAM,EAAE,OAAO,SAAS,UAAU;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AC9JA,SAAS,eAAe,SAAyB;AAC/C,QAAM,UAAU,QACb,QAAQ,qBAAqB,MAAM,EACnC,QAAQ,OAAO,IAAI,EACnB,QAAQ,OAAO,GAAG;AACrB,SAAO,IAAI,OAAO,IAAI,OAAO,GAAG;AAClC;AAEA,IAAO,iCAAQ,WAAgC;AAAA,EAC7C,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,UAAU;AAAA,MACR,gBACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,YAAY;AAAA,UACV,cAAc;AAAA,YACZ,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,QACF;AAAA,QACA,sBAAsB;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,EAAE,cAAc,YAAY,CAAC;AAAA,EAC9C,OAAO,SAAS;AACd,UAAM,UAAU,QAAQ,QAAQ,CAAC,KAAK,CAAC;AACvC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,QAAQ,eAAe,OAAO;AAEpC,WAAO;AAAA,MACL,kBAAkB,MAAM;AAEtB,cAAM,SAAS,KAAK,OAAO;AAC3B,YAAI,CAAC,OAAO,SAAS,OAAO,EAAG;AAG/B,mBAAW,aAAa,KAAK,YAAY;AACvC,cAAI,UAAU,SAAS,mBAAmB;AACxC,kBAAM,eACJ,UAAU,SAAS,SAAS,eACxB,UAAU,SAAS,OACnB,UAAU,SAAS;AAEzB,gBAAI,MAAM,KAAK,YAAY,GAAG;AAC5B,sBAAQ,OAAO;AAAA,gBACb,MAAM;AAAA,gBACN,WAAW;AAAA,gBACX,MAAM,EAAE,MAAM,aAAa;AAAA,cAC7B,CAAC;AAAA,YACH;AAAA,UACF;AAEA,cAAI,UAAU,SAAS,0BAA0B;AAC/C,kBAAM,YAAY,UAAU,MAAM;AAClC,gBAAI,MAAM,KAAK,SAAS,GAAG;AACzB,sBAAQ,OAAO;AAAA,gBACb,MAAM;AAAA,gBACN,WAAW;AAAA,gBACX,MAAM,EAAE,MAAM,UAAU;AAAA,cAC1B,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC1ED,IAAM,mBAAkD;AAAA,EACtD,QAAQ,CAAC,mBAAmB,cAAc,gBAAgB;AAAA,EAC1D,KAAK,CAAC,iBAAiB,uBAAuB,WAAW;AAC3D;AAEA,IAAO,uCAAQ,WAAgC;AAAA,EAC7C,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,IACf;AAAA,IACA,UAAU;AAAA,MACR,gBACE;AAAA,MACF,qBACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,YAAY;AAAA,UACV,WAAW;AAAA,YACT,MAAM;AAAA,YACN,OAAO,EAAE,MAAM,UAAU,MAAM,CAAC,UAAU,KAAK,EAAE;AAAA,UACnD;AAAA,UACA,WAAW;AAAA,YACT,MAAM;AAAA,YACN,MAAM,CAAC,UAAU,KAAK;AAAA,UACxB;AAAA,QACF;AAAA,QACA,sBAAsB;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,EAAE,WAAW,CAAC,UAAU,KAAK,EAAE,CAAC;AAAA,EACjD,OAAO,SAAS;AACd,UAAM,UAAU,QAAQ,QAAQ,CAAC,KAAK,CAAC;AACvC,UAAM,YAAa,QAAQ,aAAa,CAAC,UAAU,KAAK;AACxD,UAAM,YAAY,QAAQ;AAC1B,UAAM,WAAyD,oBAAI,IAAI;AAEvE,WAAO;AAAA,MACL,kBAAkB,MAAM;AACtB,cAAM,SAAS,KAAK,OAAO;AAE3B,mBAAW,OAAO,WAAW;AAC3B,gBAAM,WAAW,iBAAiB,GAAG;AACrC,cAAI,UAAU,KAAK,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC,GAAG;AAC7C,gBAAI,CAAC,SAAS,IAAI,GAAG,GAAG;AACtB,uBAAS,IAAI,KAAK,IAAI;AAAA,YACxB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MAEA,iBAAiB;AAEf,YAAI,SAAS,OAAO,GAAG;AACrB,gBAAM,OAAO,CAAC,GAAG,SAAS,KAAK,CAAC;AAChC,gBAAM,YAAY,KAAK,CAAC;AACxB,gBAAM,aAAa,SAAS,IAAI,SAAS;AAEzC,kBAAQ,OAAO;AAAA,YACb,MAAM;AAAA,YACN,WAAW;AAAA,YACX,MAAM,EAAE,MAAM,KAAK,CAAC,GAAG,MAAM,UAAU;AAAA,UACzC,CAAC;AACD;AAAA,QACF;AAGA,YAAI,aAAa,SAAS,SAAS,GAAG;AACpC,gBAAM,UAAU,CAAC,GAAG,SAAS,KAAK,CAAC,EAAE,CAAC;AACtC,cAAI,WAAW,YAAY,WAAW;AACpC,kBAAM,OAAO,SAAS,IAAI,OAAO;AACjC,oBAAQ,OAAO;AAAA,cACb;AAAA,cACA,WAAW;AAAA,cACX,MAAM,EAAE,KAAK,SAAS,UAAU;AAAA,YAClC,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACjGD,SAAS,cAAAA,aAAY,gBAAAC,qBAAoB;AACzC,SAAS,iBAAiB;AAC1B,SAAS,WAAAC,UAAS,QAAAC,OAAM,gBAAgB;;;ACHxC,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,SAAS,YAAY;AAG9B,IAAI,iBAEO;AAEX,eAAe,YAAY;AACzB,MAAI,CAAC,gBAAgB;AACnB,UAAM,SAAS,MAAM,OAAO,aAAa;AACzC,qBAAiB,MAAM,OAAO,QAAQ;AAAA,EACxC;AACA,SAAO;AACT;AAKA,SAAS,SAAS,KAAqB;AACrC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,WAAQ,OAAO,KAAM,IAAI,WAAW,CAAC;AAAA,EACvC;AACA,UAAQ,SAAS,GAAG,SAAS,EAAE;AACjC;AAKA,eAAsB,YAAY,SAAkC;AAClE,MAAI;AACF,UAAM,SAAS,MAAM,UAAU;AAC/B,WAAO,OAAO,YAAY,OAAO;AAAA,EACnC,QAAQ;AACN,WAAO,SAAS,OAAO;AAAA,EACzB;AACF;AAKO,SAAS,gBAAgB,SAAyB;AACvD,SAAO,SAAS,OAAO;AACzB;AAsBA,IAAM,gBAAgB;AACtB,IAAM,aAAa;AAKZ,SAAS,aAAa,aAA6B;AACxD,SAAO,KAAK,aAAa,UAAU;AACrC;AAKO,SAAS,UAAU,aAAiC;AACzD,QAAM,YAAY,aAAa,WAAW;AAE1C,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,WAAO,EAAE,SAAS,eAAe,SAAS,CAAC,EAAE;AAAA,EAC/C;AAEA,MAAI;AACF,UAAM,UAAU,aAAa,WAAW,OAAO;AAC/C,UAAM,QAAQ,KAAK,MAAM,OAAO;AAGhC,QAAI,MAAM,YAAY,eAAe;AACnC,aAAO,EAAE,SAAS,eAAe,SAAS,CAAC,EAAE;AAAA,IAC/C;AAEA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,EAAE,SAAS,eAAe,SAAS,CAAC,EAAE;AAAA,EAC/C;AACF;AAKO,SAAS,UAAU,aAAqB,OAAyB;AACtE,QAAM,YAAY,aAAa,WAAW;AAE1C,MAAI;AACF,UAAM,WAAW,QAAQ,SAAS;AAClC,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,gBAAU,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,IACzC;AAEA,kBAAc,WAAW,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,OAAO;AAAA,EAClE,QAAQ;AAAA,EAER;AACF;AAKO,SAAS,cACd,aACA,UACA,UACA,gBACmB;AACnB,QAAM,QAAQ,UAAU,WAAW;AACnC,QAAM,QAAQ,MAAM,QAAQ,QAAQ;AAEpC,MAAI,CAAC,MAAO,QAAO;AAGnB,MAAI,MAAM,aAAa,YAAY,MAAM,mBAAmB,gBAAgB;AAC1E,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKO,SAAS,cACd,aACA,UACA,OACM;AACN,QAAM,QAAQ,UAAU,WAAW;AACnC,QAAM,QAAQ,QAAQ,IAAI;AAC1B,YAAU,aAAa,KAAK;AAC9B;AAKO,SAAS,gBAAgB,aAAqB,UAAwB;AAC3E,QAAM,QAAQ,UAAU,WAAW;AACnC,SAAO,MAAM,QAAQ,QAAQ;AAC7B,YAAU,aAAa,KAAK;AAC9B;AAKO,SAAS,WAAW,aAA2B;AACpD,YAAU,aAAa,EAAE,SAAS,eAAe,SAAS,CAAC,EAAE,CAAC;AAChE;;;ACxKA,SAAS,cAAAC,aAAY,gBAAAC,qBAAoB;AACzC,SAAS,WAAAC,UAAS,YAAY,QAAAC,OAAM,eAAe;AAEnD,IAAM,2BAA2B;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF;AAKA,SAAS,kBAAkB,UAA0B;AACnD,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,QACEH,YAAWG,MAAK,KAAK,qBAAqB,CAAC,KAC3CH,YAAWG,MAAK,KAAK,MAAM,CAAC,GAC5B;AACA,aAAO;AAAA,IACT;AACA,UAAM,SAASD,SAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAMA,SAAS,uBACP,UACA,eACQ;AACR,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,QAAIF,YAAWG,MAAK,KAAK,cAAc,CAAC,EAAG,QAAO;AAClD,QAAI,QAAQ,cAAe;AAC3B,UAAM,SAASD,SAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAKO,SAAS,mBACd,UACA,cACe;AAEf,MAAI,cAAc;AAChB,QAAI,WAAW,YAAY,GAAG;AAC5B,aAAOF,YAAW,YAAY,IAAI,eAAe;AAAA,IACnD;AAMA,UAAMI,iBAAgB,kBAAkB,QAAQ;AAChD,UAAM,cAAc,uBAAuB,UAAUA,cAAa;AAElE,UAAM,aAAa;AAAA,MACjB,QAAQ,UAAU,YAAY;AAAA,MAC9B,QAAQ,aAAa,YAAY;AAAA,MACjC,QAAQA,gBAAe,YAAY;AAAA,IACrC;AAEA,eAAW,KAAK,YAAY;AAC1B,UAAIJ,YAAW,CAAC,EAAG,QAAO;AAAA,IAC5B;AAEA,WAAO;AAAA,EACT;AAGA,QAAM,gBAAgB,kBAAkB,QAAQ;AAChD,MAAI,MAAM;AAEV,SAAO,MAAM;AACX,eAAW,gBAAgB,0BAA0B;AACnD,YAAM,WAAWG,MAAK,KAAK,YAAY;AACvC,UAAIH,YAAW,QAAQ,GAAG;AACxB,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,QAAQ,cAAe;AAE3B,UAAM,SAASE,SAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AAEA,SAAO;AACT;AAKO,SAAS,eACd,UACA,cACe;AACf,QAAM,OAAO,mBAAmB,UAAU,YAAY;AACtD,MAAI,CAAC,KAAM,QAAO;AAElB,MAAI;AACF,WAAOD,cAAa,MAAM,OAAO;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,cACd,UACA,cACiD;AACjD,QAAM,OAAO,mBAAmB,UAAU,YAAY;AACtD,MAAI,CAAC,KAAM,QAAO,EAAE,MAAM,MAAM,SAAS,KAAK;AAE9C,MAAI;AACF,UAAM,UAAUA,cAAa,MAAM,OAAO;AAC1C,WAAO,EAAE,MAAM,QAAQ;AAAA,EACzB,QAAQ;AACN,WAAO,EAAE,MAAM,SAAS,KAAK;AAAA,EAC/B;AACF;;;AF5HA,SAAS,mCAAmC;AAC5C,SAAS,6BAA6B;AAUtC,IAAO,mBAAQ,WAAgC;AAAA,EAC7C,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,IACf;AAAA,IACA,UAAU;AAAA,MACR,eAAe;AAAA,MACf,oBACE;AAAA,MACF,eAAe;AAAA,IACjB;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,YAAY;AAAA,UACV,OAAO;AAAA,YACL,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,UACA,gBAAgB;AAAA,YACd,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,QACF;AAAA,QACA,sBAAsB;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,EAAE,OAAO,4BAA4B,CAAC;AAAA,EACvD,OAAO,SAAS;AACd,UAAM,UAAU,QAAQ,QAAQ,CAAC,KAAK,CAAC;AACvC,UAAM,WAAW,QAAQ;AACzB,UAAM,UAAUI,SAAQ,QAAQ;AAGhC,UAAM,EAAE,MAAM,gBAAgB,SAAS,WAAW,IAAI;AAAA,MACpD;AAAA,MACA,QAAQ;AAAA,IACV;AAGA,QAAI,CAAC,YAAY;AACf,cAAQ;AAAA,QACN,iDAAiD;AAAA,UAC/C,QAAQ,kBAAkB;AAAA,QAC5B,CAAC,cAAc,OAAO;AAAA,MACxB;AAEA,aAAO;AAAA,QACL,QAAQ,MAAM;AACZ,kBAAQ,OAAO;AAAA,YACb;AAAA,YACA,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACF,oBAAcC,cAAa,UAAU,OAAO;AAAA,IAC9C,QAAQ;AACN,cAAQ,MAAM,gCAAgC,QAAQ,EAAE;AACxD,aAAO;AAAA,QACL,QAAQ,MAAM;AACZ,kBAAQ,OAAO;AAAA,YACb;AAAA,YACA,WAAW;AAAA,YACX,MAAM,EAAE,OAAO,8BAA8B,QAAQ,GAAG;AAAA,UAC1D,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,gBAAgB,WAAW;AAC5C,UAAM,iBAAiB,gBAAgB,UAAU;AAGjD,UAAM,cAAc,gBAAgB,OAAO;AAC3C,UAAM,mBAAmB,SAAS,aAAa,QAAQ;AACvD,UAAM,SAAS;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,eAAe;AACrB,QAAI,gBAAgB,QAAQ;AAC1B,cAAQ,MAAM,0BAA0B,QAAQ,EAAE;AAGlD,aAAO;AAAA,QACL,QAAQ,MAAM;AACZ,qBAAW,SAAS,OAAO,QAAQ;AACjC,oBAAQ,OAAO;AAAA,cACb;AAAA,cACA,KAAK,EAAE,MAAM,MAAM,MAAM,QAAQ,MAAM,UAAU,EAAE;AAAA,cACnD,WAAW;AAAA,cACX,MAAM,EAAE,SAAS,MAAM,QAAQ;AAAA,YACjC,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,oBACE,QAAQ;AAAA,MACN,2BAA2B,QAAQ;AAAA,IACrC;AAEF,WAAO;AAAA,MACL,QAAQ,MAAM;AACZ,cAAM,SAAS;AAAA,UACb;AAAA,UACA;AAAA,UACA,QAAQ,SAAS;AAAA,UACjB;AAAA,QACF;AAEA,sBAAc,aAAa,kBAAkB;AAAA,UAC3C;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,QACtB,CAAC;AAED,mBAAW,SAAS,QAAQ;AAC1B,kBAAQ,OAAO;AAAA,YACb;AAAA,YACA,KAAK,EAAE,MAAM,MAAM,MAAM,QAAQ,MAAM,UAAU,EAAE;AAAA,YACnD,WAAW;AAAA,YACX,MAAM,EAAE,SAAS,MAAM,QAAQ;AAAA,UACjC,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAKD,SAAS,gBAAgB,UAA0B;AACjD,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,QAAIC,YAAWC,MAAK,KAAK,cAAc,CAAC,GAAG;AACzC,aAAO;AAAA,IACT;AACA,UAAM,SAASH,SAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAYA,SAAS,wBACP,YACA,YACA,OACA,UACe;AACf,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,cAAc,WAAW,IAAI,QAAQ,KAAK;AAEhD,UAAQ,MAAM,6CAA6C,WAAW,EAAE;AACxE,UAAQ,MAAM,mBAAmB,KAAK,EAAE;AAGxC,QAAM,SAAS,sBAAsB,YAAY,YAAY,CAAC,CAAC;AAM/D,QAAM,cAAc,IAAI;AAAA,IACtB;AAAA,IACA,YAAY;AAAA,EACd,EAAE;AAEF,QAAM,cAAc;AAAA,gCACU,KAAK,UAAU,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuCzD,QAAM,QAAQ;AAAA,IACZ,QAAQ;AAAA,IACR,CAAC,uBAAuB,MAAM,WAAW;AAAA,IACzC;AAAA,MACE,OAAO,KAAK,UAAU,EAAE,OAAO,OAAO,CAAC;AAAA,MACvC,UAAU;AAAA,MACV,OAAO,CAAC,QAAQ,QAAQ,SAAS;AAAA,MACjC,WAAW,KAAK,OAAO;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,UAAU,KAAK,IAAI,IAAI;AAE7B,MAAI,MAAM,OAAO;AACf,YAAQ;AAAA,MACN,2CAA2C,OAAO,OAAO,MAAM,MAAM,OAAO;AAAA,IAC9E;AACA,WAAO,CAAC;AAAA,EACV;AAEA,MAAI,OAAO,MAAM,WAAW,YAAY,MAAM,WAAW,GAAG;AAC1D,YAAQ;AAAA,MACN,2CAA2C,OAAO,oBAAoB,MAAM,MAAM;AAAA,IACpF;AACA,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,gBAAgB,MAAM,UAAU,IAAI,KAAK;AAC/C,MAAI,CAAC,cAAc;AACjB,YAAQ;AAAA,MACN,uDAAuD,OAAO;AAAA,IAChE;AACA,WAAO,CAAC;AAAA,EACV;AAEA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,YAAY;AAItC,UAAM,UAAU,OAAO,UAAU,CAAC,GAAG,IAAI,CAAC,WAAW;AAAA,MACnD,MAAM,MAAM,QAAQ;AAAA,MACpB,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM,WAAW;AAAA,MAC1B,QAAQ;AAAA,MACR,UAAU;AAAA,IACZ,EAAE;AAEF,QAAI,OAAO,SAAS,GAAG;AACrB,cAAQ,MAAM,kBAAkB,OAAO,MAAM,cAAc,OAAO,KAAK;AAAA,IACzE,OAAO;AACL,cAAQ,MAAM,6BAA6B,OAAO,KAAK;AAAA,IACzD;AAEA,WAAO;AAAA,EACT,SAAS,GAAG;AACV,YAAQ;AAAA,MACN,6DAA6D,OAAO,OAClE,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAC3C;AAAA,IACF;AACA,WAAO,CAAC;AAAA,EACV;AACF;;;AGpSO,IAAM,eAA+B;AAAA,EAC1C;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,gBAAgB,CAAC,EAAE,OAAO,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;AAAA,IAChE,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,IACF,iBAAiB;AAAA,IACjB,gBAAgB,CAAC,EAAE,uBAAuB,KAAK,CAAC;AAAA,IAChD,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,gBAAgB,CAAC,EAAE,cAAc,YAAY,CAAC;AAAA,IAC9C,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,IACF,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,MACd;AAAA,QACE,eAAe;AAAA,QACf,eAAe;AAAA,QACf,iBAAiB;AAAA,QACjB,iBAAiB;AAAA,MACnB;AAAA,IACF;AAAA,IACA,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,gBAAgB,CAAC,EAAE,WAAW,CAAC,UAAU,KAAK,EAAE,CAAC;AAAA,IACjD,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,MACd,EAAE,OAAO,mBAAmB,gBAAgB,wBAAwB;AAAA,IACtE;AAAA,IACA,oBAAoB;AAAA,IACpB,UAAU;AAAA,EACZ;AACF;AAKO,SAAS,gBAAgB,IAAsC;AACpE,SAAO,aAAa,KAAK,CAAC,SAAS,KAAK,OAAO,EAAE;AACnD;AAKO,SAAS,mBACd,UACgB;AAChB,SAAO,aAAa,OAAO,CAAC,SAAS,KAAK,aAAa,QAAQ;AACjE;;;ACtGA,IAAM,UAAU;AAKhB,IAAM,QAAQ;AAAA,EACZ,yBAAyB;AAAA,EACzB,sBAAsB;AAAA,EACtB,0BAA0B;AAAA,EAC1B,gCAAgC;AAAA,EAChC,UAAU;AACZ;AAKA,IAAM,OAAO;AAAA,EACX,MAAM;AAAA,EACN;AACF;AAKA,IAAM,SAAS;AAAA,EACb;AAAA,EACA;AACF;AAKA,IAAM,qBAAuD;AAAA,EAC3D,eAAe;AAAA,IACb,cAAc;AAAA,MACZ,KAAK;AAAA,IACP;AAAA,EACF;AACF;AAWA,IAAM,oBAAmC;AAAA,EACvC,MAAM;AAAA,EACN,SAAS;AAAA;AAAA,IAEP,QAAQ;AAAA,EACV;AAAA,EACA,iBAAiB;AAAA,EACjB,OAAO;AAAA,IACL,gCAAgC;AAAA,IAChC,6BAA6B;AAAA,IAC7B,iCAAiC;AAAA,IACjC,uCAAuC;AAAA,EACzC;AACF;AAWA,IAAM,eAA8B;AAAA,EAClC,MAAM;AAAA,EACN,SAAS;AAAA;AAAA,IAEP,QAAQ;AAAA,EACV;AAAA,EACA,iBAAiB;AAAA,EACjB,OAAO;AAAA,IACL,gCAAgC;AAAA,IAChC,6BAA6B;AAAA,IAC7B,iCAAiC;AAAA,IACjC,uCAAuC;AAAA,IACvC,mBAAmB;AAAA,EACrB;AACF;AAKA,IAAM,UAAyC;AAAA,EAC7C,aAAa;AAAA,EACb,QAAQ;AACV;AAeA,IAAM,eAA6B;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAO,gBAAQ;","names":["existsSync","readFileSync","dirname","join","existsSync","readFileSync","dirname","join","workspaceRoot","dirname","readFileSync","existsSync","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/create-rule.ts","../src/rules/no-arbitrary-tailwind.ts","../src/rules/consistent-spacing.ts","../src/rules/consistent-dark-mode.ts","../src/rules/no-direct-store-import.ts","../src/rules/prefer-zustand-state-management.ts","../src/rules/no-mixed-component-libraries.ts","../src/rules/semantic.ts","../src/utils/cache.ts","../src/utils/styleguide-loader.ts","../src/rule-registry.ts","../src/index.ts"],"sourcesContent":["/**\n * Rule creation helper using @typescript-eslint/utils\n */\n\nimport { ESLintUtils } from \"@typescript-eslint/utils\";\n\nexport const createRule = ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/peter-suggate/uilint/blob/main/packages/uilint-eslint/docs/rules/${name}.md`\n);\n","/**\n * Rule: no-arbitrary-tailwind\n *\n * Forbids arbitrary Tailwind values like w-[123px], bg-[#fff], etc.\n */\n\nimport { createRule } from \"../utils/create-rule.js\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"noArbitraryValue\";\ntype Options = [];\n\n// Regex to match arbitrary Tailwind values: word-[anything]\nconst ARBITRARY_VALUE_REGEX = /\\b[\\w-]+-\\[[^\\]]+\\]/g;\n\nexport default createRule<Options, MessageIds>({\n name: \"no-arbitrary-tailwind\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Forbid arbitrary Tailwind values like w-[123px]\",\n },\n messages: {\n noArbitraryValue:\n \"Avoid arbitrary Tailwind value '{{value}}'. Use the spacing/color scale instead.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n return {\n // Check className attributes in JSX\n JSXAttribute(node) {\n if (\n node.name.type === \"JSXIdentifier\" &&\n (node.name.name === \"className\" || node.name.name === \"class\")\n ) {\n const value = node.value;\n\n // Handle string literal: className=\"...\"\n if (value?.type === \"Literal\" && typeof value.value === \"string\") {\n checkClassString(context, value, value.value);\n }\n\n // Handle JSX expression: className={...}\n if (value?.type === \"JSXExpressionContainer\") {\n const expr = value.expression;\n\n // Direct string: className={\"...\"}\n if (expr.type === \"Literal\" && typeof expr.value === \"string\") {\n checkClassString(context, expr, expr.value);\n }\n\n // Template literal: className={`...`}\n if (expr.type === \"TemplateLiteral\") {\n for (const quasi of expr.quasis) {\n checkClassString(context, quasi, quasi.value.raw);\n }\n }\n }\n }\n },\n\n // Check cn(), clsx(), classnames() calls\n CallExpression(node) {\n if (node.callee.type !== \"Identifier\") return;\n const name = node.callee.name;\n\n if (name === \"cn\" || name === \"clsx\" || name === \"classnames\") {\n for (const arg of node.arguments) {\n if (arg.type === \"Literal\" && typeof arg.value === \"string\") {\n checkClassString(context, arg, arg.value);\n }\n if (arg.type === \"TemplateLiteral\") {\n for (const quasi of arg.quasis) {\n checkClassString(context, quasi, quasi.value.raw);\n }\n }\n }\n }\n },\n };\n },\n});\n\nfunction checkClassString(\n context: Parameters<\n ReturnType<typeof createRule<[], \"noArbitraryValue\">>[\"create\"]\n >[0],\n node: TSESTree.Node,\n classString: string\n) {\n const matches = classString.matchAll(ARBITRARY_VALUE_REGEX);\n\n for (const match of matches) {\n // Ignore data attributes (e.g., data-[value], data-test-[something])\n if (match[0].startsWith(\"data-\")) {\n continue;\n }\n\n context.report({\n node,\n messageId: \"noArbitraryValue\",\n data: { value: match[0] },\n });\n }\n}\n","/**\n * Rule: consistent-spacing\n *\n * Enforces use of spacing scale values in gap, padding, margin utilities.\n */\n\nimport { createRule } from \"../utils/create-rule.js\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"invalidSpacing\";\ntype Options = [\n {\n scale?: number[];\n }\n];\n\n// Default Tailwind spacing scale\nconst DEFAULT_SCALE = [\n 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 24,\n 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96,\n];\n\n// Spacing utilities that take numeric values\nconst SPACING_PREFIXES = [\n \"p-\",\n \"px-\",\n \"py-\",\n \"pt-\",\n \"pr-\",\n \"pb-\",\n \"pl-\",\n \"ps-\",\n \"pe-\",\n \"m-\",\n \"mx-\",\n \"my-\",\n \"mt-\",\n \"mr-\",\n \"mb-\",\n \"ml-\",\n \"ms-\",\n \"me-\",\n \"gap-\",\n \"gap-x-\",\n \"gap-y-\",\n \"space-x-\",\n \"space-y-\",\n \"inset-\",\n \"inset-x-\",\n \"inset-y-\",\n \"top-\",\n \"right-\",\n \"bottom-\",\n \"left-\",\n \"w-\",\n \"h-\",\n \"min-w-\",\n \"min-h-\",\n \"max-w-\",\n \"max-h-\",\n \"size-\",\n];\n\n// Build regex to match spacing utilities with numeric values\nfunction buildSpacingRegex(): RegExp {\n const prefixes = SPACING_PREFIXES.map((p) => p.replace(\"-\", \"\\\\-\")).join(\"|\");\n // Match prefix followed by a number (possibly with decimal)\n return new RegExp(`\\\\b(${prefixes})(\\\\d+\\\\.?\\\\d*)\\\\b`, \"g\");\n}\n\nconst SPACING_REGEX = buildSpacingRegex();\n\nexport default createRule<Options, MessageIds>({\n name: \"consistent-spacing\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Enforce spacing scale (no magic numbers in gap/padding)\",\n },\n messages: {\n invalidSpacing:\n \"Spacing value '{{value}}' is not in the allowed scale. Use one of: {{allowed}}\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n scale: {\n type: \"array\",\n items: { type: \"number\" },\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [{ scale: DEFAULT_SCALE }],\n create(context) {\n const options = context.options[0] || {};\n const scale = new Set((options.scale || DEFAULT_SCALE).map(String));\n const scaleList = [...scale].slice(0, 10).join(\", \") + \"...\";\n\n return {\n // Check className attributes in JSX\n JSXAttribute(node) {\n if (\n node.name.type === \"JSXIdentifier\" &&\n (node.name.name === \"className\" || node.name.name === \"class\")\n ) {\n const value = node.value;\n\n if (value?.type === \"Literal\" && typeof value.value === \"string\") {\n checkSpacing(context, node, value.value, scale, scaleList);\n }\n\n if (value?.type === \"JSXExpressionContainer\") {\n const expr = value.expression;\n if (expr.type === \"Literal\" && typeof expr.value === \"string\") {\n checkSpacing(context, node, expr.value, scale, scaleList);\n }\n if (expr.type === \"TemplateLiteral\") {\n for (const quasi of expr.quasis) {\n checkSpacing(context, node, quasi.value.raw, scale, scaleList);\n }\n }\n }\n }\n },\n\n // Check cn(), clsx(), classnames() calls\n CallExpression(node) {\n if (node.callee.type !== \"Identifier\") return;\n const name = node.callee.name;\n\n if (name === \"cn\" || name === \"clsx\" || name === \"classnames\") {\n for (const arg of node.arguments) {\n if (arg.type === \"Literal\" && typeof arg.value === \"string\") {\n checkSpacing(context, arg, arg.value, scale, scaleList);\n }\n if (arg.type === \"TemplateLiteral\") {\n for (const quasi of arg.quasis) {\n checkSpacing(context, quasi, quasi.value.raw, scale, scaleList);\n }\n }\n }\n }\n },\n };\n },\n});\n\nfunction checkSpacing(\n context: Parameters<\n ReturnType<typeof createRule<Options, \"invalidSpacing\">>[\"create\"]\n >[0],\n node: TSESTree.Node,\n classString: string,\n scale: Set<string>,\n scaleList: string\n) {\n // Reset regex state\n SPACING_REGEX.lastIndex = 0;\n\n let match;\n while ((match = SPACING_REGEX.exec(classString)) !== null) {\n const [, , value] = match;\n if (value && !scale.has(value)) {\n context.report({\n node,\n messageId: \"invalidSpacing\",\n data: { value, allowed: scaleList },\n });\n }\n }\n}\n","/**\n * Rule: consistent-dark-mode\n *\n * Ensures consistent dark mode theming in Tailwind CSS classes.\n * - Error: When some color classes have dark: variants but others don't within the same element\n * - Warning: When Tailwind color classes are used in a file but no dark: theming exists\n */\n\nimport { createRule } from \"../utils/create-rule.js\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"inconsistentDarkMode\" | \"missingDarkMode\";\ntype Options = [\n {\n /** Whether to warn when no dark mode classes are found in a file that uses Tailwind colors. Default: true */\n warnOnMissingDarkMode?: boolean;\n }?\n];\n\n// Color-related class prefixes that should have dark mode variants\nconst COLOR_PREFIXES = [\n \"bg-\",\n \"text-\",\n \"border-\",\n \"border-t-\",\n \"border-r-\",\n \"border-b-\",\n \"border-l-\",\n \"border-x-\",\n \"border-y-\",\n \"ring-\",\n \"ring-offset-\",\n \"divide-\",\n \"outline-\",\n \"shadow-\",\n \"accent-\",\n \"caret-\",\n \"fill-\",\n \"stroke-\",\n \"decoration-\",\n \"placeholder-\",\n \"from-\",\n \"via-\",\n \"to-\",\n];\n\n// Values that don't need dark variants (colorless or inherited)\nconst EXEMPT_SUFFIXES = [\"transparent\", \"inherit\", \"current\", \"auto\", \"none\"];\n\n// Known Tailwind color names (not exhaustive, but covers common cases)\n// This helps distinguish color classes from non-color classes like text-lg\nconst COLOR_NAMES = new Set([\n // Grayscale\n \"slate\",\n \"gray\",\n \"zinc\",\n \"neutral\",\n \"stone\",\n // Colors\n \"red\",\n \"orange\",\n \"amber\",\n \"yellow\",\n \"lime\",\n \"green\",\n \"emerald\",\n \"teal\",\n \"cyan\",\n \"sky\",\n \"blue\",\n \"indigo\",\n \"violet\",\n \"purple\",\n \"fuchsia\",\n \"pink\",\n \"rose\",\n // Special\n \"black\",\n \"white\",\n]);\n\n// Patterns that indicate a color value (after the prefix)\n// e.g., \"blue-500\", \"white\", \"black\", \"[#fff]\", \"slate-900/50\"\nconst COLOR_VALUE_PATTERN =\n /^(?:(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\\d{1,3}(?:\\/\\d+)?|black|white|inherit|current|transparent|\\[.+\\])$/;\n\n/**\n * Check if a class has 'dark' in its variant chain\n */\nfunction hasDarkVariant(className: string): boolean {\n const parts = className.split(\":\");\n // All parts except the last are variants\n const variants = parts.slice(0, -1);\n return variants.includes(\"dark\");\n}\n\n/**\n * Get the base class (without any variants like hover:, dark:, md:, etc.)\n */\nfunction getBaseClass(className: string): string {\n const parts = className.split(\":\");\n return parts[parts.length - 1] || \"\";\n}\n\n/**\n * Find the color prefix this class uses, if any\n */\nfunction getColorPrefix(baseClass: string): string | null {\n // Sort by length descending to match more specific prefixes first\n // (e.g., \"border-t-\" before \"border-\")\n const sortedPrefixes = [...COLOR_PREFIXES].sort(\n (a, b) => b.length - a.length\n );\n return sortedPrefixes.find((p) => baseClass.startsWith(p)) || null;\n}\n\n/**\n * Check if the value after the prefix looks like a color value\n * This helps avoid false positives like text-lg (font size) or text-center (alignment)\n */\nfunction isColorValue(baseClass: string, prefix: string): boolean {\n const value = baseClass.slice(prefix.length);\n\n // Check for explicit color patterns\n if (COLOR_VALUE_PATTERN.test(value)) {\n return true;\n }\n\n // Check if it starts with a known color name (handles shades like blue-500)\n const firstPart = value.split(\"-\")[0];\n if (COLOR_NAMES.has(firstPart)) {\n return true;\n }\n\n // Check for arbitrary values that might be colors\n if (value.startsWith(\"[\") && value.endsWith(\"]\")) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Check if a class is exempt from dark mode requirements\n */\nfunction isExempt(baseClass: string): boolean {\n return EXEMPT_SUFFIXES.some((suffix) => baseClass.endsWith(suffix));\n}\n\nexport default createRule<Options, MessageIds>({\n name: \"consistent-dark-mode\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Ensure consistent dark mode theming in Tailwind classes\",\n },\n messages: {\n inconsistentDarkMode:\n \"Inconsistent dark mode: '{{unthemed}}' lack dark: variants while other color classes have them.\",\n missingDarkMode:\n \"No dark mode theming detected. Consider adding dark: variants for color classes.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n warnOnMissingDarkMode: {\n type: \"boolean\",\n description:\n \"Whether to warn when no dark mode classes are found in a file that uses Tailwind colors\",\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [{ warnOnMissingDarkMode: true }],\n create(context) {\n const options = context.options[0] || {};\n const warnOnMissingDarkMode = options.warnOnMissingDarkMode ?? true;\n\n let fileHasColorClasses = false;\n let fileHasDarkMode = false;\n const reportedNodes = new Set<TSESTree.Node>();\n\n function checkClassString(node: TSESTree.Node, classString: string) {\n const classes = classString.split(/\\s+/).filter(Boolean);\n if (classes.length === 0) return;\n\n // Track usage per color prefix: { hasLight, hasDark, lightClasses }\n const prefixUsage = new Map<\n string,\n { hasLight: boolean; hasDark: boolean; lightClasses: string[] }\n >();\n\n for (const cls of classes) {\n const baseClass = getBaseClass(cls);\n const prefix = getColorPrefix(baseClass);\n\n if (!prefix) continue;\n if (isExempt(baseClass)) continue;\n\n // Verify this is actually a color class, not something like text-lg\n if (!isColorValue(baseClass, prefix)) continue;\n\n if (!prefixUsage.has(prefix)) {\n prefixUsage.set(prefix, {\n hasLight: false,\n hasDark: false,\n lightClasses: [],\n });\n }\n\n const usage = prefixUsage.get(prefix)!;\n\n if (hasDarkVariant(cls)) {\n usage.hasDark = true;\n fileHasDarkMode = true;\n } else {\n usage.hasLight = true;\n usage.lightClasses.push(cls);\n }\n }\n\n // Track if file uses color classes\n if (prefixUsage.size > 0) {\n fileHasColorClasses = true;\n }\n\n // Check for inconsistency: some prefixes have dark variants, others don't\n const entries = Array.from(prefixUsage.entries());\n const hasSomeDark = entries.some(([_, u]) => u.hasDark);\n\n if (hasSomeDark) {\n const unthemedEntries = entries.filter(\n ([_, usage]) => usage.hasLight && !usage.hasDark\n );\n\n if (unthemedEntries.length > 0 && !reportedNodes.has(node)) {\n reportedNodes.add(node);\n // Collect the actual class names that lack dark variants\n const unthemedClasses = unthemedEntries.flatMap(\n ([_, u]) => u.lightClasses\n );\n\n context.report({\n node,\n messageId: \"inconsistentDarkMode\",\n data: { unthemed: unthemedClasses.join(\", \") },\n });\n }\n }\n }\n\n function processStringValue(node: TSESTree.Node, value: string) {\n checkClassString(node, value);\n }\n\n function processTemplateLiteral(node: TSESTree.TemplateLiteral) {\n for (const quasi of node.quasis) {\n checkClassString(quasi, quasi.value.raw);\n }\n }\n\n return {\n // Check className attributes in JSX\n JSXAttribute(node) {\n if (\n node.name.type === \"JSXIdentifier\" &&\n (node.name.name === \"className\" || node.name.name === \"class\")\n ) {\n const value = node.value;\n\n // Handle string literal: className=\"...\"\n if (value?.type === \"Literal\" && typeof value.value === \"string\") {\n processStringValue(value, value.value);\n }\n\n // Handle JSX expression: className={...}\n if (value?.type === \"JSXExpressionContainer\") {\n const expr = value.expression;\n\n // Direct string: className={\"...\"}\n if (expr.type === \"Literal\" && typeof expr.value === \"string\") {\n processStringValue(expr, expr.value);\n }\n\n // Template literal: className={`...`}\n if (expr.type === \"TemplateLiteral\") {\n processTemplateLiteral(expr);\n }\n }\n }\n },\n\n // Check cn(), clsx(), classnames(), cva() calls\n CallExpression(node) {\n if (node.callee.type !== \"Identifier\") return;\n const name = node.callee.name;\n\n if (\n name === \"cn\" ||\n name === \"clsx\" ||\n name === \"classnames\" ||\n name === \"cva\" ||\n name === \"twMerge\"\n ) {\n for (const arg of node.arguments) {\n if (arg.type === \"Literal\" && typeof arg.value === \"string\") {\n processStringValue(arg, arg.value);\n }\n if (arg.type === \"TemplateLiteral\") {\n processTemplateLiteral(arg);\n }\n // Handle arrays of class strings\n if (arg.type === \"ArrayExpression\") {\n for (const element of arg.elements) {\n if (\n element?.type === \"Literal\" &&\n typeof element.value === \"string\"\n ) {\n processStringValue(element, element.value);\n }\n if (element?.type === \"TemplateLiteral\") {\n processTemplateLiteral(element);\n }\n }\n }\n }\n }\n },\n\n // At the end of the file, check if Tailwind colors are used without any dark mode\n \"Program:exit\"(node) {\n if (warnOnMissingDarkMode && fileHasColorClasses && !fileHasDarkMode) {\n context.report({\n node,\n messageId: \"missingDarkMode\",\n });\n }\n },\n };\n },\n});\n","/**\n * Rule: no-direct-store-import\n *\n * Forbids direct Zustand store imports - prefer using hooks via context.\n */\n\nimport { createRule } from \"../utils/create-rule.js\";\n\ntype MessageIds = \"noDirectImport\";\ntype Options = [\n {\n storePattern?: string;\n }\n];\n\n// Convert glob pattern to regex\nfunction patternToRegex(pattern: string): RegExp {\n const escaped = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*/g, \".*\")\n .replace(/\\?/g, \".\");\n return new RegExp(`^${escaped}$`);\n}\n\nexport default createRule<Options, MessageIds>({\n name: \"no-direct-store-import\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Forbid direct Zustand store imports (use hooks via context)\",\n },\n messages: {\n noDirectImport:\n \"Avoid importing store '{{name}}' directly. Use the store via a context hook instead.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n storePattern: {\n type: \"string\",\n description: \"Glob pattern for store names\",\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [{ storePattern: \"use*Store\" }],\n create(context) {\n const options = context.options[0] || {};\n const pattern = options.storePattern || \"use*Store\";\n const regex = patternToRegex(pattern);\n\n return {\n ImportDeclaration(node) {\n // Check if importing from a store file\n const source = node.source.value as string;\n if (!source.includes(\"store\")) return;\n\n // Check imported specifiers\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportSpecifier\") {\n const importedName =\n specifier.imported.type === \"Identifier\"\n ? specifier.imported.name\n : specifier.imported.value;\n\n if (regex.test(importedName)) {\n context.report({\n node: specifier,\n messageId: \"noDirectImport\",\n data: { name: importedName },\n });\n }\n }\n\n if (specifier.type === \"ImportDefaultSpecifier\") {\n const localName = specifier.local.name;\n if (regex.test(localName)) {\n context.report({\n node: specifier,\n messageId: \"noDirectImport\",\n data: { name: localName },\n });\n }\n }\n }\n },\n };\n },\n});\n","/**\n * Rule: prefer-zustand-state-management\n *\n * Detects excessive use of React state hooks (useState, useReducer, useContext)\n * in components and suggests using Zustand stores for better state management.\n */\n\nimport { createRule } from \"../utils/create-rule.js\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"excessiveStateHooks\";\ntype Options = [\n {\n /** Maximum number of state hooks before warning. Default: 3 */\n maxStateHooks?: number;\n /** Whether to count useState calls. Default: true */\n countUseState?: boolean;\n /** Whether to count useReducer calls. Default: true */\n countUseReducer?: boolean;\n /** Whether to count useContext calls. Default: true */\n countUseContext?: boolean;\n }?\n];\n\ninterface ComponentInfo {\n name: string;\n node: TSESTree.Node;\n hookCount: number;\n functionNode:\n | TSESTree.FunctionDeclaration\n | TSESTree.FunctionExpression\n | TSESTree.ArrowFunctionExpression;\n}\n\nconst STATE_HOOKS = new Set([\"useState\", \"useReducer\", \"useContext\"]);\n\nexport default createRule<Options, MessageIds>({\n name: \"prefer-zustand-state-management\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Detect excessive use of React state hooks and suggest Zustand stores\",\n },\n messages: {\n excessiveStateHooks:\n \"Component '{{component}}' has {{count}} state hooks (max: {{max}}). Consider using a Zustand store for state management.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n maxStateHooks: {\n type: \"number\",\n minimum: 1,\n description: \"Maximum number of state hooks before warning\",\n },\n countUseState: {\n type: \"boolean\",\n description: \"Whether to count useState calls\",\n },\n countUseReducer: {\n type: \"boolean\",\n description: \"Whether to count useReducer calls\",\n },\n countUseContext: {\n type: \"boolean\",\n description: \"Whether to count useContext calls\",\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [\n {\n maxStateHooks: 3,\n countUseState: true,\n countUseReducer: true,\n countUseContext: true,\n },\n ],\n create(context) {\n const options = context.options[0] || {};\n const maxStateHooks = options.maxStateHooks ?? 3;\n const countUseState = options.countUseState ?? true;\n const countUseReducer = options.countUseReducer ?? true;\n const countUseContext = options.countUseContext ?? true;\n\n // Stack to track current component context\n const componentStack: ComponentInfo[] = [];\n\n // Set of function nodes we've identified as components to check\n const componentFunctions = new Map<TSESTree.Node, ComponentInfo>();\n\n /**\n * Check if a function name indicates a React component (PascalCase)\n */\n function isComponentName(name: string | null | undefined): boolean {\n if (!name) return false;\n // Components start with uppercase, hooks start with lowercase \"use\"\n return /^[A-Z]/.test(name);\n }\n\n /**\n * Check if a function name indicates a custom hook (starts with \"use\")\n */\n function isCustomHookName(name: string | null | undefined): boolean {\n if (!name) return false;\n return /^use[A-Z]/.test(name);\n }\n\n /**\n * Get the name of a function from various declaration patterns\n */\n function getFunctionName(\n node:\n | TSESTree.FunctionDeclaration\n | TSESTree.FunctionExpression\n | TSESTree.ArrowFunctionExpression\n ): string | null {\n // Function declaration: function MyComponent() {}\n if (node.type === \"FunctionDeclaration\" && node.id) {\n return node.id.name;\n }\n\n // Check parent for variable declaration: const MyComponent = () => {}\n const parent = node.parent;\n\n if (\n parent?.type === \"VariableDeclarator\" &&\n parent.id.type === \"Identifier\"\n ) {\n return parent.id.name;\n }\n\n // Check for forwardRef/memo: const MyComponent = forwardRef(function MyComponent() {})\n // or const MyComponent = forwardRef(() => {})\n if (parent?.type === \"CallExpression\") {\n const callParent = parent.parent;\n if (\n callParent?.type === \"VariableDeclarator\" &&\n callParent.id.type === \"Identifier\"\n ) {\n return callParent.id.name;\n }\n }\n\n // Named function expression: const x = function MyComponent() {}\n if (node.type === \"FunctionExpression\" && node.id) {\n return node.id.name;\n }\n\n return null;\n }\n\n /**\n * Check if a hook call should be counted based on options\n */\n function shouldCountHook(hookName: string): boolean {\n switch (hookName) {\n case \"useState\":\n return countUseState;\n case \"useReducer\":\n return countUseReducer;\n case \"useContext\":\n return countUseContext;\n default:\n return false;\n }\n }\n\n /**\n * Get hook name from a call expression (handles both useState and React.useState)\n */\n function getHookName(callee: TSESTree.Expression): string | null {\n // Direct call: useState()\n if (callee.type === \"Identifier\" && STATE_HOOKS.has(callee.name)) {\n return callee.name;\n }\n\n // Member expression: React.useState()\n if (\n callee.type === \"MemberExpression\" &&\n callee.object.type === \"Identifier\" &&\n callee.object.name === \"React\" &&\n callee.property.type === \"Identifier\" &&\n STATE_HOOKS.has(callee.property.name)\n ) {\n return callee.property.name;\n }\n\n return null;\n }\n\n /**\n * Check if we're directly inside a component function (not in a nested function)\n */\n function isDirectChildOfComponent(\n node: TSESTree.Node,\n componentNode: TSESTree.Node\n ): boolean {\n let current: TSESTree.Node | undefined = node.parent;\n\n while (current) {\n // If we hit the component node, we're a direct child\n if (current === componentNode) {\n return true;\n }\n\n // If we hit another function first, we're nested\n if (\n current.type === \"FunctionDeclaration\" ||\n current.type === \"FunctionExpression\" ||\n current.type === \"ArrowFunctionExpression\"\n ) {\n return false;\n }\n\n current = current.parent;\n }\n\n return false;\n }\n\n /**\n * Enter a function that might be a component\n */\n function enterFunction(\n node:\n | TSESTree.FunctionDeclaration\n | TSESTree.FunctionExpression\n | TSESTree.ArrowFunctionExpression\n ) {\n const name = getFunctionName(node);\n\n // Skip custom hooks - they're allowed to have many state hooks\n if (isCustomHookName(name)) {\n return;\n }\n\n // Skip non-component functions (lowercase names that aren't anonymous)\n if (name && !isComponentName(name)) {\n return;\n }\n\n // Track this as a potential component\n const componentInfo: ComponentInfo = {\n name: name || \"AnonymousComponent\",\n node,\n hookCount: 0,\n functionNode: node,\n };\n\n componentStack.push(componentInfo);\n componentFunctions.set(node, componentInfo);\n }\n\n /**\n * Exit a function and report if it had too many hooks\n */\n function exitFunction(\n node:\n | TSESTree.FunctionDeclaration\n | TSESTree.FunctionExpression\n | TSESTree.ArrowFunctionExpression\n ) {\n const componentInfo = componentFunctions.get(node);\n\n if (!componentInfo) {\n return;\n }\n\n // Remove from stack\n const index = componentStack.findIndex((c) => c.functionNode === node);\n if (index !== -1) {\n componentStack.splice(index, 1);\n }\n\n // Check if exceeded threshold\n if (componentInfo.hookCount > maxStateHooks) {\n context.report({\n node: componentInfo.node,\n messageId: \"excessiveStateHooks\",\n data: {\n component: componentInfo.name,\n count: componentInfo.hookCount,\n max: maxStateHooks,\n },\n });\n }\n\n componentFunctions.delete(node);\n }\n\n return {\n FunctionDeclaration: enterFunction,\n FunctionExpression: enterFunction,\n ArrowFunctionExpression: enterFunction,\n \"FunctionDeclaration:exit\": exitFunction,\n \"FunctionExpression:exit\": exitFunction,\n \"ArrowFunctionExpression:exit\": exitFunction,\n\n CallExpression(node) {\n const hookName = getHookName(node.callee);\n\n if (!hookName || !shouldCountHook(hookName)) {\n return;\n }\n\n // Find the innermost component this hook belongs to\n // We iterate from the end to find the most recent (innermost) component\n for (let i = componentStack.length - 1; i >= 0; i--) {\n const component = componentStack[i];\n\n if (isDirectChildOfComponent(node, component.functionNode)) {\n component.hookCount++;\n break;\n }\n }\n },\n };\n },\n});\n","/**\n * Rule: no-mixed-component-libraries\n *\n * Forbids mixing shadcn/ui and MUI components in the same file.\n */\n\nimport type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../utils/create-rule.js\";\n\ntype MessageIds = \"mixedLibraries\" | \"nonPreferredLibrary\";\ntype LibraryName = \"shadcn\" | \"mui\";\ntype Options = [\n {\n libraries?: LibraryName[];\n preferred?: LibraryName;\n }\n];\n\nconst LIBRARY_PATTERNS: Record<LibraryName, string[]> = {\n shadcn: [\"@/components/ui\", \"@radix-ui/\", \"components/ui/\"],\n mui: [\"@mui/material\", \"@mui/icons-material\", \"@emotion/\"],\n};\n\nexport default createRule<Options, MessageIds>({\n name: \"no-mixed-component-libraries\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Forbid mixing component libraries in the same file\",\n },\n messages: {\n mixedLibraries:\n \"Mixing {{lib1}} and {{lib2}} components. Choose one library per file.\",\n nonPreferredLibrary:\n \"Using {{lib}} components, but {{preferred}} is the preferred library. Use {{preferred}} instead.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n libraries: {\n type: \"array\",\n items: { type: \"string\", enum: [\"shadcn\", \"mui\"] },\n },\n preferred: {\n type: \"string\",\n enum: [\"shadcn\", \"mui\"],\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [{ libraries: [\"shadcn\", \"mui\"] }],\n create(context) {\n const options = context.options[0] || {};\n const libraries = (options.libraries || [\"shadcn\", \"mui\"]) as LibraryName[];\n const preferred = options.preferred as LibraryName | undefined;\n const detected: Map<LibraryName, TSESTree.ImportDeclaration> = new Map();\n\n return {\n ImportDeclaration(node) {\n const source = node.source.value as string;\n\n for (const lib of libraries) {\n const patterns = LIBRARY_PATTERNS[lib];\n if (patterns?.some((p) => source.includes(p))) {\n if (!detected.has(lib)) {\n detected.set(lib, node);\n }\n }\n }\n },\n\n \"Program:exit\"() {\n // Check for mixing libraries (existing behavior)\n if (detected.size > 1) {\n const libs = [...detected.keys()];\n const secondLib = libs[1]!;\n const secondNode = detected.get(secondLib)!;\n\n context.report({\n node: secondNode,\n messageId: \"mixedLibraries\",\n data: { lib1: libs[0], lib2: secondLib },\n });\n return;\n }\n\n // Check for non-preferred library usage\n if (preferred && detected.size === 1) {\n const usedLib = [...detected.keys()][0];\n if (usedLib && usedLib !== preferred) {\n const node = detected.get(usedLib)!;\n context.report({\n node,\n messageId: \"nonPreferredLibrary\",\n data: { lib: usedLib, preferred },\n });\n }\n }\n },\n };\n },\n});\n","/**\n * Rule: semantic\n *\n * LLM-powered semantic UI analysis using the project's styleguide.\n * This is the only rule that reads .uilint/styleguide.md.\n */\n\nimport { existsSync, readFileSync } from \"fs\";\nimport { spawnSync } from \"child_process\";\nimport { dirname, join, relative } from \"path\";\nimport { createRule } from \"../utils/create-rule.js\";\nimport {\n getCacheEntry,\n hashContentSync,\n setCacheEntry,\n type CachedIssue,\n} from \"../utils/cache.js\";\nimport { getStyleguide } from \"../utils/styleguide-loader.js\";\nimport { UILINT_DEFAULT_OLLAMA_MODEL } from \"uilint-core\";\nimport { buildSourceScanPrompt } from \"uilint-core\";\n\ntype MessageIds = \"semanticIssue\" | \"styleguideNotFound\" | \"analysisError\";\ntype Options = [\n {\n model?: string;\n styleguidePath?: string;\n }\n];\n\nexport default createRule<Options, MessageIds>({\n name: \"semantic\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"LLM-powered semantic UI analysis using styleguide\",\n },\n messages: {\n semanticIssue: \"{{message}}\",\n styleguideNotFound:\n \"No styleguide found. Create .uilint/styleguide.md or specify styleguidePath.\",\n analysisError: \"Semantic analysis failed: {{error}}\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n model: {\n type: \"string\",\n description: \"Ollama model to use\",\n },\n styleguidePath: {\n type: \"string\",\n description: \"Path to styleguide file\",\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [{ model: UILINT_DEFAULT_OLLAMA_MODEL }],\n create(context) {\n const options = context.options[0] || {};\n const filePath = context.filename;\n const fileDir = dirname(filePath);\n\n // Get styleguide\n const { path: styleguidePath, content: styleguide } = getStyleguide(\n fileDir,\n options.styleguidePath\n );\n\n // Skip if no styleguide\n if (!styleguide) {\n console.error(\n `[uilint] Styleguide not found (styleguidePath=${String(\n options.styleguidePath ?? \"\"\n )}, startDir=${fileDir})`\n );\n\n return {\n Program(node) {\n context.report({\n node,\n messageId: \"styleguideNotFound\",\n });\n },\n };\n }\n\n // Read and hash file contents\n let fileContent: string;\n try {\n fileContent = readFileSync(filePath, \"utf-8\");\n } catch {\n console.error(`[uilint] Failed to read file ${filePath}`);\n return {\n Program(node) {\n context.report({\n node,\n messageId: \"analysisError\",\n data: { error: `Failed to read source file ${filePath}` },\n });\n },\n };\n }\n\n const fileHash = hashContentSync(fileContent);\n const styleguideHash = hashContentSync(styleguide);\n\n // Check cache\n const projectRoot = findProjectRoot(fileDir);\n const relativeFilePath = relative(projectRoot, filePath);\n const cached = getCacheEntry(\n projectRoot,\n relativeFilePath,\n fileHash,\n styleguideHash\n );\n\n const ENABLE_CACHE = false;\n if (ENABLE_CACHE && cached) {\n console.error(`[uilint] Cache hit for ${filePath}`);\n\n // Report cached issues\n return {\n Program(node) {\n for (const issue of cached.issues) {\n context.report({\n node,\n loc: { line: issue.line, column: issue.column || 0 },\n messageId: \"semanticIssue\",\n data: { message: issue.message },\n });\n }\n },\n };\n }\n\n // Cache miss: run sync analysis now (slow), cache, then report.\n ENABLE_CACHE &&\n console.error(\n `[uilint] Cache miss for ${filePath}, running semantic analysis`\n );\n\n return {\n Program(node) {\n const issues = runSemanticAnalysisSync(\n fileContent,\n styleguide,\n options.model || UILINT_DEFAULT_OLLAMA_MODEL,\n filePath\n );\n\n setCacheEntry(projectRoot, relativeFilePath, {\n fileHash,\n styleguideHash,\n issues,\n timestamp: Date.now(),\n });\n\n for (const issue of issues) {\n context.report({\n node,\n loc: { line: issue.line, column: issue.column || 0 },\n messageId: \"semanticIssue\",\n data: { message: issue.message },\n });\n }\n },\n };\n },\n});\n\n/**\n * Find project root by looking for package.json\n */\nfunction findProjectRoot(startDir: string): string {\n let dir = startDir;\n for (let i = 0; i < 20; i++) {\n if (existsSync(join(dir, \"package.json\"))) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return startDir;\n}\n\n/**\n * Run semantic analysis using Ollama (synchronously).\n *\n * Implementation detail:\n * - ESLint rules are synchronous.\n * - Blocking on a Promise (sleep-loop/Atomics) would also block Node's event loop,\n * preventing the HTTP request to Ollama from ever completing.\n * - To keep this simple & debuggable, we run the async LLM call in a child Node\n * process and synchronously wait for it to exit.\n */\nfunction runSemanticAnalysisSync(\n sourceCode: string,\n styleguide: string,\n model: string,\n filePath?: string\n): CachedIssue[] {\n const startTime = Date.now();\n const fileDisplay = filePath ? ` ${filePath}` : \"\";\n\n console.error(`[uilint] Starting semantic analysis (sync)${fileDisplay}`);\n console.error(`[uilint] Model: ${model}`);\n\n // Build prompt in-process (pure string building).\n const prompt = buildSourceScanPrompt(sourceCode, styleguide, {});\n\n // Avoid `uilint-core/node` exports *and* CJS resolution:\n // resolve the installed dependency by file URL relative to this plugin bundle.\n // When built, `import.meta.url` points at `.../uilint-eslint/dist/index.js`,\n // and the dependency lives at `.../uilint-eslint/node_modules/uilint-core/dist/node.js`.\n const coreNodeUrl = new URL(\n \"../node_modules/uilint-core/dist/node.js\",\n import.meta.url\n ).href;\n\n const childScript = `\n import * as coreNode from ${JSON.stringify(coreNodeUrl)};\n const { OllamaClient, logInfo, logWarning, createProgress, pc } = coreNode;\n const chunks = [];\n for await (const c of process.stdin) chunks.push(c);\n const input = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n const model = input.model;\n const prompt = input.prompt;\n\n const client = new OllamaClient({ model });\n const ok = await client.isAvailable();\n if (!ok) {\n logWarning(\"Ollama not available, skipping semantic analysis\");\n process.stdout.write(JSON.stringify({ issues: [] }));\n process.exit(0);\n }\n\n logInfo(\\`Ollama connected \\${pc.dim(\\`(model: \\${model})\\`)}\\`);\n const progress = createProgress(\"Analyzing with LLM...\");\n try {\n const response = await client.complete(prompt, {\n json: true,\n stream: true,\n onProgress: (latestLine) => {\n const maxLen = 60;\n const display =\n latestLine.length > maxLen\n ? latestLine.slice(0, maxLen) + \"…\"\n : latestLine;\n progress.update(\\`LLM: \\${pc.dim(display || \"...\")}\\`);\n },\n });\n progress.succeed(\"LLM complete\");\n process.stdout.write(response);\n } catch (e) {\n progress.fail(\\`LLM failed: \\${e instanceof Error ? e.message : String(e)}\\`);\n process.exit(1);\n }\n `;\n\n const child = spawnSync(\n process.execPath,\n [\"--input-type=module\", \"-e\", childScript],\n {\n input: JSON.stringify({ model, prompt }),\n encoding: \"utf8\",\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n maxBuffer: 20 * 1024 * 1024,\n }\n );\n\n const elapsed = Date.now() - startTime;\n\n if (child.error) {\n console.error(\n `[uilint] Semantic analysis failed after ${elapsed}ms: ${child.error.message}`\n );\n return [];\n }\n\n if (typeof child.status === \"number\" && child.status !== 0) {\n console.error(\n `[uilint] Semantic analysis failed after ${elapsed}ms: child exited ${child.status}`\n );\n return [];\n }\n\n const responseText = (child.stdout || \"\").trim();\n if (!responseText) {\n console.error(\n `[uilint] Semantic analysis returned empty response (${elapsed}ms)`\n );\n return [];\n }\n\n try {\n const parsed = JSON.parse(responseText) as {\n issues?: Array<{ line?: number; column?: number; message?: string }>;\n };\n\n const issues = (parsed.issues || []).map((issue) => ({\n line: issue.line || 1,\n column: issue.column,\n message: issue.message || \"Semantic issue detected\",\n ruleId: \"uilint/semantic\",\n severity: 1 as const,\n }));\n\n if (issues.length > 0) {\n console.error(`[uilint] Found ${issues.length} issue(s) (${elapsed}ms)`);\n } else {\n console.error(`[uilint] No issues found (${elapsed}ms)`);\n }\n\n return issues;\n } catch (e) {\n console.error(\n `[uilint] Semantic analysis failed to parse response after ${elapsed}ms: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n return [];\n }\n}\n","/**\n * File-hash based caching for LLM semantic rule\n *\n * Uses xxhash for fast hashing of file contents.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\n\n// Lazy-loaded xxhash\nlet xxhashInstance: Awaited<\n ReturnType<typeof import(\"xxhash-wasm\")[\"default\"]>\n> | null = null;\n\nasync function getXxhash() {\n if (!xxhashInstance) {\n const xxhash = await import(\"xxhash-wasm\");\n xxhashInstance = await xxhash.default();\n }\n return xxhashInstance;\n}\n\n/**\n * Synchronous hash using a simple djb2 algorithm (fallback when xxhash not available)\n */\nfunction djb2Hash(str: string): string {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash * 33) ^ str.charCodeAt(i);\n }\n return (hash >>> 0).toString(16);\n}\n\n/**\n * Hash content using xxhash (async) or djb2 (sync fallback)\n */\nexport async function hashContent(content: string): Promise<string> {\n try {\n const xxhash = await getXxhash();\n return xxhash.h64ToString(content);\n } catch {\n return djb2Hash(content);\n }\n}\n\n/**\n * Synchronous hash for when async is not possible\n */\nexport function hashContentSync(content: string): string {\n return djb2Hash(content);\n}\n\nexport interface CacheEntry {\n fileHash: string;\n styleguideHash: string;\n issues: CachedIssue[];\n timestamp: number;\n}\n\nexport interface CachedIssue {\n line: number;\n column?: number;\n message: string;\n ruleId: string;\n severity: 1 | 2; // 1 = warn, 2 = error\n}\n\nexport interface CacheStore {\n version: number;\n entries: Record<string, CacheEntry>;\n}\n\nconst CACHE_VERSION = 1;\nconst CACHE_FILE = \".uilint/.cache/eslint-semantic.json\";\n\n/**\n * Get the cache file path for a project\n */\nexport function getCachePath(projectRoot: string): string {\n return join(projectRoot, CACHE_FILE);\n}\n\n/**\n * Load the cache store\n */\nexport function loadCache(projectRoot: string): CacheStore {\n const cachePath = getCachePath(projectRoot);\n\n if (!existsSync(cachePath)) {\n return { version: CACHE_VERSION, entries: {} };\n }\n\n try {\n const content = readFileSync(cachePath, \"utf-8\");\n const cache = JSON.parse(content) as CacheStore;\n\n // Invalidate if version mismatch\n if (cache.version !== CACHE_VERSION) {\n return { version: CACHE_VERSION, entries: {} };\n }\n\n return cache;\n } catch {\n return { version: CACHE_VERSION, entries: {} };\n }\n}\n\n/**\n * Save the cache store\n */\nexport function saveCache(projectRoot: string, cache: CacheStore): void {\n const cachePath = getCachePath(projectRoot);\n\n try {\n const cacheDir = dirname(cachePath);\n if (!existsSync(cacheDir)) {\n mkdirSync(cacheDir, { recursive: true });\n }\n\n writeFileSync(cachePath, JSON.stringify(cache, null, 2), \"utf-8\");\n } catch {\n // Silently fail - caching is optional\n }\n}\n\n/**\n * Get cached entry for a file\n */\nexport function getCacheEntry(\n projectRoot: string,\n filePath: string,\n fileHash: string,\n styleguideHash: string\n): CacheEntry | null {\n const cache = loadCache(projectRoot);\n const entry = cache.entries[filePath];\n\n if (!entry) return null;\n\n // Check if hashes match\n if (entry.fileHash !== fileHash || entry.styleguideHash !== styleguideHash) {\n return null;\n }\n\n return entry;\n}\n\n/**\n * Set cached entry for a file\n */\nexport function setCacheEntry(\n projectRoot: string,\n filePath: string,\n entry: CacheEntry\n): void {\n const cache = loadCache(projectRoot);\n cache.entries[filePath] = entry;\n saveCache(projectRoot, cache);\n}\n\n/**\n * Clear cache for a specific file\n */\nexport function clearCacheEntry(projectRoot: string, filePath: string): void {\n const cache = loadCache(projectRoot);\n delete cache.entries[filePath];\n saveCache(projectRoot, cache);\n}\n\n/**\n * Clear entire cache\n */\nexport function clearCache(projectRoot: string): void {\n saveCache(projectRoot, { version: CACHE_VERSION, entries: {} });\n}\n","/**\n * Styleguide loader for the LLM semantic rule\n *\n * Only the semantic rule reads the styleguide - static rules use ESLint options.\n */\n\nimport { existsSync, readFileSync } from \"fs\";\nimport { dirname, isAbsolute, join, resolve } from \"path\";\n\nconst DEFAULT_STYLEGUIDE_PATHS = [\n \".uilint/styleguide.md\",\n \".uilint/styleguide.yaml\",\n \".uilint/styleguide.yml\",\n];\n\n/**\n * Find workspace root by walking up looking for pnpm-workspace.yaml, package.json, or .git\n */\nfunction findWorkspaceRoot(startDir: string): string {\n let dir = startDir;\n for (let i = 0; i < 20; i++) {\n if (\n existsSync(join(dir, \"pnpm-workspace.yaml\")) ||\n existsSync(join(dir, \".git\"))\n ) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return startDir;\n}\n\n/**\n * Find the nearest package root (directory containing package.json),\n * stopping at the workspace root.\n */\nfunction findNearestPackageRoot(\n startDir: string,\n workspaceRoot: string\n): string {\n let dir = startDir;\n for (let i = 0; i < 30; i++) {\n if (existsSync(join(dir, \"package.json\"))) return dir;\n if (dir === workspaceRoot) break;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return startDir;\n}\n\n/**\n * Find the styleguide file path\n */\nexport function findStyleguidePath(\n startDir: string,\n explicitPath?: string\n): string | null {\n // Explicit path takes precedence\n if (explicitPath) {\n if (isAbsolute(explicitPath)) {\n return existsSync(explicitPath) ? explicitPath : null;\n }\n\n // For relative explicit paths, try:\n // 1) relative to the file dir (back-compat)\n // 2) relative to the nearest package root (typical \"project root\")\n // 3) relative to workspace root (monorepo root)\n const workspaceRoot = findWorkspaceRoot(startDir);\n const packageRoot = findNearestPackageRoot(startDir, workspaceRoot);\n\n const candidates = [\n resolve(startDir, explicitPath),\n resolve(packageRoot, explicitPath),\n resolve(workspaceRoot, explicitPath),\n ];\n\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n\n return null;\n }\n\n // Check from start dir up to workspace root\n const workspaceRoot = findWorkspaceRoot(startDir);\n let dir = startDir;\n\n while (true) {\n for (const relativePath of DEFAULT_STYLEGUIDE_PATHS) {\n const fullPath = join(dir, relativePath);\n if (existsSync(fullPath)) {\n return fullPath;\n }\n }\n\n // Stop at workspace root\n if (dir === workspaceRoot) break;\n\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n return null;\n}\n\n/**\n * Load styleguide content from file\n */\nexport function loadStyleguide(\n startDir: string,\n explicitPath?: string\n): string | null {\n const path = findStyleguidePath(startDir, explicitPath);\n if (!path) return null;\n\n try {\n return readFileSync(path, \"utf-8\");\n } catch {\n return null;\n }\n}\n\n/**\n * Get styleguide path and content\n */\nexport function getStyleguide(\n startDir: string,\n explicitPath?: string\n): { path: string | null; content: string | null } {\n const path = findStyleguidePath(startDir, explicitPath);\n if (!path) return { path: null, content: null };\n\n try {\n const content = readFileSync(path, \"utf-8\");\n return { path, content };\n } catch {\n return { path, content: null };\n }\n}\n","/**\n * Rule Registry\n *\n * Central registry of all UILint ESLint rules with metadata for CLI tooling.\n * This allows the installer to auto-discover available rules and present them\n * to the user with descriptions.\n */\n\nexport interface RuleMetadata {\n /** Rule identifier (e.g., \"no-arbitrary-tailwind\") */\n id: string;\n /** Display name for CLI */\n name: string;\n /** Short description for CLI selection prompts */\n description: string;\n /** Default severity level */\n defaultSeverity: \"error\" | \"warn\" | \"off\";\n /** Default options for the rule */\n defaultOptions?: unknown[];\n /** Whether this rule requires a styleguide file */\n requiresStyleguide?: boolean;\n /** Category for grouping */\n category: \"static\" | \"semantic\";\n}\n\n/**\n * Registry of all available UILint ESLint rules\n *\n * When adding a new rule:\n * 1. Add the rule implementation to src/rules/\n * 2. Add an entry here with metadata\n * 3. Export the rule from src/index.ts\n * 4. The CLI installer will automatically discover and offer it\n */\nexport const ruleRegistry: RuleMetadata[] = [\n {\n id: \"no-arbitrary-tailwind\",\n name: \"No Arbitrary Tailwind\",\n description: \"Forbid arbitrary values like w-[123px], bg-[#fff]\",\n defaultSeverity: \"error\",\n category: \"static\",\n },\n {\n id: \"consistent-spacing\",\n name: \"Consistent Spacing\",\n description: \"Enforce spacing scale (no magic numbers in gap/padding)\",\n defaultSeverity: \"warn\",\n defaultOptions: [{ scale: [0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16] }],\n category: \"static\",\n },\n {\n id: \"consistent-dark-mode\",\n name: \"Consistent Dark Mode\",\n description:\n \"Ensure consistent dark: theming (error on mix, warn on missing)\",\n defaultSeverity: \"error\",\n defaultOptions: [{ warnOnMissingDarkMode: true }],\n category: \"static\",\n },\n {\n id: \"no-direct-store-import\",\n name: \"No Direct Store Import\",\n description: \"Forbid direct Zustand store imports (use context hooks)\",\n defaultSeverity: \"warn\",\n defaultOptions: [{ storePattern: \"use*Store\" }],\n category: \"static\",\n },\n {\n id: \"prefer-zustand-state-management\",\n name: \"Prefer Zustand State Management\",\n description:\n \"Detect excessive useState/useReducer/useContext; suggest Zustand\",\n defaultSeverity: \"warn\",\n defaultOptions: [\n {\n maxStateHooks: 3,\n countUseState: true,\n countUseReducer: true,\n countUseContext: true,\n },\n ],\n category: \"static\",\n },\n {\n id: \"no-mixed-component-libraries\",\n name: \"No Mixed Component Libraries\",\n description: \"Forbid mixing component libraries (e.g., shadcn + MUI)\",\n defaultSeverity: \"error\",\n defaultOptions: [{ libraries: [\"shadcn\", \"mui\"] }],\n category: \"static\",\n },\n {\n id: \"semantic\",\n name: \"Semantic Analysis\",\n description: \"LLM-powered semantic UI analysis using your styleguide\",\n defaultSeverity: \"warn\",\n defaultOptions: [\n { model: \"qwen3-coder:30b\", styleguidePath: \".uilint/styleguide.md\" },\n ],\n requiresStyleguide: true,\n category: \"semantic\",\n },\n];\n\n/**\n * Get rule metadata by ID\n */\nexport function getRuleMetadata(id: string): RuleMetadata | undefined {\n return ruleRegistry.find((rule) => rule.id === id);\n}\n\n/**\n * Get all rules in a category\n */\nexport function getRulesByCategory(\n category: \"static\" | \"semantic\"\n): RuleMetadata[] {\n return ruleRegistry.filter((rule) => rule.category === category);\n}\n","/**\n * UILint ESLint Plugin\n *\n * THIS FILE IS AUTO-GENERATED from src/rule-registry.ts.\n * Do not edit by hand. Run: pnpm -C packages/uilint-eslint generate:index\n */\n\nimport type { Linter } from \"eslint\";\nimport noArbitraryTailwind from \"./rules/no-arbitrary-tailwind.js\";\nimport consistentSpacing from \"./rules/consistent-spacing.js\";\nimport consistentDarkMode from \"./rules/consistent-dark-mode.js\";\nimport noDirectStoreImport from \"./rules/no-direct-store-import.js\";\nimport preferZustandStateManagement from \"./rules/prefer-zustand-state-management.js\";\nimport noMixedComponentLibraries from \"./rules/no-mixed-component-libraries.js\";\nimport semantic from \"./rules/semantic.js\";\n\n/**\n * All available rules\n */\nconst rules = {\n \"no-arbitrary-tailwind\": noArbitraryTailwind,\n \"consistent-spacing\": consistentSpacing,\n \"consistent-dark-mode\": consistentDarkMode,\n \"no-direct-store-import\": noDirectStoreImport,\n \"prefer-zustand-state-management\": preferZustandStateManagement,\n \"no-mixed-component-libraries\": noMixedComponentLibraries,\n \"semantic\": semantic,\n};\n\n// Package version (injected at build time or fallback)\nconst version = \"0.1.0\";\n\n/**\n * Plugin metadata\n */\nconst meta = {\n name: \"uilint\",\n version,\n};\n\n/**\n * The ESLint plugin object\n */\nconst plugin = {\n meta,\n rules,\n};\n\n/**\n * Shared language options for all configs\n */\nconst jsxLanguageOptions: Linter.Config[\"languageOptions\"] = {\n parserOptions: {\n ecmaFeatures: {\n jsx: true,\n },\n },\n};\n\n/**\n * Recommended config - static rules only\n *\n * Usage:\n * ```js\n * import uilint from 'uilint-eslint';\n * export default [uilint.configs.recommended];\n * ```\n */\nconst recommendedConfig: Linter.Config = {\n name: \"uilint/recommended\",\n plugins: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n uilint: plugin as any,\n },\n languageOptions: jsxLanguageOptions,\n rules: {\n \"uilint/no-arbitrary-tailwind\": \"error\",\n \"uilint/consistent-spacing\": [\"warn\", ...[\n {\n \"scale\": [\n 0,\n 1,\n 2,\n 3,\n 4,\n 5,\n 6,\n 8,\n 10,\n 12,\n 16\n ]\n }\n ]],\n \"uilint/consistent-dark-mode\": [\"error\", ...[\n {\n \"warnOnMissingDarkMode\": true\n }\n ]],\n \"uilint/no-direct-store-import\": [\"warn\", ...[\n {\n \"storePattern\": \"use*Store\"\n }\n ]],\n \"uilint/prefer-zustand-state-management\": [\"warn\", ...[\n {\n \"maxStateHooks\": 3,\n \"countUseState\": true,\n \"countUseReducer\": true,\n \"countUseContext\": true\n }\n ]],\n \"uilint/no-mixed-component-libraries\": [\"error\", ...[\n {\n \"libraries\": [\n \"shadcn\",\n \"mui\"\n ]\n }\n ]],\n },\n};\n\n/**\n * Strict config - static rules + semantic rules\n *\n * Usage:\n * ```js\n * import uilint from 'uilint-eslint';\n * export default [uilint.configs.strict];\n * ```\n */\nconst strictConfig: Linter.Config = {\n name: \"uilint/strict\",\n plugins: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n uilint: plugin as any,\n },\n languageOptions: jsxLanguageOptions,\n rules: {\n \"uilint/no-arbitrary-tailwind\": \"error\",\n \"uilint/consistent-spacing\": [\"warn\", ...[\n {\n \"scale\": [\n 0,\n 1,\n 2,\n 3,\n 4,\n 5,\n 6,\n 8,\n 10,\n 12,\n 16\n ]\n }\n ]],\n \"uilint/consistent-dark-mode\": [\"error\", ...[\n {\n \"warnOnMissingDarkMode\": true\n }\n ]],\n \"uilint/no-direct-store-import\": [\"warn\", ...[\n {\n \"storePattern\": \"use*Store\"\n }\n ]],\n \"uilint/prefer-zustand-state-management\": [\"warn\", ...[\n {\n \"maxStateHooks\": 3,\n \"countUseState\": true,\n \"countUseReducer\": true,\n \"countUseContext\": true\n }\n ]],\n \"uilint/no-mixed-component-libraries\": [\"error\", ...[\n {\n \"libraries\": [\n \"shadcn\",\n \"mui\"\n ]\n }\n ]],\n \"uilint/semantic\": [\"warn\", ...[\n {\n \"model\": \"qwen3-coder:30b\",\n \"styleguidePath\": \".uilint/styleguide.md\"\n }\n ]],\n },\n};\n\n/**\n * Pre-configured configs\n */\nconst configs: Record<string, Linter.Config> = {\n recommended: recommendedConfig,\n strict: strictConfig,\n};\n\n/**\n * UILint ESLint export interface\n */\nexport interface UILintESLint {\n meta: typeof meta;\n plugin: typeof plugin;\n rules: typeof rules;\n configs: Record<string, Linter.Config>;\n}\n\n/**\n * Default export for ESLint flat config\n */\nconst uilintEslint: UILintESLint = {\n meta,\n plugin,\n rules,\n configs,\n};\n\nexport default uilintEslint;\n\n// Named exports for convenience\nexport { plugin, rules, configs, meta };\n\n// Re-export utilities for custom rule creation\nexport { createRule } from \"./utils/create-rule.js\";\nexport {\n loadStyleguide,\n findStyleguidePath,\n getStyleguide,\n} from \"./utils/styleguide-loader.js\";\nexport {\n hashContent,\n hashContentSync,\n getCacheEntry,\n setCacheEntry,\n clearCache,\n clearCacheEntry,\n loadCache,\n saveCache,\n type CacheEntry,\n type CachedIssue,\n type CacheStore,\n} from \"./utils/cache.js\";\n\n// Re-export rule registry for CLI tooling\nexport {\n ruleRegistry,\n getRuleMetadata,\n getRulesByCategory,\n type RuleMetadata,\n} from \"./rule-registry.js\";\n"],"mappings":"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","names":["checkClassString","existsSync","readFileSync","dirname","join","existsSync","readFileSync","dirname","join","workspaceRoot","dirname","readFileSync","existsSync","join"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uilint-eslint",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "ESLint plugin for UILint - AI-powered UI consistency checking",
|
|
5
5
|
"author": "Peter Suggate",
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@typescript-eslint/utils": "^8.35.1",
|
|
34
|
-
"uilint-core": "^0.1.
|
|
34
|
+
"uilint-core": "^0.1.43",
|
|
35
35
|
"xxhash-wasm": "^1.1.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"@types/node": "^22.16.0",
|
|
40
40
|
"@typescript-eslint/rule-tester": "^8.35.1",
|
|
41
41
|
"eslint": "^9.28.0",
|
|
42
|
+
"tsx": "^4.21.0",
|
|
42
43
|
"tsup": "^8.5.1",
|
|
43
44
|
"typescript": "^5.9.3",
|
|
44
45
|
"vitest": "^4.0.16"
|
|
@@ -62,7 +63,8 @@
|
|
|
62
63
|
"access": "public"
|
|
63
64
|
},
|
|
64
65
|
"scripts": {
|
|
65
|
-
"
|
|
66
|
+
"generate:index": "node --import tsx scripts/generate-index.ts",
|
|
67
|
+
"build": "pnpm run generate:index && tsup",
|
|
66
68
|
"dev": "tsup --watch",
|
|
67
69
|
"typecheck": "tsc --noEmit",
|
|
68
70
|
"lint": "eslint src/",
|