tailwind-lint 0.11.1 → 0.12.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.
package/dist/cli.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const require_linter = require("./linter-BBFc8gnJ.cjs");
2
+ const require_linter = require("./linter-DQMo7IYe.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");
@@ -37,7 +37,7 @@ function toJsonDiagnostic(diagnostic) {
37
37
  source: diagnostic.source
38
38
  };
39
39
  }
40
- function createJsonOutput({ files, totalFilesProcessed, cwd, configPath, autoDiscover, fix, patterns }) {
40
+ function createJsonOutput({ files, totalFilesProcessed, skippedFiles = [], cwd, configPath, autoDiscover, fix, patterns }) {
41
41
  let errors = 0;
42
42
  let warnings = 0;
43
43
  let fixed = 0;
@@ -71,7 +71,8 @@ function createJsonOutput({ files, totalFilesProcessed, cwd, configPath, autoDis
71
71
  fix,
72
72
  patterns
73
73
  },
74
- files: mappedFiles
74
+ files: mappedFiles,
75
+ skippedFiles
75
76
  };
76
77
  }
77
78
  function createJsonErrorOutput({ error, cwd, configPath, autoDiscover, fix, patterns }) {
@@ -92,7 +93,8 @@ function createJsonErrorOutput({ error, cwd, configPath, autoDiscover, fix, patt
92
93
  fix,
93
94
  patterns
94
95
  },
95
- files: []
96
+ files: [],
97
+ skippedFiles: []
96
98
  };
97
99
  }
98
100
  //#endregion
@@ -108,7 +110,8 @@ function resolveOptions(files, options) {
108
110
  let autoDiscover = hasAutoFlag;
109
111
  if (hasConfigFlag && options.config) configPath = node_path.isAbsolute(options.config) ? options.config : node_path.resolve(process.cwd(), options.config);
110
112
  if (hasConfigFlag && options.config && !hasFiles) {
111
- cwd = node_path.dirname(configPath);
113
+ const configDir = node_path.dirname(configPath);
114
+ cwd = require_linter.isCssConfigFile(configPath) ? require_linter.findProjectRoot(configDir) : configDir;
112
115
  patterns = [];
113
116
  autoDiscover = true;
114
117
  }
@@ -250,11 +253,13 @@ ${ansis.default.bold.cyan("Notes:")}
250
253
  }
251
254
  });
252
255
  if (process.stdout.isTTY && !resolved.verbose && !isJsonOutput) process.stdout.write(`\r${" ".repeat(80)}\r`);
