tailwind-lint 0.9.3 → 0.10.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![](https://img.shields.io/npm/v/tailwind-lint)](https://www.npmjs.com/package/tailwind-lint) ![](https://github.com/ph1p/tailwind-lint/actions/workflows/ci.yml/badge.svg) ![](https://github.com/ph1p/tailwind-lint/actions/workflows/release.yml/badge.svg)
4
4
 
5
5
  It just lints your Tailwind project as the IDE will do, just on the command line.
6
- Supports **Tailwind CSS v4** (CSS-based config) and **v3** (JavaScript config, legacy).
6
+ Supports **Tailwind CSS v4** (CSS config files and Vite projects using `@tailwindcss/vite`) and **v3** (JavaScript config, legacy).
7
7
 
8
8
  ## Installation
9
9
 
@@ -51,6 +51,8 @@ tailwind-lint --verbose
51
51
 
52
52
  - Finds CSS config files in common locations: `app.css`, `index.css`, `tailwind.css`, `global.css`, etc.
53
53
  - Searches in project root and subdirectories: `./`, `./src/`, `./src/styles/`, `./app/`, etc.
54
+ - Detects `vite.config.*` files that use `@tailwindcss/vite`
55
+ - Prefers real CSS configs when present, so custom `@theme` tokens and `@source` directives are available to diagnostics
54
56
  - Uses file patterns from `@source` directives if present
55
57
  - Falls back to default pattern: `./**/*.{js,jsx,ts,tsx,html,vue,svelte,astro,mdx}`
56
58
  - **Note:** When CSS config is in a subdirectory (e.g., `src/styles/global.css`), files are discovered from the project root
@@ -85,6 +87,9 @@ tailwind-lint "**/*.vue" --fix
85
87
  # Lint with a specific CSS config (v4)
86
88
  tailwind-lint --config ./styles/app.css
87
89
 
90
+ # Lint a Vite-based Tailwind v4 project
91
+ tailwind-lint --auto
92
+
88
93
  # Machine-readable output for LLMs/agents
89
94
  tailwind-lint --auto --format json
90
95
  ```
@@ -129,6 +134,19 @@ Create a CSS config file (`app.css`, `index.css`, or `tailwind.css`):
129
134
 
130
135
  The CLI auto-detects configs at: `app.css`, `src/app.css`, `index.css`, `src/index.css`, `tailwind.css`, `src/tailwind.css`
131
136
 
137
+ You can also use a Vite-based Tailwind v4 project with `@tailwindcss/vite` and no standalone Tailwind CSS config file:
138
+
139
+ ```ts
140
+ import tailwindcss from "@tailwindcss/vite";
141
+ import { defineConfig } from "vite";
142
+
143
+ export default defineConfig({
144
+ plugins: [tailwindcss()],
145
+ });
146
+ ```
147
+
148
+ If a real CSS config is also present, the CLI uses that file so custom `@theme` tokens and `@source` directives are available to diagnostics.
149
+
132
150
  ### Tailwind CSS v3 (Legacy)
133
151
 
134
152
  Create a JavaScript config file (`tailwind.config.js`):
@@ -165,6 +183,7 @@ Files are written atomically with multiple iterations to ensure all fixes are ap
165
183
  - Recommended Variant Order - Suggests preferred ordering of variants
166
184
  - Blocklisted Classes - Detects usage of blocklisted classes
167
185
  - Autofix - Automatically fix issues with `--fix` flag
186
+ - Quoted class string extraction - Lints class-like strings in common helper patterns, not only direct `class` and `className` attributes
168
187
 
169
188
  **v4-Specific:**
170
189
 
package/dist/cli.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const require_linter = require("./linter-DTFnITa5.cjs");
2
+ const require_linter = require("./linter-CQy2hg_B.cjs");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_linter.__toESM(node_path, 1);
5
5
  let ansis = require("ansis");
@@ -80,8 +80,19 @@ const V4_CSS_FOLDERS = [
80
80
  "./style/",
81
81
  "./styles/",
82
82
  "./assets/css/",
83
- "./resources/css/"
83
+ "./resources/css/",
84
+ "./.ladle/"
84
85
  ];
86
+ const VITE_CONFIG_PATHS = [
87
+ "vite.config.js",
88
+ "vite.config.mjs",
89
+ "vite.config.cjs",
90
+ "vite.config.ts",
91
+ "vite.config.mts",
92
+ "vite.config.cts"
93
+ ];
94
+ const SYNTHETIC_VITE_CSS_CONFIG_NAME = ".tailwind-lint.vite.css";
95
+ const SYNTHETIC_VITE_CSS_CONFIG_CONTENT = "@import \"tailwindcss\";";
85
96
  const LANGUAGE_MAP = {
86
97
  ".astro": "astro",
87
98
  ".css": "css",
@@ -109,6 +120,7 @@ const SEVERITY = {
109
120
  WARNING: 2
110
121
  };
111
122
  const TAILWIND_V4_IMPORT_REGEX = /@import\s+["']tailwindcss(?:["'\s/]|$)/;
123
+ const TAILWIND_VITE_PLUGIN_REGEX = /(?:from\s+["']@tailwindcss\/vite["']|require\(\s*["']@tailwindcss\/vite["']\s*\)|import\(\s*["']@tailwindcss\/vite["']\s*\))/;
112
124
  function getLanguageId(filePath) {
113
125
  return LANGUAGE_MAP[filePath.substring(filePath.lastIndexOf(".")).toLowerCase()] || "html";
114
126
  }
@@ -238,7 +250,7 @@ function createEditorState(cwd) {
238
250
  ] },
239
251
  experimental: {
240
252
  configFile: null,
241
- classRegex: []
253
+ classRegex: ["[\"'`]([^\"'`]*\\b(?:bg|text|border|font|tracking|rounded|px|py|p|m|flex|grid|items|justify|gap|whitespace|overflow|align|leading|uppercase|lowercase|capitalize|shadow|transition|hover:|focus:)[^\"'`]*)[\"'`]"]
242
254
  },
