@empline/preflight 1.1.4 → 1.1.6
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/checks/auth/auth-error-handling.d.ts +18 -0
- package/dist/checks/auth/auth-error-handling.d.ts.map +1 -0
- package/dist/checks/auth/auth-error-handling.js +257 -0
- package/dist/checks/auth/auth-error-handling.js.map +1 -0
- package/dist/checks/tailwind/undefined-tailwind-utilities.d.ts +29 -0
- package/dist/checks/tailwind/undefined-tailwind-utilities.d.ts.map +1 -0
- package/dist/checks/tailwind/undefined-tailwind-utilities.js +337 -0
- package/dist/checks/tailwind/undefined-tailwind-utilities.js.map +1 -0
- package/dist/checks/turbopack/map-closure-safety.d.ts +37 -0
- package/dist/checks/turbopack/map-closure-safety.d.ts.map +1 -0
- package/dist/checks/turbopack/map-closure-safety.js +303 -0
- package/dist/checks/turbopack/map-closure-safety.js.map +1 -0
- package/dist/checks/turbopack/nested-property-jsx-access.d.ts +39 -0
- package/dist/checks/turbopack/nested-property-jsx-access.d.ts.map +1 -0
- package/dist/checks/turbopack/nested-property-jsx-access.js +379 -0
- package/dist/checks/turbopack/nested-property-jsx-access.js.map +1 -0
- package/dist/core/categories.d.ts.map +1 -1
- package/dist/core/categories.js +42 -0
- package/dist/core/categories.js.map +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Error Handling Preflight
|
|
3
|
+
*
|
|
4
|
+
* Detects client components that make authenticated API calls without proper
|
|
5
|
+
* auth error handling. This prevents confusing "Seller account required" or
|
|
6
|
+
* similar errors when the session has expired but the UI still shows as logged in.
|
|
7
|
+
*
|
|
8
|
+
* What it checks:
|
|
9
|
+
* 1. Client components using fetch() to /api/store/* endpoints
|
|
10
|
+
* 2. Whether they use authFetch or have explicit auth error handling
|
|
11
|
+
* 3. Flags components that may show confusing errors on stale sessions
|
|
12
|
+
*
|
|
13
|
+
* Fix:
|
|
14
|
+
* Replace `fetch("/api/store/...")` with `authFetch("/api/store/...")`
|
|
15
|
+
* from "@/lib/auth-fetch"
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=auth-error-handling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-error-handling.d.ts","sourceRoot":"","sources":["../../../src/checks/auth/auth-error-handling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Auth Error Handling Preflight
|
|
4
|
+
*
|
|
5
|
+
* Detects client components that make authenticated API calls without proper
|
|
6
|
+
* auth error handling. This prevents confusing "Seller account required" or
|
|
7
|
+
* similar errors when the session has expired but the UI still shows as logged in.
|
|
8
|
+
*
|
|
9
|
+
* What it checks:
|
|
10
|
+
* 1. Client components using fetch() to /api/store/* endpoints
|
|
11
|
+
* 2. Whether they use authFetch or have explicit auth error handling
|
|
12
|
+
* 3. Flags components that may show confusing errors on stale sessions
|
|
13
|
+
*
|
|
14
|
+
* Fix:
|
|
15
|
+
* Replace `fetch("/api/store/...")` with `authFetch("/api/store/...")`
|
|
16
|
+
* from "@/lib/auth-fetch"
|
|
17
|
+
*/
|
|
18
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
21
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
22
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
23
|
+
}
|
|
24
|
+
Object.defineProperty(o, k2, desc);
|
|
25
|
+
}) : (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
o[k2] = m[k];
|
|
28
|
+
}));
|
|
29
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
30
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
31
|
+
}) : function(o, v) {
|
|
32
|
+
o["default"] = v;
|
|
33
|
+
});
|
|
34
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
35
|
+
var ownKeys = function(o) {
|
|
36
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
37
|
+
var ar = [];
|
|
38
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
39
|
+
return ar;
|
|
40
|
+
};
|
|
41
|
+
return ownKeys(o);
|
|
42
|
+
};
|
|
43
|
+
return function (mod) {
|
|
44
|
+
if (mod && mod.__esModule) return mod;
|
|
45
|
+
var result = {};
|
|
46
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
47
|
+
__setModuleDefault(result, mod);
|
|
48
|
+
return result;
|
|
49
|
+
};
|
|
50
|
+
})();
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
// Direct execution - parse args manually
|
|
55
|
+
const args = process.argv.slice(2);
|
|
56
|
+
const jsonOutput = args.includes("--json");
|
|
57
|
+
const showAll = args.includes("--all");
|
|
58
|
+
/** Protected API prefixes that require authentication */
|
|
59
|
+
const PROTECTED_API_PREFIXES = [
|
|
60
|
+
"/api/store/",
|
|
61
|
+
"/api/admin/",
|
|
62
|
+
"/api/seller/",
|
|
63
|
+
"/api/account/",
|
|
64
|
+
"/api/listings/bulk",
|
|
65
|
+
"/api/listings/save",
|
|
66
|
+
"/api/listings/submit",
|
|
67
|
+
"/api/listings/create",
|
|
68
|
+
"/api/listings/delete",
|
|
69
|
+
"/api/listings/load",
|
|
70
|
+
];
|
|
71
|
+
/** Files/patterns to skip */
|
|
72
|
+
const SKIP_PATTERNS = [
|
|
73
|
+
"node_modules",
|
|
74
|
+
".next",
|
|
75
|
+
"dist",
|
|
76
|
+
".test.",
|
|
77
|
+
".spec.",
|
|
78
|
+
"__tests__",
|
|
79
|
+
"__mocks__",
|
|
80
|
+
];
|
|
81
|
+
function shouldSkipFile(filePath) {
|
|
82
|
+
return SKIP_PATTERNS.some((pattern) => filePath.includes(pattern));
|
|
83
|
+
}
|
|
84
|
+
function isClientComponent(content) {
|
|
85
|
+
return content.trimStart().startsWith('"use client"') ||
|
|
86
|
+
content.trimStart().startsWith("'use client'");
|
|
87
|
+
}
|
|
88
|
+
function isProtectedEndpoint(endpoint) {
|
|
89
|
+
return PROTECTED_API_PREFIXES.some((prefix) => endpoint.includes(prefix));
|
|
90
|
+
}
|
|
91
|
+
function hasAuthFetchImport(content) {
|
|
92
|
+
return content.includes("from \"@/lib/auth-fetch\"") ||
|
|
93
|
+
content.includes("from '@/lib/auth-fetch'") ||
|
|
94
|
+
content.includes("authFetch");
|
|
95
|
+
}
|
|
96
|
+
function hasExplicitAuthHandling(content, lineIndex) {
|
|
97
|
+
// Check if there's auth error handling within ~30 lines after the fetch
|
|
98
|
+
const lines = content.split("\n");
|
|
99
|
+
const contextLines = lines.slice(lineIndex, lineIndex + 30).join("\n").toLowerCase();
|
|
100
|
+
// Patterns that indicate auth error handling
|
|
101
|
+
const authHandlingPatterns = [
|
|
102
|
+
"401",
|
|
103
|
+
"403",
|
|
104
|
+
"unauthorized",
|
|
105
|
+
"forbidden",
|
|
106
|
+
"session",
|
|
107
|
+
"signin",
|
|
108
|
+
"signout",
|
|
109
|
+
"login",
|
|
110
|
+
"logout",
|
|
111
|
+
"redirect",
|
|
112
|
+
"onunauthorized",
|
|
113
|
+
"onforbidden",
|
|
114
|
+
"handleapierror",
|
|
115
|
+
];
|
|
116
|
+
return authHandlingPatterns.some((pattern) => contextLines.includes(pattern));
|
|
117
|
+
}
|
|
118
|
+
function extractEndpoint(line) {
|
|
119
|
+
// Match fetch("/api/...") or fetch('/api/...')
|
|
120
|
+
const match = line.match(/fetch\s*\(\s*["'`](\/api[^"'`]+)["'`]/);
|
|
121
|
+
if (match) {
|
|
122
|
+
return match[1] || null;
|
|
123
|
+
}
|
|
124
|
+
// Match fetch(url) where url contains /api/
|
|
125
|
+
if (line.includes("fetch") && line.includes("/api/")) {
|
|
126
|
+
const apiMatch = line.match(/["'`](\/api[^"'`]+)["'`]/);
|
|
127
|
+
return apiMatch?.[1] || "/api/... (dynamic)";
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
function scanFile(filePath) {
|
|
132
|
+
const issues = [];
|
|
133
|
+
try {
|
|
134
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
135
|
+
// Only check client components
|
|
136
|
+
if (!isClientComponent(content)) {
|
|
137
|
+
return issues;
|
|
138
|
+
}
|
|
139
|
+
// If the file already uses authFetch, it's good
|
|
140
|
+
if (hasAuthFetchImport(content)) {
|
|
141
|
+
return issues;
|
|
142
|
+
}
|
|
143
|
+
const lines = content.split("\n");
|
|
144
|
+
for (let i = 0; i < lines.length; i++) {
|
|
145
|
+
const line = lines[i] || "";
|
|
146
|
+
// Skip comments
|
|
147
|
+
if (line.trim().startsWith("//") || line.trim().startsWith("*")) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
// Check for fetch calls
|
|
151
|
+
if (line.includes("fetch(") || line.includes("fetch (")) {
|
|
152
|
+
const endpoint = extractEndpoint(line);
|
|
153
|
+
if (endpoint && isProtectedEndpoint(endpoint)) {
|
|
154
|
+
// Check if there's explicit auth handling nearby
|
|
155
|
+
if (!hasExplicitAuthHandling(content, i)) {
|
|
156
|
+
issues.push({
|
|
157
|
+
file: filePath,
|
|
158
|
+
line: i + 1,
|
|
159
|
+
endpoint,
|
|
160
|
+
severity: "warning",
|
|
161
|
+
message: `Unprotected fetch to authenticated endpoint. Use authFetch from "@/lib/auth-fetch" to handle stale session errors.`,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
// Skip files that can't be read
|
|
170
|
+
}
|
|
171
|
+
return issues;
|
|
172
|
+
}
|
|
173
|
+
function findClientComponents(dir) {
|
|
174
|
+
const files = [];
|
|
175
|
+
function walk(directory) {
|
|
176
|
+
try {
|
|
177
|
+
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
178
|
+
for (const entry of entries) {
|
|
179
|
+
const fullPath = path.join(directory, entry.name);
|
|
180
|
+
if (shouldSkipFile(fullPath)) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (entry.isDirectory()) {
|
|
184
|
+
walk(fullPath);
|
|
185
|
+
}
|
|
186
|
+
else if (entry.isFile() && /\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
187
|
+
files.push(fullPath);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Skip directories that can't be read
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
walk(dir);
|
|
196
|
+
return files;
|
|
197
|
+
}
|
|
198
|
+
function main() {
|
|
199
|
+
const projectRoot = process.cwd();
|
|
200
|
+
const scanDirs = ["app", "components", "lib"].map((d) => path.join(projectRoot, d));
|
|
201
|
+
let allFiles = [];
|
|
202
|
+
for (const dir of scanDirs) {
|
|
203
|
+
if (fs.existsSync(dir)) {
|
|
204
|
+
allFiles = allFiles.concat(findClientComponents(dir));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const allIssues = [];
|
|
208
|
+
for (const file of allFiles) {
|
|
209
|
+
const issues = scanFile(file);
|
|
210
|
+
allIssues.push(...issues);
|
|
211
|
+
}
|
|
212
|
+
// Output results
|
|
213
|
+
if (jsonOutput) {
|
|
214
|
+
console.log(JSON.stringify(allIssues, null, 2));
|
|
215
|
+
process.exit(allIssues.length > 0 ? 1 : 0);
|
|
216
|
+
}
|
|
217
|
+
// Human-readable output
|
|
218
|
+
console.log("");
|
|
219
|
+
console.log("🔐 Auth Error Handling Preflight");
|
|
220
|
+
console.log("━".repeat(50));
|
|
221
|
+
if (allIssues.length === 0) {
|
|
222
|
+
console.log("✅ No issues found. All authenticated endpoints are properly protected.");
|
|
223
|
+
process.exit(0);
|
|
224
|
+
}
|
|
225
|
+
console.log(`⚠️ Found ${allIssues.length} unprotected authenticated API calls\n`);
|
|
226
|
+
// Group by file
|
|
227
|
+
const byFile = new Map();
|
|
228
|
+
for (const issue of allIssues) {
|
|
229
|
+
const relativePath = path.relative(projectRoot, issue.file);
|
|
230
|
+
if (!byFile.has(relativePath)) {
|
|
231
|
+
byFile.set(relativePath, []);
|
|
232
|
+
}
|
|
233
|
+
byFile.get(relativePath).push(issue);
|
|
234
|
+
}
|
|
235
|
+
// Display (limit to 10 files unless --all)
|
|
236
|
+
const entries = [...byFile.entries()];
|
|
237
|
+
const displayEntries = showAll ? entries : entries.slice(0, 10);
|
|
238
|
+
for (const [file, issues] of displayEntries) {
|
|
239
|
+
console.log(`📁 ${file}`);
|
|
240
|
+
for (const issue of issues) {
|
|
241
|
+
console.log(` Line ${issue.line}: ${issue.endpoint}`);
|
|
242
|
+
}
|
|
243
|
+
console.log("");
|
|
244
|
+
}
|
|
245
|
+
if (!showAll && entries.length > 10) {
|
|
246
|
+
console.log(` ... and ${entries.length - 10} more files`);
|
|
247
|
+
console.log(` Use --all to see all issues`);
|
|
248
|
+
console.log("");
|
|
249
|
+
}
|
|
250
|
+
console.log("━".repeat(50));
|
|
251
|
+
console.log(`\n🔧 Fix: Replace fetch("/api/store/...") with authFetch("/api/store/...")`);
|
|
252
|
+
console.log(` Import: import { authFetch } from "@/lib/auth-fetch";`);
|
|
253
|
+
console.log("");
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
main();
|
|
257
|
+
//# sourceMappingURL=auth-error-handling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-error-handling.js","sourceRoot":"","sources":["../../../src/checks/auth/auth-error-handling.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Undefined Tailwind Utilities Preflight (BLOCKING)
|
|
4
|
+
*
|
|
5
|
+
* Detects Tailwind utility classes that reference undefined CSS variables.
|
|
6
|
+
* This prevents invisible UI elements caused by missing theme tokens.
|
|
7
|
+
*
|
|
8
|
+
* Common issue detected:
|
|
9
|
+
* - `bg-background` used when theme defines `--bg-primary` instead of `--background`
|
|
10
|
+
* - Other utility classes that expect shadcn-style CSS variables
|
|
11
|
+
*
|
|
12
|
+
* Why this matters:
|
|
13
|
+
* - Using undefined CSS variables results in invisible elements
|
|
14
|
+
* - Components may appear broken (e.g., toggle thumbs not visible)
|
|
15
|
+
* - These issues are hard to detect visually until specific states are reached
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* pnpm preflight:undefined-tailwind-utilities
|
|
19
|
+
* pnpm preflight:undefined-tailwind-utilities --verbose
|
|
20
|
+
*/
|
|
21
|
+
export declare const id = "tailwind/undefined-tailwind-utilities";
|
|
22
|
+
export declare const name = "Undefined Tailwind Utilities";
|
|
23
|
+
export declare const category = "tailwind";
|
|
24
|
+
export declare const blocking = true;
|
|
25
|
+
export declare const description = "Detects Tailwind classes using undefined CSS variables (BLOCKING)";
|
|
26
|
+
export declare const tags: string[];
|
|
27
|
+
export declare const requires: string[];
|
|
28
|
+
export declare function run(): Promise<void>;
|
|
29
|
+
//# sourceMappingURL=undefined-tailwind-utilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"undefined-tailwind-utilities.d.ts","sourceRoot":"","sources":["../../../src/checks/tailwind/undefined-tailwind-utilities.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;GAkBG;AASH,eAAO,MAAM,EAAE,0CAA0C,CAAC;AAC1D,eAAO,MAAM,IAAI,iCAAiC,CAAC;AACnD,eAAO,MAAM,QAAQ,aAAa,CAAC;AACnC,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,WAAW,sEAAsE,CAAC;AAC/F,eAAO,MAAM,IAAI,UAAiD,CAAC;AACnE,eAAO,MAAM,QAAQ,UAAkB,CAAC;AAoLxC,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAuHzC"}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Undefined Tailwind Utilities Preflight (BLOCKING)
|
|
5
|
+
*
|
|
6
|
+
* Detects Tailwind utility classes that reference undefined CSS variables.
|
|
7
|
+
* This prevents invisible UI elements caused by missing theme tokens.
|
|
8
|
+
*
|
|
9
|
+
* Common issue detected:
|
|
10
|
+
* - `bg-background` used when theme defines `--bg-primary` instead of `--background`
|
|
11
|
+
* - Other utility classes that expect shadcn-style CSS variables
|
|
12
|
+
*
|
|
13
|
+
* Why this matters:
|
|
14
|
+
* - Using undefined CSS variables results in invisible elements
|
|
15
|
+
* - Components may appear broken (e.g., toggle thumbs not visible)
|
|
16
|
+
* - These issues are hard to detect visually until specific states are reached
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* pnpm preflight:undefined-tailwind-utilities
|
|
20
|
+
* pnpm preflight:undefined-tailwind-utilities --verbose
|
|
21
|
+
*/
|
|
22
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
25
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
26
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
27
|
+
}
|
|
28
|
+
Object.defineProperty(o, k2, desc);
|
|
29
|
+
}) : (function(o, m, k, k2) {
|
|
30
|
+
if (k2 === undefined) k2 = k;
|
|
31
|
+
o[k2] = m[k];
|
|
32
|
+
}));
|
|
33
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
34
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
35
|
+
}) : function(o, v) {
|
|
36
|
+
o["default"] = v;
|
|
37
|
+
});
|
|
38
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
39
|
+
var ownKeys = function(o) {
|
|
40
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
41
|
+
var ar = [];
|
|
42
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
43
|
+
return ar;
|
|
44
|
+
};
|
|
45
|
+
return ownKeys(o);
|
|
46
|
+
};
|
|
47
|
+
return function (mod) {
|
|
48
|
+
if (mod && mod.__esModule) return mod;
|
|
49
|
+
var result = {};
|
|
50
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
51
|
+
__setModuleDefault(result, mod);
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
})();
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.requires = exports.tags = exports.description = exports.blocking = exports.category = exports.name = exports.id = void 0;
|
|
57
|
+
exports.run = run;
|
|
58
|
+
const fs = __importStar(require("fs"));
|
|
59
|
+
const file_cache_1 = require("../../shared/file-cache");
|
|
60
|
+
const glob_patterns_1 = require("../../shared/glob-patterns");
|
|
61
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
62
|
+
const universal_progress_reporter_1 = require("../system/universal-progress-reporter");
|
|
63
|
+
// Check metadata
|
|
64
|
+
exports.id = "tailwind/undefined-tailwind-utilities";
|
|
65
|
+
exports.name = "Undefined Tailwind Utilities";
|
|
66
|
+
exports.category = "tailwind";
|
|
67
|
+
exports.blocking = true;
|
|
68
|
+
exports.description = "Detects Tailwind classes using undefined CSS variables (BLOCKING)";
|
|
69
|
+
exports.tags = ["tailwind", "css-variables", "theming", "ui"];
|
|
70
|
+
exports.requires = ["tailwindcss"];
|
|
71
|
+
const EXCLUDED = (0, glob_patterns_1.extendExcludes)(glob_patterns_1.STANDARD_EXCLUDES, [
|
|
72
|
+
"**/test-results/**",
|
|
73
|
+
"**/*.test.tsx",
|
|
74
|
+
"**/*.spec.tsx",
|
|
75
|
+
"**/*.stories.tsx",
|
|
76
|
+
]);
|
|
77
|
+
/**
|
|
78
|
+
* Tailwind utility classes that rely on CSS variables not defined in our theme.
|
|
79
|
+
* These often come from copying shadcn/ui components that expect different variable names.
|
|
80
|
+
*
|
|
81
|
+
* Patterns use (?:\/\d+)? to optionally match opacity modifiers like /20, /50, etc.
|
|
82
|
+
*
|
|
83
|
+
* Format: { pattern, name, message, suggestion, severity }
|
|
84
|
+
*/
|
|
85
|
+
const UNDEFINED_UTILITIES = [
|
|
86
|
+
{
|
|
87
|
+
// shadcn expects --background, we use --bg-primary
|
|
88
|
+
pattern: /\bbg-background(?:\/\d+)?\b/g,
|
|
89
|
+
name: "bg-background",
|
|
90
|
+
message: "bg-background uses undefined --background CSS variable",
|
|
91
|
+
suggestion: "Use bg-white, bg-[var(--bg-primary)], or appropriate color class",
|
|
92
|
+
severity: "error",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
// shadcn expects --foreground, we use --text-primary
|
|
96
|
+
pattern: /\btext-foreground(?:\/\d+)?\b/g,
|
|
97
|
+
name: "text-foreground",
|
|
98
|
+
message: "text-foreground uses undefined --foreground CSS variable",
|
|
99
|
+
suggestion: "Use text-[var(--text-primary)] or appropriate color class",
|
|
100
|
+
severity: "error",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
// shadcn expects --muted, we use --text-secondary or --bg-tertiary
|
|
104
|
+
// Matches: bg-muted, bg-muted/20, bg-muted/50, etc.
|
|
105
|
+
pattern: /\bbg-muted(?:\/\d+)?\b/g,
|
|
106
|
+
name: "bg-muted",
|
|
107
|
+
message: "bg-muted uses undefined --muted CSS variable",
|
|
108
|
+
suggestion: "Use bg-[var(--bg-tertiary)] or bg-[var(--bg-tertiary)]/20 for opacity",
|
|
109
|
+
severity: "error",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
// Matches: text-muted-foreground, text-muted-foreground/70, etc.
|
|
113
|
+
pattern: /\btext-muted-foreground(?:\/\d+)?\b/g,
|
|
114
|
+
name: "text-muted-foreground",
|
|
115
|
+
message: "text-muted-foreground uses undefined --muted-foreground CSS variable",
|
|
116
|
+
suggestion: "Use text-[var(--text-secondary)] or text-[var(--text-secondary)]/70 for opacity",
|
|
117
|
+
severity: "error",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
// shadcn expects --card, we use --bg-primary
|
|
121
|
+
pattern: /\bbg-card(?:\/\d+)?\b/g,
|
|
122
|
+
name: "bg-card",
|
|
123
|
+
message: "bg-card uses undefined --card CSS variable",
|
|
124
|
+
suggestion: "Use bg-[var(--bg-primary)] or appropriate color class",
|
|
125
|
+
severity: "error",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
// shadcn expects --popover
|
|
129
|
+
pattern: /\bbg-popover(?:\/\d+)?\b/g,
|
|
130
|
+
name: "bg-popover",
|
|
131
|
+
message: "bg-popover uses undefined --popover CSS variable",
|
|
132
|
+
suggestion: "Use bg-[var(--bg-primary)] or bg-white for popover backgrounds",
|
|
133
|
+
severity: "error",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
// shadcn expects --accent
|
|
137
|
+
pattern: /\bbg-accent(?:\/\d+)?\b/g,
|
|
138
|
+
name: "bg-accent",
|
|
139
|
+
message: "bg-accent uses undefined --accent CSS variable",
|
|
140
|
+
suggestion: "Use bg-[var(--color-primary-light)] or appropriate accent color",
|
|
141
|
+
severity: "warning",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
pattern: /\btext-accent-foreground(?:\/\d+)?\b/g,
|
|
145
|
+
name: "text-accent-foreground",
|
|
146
|
+
message: "text-accent-foreground uses undefined --accent-foreground CSS variable",
|
|
147
|
+
suggestion: "Use text-[var(--color-primary)] or appropriate color class",
|
|
148
|
+
severity: "warning",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
// shadcn expects --border, we use --border-color
|
|
152
|
+
pattern: /\bborder-border(?:\/\d+)?\b/g,
|
|
153
|
+
name: "border-border",
|
|
154
|
+
message: "border-border uses undefined --border CSS variable",
|
|
155
|
+
suggestion: "Use border-[var(--border-color)] instead",
|
|
156
|
+
severity: "error",
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
// shadcn expects --input
|
|
160
|
+
pattern: /\bbg-input(?:\/\d+)?\b/g,
|
|
161
|
+
name: "bg-input",
|
|
162
|
+
message: "bg-input uses undefined --input CSS variable",
|
|
163
|
+
suggestion: "Use bg-[var(--bg-secondary)] or bg-white for input backgrounds",
|
|
164
|
+
severity: "warning",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
// shadcn expects --ring
|
|
168
|
+
pattern: /\bring-ring(?:\/\d+)?\b/g,
|
|
169
|
+
name: "ring-ring",
|
|
170
|
+
message: "ring-ring uses undefined --ring CSS variable",
|
|
171
|
+
suggestion: "Use ring-[var(--color-primary)] for focus rings",
|
|
172
|
+
severity: "warning",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
// shadcn expects --destructive
|
|
176
|
+
pattern: /\bbg-destructive(?:\/\d+)?\b/g,
|
|
177
|
+
name: "bg-destructive",
|
|
178
|
+
message: "bg-destructive uses undefined --destructive CSS variable",
|
|
179
|
+
suggestion: "Use bg-[var(--error-main)] for destructive/error backgrounds",
|
|
180
|
+
severity: "warning",
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
pattern: /\btext-destructive(?:\/\d+)?\b/g,
|
|
184
|
+
name: "text-destructive",
|
|
185
|
+
message: "text-destructive uses undefined --destructive CSS variable",
|
|
186
|
+
suggestion: "Use text-[var(--error-main)] for error text",
|
|
187
|
+
severity: "warning",
|
|
188
|
+
},
|
|
189
|
+
];
|
|
190
|
+
function checkFile(filePath, content) {
|
|
191
|
+
const issues = [];
|
|
192
|
+
const lines = content.split("\n");
|
|
193
|
+
for (const utility of UNDEFINED_UTILITIES) {
|
|
194
|
+
let match;
|
|
195
|
+
// Reset lastIndex for global regex
|
|
196
|
+
utility.pattern.lastIndex = 0;
|
|
197
|
+
while ((match = utility.pattern.exec(content)) !== null) {
|
|
198
|
+
// Find line number
|
|
199
|
+
let lineNumber = 1;
|
|
200
|
+
let charCount = 0;
|
|
201
|
+
for (let i = 0; i < lines.length; i++) {
|
|
202
|
+
charCount += lines[i].length + 1;
|
|
203
|
+
if (charCount > match.index) {
|
|
204
|
+
lineNumber = i + 1;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
issues.push({
|
|
209
|
+
file: filePath,
|
|
210
|
+
line: lineNumber,
|
|
211
|
+
type: utility.name,
|
|
212
|
+
severity: utility.severity,
|
|
213
|
+
message: utility.message,
|
|
214
|
+
suggestion: utility.suggestion,
|
|
215
|
+
snippet: lines[lineNumber - 1]?.trim().substring(0, 100),
|
|
216
|
+
match: match[0],
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return issues;
|
|
221
|
+
}
|
|
222
|
+
// CACHED FILE LISTS
|
|
223
|
+
let _cachedComponentFiles = null;
|
|
224
|
+
async function getComponentFiles() {
|
|
225
|
+
if (!_cachedComponentFiles) {
|
|
226
|
+
_cachedComponentFiles = await file_cache_1.fileCache.getComponentFiles();
|
|
227
|
+
}
|
|
228
|
+
return _cachedComponentFiles;
|
|
229
|
+
}
|
|
230
|
+
async function run() {
|
|
231
|
+
const reporter = (0, universal_progress_reporter_1.createUniversalProgressReporter)(exports.name);
|
|
232
|
+
const args = process.argv.slice(2);
|
|
233
|
+
const verbose = args.includes("--verbose") || args.includes("-v");
|
|
234
|
+
const warnOnly = args.includes("--warn") || args.includes("-w");
|
|
235
|
+
console.log(`\n${console_chars_1.emoji.palette} Undefined Tailwind Utilities Preflight`);
|
|
236
|
+
console.log((0, console_chars_1.createDivider)(80, "heavy"));
|
|
237
|
+
if (warnOnly) {
|
|
238
|
+
console.log(`${console_chars_1.emoji.warning} Running in warning mode - not blocking.\n`);
|
|
239
|
+
}
|
|
240
|
+
const startTime = Date.now();
|
|
241
|
+
const allIssues = [];
|
|
242
|
+
// Find all component files
|
|
243
|
+
const files = await getComponentFiles();
|
|
244
|
+
let filesScanned = 0;
|
|
245
|
+
for (const file of files) {
|
|
246
|
+
try {
|
|
247
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
248
|
+
filesScanned++;
|
|
249
|
+
allIssues.push(...checkFile(file, content));
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
if (verbose) {
|
|
253
|
+
console.error(`${console_chars_1.emoji.warning} Error reading ${file}: ${error.message}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const duration = Date.now() - startTime;
|
|
258
|
+
// Report results
|
|
259
|
+
console.log(`\n${console_chars_1.emoji.chart} Scan Summary:`);
|
|
260
|
+
console.log(` Files scanned: ${filesScanned}`);
|
|
261
|
+
console.log(` Duration: ${duration}ms`);
|
|
262
|
+
const errors = allIssues.filter((i) => i.severity === "error");
|
|
263
|
+
const warnings = allIssues.filter((i) => i.severity === "warning");
|
|
264
|
+
const infos = allIssues.filter((i) => i.severity === "info");
|
|
265
|
+
if (allIssues.length === 0) {
|
|
266
|
+
console.log(`\n${console_chars_1.emoji.success} No undefined Tailwind utility classes found`);
|
|
267
|
+
console.log(` All CSS variable references are valid.`);
|
|
268
|
+
process.exit(0);
|
|
269
|
+
}
|
|
270
|
+
// Group by type
|
|
271
|
+
const byType = new Map();
|
|
272
|
+
for (const issue of allIssues) {
|
|
273
|
+
const existing = byType.get(issue.type) || [];
|
|
274
|
+
existing.push(issue);
|
|
275
|
+
byType.set(issue.type, existing);
|
|
276
|
+
}
|
|
277
|
+
console.log(`\n${console_chars_1.emoji.clipboard} Issues Found:`);
|
|
278
|
+
console.log(` ${console_chars_1.emoji.error} Errors: ${errors.length}`);
|
|
279
|
+
console.log(` ${console_chars_1.emoji.warning} Warnings: ${warnings.length}`);
|
|
280
|
+
console.log(` ${console_chars_1.emoji.info} Info: ${infos.length}`);
|
|
281
|
+
// Print issues by type
|
|
282
|
+
for (const [type, issues] of byType) {
|
|
283
|
+
const icon = issues[0].severity === "error"
|
|
284
|
+
? `${console_chars_1.emoji.error}`
|
|
285
|
+
: issues[0].severity === "warning"
|
|
286
|
+
? `${console_chars_1.emoji.warning}`
|
|
287
|
+
: `${console_chars_1.emoji.info}`;
|
|
288
|
+
console.log(`\n${icon} ${type} (${issues.length} occurrences)`);
|
|
289
|
+
if (verbose) {
|
|
290
|
+
for (const issue of issues.slice(0, 5)) {
|
|
291
|
+
console.log(` ${issue.file}:${issue.line}`);
|
|
292
|
+
console.log(` ${issue.message}`);
|
|
293
|
+
console.log(` ${console_chars_1.emoji.hint} ${issue.suggestion}`);
|
|
294
|
+
if (issue.snippet) {
|
|
295
|
+
console.log(` ${console_chars_1.emoji.memo} ${issue.snippet}`);
|
|
296
|
+
}
|
|
297
|
+
console.log("");
|
|
298
|
+
}
|
|
299
|
+
if (issues.length > 5) {
|
|
300
|
+
console.log(` ... and ${issues.length - 5} more`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
console.log(` ${issues[0].message}`);
|
|
305
|
+
console.log(` ${console_chars_1.emoji.hint} ${issues[0].suggestion}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Print background info
|
|
309
|
+
console.log(`\n${console_chars_1.emoji.docs} Background:`);
|
|
310
|
+
console.log(` This project uses custom CSS variable names (--bg-primary,`);
|
|
311
|
+
console.log(` --text-primary, etc.) instead of shadcn defaults (--background,`);
|
|
312
|
+
console.log(` --foreground, etc.). Using shadcn-style classes results in`);
|
|
313
|
+
console.log(` undefined colors and invisible UI elements.`);
|
|
314
|
+
console.log("");
|
|
315
|
+
console.log(`${console_chars_1.emoji.hint} Common fixes:`);
|
|
316
|
+
console.log(` • bg-background → bg-white or bg-[var(--bg-primary)]`);
|
|
317
|
+
console.log(` • text-foreground → text-[var(--text-primary)]`);
|
|
318
|
+
console.log(` • bg-muted → bg-[var(--bg-tertiary)]`);
|
|
319
|
+
console.log(` • border-border → border-[var(--border-color)]`);
|
|
320
|
+
// Exit code
|
|
321
|
+
if (warnOnly) {
|
|
322
|
+
console.log(`\n${console_chars_1.emoji.warning} Running in warning mode - not blocking`);
|
|
323
|
+
process.exit(0);
|
|
324
|
+
}
|
|
325
|
+
if (errors.length > 0) {
|
|
326
|
+
console.log(`\n⛔ ${errors.length} blocking issue(s) found. Fix before deploying.`);
|
|
327
|
+
console.log(` These will cause invisible or broken UI elements.`);
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
console.log(`\n${console_chars_1.emoji.success} No blocking issues (warnings/info only)`);
|
|
331
|
+
process.exit(0);
|
|
332
|
+
}
|
|
333
|
+
// Allow direct execution
|
|
334
|
+
if (require.main === module) {
|
|
335
|
+
run().catch(console.error);
|
|
336
|
+
}
|
|
337
|
+
//# sourceMappingURL=undefined-tailwind-utilities.js.map
|