frontend-guardian 0.1.20 → 0.1.22
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.js +22 -16
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,12 @@ import { scanFiles, scanZip } from "@justinmoto/frontend-guardian-core";
|
|
|
9
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const EXT = [".js", ".jsx", ".ts", ".tsx", ".css"];
|
|
11
11
|
const IGNORE = ["node_modules", ".next", "dist", "build", ".git"];
|
|
12
|
+
const theme = {
|
|
13
|
+
primary: chalk.hex("#0080ff"),
|
|
14
|
+
primaryBold: chalk.hex("#0080ff").bold,
|
|
15
|
+
muted: chalk.hex("#5b8fb9"),
|
|
16
|
+
gray: chalk.gray,
|
|
17
|
+
};
|
|
12
18
|
function isCodeFile(name) {
|
|
13
19
|
return EXT.some((e) => name.toLowerCase().endsWith(e));
|
|
14
20
|
}
|
|
@@ -128,9 +134,9 @@ function wrapLine(prefix, text, continuationPrefix, firstLinePrefix) {
|
|
|
128
134
|
}
|
|
129
135
|
}
|
|
130
136
|
function printResult(projectName, result) {
|
|
131
|
-
const line =
|
|
137
|
+
const line = theme.gray("━".repeat(28));
|
|
132
138
|
console.log("\n" + line);
|
|
133
|
-
console.log(
|
|
139
|
+
console.log(" " + theme.primary("</>") + " " + theme.muted("Frontend") + " " + theme.primaryBold("Guardian"));
|
|
134
140
|
console.log(line + "\n");
|
|
135
141
|
let filesScanned = "filesScanned" in result ? result.filesScanned : 0;
|
|
136
142
|
if (filesScanned === 0 && (result.warnings.length > 0 || result.duplicates.length > 0)) {
|
|
@@ -139,11 +145,11 @@ function printResult(projectName, result) {
|
|
|
139
145
|
result.duplicates.forEach((d) => d.files.forEach((f) => files.add(f)));
|
|
140
146
|
filesScanned = files.size;
|
|
141
147
|
}
|
|
142
|
-
console.log(" Project: " + projectName);
|
|
143
|
-
console.log(" Files scanned: " + String(filesScanned));
|
|
148
|
+
console.log(" " + theme.muted("Project:") + " " + projectName);
|
|
149
|
+
console.log(" " + theme.muted("Files scanned:") + " " + String(filesScanned));
|
|
144
150
|
const scoreColorFn = scoreColor(result.score);
|
|
145
|
-
console.log(" Consistency Score: " + scoreColorFn(result.score + " / 100"));
|
|
146
|
-
console.log(
|
|
151
|
+
console.log(" " + theme.muted("Consistency Score:") + " " + scoreColorFn(result.score + " / 100"));
|
|
152
|
+
console.log(theme.gray(" (each warning −5, each duplicate group −10, from 100)") + "\n");
|
|
147
153
|
function locationStrings(w) {
|
|
148
154
|
if (w.locations?.length)
|
|
149
155
|
return w.locations.map((loc) => (loc.line != null && loc.line > 0 ? `${loc.file}:${loc.line}` : loc.file)).filter((s) => s.length > 0);
|
|
@@ -197,18 +203,18 @@ function printResult(projectName, result) {
|
|
|
197
203
|
return [...byFile.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([file, lines]) => lines.length > 0 ? `${file}: line ${[...lines].sort((a, b) => a - b).join(", line ")}` : file);
|
|
198
204
|
}
|
|
199
205
|
if (allIssues.length > 0) {
|
|
200
|
-
console.log(chalk.yellow(" ⚠ ") +
|
|
206
|
+
console.log(chalk.yellow(" ⚠ ") + theme.primaryBold("Issues Found") + "\n");
|
|
201
207
|
allIssues.forEach((issue, i) => {
|
|
202
|
-
console.log(" " + (i + 1) + ". " +
|
|
208
|
+
console.log(" " + (i + 1) + ". " + theme.primaryBold(issue.title));
|
|
203
209
|
if (issue.title === "Unused Imports" || issue.title === "Unused Variables") {
|
|
204
210
|
wrapLine(INDENT, issue.detected);
|
|
205
211
|
}
|
|
206
212
|
else {
|
|
207
|
-
wrapLine(INDENT + "Detected: ", issue.detected, INDENT, INDENT +
|
|
208
|
-
console.log(INDENT +
|
|
213
|
+
wrapLine(INDENT + "Detected: ", issue.detected, INDENT, INDENT + theme.primaryBold("Detected: "));
|
|
214
|
+
console.log(INDENT + theme.primaryBold("Suggestion: ") + issue.suggestion);
|
|
209
215
|
}
|
|
210
216
|
if (issue.fileLines.length > 0) {
|
|
211
|
-
console.log(INDENT +
|
|
217
|
+
console.log(INDENT + theme.primaryBold("Files:"));
|
|
212
218
|
const raw = STYLE_TITLES.has(issue.title) ? groupFileLines(issue.fileLines) : issue.fileLines.map((f) => (f.includes(":") ? f.replace(/:(\s*\d+)$/, ": line $1") : f));
|
|
213
219
|
raw.forEach((f) => wrapLine(FILES_INDENT, f, FILES_INDENT));
|
|
214
220
|
}
|
|
@@ -218,8 +224,8 @@ function printResult(projectName, result) {
|
|
|
218
224
|
console.log("");
|
|
219
225
|
}
|
|
220
226
|
console.log(line);
|
|
221
|
-
console.log(
|
|
222
|
-
console.log(
|
|
227
|
+
console.log(theme.primaryBold(" Scan Complete"));
|
|
228
|
+
console.log(theme.gray(" © Justinmoto · Frontend Guardian") + "\n");
|
|
223
229
|
}
|
|
224
230
|
const WORKFLOW_YAML = `name: Frontend Guardian
|
|
225
231
|
|
|
@@ -351,7 +357,7 @@ const DEFAULT_SUGGEST_API = "https://frontend-guardian.vercel.app/api/suggest";
|
|
|
351
357
|
async function getGeminiSuggestion(apiKey, issueTitle, detected) {
|
|
352
358
|
const ai = new GoogleGenAI({ apiKey });
|
|
353
359
|
const prompt = `Frontend Guardian found this issue: "${issueTitle}". Detected: ${detected.slice(0, 500)}. Give a brief concrete fix suggestion in 1-2 sentences. No preamble.`;
|
|
354
|
-
const res = await ai.models.generateContent({ model: "gemini-
|
|
360
|
+
const res = await ai.models.generateContent({ model: "gemini-2.0-flash", contents: prompt });
|
|
355
361
|
const r = res;
|
|
356
362
|
const text = typeof r?.text === "string" ? r.text : r?.candidates?.[0]?.content?.parts?.find((p) => typeof p?.text === "string")?.text ?? "";
|
|
357
363
|
return text.trim();
|
|
@@ -461,7 +467,7 @@ async function main() {
|
|
|
461
467
|
else
|
|
462
468
|
byTitle.set(key, byTitle.get(key) + "; " + detected);
|
|
463
469
|
}
|
|
464
|
-
console.log(
|
|
470
|
+
console.log(theme.primary("\n 🤖 AI suggestions\n"));
|
|
465
471
|
const spinner = ora("Getting suggestions...").start();
|
|
466
472
|
try {
|
|
467
473
|
for (const [title, detected] of byTitle.entries()) {
|
|
@@ -469,7 +475,7 @@ async function main() {
|
|
|
469
475
|
? await getGeminiSuggestion(apiKey, title, detected)
|
|
470
476
|
: await getSuggestFromBackend(title, detected);
|
|
471
477
|
spinner.stop();
|
|
472
|
-
console.log(" " +
|
|
478
|
+
console.log(" " + theme.primaryBold(title) + "\n " + theme.gray(suggestion) + "\n");
|
|
473
479
|
spinner.start("Getting suggestions...");
|
|
474
480
|
}
|
|
475
481
|
spinner.succeed("Done.");
|