tailwind-unwind 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +157 -0
- package/bin/index.js +6 -0
- package/dist/chunk-N7HD4T2I.js +1469 -0
- package/dist/chunk-N7HD4T2I.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +63 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +265 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/analyzer/suggestions.ts","../src/analyzer/dedupe.ts","../src/analyzer/combiner.ts","../src/analyzer/patternFinder.ts","../src/parser/classHelpers.ts","../src/parser/ast.ts","../src/parser/jsxParser.ts","../src/scanner/ignore.ts","../src/scanner/fileWalker.ts","../src/core/scanProject.ts","../src/reporters/consoleReporter.ts","../src/reporters/jsonReporter.ts","../src/commands/analyze.ts","../src/codemod/replaceClassNames.ts","../src/generator/classPrefix.ts","../src/generator/cssGenerator.ts","../src/core/buildComponents.ts","../src/commands/apply.ts","../src/commands/generate.ts"],"sourcesContent":["const BREAKPOINT_PREFIX = /^(sm|md|lg|xl|2xl):/;\n\nfunction baseClass(cls: string): string {\n return cls.replace(BREAKPOINT_PREFIX, '');\n}\n\nfunction has(classes: string[], name: string): boolean {\n return classes.some((cls) => baseClass(cls) === name);\n}\n\nfunction hasPrefix(classes: string[], prefix: string): boolean {\n return classes.some((cls) => baseClass(cls).startsWith(prefix));\n}\n\nfunction hasAll(classes: string[], names: string[]): boolean {\n return names.every((name) => has(classes, name));\n}\n\nfunction hasAny(classes: string[], names: string[]): boolean {\n return names.some((name) => has(classes, name));\n}\n\nfunction hasAnyPrefix(classes: string[], prefixes: string[]): boolean {\n return prefixes.some((prefix) => hasPrefix(classes, prefix));\n}\n\nfunction hasPadding(classes: string[]): boolean {\n return hasAnyPrefix(classes, ['p-', 'px-', 'py-', 'pt-', 'pb-', 'pl-', 'pr-']);\n}\n\nfunction hasMargin(classes: string[]): boolean {\n return hasAnyPrefix(classes, ['m-', 'mx-', 'my-', 'mt-', 'mb-', 'ml-', 'mr-']);\n}\n\nfunction hasGap(classes: string[]): boolean {\n return hasPrefix(classes, 'gap-') || hasPrefix(classes, 'gap-x-') || hasPrefix(classes, 'gap-y-');\n}\n\nfunction hasShadow(classes: string[]): boolean {\n return hasPrefix(classes, 'shadow-');\n}\n\nfunction hasRing(classes: string[]): boolean {\n return hasPrefix(classes, 'ring-');\n}\n\nfunction isFullWidth(classes: string[]): boolean {\n return has(classes, 'w-full');\n}\n\nfunction gridColumnCount(classes: string[]): string | null {\n const colClass = classes.find((cls) => baseClass(cls).startsWith('grid-cols-'));\n if (!colClass) return null;\n return baseClass(colClass).replace('grid-cols-', '');\n}\n\nfunction iconSize(classes: string[]): boolean {\n return hasAny(classes, ['w-4', 'h-4', 'w-5', 'h-5', 'w-6', 'h-6', 'size-4', 'size-5', 'size-6']);\n}\n\ninterface NamingRule {\n name: string;\n match: (classes: string[]) => boolean;\n}\n\n/**\n * Ordered from most specific to general — first match wins.\n * Names describe UI intent, not utility lists.\n */\nconst SEMANTIC_RULES: NamingRule[] = [\n // ── Navigation & chrome ──────────────────────────────────────────\n {\n name: 'page-header',\n match: (c) =>\n hasAll(c, ['flex', 'items-center', 'justify-between']) &&\n (hasPadding(c) || hasPrefix(c, 'border-b')),\n },\n {\n name: 'toolbar',\n match: (c) => hasAll(c, ['flex', 'items-center', 'justify-between']),\n },\n {\n name: 'footer-bar',\n match: (c) =>\n has(c, 'flex') && hasPrefix(c, 'border-t') && hasPadding(c),\n },\n {\n name: 'action-bar',\n match: (c) =>\n hasAll(c, ['flex', 'items-center', 'justify-end']) && hasPadding(c),\n },\n {\n name: 'breadcrumb',\n match: (c) =>\n hasAll(c, ['flex', 'items-center']) &&\n hasGap(c) &&\n hasAny(c, ['text-sm', 'text-xs']),\n },\n {\n name: 'nav-item',\n match: (c) =>\n hasAll(c, ['flex', 'items-center']) && hasGap(c) && hasPadding(c),\n },\n {\n name: 'sidebar',\n match: (c) =>\n hasAll(c, ['flex', 'flex-col']) &&\n (has(c, 'h-full') || has(c, 'h-screen') || hasPrefix(c, 'w-')),\n },\n {\n name: 'tab-bar',\n match: (c) =>\n has(c, 'flex') && hasPrefix(c, 'border-b') && hasGap(c),\n },\n\n // ── Flex layout ──────────────────────────────────────────────────\n {\n name: 'centered-row',\n match: (c) => hasAll(c, ['flex', 'items-center', 'justify-center']),\n },\n {\n name: 'spread-row',\n match: (c) => has(c, 'flex') && has(c, 'justify-between'),\n },\n {\n name: 'aligned-row',\n match: (c) => hasAll(c, ['flex', 'items-center']),\n },\n {\n name: 'inline-actions',\n match: (c) => has(c, 'inline-flex') && has(c, 'items-center'),\n },\n {\n name: 'wrap-row',\n match: (c) => has(c, 'flex') && has(c, 'flex-wrap'),\n },\n {\n name: 'form-row',\n match: (c) => hasAll(c, ['flex', 'flex-col']) && hasGap(c),\n },\n {\n name: 'stack',\n match: (c) => hasAll(c, ['flex', 'flex-col']),\n },\n {\n name: 'row',\n match: (c) => has(c, 'flex'),\n },\n\n // ── Grid layout ──────────────────────────────────────────────────\n {\n name: 'photo-grid',\n match: (c) => has(c, 'grid') && hasPrefix(c, 'grid-cols-') && has(c, 'object-cover'),\n },\n {\n name: 'card-grid',\n match: (c) => has(c, 'grid') && hasGap(c) && hasPrefix(c, 'grid-cols-'),\n },\n {\n name: 'grid',\n match: (c) => has(c, 'grid'),\n },\n\n // ── Media & images ───────────────────────────────────────────────\n {\n name: 'media-cover',\n match: (c) => has(c, 'object-cover') && isFullWidth(c),\n },\n {\n name: 'media-contain',\n match: (c) => has(c, 'object-contain') && isFullWidth(c),\n },\n {\n name: 'aspect-video',\n match: (c) => has(c, 'aspect-video'),\n },\n {\n name: 'aspect-square',\n match: (c) => has(c, 'aspect-square'),\n },\n {\n name: 'avatar',\n match: (c) =>\n has(c, 'rounded-full') &&\n (hasPrefix(c, 'w-') || hasPrefix(c, 'h-') || hasPrefix(c, 'size-')) &&\n hasPrefix(c, 'object-'),\n },\n {\n name: 'thumbnail',\n match: (c) =>\n has(c, 'object-cover') &&\n hasAnyPrefix(c, ['w-16', 'w-12', 'w-20', 'h-16', 'h-12', 'h-20']),\n },\n {\n name: 'icon',\n match: (c) => iconSize(c) && !has(c, 'flex'),\n },\n {\n name: 'logo',\n match: (c) => hasAny(c, ['h-8', 'h-6', 'h-10']) && has(c, 'w-auto'),\n },\n {\n name: 'media-frame',\n match: (c) => isFullWidth(c) && has(c, 'h-auto'),\n },\n\n // ── Interactive (before surfaces — shares bg-/rounded-/px-) ─────\n {\n name: 'badge',\n match: (c) =>\n has(c, 'rounded-full') &&\n hasAnyPrefix(c, ['px-', 'py-']) &&\n hasAny(c, ['text-xs', 'text-sm']),\n },\n {\n name: 'chip',\n match: (c) =>\n hasAnyPrefix(c, ['px-', 'py-']) &&\n !has(c, 'rounded-full') &&\n hasPrefix(c, 'rounded-') &&\n hasAny(c, ['text-xs', 'text-sm']),\n },\n {\n name: 'tag',\n match: (c) =>\n hasAnyPrefix(c, ['px-2', 'px-3']) &&\n hasAnyPrefix(c, ['py-0', 'py-1']) &&\n hasPrefix(c, 'rounded-md'),\n },\n {\n name: 'icon-button',\n match: (c) =>\n hasAnyPrefix(c, ['p-1', 'p-1.5', 'p-2', 'p-3']) &&\n hasPrefix(c, 'rounded-') &&\n !hasAnyPrefix(c, ['px-4', 'px-5', 'px-6', 'px-8']),\n },\n {\n name: 'primary-button',\n match: (c) =>\n hasAnyPrefix(c, ['px-', 'py-']) &&\n hasPrefix(c, 'rounded-') &&\n hasAnyPrefix(c, ['bg-blue', 'bg-indigo', 'bg-primary', 'bg-violet']),\n },\n {\n name: 'danger-button',\n match: (c) =>\n hasAnyPrefix(c, ['px-', 'py-']) &&\n hasPrefix(c, 'rounded-') &&\n hasAnyPrefix(c, ['bg-red', 'bg-rose', 'bg-destructive']),\n },\n {\n name: 'ghost-button',\n match: (c) =>\n hasAnyPrefix(c, ['px-', 'py-']) &&\n hasPrefix(c, 'rounded-') &&\n hasAnyPrefix(c, ['hover:bg-', 'bg-transparent']),\n },\n {\n name: 'input',\n match: (c) =>\n hasAnyPrefix(c, ['px-', 'py-']) &&\n hasPrefix(c, 'border') &&\n hasPrefix(c, 'rounded-'),\n },\n {\n name: 'textarea',\n match: (c) =>\n hasPrefix(c, 'border') &&\n hasPrefix(c, 'rounded-') &&\n hasAny(c, ['resize-none', 'min-h-']),\n },\n {\n name: 'select',\n match: (c) =>\n hasPrefix(c, 'border') &&\n hasPrefix(c, 'rounded-') &&\n has(c, 'appearance-none'),\n },\n {\n name: 'checkbox-row',\n match: (c) =>\n hasAll(c, ['flex', 'items-center']) && hasGap(c) && hasAny(c, ['text-sm', 'text-base']),\n },\n {\n name: 'button',\n match: (c) =>\n hasAnyPrefix(c, ['px-', 'py-']) && hasPrefix(c, 'rounded-'),\n },\n\n // ── Surfaces & containers ────────────────────────────────────────\n {\n name: 'page-container',\n match: (c) => has(c, 'mx-auto') && hasPrefix(c, 'max-w-'),\n },\n {\n name: 'section',\n match: (c) => hasPadding(c) && hasMargin(c) && !has(c, 'flex') && !has(c, 'grid'),\n },\n {\n name: 'hero',\n match: (c) =>\n hasPadding(c) &&\n hasAny(c, ['text-center', 'justify-center']) &&\n hasAnyPrefix(c, ['text-3xl', 'text-4xl', 'text-5xl']),\n },\n {\n name: 'alert',\n match: (c) =>\n hasAnyPrefix(c, ['border-l-4', 'border-l-2']) &&\n hasPadding(c) &&\n hasPrefix(c, 'bg-'),\n },\n {\n name: 'callout',\n match: (c) =>\n hasPrefix(c, 'bg-') && hasPadding(c) && hasPrefix(c, 'border') && hasPrefix(c, 'rounded-'),\n },\n {\n name: 'panel',\n match: (c) =>\n hasPrefix(c, 'rounded-') && hasPadding(c) && hasPrefix(c, 'border'),\n },\n {\n name: 'card',\n match: (c) =>\n hasPrefix(c, 'rounded-') && hasPadding(c) && hasPrefix(c, 'bg-'),\n },\n {\n name: 'elevated-card',\n match: (c) =>\n hasShadow(c) && hasPrefix(c, 'rounded-') && hasPadding(c),\n },\n {\n name: 'surface',\n match: (c) => hasPrefix(c, 'bg-') && hasPadding(c),\n },\n\n // ── Overlays & positioning ───────────────────────────────────────\n {\n name: 'backdrop',\n match: (c) =>\n has(c, 'fixed') && has(c, 'inset-0') && hasAnyPrefix(c, ['bg-black', 'bg-white', 'bg-gray']),\n },\n {\n name: 'overlay',\n match: (c) => has(c, 'fixed') && has(c, 'inset-0'),\n },\n {\n name: 'modal-shell',\n match: (c) =>\n has(c, 'fixed') && hasAll(c, ['flex', 'items-center', 'justify-center']),\n },\n {\n name: 'drawer',\n match: (c) =>\n has(c, 'fixed') &&\n hasAny(c, ['inset-y-0', 'top-0', 'bottom-0']) &&\n hasPrefix(c, 'w-'),\n },\n {\n name: 'sticky-header',\n match: (c) =>\n has(c, 'sticky') && hasPrefix(c, 'top-') && hasPrefix(c, 'z-'),\n },\n {\n name: 'dropdown',\n match: (c) =>\n has(c, 'absolute') && hasPrefix(c, 'z-') && hasPrefix(c, 'rounded-') && hasShadow(c),\n },\n\n // ── Typography ───────────────────────────────────────────────────\n {\n name: 'prose',\n match: (c) => hasPrefix(c, 'prose'),\n },\n {\n name: 'heading',\n match: (c) =>\n hasAnyPrefix(c, ['text-xl', 'text-2xl', 'text-3xl', 'text-4xl', 'text-5xl']) &&\n hasPrefix(c, 'font-'),\n },\n {\n name: 'title',\n match: (c) =>\n hasAnyPrefix(c, ['text-lg', 'text-xl', 'text-2xl']) && hasPrefix(c, 'font-semibold'),\n },\n {\n name: 'subtitle',\n match: (c) =>\n hasAnyPrefix(c, ['text-base', 'text-lg']) && hasPrefix(c, 'text-gray'),\n },\n {\n name: 'label',\n match: (c) =>\n hasAny(c, ['text-xs', 'text-sm']) && hasAnyPrefix(c, ['font-medium', 'font-semibold']),\n },\n {\n name: 'caption',\n match: (c) =>\n hasAny(c, ['text-sm', 'text-xs']) &&\n hasPrefix(c, 'font-') &&\n !hasAnyPrefix(c, ['font-medium', 'font-semibold']),\n },\n {\n name: 'muted',\n match: (c) =>\n hasAnyPrefix(c, ['text-gray', 'text-slate', 'text-zinc', 'text-neutral']),\n },\n {\n name: 'error-text',\n match: (c) => hasAnyPrefix(c, ['text-red', 'text-rose', 'text-destructive']),\n },\n {\n name: 'link',\n match: (c) =>\n has(c, 'underline') ||\n hasAnyPrefix(c, ['text-blue', 'text-indigo', 'text-primary']),\n },\n {\n name: 'truncate',\n match: (c) => has(c, 'truncate') || has(c, 'line-clamp-1'),\n },\n\n // ── Lists & tables ───────────────────────────────────────────────\n {\n name: 'table-header',\n match: (c) =>\n hasAnyPrefix(c, ['text-xs', 'uppercase']) &&\n hasAnyPrefix(c, ['font-medium', 'font-semibold', 'tracking-']),\n },\n {\n name: 'table-row',\n match: (c) => hasPrefix(c, 'border-b') && hasAll(c, ['flex', 'items-center']),\n },\n {\n name: 'list-item',\n match: (c) =>\n hasAll(c, ['flex', 'items-center']) && hasGap(c) && hasPadding(c),\n },\n {\n name: 'menu-item',\n match: (c) =>\n hasAll(c, ['flex', 'items-center']) &&\n hasGap(c) &&\n hasAnyPrefix(c, ['px-', 'py-']) &&\n hasAnyPrefix(c, ['hover:bg-', 'rounded-']),\n },\n {\n name: 'divider',\n match: (c) =>\n hasPrefix(c, 'border-') && (isFullWidth(c) || hasMargin(c)),\n },\n\n // ── States & effects ─────────────────────────────────────────────\n {\n name: 'skeleton',\n match: (c) => has(c, 'animate-pulse') && hasPrefix(c, 'bg-'),\n },\n {\n name: 'loading',\n match: (c) => has(c, 'animate-spin') || has(c, 'animate-pulse'),\n },\n {\n name: 'disabled',\n match: (c) => has(c, 'opacity-50') || has(c, 'cursor-not-allowed'),\n },\n {\n name: 'focus-ring',\n match: (c) => hasRing(c) || hasAnyPrefix(c, ['focus:ring-', 'focus-visible:ring-']),\n },\n {\n name: 'hover-lift',\n match: (c) =>\n hasAnyPrefix(c, ['hover:shadow-', 'hover:-translate-y-']) && hasPrefix(c, 'transition'),\n },\n\n // ── Scroll & overflow ────────────────────────────────────────────\n {\n name: 'scroll-panel',\n match: (c) =>\n hasAny(c, ['overflow-y-auto', 'overflow-auto', 'overflow-x-auto', 'overflow-hidden']),\n },\n\n // ── Visual fallbacks ─────────────────────────────────────────────\n {\n name: 'shadow-box',\n match: (c) => hasShadow(c) && hasPrefix(c, 'rounded-'),\n },\n {\n name: 'bordered',\n match: (c) => hasPrefix(c, 'border') && hasPadding(c),\n },\n {\n name: 'rounded-box',\n match: (c) => hasPrefix(c, 'rounded-'),\n },\n {\n name: 'padded',\n match: (c) => hasPadding(c),\n },\n];\n\n/** Build a short compositional name from layout + traits. */\nfunction buildCompositionalName(classes: string[]): string {\n const parts: string[] = [];\n\n if (has(classes, 'flex')) {\n if (has(classes, 'flex-col')) parts.push('stack');\n else if (has(classes, 'flex-wrap')) parts.push('wrap');\n else if (has(classes, 'justify-between')) parts.push('between');\n else if (has(classes, 'justify-center')) parts.push('centered');\n else if (has(classes, 'items-center')) parts.push('aligned');\n else parts.push('row');\n } else if (has(classes, 'grid')) {\n const cols = gridColumnCount(classes);\n parts.push(cols ? `grid-${cols}` : 'grid');\n } else if (has(classes, 'inline-flex')) {\n parts.push('inline-row');\n } else if (has(classes, 'inline-block')) {\n parts.push('inline');\n } else if (has(classes, 'block')) {\n parts.push('block');\n }\n\n if (has(classes, 'fixed')) parts.push('fixed');\n else if (has(classes, 'absolute')) parts.push('absolute');\n else if (has(classes, 'sticky')) parts.push('sticky');\n else if (has(classes, 'relative')) parts.push('relative');\n\n if (has(classes, 'object-cover')) parts.push('cover');\n else if (has(classes, 'object-contain')) parts.push('contain');\n else if (has(classes, 'aspect-video')) parts.push('video');\n else if (has(classes, 'rounded-full')) parts.push('circle');\n else if (hasPrefix(classes, 'rounded-')) parts.push('rounded');\n\n if (hasShadow(classes)) parts.push('shadow');\n else if (hasRing(classes)) parts.push('ring');\n else if (hasPrefix(classes, 'border')) parts.push('bordered');\n\n if (hasPrefix(classes, 'bg-')) parts.push('surface');\n else if (hasPrefix(classes, 'text-')) parts.push('text');\n\n if (has(classes, 'truncate')) parts.push('truncate');\n if (has(classes, 'animate-pulse')) parts.push('pulse');\n if (has(classes, 'transition')) parts.push('transition');\n\n if (parts.length === 0 && hasPadding(classes)) parts.push('padded');\n if (parts.length === 0 && hasMargin(classes)) parts.push('spaced');\n\n const unique = [...new Set(parts)];\n return unique.slice(0, 2).join('-') || 'component';\n}\n\n/**\n * Suggest a short, human-readable component class name from a utility list.\n * Prefers semantic names (toolbar, media-cover) over utility concatenation.\n */\nexport function suggestClassName(classes: string[]): string {\n if (classes.length === 0) {\n return 'component';\n }\n\n for (const rule of SEMANTIC_RULES) {\n if (rule.match(classes)) {\n return rule.name;\n }\n }\n\n return buildCompositionalName(classes);\n}\n","import type { ClassCombination } from '../parser/types.js';\n\n/** True when every class in `smaller` is also present in `larger`. */\nexport function isStrictSubset(smaller: string[], larger: string[]): boolean {\n if (smaller.length >= larger.length) {\n return false;\n }\n\n const largerSet = new Set(larger);\n return smaller.every((cls) => largerSet.has(cls));\n}\n\n/**\n * Drop subset combinations when a strict superset is already in the list\n * (e.g. drop \"flex p-4\" when \"flex items-center p-4\" is present).\n */\nexport function dedupeSubsetCombinations(\n combinations: ClassCombination[],\n): ClassCombination[] {\n return combinations.filter((combo) => {\n return !combinations.some(\n (other) =>\n other.normalized !== combo.normalized &&\n isStrictSubset(combo.classes, other.classes),\n );\n });\n}\n","/**\n * Normalize a class list so order does not affect identity.\n * \"flex p-4\" and \"p-4 flex\" produce the same key.\n */\nexport function normalizeClasses(classes: string[]): string {\n return [...classes].sort().join(' ');\n}\n\n/** Split a className string into individual Tailwind tokens. */\nexport function splitClassString(classString: string): string[] {\n return classString\n .trim()\n .split(/\\s+/)\n .filter((token) => token.length > 0);\n}\n\n/**\n * Generate every combination of size `size` from `classes` (order preserved in output,\n * but callers should normalize before counting).\n */\nexport function generateCombinations(\n classes: string[],\n size: number,\n): string[][] {\n if (size < 1 || size > classes.length) {\n return [];\n }\n\n const results: string[][] = [];\n\n const backtrack = (start: number, current: string[]): void => {\n if (current.length === size) {\n results.push([...current]);\n return;\n }\n\n for (let i = start; i <= classes.length - (size - current.length); i += 1) {\n const item = classes[i];\n if (item !== undefined) {\n current.push(item);\n backtrack(i + 1, current);\n current.pop();\n }\n }\n };\n\n backtrack(0, []);\n return results;\n}\n","import type {\n ClassCombination,\n ClassNameOccurrence,\n CombinationLocation,\n} from '../parser/types.js';\nimport { suggestClassName } from './suggestions.js';\nimport { dedupeSubsetCombinations } from './dedupe.js';\nimport { generateCombinations, normalizeClasses } from './combiner.js';\n\nconst DEFAULT_MIN_COMBINATION_SIZE = 2;\nconst DEFAULT_MAX_COMBINATION_SIZE = 5;\nconst DEFAULT_MIN_OCCURRENCES = 5;\nconst DEFAULT_TOP_LIMIT = 10;\n\nexport interface PatternFinderOptions {\n minOccurrences?: number;\n minSize?: number;\n maxSize?: number;\n topLimit?: number;\n dedupeSubsets?: boolean;\n}\n\ninterface FrequencyEntry {\n classes: string[];\n count: number;\n locations: CombinationLocation[];\n}\n\nfunction locationKey(location: CombinationLocation): string {\n return `${location.filePath}:${location.line ?? 0}`;\n}\n\n/**\n * From every className occurrence, enumerate class subsets,\n * count normalized combinations, and return the most frequent ones.\n */\nexport function findFrequentPatterns(\n occurrences: ClassNameOccurrence[],\n options: PatternFinderOptions = {},\n): ClassCombination[] {\n const minOccurrences = options.minOccurrences ?? DEFAULT_MIN_OCCURRENCES;\n const minSize = options.minSize ?? DEFAULT_MIN_COMBINATION_SIZE;\n const maxSize = options.maxSize ?? DEFAULT_MAX_COMBINATION_SIZE;\n const topLimit = options.topLimit ?? DEFAULT_TOP_LIMIT;\n const dedupeSubsets = options.dedupeSubsets ?? true;\n\n const frequency = new Map<string, FrequencyEntry>();\n\n for (const occurrence of occurrences) {\n const uniqueInElement = [...new Set(occurrence.classes)];\n\n if (uniqueInElement.length < minSize) {\n continue;\n }\n\n const location: CombinationLocation = {\n filePath: occurrence.filePath,\n line: occurrence.line,\n };\n\n const cappedMaxSize = Math.min(maxSize, uniqueInElement.length);\n\n for (let size = minSize; size <= cappedMaxSize; size += 1) {\n const combos = generateCombinations(uniqueInElement, size);\n\n for (const combo of combos) {\n const normalized = normalizeClasses(combo);\n const existing = frequency.get(normalized);\n\n if (existing) {\n existing.count += 1;\n const key = locationKey(location);\n if (!existing.locations.some((loc) => locationKey(loc) === key)) {\n existing.locations.push(location);\n }\n } else {\n frequency.set(normalized, {\n classes: [...combo].sort(),\n count: 1,\n locations: [location],\n });\n }\n }\n }\n }\n\n let frequent = [...frequency.entries()]\n .filter(([, value]) => value.count > minOccurrences)\n .map(([normalized, value]) => ({\n normalized,\n classes: value.classes,\n occurrences: value.count,\n suggestion: `.${suggestClassName(value.classes)}`,\n locations: value.locations,\n }))\n .sort((a, b) => {\n if (b.occurrences !== a.occurrences) {\n return b.occurrences - a.occurrences;\n }\n return b.classes.length - a.classes.length;\n });\n\n if (dedupeSubsets) {\n frequent = dedupeSubsetCombinations(frequent);\n }\n\n return frequent.slice(0, topLimit);\n}\n\n/**\n * Find exact duplicate className sets (full class lists per element).\n * Better suited for CSS generation than combinatorial subset search.\n */\nexport function findRepeatedClassSets(\n occurrences: ClassNameOccurrence[],\n options: PatternFinderOptions = {},\n): ClassCombination[] {\n const minOccurrences = options.minOccurrences ?? 3;\n const minSize = options.minSize ?? 2;\n const maxSize = options.maxSize ?? DEFAULT_MAX_COMBINATION_SIZE;\n const topLimit = options.topLimit ?? DEFAULT_TOP_LIMIT;\n\n const frequency = new Map<string, FrequencyEntry>();\n\n for (const occurrence of occurrences) {\n const uniqueInElement = [...new Set(occurrence.classes)];\n\n if (uniqueInElement.length < minSize || uniqueInElement.length > maxSize) {\n continue;\n }\n\n const normalized = normalizeClasses(uniqueInElement);\n const location: CombinationLocation = {\n filePath: occurrence.filePath,\n line: occurrence.line,\n };\n\n const existing = frequency.get(normalized);\n\n if (existing) {\n existing.count += 1;\n const key = locationKey(location);\n if (!existing.locations.some((loc) => locationKey(loc) === key)) {\n existing.locations.push(location);\n }\n } else {\n frequency.set(normalized, {\n classes: [...uniqueInElement].sort(),\n count: 1,\n locations: [location],\n });\n }\n }\n\n return [...frequency.entries()]\n .filter(([, value]) => value.count >= minOccurrences)\n .map(([normalized, value]) => ({\n normalized,\n classes: value.classes,\n occurrences: value.count,\n suggestion: `.${suggestClassName(value.classes)}`,\n locations: value.locations,\n }))\n .sort((a, b) => {\n if (b.occurrences !== a.occurrences) {\n return b.occurrences - a.occurrences;\n }\n return b.classes.length - a.classes.length;\n })\n .slice(0, topLimit);\n}\n\n/**\n * Estimate how much repeated utility usage could be collapsed into components.\n * Uses the best full combination: each redundant instance could replace N utilities with 1.\n */\nexport function calculatePotentialReduction(\n occurrences: ClassNameOccurrence[],\n topCombinations: ClassCombination[],\n): number {\n const totalClassUsages = occurrences.reduce(\n (sum, occurrence) => sum + occurrence.classes.length,\n 0,\n );\n\n if (totalClassUsages === 0 || topCombinations.length === 0) {\n return 0;\n }\n\n const best = [...topCombinations].sort((a, b) => {\n if (b.classes.length !== a.classes.length) {\n return b.classes.length - a.classes.length;\n }\n return b.occurrences - a.occurrences;\n })[0];\n\n if (!best || best.occurrences <= 1 || best.classes.length < 2) {\n return 0;\n }\n\n const redundantInstances = best.occurrences - 1;\n const utilitiesSavedPerInstance = best.classes.length - 1;\n const savable = redundantInstances * utilitiesSavedPerInstance;\n\n return Math.min(100, Math.round((savable / totalClassUsages) * 100));\n}\n","import type {\n ArrayExpression,\n CallExpression,\n ConditionalExpression,\n Expression,\n LogicalExpression,\n TemplateLiteral,\n} from '@babel/types';\nimport { splitClassString } from '../analyzer/combiner.js';\n\n/** Utilities commonly used to merge Tailwind class strings. */\nexport const CLASS_MERGE_CALLEES = new Set([\n 'cn',\n 'clsx',\n 'classnames',\n 'classNames',\n 'twMerge',\n 'cx',\n]);\n\nexport interface ExtractedClasses {\n classes: string[];\n isDynamic: boolean;\n}\n\nfunction mergeExtractions(parts: ExtractedClasses[]): ExtractedClasses {\n const classes = [...new Set(parts.flatMap((part) => part.classes))];\n const isDynamic = parts.some((part) => part.isDynamic);\n\n return { classes, isDynamic };\n}\n\nfunction extractFromStringLiteral(value: string): ExtractedClasses {\n return {\n classes: splitClassString(value),\n isDynamic: false,\n };\n}\n\nfunction extractFromTemplateLiteral(node: TemplateLiteral): ExtractedClasses {\n const parts: string[] = [];\n\n for (const quasi of node.quasis) {\n if (quasi.value.cooked) {\n parts.push(quasi.value.cooked);\n }\n }\n\n const combined = parts.join(' ').trim();\n\n return {\n classes: combined ? splitClassString(combined) : [],\n isDynamic: node.expressions.length > 0,\n };\n}\n\nfunction isClassMergeCallee(expression: Expression): boolean {\n if (expression.type === 'Identifier') {\n return CLASS_MERGE_CALLEES.has(expression.name);\n }\n\n if (\n expression.type === 'MemberExpression' &&\n expression.property.type === 'Identifier'\n ) {\n return CLASS_MERGE_CALLEES.has(expression.property.name);\n }\n\n return false;\n}\n\nfunction extractFromCallArguments(\n args: CallExpression['arguments'],\n): ExtractedClasses {\n const parts: ExtractedClasses[] = [];\n\n for (const arg of args) {\n if (arg.type === 'SpreadElement' || arg.type === 'ArgumentPlaceholder') {\n parts.push({ classes: [], isDynamic: true });\n continue;\n }\n\n parts.push(extractClassesFromExpression(arg));\n }\n\n return mergeExtractions(parts);\n}\n\n/**\n * Recursively pull static Tailwind tokens from JSX className expressions.\n * Unknown/dynamic fragments set `isDynamic: true` but may still yield partial classes.\n */\nexport function extractClassesFromExpression(\n expression: Expression,\n): ExtractedClasses {\n switch (expression.type) {\n case 'StringLiteral':\n return extractFromStringLiteral(expression.value);\n\n case 'TemplateLiteral':\n return extractFromTemplateLiteral(expression);\n\n case 'CallExpression': {\n const { callee } = expression;\n if (callee.type !== 'V8IntrinsicIdentifier' && isClassMergeCallee(callee)) {\n return extractFromCallArguments(expression.arguments);\n }\n return { classes: [], isDynamic: true };\n }\n\n case 'ConditionalExpression': {\n const merged = mergeExtractions([\n extractClassesFromExpression(expression.consequent),\n extractClassesFromExpression(expression.alternate),\n ]);\n return { ...merged, isDynamic: true };\n }\n\n case 'LogicalExpression': {\n const merged = mergeExtractions([\n extractClassesFromExpression(expression.left),\n extractClassesFromExpression(expression.right),\n ]);\n return { ...merged, isDynamic: true };\n }\n\n case 'ArrayExpression':\n return extractFromArrayExpression(expression);\n\n case 'ObjectExpression':\n // clsx object form: { 'p-4': isActive } — keys are potential classes\n return extractFromObjectExpression(expression);\n\n default:\n return { classes: [], isDynamic: true };\n }\n}\n\nfunction extractFromArrayExpression(node: ArrayExpression): ExtractedClasses {\n const parts: ExtractedClasses[] = [];\n\n for (const element of node.elements) {\n if (element === null || element.type === 'SpreadElement') {\n parts.push({ classes: [], isDynamic: true });\n continue;\n }\n\n parts.push(extractClassesFromExpression(element));\n }\n\n return mergeExtractions(parts);\n}\n\nfunction extractFromObjectExpression(\n node: import('@babel/types').ObjectExpression,\n): ExtractedClasses {\n const classes: string[] = [];\n let isDynamic = false;\n\n for (const prop of node.properties) {\n if (prop.type === 'SpreadElement') {\n isDynamic = true;\n continue;\n }\n\n if (prop.type !== 'ObjectProperty') {\n isDynamic = true;\n continue;\n }\n\n const key = prop.key;\n if (key.type === 'StringLiteral') {\n classes.push(...splitClassString(key.value));\n } else if (key.type === 'Identifier') {\n classes.push(...splitClassString(key.name));\n }\n\n if (prop.value.type !== 'BooleanLiteral' || prop.value.value !== true) {\n isDynamic = true;\n }\n }\n\n return {\n classes: [...new Set(classes)],\n isDynamic,\n };\n}\n","import { parse } from '@babel/parser';\nimport type { JSXAttribute, Node } from '@babel/types';\nimport type { ClassNameExtraction } from './types.js';\nimport { extractClassesFromExpression } from './classHelpers.js';\n\nexport const PARSER_PLUGINS = [\n 'jsx',\n 'typescript',\n 'decorators-legacy',\n 'classProperties',\n 'classPrivateProperties',\n 'classPrivateMethods',\n 'dynamicImport',\n 'importMeta',\n] as const;\n\nconst CLASS_ATTRIBUTES = new Set(['className', 'class']);\n\nfunction getAttributeName(attr: JSXAttribute): string | null {\n if (attr.name.type === 'JSXIdentifier') {\n return attr.name.name;\n }\n return null;\n}\n\nexport function isClassAttribute(attr: JSXAttribute): boolean {\n const name = getAttributeName(attr);\n return name !== null && CLASS_ATTRIBUTES.has(name);\n}\n\n/** Extract Tailwind classes from a className/class JSX attribute. */\nexport function extractFromJSXAttribute(\n attr: JSXAttribute,\n): ClassNameExtraction | null {\n if (!isClassAttribute(attr)) {\n return null;\n }\n\n const line = attr.loc?.start.line;\n const value = attr.value;\n\n if (value == null) {\n return { classes: [], isDynamic: true, line };\n }\n\n if (value.type === 'StringLiteral') {\n const result = extractClassesFromExpression(value);\n return { classes: result.classes, isDynamic: result.isDynamic, line };\n }\n\n if (value.type === 'JSXExpressionContainer') {\n const expr = value.expression;\n\n if (expr.type === 'JSXEmptyExpression') {\n return { classes: [], isDynamic: true, line };\n }\n\n const result = extractClassesFromExpression(expr);\n return { classes: result.classes, isDynamic: result.isDynamic, line };\n }\n\n return { classes: [], isDynamic: true, line };\n}\n\nexport function parseSourceToAst(source: string): Node {\n return parse(source, {\n sourceType: 'module',\n plugins: [...PARSER_PLUGINS],\n errorRecovery: true,\n allowReturnOutsideFunction: true,\n ranges: false,\n tokens: false,\n });\n}\n","import babelTraverse from '@babel/traverse';\nimport type { NodePath } from '@babel/traverse';\nimport type { JSXAttribute, JSXElement, Node } from '@babel/types';\nimport fs from 'node:fs/promises';\nimport type { ClassNameExtraction, ParseResult } from './types.js';\nimport {\n extractFromJSXAttribute,\n isClassAttribute,\n parseSourceToAst,\n} from './ast.js';\n\ntype TraverseFn = (\n ast: Node,\n visitors: { JSXElement?: (path: NodePath<JSXElement>) => void },\n) => void;\n\nfunction resolveTraverse(module: unknown): TraverseFn {\n if (typeof module === 'function') {\n return module as TraverseFn;\n }\n\n const withDefault = module as { default?: unknown };\n if (typeof withDefault.default === 'function') {\n return withDefault.default as TraverseFn;\n }\n\n throw new Error('Failed to load @babel/traverse');\n}\n\nconst traverse = resolveTraverse(babelTraverse);\n\nfunction isJSXElementWithClassAttribute(path: NodePath<JSXElement>): boolean {\n const opening = path.node.openingElement;\n return opening.attributes.some(\n (attr) => attr.type === 'JSXAttribute' && isClassAttribute(attr),\n );\n}\n\nfunction collectExtractionsFromAst(\n ast: Node,\n filePath: string,\n): { extractions: ClassNameExtraction[]; warnings: string[] } {\n const extractions: ClassNameExtraction[] = [];\n const warnings: string[] = [];\n\n traverse(ast, {\n JSXElement(path: NodePath<JSXElement>) {\n if (!isJSXElementWithClassAttribute(path)) {\n return;\n }\n\n const opening = path.node.openingElement;\n\n for (const attr of opening.attributes) {\n if (attr.type !== 'JSXAttribute') continue;\n\n const extraction = extractFromJSXAttribute(attr);\n if (!extraction) continue;\n\n if (extraction.isDynamic && extraction.classes.length === 0) {\n const lineInfo = extraction.line ? `:${extraction.line}` : '';\n warnings.push(\n `Dynamic className skipped in ${filePath}${lineInfo}`,\n );\n continue;\n }\n\n if (extraction.classes.length > 0) {\n extractions.push(extraction);\n }\n }\n },\n });\n\n return { extractions, warnings };\n}\n\n/**\n * Parse in-memory source (used by tests and parseFile).\n */\nexport function parseSource(\n source: string,\n filePath = 'unknown',\n): ParseResult {\n const extractions: ClassNameExtraction[] = [];\n const warnings: string[] = [];\n\n try {\n const ast = parseSourceToAst(source);\n const collected = collectExtractionsFromAst(ast, filePath);\n extractions.push(...collected.extractions);\n warnings.push(...collected.warnings);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n warnings.push(`Failed to parse ${filePath}: ${message}`);\n }\n\n return { filePath, extractions, warnings };\n}\n\n/**\n * Parse a single source file and collect all className/class values from JSX.\n */\nexport async function parseFile(filePath: string): Promise<ParseResult> {\n const source = await fs.readFile(filePath, 'utf-8');\n return parseSource(source, filePath);\n}\n","/** Directories excluded from recursive file scanning. */\nexport const IGNORED_DIRECTORIES = [\n 'node_modules',\n '.next',\n 'dist',\n 'build',\n '.git',\n] as const;\n\n/** fast-glob ignore patterns — match directories at any depth. */\nexport const IGNORE_PATTERNS: string[] = IGNORED_DIRECTORIES.map(\n (dir) => `**/${dir}/**`,\n);\n","import fg from 'fast-glob';\nimport path from 'node:path';\nimport { IGNORE_PATTERNS } from './ignore.js';\n\nconst SOURCE_EXTENSIONS = ['tsx', 'jsx', 'ts', 'js'] as const;\n\n/**\n * Recursively collect source files under `targetPath`, skipping common build/cache dirs.\n */\nexport async function walkSourceFiles(targetPath: string): Promise<string[]> {\n const absolutePath = path.resolve(targetPath);\n\n const patterns = SOURCE_EXTENSIONS.map((ext) =>\n path.join(absolutePath, `**/*.${ext}`).replace(/\\\\/g, '/'),\n );\n\n const files = await fg(patterns, {\n absolute: true,\n onlyFiles: true,\n unique: true,\n ignore: IGNORE_PATTERNS,\n dot: false,\n });\n\n return files.sort();\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport {\n calculatePotentialReduction,\n findFrequentPatterns,\n type PatternFinderOptions,\n} from '../analyzer/patternFinder.js';\nimport { normalizeClasses } from '../analyzer/combiner.js';\nimport { parseFile } from '../parser/jsxParser.js';\nimport type {\n AnalysisReport,\n ClassNameOccurrence,\n} from '../parser/types.js';\nimport { walkSourceFiles } from '../scanner/fileWalker.js';\n\nasync function pathExists(targetPath: string): Promise<boolean> {\n try {\n await fs.access(targetPath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport interface ScanProjectOptions extends PatternFinderOptions {\n targetPath: string;\n}\n\nexport interface ScanProjectResult {\n resolvedPath: string;\n files: string[];\n occurrences: ClassNameOccurrence[];\n warnings: string[];\n report: AnalysisReport;\n}\n\n/**\n * Scan a directory, parse className usage, and build an analysis report.\n */\nexport async function scanProject(\n options: ScanProjectOptions,\n): Promise<ScanProjectResult> {\n const resolvedPath = path.resolve(options.targetPath);\n\n if (!(await pathExists(resolvedPath))) {\n throw new Error(`Path does not exist: ${resolvedPath}`);\n }\n\n const files = await walkSourceFiles(resolvedPath);\n\n if (files.length === 0) {\n throw new Error(\n `No source files (.tsx, .jsx, .ts, .js) found in: ${resolvedPath}`,\n );\n }\n\n const occurrences: ClassNameOccurrence[] = [];\n const warnings: string[] = [];\n\n for (const file of files) {\n const result = await parseFile(file);\n warnings.push(...result.warnings);\n\n for (const extraction of result.extractions) {\n if (extraction.classes.length > 0) {\n occurrences.push({\n classes: extraction.classes,\n filePath: file,\n line: extraction.line,\n });\n }\n }\n }\n\n const uniqueCombinationKeys = new Set(\n occurrences.map((occurrence) =>\n normalizeClasses([...new Set(occurrence.classes)]),\n ),\n );\n\n const topCombinations = findFrequentPatterns(occurrences, {\n minOccurrences: options.minOccurrences,\n minSize: options.minSize,\n maxSize: options.maxSize,\n topLimit: options.topLimit,\n dedupeSubsets: options.dedupeSubsets,\n });\n\n const potentialReductionPercent = calculatePotentialReduction(\n occurrences,\n topCombinations,\n );\n\n const report: AnalysisReport = {\n targetPath: resolvedPath,\n stats: {\n filesScanned: files.length,\n componentsWithClassName: occurrences.length,\n uniqueCombinations: uniqueCombinationKeys.size,\n totalClassUsages: occurrences.reduce(\n (sum, occurrence) => sum + occurrence.classes.length,\n 0,\n ),\n topCombinations,\n potentialReductionPercent,\n },\n parseWarnings: warnings,\n };\n\n return {\n resolvedPath,\n files,\n occurrences,\n warnings,\n report,\n };\n}\n","import chalk from 'chalk';\nimport type { AnalysisReport } from '../parser/types.js';\nimport { normalizeClasses } from '../analyzer/combiner.js';\n\nfunction formatNumber(value: number): string {\n return value.toLocaleString('en-US');\n}\n\nfunction formatLocations(\n locations: AnalysisReport['stats']['topCombinations'][number]['locations'],\n): string {\n const preview = locations.slice(0, 3).map((loc) => {\n const line = loc.line ? `:${loc.line}` : '';\n return `${loc.filePath}${line}`;\n });\n\n const suffix =\n locations.length > 3 ? ` (+${locations.length - 3} more)` : '';\n\n return preview.join(', ') + suffix;\n}\n\ninterface ConsoleReportOptions {\n topLimit?: number;\n}\n\n/**\n * Render the analysis report to stdout with colors and the layout from the spec.\n */\nexport function printConsoleReport(\n report: AnalysisReport,\n options: ConsoleReportOptions = {},\n): void {\n const { stats } = report;\n const topLimit = options.topLimit ?? 10;\n\n console.log('');\n console.log(chalk.bold.cyan('📊 Tailwind Analysis Report'));\n console.log(chalk.cyan('━'.repeat(41)));\n console.log(`Files scanned: ${chalk.white(formatNumber(stats.filesScanned))}`);\n console.log(\n `Components with className: ${chalk.white(formatNumber(stats.componentsWithClassName))}`,\n );\n console.log(\n `Unique class combinations: ${chalk.white(formatNumber(stats.uniqueCombinations))}`,\n );\n console.log('');\n\n if (stats.topCombinations.length === 0) {\n console.log(\n chalk.yellow(\n 'No frequent class combinations found matching the current filters.',\n ),\n );\n } else {\n console.log(\n chalk.bold.green(`🏆 Top ${Math.min(topLimit, stats.topCombinations.length)} most frequent combinations:`),\n );\n console.log('');\n\n stats.topCombinations.forEach((combo, index) => {\n const displayClasses = normalizeClasses(combo.classes);\n console.log(\n chalk.white(`${index + 1}. `) +\n chalk.yellow(`\"${displayClasses}\"`),\n );\n console.log(\n chalk.gray(` Occurrences: `) + chalk.white(String(combo.occurrences)),\n );\n console.log(\n chalk.gray(` Suggestion: `) + chalk.green(combo.suggestion),\n );\n console.log(\n chalk.gray(` Found in: `) + chalk.dim(formatLocations(combo.locations)),\n );\n console.log('');\n });\n }\n\n console.log(\n chalk.magenta(\n `💡 Potential code reduction: ${stats.potentialReductionPercent}%`,\n ),\n );\n console.log(\n chalk.magenta(\n '💡 Generate CSS: npx tailwind-unwind generate <path> --output styles.css',\n ),\n );\n console.log(\n chalk.magenta(\n '💡 Apply classes: npx tailwind-unwind apply <path> --output styles.css',\n ),\n );\n console.log('');\n}\n","import type { AnalysisReport } from '../parser/types.js';\n\n/** Serialize the analysis report as formatted JSON. */\nexport function printJsonReport(report: AnalysisReport): void {\n console.log(JSON.stringify(report, null, 2));\n}\n","import chalk from 'chalk';\nimport { scanProject } from '../core/scanProject.js';\nimport type { AnalysisReport, AnalyzeOptions } from '../parser/types.js';\nimport { printConsoleReport } from '../reporters/consoleReporter.js';\nimport { printJsonReport } from '../reporters/jsonReporter.js';\n\n/**\n * Run the full analyze pipeline: scan → parse → find patterns → report.\n */\nexport async function analyzeCommand(\n targetPath: string,\n options: AnalyzeOptions = {},\n): Promise<AnalysisReport> {\n let scanResult;\n\n try {\n scanResult = await scanProject({\n targetPath,\n minOccurrences: options.minOccurrences,\n minSize: options.minSize,\n maxSize: options.maxSize,\n topLimit: options.top,\n dedupeSubsets: options.dedupeSubsets,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(chalk.red(`Error: ${message}`));\n process.exit(1);\n }\n\n if (options.format !== 'json') {\n for (const warning of scanResult.warnings) {\n console.warn(chalk.yellow(`⚠ ${warning}`));\n }\n }\n\n const report = scanResult.report;\n\n if (options.format === 'json') {\n printJsonReport(report);\n } else {\n printConsoleReport(report, { topLimit: options.top });\n }\n\n return report;\n}\n","import babelGenerate from '@babel/generator';\nimport babelTraverse from '@babel/traverse';\nimport * as t from '@babel/types';\nimport type { NodePath } from '@babel/traverse';\nimport type { JSXAttribute, JSXElement, Node } from '@babel/types';\nimport { normalizeClasses } from '../analyzer/combiner.js';\nimport {\n extractFromJSXAttribute,\n isClassAttribute,\n parseSourceToAst,\n} from '../parser/ast.js';\n\ntype TraverseFn = (\n ast: Node,\n visitors: { JSXElement?: (path: NodePath<JSXElement>) => void },\n) => void;\n\ntype GenerateFn = (\n ast: Node,\n options?: { retainLines?: boolean },\n source?: string,\n) => { code: string };\n\nfunction resolveTraverse(module: unknown): TraverseFn {\n if (typeof module === 'function') {\n return module as TraverseFn;\n }\n\n const withDefault = module as { default?: unknown };\n if (typeof withDefault.default === 'function') {\n return withDefault.default as TraverseFn;\n }\n\n throw new Error('Failed to load @babel/traverse');\n}\n\nfunction resolveGenerator(module: unknown): GenerateFn {\n if (typeof module === 'function') {\n return module as GenerateFn;\n }\n\n const withDefault = module as { default?: unknown };\n if (typeof withDefault.default === 'function') {\n return withDefault.default as GenerateFn;\n }\n\n throw new Error('Failed to load @babel/generator');\n}\n\nconst traverse = resolveTraverse(babelTraverse);\nconst generate = resolveGenerator(babelGenerate);\n\nexport interface ClassReplacement {\n filePath: string;\n line?: number;\n from: string;\n to: string;\n}\n\nexport interface SkippedReplacement {\n filePath: string;\n line?: number;\n reason: string;\n classes: string[];\n}\n\nexport interface ReplaceClassNamesResult {\n source: string;\n replacements: ClassReplacement[];\n skipped: SkippedReplacement[];\n changed: boolean;\n}\n\nfunction lookupReplacement(\n extraction: { classes: string[]; isDynamic: boolean },\n replacementMap: Map<string, string>,\n): string | null {\n if (extraction.isDynamic || extraction.classes.length === 0) {\n return null;\n }\n\n const key = normalizeClasses([...new Set(extraction.classes)]);\n return replacementMap.get(key) ?? null;\n}\n\nfunction setStringClassAttribute(\n attr: JSXAttribute,\n className: string,\n): void {\n attr.value = t.stringLiteral(className);\n}\n\n/**\n * Replace exact matching className/class values with generated component classes.\n */\nexport function replaceClassNamesInSource(\n source: string,\n replacementMap: Map<string, string>,\n filePath: string,\n): ReplaceClassNamesResult {\n const replacements: ClassReplacement[] = [];\n const skipped: SkippedReplacement[] = [];\n\n if (replacementMap.size === 0) {\n return { source, replacements, skipped, changed: false };\n }\n\n let ast: Node;\n\n try {\n ast = parseSourceToAst(source);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to parse ${filePath}: ${message}`);\n }\n\n traverse(ast, {\n JSXElement(path: NodePath<JSXElement>) {\n const opening = path.node.openingElement;\n\n for (const attr of opening.attributes) {\n if (attr.type !== 'JSXAttribute' || !isClassAttribute(attr)) {\n continue;\n }\n\n const extraction = extractFromJSXAttribute(attr);\n if (!extraction) continue;\n\n const replacement = lookupReplacement(extraction, replacementMap);\n\n if (!replacement) {\n if (extraction.classes.length > 0) {\n const key = normalizeClasses([...new Set(extraction.classes)]);\n if (replacementMap.size > 0 && !replacementMap.has(key)) {\n if (extraction.isDynamic) {\n skipped.push({\n filePath,\n line: extraction.line,\n reason: 'dynamic className',\n classes: extraction.classes,\n });\n }\n }\n }\n continue;\n }\n\n const from = normalizeClasses([...new Set(extraction.classes)]);\n setStringClassAttribute(attr, replacement);\n\n replacements.push({\n filePath,\n line: extraction.line,\n from,\n to: replacement,\n });\n }\n },\n });\n\n if (replacements.length === 0) {\n return { source, replacements, skipped, changed: false };\n }\n\n const output = generate(ast, { retainLines: true }, source);\n return {\n source: output.code,\n replacements,\n skipped,\n changed: true,\n };\n}\n","/** Default namespace for generated component classes. */\nexport const DEFAULT_CLASS_PREFIX = 'twu-';\n\n/** Normalize prefix so generated classes are clearly namespaced. */\nexport function normalizeClassPrefix(prefix?: string): string {\n if (!prefix || prefix.trim().length === 0) {\n return DEFAULT_CLASS_PREFIX;\n }\n\n const trimmed = prefix.trim();\n return trimmed.endsWith('-') ? trimmed : `${trimmed}-`;\n}\n\n/** Attach a namespace prefix to a base component class name. */\nexport function withClassPrefix(baseName: string, prefix?: string): string {\n const normalizedPrefix = normalizeClassPrefix(prefix);\n\n if (baseName.startsWith(normalizedPrefix)) {\n return baseName;\n }\n\n return `${normalizedPrefix}${baseName}`;\n}\n","import { suggestClassName } from '../analyzer/suggestions.js';\nimport type { ClassCombination } from '../parser/types.js';\nimport { normalizeClassPrefix, withClassPrefix } from './classPrefix.js';\n\nexport interface GeneratedComponent {\n className: string;\n classes: string[];\n occurrences: number;\n}\n\nexport interface CssGeneratorOptions {\n sourcePath: string;\n combinations: ClassCombination[];\n prefix?: string;\n}\n\nexport interface CssGeneratorResult {\n css: string;\n components: GeneratedComponent[];\n}\n\nexport interface AssignClassNamesOptions {\n prefix?: string;\n}\n\n/** Assign unique, prefixed component class names. */\nexport function assignComponentClassNames(\n combinations: ClassCombination[],\n options: AssignClassNamesOptions = {},\n): GeneratedComponent[] {\n const used = new Set<string>();\n\n return combinations.map((combo) => {\n const base = suggestClassName(combo.classes);\n let className = withClassPrefix(base, options.prefix);\n let suffix = 2;\n\n while (used.has(className)) {\n className = withClassPrefix(`${base}-${suffix}`, options.prefix);\n suffix += 1;\n }\n\n used.add(className);\n\n return {\n className,\n classes: combo.classes,\n occurrences: combo.occurrences,\n };\n });\n}\n\n/**\n * Build a Tailwind CSS file with @layer components and @apply rules\n * for the most frequent class combinations.\n */\nexport function generateComponentCss(\n options: CssGeneratorOptions,\n): CssGeneratorResult {\n const components = assignComponentClassNames(options.combinations, {\n prefix: options.prefix,\n });\n const timestamp = new Date().toISOString();\n const classPrefix = normalizeClassPrefix(options.prefix);\n\n const header = [\n '/**',\n ' * Generated by tailwind-unwind',\n ` * Source: ${options.sourcePath}`,\n ` * Generated at: ${timestamp}`,\n ` * Class prefix: ${classPrefix}`,\n ' */',\n '',\n ].join('\\n');\n\n if (components.length === 0) {\n return {\n css: `${header}\\n/* No frequent class combinations found. Try lowering --min-occurrences. */\\n`,\n components: [],\n };\n }\n\n const rules = components\n .map((component) => {\n const applyClasses = component.classes.join(' ');\n return ` .${component.className} {\\n @apply ${applyClasses};\\n }`;\n })\n .join('\\n\\n');\n\n const css = `${header}\\n@layer components {\\n${rules}\\n}\\n`;\n\n return { css, components };\n}\n","import { findRepeatedClassSets } from '../analyzer/patternFinder.js';\nimport {\n assignComponentClassNames,\n generateComponentCss,\n type GeneratedComponent,\n} from '../generator/cssGenerator.js';\nimport type { ClassNameOccurrence } from '../parser/types.js';\nimport type { PatternFinderOptions } from '../analyzer/patternFinder.js';\n\nexport interface BuildComponentsOptions extends PatternFinderOptions {\n sourcePath: string;\n prefix?: string;\n}\n\nexport interface BuildComponentsResult {\n components: GeneratedComponent[];\n css: string;\n replacementMap: Map<string, string>;\n}\n\n/** Build component classes, CSS, and a normalized-class → name lookup map. */\nexport function buildComponents(\n occurrences: ClassNameOccurrence[],\n options: BuildComponentsOptions,\n): BuildComponentsResult {\n const combinations = findRepeatedClassSets(occurrences, {\n minOccurrences: options.minOccurrences,\n minSize: options.minSize,\n maxSize: options.maxSize,\n topLimit: options.topLimit,\n });\n\n const { css, components } = generateComponentCss({\n sourcePath: options.sourcePath,\n combinations,\n prefix: options.prefix,\n });\n\n const replacementMap = new Map<string, string>();\n\n for (const component of components) {\n const key = [...component.classes].sort().join(' ');\n replacementMap.set(key, component.className);\n }\n\n return { components, css, replacementMap };\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { replaceClassNamesInSource } from '../codemod/replaceClassNames.js';\nimport { buildComponents } from '../core/buildComponents.js';\nimport { scanProject } from '../core/scanProject.js';\nimport type { AnalyzeOptions } from '../parser/types.js';\n\nexport interface ApplyOptions extends AnalyzeOptions {\n output: string;\n dryRun?: boolean;\n}\n\nexport interface ApplyResult {\n filesModified: number;\n replacementsTotal: number;\n outputPath: string;\n componentsGenerated: number;\n}\n\n/**\n * Generate component CSS and replace matching className strings in source files.\n */\nexport async function applyCommand(\n targetPath: string,\n options: ApplyOptions,\n): Promise<ApplyResult> {\n let scanResult;\n\n try {\n scanResult = await scanProject({ targetPath });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(chalk.red(`Error: ${message}`));\n process.exit(1);\n }\n\n for (const warning of scanResult.warnings) {\n console.warn(chalk.yellow(`⚠ ${warning}`));\n }\n\n const { components, css, replacementMap } = buildComponents(\n scanResult.occurrences,\n {\n sourcePath: scanResult.resolvedPath,\n minOccurrences: options.minOccurrences ?? 3,\n minSize: options.minSize,\n maxSize: options.maxSize,\n topLimit: options.top,\n prefix: options.prefix,\n },\n );\n\n if (components.length === 0) {\n console.error(\n chalk.yellow(\n 'No repeated className sets found. Try lowering --min-occurrences.',\n ),\n );\n process.exit(1);\n }\n\n const outputPath = path.resolve(options.output);\n let filesModified = 0;\n let replacementsTotal = 0;\n const allReplacements: Array<{\n filePath: string;\n line?: number;\n from: string;\n to: string;\n }> = [];\n\n for (const file of scanResult.files) {\n const original = await fs.readFile(file, 'utf-8');\n const result = replaceClassNamesInSource(\n original,\n replacementMap,\n file,\n );\n\n replacementsTotal += result.replacements.length;\n allReplacements.push(...result.replacements);\n\n if (result.changed) {\n filesModified += 1;\n if (!options.dryRun) {\n await fs.writeFile(file, result.source, 'utf-8');\n }\n }\n }\n\n if (!options.dryRun) {\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, css, 'utf-8');\n }\n\n console.log('');\n if (options.dryRun) {\n console.log(chalk.bold.yellow('🔍 Dry run — no files were modified'));\n } else {\n console.log(chalk.bold.green('✅ Classes applied successfully'));\n }\n\n console.log(chalk.gray(` CSS output: `) + chalk.white(outputPath));\n console.log(\n chalk.gray(` Component classes: `) + chalk.white(String(components.length)),\n );\n console.log(\n chalk.gray(` Files modified: `) + chalk.white(String(filesModified)),\n );\n console.log(\n chalk.gray(` Replacements: `) + chalk.white(String(replacementsTotal)),\n );\n\n if (allReplacements.length > 0) {\n console.log('');\n console.log(chalk.bold('Replacements:'));\n for (const item of allReplacements) {\n const line = item.line ? `:${item.line}` : '';\n console.log(\n chalk.gray(` ${item.filePath}${line}`) +\n chalk.white(` \"${item.from}\" `) +\n chalk.cyan('→') +\n chalk.green(` \"${item.to}\"`),\n );\n }\n }\n\n console.log('');\n if (!options.dryRun) {\n console.log(\n chalk.cyan(\n `Import ${path.basename(outputPath)} in your global CSS if you haven't already.`,\n ),\n );\n console.log('');\n }\n\n return {\n filesModified,\n replacementsTotal,\n outputPath,\n componentsGenerated: components.length,\n };\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { buildComponents } from '../core/buildComponents.js';\nimport { scanProject } from '../core/scanProject.js';\nimport type { AnalyzeOptions } from '../parser/types.js';\n\nexport interface GenerateOptions extends AnalyzeOptions {\n output: string;\n}\n\nexport interface GenerateResult {\n outputPath: string;\n componentsGenerated: number;\n report: Awaited<ReturnType<typeof scanProject>>['report'];\n}\n\n/**\n * Analyze a project and write @layer components CSS to the output file.\n */\nexport async function generateCommand(\n targetPath: string,\n options: GenerateOptions,\n): Promise<GenerateResult> {\n let scanResult;\n\n try {\n scanResult = await scanProject({ targetPath });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(chalk.red(`Error: ${message}`));\n process.exit(1);\n }\n\n for (const warning of scanResult.warnings) {\n console.warn(chalk.yellow(`⚠ ${warning}`));\n }\n\n const { css, components } = buildComponents(scanResult.occurrences, {\n sourcePath: scanResult.resolvedPath,\n minOccurrences: options.minOccurrences ?? 3,\n minSize: options.minSize,\n maxSize: options.maxSize,\n topLimit: options.top,\n prefix: options.prefix,\n });\n\n const outputPath = path.resolve(options.output);\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, css, 'utf-8');\n\n console.log('');\n console.log(chalk.bold.green('✅ CSS generated successfully'));\n console.log(chalk.gray(` Output: `) + chalk.white(outputPath));\n console.log(\n chalk.gray(` Components: `) + chalk.white(String(components.length)),\n );\n\n if (components.length > 0) {\n console.log('');\n console.log(chalk.bold('Generated classes:'));\n for (const component of components) {\n console.log(\n chalk.green(` .${component.className}`) +\n chalk.gray(` — ${component.occurrences} occurrences, `) +\n chalk.dim(component.classes.join(' ')),\n );\n }\n console.log('');\n console.log(\n chalk.cyan(\n 'Run apply to replace className strings: npx tailwind-unwind apply <path> --output styles.css',\n ),\n );\n } else {\n console.log(\n chalk.yellow(\n '\\nNo repeated className sets matched the filters. Try lowering --min-occurrences.',\n ),\n );\n }\n\n console.log('');\n\n return {\n outputPath,\n componentsGenerated: components.length,\n report: scanResult.report,\n };\n}\n"],"mappings":"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","names":["path","fs","path","fs","path","chalk","chalk","babelTraverse","resolveTraverse","traverse","babelTraverse","path","fs","path","chalk","chalk","path","fs","fs","path","chalk","chalk","path","fs"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
analyzeCommand,
|
|
4
|
+
applyCommand,
|
|
5
|
+
generateCommand
|
|
6
|
+
} from "../chunk-N7HD4T2I.js";
|
|
7
|
+
|
|
8
|
+
// src/cli/index.ts
|
|
9
|
+
import { Command } from "commander";
|
|
10
|
+
var program = new Command();
|
|
11
|
+
program.name("tailwind-unwind").description("Analyze Tailwind CSS class usage in React/Next.js projects").version("0.1.0");
|
|
12
|
+
program.command("analyze").description("Scan a directory and report frequent Tailwind class combinations").argument("<path>", "Directory to analyze").option("--min-occurrences <n>", "Minimum occurrences threshold", "5").option("--min-size <n>", "Minimum classes per combination", "2").option("--max-size <n>", "Maximum classes per combination", "5").option("--top <n>", "Number of top combinations to show", "10").option("--format <type>", "Output format: console or json", "console").option("--no-dedupe-subsets", "Include subset combinations in results").action(async (targetPath, opts) => {
|
|
13
|
+
try {
|
|
14
|
+
const format = opts.format === "json" ? "json" : "console";
|
|
15
|
+
await analyzeCommand(targetPath, {
|
|
16
|
+
minOccurrences: Number(opts.minOccurrences),
|
|
17
|
+
minSize: Number(opts.minSize),
|
|
18
|
+
maxSize: Number(opts.maxSize),
|
|
19
|
+
top: Number(opts.top),
|
|
20
|
+
format,
|
|
21
|
+
dedupeSubsets: opts.dedupeSubsets
|
|
22
|
+
});
|
|
23
|
+
} catch (error) {
|
|
24
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
25
|
+
console.error("Unexpected error:", message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
program.command("generate").description("Generate @layer components CSS from repeated className sets").argument("<path>", "Directory to analyze").requiredOption("--output <file>", "Output CSS file path").option("--min-occurrences <n>", "Minimum occurrences threshold", "3").option("--min-size <n>", "Minimum classes per combination", "2").option("--max-size <n>", "Maximum classes per combination", "5").option("--top <n>", "Number of combinations to generate", "10").option("--prefix <name>", "Namespace prefix for generated classes", "twu-").action(async (targetPath, opts) => {
|
|
30
|
+
try {
|
|
31
|
+
await generateCommand(targetPath, {
|
|
32
|
+
output: opts.output,
|
|
33
|
+
minOccurrences: Number(opts.minOccurrences),
|
|
34
|
+
minSize: Number(opts.minSize),
|
|
35
|
+
maxSize: Number(opts.maxSize),
|
|
36
|
+
top: Number(opts.top),
|
|
37
|
+
prefix: opts.prefix
|
|
38
|
+
});
|
|
39
|
+
} catch (error) {
|
|
40
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
41
|
+
console.error("Unexpected error:", message);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
program.command("apply").description("Replace repeated className strings with generated component classes").argument("<path>", "Directory to modify").requiredOption("--output <file>", "Output CSS file path").option("--min-occurrences <n>", "Minimum occurrences threshold", "3").option("--min-size <n>", "Minimum classes per combination", "2").option("--max-size <n>", "Maximum classes per combination", "5").option("--top <n>", "Number of component classes to use", "10").option("--dry-run", "Preview replacements without writing files").option("--prefix <name>", "Namespace prefix for generated classes", "twu-").action(async (targetPath, opts) => {
|
|
46
|
+
try {
|
|
47
|
+
await applyCommand(targetPath, {
|
|
48
|
+
output: opts.output,
|
|
49
|
+
minOccurrences: Number(opts.minOccurrences),
|
|
50
|
+
minSize: Number(opts.minSize),
|
|
51
|
+
maxSize: Number(opts.maxSize),
|
|
52
|
+
top: Number(opts.top),
|
|
53
|
+
prefix: opts.prefix,
|
|
54
|
+
dryRun: Boolean(opts.dryRun)
|
|
55
|
+
});
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
+
console.error("Unexpected error:", message);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
program.parse();
|
|
63
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { analyzeCommand } from '../commands/analyze.js';\nimport { applyCommand } from '../commands/apply.js';\nimport { generateCommand } from '../commands/generate.js';\n\nconst program = new Command();\n\nprogram\n .name('tailwind-unwind')\n .description('Analyze Tailwind CSS class usage in React/Next.js projects')\n .version('0.1.0');\n\nprogram\n .command('analyze')\n .description('Scan a directory and report frequent Tailwind class combinations')\n .argument('<path>', 'Directory to analyze')\n .option('--min-occurrences <n>', 'Minimum occurrences threshold', '5')\n .option('--min-size <n>', 'Minimum classes per combination', '2')\n .option('--max-size <n>', 'Maximum classes per combination', '5')\n .option('--top <n>', 'Number of top combinations to show', '10')\n .option('--format <type>', 'Output format: console or json', 'console')\n .option('--no-dedupe-subsets', 'Include subset combinations in results')\n .action(async (targetPath: string, opts) => {\n try {\n const format = opts.format === 'json' ? 'json' : 'console';\n\n await analyzeCommand(targetPath, {\n minOccurrences: Number(opts.minOccurrences),\n minSize: Number(opts.minSize),\n maxSize: Number(opts.maxSize),\n top: Number(opts.top),\n format,\n dedupeSubsets: opts.dedupeSubsets,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error('Unexpected error:', message);\n process.exit(1);\n }\n });\n\nprogram\n .command('generate')\n .description('Generate @layer components CSS from repeated className sets')\n .argument('<path>', 'Directory to analyze')\n .requiredOption('--output <file>', 'Output CSS file path')\n .option('--min-occurrences <n>', 'Minimum occurrences threshold', '3')\n .option('--min-size <n>', 'Minimum classes per combination', '2')\n .option('--max-size <n>', 'Maximum classes per combination', '5')\n .option('--top <n>', 'Number of combinations to generate', '10')\n .option('--prefix <name>', 'Namespace prefix for generated classes', 'twu-')\n .action(async (targetPath: string, opts) => {\n try {\n await generateCommand(targetPath, {\n output: opts.output,\n minOccurrences: Number(opts.minOccurrences),\n minSize: Number(opts.minSize),\n maxSize: Number(opts.maxSize),\n top: Number(opts.top),\n prefix: opts.prefix,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error('Unexpected error:', message);\n process.exit(1);\n }\n });\n\nprogram\n .command('apply')\n .description('Replace repeated className strings with generated component classes')\n .argument('<path>', 'Directory to modify')\n .requiredOption('--output <file>', 'Output CSS file path')\n .option('--min-occurrences <n>', 'Minimum occurrences threshold', '3')\n .option('--min-size <n>', 'Minimum classes per combination', '2')\n .option('--max-size <n>', 'Maximum classes per combination', '5')\n .option('--top <n>', 'Number of component classes to use', '10')\n .option('--dry-run', 'Preview replacements without writing files')\n .option('--prefix <name>', 'Namespace prefix for generated classes', 'twu-')\n .action(async (targetPath: string, opts) => {\n try {\n await applyCommand(targetPath, {\n output: opts.output,\n minOccurrences: Number(opts.minOccurrences),\n minSize: Number(opts.minSize),\n maxSize: Number(opts.maxSize),\n top: Number(opts.top),\n prefix: opts.prefix,\n dryRun: Boolean(opts.dryRun),\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error('Unexpected error:', message);\n process.exit(1);\n }\n });\n\nprogram.parse();\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { Expression, JSXAttribute, Node } from '@babel/types';
|
|
2
|
+
|
|
3
|
+
interface ClassNameExtraction {
|
|
4
|
+
classes: string[];
|
|
5
|
+
isDynamic: boolean;
|
|
6
|
+
line?: number;
|
|
7
|
+
}
|
|
8
|
+
interface ClassNameOccurrence {
|
|
9
|
+
classes: string[];
|
|
10
|
+
filePath: string;
|
|
11
|
+
line?: number;
|
|
12
|
+
}
|
|
13
|
+
interface CombinationLocation {
|
|
14
|
+
filePath: string;
|
|
15
|
+
line?: number;
|
|
16
|
+
}
|
|
17
|
+
interface ParseResult {
|
|
18
|
+
filePath: string;
|
|
19
|
+
extractions: ClassNameExtraction[];
|
|
20
|
+
warnings: string[];
|
|
21
|
+
}
|
|
22
|
+
interface ClassCombination {
|
|
23
|
+
normalized: string;
|
|
24
|
+
classes: string[];
|
|
25
|
+
occurrences: number;
|
|
26
|
+
suggestion: string;
|
|
27
|
+
locations: CombinationLocation[];
|
|
28
|
+
}
|
|
29
|
+
interface AnalysisStats {
|
|
30
|
+
filesScanned: number;
|
|
31
|
+
componentsWithClassName: number;
|
|
32
|
+
uniqueCombinations: number;
|
|
33
|
+
totalClassUsages: number;
|
|
34
|
+
topCombinations: ClassCombination[];
|
|
35
|
+
potentialReductionPercent: number;
|
|
36
|
+
}
|
|
37
|
+
interface AnalysisReport {
|
|
38
|
+
targetPath: string;
|
|
39
|
+
stats: AnalysisStats;
|
|
40
|
+
parseWarnings: string[];
|
|
41
|
+
}
|
|
42
|
+
interface AnalyzeOptions {
|
|
43
|
+
minOccurrences?: number;
|
|
44
|
+
minSize?: number;
|
|
45
|
+
maxSize?: number;
|
|
46
|
+
top?: number;
|
|
47
|
+
format?: 'console' | 'json';
|
|
48
|
+
dedupeSubsets?: boolean;
|
|
49
|
+
/** Namespace prefix for generated component classes (default: twu-) */
|
|
50
|
+
prefix?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Run the full analyze pipeline: scan → parse → find patterns → report.
|
|
55
|
+
*/
|
|
56
|
+
declare function analyzeCommand(targetPath: string, options?: AnalyzeOptions): Promise<AnalysisReport>;
|
|
57
|
+
|
|
58
|
+
interface ApplyOptions extends AnalyzeOptions {
|
|
59
|
+
output: string;
|
|
60
|
+
dryRun?: boolean;
|
|
61
|
+
}
|
|
62
|
+
interface ApplyResult {
|
|
63
|
+
filesModified: number;
|
|
64
|
+
replacementsTotal: number;
|
|
65
|
+
outputPath: string;
|
|
66
|
+
componentsGenerated: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Generate component CSS and replace matching className strings in source files.
|
|
70
|
+
*/
|
|
71
|
+
declare function applyCommand(targetPath: string, options: ApplyOptions): Promise<ApplyResult>;
|
|
72
|
+
|
|
73
|
+
interface PatternFinderOptions {
|
|
74
|
+
minOccurrences?: number;
|
|
75
|
+
minSize?: number;
|
|
76
|
+
maxSize?: number;
|
|
77
|
+
topLimit?: number;
|
|
78
|
+
dedupeSubsets?: boolean;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* From every className occurrence, enumerate class subsets,
|
|
82
|
+
* count normalized combinations, and return the most frequent ones.
|
|
83
|
+
*/
|
|
84
|
+
declare function findFrequentPatterns(occurrences: ClassNameOccurrence[], options?: PatternFinderOptions): ClassCombination[];
|
|
85
|
+
/**
|
|
86
|
+
* Find exact duplicate className sets (full class lists per element).
|
|
87
|
+
* Better suited for CSS generation than combinatorial subset search.
|
|
88
|
+
*/
|
|
89
|
+
declare function findRepeatedClassSets(occurrences: ClassNameOccurrence[], options?: PatternFinderOptions): ClassCombination[];
|
|
90
|
+
/**
|
|
91
|
+
* Estimate how much repeated utility usage could be collapsed into components.
|
|
92
|
+
* Uses the best full combination: each redundant instance could replace N utilities with 1.
|
|
93
|
+
*/
|
|
94
|
+
declare function calculatePotentialReduction(occurrences: ClassNameOccurrence[], topCombinations: ClassCombination[]): number;
|
|
95
|
+
|
|
96
|
+
interface ScanProjectOptions extends PatternFinderOptions {
|
|
97
|
+
targetPath: string;
|
|
98
|
+
}
|
|
99
|
+
interface ScanProjectResult {
|
|
100
|
+
resolvedPath: string;
|
|
101
|
+
files: string[];
|
|
102
|
+
occurrences: ClassNameOccurrence[];
|
|
103
|
+
warnings: string[];
|
|
104
|
+
report: AnalysisReport;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Scan a directory, parse className usage, and build an analysis report.
|
|
108
|
+
*/
|
|
109
|
+
declare function scanProject(options: ScanProjectOptions): Promise<ScanProjectResult>;
|
|
110
|
+
|
|
111
|
+
interface GenerateOptions extends AnalyzeOptions {
|
|
112
|
+
output: string;
|
|
113
|
+
}
|
|
114
|
+
interface GenerateResult {
|
|
115
|
+
outputPath: string;
|
|
116
|
+
componentsGenerated: number;
|
|
117
|
+
report: Awaited<ReturnType<typeof scanProject>>['report'];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Analyze a project and write @layer components CSS to the output file.
|
|
121
|
+
*/
|
|
122
|
+
declare function generateCommand(targetPath: string, options: GenerateOptions): Promise<GenerateResult>;
|
|
123
|
+
|
|
124
|
+
interface GeneratedComponent {
|
|
125
|
+
className: string;
|
|
126
|
+
classes: string[];
|
|
127
|
+
occurrences: number;
|
|
128
|
+
}
|
|
129
|
+
interface CssGeneratorOptions {
|
|
130
|
+
sourcePath: string;
|
|
131
|
+
combinations: ClassCombination[];
|
|
132
|
+
prefix?: string;
|
|
133
|
+
}
|
|
134
|
+
interface CssGeneratorResult {
|
|
135
|
+
css: string;
|
|
136
|
+
components: GeneratedComponent[];
|
|
137
|
+
}
|
|
138
|
+
interface AssignClassNamesOptions {
|
|
139
|
+
prefix?: string;
|
|
140
|
+
}
|
|
141
|
+
/** Assign unique, prefixed component class names. */
|
|
142
|
+
declare function assignComponentClassNames(combinations: ClassCombination[], options?: AssignClassNamesOptions): GeneratedComponent[];
|
|
143
|
+
/**
|
|
144
|
+
* Build a Tailwind CSS file with @layer components and @apply rules
|
|
145
|
+
* for the most frequent class combinations.
|
|
146
|
+
*/
|
|
147
|
+
declare function generateComponentCss(options: CssGeneratorOptions): CssGeneratorResult;
|
|
148
|
+
|
|
149
|
+
interface BuildComponentsOptions extends PatternFinderOptions {
|
|
150
|
+
sourcePath: string;
|
|
151
|
+
prefix?: string;
|
|
152
|
+
}
|
|
153
|
+
interface BuildComponentsResult {
|
|
154
|
+
components: GeneratedComponent[];
|
|
155
|
+
css: string;
|
|
156
|
+
replacementMap: Map<string, string>;
|
|
157
|
+
}
|
|
158
|
+
/** Build component classes, CSS, and a normalized-class → name lookup map. */
|
|
159
|
+
declare function buildComponents(occurrences: ClassNameOccurrence[], options: BuildComponentsOptions): BuildComponentsResult;
|
|
160
|
+
|
|
161
|
+
interface ClassReplacement {
|
|
162
|
+
filePath: string;
|
|
163
|
+
line?: number;
|
|
164
|
+
from: string;
|
|
165
|
+
to: string;
|
|
166
|
+
}
|
|
167
|
+
interface SkippedReplacement {
|
|
168
|
+
filePath: string;
|
|
169
|
+
line?: number;
|
|
170
|
+
reason: string;
|
|
171
|
+
classes: string[];
|
|
172
|
+
}
|
|
173
|
+
interface ReplaceClassNamesResult {
|
|
174
|
+
source: string;
|
|
175
|
+
replacements: ClassReplacement[];
|
|
176
|
+
skipped: SkippedReplacement[];
|
|
177
|
+
changed: boolean;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Replace exact matching className/class values with generated component classes.
|
|
181
|
+
*/
|
|
182
|
+
declare function replaceClassNamesInSource(source: string, replacementMap: Map<string, string>, filePath: string): ReplaceClassNamesResult;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Suggest a short, human-readable component class name from a utility list.
|
|
186
|
+
* Prefers semantic names (toolbar, media-cover) over utility concatenation.
|
|
187
|
+
*/
|
|
188
|
+
declare function suggestClassName(classes: string[]): string;
|
|
189
|
+
|
|
190
|
+
/** True when every class in `smaller` is also present in `larger`. */
|
|
191
|
+
declare function isStrictSubset(smaller: string[], larger: string[]): boolean;
|
|
192
|
+
/**
|
|
193
|
+
* Drop subset combinations when a strict superset is already in the list
|
|
194
|
+
* (e.g. drop "flex p-4" when "flex items-center p-4" is present).
|
|
195
|
+
*/
|
|
196
|
+
declare function dedupeSubsetCombinations(combinations: ClassCombination[]): ClassCombination[];
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Normalize a class list so order does not affect identity.
|
|
200
|
+
* "flex p-4" and "p-4 flex" produce the same key.
|
|
201
|
+
*/
|
|
202
|
+
declare function normalizeClasses(classes: string[]): string;
|
|
203
|
+
/** Split a className string into individual Tailwind tokens. */
|
|
204
|
+
declare function splitClassString(classString: string): string[];
|
|
205
|
+
/**
|
|
206
|
+
* Generate every combination of size `size` from `classes` (order preserved in output,
|
|
207
|
+
* but callers should normalize before counting).
|
|
208
|
+
*/
|
|
209
|
+
declare function generateCombinations(classes: string[], size: number): string[][];
|
|
210
|
+
|
|
211
|
+
/** Default namespace for generated component classes. */
|
|
212
|
+
declare const DEFAULT_CLASS_PREFIX = "twu-";
|
|
213
|
+
/** Normalize prefix so generated classes are clearly namespaced. */
|
|
214
|
+
declare function normalizeClassPrefix(prefix?: string): string;
|
|
215
|
+
/** Attach a namespace prefix to a base component class name. */
|
|
216
|
+
declare function withClassPrefix(baseName: string, prefix?: string): string;
|
|
217
|
+
|
|
218
|
+
/** Utilities commonly used to merge Tailwind class strings. */
|
|
219
|
+
declare const CLASS_MERGE_CALLEES: Set<string>;
|
|
220
|
+
interface ExtractedClasses {
|
|
221
|
+
classes: string[];
|
|
222
|
+
isDynamic: boolean;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Recursively pull static Tailwind tokens from JSX className expressions.
|
|
226
|
+
* Unknown/dynamic fragments set `isDynamic: true` but may still yield partial classes.
|
|
227
|
+
*/
|
|
228
|
+
declare function extractClassesFromExpression(expression: Expression): ExtractedClasses;
|
|
229
|
+
|
|
230
|
+
declare function isClassAttribute(attr: JSXAttribute): boolean;
|
|
231
|
+
/** Extract Tailwind classes from a className/class JSX attribute. */
|
|
232
|
+
declare function extractFromJSXAttribute(attr: JSXAttribute): ClassNameExtraction | null;
|
|
233
|
+
declare function parseSourceToAst(source: string): Node;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Parse in-memory source (used by tests and parseFile).
|
|
237
|
+
*/
|
|
238
|
+
declare function parseSource(source: string, filePath?: string): ParseResult;
|
|
239
|
+
/**
|
|
240
|
+
* Parse a single source file and collect all className/class values from JSX.
|
|
241
|
+
*/
|
|
242
|
+
declare function parseFile(filePath: string): Promise<ParseResult>;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Recursively collect source files under `targetPath`, skipping common build/cache dirs.
|
|
246
|
+
*/
|
|
247
|
+
declare function walkSourceFiles(targetPath: string): Promise<string[]>;
|
|
248
|
+
|
|
249
|
+
interface ConsoleReportOptions {
|
|
250
|
+
topLimit?: number;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Render the analysis report to stdout with colors and the layout from the spec.
|
|
254
|
+
*/
|
|
255
|
+
declare function printConsoleReport(report: AnalysisReport, options?: ConsoleReportOptions): void;
|
|
256
|
+
|
|
257
|
+
/** Serialize the analysis report as formatted JSON. */
|
|
258
|
+
declare function printJsonReport(report: AnalysisReport): void;
|
|
259
|
+
|
|
260
|
+
/** Directories excluded from recursive file scanning. */
|
|
261
|
+
declare const IGNORED_DIRECTORIES: readonly ["node_modules", ".next", "dist", "build", ".git"];
|
|
262
|
+
/** fast-glob ignore patterns — match directories at any depth. */
|
|
263
|
+
declare const IGNORE_PATTERNS: string[];
|
|
264
|
+
|
|
265
|
+
export { type AnalysisReport, type AnalysisStats, type AnalyzeOptions, type ApplyOptions, type ApplyResult, CLASS_MERGE_CALLEES, type ClassCombination, type ClassNameExtraction, type ClassNameOccurrence, type ClassReplacement, type CombinationLocation, type CssGeneratorOptions, type CssGeneratorResult, DEFAULT_CLASS_PREFIX, type GenerateOptions, type GenerateResult, type GeneratedComponent, IGNORED_DIRECTORIES, IGNORE_PATTERNS, type ParseResult, type ReplaceClassNamesResult, type SkippedReplacement, analyzeCommand, applyCommand, assignComponentClassNames, buildComponents, calculatePotentialReduction, dedupeSubsetCombinations, extractClassesFromExpression, extractFromJSXAttribute, findFrequentPatterns, findRepeatedClassSets, generateCombinations, generateCommand, generateComponentCss, isClassAttribute, isStrictSubset, normalizeClassPrefix, normalizeClasses, parseFile, parseSource, parseSourceToAst, printConsoleReport, printJsonReport, replaceClassNamesInSource, scanProject, splitClassString, suggestClassName, walkSourceFiles, withClassPrefix };
|