256
+ if (results.skippedFiles.length > 0 && !isJsonOutput) for (const skipped of results.skippedFiles) console.log(ansis.default.yellow(`⚠ Skipped ${truncateFilename(skipped)} (exceeds size limit)`));
253
257
  if (results.totalFilesProcessed === 0) {
254
258
  if (isJsonOutput) console.log(JSON.stringify(createJsonOutput({
255
259
  ...resolved,
256
260
  files: [],
257
- totalFilesProcessed: 0
261
+ totalFilesProcessed: 0,
262
+ skippedFiles: results.skippedFiles
258
263
  })));
259
264
  else {
260
265
  console.log();
@@ -266,7 +271,8 @@ ${ansis.default.bold.cyan("Notes:")}
266
271
  if (isJsonOutput) console.log(JSON.stringify(createJsonOutput({
267
272
  ...resolved,
268
273
  files: [],
269
- totalFilesProcessed: results.totalFilesProcessed
274
+ totalFilesProcessed: results.totalFilesProcessed,
275
+ skippedFiles: results.skippedFiles
270
276
  })));
271
277
  else console.log(ansis.default.green.bold("✔ No issues found"));
272
278
  process.exit(0);
@@ -274,7 +280,8 @@ ${ansis.default.bold.cyan("Notes:")}
274
280
  if (isJsonOutput) console.log(JSON.stringify(createJsonOutput({
275
281
  ...resolved,
276
282
  files: results.files,
277
- totalFilesProcessed: results.totalFilesProcessed
283
+ totalFilesProcessed: results.totalFilesProcessed,
284
+ skippedFiles: results.skippedFiles
278
285
  })));
279
286
  else await displayResults(results.files, resolved.fix);
280
287
  const hasErrors = results.files.some((file) => file.diagnostics.some((d) => d.severity === require_linter.SEVERITY.ERROR));
@@ -214,6 +214,13 @@ function fileExists(filePath) {
214
214
  return false;
215
215
  }
216
216
  }
217
+ function getFileSize(filePath) {
218
+ try {
219
+ return node_fs.statSync(filePath).size;
220
+ } catch {
221
+ return 0;
222
+ }
223
+ }
217
224
  function readFileSync(filePath) {
218
225
  if (!filePath || typeof filePath !== "string") throw new TypeError("File path must be a non-empty string");
219
226
  return node_fs.readFileSync(filePath, "utf-8");
@@ -621,7 +628,17 @@ async function loadV4DesignSystem(state, cwd, configPath, verbose = false) {
621
628
  //#region src/utils/config.ts
622
629
  const require$2 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href || __filename);
623
630
  const CONFIG_DISCOVERY_MAX_DEPTH = 8;
631
+ const PROJECT_ROOT_MARKERS = ["package.json", ".git"];
624
632
  const isCssConfigFile = (filePath) => filePath.endsWith(".css");
633
+ function findProjectRoot(startDir) {
634
+ let current = node_path.resolve(startDir);
635
+ while (true) {
636
+ if (PROJECT_ROOT_MARKERS.some((marker) => fileExists(node_path.join(current, marker)))) return current;
637
+ const parent = node_path.dirname(current);
638
+ if (parent === current) return node_path.resolve(startDir);
639
+ current = parent;
640
+ }
641
+ }
625
642
  function syntheticViteCssConfigPath(viteConfigPath) {
626
643
  return node_path.join(node_path.dirname(viteConfigPath), SYNTHETIC_VITE_CSS_CONFIG_NAME);
627
644
  }
@@ -646,17 +663,6 @@ async function findTailwindConfigPath(cwd, configPath) {
646
663
  const resolved = node_path.isAbsolute(configPath) ? configPath : node_path.resolve(cwd, configPath);
647
664
  return fileExists(resolved) ? resolved : null;
648
665
  }
649
- for (const p of V3_CONFIG_PATHS) {
650
- const fullPath = node_path.join(cwd, p);
651
- if (fileExists(fullPath)) return fullPath;
652
- }
653
- const v3Recursive = await (0, tinyglobby.glob)(V3_CONFIG_PATHS.map((p) => `**/${p}`), {
654
- cwd,
655
- absolute: true,
656
- ignore: DEFAULT_IGNORE_PATTERNS,
657
- deep: CONFIG_DISCOVERY_MAX_DEPTH
658
- });
659
- if (v3Recursive.length > 0) return sortByPathDepth(v3Recursive)[0];
660
666
  const v4Paths = V4_CSS_FOLDERS.flatMap((folder) => V4_CSS_NAMES.map((name) => node_path.join(folder, name)));
661
667
  for (const p of v4Paths) {
662
668
  const fullPath = node_path.join(cwd, p);
@@ -665,6 +671,10 @@ async function findTailwindConfigPath(cwd, configPath) {
665
671
  if (TAILWIND_V4_IMPORT_REGEX.test(content)) return fullPath;
666
672
  } catch {}
667
673
  }
674
+ for (const p of V3_CONFIG_PATHS) {
675
+ const fullPath = node_path.join(cwd, p);
676
+ if (fileExists(fullPath)) return fullPath;
677
+ }
668
678
  const cssCandidates = await (0, tinyglobby.glob)("**/*.css", {
669
679
  cwd,
670
680
  absolute: true,
@@ -677,6 +687,13 @@ async function findTailwindConfigPath(cwd, configPath) {
677
687
  if (TAILWIND_V4_IMPORT_REGEX.test(content)) v4Matches.push(candidate);
678
688
  } catch {}
679
689
  if (v4Matches.length > 0) return sortCssCandidates(cwd, v4Matches)[0];
690
+ const v3Recursive = await (0, tinyglobby.glob)(V3_CONFIG_PATHS.map((p) => `**/${p}`), {
691
+ cwd,
692
+ absolute: true,
693
+ ignore: DEFAULT_IGNORE_PATTERNS,
694
+ deep: CONFIG_DISCOVERY_MAX_DEPTH
695
+ });
696
+ if (v3Recursive.length > 0) return sortByPathDepth(v3Recursive)[0];
680
697
  const viteConfigPath = await findTailwindViteConfigPath(cwd);
681
698
  if (viteConfigPath) return syntheticViteCssConfigPath(viteConfigPath);
682
699
  return null;
@@ -803,6 +820,8 @@ async function createState(cwd, configPath, verbose = false) {
803
820
  }
804
821
  //#endregion
805
822
  //#region src/linter.ts
823
+ const SOURCE_FILE_PATTERN = "**/*.{js,jsx,ts,tsx,html,vue,svelte,astro,mdx}";
824
+ const GLOB_PATTERN_REGEX = /[*?[\]{}]/;
806
825
  async function validateDocument(state, filePath, content) {
807
826
  try {
808
827
  if (!state) throw new Error("State is not initialized");
@@ -836,33 +855,41 @@ async function expandPatterns(cwd, patterns, extraIgnore = []) {
836
855
  async function discoverFilesFromConfig(cwd, configPath) {
837
856
  const configFilePath = await findTailwindConfigPath(cwd, configPath);
838
857
  if (!configFilePath) throw new Error("Could not find Tailwind config for auto-discovery.\nUse --config to specify the path, or provide file patterns directly.");
858
+ const discoveryCwd = isCssConfigFile(configFilePath) ? findProjectRoot(node_path.dirname(configFilePath)) : cwd;
839
859
  if (!isCssConfigFile(configFilePath)) {
840
860
  const config = await loadTailwindConfig(configFilePath);
841
861
  if (!config || !config.content) throw new Error("Tailwind config is missing the 'content' property.\nAdd a content array to specify which files to scan:\n content: ['./src/**/*.{js,jsx,ts,tsx}']");
842
862
  const patterns = extractContentPatterns(config);
843
863
  if (patterns.length === 0) throw new Error("No content patterns found in Tailwind config.\nEnsure your config has a content array with file patterns.");
844
- return expandPatterns(cwd, patterns);
864
+ return expandPatterns(discoveryCwd, patterns);
845
865
  }
846
866
  const configDir = node_path.dirname(configFilePath);
847
867
  const cssContent = fileExists(configFilePath) ? readFileSync(configFilePath) : SYNTHETIC_VITE_CSS_CONFIG_CONTENT;
848
868
  const includeConfigFile = (files) => {
849
869
  if (!fileExists(configFilePath)) return files;
850
- const relativeConfigPath = node_path.relative(cwd, configFilePath);
870
+ const relativeConfigPath = node_path.relative(discoveryCwd, configFilePath);
851
871
  return [...new Set([relativeConfigPath, ...files])].sort((a, b) => a.localeCompare(b));
852
872
  };
873
+ const resolveDiscoveredFiles = (files) => discoveryCwd === cwd ? files : files.map((file) => node_path.resolve(discoveryCwd, file)).sort((a, b) => a.localeCompare(b));
853
874
  const { include, exclude } = extractSourcePatterns(cssContent);
854
875
  const importSource = extractImportSourceDirectives(cssContent);
855
876
  const resolveFromConfig = (pattern) => {
856
877
  const absolutePattern = node_path.resolve(configDir, pattern);
857
- return node_path.relative(cwd, absolutePattern);
878
+ return node_path.relative(discoveryCwd, absolutePattern);
858
879
  };
859
880
  const resolvedExclude = exclude.map(resolveFromConfig);
860
- const gitignorePatterns = readGitignorePatterns(cwd);
881
+ const gitignorePatterns = readGitignorePatterns(discoveryCwd);
861
882
  const extraIgnore = [...resolvedExclude, ...gitignorePatterns];
862
- if (include.length > 0) return includeConfigFile(await expandPatterns(cwd, include.map(resolveFromConfig), extraIgnore));
863
- if (importSource.roots.length > 0) return includeConfigFile(await expandPatterns(cwd, importSource.roots.map((root) => resolveFromConfig(node_path.join(root, "**/*.{js,jsx,ts,tsx,html,vue,svelte,astro,mdx}"))), extraIgnore));
864
- if (importSource.disableAutoSource) return includeConfigFile([]);
865
- return includeConfigFile(await expandPatterns(cwd, [DEFAULT_FILE_PATTERN], extraIgnore));
883
+ const explicitSourcePatterns = include.map((source) => resolveFromConfig(normalizeSourcePattern(source)));
884
+ let autoPatterns = [];
885
+ if (!importSource.disableAutoSource) autoPatterns = importSource.roots.length > 0 ? importSource.roots.map((root) => resolveFromConfig(normalizeSourcePattern(root))) : [DEFAULT_FILE_PATTERN];
886
+ const autoFiles = autoPatterns.length > 0 ? await expandPatterns(discoveryCwd, autoPatterns, extraIgnore) : [];
887
+ const explicitFiles = explicitSourcePatterns.length > 0 ? await expandPatterns(discoveryCwd, explicitSourcePatterns, resolvedExclude) : [];
888
+ return resolveDiscoveredFiles(includeConfigFile([...new Set([...autoFiles, ...explicitFiles])].sort((a, b) => a.localeCompare(b))));
889
+ }
890
+ function normalizeSourcePattern(pattern) {
891
+ if (GLOB_PATTERN_REGEX.test(pattern) || node_path.extname(pattern)) return pattern;
892
+ return node_path.join(pattern, SOURCE_FILE_PATTERN);
866
893
  }
867
894
  function extractContentPatterns(config) {
868
895
  if (!config.content) return [];
@@ -947,20 +974,41 @@ async function initializeState(cwd, configPath, verbose = false) {
947
974
  async function lint({ cwd, patterns, configPath, autoDiscover, fix = false, verbose = false, onProgress }) {
948
975
  const state = await initializeState(cwd, configPath, verbose);
949
976
  const discoveredFiles = await discoverFiles(cwd, patterns, configPath, autoDiscover);
950
- const files = state.configPath && isCssConfigFile(state.configPath) && fileExists(state.configPath) ? [...new Set([node_path.relative(cwd, state.configPath), ...discoveredFiles])].sort((a, b) => a.localeCompare(b)) : discoveredFiles;
977
+ const files = uniqueFilesByResolvedPath(cwd, state.configPath && isCssConfigFile(state.configPath) && fileExists(state.configPath) ? [node_path.relative(cwd, state.configPath), ...discoveredFiles] : discoveredFiles);
978
+ const skippedFiles = [];
979
+ const lintableFiles = files.filter((file) => {
980
+ if (getFileSize(node_path.isAbsolute(file) ? file : node_path.resolve(cwd, file)) > 524288) {
981
+ skippedFiles.push(file);
982
+ return false;
983
+ }
984
+ return true;
985
+ });
951
986
  if (verbose) {
952
- console.log(ansis.default.cyan.bold(`→ Discovered ${files.length} file${files.length !== 1 ? "s" : ""} to lint`));
987
+ console.log(ansis.default.cyan.bold(`→ Discovered ${lintableFiles.length} file${lintableFiles.length !== 1 ? "s" : ""} to lint`));
953
988
  console.log();
954
989
  }
955
- if (files.length === 0) return {
990
+ if (lintableFiles.length === 0) return {
956
991
  files: [],
957
- totalFilesProcessed: 0
992
+ totalFilesProcessed: 0,
993
+ skippedFiles
958
994
  };
959
995
  return {
960
- files: (await processFiles(state, cwd, files, fix, onProgress)).filter((result) => result.diagnostics.length > 0 || result.fixed),
961
- totalFilesProcessed: files.length
996
+ files: (await processFiles(state, cwd, lintableFiles, fix, onProgress)).filter((result) => result.diagnostics.length > 0 || result.fixed),
997
+ totalFilesProcessed: lintableFiles.length,
998
+ skippedFiles
962
999
  };
963
1000
  }
1001
+ function uniqueFilesByResolvedPath(cwd, files) {
1002
+ const seen = /* @__PURE__ */ new Set();
1003
+ const unique = [];
1004
+ for (const file of files) {
1005
+ const absolutePath = node_path.isAbsolute(file) ? file : node_path.resolve(cwd, file);
1006
+ if (seen.has(absolutePath)) continue;
1007
+ seen.add(absolutePath);
1008
+ unique.push(file);
1009
+ }
1010
+ return unique.sort((a, b) => a.localeCompare(b));
1011
+ }
964
1012
  //#endregion
965
1013
  Object.defineProperty(exports, "DEFAULT_FILE_PATTERN", {
966
1014
  enumerable: true,
@@ -998,6 +1046,18 @@ Object.defineProperty(exports, "extractSourcePatterns", {
998
1046
  return extractSourcePatterns;
999
1047
  }
1000
1048
  });
1049
+ Object.defineProperty(exports, "findProjectRoot", {
1050
+ enumerable: true,
1051
+ get: function() {
1052
+ return findProjectRoot;
1053
+ }
1054
+ });
1055
+ Object.defineProperty(exports, "isCssConfigFile", {
1056
+ enumerable: true,
1057
+ get: function() {
1058
+ return isCssConfigFile;
1059
+ }
1060
+ });
1001
1061
  Object.defineProperty(exports, "lint", {
1002
1062
  enumerable: true,
1003
1063
  get: function() {
package/dist/linter.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_linter = require("./linter-BBFc8gnJ.cjs");
2
+ const require_linter = require("./linter-DQMo7IYe.cjs");
3
3
  exports.extractImportSourceDirectives = require_linter.extractImportSourceDirectives;
4
4
  exports.extractSourcePatterns = require_linter.extractSourcePatterns;
5
5
  exports.lint = require_linter.lint;
package/dist/linter.d.cts CHANGED
@@ -371,6 +371,7 @@ interface LintOptions {
371
371
  interface LintResult {
372
372
  files: LintFileResult[];
373
373
  totalFilesProcessed: number;
374
+ skippedFiles: string[];
374
375
  }
375
376
  //#endregion
376
377
  //#region src/linter.d.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwind-lint",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
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",
@@ -59,23 +59,23 @@
59
59
  },
60
60
  "dependencies": {
61
61
  "@tailwindcss/language-service": "^0.14.29",
62
- "ansis": "^4.3.0",
63
- "commander": "^14.0.3",
62
+ "ansis": "^4.3.1",
63
+ "commander": "^15.0.0",
64
64
  "postcss": "^8.5.15",
65
- "tinyglobby": "^0.2.16",
65
+ "tinyglobby": "^0.2.17",
66
66
  "vscode-languageserver-textdocument": "^1.0.12"
67
67
  },
68
68
  "devDependencies": {
69
- "@types/node": "^25.9.1",
70
- "oxfmt": "^0.51.0",
71
- "oxlint": "^1.66.0",
72
- "tsdown": "^0.22.0",
69
+ "@types/node": "^25.9.3",
70
+ "oxfmt": "^0.54.0",
71
+ "oxlint": "^1.69.0",
72
+ "tsdown": "^0.22.2",
73
73
  "typescript": "^6.0.3",
74
- "vitest": "^4.1.7"
74
+ "vitest": "^4.1.8"
75
75
  },
76
76
  "engines": {
77
77
  "node": ">=22.0.0",
78
78
  "pnpm": ">=10.0.0"
79
79
  },
80
- "packageManager": "pnpm@11.2.2"
80
+ "packageManager": "pnpm@11.5.3"
81
81
  }