tailwind-canonical 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1,14 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  import { analyzeFile } from '../core/analyzer.js';
3
3
  import { fixFile } from '../core/fixer.js';
4
+ import { mergeFile } from '../core/merger.js';
4
5
  import { scanFiles } from '../core/scanner.js';
5
6
  const args = process.argv.slice(2);
6
7
  const fix = args.includes('--fix');
8
+ const merge = args.includes('--merge');
7
9
  const targets = args.filter((a) => !a.startsWith('--'));
8
10
  if (targets.length === 0) {
9
- console.error('Usage: tailwind-canonical [--fix] <dir|file> [dir|file...]');
11
+ console.error('Usage: tailwind-canonical [--fix] [--merge] <dir|file> [dir|file...]');
10
12
  process.exit(1);
11
13
  }
14
+ if (merge) {
15
+ try {
16
+ await import('tailwind-merge');
17
+ }
18
+ catch {
19
+ console.error('--merge requires tailwind-merge: pnpm add -D tailwind-merge');
20
+ process.exit(1);
21
+ }
22
+ }
12
23
  let config = {};
13
24
  try {
14
25
  const { default: userConfig } = await import(new URL(`file://${process.cwd()}/tailwind-canonical.config.js`).href);
@@ -18,6 +29,7 @@ catch { }
18
29
  const files = targets.flatMap((t) => scanFiles(t));
19
30
  let totalFindings = 0;
20
31
  let totalFixed = 0;
32
+ let totalMerged = 0;
21
33
  for (const file of files) {
22
34
  if (fix) {
23
35
  const count = fixFile(file, config);
@@ -26,7 +38,14 @@ for (const file of files) {
26
38
  totalFixed += count;
27
39
  }
28
40
  }
29
- else {
41
+ if (merge) {
42
+ const count = await mergeFile(file);
43
+ if (count > 0) {
44
+ console.log(` merged ${file} (${count} conflict${count > 1 ? 's' : ''})`);
45
+ totalMerged += count;
46
+ }
47
+ }
48
+ if (!fix && !merge) {
30
49
  const findings = analyzeFile(file, config);
31
50
  for (const f of findings) {
32
51
  const tag = f.suggestion.isCustomToken ? ' [custom token]' : '';
@@ -35,8 +54,13 @@ for (const file of files) {
35
54
  totalFindings += findings.length;
36
55
  }
37
56
  }
38
- if (fix) {
39
- console.log(`\n✓ Fixed ${totalFixed} occurrence${totalFixed !== 1 ? 's' : ''} across ${files.length} files`);
57
+ if (fix || merge) {
58
+ const parts = [];
59
+ if (fix)
60
+ parts.push(`${totalFixed} replacement${totalFixed !== 1 ? 's' : ''}`);
61
+ if (merge)
62
+ parts.push(`${totalMerged} conflict${totalMerged !== 1 ? 's' : ''} merged`);
63
+ console.log(`\n✓ Fixed ${parts.join(', ')} across ${files.length} file${files.length !== 1 ? 's' : ''}`);
40
64
  }
41
65
  else if (totalFindings > 0) {
42
66
  console.log(`\n✖ Found ${totalFindings} non-canonical class${totalFindings !== 1 ? 'es' : ''}`);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function mergeFile(filePath: string): Promise<number>;
2
+ //# sourceMappingURL=merger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merger.d.ts","sourceRoot":"","sources":["../../src/core/merger.ts"],"names":[],"mappings":"AAEA,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBjE"}
@@ -0,0 +1,20 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ export async function mergeFile(filePath) {
3
+ const { twMerge } = await import('tailwind-merge');
4
+ let content = readFileSync(filePath, 'utf8');
5
+ let count = 0;
6
+ const CLASS_ATTR_REGEX = /className\s*=\s*(?:"([^"]+)"|'([^']+)'|`([^`]+)`)/g;
7
+ content = content.replace(CLASS_ATTR_REGEX, (full, dq, sq, bt) => {
8
+ const raw = dq ?? sq ?? bt ?? '';
9
+ const quote = dq !== undefined ? '"' : sq !== undefined ? "'" : '`';
10
+ const merged = twMerge(raw);
11
+ if (merged === raw)
12
+ return full;
13
+ count++;
14
+ return `className=${quote}${merged}${quote}`;
15
+ });
16
+ if (count > 0)
17
+ writeFileSync(filePath, content, 'utf8');
18
+ return count;
19
+ }
20
+ //# sourceMappingURL=merger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merger.js","sourceRoot":"","sources":["../../src/core/merger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEtD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB;IAC9C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACnD,IAAI,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC7C,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,MAAM,gBAAgB,GAAG,oDAAoD,CAAC;IAE9E,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QAC/D,MAAM,GAAG,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QACpE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAChC,KAAK,EAAE,CAAC;QACR,OAAO,aAAa,KAAK,GAAG,MAAM,GAAG,KAAK,EAAE,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,IAAI,KAAK,GAAG,CAAC;QAAE,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACxD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/core/rules.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AA6CF,MAAM,MAAM,MAAM,GAAG;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAEF,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,MAAW,GAClB,UAAU,GAAG,IAAI,CAyFnB"}
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/core/rules.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAuFF,MAAM,MAAM,MAAM,GAAG;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAMF,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,MAAW,GAClB,UAAU,GAAG,IAAI,CAkKnB"}
@@ -38,17 +38,76 @@ const ROUNDED_MAP = {
38
38
  16: '2xl',
39
39
  24: '3xl',
40
40
  };
41
+ const FRACTION_MAP = {
42
+ '50': '1/2',
43
+ '33.333333': '1/3',
44
+ '33.33': '1/3',
45
+ '66.666667': '2/3',
46
+ '66.67': '2/3',
47
+ '25': '1/4',
48
+ '75': '3/4',
49
+ '20': '1/5',
50
+ '40': '2/5',
51
+ '60': '3/5',
52
+ '80': '4/5',
53
+ '16.666667': '1/6',
54
+ '16.67': '1/6',
55
+ '83.333333': '5/6',
56
+ '83.33': '5/6',
57
+ '8.333333': '1/12',
58
+ '8.33': '1/12',
59
+ '91.666667': '11/12',
60
+ '91.67': '11/12',
61
+ };
62
+ const FRACTION_PREFIXES = [
63
+ 'w',
64
+ 'h',
65
+ 'min-w',
66
+ 'max-w',
67
+ 'min-h',
68
+ 'max-h',
69
+ 'inset',
70
+ 'top',
71
+ 'left',
72
+ 'right',
73
+ 'bottom',
74
+ 'translate-x',
75
+ 'translate-y',
76
+ ];
77
+ const OPACITY_SCALE = new Set([
78
+ 0, 5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80, 90, 95, 100,
79
+ ]);
80
+ function remToPx(rem) {
81
+ return Math.round(rem * 16);
82
+ }
41
83
  export function suggestCanonical(cls, config = {}) {
42
84
  const textTokens = { ...TEXT_SIZE_MAP, ...config.customTextTokens };
43
85
  const spacingTokens = config.customSpacingTokens ?? {};
44
- const textMatch = cls.match(/^(text)-\[(\d+)px\]$/);
45
- if (textMatch) {
46
- const px = parseInt(textMatch[2], 10);
86
+ // text-[Npx]
87
+ const textPxMatch = cls.match(/^text-\[(\d+)px\]$/);
88
+ if (textPxMatch) {
89
+ const px = parseInt(textPxMatch[1], 10);
90
+ const token = textTokens[px];
91
+ if (!token)
92
+ return null;
93
+ return {
94
+ original: cls,
95
+ canonical: `text-${token}`,
96
+ isCustomToken: !BUILT_IN_TEXT[px],
97
+ };
98
+ }
99
+ // text-[N.Nrem]
100
+ const textRemMatch = cls.match(/^text-\[(\d+(?:\.\d+)?)rem\]$/);
101
+ if (textRemMatch) {
102
+ const px = remToPx(parseFloat(textRemMatch[1]));
47
103
  const token = textTokens[px];
48
104
  if (!token)
49
105
  return null;
50
- const isCustomToken = !BUILT_IN_TEXT[px];
51
- return { original: cls, canonical: `text-${token}`, isCustomToken };
106
+ return {
107
+ original: cls,
108
+ canonical: `text-${token}`,
109
+ isCustomToken: !BUILT_IN_TEXT[px],
110
+ };
52
111
  }
53
112
  const spacingPrefixes = [
54
113
  'h',
@@ -85,10 +144,12 @@ export function suggestCanonical(cls, config = {}) {
85
144
  'space-x',
86
145
  'space-y',
87
146
  ];
88
- const spacingMatch = cls.match(new RegExp(`^(${spacingPrefixes.join('|')})-\\[(\\d+)px\\]$`));
89
- if (spacingMatch) {
90
- const prefix = spacingMatch[1];
91
- const px = parseInt(spacingMatch[2], 10);
147
+ const spacingPxRegex = new RegExp(`^(${spacingPrefixes.join('|')})-\\[(\\d+)px\\]$`);
148
+ // spacing-[Npx]
149
+ const spacingPxMatch = cls.match(spacingPxRegex);
150
+ if (spacingPxMatch) {
151
+ const prefix = spacingPxMatch[1];
152
+ const px = parseInt(spacingPxMatch[2], 10);
92
153
  if (spacingTokens[px]) {
93
154
  return {
94
155
  original: cls,
@@ -97,15 +158,65 @@ export function suggestCanonical(cls, config = {}) {
97
158
  };
98
159
  }
99
160
  if (px % 4 === 0) {
100
- const unit = px / 4;
101
161
  return {
102
162
  original: cls,
103
- canonical: `${prefix}-${unit}`,
163
+ canonical: `${prefix}-${px / 4}`,
104
164
  isCustomToken: false,
105
165
  };
106
166
  }
107
167
  return null;
108
168
  }
169
+ // spacing-[N.Nrem]
170
+ const spacingRemRegex = new RegExp(`^(${spacingPrefixes.join('|')})-\\[(\\d+(?:\\.\\d+)?)rem\\]$`);
171
+ const spacingRemMatch = cls.match(spacingRemRegex);
172
+ if (spacingRemMatch) {
173
+ const prefix = spacingRemMatch[1];
174
+ const px = remToPx(parseFloat(spacingRemMatch[2]));
175
+ if (spacingTokens[px]) {
176
+ return {
177
+ original: cls,
178
+ canonical: `${prefix}-${spacingTokens[px]}`,
179
+ isCustomToken: true,
180
+ };
181
+ }
182
+ if (px % 4 === 0) {
183
+ return {
184
+ original: cls,
185
+ canonical: `${prefix}-${px / 4}`,
186
+ isCustomToken: false,
187
+ };
188
+ }
189
+ return null;
190
+ }
191
+ // w/h/inset/etc-[N.N%]
192
+ const fractionPrefixPattern = FRACTION_PREFIXES.join('|');
193
+ const fractionMatch = cls.match(new RegExp(`^(${fractionPrefixPattern})-\\[([\\d.]+)%\\]$`));
194
+ if (fractionMatch) {
195
+ const prefix = fractionMatch[1];
196
+ const pct = fractionMatch[2];
197
+ const fraction = FRACTION_MAP[pct];
198
+ if (!fraction)
199
+ return null;
200
+ return {
201
+ original: cls,
202
+ canonical: `${prefix}-${fraction}`,
203
+ isCustomToken: false,
204
+ };
205
+ }
206
+ // opacity-[N.N] or opacity-[0.N]
207
+ const opacityMatch = cls.match(/^opacity-\[(\d+(?:\.\d+)?)\]$/);
208
+ if (opacityMatch) {
209
+ const raw = parseFloat(opacityMatch[1]);
210
+ const value = raw <= 1 ? Math.round(raw * 100) : Math.round(raw);
211
+ if (!OPACITY_SCALE.has(value))
212
+ return null;
213
+ return {
214
+ original: cls,
215
+ canonical: `opacity-${value}`,
216
+ isCustomToken: false,
217
+ };
218
+ }
219
+ // rounded-[Npx]
109
220
  const roundedMatch = cls.match(/^(rounded|rounded-[a-z]+)-\[(\d+)px\]$/);
110
221
  if (roundedMatch) {
111
222
  const prefix = roundedMatch[1];
@@ -1 +1 @@
1
- {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/core/rules.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/core/rules.ts"],"names":[],"mappings":"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"}
@@ -47,6 +47,33 @@ declare const _default: {
47
47
  }): void;
48
48
  };
49
49
  };
50
+ 'no-conflicting-classes': {
51
+ meta: {
52
+ type: "suggestion";
53
+ fixable: "code";
54
+ schema: never[];
55
+ messages: {
56
+ conflicting: string;
57
+ };
58
+ };
59
+ create(context: RuleContext & {
60
+ options: [];
61
+ }): {
62
+ Literal: (node: {
63
+ value: unknown;
64
+ raw?: string;
65
+ type: string;
66
+ }) => void;
67
+ TemplateLiteral(node: {
68
+ quasis: Array<{
69
+ value: {
70
+ raw: string;
71
+ };
72
+ type: string;
73
+ }>;
74
+ }): void;
75
+ };
76
+ };
50
77
  };
51
78
  configs: {
52
79
  recommended: {
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/eslint/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,kBAAkB,CAAC;AAEjE,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IACnB,MAAM,EAAE,CAAC,UAAU,EAAE;QACnB,IAAI,EAAE,OAAO,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE;YACZ,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;SACvD,KAAK,OAAO,CAAC;KACf,KAAK,IAAI,CAAC;CACZ,CAAC;;;;;;;;;;;;;;;;;;;;;;;4BAqBgB,WAAW;gCAGG;oBAC1B,KAAK,EAAE,OAAO,CAAC;oBACf,GAAG,CAAC,EAAE,MAAM,CAAC;oBACb,IAAI,EAAE,MAAM,CAAC;iBACd;sCAqBuB;oBACpB,MAAM,EAAE,KAAK,CAAC;wBACZ,KAAK,EAAE;4BAAE,GAAG,EAAE,MAAM,CAAA;yBAAE,CAAC;wBACvB,IAAI,EAAE,MAAM,CAAC;qBACd,CAAC,CAAC;iBACJ;;;;;;;;;;;;;AASP,wBAYE"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/eslint/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,kBAAkB,CAAC;AAIjE,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IACnB,MAAM,EAAE,CAAC,UAAU,EAAE;QACnB,IAAI,EAAE,OAAO,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE;YACZ,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;SACvD,KAAK,OAAO,CAAC;KACf,KAAK,IAAI,CAAC;CACZ,CAAC;;;;;;;;;;;;;;;;;;;;;;;4BAqBgB,WAAW;gCAGG;oBAC1B,KAAK,EAAE,OAAO,CAAC;oBACf,GAAG,CAAC,EAAE,MAAM,CAAC;oBACb,IAAI,EAAE,MAAM,CAAC;iBACd;sCAqBuB;oBACpB,MAAM,EAAE,KAAK,CAAC;wBACZ,KAAK,EAAE;4BAAE,GAAG,EAAE,MAAM,CAAA;yBAAE,CAAC;wBACvB,IAAI,EAAE,MAAM,CAAC;qBACd,CAAC,CAAC;iBACJ;;;;;;;;;;;;4BAqBW,WAAW,GAAG;gBAAE,OAAO,EAAE,EAAE,CAAA;aAAE;gCAWf;oBAC1B,KAAK,EAAE,OAAO,CAAC;oBACf,GAAG,CAAC,EAAE,MAAM,CAAC;oBACb,IAAI,EAAE,MAAM,CAAC;iBACd;sCAiBuB;oBACpB,MAAM,EAAE,KAAK,CAAC;wBAAE,KAAK,EAAE;4BAAE,GAAG,EAAE,MAAM,CAAA;yBAAE,CAAC;wBAAC,IAAI,EAAE,MAAM,CAAA;qBAAE,CAAC,CAAC;iBACzD;;;;;;;;;;;;;AASP,wBAaE"}
@@ -1,4 +1,6 @@
1
+ import { createRequire } from 'node:module';
1
2
  import { suggestCanonical } from '../core/rules.js';
3
+ const _require = createRequire(import.meta.url);
2
4
  const noArbitraryCanonical = {
3
5
  meta: {
4
6
  type: 'suggestion',
@@ -49,9 +51,56 @@ const noArbitraryCanonical = {
49
51
  };
50
52
  },
51
53
  };
54
+ const noConflictingClasses = {
55
+ meta: {
56
+ type: 'suggestion',
57
+ fixable: 'code',
58
+ schema: [],
59
+ messages: {
60
+ conflicting: "Conflicting Tailwind classes detected. Use '{{merged}}' instead.",
61
+ },
62
+ },
63
+ create(context) {
64
+ let twMerge = null;
65
+ let peerMissing = false;
66
+ try {
67
+ const mod = _require('tailwind-merge');
68
+ twMerge = mod.twMerge;
69
+ }
70
+ catch {
71
+ peerMissing = true;
72
+ }
73
+ function checkLiteral(node) {
74
+ if (peerMissing || !twMerge)
75
+ return;
76
+ if (typeof node.value !== 'string')
77
+ return;
78
+ const merged = twMerge(node.value);
79
+ if (merged === node.value)
80
+ return;
81
+ context.report({
82
+ node,
83
+ message: `Conflicting Tailwind classes detected. Use '${merged}' instead.`,
84
+ fix(fixer) {
85
+ const quote = node.raw?.startsWith('"') ? '"' : "'";
86
+ return fixer.replaceText(node, `${quote}${merged}${quote}`);
87
+ },
88
+ });
89
+ }
90
+ return {
91
+ Literal: checkLiteral,
92
+ TemplateLiteral(node) {
93
+ for (const quasi of node.quasis) {
94
+ checkLiteral({ value: quasi.value.raw, type: 'Literal' });
95
+ }
96
+ },
97
+ };
98
+ },
99
+ };
52
100
  export default {
53
101
  rules: {
54
102
  'no-arbitrary-canonical': noArbitraryCanonical,
103
+ 'no-conflicting-classes': noConflictingClasses,
55
104
  },
56
105
  configs: {
57
106
  recommended: {
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/eslint/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAajE,MAAM,oBAAoB,GAAG;IAC3B,IAAI,EAAE;QACJ,IAAI,EAAE,YAAqB;QAC3B,OAAO,EAAE,MAAe;QACxB,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACpC,mBAAmB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBACxC;gBACD,oBAAoB,EAAE,KAAK;aAC5B;SACF;QACD,QAAQ,EAAE;YACR,YAAY,EACV,+DAA+D;SAClE;KACF;IACD,MAAM,CAAC,OAAoB;QACzB,MAAM,MAAM,GAAW,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAEhD,SAAS,YAAY,CAAC,IAIrB;YACC,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;gBAAE,OAAO;YAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACxC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;gBAC1B,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBACjD,IAAI,CAAC,UAAU;oBAAE,SAAS;gBAC1B,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI;oBACJ,OAAO,EAAE,wBAAwB,UAAU,CAAC,SAAS,iBAAiB,UAAU,CAAC,QAAQ,GAAG;oBAC5F,GAAG,CAAC,KAAK;wBACP,MAAM,KAAK,GAAG,IAAI,CAAC,KAAe,CAAC;wBACnC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;wBACxD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;wBACpD,OAAO,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC;oBAC9D,CAAC;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO;YACL,OAAO,EAAE,YAAY;YACrB,eAAe,CAAC,IAKf;gBACC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBAChC,YAAY,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe;IACb,KAAK,EAAE;QACL,wBAAwB,EAAE,oBAAoB;KAC/C;IACD,OAAO,EAAE;QACP,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,oBAAoB,CAAC;YAC/B,KAAK,EAAE;gBACL,2CAA2C,EAAE,MAAM;aACpD;SACF;KACF;CACF,CAAC"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/eslint/plugin.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export type { Finding } from './core/analyzer.js';
2
2
  export { analyzeFile } from './core/analyzer.js';
3
3
  export { fixFile } from './core/fixer.js';
4
+ export { mergeFile } from './core/merger.js';
4
5
  export type { Config, Suggestion } from './core/rules.js';
5
6
  export { suggestCanonical } from './core/rules.js';
6
7
  export { scanFiles } from './core/scanner.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { analyzeFile } from './core/analyzer.js';
2
2
  export { fixFile } from './core/fixer.js';
3
+ export { mergeFile } from './core/merger.js';
3
4
  export { suggestCanonical } from './core/rules.js';
4
5
  export { scanFiles } from './core/scanner.js';
5
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwind-canonical",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Lint and auto-fix Tailwind CSS arbitrary values that have canonical equivalents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -51,15 +51,20 @@
51
51
  "@biomejs/biome": "^2.4.16",
52
52
  "@types/node": "^22.0.0",
53
53
  "lefthook": "^2.1.8",
54
+ "tailwind-merge": "^3.6.0",
54
55
  "tsx": "^4.0.0",
55
56
  "typescript": "^6.0.0"
56
57
  },
57
58
  "peerDependencies": {
58
- "eslint": ">=8.0.0"
59
+ "eslint": ">=8.0.0",
60
+ "tailwind-merge": ">=2.0.0"
59
61
  },
60
62
  "peerDependenciesMeta": {
61
63
  "eslint": {
62
64
  "optional": true
65
+ },
66
+ "tailwind-merge": {
67
+ "optional": true
63
68
  }
64
69
  },
65
70
  "scripts": {