tailwind-canonical 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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"}
@@ -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.1",
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": {