uilint 0.2.65 → 0.2.67
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 +724 -4
- package/dist/index.js.map +1 -1
- package/package.json +5 -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 Command7 } from "commander";
|
|
29
29
|
|
|
30
30
|
// src/commands/scan.ts
|
|
31
31
|
import { dirname, resolve as resolve2 } from "path";
|
|
@@ -3720,9 +3720,728 @@ function createDuplicatesCommand() {
|
|
|
3720
3720
|
}
|
|
3721
3721
|
|
|
3722
3722
|
// src/index.ts
|
|
3723
|
-
import { readFileSync as
|
|
3723
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
3724
3724
|
import { dirname as dirname7, join as join5 } from "path";
|
|
3725
3725
|
import { fileURLToPath } from "url";
|
|
3726
|
+
|
|
3727
|
+
// src/commands/socket/index.ts
|
|
3728
|
+
import { Command as Command6 } from "commander";
|
|
3729
|
+
import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
|
|
3730
|
+
import chalk6 from "chalk";
|
|
3731
|
+
|
|
3732
|
+
// src/commands/socket/client.ts
|
|
3733
|
+
import WebSocket3 from "ws";
|
|
3734
|
+
var SocketClient = class {
|
|
3735
|
+
ws = null;
|
|
3736
|
+
url;
|
|
3737
|
+
debug;
|
|
3738
|
+
connectTimeout;
|
|
3739
|
+
connected = false;
|
|
3740
|
+
connectionPromise = null;
|
|
3741
|
+
pendingRequests = [];
|
|
3742
|
+
messageQueue = [];
|
|
3743
|
+
messageHandlers = [];
|
|
3744
|
+
constructor(options = {}) {
|
|
3745
|
+
const port = options.port || 9234;
|
|
3746
|
+
this.url = options.url || `ws://localhost:${port}`;
|
|
3747
|
+
this.debug = options.debug || false;
|
|
3748
|
+
this.connectTimeout = options.connectTimeout || 1e4;
|
|
3749
|
+
}
|
|
3750
|
+
/**
|
|
3751
|
+
* Connect to the socket server
|
|
3752
|
+
*/
|
|
3753
|
+
async connect() {
|
|
3754
|
+
if (this.connected) return;
|
|
3755
|
+
if (this.connectionPromise) return this.connectionPromise;
|
|
3756
|
+
this.connectionPromise = new Promise((resolve8, reject) => {
|
|
3757
|
+
const timeout = setTimeout(() => {
|
|
3758
|
+
reject(new Error(`Connection timeout to ${this.url}`));
|
|
3759
|
+
}, this.connectTimeout);
|
|
3760
|
+
this.ws = new WebSocket3(this.url);
|
|
3761
|
+
this.ws.on("open", () => {
|
|
3762
|
+
clearTimeout(timeout);
|
|
3763
|
+
this.connected = true;
|
|
3764
|
+
this.log("Connected to", this.url);
|
|
3765
|
+
resolve8();
|
|
3766
|
+
});
|
|
3767
|
+
this.ws.on("message", (data) => {
|
|
3768
|
+
try {
|
|
3769
|
+
const message = JSON.parse(data.toString());
|
|
3770
|
+
this.log("Received:", message.type);
|
|
3771
|
+
this.handleMessage(message);
|
|
3772
|
+
} catch (e) {
|
|
3773
|
+
console.error("Failed to parse message:", e);
|
|
3774
|
+
}
|
|
3775
|
+
});
|
|
3776
|
+
this.ws.on("error", (err) => {
|
|
3777
|
+
clearTimeout(timeout);
|
|
3778
|
+
if (!this.connected) {
|
|
3779
|
+
reject(err);
|
|
3780
|
+
} else {
|
|
3781
|
+
console.error("WebSocket error:", err);
|
|
3782
|
+
}
|
|
3783
|
+
});
|
|
3784
|
+
this.ws.on("close", () => {
|
|
3785
|
+
this.connected = false;
|
|
3786
|
+
this.log("Disconnected");
|
|
3787
|
+
for (const req of this.pendingRequests) {
|
|
3788
|
+
clearTimeout(req.timeout);
|
|
3789
|
+
req.reject(new Error("Disconnected"));
|
|
3790
|
+
}
|
|
3791
|
+
this.pendingRequests = [];
|
|
3792
|
+
});
|
|
3793
|
+
});
|
|
3794
|
+
return this.connectionPromise;
|
|
3795
|
+
}
|
|
3796
|
+
/**
|
|
3797
|
+
* Disconnect from the server
|
|
3798
|
+
*/
|
|
3799
|
+
disconnect() {
|
|
3800
|
+
if (this.ws) {
|
|
3801
|
+
this.ws.close();
|
|
3802
|
+
this.ws = null;
|
|
3803
|
+
this.connected = false;
|
|
3804
|
+
this.connectionPromise = null;
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
/**
|
|
3808
|
+
* Check if connected
|
|
3809
|
+
*/
|
|
3810
|
+
isConnected() {
|
|
3811
|
+
return this.connected;
|
|
3812
|
+
}
|
|
3813
|
+
/**
|
|
3814
|
+
* Add a handler for all incoming messages
|
|
3815
|
+
*/
|
|
3816
|
+
onMessage(handler) {
|
|
3817
|
+
this.messageHandlers.push(handler);
|
|
3818
|
+
return () => {
|
|
3819
|
+
this.messageHandlers = this.messageHandlers.filter((h) => h !== handler);
|
|
3820
|
+
};
|
|
3821
|
+
}
|
|
3822
|
+
/**
|
|
3823
|
+
* Send a raw message
|
|
3824
|
+
*/
|
|
3825
|
+
send(message) {
|
|
3826
|
+
if (!this.ws || !this.connected) {
|
|
3827
|
+
throw new Error("Not connected");
|
|
3828
|
+
}
|
|
3829
|
+
this.log("Sending:", message.type);
|
|
3830
|
+
this.ws.send(JSON.stringify(message));
|
|
3831
|
+
}
|
|
3832
|
+
/**
|
|
3833
|
+
* Wait for a message matching the predicate
|
|
3834
|
+
*/
|
|
3835
|
+
waitFor(predicate, timeout = 3e4) {
|
|
3836
|
+
const existing = this.messageQueue.find(predicate);
|
|
3837
|
+
if (existing) {
|
|
3838
|
+
this.messageQueue = this.messageQueue.filter((m) => m !== existing);
|
|
3839
|
+
return Promise.resolve(existing);
|
|
3840
|
+
}
|
|
3841
|
+
return new Promise((resolve8, reject) => {
|
|
3842
|
+
const timeoutId = setTimeout(() => {
|
|
3843
|
+
this.pendingRequests = this.pendingRequests.filter(
|
|
3844
|
+
(r) => r.timeout !== timeoutId
|
|
3845
|
+
);
|
|
3846
|
+
reject(new Error(`Timeout waiting for message (${timeout}ms)`));
|
|
3847
|
+
}, timeout);
|
|
3848
|
+
this.pendingRequests.push({
|
|
3849
|
+
predicate,
|
|
3850
|
+
resolve: resolve8,
|
|
3851
|
+
reject,
|
|
3852
|
+
timeout: timeoutId
|
|
3853
|
+
});
|
|
3854
|
+
});
|
|
3855
|
+
}
|
|
3856
|
+
/**
|
|
3857
|
+
* Generate a unique request ID
|
|
3858
|
+
*/
|
|
3859
|
+
generateRequestId() {
|
|
3860
|
+
return `cli-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3861
|
+
}
|
|
3862
|
+
// ==========================================================================
|
|
3863
|
+
// High-Level API Methods
|
|
3864
|
+
// ==========================================================================
|
|
3865
|
+
/**
|
|
3866
|
+
* Wait for workspace info (sent on connect)
|
|
3867
|
+
*/
|
|
3868
|
+
async waitForWorkspaceInfo(timeout = 5e3) {
|
|
3869
|
+
const existing = this.messageQueue.find((m) => m.type === "workspace:info");
|
|
3870
|
+
if (existing) {
|
|
3871
|
+
return existing;
|
|
3872
|
+
}
|
|
3873
|
+
return await this.waitFor(
|
|
3874
|
+
(msg) => msg.type === "workspace:info",
|
|
3875
|
+
timeout
|
|
3876
|
+
);
|
|
3877
|
+
}
|
|
3878
|
+
/**
|
|
3879
|
+
* Wait for rules metadata (sent on connect)
|
|
3880
|
+
*/
|
|
3881
|
+
async waitForRulesMetadata(timeout = 5e3) {
|
|
3882
|
+
const existing = this.messageQueue.find((m) => m.type === "rules:metadata");
|
|
3883
|
+
if (existing) {
|
|
3884
|
+
return existing;
|
|
3885
|
+
}
|
|
3886
|
+
return await this.waitFor(
|
|
3887
|
+
(msg) => msg.type === "rules:metadata",
|
|
3888
|
+
timeout
|
|
3889
|
+
);
|
|
3890
|
+
}
|
|
3891
|
+
/**
|
|
3892
|
+
* Lint a file
|
|
3893
|
+
*/
|
|
3894
|
+
async lintFile(filePath, timeout = 3e4) {
|
|
3895
|
+
const requestId = this.generateRequestId();
|
|
3896
|
+
this.send({ type: "lint:file", filePath, requestId });
|
|
3897
|
+
return await this.waitFor(
|
|
3898
|
+
(msg) => msg.type === "lint:result" && msg.requestId === requestId,
|
|
3899
|
+
timeout
|
|
3900
|
+
);
|
|
3901
|
+
}
|
|
3902
|
+
/**
|
|
3903
|
+
* Lint a specific element
|
|
3904
|
+
*/
|
|
3905
|
+
async lintElement(filePath, dataLoc, timeout = 3e4) {
|
|
3906
|
+
const requestId = this.generateRequestId();
|
|
3907
|
+
this.send({ type: "lint:element", filePath, dataLoc, requestId });
|
|
3908
|
+
return await this.waitFor(
|
|
3909
|
+
(msg) => msg.type === "lint:result" && msg.requestId === requestId,
|
|
3910
|
+
timeout
|
|
3911
|
+
);
|
|
3912
|
+
}
|
|
3913
|
+
/**
|
|
3914
|
+
* Check if vision analysis is available
|
|
3915
|
+
*/
|
|
3916
|
+
async visionCheck(timeout = 1e4) {
|
|
3917
|
+
const requestId = this.generateRequestId();
|
|
3918
|
+
this.send({ type: "vision:check", requestId });
|
|
3919
|
+
return await this.waitFor(
|
|
3920
|
+
(msg) => msg.type === "vision:status" && msg.requestId === requestId,
|
|
3921
|
+
timeout
|
|
3922
|
+
);
|
|
3923
|
+
}
|
|
3924
|
+
/**
|
|
3925
|
+
* Run vision analysis
|
|
3926
|
+
*/
|
|
3927
|
+
async visionAnalyze(params, timeout = 12e4) {
|
|
3928
|
+
const requestId = this.generateRequestId();
|
|
3929
|
+
this.send({
|
|
3930
|
+
type: "vision:analyze",
|
|
3931
|
+
route: params.route,
|
|
3932
|
+
timestamp: Date.now(),
|
|
3933
|
+
screenshot: params.screenshot,
|
|
3934
|
+
screenshotFile: params.screenshotFile,
|
|
3935
|
+
manifest: params.manifest,
|
|
3936
|
+
requestId
|
|
3937
|
+
});
|
|
3938
|
+
return await this.waitFor(
|
|
3939
|
+
(msg) => msg.type === "vision:result" && msg.requestId === requestId,
|
|
3940
|
+
timeout
|
|
3941
|
+
);
|
|
3942
|
+
}
|
|
3943
|
+
/**
|
|
3944
|
+
* Fetch source code
|
|
3945
|
+
*/
|
|
3946
|
+
async fetchSource(filePath, timeout = 1e4) {
|
|
3947
|
+
const requestId = this.generateRequestId();
|
|
3948
|
+
this.send({ type: "source:fetch", filePath, requestId });
|
|
3949
|
+
return await this.waitFor(
|
|
3950
|
+
(msg) => (msg.type === "source:result" || msg.type === "source:error") && (msg.requestId === requestId || msg.requestId === requestId),
|
|
3951
|
+
timeout
|
|
3952
|
+
);
|
|
3953
|
+
}
|
|
3954
|
+
/**
|
|
3955
|
+
* Request coverage data
|
|
3956
|
+
*/
|
|
3957
|
+
async requestCoverage(timeout = 3e4) {
|
|
3958
|
+
const requestId = this.generateRequestId();
|
|
3959
|
+
this.send({ type: "coverage:request", requestId });
|
|
3960
|
+
return await this.waitFor(
|
|
3961
|
+
(msg) => (msg.type === "coverage:result" || msg.type === "coverage:error") && (msg.requestId === requestId || msg.requestId === requestId),
|
|
3962
|
+
timeout
|
|
3963
|
+
);
|
|
3964
|
+
}
|
|
3965
|
+
/**
|
|
3966
|
+
* Set rule configuration
|
|
3967
|
+
*/
|
|
3968
|
+
async setRuleConfig(ruleId, severity, options, timeout = 1e4) {
|
|
3969
|
+
const requestId = this.generateRequestId();
|
|
3970
|
+
this.send({
|
|
3971
|
+
type: "rule:config:set",
|
|
3972
|
+
ruleId,
|
|
3973
|
+
severity,
|
|
3974
|
+
options,
|
|
3975
|
+
requestId
|
|
3976
|
+
});
|
|
3977
|
+
return await this.waitFor(
|
|
3978
|
+
(msg) => msg.type === "rule:config:result" && msg.requestId === requestId,
|
|
3979
|
+
timeout
|
|
3980
|
+
);
|
|
3981
|
+
}
|
|
3982
|
+
/**
|
|
3983
|
+
* Set a configuration value
|
|
3984
|
+
*/
|
|
3985
|
+
setConfig(key, value) {
|
|
3986
|
+
this.send({ type: "config:set", key, value });
|
|
3987
|
+
}
|
|
3988
|
+
/**
|
|
3989
|
+
* Subscribe to file changes
|
|
3990
|
+
*/
|
|
3991
|
+
subscribeFile(filePath) {
|
|
3992
|
+
this.send({ type: "subscribe:file", filePath });
|
|
3993
|
+
}
|
|
3994
|
+
/**
|
|
3995
|
+
* Invalidate cache
|
|
3996
|
+
*/
|
|
3997
|
+
invalidateCache(filePath) {
|
|
3998
|
+
this.send({ type: "cache:invalidate", filePath });
|
|
3999
|
+
}
|
|
4000
|
+
/**
|
|
4001
|
+
* Get all queued messages
|
|
4002
|
+
*/
|
|
4003
|
+
getMessages() {
|
|
4004
|
+
return [...this.messageQueue];
|
|
4005
|
+
}
|
|
4006
|
+
/**
|
|
4007
|
+
* Clear message queue
|
|
4008
|
+
*/
|
|
4009
|
+
clearMessages() {
|
|
4010
|
+
this.messageQueue = [];
|
|
4011
|
+
}
|
|
4012
|
+
handleMessage(message) {
|
|
4013
|
+
for (const handler of this.messageHandlers) {
|
|
4014
|
+
handler(message);
|
|
4015
|
+
}
|
|
4016
|
+
for (let i = 0; i < this.pendingRequests.length; i++) {
|
|
4017
|
+
const req = this.pendingRequests[i];
|
|
4018
|
+
if (req.predicate(message)) {
|
|
4019
|
+
clearTimeout(req.timeout);
|
|
4020
|
+
this.pendingRequests.splice(i, 1);
|
|
4021
|
+
req.resolve(message);
|
|
4022
|
+
return;
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
this.messageQueue.push(message);
|
|
4026
|
+
}
|
|
4027
|
+
log(...args) {
|
|
4028
|
+
if (this.debug) {
|
|
4029
|
+
console.log("[SocketClient]", ...args);
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
};
|
|
4033
|
+
async function createSocketClient(options = {}) {
|
|
4034
|
+
const client = new SocketClient(options);
|
|
4035
|
+
await client.connect();
|
|
4036
|
+
return client;
|
|
4037
|
+
}
|
|
4038
|
+
|
|
4039
|
+
// src/commands/socket/index.ts
|
|
4040
|
+
function formatMessage(msg, json) {
|
|
4041
|
+
if (json) {
|
|
4042
|
+
return JSON.stringify(msg, null, 2);
|
|
4043
|
+
}
|
|
4044
|
+
switch (msg.type) {
|
|
4045
|
+
case "lint:result": {
|
|
4046
|
+
const lines = [`${chalk6.cyan("lint:result")} ${msg.filePath}`];
|
|
4047
|
+
if (msg.issues.length === 0) {
|
|
4048
|
+
lines.push(chalk6.green(" No issues found"));
|
|
4049
|
+
} else {
|
|
4050
|
+
for (const issue of msg.issues) {
|
|
4051
|
+
const loc = issue.column ? `${issue.line}:${issue.column}` : `${issue.line}`;
|
|
4052
|
+
const rule = issue.ruleId ? chalk6.dim(` [${issue.ruleId}]`) : "";
|
|
4053
|
+
lines.push(` ${chalk6.yellow(loc)} ${issue.message}${rule}`);
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
return lines.join("\n");
|
|
4057
|
+
}
|
|
4058
|
+
case "vision:status":
|
|
4059
|
+
if (msg.available) {
|
|
4060
|
+
return `${chalk6.cyan("vision:status")} ${chalk6.green("available")} (${msg.model})`;
|
|
4061
|
+
}
|
|
4062
|
+
return `${chalk6.cyan("vision:status")} ${chalk6.red("not available")}`;
|
|
4063
|
+
case "vision:result": {
|
|
4064
|
+
const lines = [
|
|
4065
|
+
`${chalk6.cyan("vision:result")} ${msg.route} (${msg.analysisTime}ms)`
|
|
4066
|
+
];
|
|
4067
|
+
if (msg.error) {
|
|
4068
|
+
lines.push(chalk6.red(` Error: ${msg.error}`));
|
|
4069
|
+
} else if (msg.issues.length === 0) {
|
|
4070
|
+
lines.push(chalk6.green(" No issues found"));
|
|
4071
|
+
} else {
|
|
4072
|
+
for (const issue of msg.issues) {
|
|
4073
|
+
const severity = issue.severity === "error" ? chalk6.red("error") : issue.severity === "warning" ? chalk6.yellow("warn") : chalk6.blue("info");
|
|
4074
|
+
lines.push(` ${severity} [${issue.category}] ${issue.message}`);
|
|
4075
|
+
if (issue.dataLoc) {
|
|
4076
|
+
lines.push(chalk6.dim(` at ${issue.dataLoc}`));
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
return lines.join("\n");
|
|
4081
|
+
}
|
|
4082
|
+
case "workspace:info":
|
|
4083
|
+
return `${chalk6.cyan("workspace:info")}
|
|
4084
|
+
appRoot: ${msg.appRoot}
|
|
4085
|
+
workspaceRoot: ${msg.workspaceRoot}`;
|
|
4086
|
+
case "rules:metadata":
|
|
4087
|
+
return `${chalk6.cyan("rules:metadata")} ${msg.rules.length} rules available`;
|
|
4088
|
+
case "source:result":
|
|
4089
|
+
return `${chalk6.cyan("source:result")} ${msg.filePath} (${msg.totalLines} lines)`;
|
|
4090
|
+
case "source:error":
|
|
4091
|
+
return `${chalk6.cyan("source:error")} ${msg.filePath}: ${chalk6.red(msg.error)}`;
|
|
4092
|
+
case "coverage:result":
|
|
4093
|
+
return `${chalk6.cyan("coverage:result")} Coverage data received`;
|
|
4094
|
+
case "coverage:error":
|
|
4095
|
+
return `${chalk6.cyan("coverage:error")} ${chalk6.red(msg.error)}`;
|
|
4096
|
+
case "rule:config:result":
|
|
4097
|
+
if (msg.success) {
|
|
4098
|
+
return `${chalk6.cyan("rule:config:result")} ${msg.ruleId} -> ${msg.severity}`;
|
|
4099
|
+
}
|
|
4100
|
+
return `${chalk6.cyan("rule:config:result")} ${msg.ruleId} ${chalk6.red("failed")}: ${msg.error}`;
|
|
4101
|
+
case "duplicates:indexing:start":
|
|
4102
|
+
return `${chalk6.cyan("duplicates:indexing:start")}`;
|
|
4103
|
+
case "duplicates:indexing:progress":
|
|
4104
|
+
return `${chalk6.cyan("duplicates:indexing:progress")} ${msg.message}`;
|
|
4105
|
+
case "duplicates:indexing:complete":
|
|
4106
|
+
return `${chalk6.cyan("duplicates:indexing:complete")} ${msg.totalChunks} chunks in ${msg.duration}ms`;
|
|
4107
|
+
case "duplicates:indexing:error":
|
|
4108
|
+
return `${chalk6.cyan("duplicates:indexing:error")} ${chalk6.red(msg.error)}`;
|
|
4109
|
+
case "file:changed":
|
|
4110
|
+
return `${chalk6.cyan("file:changed")} ${msg.filePath}`;
|
|
4111
|
+
case "config:update":
|
|
4112
|
+
return `${chalk6.cyan("config:update")} ${msg.key} = ${JSON.stringify(msg.value)}`;
|
|
4113
|
+
default:
|
|
4114
|
+
return `${chalk6.cyan(msg.type)} ${JSON.stringify(msg)}`;
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
async function runRepl(client, options) {
|
|
4118
|
+
const readline = await import("readline");
|
|
4119
|
+
const rl = readline.createInterface({
|
|
4120
|
+
input: process.stdin,
|
|
4121
|
+
output: process.stdout,
|
|
4122
|
+
prompt: chalk6.green("uilint> ")
|
|
4123
|
+
});
|
|
4124
|
+
console.log(chalk6.bold("\nUILint Socket REPL"));
|
|
4125
|
+
console.log("Type 'help' for available commands, 'exit' to quit.\n");
|
|
4126
|
+
client.onMessage((msg) => {
|
|
4127
|
+
console.log("\n" + formatMessage(msg, options.json));
|
|
4128
|
+
rl.prompt();
|
|
4129
|
+
});
|
|
4130
|
+
try {
|
|
4131
|
+
await client.waitForWorkspaceInfo(2e3);
|
|
4132
|
+
} catch {
|
|
4133
|
+
}
|
|
4134
|
+
rl.prompt();
|
|
4135
|
+
rl.on("line", async (line) => {
|
|
4136
|
+
const trimmed = line.trim();
|
|
4137
|
+
if (!trimmed) {
|
|
4138
|
+
rl.prompt();
|
|
4139
|
+
return;
|
|
4140
|
+
}
|
|
4141
|
+
const [cmd, ...args] = trimmed.split(/\s+/);
|
|
4142
|
+
try {
|
|
4143
|
+
switch (cmd) {
|
|
4144
|
+
case "help":
|
|
4145
|
+
console.log(`
|
|
4146
|
+
Available commands:
|
|
4147
|
+
lint:file <path> Lint a file
|
|
4148
|
+
lint:element <path> <dataLoc> Lint a specific element
|
|
4149
|
+
vision:check Check if vision is available
|
|
4150
|
+
vision:analyze <route> <manifestFile> [screenshotFile]
|
|
4151
|
+
Run vision analysis
|
|
4152
|
+
source:fetch <path> Fetch source code
|
|
4153
|
+
coverage:request Request coverage data
|
|
4154
|
+
rule:config <ruleId> <severity> [optionsJson]
|
|
4155
|
+
Set rule configuration
|
|
4156
|
+
config:set <key> <value> Set a config value
|
|
4157
|
+
subscribe <path> Subscribe to file changes
|
|
4158
|
+
invalidate [path] Invalidate cache
|
|
4159
|
+
messages Show queued messages
|
|
4160
|
+
clear Clear message queue
|
|
4161
|
+
json Toggle JSON output
|
|
4162
|
+
exit Exit REPL
|
|
4163
|
+
`);
|
|
4164
|
+
break;
|
|
4165
|
+
case "lint:file":
|
|
4166
|
+
if (!args[0]) {
|
|
4167
|
+
console.log(chalk6.red("Usage: lint:file <path>"));
|
|
4168
|
+
} else {
|
|
4169
|
+
const result = await client.lintFile(args[0], options.timeout);
|
|
4170
|
+
console.log(formatMessage(result, options.json));
|
|
4171
|
+
}
|
|
4172
|
+
break;
|
|
4173
|
+
case "lint:element":
|
|
4174
|
+
if (!args[0] || !args[1]) {
|
|
4175
|
+
console.log(chalk6.red("Usage: lint:element <path> <dataLoc>"));
|
|
4176
|
+
} else {
|
|
4177
|
+
const result = await client.lintElement(args[0], args[1], options.timeout);
|
|
4178
|
+
console.log(formatMessage(result, options.json));
|
|
4179
|
+
}
|
|
4180
|
+
break;
|
|
4181
|
+
case "vision:check": {
|
|
4182
|
+
const result = await client.visionCheck(options.timeout);
|
|
4183
|
+
console.log(formatMessage(result, options.json));
|
|
4184
|
+
break;
|
|
4185
|
+
}
|
|
4186
|
+
case "vision:analyze":
|
|
4187
|
+
if (!args[0] || !args[1]) {
|
|
4188
|
+
console.log(chalk6.red("Usage: vision:analyze <route> <manifestFile> [screenshotFile]"));
|
|
4189
|
+
} else {
|
|
4190
|
+
const manifestPath = args[1];
|
|
4191
|
+
if (!existsSync7(manifestPath)) {
|
|
4192
|
+
console.log(chalk6.red(`Manifest file not found: ${manifestPath}`));
|
|
4193
|
+
} else {
|
|
4194
|
+
const manifest = JSON.parse(readFileSync3(manifestPath, "utf-8"));
|
|
4195
|
+
const params = { route: args[0], manifest };
|
|
4196
|
+
if (args[2]) {
|
|
4197
|
+
if (existsSync7(args[2])) {
|
|
4198
|
+
const imageBuffer = readFileSync3(args[2]);
|
|
4199
|
+
params.screenshot = imageBuffer.toString("base64");
|
|
4200
|
+
} else {
|
|
4201
|
+
console.log(chalk6.red(`Screenshot file not found: ${args[2]}`));
|
|
4202
|
+
break;
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4205
|
+
const result = await client.visionAnalyze(params, options.timeout);
|
|
4206
|
+
console.log(formatMessage(result, options.json));
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
break;
|
|
4210
|
+
case "source:fetch":
|
|
4211
|
+
if (!args[0]) {
|
|
4212
|
+
console.log(chalk6.red("Usage: source:fetch <path>"));
|
|
4213
|
+
} else {
|
|
4214
|
+
const result = await client.fetchSource(args[0], options.timeout);
|
|
4215
|
+
console.log(formatMessage(result, options.json));
|
|
4216
|
+
}
|
|
4217
|
+
break;
|
|
4218
|
+
case "coverage:request": {
|
|
4219
|
+
const result = await client.requestCoverage(options.timeout);
|
|
4220
|
+
console.log(formatMessage(result, options.json));
|
|
4221
|
+
break;
|
|
4222
|
+
}
|
|
4223
|
+
case "rule:config":
|
|
4224
|
+
if (!args[0] || !args[1]) {
|
|
4225
|
+
console.log(chalk6.red("Usage: rule:config <ruleId> <severity> [optionsJson]"));
|
|
4226
|
+
} else {
|
|
4227
|
+
const severity = args[1];
|
|
4228
|
+
const ruleOptions = args[2] ? JSON.parse(args[2]) : void 0;
|
|
4229
|
+
const result = await client.setRuleConfig(args[0], severity, ruleOptions, options.timeout);
|
|
4230
|
+
console.log(formatMessage(result, options.json));
|
|
4231
|
+
}
|
|
4232
|
+
break;
|
|
4233
|
+
case "config:set":
|
|
4234
|
+
if (!args[0] || !args[1]) {
|
|
4235
|
+
console.log(chalk6.red("Usage: config:set <key> <value>"));
|
|
4236
|
+
} else {
|
|
4237
|
+
let value = args[1];
|
|
4238
|
+
try {
|
|
4239
|
+
value = JSON.parse(args[1]);
|
|
4240
|
+
} catch {
|
|
4241
|
+
}
|
|
4242
|
+
client.setConfig(args[0], value);
|
|
4243
|
+
console.log(chalk6.green(`Set ${args[0]} = ${JSON.stringify(value)}`));
|
|
4244
|
+
}
|
|
4245
|
+
break;
|
|
4246
|
+
case "subscribe":
|
|
4247
|
+
if (!args[0]) {
|
|
4248
|
+
console.log(chalk6.red("Usage: subscribe <path>"));
|
|
4249
|
+
} else {
|
|
4250
|
+
client.subscribeFile(args[0]);
|
|
4251
|
+
console.log(chalk6.green(`Subscribed to ${args[0]}`));
|
|
4252
|
+
}
|
|
4253
|
+
break;
|
|
4254
|
+
case "invalidate":
|
|
4255
|
+
client.invalidateCache(args[0]);
|
|
4256
|
+
console.log(chalk6.green(args[0] ? `Invalidated ${args[0]}` : "Invalidated all cache"));
|
|
4257
|
+
break;
|
|
4258
|
+
case "messages": {
|
|
4259
|
+
const msgs = client.getMessages();
|
|
4260
|
+
if (msgs.length === 0) {
|
|
4261
|
+
console.log(chalk6.dim("No queued messages"));
|
|
4262
|
+
} else {
|
|
4263
|
+
for (const msg of msgs) {
|
|
4264
|
+
console.log(formatMessage(msg, options.json));
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4267
|
+
break;
|
|
4268
|
+
}
|
|
4269
|
+
case "clear":
|
|
4270
|
+
client.clearMessages();
|
|
4271
|
+
console.log(chalk6.green("Message queue cleared"));
|
|
4272
|
+
break;
|
|
4273
|
+
case "json":
|
|
4274
|
+
options.json = !options.json;
|
|
4275
|
+
console.log(chalk6.green(`JSON output ${options.json ? "enabled" : "disabled"}`));
|
|
4276
|
+
break;
|
|
4277
|
+
case "exit":
|
|
4278
|
+
case "quit":
|
|
4279
|
+
client.disconnect();
|
|
4280
|
+
rl.close();
|
|
4281
|
+
process.exit(0);
|
|
4282
|
+
break;
|
|
4283
|
+
default:
|
|
4284
|
+
console.log(chalk6.red(`Unknown command: ${cmd}. Type 'help' for available commands.`));
|
|
4285
|
+
}
|
|
4286
|
+
} catch (err) {
|
|
4287
|
+
console.log(chalk6.red(`Error: ${err.message}`));
|
|
4288
|
+
}
|
|
4289
|
+
rl.prompt();
|
|
4290
|
+
});
|
|
4291
|
+
rl.on("close", () => {
|
|
4292
|
+
client.disconnect();
|
|
4293
|
+
process.exit(0);
|
|
4294
|
+
});
|
|
4295
|
+
}
|
|
4296
|
+
async function runListen(client, options, filter) {
|
|
4297
|
+
console.log(chalk6.bold("\nListening for messages..."));
|
|
4298
|
+
if (filter) {
|
|
4299
|
+
console.log(chalk6.dim(`Filter: ${filter}`));
|
|
4300
|
+
}
|
|
4301
|
+
console.log(chalk6.dim("Press Ctrl+C to stop.\n"));
|
|
4302
|
+
client.onMessage((msg) => {
|
|
4303
|
+
if (filter && !msg.type.includes(filter.replace("*", ""))) {
|
|
4304
|
+
return;
|
|
4305
|
+
}
|
|
4306
|
+
console.log(formatMessage(msg, options.json));
|
|
4307
|
+
});
|
|
4308
|
+
await new Promise(() => {
|
|
4309
|
+
});
|
|
4310
|
+
}
|
|
4311
|
+
function createSocketCommand() {
|
|
4312
|
+
const cmd = new Command6("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
|
+
cmd.action(async (cmdOptions) => {
|
|
4314
|
+
const options = {
|
|
4315
|
+
port: parseInt(cmdOptions.port, 10),
|
|
4316
|
+
debug: cmdOptions.debug,
|
|
4317
|
+
json: cmdOptions.json,
|
|
4318
|
+
timeout: parseInt(cmdOptions.timeout, 10)
|
|
4319
|
+
};
|
|
4320
|
+
try {
|
|
4321
|
+
const client = await createSocketClient({ port: options.port, debug: options.debug });
|
|
4322
|
+
await runRepl(client, options);
|
|
4323
|
+
} catch (err) {
|
|
4324
|
+
console.error(chalk6.red(`Failed to connect: ${err.message}`));
|
|
4325
|
+
console.error(chalk6.dim(`Make sure the server is running: uilint serve -p ${options.port}`));
|
|
4326
|
+
process.exit(1);
|
|
4327
|
+
}
|
|
4328
|
+
});
|
|
4329
|
+
cmd.command("listen").description("Listen for all messages from the server").option("-f, --filter <pattern>", "Filter messages by type pattern (e.g., lint:*, vision:*)").action(async (subOptions, command) => {
|
|
4330
|
+
const parentOptions = command.parent?.opts() || {};
|
|
4331
|
+
const options = {
|
|
4332
|
+
port: parseInt(parentOptions.port || "9234", 10),
|
|
4333
|
+
debug: parentOptions.debug || false,
|
|
4334
|
+
json: parentOptions.json || false,
|
|
4335
|
+
timeout: parseInt(parentOptions.timeout || "30000", 10)
|
|
4336
|
+
};
|
|
4337
|
+
try {
|
|
4338
|
+
const client = await createSocketClient({ port: options.port, debug: options.debug });
|
|
4339
|
+
await runListen(client, options, subOptions.filter);
|
|
4340
|
+
} catch (err) {
|
|
4341
|
+
console.error(chalk6.red(`Failed to connect: ${err.message}`));
|
|
4342
|
+
process.exit(1);
|
|
4343
|
+
}
|
|
4344
|
+
});
|
|
4345
|
+
cmd.command("lint:file <path>").description("Lint a file and output results").action(async (filePath, _subOptions, command) => {
|
|
4346
|
+
const parentOptions = command.parent?.opts() || {};
|
|
4347
|
+
const options = {
|
|
4348
|
+
port: parseInt(parentOptions.port || "9234", 10),
|
|
4349
|
+
debug: parentOptions.debug || false,
|
|
4350
|
+
json: parentOptions.json || false,
|
|
4351
|
+
timeout: parseInt(parentOptions.timeout || "30000", 10)
|
|
4352
|
+
};
|
|
4353
|
+
try {
|
|
4354
|
+
const client = await createSocketClient({ port: options.port, debug: options.debug });
|
|
4355
|
+
await client.waitForWorkspaceInfo(2e3).catch(() => {
|
|
4356
|
+
});
|
|
4357
|
+
const result = await client.lintFile(filePath, options.timeout);
|
|
4358
|
+
console.log(formatMessage(result, options.json));
|
|
4359
|
+
client.disconnect();
|
|
4360
|
+
process.exit(result.issues.length > 0 ? 1 : 0);
|
|
4361
|
+
} catch (err) {
|
|
4362
|
+
console.error(chalk6.red(`Error: ${err.message}`));
|
|
4363
|
+
process.exit(1);
|
|
4364
|
+
}
|
|
4365
|
+
});
|
|
4366
|
+
cmd.command("vision:check").description("Check if vision analysis is available").action(async (_subOptions, command) => {
|
|
4367
|
+
const parentOptions = command.parent?.opts() || {};
|
|
4368
|
+
const options = {
|
|
4369
|
+
port: parseInt(parentOptions.port || "9234", 10),
|
|
4370
|
+
debug: parentOptions.debug || false,
|
|
4371
|
+
json: parentOptions.json || false,
|
|
4372
|
+
timeout: parseInt(parentOptions.timeout || "10000", 10)
|
|
4373
|
+
};
|
|
4374
|
+
try {
|
|
4375
|
+
const client = await createSocketClient({ port: options.port, debug: options.debug });
|
|
4376
|
+
const result = await client.visionCheck(options.timeout);
|
|
4377
|
+
console.log(formatMessage(result, options.json));
|
|
4378
|
+
client.disconnect();
|
|
4379
|
+
process.exit(result.available ? 0 : 1);
|
|
4380
|
+
} catch (err) {
|
|
4381
|
+
console.error(chalk6.red(`Error: ${err.message}`));
|
|
4382
|
+
process.exit(1);
|
|
4383
|
+
}
|
|
4384
|
+
});
|
|
4385
|
+
cmd.command("source:fetch <path>").description("Fetch source code for a file").action(async (filePath, _subOptions, command) => {
|
|
4386
|
+
const parentOptions = command.parent?.opts() || {};
|
|
4387
|
+
const options = {
|
|
4388
|
+
port: parseInt(parentOptions.port || "9234", 10),
|
|
4389
|
+
debug: parentOptions.debug || false,
|
|
4390
|
+
json: parentOptions.json || false,
|
|
4391
|
+
timeout: parseInt(parentOptions.timeout || "10000", 10)
|
|
4392
|
+
};
|
|
4393
|
+
try {
|
|
4394
|
+
const client = await createSocketClient({ port: options.port, debug: options.debug });
|
|
4395
|
+
await client.waitForWorkspaceInfo(2e3).catch(() => {
|
|
4396
|
+
});
|
|
4397
|
+
const result = await client.fetchSource(filePath, options.timeout);
|
|
4398
|
+
if (options.json) {
|
|
4399
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4400
|
+
} else if (result.type === "source:result") {
|
|
4401
|
+
console.log(result.content);
|
|
4402
|
+
} else {
|
|
4403
|
+
console.error(chalk6.red(`Error: ${result.error}`));
|
|
4404
|
+
}
|
|
4405
|
+
client.disconnect();
|
|
4406
|
+
process.exit(result.type === "source:result" ? 0 : 1);
|
|
4407
|
+
} catch (err) {
|
|
4408
|
+
console.error(chalk6.red(`Error: ${err.message}`));
|
|
4409
|
+
process.exit(1);
|
|
4410
|
+
}
|
|
4411
|
+
});
|
|
4412
|
+
cmd.command("rules").description("List available rules").action(async (_subOptions, command) => {
|
|
4413
|
+
const parentOptions = command.parent?.opts() || {};
|
|
4414
|
+
const options = {
|
|
4415
|
+
port: parseInt(parentOptions.port || "9234", 10),
|
|
4416
|
+
debug: parentOptions.debug || false,
|
|
4417
|
+
json: parentOptions.json || false,
|
|
4418
|
+
timeout: parseInt(parentOptions.timeout || "10000", 10)
|
|
4419
|
+
};
|
|
4420
|
+
try {
|
|
4421
|
+
const client = await createSocketClient({ port: options.port, debug: options.debug });
|
|
4422
|
+
const metadata = await client.waitForRulesMetadata(options.timeout);
|
|
4423
|
+
if (options.json) {
|
|
4424
|
+
console.log(JSON.stringify(metadata.rules, null, 2));
|
|
4425
|
+
} else {
|
|
4426
|
+
console.log(chalk6.bold(`
|
|
4427
|
+
${metadata.rules.length} rules available:
|
|
4428
|
+
`));
|
|
4429
|
+
for (const rule of metadata.rules) {
|
|
4430
|
+
const severity = rule.currentSeverity === "error" ? chalk6.red("error") : rule.currentSeverity === "warn" ? chalk6.yellow("warn") : chalk6.dim("off");
|
|
4431
|
+
console.log(` ${chalk6.cyan(rule.id)} [${severity}]`);
|
|
4432
|
+
console.log(chalk6.dim(` ${rule.description}`));
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
client.disconnect();
|
|
4436
|
+
} catch (err) {
|
|
4437
|
+
console.error(chalk6.red(`Error: ${err.message}`));
|
|
4438
|
+
process.exit(1);
|
|
4439
|
+
}
|
|
4440
|
+
});
|
|
4441
|
+
return cmd;
|
|
4442
|
+
}
|
|
4443
|
+
|
|
4444
|
+
// src/index.ts
|
|
3726
4445
|
function assertNodeVersion(minMajor, minMinor) {
|
|
3727
4446
|
const ver = process.versions.node || "";
|
|
3728
4447
|
const parts = ver.split(".");
|
|
@@ -3737,12 +4456,12 @@ function assertNodeVersion(minMajor, minMinor) {
|
|
|
3737
4456
|
}
|
|
3738
4457
|
}
|
|
3739
4458
|
assertNodeVersion(20, 19);
|
|
3740
|
-
var program = new
|
|
4459
|
+
var program = new Command7();
|
|
3741
4460
|
function getCLIVersion() {
|
|
3742
4461
|
try {
|
|
3743
4462
|
const __dirname = dirname7(fileURLToPath(import.meta.url));
|
|
3744
4463
|
const pkgPath = join5(__dirname, "..", "package.json");
|
|
3745
|
-
const pkg = JSON.parse(
|
|
4464
|
+
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
3746
4465
|
return pkg.version || "0.0.0";
|
|
3747
4466
|
} catch {
|
|
3748
4467
|
return "0.0.0";
|
|
@@ -3868,6 +4587,7 @@ program.command("config").description("Get or set UILint configuration options")
|
|
|
3868
4587
|
});
|
|
3869
4588
|
});
|
|
3870
4589
|
program.addCommand(createDuplicatesCommand());
|
|
4590
|
+
program.addCommand(createSocketCommand());
|
|
3871
4591
|
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) => {
|
|
3872
4592
|
const { upgrade } = await import("./upgrade-TGYLZ4QX.js");
|
|
3873
4593
|
await upgrade({
|