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 +15 -8
- package/dist/{linter-BBFc8gnJ.cjs → linter-DQMo7IYe.cjs} +85 -25
- package/dist/linter.cjs +1 -1
- package/dist/linter.d.cts +1 -0
- package/package.json +10 -10
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_linter = require("./linter-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
878
|
+
return node_path.relative(discoveryCwd, absolutePattern);
|
|
858
879
|
};
|
|
859
880
|
const resolvedExclude = exclude.map(resolveFromConfig);
|
|
860
|
-
const gitignorePatterns = readGitignorePatterns(
|
|
881
|
+
const gitignorePatterns = readGitignorePatterns(discoveryCwd);
|
|
861
882
|
const extraIgnore = [...resolvedExclude, ...gitignorePatterns];
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
if (importSource.disableAutoSource)
|
|
865
|
-
|
|
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) ? [
|
|
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 ${
|
|
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 (
|
|
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,
|
|
961
|
-
totalFilesProcessed:
|
|
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-
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tailwind-lint",
|
|
3
|
-
"version": "0.
|
|
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.
|
|
63
|
-
"commander": "^
|
|
62
|
+
"ansis": "^4.3.1",
|
|
63
|
+
"commander": "^15.0.0",
|
|
64
64
|
"postcss": "^8.5.15",
|
|
65
|
-
"tinyglobby": "^0.2.
|
|
65
|
+
"tinyglobby": "^0.2.17",
|
|
66
66
|
"vscode-languageserver-textdocument": "^1.0.12"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@types/node": "^25.9.
|
|
70
|
-
"oxfmt": "^0.
|
|
71
|
-
"oxlint": "^1.
|
|
72
|
-
"tsdown": "^0.22.
|
|
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.
|
|
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.
|
|
80
|
+
"packageManager": "pnpm@11.5.3"
|
|
81
81
|
}
|