tailwind-lint 0.10.1 → 0.11.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/README.md +52 -1
- package/dist/cli.cjs +37 -13
- package/dist/{linter-CQy2hg_B.cjs → linter-BygZ0yGf.cjs} +190 -40
- package/dist/linter.cjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ tailwind-lint --verbose
|
|
|
65
65
|
### Options
|
|
66
66
|
|
|
67
67
|
- `-c, --config <path>` - Path to Tailwind config file (default: auto-discover)
|
|
68
|
-
- `-a, --auto` - Auto-discover files from
|
|
68
|
+
- `-a, --auto` - Auto-discover files from the resolved Tailwind config or CSS `@source` directives
|
|
69
69
|
- `--fix` - Automatically fix problems that can be fixed
|
|
70
70
|
- `--format <text|json>` - Output format (`text` default, `json` for machine-readable output)
|
|
71
71
|
- `-v, --verbose` - Enable verbose logging for debugging
|
|
@@ -81,6 +81,9 @@ tailwind-lint "src/**/*.{js,jsx,ts,tsx}"
|
|
|
81
81
|
# Specify a custom config location
|
|
82
82
|
tailwind-lint --config ./config/tailwind.config.js
|
|
83
83
|
|
|
84
|
+
# Scan files based on an explicit config
|
|
85
|
+
tailwind-lint --config ./src/app.css
|
|
86
|
+
|
|
84
87
|
# Lint and fix specific file types
|
|
85
88
|
tailwind-lint "**/*.vue" --fix
|
|
86
89
|
|
|
@@ -109,6 +112,8 @@ The JSON payload includes:
|
|
|
109
112
|
- `config` - resolved runtime values (`cwd`, `configPath`, `autoDiscover`, `fix`, `patterns`)
|
|
110
113
|
- `files[]` - per-file diagnostics with 1-based `line`/`column` ranges
|
|
111
114
|
|
|
115
|
+
On JSON errors, the CLI keeps the same top-level shape and adds `error` so agent integrations do not need a separate parser for failure cases.
|
|
116
|
+
|
|
112
117
|
Typical agent flow:
|
|
113
118
|
|
|
114
119
|
1. Run `tailwind-lint --auto --format json`.
|
|
@@ -147,6 +152,52 @@ export default defineConfig({
|
|
|
147
152
|
|
|
148
153
|
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
154
|
|
|
155
|
+
### Workspace Settings
|
|
156
|
+
|
|
157
|
+
The CLI also reads Tailwind workspace settings from common editor config files so extraction behavior can match your editor more closely.
|
|
158
|
+
|
|
159
|
+
Supported locations:
|
|
160
|
+
|
|
161
|
+
- `.zed/settings.json`
|
|
162
|
+
- `.vscode/settings.json`
|
|
163
|
+
|
|
164
|
+
Supported settings:
|
|
165
|
+
|
|
166
|
+
- `classFunctions`
|
|
167
|
+
- `experimental.classRegex`
|
|
168
|
+
- `classAttributes`
|
|
169
|
+
- `includeLanguages`
|
|
170
|
+
- `files.exclude`
|
|
171
|
+
- `lint`
|
|
172
|
+
|
|
173
|
+
Zed format:
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"lsp": {
|
|
178
|
+
"tailwindcss-language-server": {
|
|
179
|
+
"settings": {
|
|
180
|
+
"classFunctions": ["cva", "cx"],
|
|
181
|
+
"experimental": {
|
|
182
|
+
"classRegex": ["tw=\"([^\"]*)\""]
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
VS Code format:
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"tailwindCSS.classFunctions": ["cva", "cx"],
|
|
195
|
+
"tailwindCSS.experimental": {
|
|
196
|
+
"classRegex": ["tw=\"([^\"]*)\""]
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
150
201
|
### Tailwind CSS v3 (Legacy)
|
|
151
202
|
|
|
152
203
|
Create a JavaScript config file (`tailwind.config.js`):
|
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-BygZ0yGf.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");
|
|
@@ -74,6 +74,27 @@ function createJsonOutput({ files, totalFilesProcessed, cwd, configPath, autoDis
|
|
|
74
74
|
files: mappedFiles
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
+
function createJsonErrorOutput({ error, cwd, configPath, autoDiscover, fix, patterns }) {
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
error,
|
|
81
|
+
summary: {
|
|
82
|
+
errors: 0,
|
|
83
|
+
warnings: 0,
|
|
84
|
+
fixed: 0,
|
|
85
|
+
filesWithIssues: 0,
|
|
86
|
+
totalFilesProcessed: 0
|
|
87
|
+
},
|
|
88
|
+
config: {
|
|
89
|
+
cwd,
|
|
90
|
+
configPath: configPath || null,
|
|
91
|
+
autoDiscover,
|
|
92
|
+
fix,
|
|
93
|
+
patterns
|
|
94
|
+
},
|
|
95
|
+
files: []
|
|
96
|
+
};
|
|
97
|
+
}
|
|
77
98
|
//#endregion
|
|
78
99
|
//#region src/cli.ts
|
|
79
100
|
const MAX_FILENAME_DISPLAY_LENGTH = 50;
|
|
@@ -84,13 +105,13 @@ function resolveOptions(files, options) {
|
|
|
84
105
|
let cwd = process.cwd();
|
|
85
106
|
let configPath = options.config;
|
|
86
107
|
let patterns = files;
|
|
108
|
+
let autoDiscover = hasAutoFlag;
|
|
109
|
+
if (hasConfigFlag && options.config) configPath = node_path.isAbsolute(options.config) ? options.config : node_path.resolve(process.cwd(), options.config);
|
|
87
110
|
if (hasConfigFlag && options.config && !hasFiles) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
patterns = [require_linter.DEFAULT_FILE_PATTERN];
|
|
111
|
+
cwd = node_path.dirname(configPath);
|
|
112
|
+
patterns = [];
|
|
113
|
+
autoDiscover = true;
|
|
92
114
|
}
|
|
93
|
-
const autoDiscover = hasAutoFlag;
|
|
94
115
|
return {
|
|
95
116
|
cwd,
|
|
96
117
|
configPath,
|
|
@@ -186,19 +207,22 @@ program.configureHelp({ formatHelp: (cmd, helper) => {
|
|
|
186
207
|
}
|
|
187
208
|
return output;
|
|
188
209
|
} });
|
|
189
|
-
program.name("tailwind-lint").description("A CLI tool for linting Tailwind CSS class usage").version(getVersion()).argument("[files...]", "File patterns to lint (e.g., \"src/**/*.{js,jsx,ts,tsx}\")").option("-c, --config <path>", "Path to Tailwind config file (default: auto-discover)").option("-a, --auto", "Auto-discover files from Tailwind config
|
|
210
|
+
program.name("tailwind-lint").description("A CLI tool for linting Tailwind CSS class usage").version(getVersion()).argument("[files...]", "File patterns to lint (e.g., \"src/**/*.{js,jsx,ts,tsx}\")").option("-c, --config <path>", "Path to Tailwind config file (default: auto-discover)").option("-a, --auto", "Auto-discover files from Tailwind config or CSS @source patterns").option("--fix", "Automatically fix problems that can be fixed").option("-v, --verbose", "Enable verbose logging for debugging").option("--format <format>", "Output format: text or json", "text").addHelpText("after", `
|
|
190
211
|
${ansis.default.bold.cyan("Examples:")}
|
|
191
212
|
${ansis.default.dim("$")} tailwind-lint
|
|
192
213
|
${ansis.default.dim("$")} tailwind-lint ${ansis.default.green("\"src/**/*.{js,jsx,ts,tsx}\"")}
|
|
193
214
|
${ansis.default.dim("$")} tailwind-lint ${ansis.default.yellow("--config")} ${ansis.default.green("./tailwind.config.js")}
|
|
215
|
+
${ansis.default.dim("$")} tailwind-lint ${ansis.default.yellow("--config")} ${ansis.default.green("./src/app.css")}
|
|
194
216
|
${ansis.default.dim("$")} tailwind-lint ${ansis.default.green("\"src/**/*.tsx\"")} ${ansis.default.yellow("--fix")}
|
|
195
217
|
${ansis.default.dim("$")} tailwind-lint ${ansis.default.green("\"**/*.vue\"")}
|
|
196
218
|
|
|
197
219
|
${ansis.default.bold.cyan("Notes:")}
|
|
198
220
|
${ansis.default.dim("•")} Running without arguments auto-discovers config and files
|
|
221
|
+
${ansis.default.dim("•")} Using ${ansis.default.yellow("--config")} without files scans based on that config
|
|
199
222
|
${ansis.default.dim("•")} For v3: uses content patterns from tailwind.config.js
|
|
200
|
-
${ansis.default.dim("•")} For v4: uses @source patterns from CSS config, or default pattern
|
|
201
|
-
${ansis.default.dim("•")} Default pattern: ${ansis.default.dim(
|
|
223
|
+
${ansis.default.dim("•")} For v4: uses @source patterns from CSS config, Vite detection, or default pattern
|
|
224
|
+
${ansis.default.dim("•")} Default pattern: ${ansis.default.dim(require_linter.DEFAULT_FILE_PATTERN)}
|
|
225
|
+
${ansis.default.dim("•")} Reads Tailwind workspace settings from ${ansis.default.dim(".zed/settings.json")} and ${ansis.default.dim(".vscode/settings.json")}
|
|
202
226
|
${ansis.default.dim("•")} Use ${ansis.default.yellow("--fix")} to automatically resolve fixable issues
|
|
203
227
|
`).action(async (files, options) => {
|
|
204
228
|
const hasConfigFlag = !!options.config;
|
|
@@ -257,10 +281,10 @@ ${ansis.default.bold.cyan("Notes:")}
|
|
|
257
281
|
process.exit(hasErrors ? 1 : 0);
|
|
258
282
|
} catch (error) {
|
|
259
283
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
260
|
-
if (isJsonOutput) console.log(JSON.stringify({
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}));
|
|
284
|
+
if (isJsonOutput) console.log(JSON.stringify(createJsonErrorOutput({
|
|
285
|
+
error: errorMessage,
|
|
286
|
+
...resolved
|
|
287
|
+
})));
|
|
264
288
|
else console.error(ansis.default.red("✖ Error:"), errorMessage);
|
|
265
289
|
if (resolved.verbose && error instanceof Error && !isJsonOutput) {
|
|
266
290
|
console.error(ansis.default.dim("\nStack trace:"));
|
|
@@ -80,8 +80,7 @@ const V4_CSS_FOLDERS = [
|
|
|
80
80
|
"./style/",
|
|
81
81
|
"./styles/",
|
|
82
82
|
"./assets/css/",
|
|
83
|
-
"./resources/css/"
|
|
84
|
-
"./.ladle/"
|
|
83
|
+
"./resources/css/"
|
|
85
84
|
];
|
|
86
85
|
const VITE_CONFIG_PATHS = [
|
|
87
86
|
"vite.config.js",
|
|
@@ -207,6 +206,182 @@ async function applyCodeActions(state, filePath, content, diagnostics) {
|
|
|
207
206
|
}
|
|
208
207
|
}
|
|
209
208
|
//#endregion
|
|
209
|
+
//#region src/utils/fs.ts
|
|
210
|
+
function fileExists(filePath) {
|
|
211
|
+
try {
|
|
212
|
+
return node_fs.existsSync(filePath);
|
|
213
|
+
} catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function readFileSync(filePath) {
|
|
218
|
+
if (!filePath || typeof filePath !== "string") throw new TypeError("File path must be a non-empty string");
|
|
219
|
+
return node_fs.readFileSync(filePath, "utf-8");
|
|
220
|
+
}
|
|
221
|
+
function writeFileSync(filePath, content) {
|
|
222
|
+
if (!filePath || typeof filePath !== "string") throw new TypeError("File path must be a non-empty string");
|
|
223
|
+
if (typeof content !== "string") throw new TypeError("Content must be a string");
|
|
224
|
+
node_fs.writeFileSync(filePath, content, "utf-8");
|
|
225
|
+
}
|
|
226
|
+
function readGitignorePatterns(cwd) {
|
|
227
|
+
const gitignorePath = node_path.join(cwd, ".gitignore");
|
|
228
|
+
if (!fileExists(gitignorePath)) return [];
|
|
229
|
+
try {
|
|
230
|
+
return node_fs.readFileSync(gitignorePath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!")).map((pattern) => {
|
|
231
|
+
const cleaned = pattern.replace(/\/+$/, "");
|
|
232
|
+
if (cleaned.includes("/") || cleaned.includes("*")) return cleaned.endsWith("/**") ? cleaned : `${cleaned}/**`;
|
|
233
|
+
return `**/${cleaned}/**`;
|
|
234
|
+
});
|
|
235
|
+
} catch {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/utils/settings.ts
|
|
241
|
+
function loadWorkspaceTailwindSettings(cwd) {
|
|
242
|
+
return mergeTailwindSettings(readVsCodeTailwindSettings(cwd), readZedTailwindSettings(cwd));
|
|
243
|
+
}
|
|
244
|
+
function readVsCodeTailwindSettings(cwd) {
|
|
245
|
+
const parsed = readJsoncFile(node_path.join(cwd, ".vscode", "settings.json"));
|
|
246
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
247
|
+
const raw = parsed;
|
|
248
|
+
const experimental = readNestedObject(raw, "tailwindCSS.experimental");
|
|
249
|
+
const files = readNestedObject(raw, "tailwindCSS.files");
|
|
250
|
+
const lint = readNestedObject(raw, "tailwindCSS.lint");
|
|
251
|
+
return {
|
|
252
|
+
includeLanguages: asRecord(raw["tailwindCSS.includeLanguages"]),
|
|
253
|
+
classAttributes: asStringArray(raw["tailwindCSS.classAttributes"]),
|
|
254
|
+
classFunctions: asStringArray(raw["tailwindCSS.classFunctions"]),
|
|
255
|
+
experimental: {
|
|
256
|
+
classRegex: asClassRegex(experimental?.classRegex),
|
|
257
|
+
configFile: asConfigFile(experimental?.configFile)
|
|
258
|
+
},
|
|
259
|
+
files: { exclude: asStringArray(files?.exclude) },
|
|
260
|
+
lint: asLintSettings(lint)
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function readZedTailwindSettings(cwd) {
|
|
264
|
+
const parsed = readJsoncFile(node_path.join(cwd, ".zed", "settings.json"));
|
|
265
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
266
|
+
const serverSettings = asRecord(asRecord(asRecord(parsed.lsp)?.["tailwindcss-language-server"])?.settings);
|
|
267
|
+
const normalized = asRecord(serverSettings?.tailwindCSS) ?? serverSettings;
|
|
268
|
+
if (!normalized) return {};
|
|
269
|
+
return {
|
|
270
|
+
includeLanguages: asRecord(normalized.includeLanguages),
|
|
271
|
+
classAttributes: asStringArray(normalized.classAttributes),
|
|
272
|
+
classFunctions: asStringArray(normalized.classFunctions),
|
|
273
|
+
experimental: {
|
|
274
|
+
classRegex: asClassRegex(asRecord(normalized.experimental)?.classRegex),
|
|
275
|
+
configFile: asConfigFile(asRecord(normalized.experimental)?.configFile)
|
|
276
|
+
},
|
|
277
|
+
files: { exclude: asStringArray(asRecord(normalized.files)?.exclude) },
|
|
278
|
+
lint: asLintSettings(asRecord(normalized.lint))
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function readJsoncFile(filePath) {
|
|
282
|
+
if (!fileExists(filePath)) return null;
|
|
283
|
+
try {
|
|
284
|
+
return JSON.parse(stripJsonComments(readFileSync(filePath)));
|
|
285
|
+
} catch {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function stripJsonComments(content) {
|
|
290
|
+
return content.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "").replace(/,\s*([}\]])/g, "$1");
|
|
291
|
+
}
|
|
292
|
+
function readNestedObject(input, key) {
|
|
293
|
+
return asRecord(input[key]);
|
|
294
|
+
}
|
|
295
|
+
function asRecord(value) {
|
|
296
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
297
|
+
}
|
|
298
|
+
function asStringArray(value) {
|
|
299
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
|
|
300
|
+
}
|
|
301
|
+
function asClassRegex(value) {
|
|
302
|
+
if (!Array.isArray(value)) return;
|
|
303
|
+
if (value.every((item) => typeof item === "string")) return value;
|
|
304
|
+
if (value.every((item) => Array.isArray(item) && item.length === 2 && item.every((part) => typeof part === "string"))) return value;
|
|
305
|
+
}
|
|
306
|
+
function asConfigFile(value) {
|
|
307
|
+
if (typeof value === "string" || value === null) return value;
|
|
308
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
309
|
+
const record = value;
|
|
310
|
+
const entries = Object.entries(record).filter(([, entry]) => typeof entry === "string" || Array.isArray(entry) && entry.every((item) => typeof item === "string"));
|
|
311
|
+
return entries.length === Object.keys(record).length ? Object.fromEntries(entries) : void 0;
|
|
312
|
+
}
|
|
313
|
+
function asLintSettings(value) {
|
|
314
|
+
if (!value) return;
|
|
315
|
+
const entries = Object.entries(value).filter(([, entry]) => [
|
|
316
|
+
"ignore",
|
|
317
|
+
"warning",
|
|
318
|
+
"error"
|
|
319
|
+
].includes(String(entry)));
|
|
320
|
+
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
321
|
+
}
|
|
322
|
+
function mergeTailwindSettings(...patches) {
|
|
323
|
+
return patches.reduce((acc, patch) => {
|
|
324
|
+
if (patch.includeLanguages) acc.includeLanguages = {
|
|
325
|
+
...acc.includeLanguages,
|
|
326
|
+
...patch.includeLanguages
|
|
327
|
+
};
|
|
328
|
+
if (patch.classAttributes) acc.classAttributes = uniqueStrings(acc.classAttributes, patch.classAttributes);
|
|
329
|
+
if (patch.classFunctions) acc.classFunctions = uniqueStrings(acc.classFunctions, patch.classFunctions);
|
|
330
|
+
if (patch.experimental) acc.experimental = {
|
|
331
|
+
...acc.experimental,
|
|
332
|
+
...patch.experimental,
|
|
333
|
+
classRegex: mergeClassRegex(acc.experimental?.classRegex, patch.experimental.classRegex)
|
|
334
|
+
};
|
|
335
|
+
if (patch.files?.exclude) acc.files = { exclude: uniqueStrings(acc.files?.exclude, patch.files.exclude) };
|
|
336
|
+
if (patch.lint) acc.lint = {
|
|
337
|
+
...acc.lint,
|
|
338
|
+
...patch.lint
|
|
339
|
+
};
|
|
340
|
+
return acc;
|
|
341
|
+
}, {});
|
|
342
|
+
}
|
|
343
|
+
function uniqueStrings(...arrays) {
|
|
344
|
+
return [...new Set(arrays.flatMap((array) => array ?? []))];
|
|
345
|
+
}
|
|
346
|
+
function mergeClassRegex(current, next) {
|
|
347
|
+
if (!next) return current;
|
|
348
|
+
if (!current) return next;
|
|
349
|
+
const serialized = /* @__PURE__ */ new Set();
|
|
350
|
+
const merged = [];
|
|
351
|
+
for (const item of [...current, ...next]) {
|
|
352
|
+
const key = JSON.stringify(item);
|
|
353
|
+
if (serialized.has(key)) continue;
|
|
354
|
+
serialized.add(key);
|
|
355
|
+
merged.push(item);
|
|
356
|
+
}
|
|
357
|
+
return merged;
|
|
358
|
+
}
|
|
359
|
+
function mergeEditorSettings(defaultSettings, overrides) {
|
|
360
|
+
return {
|
|
361
|
+
...defaultSettings,
|
|
362
|
+
tailwindCSS: {
|
|
363
|
+
...defaultSettings.tailwindCSS,
|
|
364
|
+
...overrides,
|
|
365
|
+
includeLanguages: {
|
|
366
|
+
...defaultSettings.tailwindCSS.includeLanguages,
|
|
367
|
+
...overrides.includeLanguages
|
|
368
|
+
},
|
|
369
|
+
classAttributes: uniqueStrings(defaultSettings.tailwindCSS.classAttributes, overrides.classAttributes),
|
|
370
|
+
classFunctions: uniqueStrings(defaultSettings.tailwindCSS.classFunctions, overrides.classFunctions),
|
|
371
|
+
files: { exclude: uniqueStrings(defaultSettings.tailwindCSS.files.exclude, overrides.files?.exclude) },
|
|
372
|
+
experimental: {
|
|
373
|
+
...defaultSettings.tailwindCSS.experimental,
|
|
374
|
+
...overrides.experimental,
|
|
375
|
+
classRegex: mergeClassRegex(defaultSettings.tailwindCSS.experimental.classRegex, overrides.experimental?.classRegex) ?? defaultSettings.tailwindCSS.experimental.classRegex
|
|
376
|
+
},
|
|
377
|
+
lint: {
|
|
378
|
+
...defaultSettings.tailwindCSS.lint,
|
|
379
|
+
...overrides.lint
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
//#endregion
|
|
210
385
|
//#region src/adapters/editor-state-adapter.ts
|
|
211
386
|
function isDirectory(filePath) {
|
|
212
387
|
try {
|
|
@@ -216,7 +391,7 @@ function isDirectory(filePath) {
|
|
|
216
391
|
}
|
|
217
392
|
}
|
|
218
393
|
function createEditorState(cwd) {
|
|
219
|
-
const settings = {
|
|
394
|
+
const settings = mergeEditorSettings({
|
|
220
395
|
editor: { tabSize: 2 },
|
|
221
396
|
tailwindCSS: {
|
|
222
397
|
validate: true,
|
|
@@ -265,7 +440,7 @@ function createEditorState(cwd) {
|
|
|
265
440
|
suggestCanonicalClasses: "warning"
|
|
266
441
|
}
|
|
267
442
|
}
|
|
268
|
-
};
|
|
443
|
+
}, loadWorkspaceTailwindSettings(cwd));
|
|
269
444
|
return {
|
|
270
445
|
connection: null,
|
|
271
446
|
folder: cwd,
|
|
@@ -367,37 +542,6 @@ function extractConfigMetadata(state) {
|
|
|
367
542
|
if (config.corePlugins) state.corePlugins = Array.isArray(config.corePlugins) ? config.corePlugins : Object.keys(config.corePlugins);
|
|
368
543
|
}
|
|
369
544
|
//#endregion
|
|
370
|
-
//#region src/utils/fs.ts
|
|
371
|
-
function fileExists(filePath) {
|
|
372
|
-
try {
|
|
373
|
-
return node_fs.existsSync(filePath);
|
|
374
|
-
} catch {
|
|
375
|
-
return false;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
function readFileSync(filePath) {
|
|
379
|
-
if (!filePath || typeof filePath !== "string") throw new TypeError("File path must be a non-empty string");
|
|
380
|
-
return node_fs.readFileSync(filePath, "utf-8");
|
|
381
|
-
}
|
|
382
|
-
function writeFileSync(filePath, content) {
|
|
383
|
-
if (!filePath || typeof filePath !== "string") throw new TypeError("File path must be a non-empty string");
|
|
384
|
-
if (typeof content !== "string") throw new TypeError("Content must be a string");
|
|
385
|
-
node_fs.writeFileSync(filePath, content, "utf-8");
|
|
386
|
-
}
|
|
387
|
-
function readGitignorePatterns(cwd) {
|
|
388
|
-
const gitignorePath = node_path.join(cwd, ".gitignore");
|
|
389
|
-
if (!fileExists(gitignorePath)) return [];
|
|
390
|
-
try {
|
|
391
|
-
return node_fs.readFileSync(gitignorePath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!")).map((pattern) => {
|
|
392
|
-
const cleaned = pattern.replace(/\/+$/, "");
|
|
393
|
-
if (cleaned.includes("/") || cleaned.includes("*")) return cleaned.endsWith("/**") ? cleaned : `${cleaned}/**`;
|
|
394
|
-
return `**/${cleaned}/**`;
|
|
395
|
-
});
|
|
396
|
-
} catch {
|
|
397
|
-
return [];
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
//#endregion
|
|
401
545
|
//#region src/adapters/v4-adapter.ts
|
|
402
546
|
const require$3 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href || __filename);
|
|
403
547
|
async function loadV4DesignSystem(state, cwd, configPath, verbose = false) {
|
|
@@ -700,6 +844,11 @@ async function discoverFilesFromConfig(cwd, configPath) {
|
|
|
700
844
|
}
|
|
701
845
|
const configDir = node_path.dirname(configFilePath);
|
|
702
846
|
const cssContent = fileExists(configFilePath) ? readFileSync(configFilePath) : SYNTHETIC_VITE_CSS_CONFIG_CONTENT;
|
|
847
|
+
const includeConfigFile = (files) => {
|
|
848
|
+
if (!fileExists(configFilePath)) return files;
|
|
849
|
+
const relativeConfigPath = node_path.relative(cwd, configFilePath);
|
|
850
|
+
return [...new Set([relativeConfigPath, ...files])].sort((a, b) => a.localeCompare(b));
|
|
851
|
+
};
|
|
703
852
|
const { include, exclude } = extractSourcePatterns(cssContent);
|
|
704
853
|
const importSource = extractImportSourceDirectives(cssContent);
|
|
705
854
|
const resolveFromConfig = (pattern) => {
|
|
@@ -709,10 +858,10 @@ async function discoverFilesFromConfig(cwd, configPath) {
|
|
|
709
858
|
const resolvedExclude = exclude.map(resolveFromConfig);
|
|
710
859
|
const gitignorePatterns = readGitignorePatterns(cwd);
|
|
711
860
|
const extraIgnore = [...resolvedExclude, ...gitignorePatterns];
|
|
712
|
-
if (include.length > 0) return expandPatterns(cwd, include.map(resolveFromConfig), extraIgnore);
|
|
713
|
-
if (importSource.roots.length > 0) return expandPatterns(cwd, importSource.roots.map((root) => resolveFromConfig(node_path.join(root, "**/*.{js,jsx,ts,tsx,html,vue,svelte,astro,mdx}"))), extraIgnore);
|
|
714
|
-
if (importSource.disableAutoSource) return [];
|
|
715
|
-
return expandPatterns(cwd, [DEFAULT_FILE_PATTERN], extraIgnore);
|
|
861
|
+
if (include.length > 0) return includeConfigFile(await expandPatterns(cwd, include.map(resolveFromConfig), extraIgnore));
|
|
862
|
+
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));
|
|
863
|
+
if (importSource.disableAutoSource) return includeConfigFile([]);
|
|
864
|
+
return includeConfigFile(await expandPatterns(cwd, [DEFAULT_FILE_PATTERN], extraIgnore));
|
|
716
865
|
}
|
|
717
866
|
function extractContentPatterns(config) {
|
|
718
867
|
if (!config.content) return [];
|
|
@@ -796,7 +945,8 @@ async function initializeState(cwd, configPath, verbose = false) {
|
|
|
796
945
|
}
|
|
797
946
|
async function lint({ cwd, patterns, configPath, autoDiscover, fix = false, verbose = false, onProgress }) {
|
|
798
947
|
const state = await initializeState(cwd, configPath, verbose);
|
|
799
|
-
const
|
|
948
|
+
const discoveredFiles = await discoverFiles(cwd, patterns, configPath, autoDiscover);
|
|
949
|
+
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;
|
|
800
950
|
if (verbose) {
|
|
801
951
|
console.log(ansis.default.cyan.bold(`→ Discovered ${files.length} file${files.length !== 1 ? "s" : ""} to lint`));
|
|
802
952
|
console.log();
|
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-BygZ0yGf.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