@vijayhardaha/dev-config 2.4.0 → 2.4.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/package.json
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// Shared handle to stub prettier.resolveConfig before modules evaluate.
|
|
4
|
+
const mockResolveConfig = vi.hoisted(() => vi.fn());
|
|
5
|
+
|
|
6
|
+
// Intercept prettier so entry-point detection can be simulated without
|
|
7
|
+
// touching real consumer configs on disk.
|
|
8
|
+
vi.mock('prettier', async (importOriginal) => {
|
|
9
|
+
const actual = await importOriginal();
|
|
10
|
+
return { ...actual, resolveConfig: mockResolveConfig };
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('eslint/lib/rules.js with prettier-plugin-tailwindcss', () => {
|
|
14
|
+
it('drops ordering and wrapping rules when prettier-plugin-tailwindcss is active', async () => {
|
|
15
|
+
// Simulate a consumer Prettier config that enables the Tailwind plugin.
|
|
16
|
+
mockResolveConfig.mockResolvedValue({ plugins: ['prettier-plugin-tailwindcss'] });
|
|
17
|
+
|
|
18
|
+
const rules = await import('../rules.js');
|
|
19
|
+
const result = rules.tailwindRules();
|
|
20
|
+
|
|
21
|
+
// Verify formatter-owned concerns are left to Prettier.
|
|
22
|
+
expect(result['better-tailwindcss/enforce-consistent-class-order']).toBeUndefined();
|
|
23
|
+
expect(result['better-tailwindcss/enforce-consistent-line-wrapping']).toBeUndefined();
|
|
24
|
+
|
|
25
|
+
// Verify lint-only concerns stay enabled.
|
|
26
|
+
expect(result['better-tailwindcss/enforce-canonical-classes']).toBe('warn');
|
|
27
|
+
expect(result['better-tailwindcss/no-unnecessary-whitespace']).toBe('warn');
|
|
28
|
+
expect(result['tailwindcss/no-unnecessary-arbitrary-value']).toBe('warn');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('keeps ordering and wrapping rules without the prettier plugin', async () => {
|
|
32
|
+
// Simulate a consumer Prettier config without the Tailwind plugin.
|
|
33
|
+
mockResolveConfig.mockResolvedValue({});
|
|
34
|
+
|
|
35
|
+
vi.resetModules();
|
|
36
|
+
const rules = await import('../rules.js');
|
|
37
|
+
const result = rules.tailwindRules();
|
|
38
|
+
|
|
39
|
+
// Verify all four better-tailwindcss rules remain enabled.
|
|
40
|
+
expect(result['better-tailwindcss/enforce-consistent-class-order']).toBe('warn');
|
|
41
|
+
expect(result['better-tailwindcss/enforce-consistent-line-wrapping']).toEqual(['warn', { printWidth: 120 }]);
|
|
42
|
+
expect(result['better-tailwindcss/enforce-canonical-classes']).toBe('warn');
|
|
43
|
+
expect(result['better-tailwindcss/no-unnecessary-whitespace']).toBe('warn');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('falls back to full rules when prettier config resolution fails', async () => {
|
|
47
|
+
// Simulate prettier being unusable during config evaluation.
|
|
48
|
+
mockResolveConfig.mockRejectedValue(new Error('simulated resolution failure'));
|
|
49
|
+
|
|
50
|
+
vi.resetModules();
|
|
51
|
+
const rules = await import('../rules.js');
|
|
52
|
+
const result = rules.tailwindRules();
|
|
53
|
+
|
|
54
|
+
// Verify ordering and wrapping rules stay enabled on failure.
|
|
55
|
+
expect(result['better-tailwindcss/enforce-consistent-class-order']).toBe('warn');
|
|
56
|
+
expect(result['better-tailwindcss/enforce-consistent-line-wrapping']).toEqual(['warn', { printWidth: 120 }]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('logs an interop notice under DEBUG when the prettier plugin is active', async () => {
|
|
60
|
+
// Enable debug logging and simulate the active Tailwind Prettier plugin.
|
|
61
|
+
process.env.DEBUG = 'eslint';
|
|
62
|
+
mockResolveConfig.mockResolvedValue({ plugins: ['prettier-plugin-tailwindcss'] });
|
|
63
|
+
vi.resetModules();
|
|
64
|
+
const debugCalls = [];
|
|
65
|
+
const debugSpy = vi.spyOn(console, 'debug').mockImplementation((...args) => debugCalls.push(args.join(' ')));
|
|
66
|
+
debugSpy.mockClear();
|
|
67
|
+
|
|
68
|
+
// Re-import so top-level detection runs again under this env.
|
|
69
|
+
await import('../rules.js');
|
|
70
|
+
|
|
71
|
+
// Verify that the interop decision is logged.
|
|
72
|
+
expect(debugCalls.some((message) => message.includes('left to Prettier'))).toBe(true);
|
|
73
|
+
|
|
74
|
+
delete process.env.DEBUG;
|
|
75
|
+
});
|
|
76
|
+
});
|
package/src/eslint/lib/rules.js
CHANGED
|
@@ -40,6 +40,31 @@ try {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Indicates whether the consumer Prettier config enables the Tailwind
|
|
45
|
+
* Prettier plugin. When present, class ordering and line wrapping belong to
|
|
46
|
+
* the formatter, and enforcing them here too would cause circular fixes.
|
|
47
|
+
*
|
|
48
|
+
* @type {boolean}
|
|
49
|
+
*/
|
|
50
|
+
let prettierTailwindPluginDetected = false;
|
|
51
|
+
try {
|
|
52
|
+
const prettierModule = await import('prettier');
|
|
53
|
+
const resolvedPrettierConfig = await prettierModule.resolveConfig(path.join(process.cwd(), 'package.json'));
|
|
54
|
+
prettierTailwindPluginDetected = Boolean(
|
|
55
|
+
Array.isArray(resolvedPrettierConfig?.plugins)
|
|
56
|
+
&& resolvedPrettierConfig.plugins.includes('prettier-plugin-tailwindcss')
|
|
57
|
+
);
|
|
58
|
+
if (prettierTailwindPluginDetected && (process.env.DEBUG?.includes('eslint') || process.env.DEBUG === '*')) {
|
|
59
|
+
console.debug(
|
|
60
|
+
'[ESLint Config] Detected "prettier-plugin-tailwindcss" in the consumer Prettier config. '
|
|
61
|
+
+ 'Class ordering and line wrapping rules are left to Prettier.'
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
// Detection is best-effort; without a resolvable config, lint rules stay active.
|
|
66
|
+
}
|
|
67
|
+
|
|
43
68
|
/**
|
|
44
69
|
* Candidate paths probed (relative to the consumer project root) for the main
|
|
45
70
|
* Tailwind CSS v4 entry stylesheet used for theme resolution.
|
|
@@ -96,7 +121,9 @@ export const tailwindSettings = () => {
|
|
|
96
121
|
/**
|
|
97
122
|
* Creates Tailwind class rules covering canonical class names, whitespace,
|
|
98
123
|
* ordering, line wrapping, and arbitrary value scale replacement. Rules rely
|
|
99
|
-
* on plugins registered separately via `getTailwindCentralPlugins`.
|
|
124
|
+
* on plugins registered separately via `getTailwindCentralPlugins`. When the
|
|
125
|
+
* consumer enables `prettier-plugin-tailwindcss`, ordering and wrapping are
|
|
126
|
+
* omitted so the formatter owns those concerns without circular fixes.
|
|
100
127
|
*
|
|
101
128
|
* @returns {object} Tailwind-related ESLint rules (may be partial when plugins are missing).
|
|
102
129
|
*/
|
|
@@ -112,11 +139,15 @@ export const tailwindRules = () => {
|
|
|
112
139
|
// Collapse redundant whitespace inside class strings.
|
|
113
140
|
'better-tailwindcss/no-unnecessary-whitespace': 'warn',
|
|
114
141
|
|
|
115
|
-
// Enforce consistent class ordering inside class strings.
|
|
116
|
-
|
|
142
|
+
// Enforce consistent class ordering inside class strings. Skipped when
|
|
143
|
+
// prettier-plugin-tailwindcss already sorts classes during formatting.
|
|
144
|
+
...(prettierTailwindPluginDetected ? {} : { 'better-tailwindcss/enforce-consistent-class-order': 'warn' }),
|
|
117
145
|
|
|
118
|
-
// Wrap long class strings into readable multi-line groups.
|
|
119
|
-
|
|
146
|
+
// Wrap long class strings into readable multi-line groups. Skipped when
|
|
147
|
+
// prettier-plugin-tailwindcss already wraps classes during formatting.
|
|
148
|
+
...(prettierTailwindPluginDetected
|
|
149
|
+
? {}
|
|
150
|
+
: { 'better-tailwindcss/enforce-consistent-line-wrapping': ['warn', { printWidth: PRETTIER.BASE.printWidth }] }),
|
|
120
151
|
|
|
121
152
|
// Replace arbitrary values with matching theme scale utilities (e.g. `p-[16px]` to `p-4`).
|
|
122
153
|
...(tailwindCssEslintPlugin && { 'tailwindcss/no-unnecessary-arbitrary-value': 'warn' }),
|