Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 34 additions & 10 deletions packages/rslint/src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,26 @@ import type {
IpcMessage,
} from './types.js';

function isIpcMessage(value: unknown): value is IpcMessage {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'kind' in value &&
'data' in value
);
}

function getErrorMessage(data: unknown): string {
if (typeof data === 'object' && data !== null && 'message' in data) {
const record = data as Record<string, unknown>;
if (typeof record.message === 'string') {
return record.message;
}
}
return String(data);
}

/**
* Browser implementation of RslintService using web workers
*/
Expand Down Expand Up @@ -39,18 +59,18 @@ export class BrowserRslintService implements RslintServiceInterface {
/**
* Initialize the web worker
*/
private async ensureWorker(wasmUrl: string): Promise<Worker> {
private ensureWorker(wasmUrl: string): void {
if (!this.worker) {
this.worker = new Worker(this.workerUrl, { name: 'rslint-worker.js' });

this.worker.onmessage = event => {
this.worker.onmessage = (event: MessageEvent<Uint8Array>) => {
this.handlePacket(event.data);
};

this.worker.onerror = error => {
this.worker.onerror = (error: ErrorEvent) => {
console.error('Worker error:', error);
// Reject all pending messages
for (const [id, pending] of this.pendingMessages) {
for (const [, pending] of this.pendingMessages) {
pending.reject(new Error(`Worker error: ${error.message}`));
}
this.pendingMessages.clear();
Expand All @@ -60,7 +80,6 @@ export class BrowserRslintService implements RslintServiceInterface {
data: { version: '1.0.0', wasmURL: wasmUrl },
});
}
return this.worker;
}

/**
Expand Down Expand Up @@ -100,8 +119,12 @@ export class BrowserRslintService implements RslintServiceInterface {

// Handle the message
try {
const parsed: IpcMessage = JSON.parse(message);
this.handleResponse(parsed);
const parsed = JSON.parse(message) as unknown;
if (isIpcMessage(parsed)) {
this.handleResponse(parsed);
} else {
console.error('Invalid message format:', parsed);
}
} catch (err) {
console.error('Error parsing message:', err);
}
Expand Down Expand Up @@ -139,8 +162,8 @@ export class BrowserRslintService implements RslintServiceInterface {
/**
* Send a message to the worker
*/
async sendMessage(kind: string, data: any): Promise<any> {
return new Promise((resolve, reject) => {
async sendMessage(kind: string, data: unknown): Promise<unknown> {
const result = await new Promise<unknown>((resolve, reject) => {
const id = this.nextMessageId++;
const message: IpcMessage = { id, kind, data };

Expand All @@ -150,6 +173,7 @@ export class BrowserRslintService implements RslintServiceInterface {
// Send message to worker
this.worker!.postMessage(message);
});
return result;
}

/**
Expand All @@ -163,7 +187,7 @@ export class BrowserRslintService implements RslintServiceInterface {
this.pendingMessages.delete(id);

if (kind === 'error') {
pending.reject(new Error(data.message));
pending.reject(new Error(getErrorMessage(data)));
} else {
pending.resolve(data);
}
Expand Down
37 changes: 31 additions & 6 deletions packages/rslint/src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,26 @@ import type {
IpcMessage,
} from './types.js';

function isIpcMessage(value: unknown): value is IpcMessage {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'kind' in value &&
'data' in value
);
}

function getErrorMessage(data: unknown): string {
if (typeof data === 'object' && data !== null && 'message' in data) {
const record = data as Record<string, unknown>;
if (typeof record.message === 'string') {
return record.message;
}
}
return String(data);
}

/**
* Node.js implementation of RslintService using child processes
*/
Expand Down Expand Up @@ -38,7 +58,7 @@ export class NodeRslintService implements RslintServiceInterface {
});

// Set up binary message reading
this.process.stdout!.on('data', data => {
this.process.stdout!.on('data', (data: Buffer) => {
this.handleChunk(data);
});
this.chunks = [];
Expand All @@ -49,8 +69,8 @@ export class NodeRslintService implements RslintServiceInterface {
/**
* Send a message to the rslint process
*/
async sendMessage(kind: string, data: any): Promise<any> {
return new Promise((resolve, reject) => {
async sendMessage(kind: string, data: unknown): Promise<unknown> {
const result = await new Promise<unknown>((resolve, reject) => {
const id = this.nextMessageId++;
const message: IpcMessage = { id, kind, data };

Expand All @@ -67,6 +87,7 @@ export class NodeRslintService implements RslintServiceInterface {
Buffer.concat([length, Buffer.from(json, 'utf8')]),
);
});
return result;
}

/**
Expand Down Expand Up @@ -100,8 +121,12 @@ export class NodeRslintService implements RslintServiceInterface {

// Handle the message
try {
const parsed: IpcMessage = JSON.parse(message);
this.handleMessage(parsed);
const parsed = JSON.parse(message) as unknown;
if (isIpcMessage(parsed)) {
this.handleMessage(parsed);
} else {
console.error('Invalid message format:', parsed);
}
} catch (err) {
console.error('Error parsing message:', err);
}
Expand All @@ -124,7 +149,7 @@ export class NodeRslintService implements RslintServiceInterface {
this.pendingMessages.delete(id);

if (kind === 'error') {
pending.reject(new Error(data.message));
pending.reject(new Error(getErrorMessage(data)));
} else {
pending.resolve(data);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/rslint/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class RSLintService {
*/
async close(): Promise<void> {
return new Promise(resolve => {
this.service.sendMessage('exit', {}).finally(() => {
void this.service.sendMessage('exit', {}).finally(() => {
this.service.terminate();
resolve();
});
Expand Down
8 changes: 4 additions & 4 deletions packages/rslint/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface Diagnostic {
filePath: string;
range: Range;
severity?: string;
suggestions: any[];
suggestions: unknown[];
}

export interface LintResponse {
Expand Down Expand Up @@ -67,18 +67,18 @@ export interface RSlintOptions {
}

export interface PendingMessage {
resolve: (data: any) => void;
resolve: (data: unknown) => void;
reject: (error: Error) => void;
}

export interface IpcMessage {
id: number;
kind: string;
data: any;
data: unknown;
}

// Service interface that all implementations must follow
export interface RslintServiceInterface {
sendMessage(kind: string, data: any): Promise<any>;
sendMessage(kind: string, data: unknown): Promise<unknown>;
terminate(): void;
}
49 changes: 29 additions & 20 deletions packages/rslint/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
interface IpcMessage {
id: number;
kind: string;
data: any;
data: unknown;
}

// Global state for the worker
let rslintProcess: any = null;
let rslintProcess: unknown = null;
let nextMessageId = 1;
let pendingMessages = new Map<
number,
{ resolve: (data: any) => void; reject: (error: Error) => void }
{ resolve: (data: unknown) => void; reject: (error: Error) => void }
>();

/**
Expand All @@ -42,51 +42,58 @@ async function initializeRslint(): Promise<void> {
/**
* Send a message to the rslint process
*/
async function sendToRslint(kind: string, data: any): Promise<any> {
function sendToRslint(kind: string, data: unknown): Promise<unknown> {
if (!rslintProcess) {
throw new Error('Rslint process not initialized');
return Promise.reject(new Error('Rslint process not initialized'));
}

// In a real implementation, this would call the appropriate method on the rslint process
// For now, we'll simulate the response

switch (kind) {
case 'handshake':
return { version: '1.0.0', status: 'ok' };
return Promise.resolve({ version: '1.0.0', status: 'ok' });

case 'lint':
case 'lint': {
const files = (data as { files?: unknown[] }).files;
// Simulate linting response
return {
return Promise.resolve({
diagnostics: [],
errorCount: 0,
fileCount: data.files?.length || 0,
fileCount: files?.length || 0,
ruleCount: 0,
duration: '0ms',
};
});
}

case 'applyFixes':
case 'applyFixes': {
const fixData = data as {
fileContent: string;
diagnostics?: unknown[];
};
// Simulate apply fixes response
return {
fixedContent: [data.fileContent],
return Promise.resolve({
fixedContent: [fixData.fileContent],
wasFixed: false,
appliedCount: 0,
unappliedCount: data.diagnostics?.length || 0,
};
unappliedCount: fixData.diagnostics?.length || 0,
});
}

case 'exit':
rslintProcess = null;
return { status: 'ok' };
return Promise.resolve({ status: 'ok' });

default:
throw new Error(`Unknown message kind: ${kind}`);
return Promise.reject(new Error(`Unknown message kind: ${kind}`));
}
}

/**
* Handle messages from the main thread
*/
async function handleMessage(event: MessageEvent): Promise<void> {
const { id, kind, data } = event.data as IpcMessage;
async function handleMessage(event: MessageEvent<IpcMessage>): Promise<void> {
const { id, kind, data } = event.data;

try {
// Ensure rslint is initialized
Expand Down Expand Up @@ -135,7 +142,9 @@ function handleError(error: ErrorEvent): void {
}

// Set up event listeners
self.addEventListener('message', handleMessage);
self.addEventListener('message', (event: MessageEvent<IpcMessage>) => {
void handleMessage(event);
});
self.addEventListener('error', handleError);

// Initialize the worker
Expand Down
1 change: 1 addition & 0 deletions packages/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
export const AST_NODE_TYPES: any = {};
export enum AST_TOKEN_TYPES {
Identifier,
Expand Down
2 changes: 1 addition & 1 deletion packages/vscode-extension/__tests__/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ async function main() {
}
}

main();
void main();
2 changes: 1 addition & 1 deletion packages/vscode-extension/src/Extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class Extension implements Disposable {
this.logger.info('Rslint extension deactivating...');

const stopPromises = Array.from(this.rslintInstances.values()).map(
instance => instance.stop(),
async instance => instance.stop(),
);

try {
Expand Down
23 changes: 15 additions & 8 deletions packages/vscode-extension/src/Rslint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
LanguageClientOptions,
ServerOptions,
State,
StateChangeEvent,
Trace,
} from 'vscode-languageclient/node';
import { Logger } from './logger';
Expand Down Expand Up @@ -138,7 +139,9 @@ export class Rslint implements Disposable {
return this.client;
}

public onDidChangeState(listener: (event: any) => void): Disposable {
public onDidChangeState(
listener: (event: StateChangeEvent) => void,
): Disposable {
if (!this.client) {
throw new Error('Client is not initialized');
}
Expand Down Expand Up @@ -223,8 +226,10 @@ export class Rslint implements Disposable {

try {
this.logger.debug('Looking for Rslint binary in PnP mode');
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
const yarnPnpApi = require(yarnPnpFile.fsPath);
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-type-assertion
const yarnPnpApi = require(yarnPnpFile.fsPath) as {
resolveRequest(request: string, issuer: string): string | null;
};

const rslintCorePackage = yarnPnpApi.resolveRequest(
'@rslint/core/package.json',
Expand All @@ -235,12 +240,14 @@ export class Rslint implements Disposable {
continue;
}

const rslintPlatformPkg = Uri.file(
yarnPnpApi.resolveRequest(
PLATFORM_BIN_REQUEST,
rslintCorePackage,
) as string,
const platformPath = yarnPnpApi.resolveRequest(
PLATFORM_BIN_REQUEST,
rslintCorePackage,
);
if (!platformPath) {
continue;
}
const rslintPlatformPkg = Uri.file(platformPath);

if (await fileExists(rslintPlatformPkg)) {
this.logger.debug(
Expand Down
Loading
Loading