uilint 0.2.72 → 0.2.73
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/index.js +461 -32
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import "./chunk-JPE27ROY.js";
|
|
26
26
|
|
|
27
27
|
// src/index.ts
|
|
28
|
-
import { Command as
|
|
28
|
+
import { Command as Command8 } from "commander";
|
|
29
29
|
|
|
30
30
|
// src/commands/scan.ts
|
|
31
31
|
import { dirname, resolve as resolve2 } from "path";
|
|
@@ -261,8 +261,8 @@ async function initializeLangfuseIfEnabled() {
|
|
|
261
261
|
},
|
|
262
262
|
{ asType: "generation" }
|
|
263
263
|
);
|
|
264
|
-
await new Promise((
|
|
265
|
-
resolveTrace =
|
|
264
|
+
await new Promise((resolve11) => {
|
|
265
|
+
resolveTrace = resolve11;
|
|
266
266
|
});
|
|
267
267
|
if (endData && generationRef) {
|
|
268
268
|
const usageDetails = endData.usage ? Object.fromEntries(
|
|
@@ -1178,14 +1178,14 @@ import {
|
|
|
1178
1178
|
} from "uilint-core";
|
|
1179
1179
|
import { ensureOllamaReady as ensureOllamaReady3 } from "uilint-core/node";
|
|
1180
1180
|
async function readStdin2() {
|
|
1181
|
-
return new Promise((
|
|
1181
|
+
return new Promise((resolve11) => {
|
|
1182
1182
|
let data = "";
|
|
1183
1183
|
const rl = createInterface({ input: process.stdin });
|
|
1184
1184
|
rl.on("line", (line) => {
|
|
1185
1185
|
data += line;
|
|
1186
1186
|
});
|
|
1187
1187
|
rl.on("close", () => {
|
|
1188
|
-
|
|
1188
|
+
resolve11(data);
|
|
1189
1189
|
});
|
|
1190
1190
|
});
|
|
1191
1191
|
}
|
|
@@ -2699,12 +2699,12 @@ async function serve(options) {
|
|
|
2699
2699
|
`UILint WebSocket server running on ${pc.cyan(`ws://localhost:${port}`)}`
|
|
2700
2700
|
);
|
|
2701
2701
|
logInfo("Press Ctrl+C to stop");
|
|
2702
|
-
await new Promise((
|
|
2702
|
+
await new Promise((resolve11) => {
|
|
2703
2703
|
process.on("SIGINT", () => {
|
|
2704
2704
|
logInfo("Shutting down...");
|
|
2705
2705
|
wss.close();
|
|
2706
2706
|
fileWatcher?.close();
|
|
2707
|
-
|
|
2707
|
+
resolve11();
|
|
2708
2708
|
});
|
|
2709
2709
|
});
|
|
2710
2710
|
}
|
|
@@ -3248,7 +3248,7 @@ function parseOptionsJson(optionsStr) {
|
|
|
3248
3248
|
}
|
|
3249
3249
|
}
|
|
3250
3250
|
async function sendConfigMessage(port, key, value) {
|
|
3251
|
-
return new Promise((
|
|
3251
|
+
return new Promise((resolve11) => {
|
|
3252
3252
|
const url = `ws://localhost:${port}`;
|
|
3253
3253
|
const ws = new WebSocket2(url);
|
|
3254
3254
|
let resolved = false;
|
|
@@ -3256,7 +3256,7 @@ async function sendConfigMessage(port, key, value) {
|
|
|
3256
3256
|
if (!resolved) {
|
|
3257
3257
|
resolved = true;
|
|
3258
3258
|
ws.close();
|
|
3259
|
-
|
|
3259
|
+
resolve11(false);
|
|
3260
3260
|
}
|
|
3261
3261
|
}, 5e3);
|
|
3262
3262
|
ws.on("open", () => {
|
|
@@ -3267,7 +3267,7 @@ async function sendConfigMessage(port, key, value) {
|
|
|
3267
3267
|
resolved = true;
|
|
3268
3268
|
clearTimeout(timeout);
|
|
3269
3269
|
ws.close();
|
|
3270
|
-
|
|
3270
|
+
resolve11(true);
|
|
3271
3271
|
}
|
|
3272
3272
|
}, 100);
|
|
3273
3273
|
});
|
|
@@ -3275,13 +3275,13 @@ async function sendConfigMessage(port, key, value) {
|
|
|
3275
3275
|
if (!resolved) {
|
|
3276
3276
|
resolved = true;
|
|
3277
3277
|
clearTimeout(timeout);
|
|
3278
|
-
|
|
3278
|
+
resolve11(false);
|
|
3279
3279
|
}
|
|
3280
3280
|
});
|
|
3281
3281
|
});
|
|
3282
3282
|
}
|
|
3283
3283
|
async function sendRuleConfigMessage(port, ruleId, severity, options) {
|
|
3284
|
-
return new Promise((
|
|
3284
|
+
return new Promise((resolve11) => {
|
|
3285
3285
|
const url = `ws://localhost:${port}`;
|
|
3286
3286
|
const ws = new WebSocket2(url);
|
|
3287
3287
|
let resolved = false;
|
|
@@ -3290,7 +3290,7 @@ async function sendRuleConfigMessage(port, ruleId, severity, options) {
|
|
|
3290
3290
|
if (!resolved) {
|
|
3291
3291
|
resolved = true;
|
|
3292
3292
|
ws.close();
|
|
3293
|
-
|
|
3293
|
+
resolve11({ success: false, error: "Request timed out" });
|
|
3294
3294
|
}
|
|
3295
3295
|
}, 1e4);
|
|
3296
3296
|
ws.on("open", () => {
|
|
@@ -3311,7 +3311,7 @@ async function sendRuleConfigMessage(port, ruleId, severity, options) {
|
|
|
3311
3311
|
resolved = true;
|
|
3312
3312
|
clearTimeout(timeout);
|
|
3313
3313
|
ws.close();
|
|
3314
|
-
|
|
3314
|
+
resolve11({
|
|
3315
3315
|
success: msg.success,
|
|
3316
3316
|
error: msg.error
|
|
3317
3317
|
});
|
|
@@ -3324,7 +3324,7 @@ async function sendRuleConfigMessage(port, ruleId, severity, options) {
|
|
|
3324
3324
|
if (!resolved) {
|
|
3325
3325
|
resolved = true;
|
|
3326
3326
|
clearTimeout(timeout);
|
|
3327
|
-
|
|
3327
|
+
resolve11({ success: false, error: "Connection error" });
|
|
3328
3328
|
}
|
|
3329
3329
|
});
|
|
3330
3330
|
});
|
|
@@ -3720,13 +3720,441 @@ function createDuplicatesCommand() {
|
|
|
3720
3720
|
}
|
|
3721
3721
|
|
|
3722
3722
|
// src/index.ts
|
|
3723
|
-
import { readFileSync as
|
|
3724
|
-
import { dirname as
|
|
3723
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
3724
|
+
import { dirname as dirname10, join as join6 } from "path";
|
|
3725
3725
|
import { fileURLToPath } from "url";
|
|
3726
3726
|
|
|
3727
|
-
// src/commands/
|
|
3727
|
+
// src/commands/manifest/index.ts
|
|
3728
3728
|
import { Command as Command6 } from "commander";
|
|
3729
|
-
import {
|
|
3729
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
3730
|
+
import { dirname as dirname9, resolve as resolve10 } from "path";
|
|
3731
|
+
|
|
3732
|
+
// src/commands/manifest/generator.ts
|
|
3733
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
3734
|
+
import { resolve as resolve9, dirname as dirname8, relative as relative6 } from "path";
|
|
3735
|
+
import { glob } from "glob";
|
|
3736
|
+
import { execSync } from "child_process";
|
|
3737
|
+
import { findWorkspaceRoot as findWorkspaceRoot5 } from "uilint-core/node";
|
|
3738
|
+
import { ruleRegistry as ruleRegistry2 } from "uilint-eslint";
|
|
3739
|
+
|
|
3740
|
+
// src/utils/eslint-utils.ts
|
|
3741
|
+
import { existsSync as existsSync7, readFileSync as readFileSync3 } from "fs";
|
|
3742
|
+
import { createRequire as createRequire2 } from "module";
|
|
3743
|
+
import { dirname as dirname7, resolve as resolve8, relative as relative5, join as join5 } from "path";
|
|
3744
|
+
var ESLINT_CONFIG_FILES2 = [
|
|
3745
|
+
// Flat config (ESLint v9+)
|
|
3746
|
+
"eslint.config.js",
|
|
3747
|
+
"eslint.config.mjs",
|
|
3748
|
+
"eslint.config.cjs",
|
|
3749
|
+
"eslint.config.ts",
|
|
3750
|
+
// Legacy config
|
|
3751
|
+
".eslintrc",
|
|
3752
|
+
".eslintrc.js",
|
|
3753
|
+
".eslintrc.cjs",
|
|
3754
|
+
".eslintrc.json",
|
|
3755
|
+
".eslintrc.yaml",
|
|
3756
|
+
".eslintrc.yml"
|
|
3757
|
+
];
|
|
3758
|
+
function buildLineStarts2(code) {
|
|
3759
|
+
const starts = [0];
|
|
3760
|
+
for (let i = 0; i < code.length; i++) {
|
|
3761
|
+
if (code.charCodeAt(i) === 10) starts.push(i + 1);
|
|
3762
|
+
}
|
|
3763
|
+
return starts;
|
|
3764
|
+
}
|
|
3765
|
+
function offsetFromLineCol2(lineStarts, line1, col0, codeLength) {
|
|
3766
|
+
const lineIndex = Math.max(0, Math.min(lineStarts.length - 1, line1 - 1));
|
|
3767
|
+
const base = lineStarts[lineIndex] ?? 0;
|
|
3768
|
+
return Math.max(0, Math.min(codeLength, base + Math.max(0, col0)));
|
|
3769
|
+
}
|
|
3770
|
+
function buildJsxElementSpans2(code, dataLocFile) {
|
|
3771
|
+
const localRequire2 = createRequire2(import.meta.url);
|
|
3772
|
+
const { parse: parse3 } = localRequire2("@typescript-eslint/typescript-estree");
|
|
3773
|
+
const ast = parse3(code, {
|
|
3774
|
+
loc: true,
|
|
3775
|
+
range: true,
|
|
3776
|
+
jsx: true,
|
|
3777
|
+
comment: false,
|
|
3778
|
+
errorOnUnknownASTType: false
|
|
3779
|
+
});
|
|
3780
|
+
const spans = [];
|
|
3781
|
+
function walk(node) {
|
|
3782
|
+
if (!node || typeof node !== "object") return;
|
|
3783
|
+
if (node.type === "JSXElement") {
|
|
3784
|
+
const range = node.range;
|
|
3785
|
+
const opening = node.openingElement;
|
|
3786
|
+
const loc = opening?.loc?.start;
|
|
3787
|
+
if (range && typeof range[0] === "number" && typeof range[1] === "number" && loc && typeof loc.line === "number" && typeof loc.column === "number") {
|
|
3788
|
+
const dataLoc = `${dataLocFile}:${loc.line}:${loc.column}`;
|
|
3789
|
+
spans.push({ start: range[0], end: range[1], dataLoc });
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
for (const key of Object.keys(node)) {
|
|
3793
|
+
const child = node[key];
|
|
3794
|
+
if (Array.isArray(child)) {
|
|
3795
|
+
for (const item of child) walk(item);
|
|
3796
|
+
} else if (child && typeof child === "object") {
|
|
3797
|
+
walk(child);
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
walk(ast);
|
|
3802
|
+
spans.sort((a, b) => a.end - a.start - (b.end - b.start));
|
|
3803
|
+
return spans;
|
|
3804
|
+
}
|
|
3805
|
+
function mapMessageToDataLoc2(params) {
|
|
3806
|
+
const col0 = typeof params.messageCol1 === "number" ? Math.max(0, params.messageCol1 - 1) : 0;
|
|
3807
|
+
const offset = offsetFromLineCol2(
|
|
3808
|
+
params.lineStarts,
|
|
3809
|
+
params.messageLine1,
|
|
3810
|
+
col0,
|
|
3811
|
+
params.codeLength
|
|
3812
|
+
);
|
|
3813
|
+
for (const s of params.spans) {
|
|
3814
|
+
if (s.start <= offset && offset < s.end) return s.dataLoc;
|
|
3815
|
+
}
|
|
3816
|
+
return void 0;
|
|
3817
|
+
}
|
|
3818
|
+
function normalizePathSlashes2(p) {
|
|
3819
|
+
return p.replace(/\\/g, "/");
|
|
3820
|
+
}
|
|
3821
|
+
function normalizeDataLocFilePath2(absoluteFilePath, projectCwd) {
|
|
3822
|
+
const abs = normalizePathSlashes2(resolve8(absoluteFilePath));
|
|
3823
|
+
const cwd = normalizePathSlashes2(resolve8(projectCwd));
|
|
3824
|
+
if (abs === cwd || abs.startsWith(cwd + "/")) {
|
|
3825
|
+
return normalizePathSlashes2(relative5(cwd, abs));
|
|
3826
|
+
}
|
|
3827
|
+
return abs;
|
|
3828
|
+
}
|
|
3829
|
+
function findESLintCwd2(startDir) {
|
|
3830
|
+
let dir = startDir;
|
|
3831
|
+
for (let i = 0; i < 30; i++) {
|
|
3832
|
+
for (const cfg of ESLINT_CONFIG_FILES2) {
|
|
3833
|
+
if (existsSync7(join5(dir, cfg))) return dir;
|
|
3834
|
+
}
|
|
3835
|
+
if (existsSync7(join5(dir, "package.json"))) return dir;
|
|
3836
|
+
const parent = dirname7(dir);
|
|
3837
|
+
if (parent === dir) break;
|
|
3838
|
+
dir = parent;
|
|
3839
|
+
}
|
|
3840
|
+
return startDir;
|
|
3841
|
+
}
|
|
3842
|
+
var eslintInstances2 = /* @__PURE__ */ new Map();
|
|
3843
|
+
async function getESLintForProject2(projectCwd) {
|
|
3844
|
+
const cached = eslintInstances2.get(projectCwd);
|
|
3845
|
+
if (cached) return cached;
|
|
3846
|
+
try {
|
|
3847
|
+
const req = createRequire2(join5(projectCwd, "package.json"));
|
|
3848
|
+
const mod = req("eslint");
|
|
3849
|
+
const ESLintCtor = mod?.ESLint ?? mod?.default?.ESLint ?? mod?.default ?? mod;
|
|
3850
|
+
if (!ESLintCtor) return null;
|
|
3851
|
+
const eslint = new ESLintCtor({ cwd: projectCwd });
|
|
3852
|
+
eslintInstances2.set(projectCwd, eslint);
|
|
3853
|
+
return eslint;
|
|
3854
|
+
} catch {
|
|
3855
|
+
return null;
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
async function lintFileWithDataLoc(absolutePath, projectCwd, onProgress) {
|
|
3859
|
+
const progress = onProgress ?? (() => {
|
|
3860
|
+
});
|
|
3861
|
+
if (!existsSync7(absolutePath)) {
|
|
3862
|
+
progress(`File not found: ${absolutePath}`);
|
|
3863
|
+
return [];
|
|
3864
|
+
}
|
|
3865
|
+
progress(`Resolving ESLint project... ${projectCwd}`);
|
|
3866
|
+
const eslint = await getESLintForProject2(projectCwd);
|
|
3867
|
+
if (!eslint) {
|
|
3868
|
+
progress("ESLint not available");
|
|
3869
|
+
return [];
|
|
3870
|
+
}
|
|
3871
|
+
try {
|
|
3872
|
+
progress("Running ESLint...");
|
|
3873
|
+
const results = await eslint.lintFiles([absolutePath]);
|
|
3874
|
+
const messages = Array.isArray(results) && results.length > 0 ? results[0].messages || [] : [];
|
|
3875
|
+
const dataLocFile = normalizeDataLocFilePath2(absolutePath, projectCwd);
|
|
3876
|
+
let spans = [];
|
|
3877
|
+
let lineStarts = [];
|
|
3878
|
+
let codeLength = 0;
|
|
3879
|
+
try {
|
|
3880
|
+
progress("Building JSX map...");
|
|
3881
|
+
const code = readFileSync3(absolutePath, "utf-8");
|
|
3882
|
+
codeLength = code.length;
|
|
3883
|
+
lineStarts = buildLineStarts2(code);
|
|
3884
|
+
spans = buildJsxElementSpans2(code, dataLocFile);
|
|
3885
|
+
progress(`JSX map: ${spans.length} element(s)`);
|
|
3886
|
+
} catch (e) {
|
|
3887
|
+
progress("JSX map failed (falling back to unmapped issues)");
|
|
3888
|
+
spans = [];
|
|
3889
|
+
lineStarts = [];
|
|
3890
|
+
codeLength = 0;
|
|
3891
|
+
}
|
|
3892
|
+
const issues = messages.filter((m) => typeof m?.message === "string").map((m) => {
|
|
3893
|
+
const line = typeof m.line === "number" ? m.line : 1;
|
|
3894
|
+
const column = typeof m.column === "number" ? m.column : void 0;
|
|
3895
|
+
const mappedDataLoc = spans.length > 0 && lineStarts.length > 0 && codeLength > 0 ? mapMessageToDataLoc2({
|
|
3896
|
+
spans,
|
|
3897
|
+
lineStarts,
|
|
3898
|
+
codeLength,
|
|
3899
|
+
messageLine1: line,
|
|
3900
|
+
messageCol1: column
|
|
3901
|
+
}) : void 0;
|
|
3902
|
+
return {
|
|
3903
|
+
line,
|
|
3904
|
+
column,
|
|
3905
|
+
message: m.message,
|
|
3906
|
+
ruleId: typeof m.ruleId === "string" ? m.ruleId : void 0,
|
|
3907
|
+
dataLoc: mappedDataLoc
|
|
3908
|
+
};
|
|
3909
|
+
});
|
|
3910
|
+
const mappedCount = issues.filter((i) => Boolean(i.dataLoc)).length;
|
|
3911
|
+
if (issues.length > 0) {
|
|
3912
|
+
progress(`Mapped ${mappedCount}/${issues.length} issue(s) to JSX elements`);
|
|
3913
|
+
}
|
|
3914
|
+
return issues;
|
|
3915
|
+
} catch (error) {
|
|
3916
|
+
progress(`ESLint failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3917
|
+
return [];
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
function extractSourceSnippet(code, centerLine, contextLines = 3) {
|
|
3921
|
+
const allLines = code.split("\n");
|
|
3922
|
+
const startLine = Math.max(1, centerLine - contextLines);
|
|
3923
|
+
const endLine = Math.min(allLines.length, centerLine + contextLines);
|
|
3924
|
+
return {
|
|
3925
|
+
lines: allLines.slice(startLine - 1, endLine),
|
|
3926
|
+
startLine,
|
|
3927
|
+
endLine
|
|
3928
|
+
};
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3931
|
+
// src/commands/manifest/generator.ts
|
|
3932
|
+
var DEFAULT_INCLUDE = ["**/*.tsx", "**/*.jsx"];
|
|
3933
|
+
var DEFAULT_EXCLUDE = [
|
|
3934
|
+
"node_modules/**",
|
|
3935
|
+
"dist/**",
|
|
3936
|
+
".next/**",
|
|
3937
|
+
"build/**",
|
|
3938
|
+
"coverage/**",
|
|
3939
|
+
".uilint/**",
|
|
3940
|
+
"**/*.test.tsx",
|
|
3941
|
+
"**/*.test.jsx",
|
|
3942
|
+
"**/*.spec.tsx",
|
|
3943
|
+
"**/*.spec.jsx"
|
|
3944
|
+
];
|
|
3945
|
+
function getGitInfo(cwd) {
|
|
3946
|
+
try {
|
|
3947
|
+
const commitSha = execSync("git rev-parse HEAD", {
|
|
3948
|
+
cwd,
|
|
3949
|
+
encoding: "utf-8",
|
|
3950
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3951
|
+
}).trim();
|
|
3952
|
+
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
3953
|
+
cwd,
|
|
3954
|
+
encoding: "utf-8",
|
|
3955
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3956
|
+
}).trim();
|
|
3957
|
+
return { commitSha, branch };
|
|
3958
|
+
} catch {
|
|
3959
|
+
return {};
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
function buildRuleMetadata(appRoot) {
|
|
3963
|
+
const eslintConfigPath = findEslintConfigFile(appRoot);
|
|
3964
|
+
const currentRuleConfigs = eslintConfigPath ? readRuleConfigsFromConfig(eslintConfigPath) : /* @__PURE__ */ new Map();
|
|
3965
|
+
return ruleRegistry2.filter((rule) => currentRuleConfigs.has(rule.id)).map((rule) => {
|
|
3966
|
+
const currentConfig = currentRuleConfigs.get(rule.id);
|
|
3967
|
+
return {
|
|
3968
|
+
id: rule.id,
|
|
3969
|
+
name: rule.name,
|
|
3970
|
+
description: rule.description,
|
|
3971
|
+
category: rule.category,
|
|
3972
|
+
defaultSeverity: rule.defaultSeverity,
|
|
3973
|
+
currentSeverity: currentConfig?.severity,
|
|
3974
|
+
docs: rule.docs,
|
|
3975
|
+
optionSchema: rule.optionSchema
|
|
3976
|
+
};
|
|
3977
|
+
});
|
|
3978
|
+
}
|
|
3979
|
+
async function generateManifest(options = {}) {
|
|
3980
|
+
const cwd = resolve9(options.cwd ?? process.cwd());
|
|
3981
|
+
const include = options.include ?? DEFAULT_INCLUDE;
|
|
3982
|
+
const exclude = options.exclude ?? DEFAULT_EXCLUDE;
|
|
3983
|
+
const includeSnippets = options.includeSnippets ?? true;
|
|
3984
|
+
const snippetContextLines = options.snippetContextLines ?? 3;
|
|
3985
|
+
const onProgress = options.onProgress ?? (() => {
|
|
3986
|
+
});
|
|
3987
|
+
onProgress("Finding workspace root...");
|
|
3988
|
+
const workspaceRoot = findWorkspaceRoot5(cwd);
|
|
3989
|
+
const appRoot = cwd;
|
|
3990
|
+
onProgress("Scanning for JSX/TSX files...");
|
|
3991
|
+
const files = await glob(include, {
|
|
3992
|
+
cwd,
|
|
3993
|
+
ignore: exclude,
|
|
3994
|
+
absolute: true,
|
|
3995
|
+
nodir: true
|
|
3996
|
+
});
|
|
3997
|
+
onProgress(`Found ${files.length} files to scan`);
|
|
3998
|
+
const gitInfo = getGitInfo(cwd);
|
|
3999
|
+
const rules = buildRuleMetadata(appRoot);
|
|
4000
|
+
onProgress(`Loaded ${rules.length} rule definitions`);
|
|
4001
|
+
const manifestFiles = [];
|
|
4002
|
+
let totalIssues = 0;
|
|
4003
|
+
let errorCount = 0;
|
|
4004
|
+
let warnCount = 0;
|
|
4005
|
+
for (let i = 0; i < files.length; i++) {
|
|
4006
|
+
const absolutePath = files[i];
|
|
4007
|
+
const relativePath = relative6(cwd, absolutePath);
|
|
4008
|
+
onProgress(`Linting ${relativePath}...`, i + 1, files.length);
|
|
4009
|
+
const fileDir = dirname8(absolutePath);
|
|
4010
|
+
const projectCwd = findESLintCwd2(fileDir);
|
|
4011
|
+
const issues = await lintFileWithDataLoc(absolutePath, projectCwd);
|
|
4012
|
+
if (issues.length === 0) continue;
|
|
4013
|
+
const manifestIssues = issues.filter((issue) => Boolean(issue.dataLoc)).map((issue) => ({
|
|
4014
|
+
line: issue.line,
|
|
4015
|
+
column: issue.column,
|
|
4016
|
+
message: issue.message,
|
|
4017
|
+
ruleId: issue.ruleId,
|
|
4018
|
+
dataLoc: issue.dataLoc
|
|
4019
|
+
}));
|
|
4020
|
+
if (manifestIssues.length === 0) continue;
|
|
4021
|
+
let snippets;
|
|
4022
|
+
if (includeSnippets) {
|
|
4023
|
+
try {
|
|
4024
|
+
const code = readFileSync4(absolutePath, "utf-8");
|
|
4025
|
+
snippets = {};
|
|
4026
|
+
const issuesByDataLoc = /* @__PURE__ */ new Map();
|
|
4027
|
+
for (const issue of manifestIssues) {
|
|
4028
|
+
if (!issuesByDataLoc.has(issue.dataLoc)) {
|
|
4029
|
+
issuesByDataLoc.set(issue.dataLoc, issue);
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
for (const [dataLoc, issue] of issuesByDataLoc) {
|
|
4033
|
+
snippets[dataLoc] = extractSourceSnippet(code, issue.line, snippetContextLines);
|
|
4034
|
+
}
|
|
4035
|
+
} catch {
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
for (const issue of manifestIssues) {
|
|
4039
|
+
if (issue.ruleId) {
|
|
4040
|
+
const rule = rules.find((r) => `uilint/${r.id}` === issue.ruleId || r.id === issue.ruleId);
|
|
4041
|
+
const severity = rule?.currentSeverity ?? rule?.defaultSeverity ?? "warn";
|
|
4042
|
+
if (severity === "error") {
|
|
4043
|
+
errorCount++;
|
|
4044
|
+
} else {
|
|
4045
|
+
warnCount++;
|
|
4046
|
+
}
|
|
4047
|
+
} else {
|
|
4048
|
+
warnCount++;
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
totalIssues += manifestIssues.length;
|
|
4052
|
+
const dataLocFilePath = normalizeDataLocFilePath2(absolutePath, projectCwd);
|
|
4053
|
+
manifestFiles.push({
|
|
4054
|
+
filePath: dataLocFilePath,
|
|
4055
|
+
issues: manifestIssues,
|
|
4056
|
+
snippets
|
|
4057
|
+
});
|
|
4058
|
+
}
|
|
4059
|
+
onProgress(`Scan complete: ${totalIssues} issues in ${manifestFiles.length} files`);
|
|
4060
|
+
return {
|
|
4061
|
+
version: "1.0",
|
|
4062
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4063
|
+
workspaceRoot,
|
|
4064
|
+
appRoot,
|
|
4065
|
+
commitSha: gitInfo.commitSha,
|
|
4066
|
+
branch: gitInfo.branch,
|
|
4067
|
+
files: manifestFiles,
|
|
4068
|
+
rules,
|
|
4069
|
+
summary: {
|
|
4070
|
+
filesScanned: files.length,
|
|
4071
|
+
filesWithIssues: manifestFiles.length,
|
|
4072
|
+
totalIssues,
|
|
4073
|
+
bySeverity: {
|
|
4074
|
+
error: errorCount,
|
|
4075
|
+
warn: warnCount
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
4078
|
+
};
|
|
4079
|
+
}
|
|
4080
|
+
|
|
4081
|
+
// src/commands/manifest/index.ts
|
|
4082
|
+
function createManifestCommand() {
|
|
4083
|
+
const cmd = new Command6("build-manifest").description("Generate static lint manifest for production deployment").option("-o, --output <path>", "Output path for manifest", ".uilint/manifest.json").option("--include <patterns...>", "File patterns to include").option("--exclude <patterns...>", "File patterns to exclude").option("--no-snippets", "Exclude source code snippets from manifest").option("--context-lines <n>", "Number of context lines for snippets", "3").option("--pretty", "Pretty-print JSON output").option("--quiet", "Suppress progress output").action(async (options) => {
|
|
4084
|
+
await buildManifest({
|
|
4085
|
+
output: options.output,
|
|
4086
|
+
include: options.include,
|
|
4087
|
+
exclude: options.exclude,
|
|
4088
|
+
includeSnippets: options.snippets !== false,
|
|
4089
|
+
contextLines: parseInt(options.contextLines, 10),
|
|
4090
|
+
pretty: options.pretty,
|
|
4091
|
+
quiet: options.quiet
|
|
4092
|
+
});
|
|
4093
|
+
});
|
|
4094
|
+
return cmd;
|
|
4095
|
+
}
|
|
4096
|
+
async function buildManifest(options) {
|
|
4097
|
+
const startTime = Date.now();
|
|
4098
|
+
const outputPath = resolve10(process.cwd(), options.output);
|
|
4099
|
+
if (!options.quiet) {
|
|
4100
|
+
logInfo(`Building lint manifest...`);
|
|
4101
|
+
logInfo(`Output: ${pc.dim(outputPath)}`);
|
|
4102
|
+
}
|
|
4103
|
+
try {
|
|
4104
|
+
const manifest = await generateManifest({
|
|
4105
|
+
cwd: process.cwd(),
|
|
4106
|
+
include: options.include,
|
|
4107
|
+
exclude: options.exclude,
|
|
4108
|
+
includeSnippets: options.includeSnippets,
|
|
4109
|
+
snippetContextLines: options.contextLines,
|
|
4110
|
+
onProgress: options.quiet ? void 0 : (message, current, total) => {
|
|
4111
|
+
if (current !== void 0 && total !== void 0) {
|
|
4112
|
+
logInfo(` [${current}/${total}] ${message}`);
|
|
4113
|
+
} else {
|
|
4114
|
+
logInfo(` ${message}`);
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
});
|
|
4118
|
+
const outputDir = dirname9(outputPath);
|
|
4119
|
+
if (!existsSync9(outputDir)) {
|
|
4120
|
+
mkdirSync6(outputDir, { recursive: true });
|
|
4121
|
+
}
|
|
4122
|
+
const json = options.pretty ? JSON.stringify(manifest, null, 2) : JSON.stringify(manifest);
|
|
4123
|
+
writeFileSync6(outputPath, json, "utf-8");
|
|
4124
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
4125
|
+
const sizeKb = Math.round(Buffer.byteLength(json) / 1024);
|
|
4126
|
+
if (!options.quiet) {
|
|
4127
|
+
logSuccess(`Manifest generated in ${elapsed}s`);
|
|
4128
|
+
logInfo(` Files scanned: ${pc.bold(String(manifest.summary.filesScanned))}`);
|
|
4129
|
+
logInfo(` Files with issues: ${pc.bold(String(manifest.summary.filesWithIssues))}`);
|
|
4130
|
+
logInfo(` Total issues: ${pc.bold(String(manifest.summary.totalIssues))}`);
|
|
4131
|
+
if (manifest.summary.bySeverity.error > 0) {
|
|
4132
|
+
logInfo(` Errors: ${pc.red(String(manifest.summary.bySeverity.error))}`);
|
|
4133
|
+
}
|
|
4134
|
+
if (manifest.summary.bySeverity.warn > 0) {
|
|
4135
|
+
logInfo(` Warnings: ${pc.yellow(String(manifest.summary.bySeverity.warn))}`);
|
|
4136
|
+
}
|
|
4137
|
+
logInfo(` Output size: ${pc.dim(`${sizeKb}kb`)}`);
|
|
4138
|
+
if (manifest.commitSha) {
|
|
4139
|
+
logInfo(` Git commit: ${pc.dim(manifest.commitSha.substring(0, 7))}`);
|
|
4140
|
+
}
|
|
4141
|
+
if (manifest.branch) {
|
|
4142
|
+
logInfo(` Git branch: ${pc.dim(manifest.branch)}`);
|
|
4143
|
+
}
|
|
4144
|
+
logSuccess(`Wrote ${pc.cyan(options.output)}`);
|
|
4145
|
+
}
|
|
4146
|
+
if (manifest.summary.bySeverity.error > 0) {
|
|
4147
|
+
process.exitCode = 1;
|
|
4148
|
+
}
|
|
4149
|
+
} catch (error) {
|
|
4150
|
+
logError(`Failed to generate manifest: ${error instanceof Error ? error.message : String(error)}`);
|
|
4151
|
+
process.exit(1);
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
|
|
4155
|
+
// src/commands/socket/index.ts
|
|
4156
|
+
import { Command as Command7 } from "commander";
|
|
4157
|
+
import { readFileSync as readFileSync5, existsSync as existsSync10 } from "fs";
|
|
3730
4158
|
import chalk6 from "chalk";
|
|
3731
4159
|
|
|
3732
4160
|
// src/commands/socket/client.ts
|
|
@@ -3753,7 +4181,7 @@ var SocketClient = class {
|
|
|
3753
4181
|
async connect() {
|
|
3754
4182
|
if (this.connected) return;
|
|
3755
4183
|
if (this.connectionPromise) return this.connectionPromise;
|
|
3756
|
-
this.connectionPromise = new Promise((
|
|
4184
|
+
this.connectionPromise = new Promise((resolve11, reject) => {
|
|
3757
4185
|
const timeout = setTimeout(() => {
|
|
3758
4186
|
reject(new Error(`Connection timeout to ${this.url}`));
|
|
3759
4187
|
}, this.connectTimeout);
|
|
@@ -3762,7 +4190,7 @@ var SocketClient = class {
|
|
|
3762
4190
|
clearTimeout(timeout);
|
|
3763
4191
|
this.connected = true;
|
|
3764
4192
|
this.log("Connected to", this.url);
|
|
3765
|
-
|
|
4193
|
+
resolve11();
|
|
3766
4194
|
});
|
|
3767
4195
|
this.ws.on("message", (data) => {
|
|
3768
4196
|
try {
|
|
@@ -3838,7 +4266,7 @@ var SocketClient = class {
|
|
|
3838
4266
|
this.messageQueue = this.messageQueue.filter((m) => m !== existing);
|
|
3839
4267
|
return Promise.resolve(existing);
|
|
3840
4268
|
}
|
|
3841
|
-
return new Promise((
|
|
4269
|
+
return new Promise((resolve11, reject) => {
|
|
3842
4270
|
const timeoutId = setTimeout(() => {
|
|
3843
4271
|
this.pendingRequests = this.pendingRequests.filter(
|
|
3844
4272
|
(r) => r.timeout !== timeoutId
|
|
@@ -3847,7 +4275,7 @@ var SocketClient = class {
|
|
|
3847
4275
|
}, timeout);
|
|
3848
4276
|
this.pendingRequests.push({
|
|
3849
4277
|
predicate,
|
|
3850
|
-
resolve:
|
|
4278
|
+
resolve: resolve11,
|
|
3851
4279
|
reject,
|
|
3852
4280
|
timeout: timeoutId
|
|
3853
4281
|
});
|
|
@@ -4188,14 +4616,14 @@ Available commands:
|
|
|
4188
4616
|
console.log(chalk6.red("Usage: vision:analyze <route> <manifestFile> [screenshotFile]"));
|
|
4189
4617
|
} else {
|
|
4190
4618
|
const manifestPath = args[1];
|
|
4191
|
-
if (!
|
|
4619
|
+
if (!existsSync10(manifestPath)) {
|
|
4192
4620
|
console.log(chalk6.red(`Manifest file not found: ${manifestPath}`));
|
|
4193
4621
|
} else {
|
|
4194
|
-
const manifest = JSON.parse(
|
|
4622
|
+
const manifest = JSON.parse(readFileSync5(manifestPath, "utf-8"));
|
|
4195
4623
|
const params = { route: args[0], manifest };
|
|
4196
4624
|
if (args[2]) {
|
|
4197
|
-
if (
|
|
4198
|
-
const imageBuffer =
|
|
4625
|
+
if (existsSync10(args[2])) {
|
|
4626
|
+
const imageBuffer = readFileSync5(args[2]);
|
|
4199
4627
|
params.screenshot = imageBuffer.toString("base64");
|
|
4200
4628
|
} else {
|
|
4201
4629
|
console.log(chalk6.red(`Screenshot file not found: ${args[2]}`));
|
|
@@ -4309,7 +4737,7 @@ async function runListen(client, options, filter) {
|
|
|
4309
4737
|
});
|
|
4310
4738
|
}
|
|
4311
4739
|
function createSocketCommand() {
|
|
4312
|
-
const cmd = new
|
|
4740
|
+
const cmd = new Command7("socket").description("Interact with the UILint socket server").option("-p, --port <number>", "Socket server port", "9234").option("-d, --debug", "Enable debug logging", false).option("-j, --json", "Output JSON format", false).option("-t, --timeout <ms>", "Request timeout in milliseconds", "30000");
|
|
4313
4741
|
cmd.action(async (cmdOptions) => {
|
|
4314
4742
|
const options = {
|
|
4315
4743
|
port: parseInt(cmdOptions.port, 10),
|
|
@@ -4456,12 +4884,12 @@ function assertNodeVersion(minMajor, minMinor) {
|
|
|
4456
4884
|
}
|
|
4457
4885
|
}
|
|
4458
4886
|
assertNodeVersion(20, 19);
|
|
4459
|
-
var program = new
|
|
4887
|
+
var program = new Command8();
|
|
4460
4888
|
function getCLIVersion() {
|
|
4461
4889
|
try {
|
|
4462
|
-
const __dirname =
|
|
4463
|
-
const pkgPath =
|
|
4464
|
-
const pkg = JSON.parse(
|
|
4890
|
+
const __dirname = dirname10(fileURLToPath(import.meta.url));
|
|
4891
|
+
const pkgPath = join6(__dirname, "..", "package.json");
|
|
4892
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
4465
4893
|
return pkg.version || "0.0.0";
|
|
4466
4894
|
} catch {
|
|
4467
4895
|
return "0.0.0";
|
|
@@ -4593,6 +5021,7 @@ program.command("config").description("Get or set UILint configuration options")
|
|
|
4593
5021
|
});
|
|
4594
5022
|
});
|
|
4595
5023
|
program.addCommand(createDuplicatesCommand());
|
|
5024
|
+
program.addCommand(createManifestCommand());
|
|
4596
5025
|
program.addCommand(createSocketCommand());
|
|
4597
5026
|
program.command("upgrade").description("Update installed ESLint rules to latest versions").option("--check", "Show available updates without applying").option("-y, --yes", "Auto-confirm all updates").option("--dry-run", "Show what would change without modifying files").option("--rule <id>", "Upgrade only a specific rule").action(async (options) => {
|
|
4598
5027
|
const { upgrade } = await import("./upgrade-GUYNMGSY.js");
|