tailwind-unwind 0.2.0 → 0.4.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/defaults.ts","../../src/cli/parseOptions.ts","../../src/cli/version.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { analyzeCommand } from '../commands/analyze.js';\nimport { applyCommand } from '../commands/apply.js';\nimport { generateCommand } from '../commands/generate.js';\nimport { ANALYZE_DEFAULTS, GENERATE_DEFAULTS } from './defaults.js';\nimport { resolveCommandOptions, withNumericDefaults } from './parseOptions.js';\nimport { CLI_VERSION } from './version.js';\n\nconst program = new Command();\n\nfunction addSharedOptions(command: Command): Command {\n return command\n .option('--config <file>', 'Path to tailwind-unwind config file')\n .option(\n '--include <patterns>',\n 'Comma-separated glob include patterns (e.g. \"src/**/*.tsx\")',\n )\n .option(\n '--exclude <patterns>',\n 'Comma-separated glob exclude patterns (e.g. \"**/*.test.tsx\")',\n );\n}\n\nprogram\n .name('tailwind-unwind')\n .description('Analyze Tailwind CSS class usage in React/Next.js projects')\n .version(CLI_VERSION);\n\naddSharedOptions(\n program\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')\n .option('--min-size <n>', 'Minimum classes per combination')\n .option('--max-size <n>', 'Maximum classes per combination')\n .option('--top <n>', 'Number of top combinations to show')\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 resolved = withNumericDefaults(\n await resolveCommandOptions('analyze', opts, targetPath),\n opts,\n ANALYZE_DEFAULTS,\n );\n\n await analyzeCommand(targetPath, {\n minOccurrences: resolved.minOccurrences,\n minSize: resolved.minSize,\n maxSize: resolved.maxSize,\n top: resolved.top,\n format: resolved.format,\n dedupeSubsets: process.argv.includes('--no-dedupe-subsets')\n ? false\n : (resolved.dedupeSubsets ?? true),\n include: resolved.include,\n exclude: resolved.exclude,\n configPath: resolved.configPath,\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\naddSharedOptions(\n program\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')\n .option('--min-size <n>', 'Minimum classes per combination')\n .option('--max-size <n>', 'Maximum classes per combination')\n .option('--top <n>', 'Number of combinations to generate')\n .option('--prefix <name>', 'Namespace prefix for generated classes'),\n).action(async (targetPath: string, opts) => {\n try {\n const resolved = withNumericDefaults(\n await resolveCommandOptions('generate', opts, targetPath),\n opts,\n GENERATE_DEFAULTS,\n );\n const output = opts.output ?? resolved.output;\n\n if (!output) {\n console.error(chalk.red('Error: --output is required'));\n process.exit(1);\n }\n\n await generateCommand(targetPath, {\n output,\n minOccurrences: resolved.minOccurrences,\n minSize: resolved.minSize,\n maxSize: resolved.maxSize,\n top: resolved.top,\n prefix: resolved.prefix,\n include: resolved.include,\n exclude: resolved.exclude,\n configPath: resolved.configPath,\n names: resolved.names,\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\naddSharedOptions(\n program\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')\n .option('--min-size <n>', 'Minimum classes per combination')\n .option('--max-size <n>', 'Maximum classes per combination')\n .option('--top <n>', 'Number of component classes to use')\n .option('--dry-run', 'Preview replacements without writing files')\n .option('--prefix <name>', 'Namespace prefix for generated classes'),\n).action(async (targetPath: string, opts) => {\n try {\n const resolved = withNumericDefaults(\n await resolveCommandOptions('apply', opts, targetPath),\n opts,\n GENERATE_DEFAULTS,\n );\n const output = opts.output ?? resolved.output;\n\n if (!output) {\n console.error(chalk.red('Error: --output is required'));\n process.exit(1);\n }\n\n await applyCommand(targetPath, {\n output,\n minOccurrences: resolved.minOccurrences,\n minSize: resolved.minSize,\n maxSize: resolved.maxSize,\n top: resolved.top,\n prefix: resolved.prefix,\n include: resolved.include,\n exclude: resolved.exclude,\n configPath: resolved.configPath,\n names: resolved.names,\n dryRun: Boolean(resolved.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","export const ANALYZE_DEFAULTS = {\n minOccurrences: 5,\n minSize: 2,\n maxSize: 5,\n top: 10,\n} as const;\n\nexport const GENERATE_DEFAULTS = {\n minOccurrences: 3,\n minSize: 2,\n maxSize: 5,\n top: 10,\n prefix: 'twu-',\n} as const;\n","import { loadCommandOptions } from '../config/loadConfig.js';\nimport type { CliCommand } from '../config/types.js';\nimport type { AnalyzeOptions } from '../parser/types.js';\n\nfunction splitPatterns(value: unknown): string[] | undefined {\n if (typeof value !== 'string' || value.trim().length === 0) {\n return undefined;\n }\n\n return value\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part.length > 0);\n}\n\nfunction optionalNumber(value: unknown): number | undefined {\n if (value === undefined || value === null || value === '') {\n return undefined;\n }\n\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nexport interface RawCliOptions {\n config?: string;\n minOccurrences?: string | number;\n minSize?: string | number;\n maxSize?: string | number;\n top?: string | number;\n prefix?: string;\n dedupeSubsets?: boolean;\n include?: string;\n exclude?: string;\n output?: string;\n format?: string;\n dryRun?: boolean;\n}\n\nfunction cliNumber(\n value: string | number | undefined,\n fallback: number,\n): number {\n const parsed = optionalNumber(value);\n return parsed ?? fallback;\n}\n\n/**\n * Merge config file values with CLI flags (CLI wins).\n */\nexport async function resolveCommandOptions(\n command: CliCommand,\n opts: RawCliOptions,\n targetPath?: string,\n): Promise<\n AnalyzeOptions & { output?: string; dryRun?: boolean; names?: Record<string, string> }\n> {\n const resolved = await loadCommandOptions(\n command,\n {\n configPath: opts.config,\n minOccurrences: optionalNumber(opts.minOccurrences),\n minSize: optionalNumber(opts.minSize),\n maxSize: optionalNumber(opts.maxSize),\n top: optionalNumber(opts.top),\n prefix: opts.prefix,\n dedupeSubsets: opts.dedupeSubsets,\n include: splitPatterns(opts.include),\n exclude: splitPatterns(opts.exclude),\n output: opts.output,\n dryRun: opts.dryRun,\n },\n { targetPath },\n );\n\n return {\n minOccurrences: resolved.minOccurrences,\n minSize: resolved.minSize,\n maxSize: resolved.maxSize,\n top: resolved.top,\n prefix: resolved.prefix,\n dedupeSubsets: resolved.dedupeSubsets,\n include: resolved.include,\n exclude: resolved.exclude,\n configPath: resolved.configPath,\n output: resolved.output,\n names: resolved.names,\n format: opts.format === 'json' ? 'json' : 'console',\n dryRun: opts.dryRun ?? resolved.dryRun,\n };\n}\n\nexport function withNumericDefaults(\n resolved: Awaited<ReturnType<typeof resolveCommandOptions>>,\n opts: RawCliOptions,\n defaults: {\n minOccurrences: number;\n minSize: number;\n maxSize: number;\n top: number;\n prefix?: string;\n },\n) {\n return {\n ...resolved,\n minOccurrences:\n resolved.minOccurrences ??\n cliNumber(opts.minOccurrences, defaults.minOccurrences),\n minSize: resolved.minSize ?? cliNumber(opts.minSize, defaults.minSize),\n maxSize: resolved.maxSize ?? cliNumber(opts.maxSize, defaults.maxSize),\n top: resolved.top ?? cliNumber(opts.top, defaults.top),\n prefix: resolved.prefix ?? opts.prefix ?? defaults.prefix,\n };\n}\n\n/** @deprecated Use resolveCommandOptions */\nexport const resolveAnalyzeOptions = (\n opts: RawCliOptions,\n targetPath?: string,\n) => resolveCommandOptions('analyze', opts, targetPath);\n","import { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nfunction readPackageVersion(): string {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const packageJsonPath = path.join(currentDir, '../../package.json');\n const raw = readFileSync(packageJsonPath, 'utf-8');\n const pkg = JSON.parse(raw) as { version?: string };\n return pkg.version ?? '0.0.0';\n}\n\nexport const CLI_VERSION = readPackageVersion();\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/defaults.ts","../../src/cli/parseOptions.ts","../../src/cli/version.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { analyzeCommand } from '../commands/analyze.js';\nimport { applyCommand } from '../commands/apply.js';\nimport { generateCommand } from '../commands/generate.js';\nimport { initCommand } from '../commands/init.js';\nimport { ANALYZE_DEFAULTS, GENERATE_DEFAULTS } from './defaults.js';\nimport { resolveCommandOptions, withNumericDefaults } from './parseOptions.js';\nimport { CLI_VERSION } from './version.js';\n\nconst program = new Command();\n\nfunction addSharedOptions(command: Command): Command {\n return command\n .option('--config <file>', 'Path to tailwind-unwind config file')\n .option(\n '--include <patterns>',\n 'Comma-separated glob include patterns (e.g. \"src/**/*.tsx\")',\n )\n .option(\n '--exclude <patterns>',\n 'Comma-separated glob exclude patterns (e.g. \"**/*.test.tsx\")',\n )\n .option(\n '--changed [ref]',\n 'Only scan git-changed files (optional ref, default: working tree vs HEAD)',\n );\n}\n\nfunction resolveChangedFlag(opts: { changed?: string | boolean }): boolean | string | undefined {\n if (!process.argv.includes('--changed')) {\n return undefined;\n }\n\n if (typeof opts.changed === 'string' && opts.changed.length > 0) {\n return opts.changed;\n }\n\n return true;\n}\n\nprogram\n .name('tailwind-unwind')\n .description('Analyze Tailwind CSS class usage in React/Next.js projects')\n .version(CLI_VERSION);\n\nprogram\n .command('init')\n .description('Create a starter tailwind-unwind.config.json from project scan')\n .argument('<path>', 'Project directory')\n .option('--output <file>', 'Config output path')\n .option('--force', 'Overwrite existing config file')\n .option('--min-occurrences <n>', 'Minimum occurrences threshold')\n .option('--top <n>', 'Number of patterns to include in names')\n .option('--prefix <name>', 'Namespace prefix for generated classes')\n .action(async (targetPath: string, opts) => {\n try {\n const resolved = withNumericDefaults(\n await resolveCommandOptions('init', opts, targetPath),\n opts,\n ANALYZE_DEFAULTS,\n );\n\n await initCommand(targetPath, {\n output: opts.output ?? resolved.output,\n force: Boolean(opts.force || resolved.force),\n minOccurrences: resolved.minOccurrences,\n top: resolved.top,\n prefix: resolved.prefix ?? GENERATE_DEFAULTS.prefix,\n include: resolved.include,\n exclude: resolved.exclude,\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\naddSharedOptions(\n program\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')\n .option('--min-size <n>', 'Minimum classes per combination')\n .option('--max-size <n>', 'Maximum classes per combination')\n .option('--top <n>', 'Number of top combinations to show')\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 resolved = withNumericDefaults(\n await resolveCommandOptions('analyze', { ...opts, changed: resolveChangedFlag(opts) }, targetPath),\n opts,\n ANALYZE_DEFAULTS,\n );\n\n await analyzeCommand(targetPath, {\n minOccurrences: resolved.minOccurrences,\n minSize: resolved.minSize,\n maxSize: resolved.maxSize,\n top: resolved.top,\n format: resolved.format,\n dedupeSubsets: process.argv.includes('--no-dedupe-subsets')\n ? false\n : (resolved.dedupeSubsets ?? true),\n include: resolved.include,\n exclude: resolved.exclude,\n changed: resolved.changed,\n configPath: resolved.configPath,\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\naddSharedOptions(\n program\n .command('generate')\n .description('Generate @layer components CSS from repeated className sets')\n .argument('[path]', 'Directory to analyze')\n .option('--output <file>', 'Output CSS file path')\n .option('--from-report <file>', 'Generate from analyze JSON report')\n .option('--extractable-only', 'Only generate extractable patterns from analyze')\n .option('--format <type>', 'Output format: console or json', 'console')\n .option('--min-occurrences <n>', 'Minimum occurrences threshold')\n .option('--min-size <n>', 'Minimum classes per combination')\n .option('--max-size <n>', 'Maximum classes per combination')\n .option('--top <n>', 'Number of combinations to generate')\n .option('--prefix <name>', 'Namespace prefix for generated classes'),\n).action(async (targetPath: string | undefined, opts) => {\n try {\n const resolved = withNumericDefaults(\n await resolveCommandOptions('generate', { ...opts, changed: resolveChangedFlag(opts) }, targetPath),\n opts,\n GENERATE_DEFAULTS,\n );\n const output = opts.output ?? resolved.output;\n const scanPath = targetPath ?? '.';\n\n if (!output) {\n console.error(chalk.red('Error: --output is required'));\n process.exit(1);\n }\n\n if (!opts.fromReport && !resolved.fromReport && !targetPath) {\n console.error(chalk.red('Error: <path> is required without --from-report'));\n process.exit(1);\n }\n\n await generateCommand(scanPath, {\n output,\n minOccurrences: resolved.minOccurrences,\n minSize: resolved.minSize,\n maxSize: resolved.maxSize,\n top: resolved.top,\n prefix: resolved.prefix,\n include: resolved.include,\n exclude: resolved.exclude,\n changed: resolved.changed,\n configPath: resolved.configPath,\n names: resolved.names,\n format: resolved.format,\n fromReport: opts.fromReport ?? resolved.fromReport,\n extractableOnly: Boolean(opts.extractableOnly || resolved.extractableOnly),\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\naddSharedOptions(\n program\n .command('apply')\n .description('Replace repeated className strings with generated component classes')\n .argument('<path>', 'Directory to modify')\n .option('--output <file>', 'Output CSS file path')\n .option('--from-report <file>', 'Use component list from analyze JSON report')\n .option('--extractable-only', 'Only apply extractable patterns from analyze')\n .option('--format <type>', 'Output format: console or json', 'console')\n .option('--prettier', 'Format modified files with Prettier when available')\n .option('--min-occurrences <n>', 'Minimum occurrences threshold')\n .option('--min-size <n>', 'Minimum classes per combination')\n .option('--max-size <n>', 'Maximum classes per combination')\n .option('--top <n>', 'Number of component classes to use')\n .option('--dry-run', 'Preview replacements without writing files')\n .option('--prefix <name>', 'Namespace prefix for generated classes'),\n).action(async (targetPath: string, opts) => {\n try {\n const resolved = withNumericDefaults(\n await resolveCommandOptions('apply', { ...opts, changed: resolveChangedFlag(opts) }, targetPath),\n opts,\n GENERATE_DEFAULTS,\n );\n const output = opts.output ?? resolved.output;\n\n if (!output) {\n console.error(chalk.red('Error: --output is required'));\n process.exit(1);\n }\n\n await applyCommand(targetPath, {\n output,\n minOccurrences: resolved.minOccurrences,\n minSize: resolved.minSize,\n maxSize: resolved.maxSize,\n top: resolved.top,\n prefix: resolved.prefix,\n include: resolved.include,\n exclude: resolved.exclude,\n changed: resolved.changed,\n configPath: resolved.configPath,\n names: resolved.names,\n format: resolved.format,\n fromReport: opts.fromReport ?? resolved.fromReport,\n extractableOnly: Boolean(opts.extractableOnly || resolved.extractableOnly),\n dryRun: process.argv.includes('--dry-run')\n ? true\n : Boolean(resolved.dryRun),\n prettier: Boolean(opts.prettier || resolved.prettier),\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","export const ANALYZE_DEFAULTS = {\n minOccurrences: 5,\n minSize: 2,\n maxSize: 5,\n top: 10,\n} as const;\n\nexport const GENERATE_DEFAULTS = {\n minOccurrences: 3,\n minSize: 2,\n maxSize: 5,\n top: 10,\n prefix: 'twu-',\n} as const;\n","import { loadCommandOptions } from '../config/loadConfig.js';\nimport type { CliCommand } from '../config/types.js';\nimport type { AnalyzeOptions } from '../parser/types.js';\n\nfunction splitPatterns(value: unknown): string[] | undefined {\n if (typeof value !== 'string' || value.trim().length === 0) {\n return undefined;\n }\n\n return value\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part.length > 0);\n}\n\nfunction optionalNumber(value: unknown): number | undefined {\n if (value === undefined || value === null || value === '') {\n return undefined;\n }\n\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nexport interface RawCliOptions {\n config?: string;\n minOccurrences?: string | number;\n minSize?: string | number;\n maxSize?: string | number;\n top?: string | number;\n prefix?: string;\n dedupeSubsets?: boolean;\n include?: string;\n exclude?: string;\n output?: string;\n format?: string;\n dryRun?: boolean;\n prettier?: boolean;\n fromReport?: string;\n extractableOnly?: boolean;\n changed?: string | boolean;\n force?: boolean;\n}\n\nfunction cliNumber(\n value: string | number | undefined,\n fallback: number,\n): number {\n const parsed = optionalNumber(value);\n return parsed ?? fallback;\n}\n\n/**\n * Merge config file values with CLI flags (CLI wins).\n */\nexport async function resolveCommandOptions(\n command: CliCommand,\n opts: RawCliOptions,\n targetPath?: string,\n): Promise<\n AnalyzeOptions & { output?: string; dryRun?: boolean; names?: Record<string, string> }\n> {\n const resolved = await loadCommandOptions(\n command,\n {\n configPath: opts.config,\n minOccurrences: optionalNumber(opts.minOccurrences),\n minSize: optionalNumber(opts.minSize),\n maxSize: optionalNumber(opts.maxSize),\n top: optionalNumber(opts.top),\n prefix: opts.prefix,\n dedupeSubsets: opts.dedupeSubsets,\n include: splitPatterns(opts.include),\n exclude: splitPatterns(opts.exclude),\n output: opts.output,\n dryRun: opts.dryRun,\n prettier: opts.prettier,\n fromReport: opts.fromReport,\n extractableOnly: opts.extractableOnly,\n changed: parseChanged(opts.changed),\n force: opts.force,\n },\n { targetPath },\n );\n\n return {\n minOccurrences: resolved.minOccurrences,\n minSize: resolved.minSize,\n maxSize: resolved.maxSize,\n top: resolved.top,\n prefix: resolved.prefix,\n dedupeSubsets: resolved.dedupeSubsets,\n include: resolved.include,\n exclude: resolved.exclude,\n configPath: resolved.configPath,\n output: resolved.output,\n names: resolved.names,\n format: opts.format === 'json' ? 'json' : 'console',\n dryRun: opts.dryRun ?? resolved.dryRun,\n prettier: opts.prettier ?? resolved.prettier,\n fromReport: opts.fromReport ?? resolved.fromReport,\n extractableOnly: opts.extractableOnly ?? resolved.extractableOnly,\n changed: parseChanged(opts.changed) ?? resolved.changed,\n force: opts.force ?? resolved.force,\n };\n}\n\nfunction parseChanged(value: unknown): boolean | string | undefined {\n if (value === undefined || value === null || value === '') {\n return undefined;\n }\n\n if (value === true || value === 'true') {\n return true;\n }\n\n if (typeof value === 'string') {\n return value;\n }\n\n return undefined;\n}\n\nexport function withNumericDefaults(\n resolved: Awaited<ReturnType<typeof resolveCommandOptions>>,\n opts: RawCliOptions,\n defaults: {\n minOccurrences: number;\n minSize: number;\n maxSize: number;\n top: number;\n prefix?: string;\n },\n) {\n return {\n ...resolved,\n minOccurrences:\n resolved.minOccurrences ??\n cliNumber(opts.minOccurrences, defaults.minOccurrences),\n minSize: resolved.minSize ?? cliNumber(opts.minSize, defaults.minSize),\n maxSize: resolved.maxSize ?? cliNumber(opts.maxSize, defaults.maxSize),\n top: resolved.top ?? cliNumber(opts.top, defaults.top),\n prefix: resolved.prefix ?? opts.prefix ?? defaults.prefix,\n };\n}\n\n/** @deprecated Use resolveCommandOptions */\nexport const resolveAnalyzeOptions = (\n opts: RawCliOptions,\n targetPath?: string,\n) => resolveCommandOptions('analyze', opts, targetPath);\n","import { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nfunction readPackageVersion(): string {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const packageJsonPath = path.join(currentDir, '../../package.json');\n const raw = readFileSync(packageJsonPath, 'utf-8');\n const pkg = JSON.parse(raw) as { version?: string };\n return pkg.version ?? '0.0.0';\n}\n\nexport const CLI_VERSION = readPackageVersion();\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { Expression, JSXAttribute, Node } from '@babel/types';
1
+ import { Node, CallExpression, Expression, JSXAttribute } from '@babel/types';
2
2
 
3
- type CliCommand = 'analyze' | 'generate' | 'apply';
3
+ type CliCommand = 'analyze' | 'generate' | 'apply' | 'init';
4
4
  interface CommandConfig {
5
5
  minOccurrences?: number;
6
6
  minSize?: number;
@@ -10,6 +10,9 @@ interface CommandConfig {
10
10
  output?: string;
11
11
  dedupeSubsets?: boolean;
12
12
  dryRun?: boolean;
13
+ prettier?: boolean;
14
+ fromReport?: string;
15
+ extractableOnly?: boolean;
13
16
  }
14
17
  /** Keys are space-separated utility strings; values are base class names (without prefix). */
15
18
  type CustomNamesConfig = Record<string, string>;
@@ -28,6 +31,8 @@ interface TailwindUnwindConfig extends CommandConfig {
28
31
  }
29
32
  interface ResolvedCommandOptions extends TailwindUnwindConfig {
30
33
  configPath?: string;
34
+ changed?: boolean | string;
35
+ force?: boolean;
31
36
  }
32
37
 
33
38
  /**
@@ -99,27 +104,105 @@ interface AnalyzeOptions {
99
104
  configPath?: string;
100
105
  /** Custom base class names keyed by space-separated utility strings */
101
106
  names?: Record<string, string>;
107
+ prettier?: boolean;
108
+ fromReport?: string;
109
+ extractableOnly?: boolean;
110
+ changed?: boolean | string;
111
+ force?: boolean;
102
112
  }
103
113
 
114
+ interface InitOptions extends AnalyzeOptions {
115
+ output?: string;
116
+ force?: boolean;
117
+ }
118
+ interface InitResult {
119
+ configPath: string;
120
+ extractablePatterns: number;
121
+ }
122
+ /**
123
+ * Scan a project and write a starter tailwind-unwind.config.json.
124
+ */
125
+ declare function initCommand(targetPath: string, options?: InitOptions): Promise<InitResult>;
126
+
104
127
  /**
105
128
  * Run the full analyze pipeline: scan → parse → find patterns → report.
106
129
  */
107
130
  declare function analyzeCommand(targetPath: string, options?: AnalyzeOptions): Promise<AnalysisReport>;
108
131
 
109
- interface ApplyOptions extends AnalyzeOptions {
110
- output: string;
111
- dryRun?: boolean;
132
+ interface ClassReplacement {
133
+ filePath: string;
134
+ line?: number;
135
+ from: string;
136
+ to: string;
137
+ partial?: boolean;
112
138
  }
113
- interface ApplyResult {
114
- filesModified: number;
115
- replacementsTotal: number;
116
- outputPath: string;
117
- componentsGenerated: number;
139
+ interface SkippedReplacement {
140
+ filePath: string;
141
+ line?: number;
142
+ reason: string;
143
+ classes: string[];
144
+ }
145
+ interface ReplaceClassNamesResult {
146
+ source: string;
147
+ replacements: ClassReplacement[];
148
+ skipped: SkippedReplacement[];
149
+ changed: boolean;
118
150
  }
119
151
  /**
120
- * Generate component CSS and replace matching className strings in source files.
152
+ * Replace exact matching className/class values with generated component classes.
153
+ * Supports partial replacement inside cn()/clsx() when dynamic args are present.
121
154
  */
122
- declare function applyCommand(targetPath: string, options: ApplyOptions): Promise<ApplyResult>;
155
+ declare function replaceClassNamesInSource(source: string, replacementMap: Map<string, string>, filePath: string): ReplaceClassNamesResult;
156
+
157
+ interface SavingsReport {
158
+ replacementCount: number;
159
+ utilityTokensBefore: number;
160
+ utilityTokensAfter: number;
161
+ tokensSaved: number;
162
+ percentReduction: number;
163
+ }
164
+ /**
165
+ * Estimate utility-token savings from applied replacements.
166
+ * Each replacement collapses N utilities into 1 component class.
167
+ */
168
+ declare function calculateSavings(replacements: ClassReplacement[]): SavingsReport;
169
+
170
+ /**
171
+ * List source files changed relative to a git ref (default: working tree vs HEAD).
172
+ */
173
+ declare function getChangedSourceFiles(rootPath: string, ref?: string): Promise<string[]>;
174
+ declare function isGitRepository(rootPath: string): Promise<boolean>;
175
+ /**
176
+ * Changed source files under `scopePath`, resolved to absolute paths.
177
+ */
178
+ declare function getChangedFilesInScope(scopePath: string, ref?: string): Promise<string[]>;
179
+
180
+ interface GeneratedComponent {
181
+ className: string;
182
+ classes: string[];
183
+ occurrences: number;
184
+ }
185
+ interface CssGeneratorOptions {
186
+ sourcePath: string;
187
+ combinations: ClassCombination[];
188
+ prefix?: string;
189
+ names?: CustomNamesConfig;
190
+ }
191
+ interface CssGeneratorResult {
192
+ css: string;
193
+ components: GeneratedComponent[];
194
+ }
195
+ interface AssignClassNamesOptions {
196
+ prefix?: string;
197
+ names?: CustomNamesConfig;
198
+ }
199
+ /** Assign unique, prefixed component class names. */
200
+ declare function assignComponentClassNames(combinations: ClassCombination[], options?: AssignClassNamesOptions): GeneratedComponent[];
201
+ /**
202
+ * Build a Tailwind CSS file with @layer components and @apply rules
203
+ * for the most frequent class combinations.
204
+ */
205
+ declare function generateComponentCss(options: CssGeneratorOptions): CssGeneratorResult;
123
206
 
124
207
  interface PatternFinderOptions {
125
208
  minOccurrences?: number;
@@ -144,12 +227,71 @@ declare function findRepeatedClassSets(occurrences: ClassNameOccurrence[], optio
144
227
  */
145
228
  declare function calculatePotentialReduction(occurrences: ClassNameOccurrence[], topCombinations: ClassCombination[]): number;
146
229
 
230
+ interface BuildComponentsOptions extends PatternFinderOptions {
231
+ sourcePath: string;
232
+ prefix?: string;
233
+ names?: Record<string, string>;
234
+ }
235
+ interface BuildComponentsResult {
236
+ components: GeneratedComponent[];
237
+ css: string;
238
+ replacementMap: Map<string, string>;
239
+ }
240
+ /** Build component classes, CSS, and a normalized-class → name lookup map. */
241
+ declare function buildComponents(occurrences: ClassNameOccurrence[], options: BuildComponentsOptions): BuildComponentsResult;
242
+ /** Build components from pre-selected combinations (e.g. analyze report). */
243
+ declare function buildComponentsFromCombinations(combinations: ClassCombination[], options: BuildComponentsOptions): BuildComponentsResult;
244
+
245
+ interface GenerateJsonReport {
246
+ command: 'generate';
247
+ outputPath: string;
248
+ componentsGenerated: number;
249
+ components: GeneratedComponent[];
250
+ cssWritten: boolean;
251
+ }
252
+ interface ApplyJsonReport {
253
+ command: 'apply';
254
+ dryRun: boolean;
255
+ outputPath: string;
256
+ filesModified: number;
257
+ replacementsTotal: number;
258
+ componentsGenerated: number;
259
+ components: GeneratedComponent[];
260
+ replacements: ClassReplacement[];
261
+ skipped: SkippedReplacement[];
262
+ savings: SavingsReport;
263
+ }
264
+ declare function printGenerateJsonReport(report: GenerateJsonReport): void;
265
+ declare function printApplyJsonReport(report: ApplyJsonReport): void;
266
+
267
+ interface ApplyOptions extends AnalyzeOptions {
268
+ output: string;
269
+ dryRun?: boolean;
270
+ }
271
+ interface ApplyResult {
272
+ filesModified: number;
273
+ replacementsTotal: number;
274
+ outputPath: string;
275
+ componentsGenerated: number;
276
+ components: Awaited<ReturnType<typeof buildComponents>>['components'];
277
+ replacements: ApplyJsonReport['replacements'];
278
+ skipped: ApplyJsonReport['skipped'];
279
+ prettierFormatted: string[];
280
+ savings: SavingsReport;
281
+ }
282
+ /**
283
+ * Generate component CSS and replace matching className strings in source files.
284
+ */
285
+ declare function applyCommand(targetPath: string, options: ApplyOptions): Promise<ApplyResult>;
286
+
147
287
  interface ScanProjectOptions extends PatternFinderOptions {
148
288
  targetPath: string;
149
289
  include?: string[];
150
290
  exclude?: string[];
151
291
  /** Threshold used to mark combinations as extractable by generate/apply */
152
292
  extractableMinOccurrences?: number;
293
+ /** Scan only git-changed files; string value is the git ref to diff against */
294
+ changed?: boolean | string;
153
295
  }
154
296
  interface ScanProjectResult {
155
297
  resolvedPath: string;
@@ -169,77 +311,54 @@ interface GenerateOptions extends AnalyzeOptions {
169
311
  interface GenerateResult {
170
312
  outputPath: string;
171
313
  componentsGenerated: number;
172
- report: Awaited<ReturnType<typeof scanProject>>['report'];
314
+ components: Awaited<ReturnType<typeof buildComponents>>['components'];
315
+ report: Awaited<ReturnType<typeof scanProject>>['report'] | null;
173
316
  }
174
317
  /**
175
318
  * Analyze a project and write @layer components CSS to the output file.
176
319
  */
177
320
  declare function generateCommand(targetPath: string, options: GenerateOptions): Promise<GenerateResult>;
178
321
 
179
- interface GeneratedComponent {
180
- className: string;
181
- classes: string[];
182
- occurrences: number;
183
- }
184
- interface CssGeneratorOptions {
185
- sourcePath: string;
322
+ interface LoadedAnalyzeReport {
323
+ targetPath: string;
186
324
  combinations: ClassCombination[];
187
- prefix?: string;
188
- names?: CustomNamesConfig;
189
- }
190
- interface CssGeneratorResult {
191
- css: string;
192
- components: GeneratedComponent[];
193
325
  }
194
- interface AssignClassNamesOptions {
195
- prefix?: string;
196
- names?: CustomNamesConfig;
197
- }
198
- /** Assign unique, prefixed component class names. */
199
- declare function assignComponentClassNames(combinations: ClassCombination[], options?: AssignClassNamesOptions): GeneratedComponent[];
200
326
  /**
201
- * Build a Tailwind CSS file with @layer components and @apply rules
202
- * for the most frequent class combinations.
327
+ * Load an analyze JSON report and return extractable combinations for generate.
203
328
  */
204
- declare function generateComponentCss(options: CssGeneratorOptions): CssGeneratorResult;
205
-
206
- interface BuildComponentsOptions extends PatternFinderOptions {
207
- sourcePath: string;
208
- prefix?: string;
209
- names?: Record<string, string>;
210
- }
211
- interface BuildComponentsResult {
212
- components: GeneratedComponent[];
213
- css: string;
214
- replacementMap: Map<string, string>;
215
- }
216
- /** Build component classes, CSS, and a normalized-class → name lookup map. */
217
- declare function buildComponents(occurrences: ClassNameOccurrence[], options: BuildComponentsOptions): BuildComponentsResult;
329
+ declare function loadExtractableCombinations(reportPath: string, options?: {
330
+ extractableOnly?: boolean;
331
+ }): Promise<LoadedAnalyzeReport>;
218
332
 
219
- interface ClassReplacement {
220
- filePath: string;
221
- line?: number;
222
- from: string;
223
- to: string;
224
- partial?: boolean;
225
- }
226
- interface SkippedReplacement {
333
+ interface FormatSourceOptions {
227
334
  filePath: string;
228
- line?: number;
229
- reason: string;
230
- classes: string[];
335
+ cwd?: string;
231
336
  }
232
- interface ReplaceClassNamesResult {
337
+ /**
338
+ * Format source with Prettier when available in the project.
339
+ * Returns original source unchanged if Prettier is not installed.
340
+ */
341
+ declare function formatSource(source: string, options: FormatSourceOptions): Promise<{
233
342
  source: string;
234
- replacements: ClassReplacement[];
235
- skipped: SkippedReplacement[];
236
- changed: boolean;
237
- }
343
+ formatted: boolean;
344
+ }>;
345
+ declare function formatModifiedFiles(files: string[], sources: Map<string, string>, cwd?: string): Promise<{
346
+ formatted: string[];
347
+ skipped: string[];
348
+ }>;
349
+
350
+ /** Variant APIs that define reusable Tailwind class sets. */
351
+ declare const VARIANT_CALLEES: Set<string>;
352
+ declare function isVariantCallee(expression: Expression): boolean;
238
353
  /**
239
- * Replace exact matching className/class values with generated component classes.
240
- * Supports partial replacement inside cn()/clsx() when dynamic args are present.
354
+ * Extract Tailwind tokens from a cva()/tv() definition call.
241
355
  */
242
- declare function replaceClassNamesInSource(source: string, replacementMap: Map<string, string>, filePath: string): ReplaceClassNamesResult;
356
+ declare function extractClassesFromVariantCall(call: CallExpression): string[];
357
+ type VariantRegistry = Map<string, string[]>;
358
+ /**
359
+ * Collect `const x = cva(...)` / `const x = tv(...)` definitions in a file.
360
+ */
361
+ declare function collectVariantRegistry(ast: Node): VariantRegistry;
243
362
 
244
363
  /**
245
364
  * Suggest a short, human-readable component class name from a utility list.
@@ -281,15 +400,11 @@ interface ExtractedClasses {
281
400
  classes: string[];
282
401
  isDynamic: boolean;
283
402
  }
284
- /**
285
- * Recursively pull static Tailwind tokens from JSX className expressions.
286
- * Unknown/dynamic fragments set `isDynamic: true` but may still yield partial classes.
287
- */
288
- declare function extractClassesFromExpression(expression: Expression): ExtractedClasses;
403
+ declare function extractClassesFromExpression(expression: Expression, registry?: VariantRegistry): ExtractedClasses;
289
404
 
290
405
  declare function isClassAttribute(attr: JSXAttribute): boolean;
291
406
  /** Extract Tailwind classes from a className/class JSX attribute. */
292
- declare function extractFromJSXAttribute(attr: JSXAttribute): ClassNameExtraction | null;
407
+ declare function extractFromJSXAttribute(attr: JSXAttribute, registry?: VariantRegistry): ClassNameExtraction | null;
293
408
  declare function parseSourceToAst(source: string): Node;
294
409
 
295
410
  /**
@@ -326,4 +441,4 @@ declare const IGNORED_DIRECTORIES: readonly ["node_modules", ".next", "dist", "b
326
441
  /** fast-glob ignore patterns — match directories at any depth. */
327
442
  declare const IGNORE_PATTERNS: string[];
328
443
 
329
- export { type AnalysisReport, type AnalysisStats, type AnalyzeOptions, type ApplyOptions, type ApplyResult, CLASS_MERGE_CALLEES, type ClassCombination, type ClassNameExtraction, type ClassNameOccurrence, type ClassReplacement, type CliCommand, type CombinationLocation, type CommandConfig, type CssGeneratorOptions, type CssGeneratorResult, type CustomNamesConfig, DEFAULT_CLASS_PREFIX, type GenerateOptions, type GenerateResult, type GeneratedComponent, IGNORED_DIRECTORIES, IGNORE_PATTERNS, type ParseResult, type ReplaceClassNamesResult, type SkippedReplacement, type TailwindUnwindConfig, type TailwindUnwindConfigFile, analyzeCommand, applyCommand, assignComponentClassNames, buildComponents, calculatePotentialReduction, dedupeSubsetCombinations, extractClassesFromExpression, extractFromJSXAttribute, findFrequentPatterns, findRepeatedClassSets, generateCombinations, generateCommand, generateComponentCss, isClassAttribute, isStrictSubset, loadCommandOptions, normalizeClassPrefix, normalizeClasses, normalizeNamesConfig, parseFile, parseSource, parseSourceToAst, printConsoleReport, printJsonReport, replaceClassNamesInSource, scanProject, splitClassString, suggestClassName, validateConfigFile, walkSourceFiles, withClassPrefix };
444
+ export { type AnalysisReport, type AnalysisStats, type AnalyzeOptions, type ApplyOptions, type ApplyResult, CLASS_MERGE_CALLEES, type ClassCombination, type ClassNameExtraction, type ClassNameOccurrence, type ClassReplacement, type CliCommand, type CombinationLocation, type CommandConfig, type CssGeneratorOptions, type CssGeneratorResult, type CustomNamesConfig, DEFAULT_CLASS_PREFIX, type GenerateOptions, type GenerateResult, type GeneratedComponent, IGNORED_DIRECTORIES, IGNORE_PATTERNS, type ParseResult, type ReplaceClassNamesResult, type SkippedReplacement, type TailwindUnwindConfig, type TailwindUnwindConfigFile, VARIANT_CALLEES, analyzeCommand, applyCommand, assignComponentClassNames, buildComponents, buildComponentsFromCombinations, calculatePotentialReduction, calculateSavings, collectVariantRegistry, dedupeSubsetCombinations, extractClassesFromExpression, extractClassesFromVariantCall, extractFromJSXAttribute, findFrequentPatterns, findRepeatedClassSets, formatModifiedFiles, formatSource, generateCombinations, generateCommand, generateComponentCss, getChangedFilesInScope, getChangedSourceFiles, initCommand, isClassAttribute, isGitRepository, isStrictSubset, isVariantCallee, loadCommandOptions, loadExtractableCombinations, normalizeClassPrefix, normalizeClasses, normalizeNamesConfig, parseFile, parseSource, parseSourceToAst, printApplyJsonReport, printConsoleReport, printGenerateJsonReport, printJsonReport, replaceClassNamesInSource, scanProject, splitClassString, suggestClassName, validateConfigFile, walkSourceFiles, withClassPrefix };
package/dist/index.js CHANGED
@@ -3,29 +3,44 @@ import {
3
3
  DEFAULT_CLASS_PREFIX,
4
4
  IGNORED_DIRECTORIES,
5
5
  IGNORE_PATTERNS,
6
+ VARIANT_CALLEES,
6
7
  analyzeCommand,
7
8
  applyCommand,
8
9
  assignComponentClassNames,
9
10
  buildComponents,
11
+ buildComponentsFromCombinations,
10
12
  calculatePotentialReduction,
13
+ calculateSavings,
14
+ collectVariantRegistry,
11
15
  dedupeSubsetCombinations,
12
16
  extractClassesFromExpression,
17
+ extractClassesFromVariantCall,
13
18
  extractFromJSXAttribute,
14
19
  findFrequentPatterns,
15
20
  findRepeatedClassSets,
21
+ formatModifiedFiles,
22
+ formatSource,
16
23
  generateCombinations,
17
24
  generateCommand,
18
25
  generateComponentCss,
26
+ getChangedFilesInScope,
27
+ getChangedSourceFiles,
28
+ initCommand,
19
29
  isClassAttribute,
30
+ isGitRepository,
20
31
  isStrictSubset,
32
+ isVariantCallee,
21
33
  loadCommandOptions,
34
+ loadExtractableCombinations,
22
35
  normalizeClassPrefix,
23
36
  normalizeClasses,
24
37
  normalizeNamesConfig,
25
38
  parseFile,
26
39
  parseSource,
27
40
  parseSourceToAst,
41
+ printApplyJsonReport,
28
42
  printConsoleReport,
43
+ printGenerateJsonReport,
29
44
  printJsonReport,
30
45
  replaceClassNamesInSource,
31
46
  scanProject,
@@ -34,35 +49,50 @@ import {
34
49
  validateConfigFile,
35
50
  walkSourceFiles,
36
51
  withClassPrefix
37
- } from "./chunk-FASYIEVZ.js";
52
+ } from "./chunk-UXXIEFP4.js";
38
53
  export {
39
54
  CLASS_MERGE_CALLEES,
40
55
  DEFAULT_CLASS_PREFIX,
41
56
  IGNORED_DIRECTORIES,
42
57
  IGNORE_PATTERNS,
58
+ VARIANT_CALLEES,
43
59
  analyzeCommand,
44
60
  applyCommand,
45
61
  assignComponentClassNames,
46
62
  buildComponents,
63
+ buildComponentsFromCombinations,
47
64
  calculatePotentialReduction,
65
+ calculateSavings,
66
+ collectVariantRegistry,
48
67
  dedupeSubsetCombinations,
49
68
  extractClassesFromExpression,
69
+ extractClassesFromVariantCall,
50
70
  extractFromJSXAttribute,
51
71
  findFrequentPatterns,
52
72
  findRepeatedClassSets,
73
+ formatModifiedFiles,
74
+ formatSource,
53
75
  generateCombinations,
54
76
  generateCommand,
55
77
  generateComponentCss,
78
+ getChangedFilesInScope,
79
+ getChangedSourceFiles,
80
+ initCommand,
56
81
  isClassAttribute,
82
+ isGitRepository,
57
83
  isStrictSubset,
84
+ isVariantCallee,
58
85
  loadCommandOptions,
86
+ loadExtractableCombinations,
59
87
  normalizeClassPrefix,
60
88
  normalizeClasses,
61
89
  normalizeNamesConfig,
62
90
  parseFile,
63
91
  parseSource,
64
92
  parseSourceToAst,
93
+ printApplyJsonReport,
65
94
  printConsoleReport,
95
+ printGenerateJsonReport,
66
96
  printJsonReport,
67
97
  replaceClassNamesInSource,
68
98
  scanProject,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwind-unwind",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Analyze Tailwind CSS class usage patterns in React/Next.js projects",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,6 +44,14 @@
44
44
  "engines": {
45
45
  "node": ">=18"
46
46
  },
47
+ "peerDependencies": {
48
+ "prettier": ">=3.0.0"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "prettier": {
52
+ "optional": true
53
+ }
54
+ },
47
55
  "dependencies": {
48
56
  "@babel/generator": "^7.27.0",
49
57
  "@babel/parser": "^7.27.0",
@@ -52,7 +60,8 @@
52
60
  "chalk": "^5.4.1",
53
61
  "cli-table3": "^0.6.5",
54
62
  "commander": "^13.1.0",
55
- "fast-glob": "^3.3.3"
63
+ "fast-glob": "^3.3.3",
64
+ "jiti": "^2.4.2"
56
65
  },
57
66
  "devDependencies": {
58
67
  "@types/babel__generator": "^7.6.8",
@@ -60,6 +69,7 @@
60
69
  "@types/node": "^22.15.3",
61
70
  "tsup": "^8.4.0",
62
71
  "typescript": "^5.8.3",
72
+ "prettier": "^3.5.3",
63
73
  "vitest": "^3.1.2"
64
74
  }
65
75
  }
@@ -14,11 +14,14 @@
14
14
  "minOccurrences": 3,
15
15
  "prefix": "twu-",
16
16
  "output": "src/styles/components.css",
17
- "top": 20
17
+ "top": 20,
18
+ "fromReport": "report.json",
19
+ "extractableOnly": true
18
20
  },
19
21
  "apply": {
20
22
  "minOccurrences": 3,
21
23
  "prefix": "twu-",
22
- "output": "src/styles/components.css"
24
+ "output": "src/styles/components.css",
25
+ "prettier": true
23
26
  }
24
27
  }