243
255
  lint: {
244
256
  cssConflict: "warning",
@@ -395,7 +407,7 @@ async function loadV4DesignSystem(state, cwd, configPath, verbose = false) {
395
407
  let cssContent;
396
408
  const basePath = node_path.dirname(configPath);
397
409
  if (fileExists(configPath)) cssContent = readFileSync(configPath);
398
- else cssContent = "@import \"tailwindcss\";";
410
+ else cssContent = SYNTHETIC_VITE_CSS_CONFIG_CONTENT;
399
411
  const loadDesignSystem = tailwindcss.__unstable__loadDesignSystem;
400
412
  const designSystem = await loadDesignSystem(cssContent, {
401
413
  base: basePath,
@@ -465,6 +477,9 @@ async function loadV4DesignSystem(state, cwd, configPath, verbose = false) {
465
477
  const require$2 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href || __filename);
466
478
  const CONFIG_DISCOVERY_MAX_DEPTH = 8;
467
479
  const isCssConfigFile = (filePath) => filePath.endsWith(".css");
480
+ function syntheticViteCssConfigPath(viteConfigPath) {
481
+ return node_path.join(node_path.dirname(viteConfigPath), SYNTHETIC_VITE_CSS_CONFIG_NAME);
482
+ }
468
483
  async function loadTailwindConfig(configPath) {
469
484
  if (isCssConfigFile(configPath)) return {};
470
485
  if (!node_path.isAbsolute(configPath)) throw new Error(`Config path must be absolute for security reasons: ${configPath}`);
@@ -517,8 +532,32 @@ async function findTailwindConfigPath(cwd, configPath) {
517
532
  if (TAILWIND_V4_IMPORT_REGEX.test(content)) v4Matches.push(candidate);
518
533
  } catch {}
519
534
  if (v4Matches.length > 0) return sortCssCandidates(cwd, v4Matches)[0];
535
+ const viteConfigPath = await findTailwindViteConfigPath(cwd);
536
+ if (viteConfigPath) return syntheticViteCssConfigPath(viteConfigPath);
520
537
  return null;
521
538
  }
539
+ async function findTailwindViteConfigPath(cwd) {
540
+ for (const p of VITE_CONFIG_PATHS) {
541
+ const fullPath = node_path.join(cwd, p);
542
+ if (isTailwindViteConfig(fullPath)) return fullPath;
543
+ }
544
+ const matches = (await (0, tinyglobby.glob)(VITE_CONFIG_PATHS.map((p) => `**/${p}`), {
545
+ cwd,
546
+ absolute: true,
547
+ ignore: DEFAULT_IGNORE_PATTERNS,
548
+ deep: CONFIG_DISCOVERY_MAX_DEPTH
549
+ })).filter(isTailwindViteConfig);
550
+ if (matches.length === 0) return null;
551
+ return sortByPathDepth(matches)[0];
552
+ }
553
+ function isTailwindViteConfig(filePath) {
554
+ if (!fileExists(filePath)) return false;
555
+ try {
556
+ return TAILWIND_VITE_PLUGIN_REGEX.test(readFileSync(filePath));
557
+ } catch {
558
+ return false;
559
+ }
560
+ }
522
561
  function sortByPathDepth(paths) {
523
562
  return [...paths].sort((a, b) => {
524
563
  const depthA = splitDepth(a);
@@ -576,7 +615,7 @@ function resolveTailwindPath(cwd, configDir) {
576
615
  }
577
616
  async function createState(cwd, configPath, verbose = false) {
578
617
  const resolvedConfigPath = await findTailwindConfigPath(cwd, configPath);
579
- if (!resolvedConfigPath) throw new Error("Could not find Tailwind config file. Expected one of:\n • Tailwind v4 (CSS): app.css, index.css, tailwind.css in project root or src/\n • Tailwind v3 (JS): tailwind.config.js, tailwind.config.ts\nRun 'npx tailwindcss init' to create a config file.");
618
+ if (!resolvedConfigPath) throw new Error("Could not find Tailwind config file. Expected one of:\n • Tailwind v4 (CSS): app.css, index.css, tailwind.css in project root or src/\n • Tailwind v4 (Vite): vite.config.* with @tailwindcss/vite\n • Tailwind v3 (JS): tailwind.config.js, tailwind.config.ts\nRun 'npx tailwindcss init' to create a config file.");
580
619
  const isCssConfig = isCssConfigFile(resolvedConfigPath);
581
620
  const tailwindcss = require$1(resolveTailwindPath(cwd, node_path.dirname(resolvedConfigPath)));
582
621
  const version = getTailwindVersion(cwd);
@@ -660,7 +699,7 @@ async function discoverFilesFromConfig(cwd, configPath) {
660
699
  return expandPatterns(cwd, patterns);
661
700
  }
662
701
  const configDir = node_path.dirname(configFilePath);
663
- const cssContent = readFileSync(configFilePath);
702
+ const cssContent = fileExists(configFilePath) ? readFileSync(configFilePath) : SYNTHETIC_VITE_CSS_CONFIG_CONTENT;
664
703
  const { include, exclude } = extractSourcePatterns(cssContent);
665
704
  const importSource = extractImportSourceDirectives(cssContent);
666
705
  const resolveFromConfig = (pattern) => {
package/dist/linter.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_linter = require("./linter-DTFnITa5.cjs");
2
+ const require_linter = require("./linter-CQy2hg_B.cjs");
3
3
  exports.extractImportSourceDirectives = require_linter.extractImportSourceDirectives;
4
4
  exports.extractSourcePatterns = require_linter.extractSourcePatterns;
5
5
  exports.lint = require_linter.lint;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwind-lint",
3
- "version": "0.9.3",
3
+ "version": "0.10.1",
4
4
  "description": "A command-line tool that uses the Tailwind CSS IntelliSense plugin to show linting suggestions for your Tailwind CSS classes",
5
5
  "keywords": [
6
6
  "cli",
@@ -61,21 +61,21 @@
61
61
  "@tailwindcss/language-service": "^0.14.29",
62
62
  "ansis": "^4.2.0",
63
63
  "commander": "^14.0.3",
64
- "postcss": "^8.5.9",
64
+ "postcss": "^8.5.10",
65
65
  "tinyglobby": "^0.2.16",
66
66
  "vscode-languageserver-textdocument": "^1.0.12"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@types/node": "^25.6.0",
70
- "oxfmt": "^0.45.0",
71
- "oxlint": "^1.60.0",
72
- "tsdown": "^0.21.8",
73
- "typescript": "^6.0.2",
74
- "vitest": "^4.1.4"
70
+ "oxfmt": "^0.46.0",
71
+ "oxlint": "^1.61.0",
72
+ "tsdown": "^0.21.10",
73
+ "typescript": "^6.0.3",
74
+ "vitest": "^4.1.5"
75
75
  },
76
76
  "engines": {
77
77
  "node": ">=22.0.0",
78
78
  "pnpm": ">=10.0.0"
79
79
  },
80
- "packageManager": "pnpm@10.33.0"
80
+ "packageManager": "pnpm@10.33.2"
81
81
  }