@rslint/core 0.6.4 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/rslint.js +23 -0
- package/dist/0~engine.js +109 -19
- package/dist/519.js +601 -0
- package/dist/770.js +18 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +42 -92
- package/dist/config-loader.d.ts +175 -9
- package/dist/config-loader.js +3 -161
- package/dist/eslint-plugin/index.d.ts +25 -13
- package/dist/eslint-plugin/index.js +92 -29
- package/dist/eslint-plugin/lint-worker.js +61 -27
- package/dist/eslint-plugin/types.d.ts +19 -9
- package/dist/index.d.ts +51 -21
- package/dist/index.js +1331 -91
- package/dist/internal.d.ts +38 -3
- package/dist/internal.js +81 -35
- package/dist/service.d.ts +90 -4
- package/dist/service.js +116 -10
- package/package.json +14 -14
- package/bin/rslint.cjs +0 -48
- package/dist/207.js +0 -897
package/dist/internal.d.ts
CHANGED
|
@@ -15,6 +15,15 @@ declare interface Fix {
|
|
|
15
15
|
endPos: number;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/** Handler for a positive-id request frame sent by the Go peer. */
|
|
19
|
+
declare type InboundRequestHandler = (message: IpcMessage) => unknown;
|
|
20
|
+
|
|
21
|
+
declare interface IpcMessage {
|
|
22
|
+
id: number;
|
|
23
|
+
kind: string;
|
|
24
|
+
data: any;
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
/**
|
|
19
28
|
* One-shot convenience: spin up a Node-backed service, run a single lint
|
|
20
29
|
* request, then tear it down. This is an internal/tooling surface (the
|
|
@@ -26,8 +35,29 @@ export declare function lint(options: LintOptions): Promise<LintResponse>;
|
|
|
26
35
|
|
|
27
36
|
export declare interface LintOptions {
|
|
28
37
|
files?: string[];
|
|
38
|
+
canonicalFiles?: string[];
|
|
29
39
|
config?: Record<string, unknown>[];
|
|
40
|
+
/**
|
|
41
|
+
* Ask native Go to discover and route JS/TS configs for this lint request.
|
|
42
|
+
* `config` and `configDiscovery` are mutually exclusive. Browser/WASM
|
|
43
|
+
* backends intentionally do not advertise this host-filesystem capability.
|
|
44
|
+
*/
|
|
45
|
+
configDiscovery?: {
|
|
46
|
+
mode: 'auto' | 'explicit';
|
|
47
|
+
explicitConfigPath?: string;
|
|
48
|
+
/** Static glob roots; Go visits only branches leading to the supplied files. */
|
|
49
|
+
directories?: string[];
|
|
50
|
+
/** Parallel to `files`; true only for caller-literal file targets. */
|
|
51
|
+
explicitFiles?: boolean[];
|
|
52
|
+
/** Normalized API override entries appended to every selected config. */
|
|
53
|
+
overrideConfig?: Record<string, unknown>[];
|
|
54
|
+
};
|
|
55
|
+
eslintPlugins?: Array<{
|
|
56
|
+
prefix: string;
|
|
57
|
+
ruleNames: string[];
|
|
58
|
+
}>;
|
|
30
59
|
configDirectory?: string;
|
|
60
|
+
pluginConfigDirectory?: string;
|
|
31
61
|
workingDirectory?: string;
|
|
32
62
|
fileContents?: Record<string, string>;
|
|
33
63
|
includeEncodedSourceFiles?: boolean;
|
|
@@ -60,6 +90,8 @@ export declare class NodeRslintService implements RslintServiceInterface {
|
|
|
60
90
|
private expectedSize;
|
|
61
91
|
private dead;
|
|
62
92
|
private closing;
|
|
93
|
+
private inboundHandler;
|
|
94
|
+
private activeInboundRequests;
|
|
63
95
|
constructor(options?: RSlintOptions);
|
|
64
96
|
/**
|
|
65
97
|
* Keep the Node event loop alive only while a request is in flight. The
|
|
@@ -72,10 +104,14 @@ export declare class NodeRslintService implements RslintServiceInterface {
|
|
|
72
104
|
* them to Readable/Writable, so narrow via `instanceof Socket` to reach those.
|
|
73
105
|
*/
|
|
74
106
|
private setLoopActive;
|
|
107
|
+
private updateLoopActivity;
|
|
108
|
+
/** Install the handler for positive-id requests sent by the Go peer. */
|
|
109
|
+
setInboundHandler(handler: InboundRequestHandler | null): void;
|
|
75
110
|
/**
|
|
76
111
|
* Send a message to the rslint process
|
|
77
112
|
*/
|
|
78
113
|
sendMessage(kind: string, data: any): Promise<any>;
|
|
114
|
+
private writeMessage;
|
|
79
115
|
/**
|
|
80
116
|
* Handle incoming binary data chunks
|
|
81
117
|
*/
|
|
@@ -102,9 +138,6 @@ export declare class NodeRslintService implements RslintServiceInterface {
|
|
|
102
138
|
terminate(): void;
|
|
103
139
|
}
|
|
104
140
|
|
|
105
|
-
/**
|
|
106
|
-
* Shared types for rslint IPC protocol across all environments
|
|
107
|
-
*/
|
|
108
141
|
declare interface Position {
|
|
109
142
|
line: number;
|
|
110
143
|
column: number;
|
|
@@ -122,6 +155,8 @@ declare interface RSlintOptions {
|
|
|
122
155
|
|
|
123
156
|
declare interface RslintServiceInterface {
|
|
124
157
|
sendMessage(kind: string, data: any): Promise<any>;
|
|
158
|
+
/** Optional for one-way backends such as the current browser worker. */
|
|
159
|
+
setInboundHandler?(handler: InboundRequestHandler | null): void;
|
|
125
160
|
terminate(): void;
|
|
126
161
|
}
|
|
127
162
|
|
package/dist/internal.js
CHANGED
|
@@ -1,23 +1,7 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
2
|
import { Socket } from "node:net";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveRslintBinary } from "./770.js";
|
|
4
4
|
import { RSLintService } from "./service.js";
|
|
5
|
-
const node_require = createRequire(import.meta.url);
|
|
6
|
-
function resolveRslintBinary() {
|
|
7
|
-
const arch = process.arch;
|
|
8
|
-
const tuples = 'linux' === process.platform ? [
|
|
9
|
-
`linux-${arch}-gnu`,
|
|
10
|
-
`linux-${arch}-musl`
|
|
11
|
-
] : 'win32' === process.platform ? [
|
|
12
|
-
`win32-${arch}-msvc`
|
|
13
|
-
] : [
|
|
14
|
-
`${process.platform}-${arch}`
|
|
15
|
-
];
|
|
16
|
-
for (const tuple of tuples)try {
|
|
17
|
-
return node_require.resolve(`@rslint/native-${tuple}/bin`);
|
|
18
|
-
} catch {}
|
|
19
|
-
throw new Error(`rslint: no native binary for ${process.platform}-${arch} (looked for @rslint/native-{${tuples.join(',')}})`);
|
|
20
|
-
}
|
|
21
5
|
class NodeRslintService {
|
|
22
6
|
nextMessageId;
|
|
23
7
|
pendingMessages;
|
|
@@ -28,12 +12,16 @@ class NodeRslintService {
|
|
|
28
12
|
expectedSize;
|
|
29
13
|
dead;
|
|
30
14
|
closing;
|
|
15
|
+
inboundHandler;
|
|
16
|
+
activeInboundRequests;
|
|
31
17
|
constructor(options = {}){
|
|
32
18
|
this.nextMessageId = 1;
|
|
33
19
|
this.pendingMessages = new Map();
|
|
34
20
|
this.rslintPath = options.rslintPath || resolveRslintBinary();
|
|
35
21
|
this.dead = false;
|
|
36
22
|
this.closing = false;
|
|
23
|
+
this.inboundHandler = null;
|
|
24
|
+
this.activeInboundRequests = 0;
|
|
37
25
|
this.process = spawn(this.rslintPath, [
|
|
38
26
|
'--api'
|
|
39
27
|
], {
|
|
@@ -74,6 +62,12 @@ class NodeRslintService {
|
|
|
74
62
|
])if (stream instanceof Socket) if (active) stream.ref();
|
|
75
63
|
else stream.unref();
|
|
76
64
|
}
|
|
65
|
+
updateLoopActivity() {
|
|
66
|
+
this.setLoopActive(!this.dead && (this.pendingMessages.size > 0 || this.activeInboundRequests > 0));
|
|
67
|
+
}
|
|
68
|
+
setInboundHandler(handler) {
|
|
69
|
+
this.inboundHandler = handler;
|
|
70
|
+
}
|
|
77
71
|
async sendMessage(kind, data) {
|
|
78
72
|
return new Promise((resolve, reject)=>{
|
|
79
73
|
if (this.dead) return void reject(new Error('rslint service is no longer running'));
|
|
@@ -88,17 +82,26 @@ class NodeRslintService {
|
|
|
88
82
|
resolve,
|
|
89
83
|
reject
|
|
90
84
|
});
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
]));
|
|
85
|
+
this.updateLoopActivity();
|
|
86
|
+
try {
|
|
87
|
+
this.writeMessage(message);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
this.pendingMessages.delete(id);
|
|
90
|
+
this.updateLoopActivity();
|
|
91
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
92
|
+
}
|
|
100
93
|
});
|
|
101
94
|
}
|
|
95
|
+
writeMessage(message) {
|
|
96
|
+
if (this.dead) return;
|
|
97
|
+
const jsonBuffer = Buffer.from(JSON.stringify(message), 'utf8');
|
|
98
|
+
const length = Buffer.alloc(4);
|
|
99
|
+
length.writeUInt32LE(jsonBuffer.length, 0);
|
|
100
|
+
this.process.stdin.write(Buffer.concat([
|
|
101
|
+
length,
|
|
102
|
+
jsonBuffer
|
|
103
|
+
]));
|
|
104
|
+
}
|
|
102
105
|
handleChunk(chunk) {
|
|
103
106
|
this.chunks.push(chunk);
|
|
104
107
|
this.chunkSize += chunk.length;
|
|
@@ -130,22 +133,63 @@ class NodeRslintService {
|
|
|
130
133
|
}
|
|
131
134
|
handleMessage(message) {
|
|
132
135
|
const { id, kind, data } = message;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
if ('response' === kind || 'error' === kind) {
|
|
137
|
+
const pending = this.pendingMessages.get(id);
|
|
138
|
+
if (!pending) return;
|
|
139
|
+
this.pendingMessages.delete(id);
|
|
140
|
+
if ('error' === kind) pending.reject(new Error(data?.message ?? 'rslint request failed'));
|
|
141
|
+
else pending.resolve(data);
|
|
142
|
+
this.updateLoopActivity();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (id <= 0) return;
|
|
146
|
+
this.activeInboundRequests++;
|
|
147
|
+
this.updateLoopActivity();
|
|
148
|
+
Promise.resolve().then(async ()=>{
|
|
149
|
+
if (!this.inboundHandler) throw new Error(`no inbound handler registered (kind=${message.kind})`);
|
|
150
|
+
return this.inboundHandler(message);
|
|
151
|
+
}).then((result)=>{
|
|
152
|
+
try {
|
|
153
|
+
this.writeMessage({
|
|
154
|
+
id,
|
|
155
|
+
kind: 'response',
|
|
156
|
+
data: result
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
160
|
+
this.writeMessage({
|
|
161
|
+
id,
|
|
162
|
+
kind: 'error',
|
|
163
|
+
data: {
|
|
164
|
+
message: `failed to encode inbound response: ${detail}`
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}, (error)=>{
|
|
169
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
170
|
+
this.writeMessage({
|
|
171
|
+
id,
|
|
172
|
+
kind: 'error',
|
|
173
|
+
data: {
|
|
174
|
+
message: detail
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}).finally(()=>{
|
|
178
|
+
this.activeInboundRequests--;
|
|
179
|
+
this.updateLoopActivity();
|
|
180
|
+
}).catch(()=>{});
|
|
139
181
|
}
|
|
140
182
|
rejectAllPending(err) {
|
|
141
183
|
if (0 === this.pendingMessages.size) return;
|
|
142
184
|
for (const [, pending] of this.pendingMessages)pending.reject(err);
|
|
143
185
|
this.pendingMessages.clear();
|
|
186
|
+
this.updateLoopActivity();
|
|
144
187
|
}
|
|
145
188
|
resolveAllPending() {
|
|
146
189
|
if (0 === this.pendingMessages.size) return;
|
|
147
190
|
for (const [, pending] of this.pendingMessages)pending.resolve(null);
|
|
148
191
|
this.pendingMessages.clear();
|
|
192
|
+
this.updateLoopActivity();
|
|
149
193
|
}
|
|
150
194
|
terminate() {
|
|
151
195
|
this.dead = true;
|
|
@@ -160,8 +204,10 @@ async function lint(options) {
|
|
|
160
204
|
const service = new RSLintService(new NodeRslintService({
|
|
161
205
|
workingDirectory: options.workingDirectory
|
|
162
206
|
}));
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
207
|
+
try {
|
|
208
|
+
return await service.lint(options);
|
|
209
|
+
} finally{
|
|
210
|
+
await service.close();
|
|
211
|
+
}
|
|
166
212
|
}
|
|
167
213
|
export { NodeRslintService, lint };
|
package/dist/service.d.ts
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
/** Go's final effective-ID selection after ignore/ownership resolution. */
|
|
2
|
+
declare interface ActivateConfigsRequest {
|
|
3
|
+
protocolVersion: typeof CONFIG_DISCOVERY_PROTOCOL_VERSION;
|
|
4
|
+
transactionId: string;
|
|
5
|
+
effectiveConfigIds: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Logical protocol shared by the native CLI, programmatic API, and LSP config
|
|
10
|
+
* discovery adapters. The transports differ (framed IPC versus JSON-RPC), but
|
|
11
|
+
* every adapter sends these payloads to the same Node-side module host.
|
|
12
|
+
*
|
|
13
|
+
* Paths in requests identify values selected by Go. Node may native-normalize
|
|
14
|
+
* configPath for local I/O, but configDirectory is an opaque routing identity
|
|
15
|
+
* and must retain its exact spelling. Load responses correlate only by opaque
|
|
16
|
+
* candidate ID. Activation responses contain only the transaction identity and
|
|
17
|
+
* plugin metadata consumed by Go; Node-only preparation data never crosses the
|
|
18
|
+
* transport boundary.
|
|
19
|
+
*/
|
|
20
|
+
declare const CONFIG_DISCOVERY_PROTOCOL_VERSION: 1;
|
|
21
|
+
|
|
22
|
+
declare interface ConfigModuleCandidate {
|
|
23
|
+
/** Opaque, transaction-local identity allocated by the Go coordinator. */
|
|
24
|
+
id: string;
|
|
25
|
+
/** Absolute path to the JS/TS config module Node must execute. */
|
|
26
|
+
configPath: string;
|
|
27
|
+
/** Go-authoritative directory used for config matching and plugin routing. */
|
|
28
|
+
configDirectory: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare type ConfigModuleLoadMode = 'cached' | 'fresh';
|
|
32
|
+
|
|
1
33
|
export declare interface Diagnostic {
|
|
2
34
|
ruleName: string;
|
|
3
35
|
message: string;
|
|
@@ -82,6 +114,9 @@ export declare interface GetAstInfoResponse {
|
|
|
82
114
|
flow?: FlowInfo;
|
|
83
115
|
}
|
|
84
116
|
|
|
117
|
+
/** Handler for a positive-id request frame sent by the Go peer. */
|
|
118
|
+
export declare type InboundRequestHandler = (message: IpcMessage) => unknown;
|
|
119
|
+
|
|
85
120
|
/**
|
|
86
121
|
* Index signature information
|
|
87
122
|
*/
|
|
@@ -97,10 +132,38 @@ export declare interface IpcMessage {
|
|
|
97
132
|
data: any;
|
|
98
133
|
}
|
|
99
134
|
|
|
135
|
+
/** Reverse-request handlers that are scoped to one outer lint request. */
|
|
136
|
+
export declare interface LintInboundHandlers {
|
|
137
|
+
pluginLint?: (request: unknown) => unknown;
|
|
138
|
+
loadConfigs?: (request: LoadConfigsRequest) => unknown;
|
|
139
|
+
activateConfigs?: (request: ActivateConfigsRequest) => unknown;
|
|
140
|
+
}
|
|
141
|
+
|
|
100
142
|
export declare interface LintOptions {
|
|
101
143
|
files?: string[];
|
|
144
|
+
canonicalFiles?: string[];
|
|
102
145
|
config?: Record<string, unknown>[];
|
|
146
|
+
/**
|
|
147
|
+
* Ask native Go to discover and route JS/TS configs for this lint request.
|
|
148
|
+
* `config` and `configDiscovery` are mutually exclusive. Browser/WASM
|
|
149
|
+
* backends intentionally do not advertise this host-filesystem capability.
|
|
150
|
+
*/
|
|
151
|
+
configDiscovery?: {
|
|
152
|
+
mode: 'auto' | 'explicit';
|
|
153
|
+
explicitConfigPath?: string;
|
|
154
|
+
/** Static glob roots; Go visits only branches leading to the supplied files. */
|
|
155
|
+
directories?: string[];
|
|
156
|
+
/** Parallel to `files`; true only for caller-literal file targets. */
|
|
157
|
+
explicitFiles?: boolean[];
|
|
158
|
+
/** Normalized API override entries appended to every selected config. */
|
|
159
|
+
overrideConfig?: Record<string, unknown>[];
|
|
160
|
+
};
|
|
161
|
+
eslintPlugins?: Array<{
|
|
162
|
+
prefix: string;
|
|
163
|
+
ruleNames: string[];
|
|
164
|
+
}>;
|
|
103
165
|
configDirectory?: string;
|
|
166
|
+
pluginConfigDirectory?: string;
|
|
104
167
|
workingDirectory?: string;
|
|
105
168
|
fileContents?: Record<string, string>;
|
|
106
169
|
includeEncodedSourceFiles?: boolean;
|
|
@@ -120,6 +183,21 @@ export declare interface LintResponse {
|
|
|
120
183
|
encodedSourceFiles?: Record<string, string>;
|
|
121
184
|
}
|
|
122
185
|
|
|
186
|
+
declare interface LoadConfigsRequest {
|
|
187
|
+
protocolVersion: typeof CONFIG_DISCOVERY_PROTOCOL_VERSION;
|
|
188
|
+
/** Isolates batches belonging to one discovery transaction. */
|
|
189
|
+
transactionId: string;
|
|
190
|
+
/**
|
|
191
|
+
* `cached` preserves one-shot CLI module-import semantics. `fresh` is used
|
|
192
|
+
* by long-lived API and editor refreshes; it cache-busts the entry module,
|
|
193
|
+
* while static transitive imports retain Node's normal module cache.
|
|
194
|
+
*/
|
|
195
|
+
loadMode: ConfigModuleLoadMode;
|
|
196
|
+
/** Serialize module evaluation when the CLI requested --singleThreaded. */
|
|
197
|
+
singleThreaded?: boolean;
|
|
198
|
+
candidates: ConfigModuleCandidate[];
|
|
199
|
+
}
|
|
200
|
+
|
|
123
201
|
/**
|
|
124
202
|
* Detailed information about an AST node
|
|
125
203
|
*/
|
|
@@ -224,9 +302,6 @@ export declare interface PendingMessage {
|
|
|
224
302
|
reject: (error: Error) => void;
|
|
225
303
|
}
|
|
226
304
|
|
|
227
|
-
/**
|
|
228
|
-
* Shared types for rslint IPC protocol across all environments
|
|
229
|
-
*/
|
|
230
305
|
declare interface Position {
|
|
231
306
|
line: number;
|
|
232
307
|
column: number;
|
|
@@ -249,24 +324,35 @@ export declare interface RSlintOptions {
|
|
|
249
324
|
*/
|
|
250
325
|
export declare class RSLintService {
|
|
251
326
|
private readonly service;
|
|
327
|
+
private activeLintHandlers;
|
|
328
|
+
private requestQueue;
|
|
329
|
+
private closeRequested;
|
|
330
|
+
private closePromise;
|
|
252
331
|
constructor(service: RslintServiceInterface);
|
|
253
332
|
/**
|
|
254
333
|
* Run the linter on specified files
|
|
255
334
|
*/
|
|
256
|
-
lint(options?: LintOptions): Promise<LintResponse>;
|
|
335
|
+
lint(options?: LintOptions, handlers?: LintInboundHandlers): Promise<LintResponse>;
|
|
336
|
+
private lintExclusive;
|
|
257
337
|
/**
|
|
258
338
|
* Get detailed AST information at a specific position
|
|
259
339
|
* Returns Node, Type, Symbol, Signature, and Flow information
|
|
260
340
|
*/
|
|
261
341
|
getAstInfo(options: GetAstInfoRequest): Promise<GetAstInfoResponse>;
|
|
342
|
+
private getAstInfoExclusive;
|
|
262
343
|
/**
|
|
263
344
|
* Close the service
|
|
264
345
|
*/
|
|
265
346
|
close(): Promise<void>;
|
|
347
|
+
private handshake;
|
|
348
|
+
private enqueue;
|
|
349
|
+
private handleInboundRequest;
|
|
266
350
|
}
|
|
267
351
|
|
|
268
352
|
export declare interface RslintServiceInterface {
|
|
269
353
|
sendMessage(kind: string, data: any): Promise<any>;
|
|
354
|
+
/** Optional for one-way backends such as the current browser worker. */
|
|
355
|
+
setInboundHandler?(handler: InboundRequestHandler | null): void;
|
|
270
356
|
terminate(): void;
|
|
271
357
|
}
|
|
272
358
|
|
package/dist/service.js
CHANGED
|
@@ -1,17 +1,45 @@
|
|
|
1
|
+
const API_REVERSE_PLUGIN_LINT_CAPABILITY = 'reversePluginLint';
|
|
2
|
+
const API_REVERSE_CONFIG_LOAD_CAPABILITY = 'reverseConfigLoadV1';
|
|
3
|
+
const EXIT_REQUEST_TIMEOUT_MS = 1000;
|
|
1
4
|
class RSLintService {
|
|
2
5
|
service;
|
|
6
|
+
activeLintHandlers = null;
|
|
7
|
+
requestQueue = Promise.resolve();
|
|
8
|
+
closeRequested = false;
|
|
9
|
+
closePromise = null;
|
|
3
10
|
constructor(service){
|
|
4
11
|
this.service = service;
|
|
12
|
+
this.service.setInboundHandler?.((message)=>this.handleInboundRequest(message));
|
|
5
13
|
}
|
|
6
|
-
async lint(options = {}) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
async lint(options = {}, handlers = {}) {
|
|
15
|
+
if (this.closeRequested) throw new Error('rslint service is closing');
|
|
16
|
+
return this.enqueue(async ()=>{
|
|
17
|
+
const hasLoadConfigs = Boolean(handlers.loadConfigs);
|
|
18
|
+
const hasActivateConfigs = Boolean(handlers.activateConfigs);
|
|
19
|
+
if (hasLoadConfigs !== hasActivateConfigs) throw new Error('reverse config discovery requires loadConfigs and activateConfigs handlers together');
|
|
20
|
+
if ((handlers.pluginLint || hasLoadConfigs || hasActivateConfigs) && !this.service.setInboundHandler) throw new Error('rslint backend does not support reverse lint requests');
|
|
21
|
+
this.activeLintHandlers = handlers;
|
|
22
|
+
try {
|
|
23
|
+
return await this.lintExclusive(options, {
|
|
24
|
+
pluginLint: Boolean(handlers.pluginLint),
|
|
25
|
+
configLoad: hasLoadConfigs && hasActivateConfigs
|
|
26
|
+
});
|
|
27
|
+
} finally{
|
|
28
|
+
this.activeLintHandlers = null;
|
|
29
|
+
}
|
|
10
30
|
});
|
|
31
|
+
}
|
|
32
|
+
async lintExclusive(options, requiredReverse) {
|
|
33
|
+
const { files, canonicalFiles, config, configDiscovery, eslintPlugins, configDirectory, pluginConfigDirectory, workingDirectory, fileContents, includeEncodedSourceFiles, fix } = options;
|
|
34
|
+
await this.handshake(requiredReverse);
|
|
11
35
|
return this.service.sendMessage('lint', {
|
|
12
36
|
files,
|
|
37
|
+
canonicalFiles,
|
|
13
38
|
config,
|
|
39
|
+
configDiscovery,
|
|
40
|
+
eslintPlugins,
|
|
14
41
|
configDirectory,
|
|
42
|
+
pluginConfigDirectory,
|
|
15
43
|
workingDirectory,
|
|
16
44
|
fileContents,
|
|
17
45
|
includeEncodedSourceFiles,
|
|
@@ -19,9 +47,17 @@ class RSLintService {
|
|
|
19
47
|
});
|
|
20
48
|
}
|
|
21
49
|
async getAstInfo(options) {
|
|
50
|
+
if (this.closeRequested) throw new Error('rslint service is closing');
|
|
51
|
+
return this.enqueue(async ()=>{
|
|
52
|
+
const response = await this.getAstInfoExclusive(options);
|
|
53
|
+
return response;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async getAstInfoExclusive(options) {
|
|
22
57
|
const { fileContent, position, end, kind, depth = 2, fileName, compilerOptions } = options;
|
|
23
|
-
await this.
|
|
24
|
-
|
|
58
|
+
await this.handshake({
|
|
59
|
+
pluginLint: false,
|
|
60
|
+
configLoad: false
|
|
25
61
|
});
|
|
26
62
|
return this.service.sendMessage('getAstInfo', {
|
|
27
63
|
fileContent,
|
|
@@ -34,10 +70,80 @@ class RSLintService {
|
|
|
34
70
|
});
|
|
35
71
|
}
|
|
36
72
|
async close() {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
73
|
+
if (this.closePromise) return void await this.closePromise;
|
|
74
|
+
this.closeRequested = true;
|
|
75
|
+
let timedOut = false;
|
|
76
|
+
let timer;
|
|
77
|
+
const timeout = new Promise((resolve)=>{
|
|
78
|
+
timer = setTimeout(()=>{
|
|
79
|
+
timedOut = true;
|
|
80
|
+
resolve();
|
|
81
|
+
}, EXIT_REQUEST_TIMEOUT_MS);
|
|
82
|
+
});
|
|
83
|
+
const gracefulExit = this.enqueue(async ()=>{
|
|
84
|
+
if (timedOut) return;
|
|
85
|
+
try {
|
|
86
|
+
await this.service.sendMessage('exit', {});
|
|
87
|
+
} catch {}
|
|
88
|
+
});
|
|
89
|
+
this.closePromise = (async ()=>{
|
|
90
|
+
try {
|
|
91
|
+
await Promise.race([
|
|
92
|
+
gracefulExit,
|
|
93
|
+
timeout
|
|
94
|
+
]);
|
|
95
|
+
} finally{
|
|
96
|
+
timedOut = true;
|
|
97
|
+
if (timer) clearTimeout(timer);
|
|
98
|
+
this.service.terminate();
|
|
99
|
+
}
|
|
100
|
+
})();
|
|
101
|
+
await this.closePromise;
|
|
102
|
+
}
|
|
103
|
+
async handshake(requiredReverse) {
|
|
104
|
+
const requestedCapabilities = [];
|
|
105
|
+
if (requiredReverse.pluginLint) requestedCapabilities.push(API_REVERSE_PLUGIN_LINT_CAPABILITY);
|
|
106
|
+
if (requiredReverse.configLoad) requestedCapabilities.push(API_REVERSE_CONFIG_LOAD_CAPABILITY);
|
|
107
|
+
const response = await this.service.sendMessage('handshake', {
|
|
108
|
+
version: "2.0.0",
|
|
109
|
+
capabilities: requestedCapabilities
|
|
110
|
+
});
|
|
111
|
+
if (null === response || 'object' != typeof response) throw new Error('rslint backend returned an invalid handshake response');
|
|
112
|
+
const handshake = response;
|
|
113
|
+
if (true !== handshake.ok || "2.0.0" !== handshake.version) throw new Error(`rslint API protocol mismatch: expected 2.0.0, received ${String(handshake.version)}`);
|
|
114
|
+
const capabilities = Array.isArray(handshake.capabilities) ? handshake.capabilities : [];
|
|
115
|
+
if (requiredReverse.pluginLint && !capabilities.includes(API_REVERSE_PLUGIN_LINT_CAPABILITY)) throw new Error('rslint backend does not support reverse pluginLint requests');
|
|
116
|
+
if (requiredReverse.configLoad && !capabilities.includes(API_REVERSE_CONFIG_LOAD_CAPABILITY)) throw new Error('rslint backend does not support reverse config loading');
|
|
117
|
+
}
|
|
118
|
+
async enqueue(operation) {
|
|
119
|
+
const result = this.requestQueue.then(operation, operation);
|
|
120
|
+
this.requestQueue = result.then(()=>void 0, ()=>void 0);
|
|
121
|
+
const value = await result;
|
|
122
|
+
return value;
|
|
123
|
+
}
|
|
124
|
+
handleInboundRequest(message) {
|
|
125
|
+
switch(message.kind){
|
|
126
|
+
case 'pluginLint':
|
|
127
|
+
{
|
|
128
|
+
const handler = this.activeLintHandlers?.pluginLint;
|
|
129
|
+
if (!handler) throw new Error('rslint service received pluginLint without an active plugin host');
|
|
130
|
+
return handler(message.data);
|
|
131
|
+
}
|
|
132
|
+
case 'loadConfigs':
|
|
133
|
+
{
|
|
134
|
+
const handler = this.activeLintHandlers?.loadConfigs;
|
|
135
|
+
if (!handler) throw new Error('rslint service received loadConfigs without an active config host');
|
|
136
|
+
return handler(message.data);
|
|
137
|
+
}
|
|
138
|
+
case 'activateConfigs':
|
|
139
|
+
{
|
|
140
|
+
const handler = this.activeLintHandlers?.activateConfigs;
|
|
141
|
+
if (!handler) throw new Error('rslint service received activateConfigs without an active config host');
|
|
142
|
+
return handler(message.data);
|
|
143
|
+
}
|
|
144
|
+
default:
|
|
145
|
+
throw new Error(`rslint service received unexpected inbound request '${message.kind}'`);
|
|
146
|
+
}
|
|
41
147
|
}
|
|
42
148
|
}
|
|
43
149
|
export { RSLintService };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rslint/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"@typescript/source": "./src/index.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@typescript/source": "./src/eslint-plugin/index.ts",
|
|
23
23
|
"default": "./dist/eslint-plugin/index.js"
|
|
24
24
|
},
|
|
25
|
-
"./bin": "./bin/rslint.
|
|
25
|
+
"./bin": "./bin/rslint.js",
|
|
26
26
|
"./package.json": "./package.json"
|
|
27
27
|
},
|
|
28
28
|
"type": "module",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"url": "https://github.com/web-infra-dev/rslint"
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
34
|
-
"bin/rslint.
|
|
34
|
+
"bin/rslint.js",
|
|
35
35
|
"dist/"
|
|
36
36
|
],
|
|
37
37
|
"bin": {
|
|
38
|
-
"rslint": "./bin/rslint.
|
|
38
|
+
"rslint": "./bin/rslint.js"
|
|
39
39
|
},
|
|
40
40
|
"publishConfig": {
|
|
41
41
|
"access": "public"
|
|
@@ -61,10 +61,10 @@
|
|
|
61
61
|
"eslint-visitor-keys": "5.0.1",
|
|
62
62
|
"espree": "11.2.0",
|
|
63
63
|
"esquery": "1.7.0",
|
|
64
|
-
"globals": "17.
|
|
64
|
+
"globals": "17.7.0",
|
|
65
65
|
"tinyglobby": "0.2.15",
|
|
66
66
|
"typescript": "5.9.3",
|
|
67
|
-
"@rslint/api": "0.
|
|
67
|
+
"@rslint/api": "0.7.0"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"jiti": "^2.0.0"
|
|
@@ -75,14 +75,14 @@
|
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
77
|
"optionalDependencies": {
|
|
78
|
-
"@rslint/native-
|
|
79
|
-
"@rslint/native-
|
|
80
|
-
"@rslint/native-
|
|
81
|
-
"@rslint/native-
|
|
82
|
-
"@rslint/native-linux-arm64-musl": "0.
|
|
83
|
-
"@rslint/native-win32-arm64-msvc": "0.
|
|
84
|
-
"@rslint/native-
|
|
85
|
-
"@rslint/native-
|
|
78
|
+
"@rslint/native-linux-arm64-gnu": "0.7.0",
|
|
79
|
+
"@rslint/native-darwin-arm64": "0.7.0",
|
|
80
|
+
"@rslint/native-darwin-x64": "0.7.0",
|
|
81
|
+
"@rslint/native-linux-x64-gnu": "0.7.0",
|
|
82
|
+
"@rslint/native-linux-arm64-musl": "0.7.0",
|
|
83
|
+
"@rslint/native-win32-arm64-msvc": "0.7.0",
|
|
84
|
+
"@rslint/native-win32-x64-msvc": "0.7.0",
|
|
85
|
+
"@rslint/native-linux-x64-musl": "0.7.0"
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
88
|
"picomatch": "4.0.4"
|
package/bin/rslint.cjs
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const startTime = Date.now();
|
|
3
|
-
const path = require('node:path');
|
|
4
|
-
const { pathToFileURL } = require('node:url');
|
|
5
|
-
const os = require('node:os');
|
|
6
|
-
|
|
7
|
-
function getBinPath() {
|
|
8
|
-
// The Go binary lives in the @rslint/native-{tuple} platform package, reached
|
|
9
|
-
// via its `./bin` export. Resolution is identical in dev and prod: `pnpm build`
|
|
10
|
-
// drops the host binary into npm/rslint/{tuple}/, and npm installs only the
|
|
11
|
-
// subpackage matching the host os/cpu/libc. On linux we just try gnu then musl
|
|
12
|
-
// and use whichever resolved — no libc sniffing (Go binaries are static, the
|
|
13
|
-
// gnu/musl distinction doesn't matter to them).
|
|
14
|
-
const arch = os.arch();
|
|
15
|
-
const tuples =
|
|
16
|
-
process.platform === 'linux'
|
|
17
|
-
? [`linux-${arch}-gnu`, `linux-${arch}-musl`]
|
|
18
|
-
: process.platform === 'win32'
|
|
19
|
-
? [`win32-${arch}-msvc`]
|
|
20
|
-
: [`${process.platform}-${arch}`];
|
|
21
|
-
for (const tuple of tuples) {
|
|
22
|
-
try {
|
|
23
|
-
return require.resolve(`@rslint/native-${tuple}/bin`);
|
|
24
|
-
} catch {}
|
|
25
|
-
}
|
|
26
|
-
throw new Error(
|
|
27
|
-
`rslint: no native binary for ${process.platform}-${arch} ` +
|
|
28
|
-
`(looked for @rslint/native-{${tuples.join(',')}})`,
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function main() {
|
|
33
|
-
const binPath = getBinPath();
|
|
34
|
-
const { run } = await import(
|
|
35
|
-
pathToFileURL(path.resolve(__dirname, '../dist/cli.js')).href
|
|
36
|
-
);
|
|
37
|
-
const exitCode = await run(binPath, process.argv.slice(2), startTime);
|
|
38
|
-
// process.exit() would tear down before async-buffered stdout writes (pipes,
|
|
39
|
-
// Windows TTYs) flush, truncating the lint tail. Setting exitCode lets the
|
|
40
|
-
// event loop drain naturally; run()'s cleanup guarantees nothing keeps it
|
|
41
|
-
// alive.
|
|
42
|
-
process.exitCode = exitCode;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
main().catch((err) => {
|
|
46
|
-
process.stderr.write(`rslint: ${err}\n`);
|
|
47
|
-
process.exitCode = 1;
|
|
48
|
-
});
|