diff --git a/packages/rslint/src/browser.ts b/packages/rslint/src/browser.ts index 12f144ab..7e23b89c 100644 --- a/packages/rslint/src/browser.ts +++ b/packages/rslint/src/browser.ts @@ -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; + if (typeof record.message === 'string') { + return record.message; + } + } + return String(data); +} + /** * Browser implementation of RslintService using web workers */ @@ -39,18 +59,18 @@ export class BrowserRslintService implements RslintServiceInterface { /** * Initialize the web worker */ - private async ensureWorker(wasmUrl: string): Promise { + 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) => { 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(); @@ -60,7 +80,6 @@ export class BrowserRslintService implements RslintServiceInterface { data: { version: '1.0.0', wasmURL: wasmUrl }, }); } - return this.worker; } /** @@ -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); } @@ -139,8 +162,8 @@ export class BrowserRslintService implements RslintServiceInterface { /** * Send a message to the worker */ - async sendMessage(kind: string, data: any): Promise { - return new Promise((resolve, reject) => { + async sendMessage(kind: string, data: unknown): Promise { + const result = await new Promise((resolve, reject) => { const id = this.nextMessageId++; const message: IpcMessage = { id, kind, data }; @@ -150,6 +173,7 @@ export class BrowserRslintService implements RslintServiceInterface { // Send message to worker this.worker!.postMessage(message); }); + return result; } /** @@ -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); } diff --git a/packages/rslint/src/node.ts b/packages/rslint/src/node.ts index 0e64fd2e..2e7eda45 100644 --- a/packages/rslint/src/node.ts +++ b/packages/rslint/src/node.ts @@ -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; + if (typeof record.message === 'string') { + return record.message; + } + } + return String(data); +} + /** * Node.js implementation of RslintService using child processes */ @@ -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 = []; @@ -49,8 +69,8 @@ export class NodeRslintService implements RslintServiceInterface { /** * Send a message to the rslint process */ - async sendMessage(kind: string, data: any): Promise { - return new Promise((resolve, reject) => { + async sendMessage(kind: string, data: unknown): Promise { + const result = await new Promise((resolve, reject) => { const id = this.nextMessageId++; const message: IpcMessage = { id, kind, data }; @@ -67,6 +87,7 @@ export class NodeRslintService implements RslintServiceInterface { Buffer.concat([length, Buffer.from(json, 'utf8')]), ); }); + return result; } /** @@ -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); } @@ -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); } diff --git a/packages/rslint/src/service.ts b/packages/rslint/src/service.ts index 224665bf..4fed74ee 100644 --- a/packages/rslint/src/service.ts +++ b/packages/rslint/src/service.ts @@ -67,7 +67,7 @@ export class RSLintService { */ async close(): Promise { return new Promise(resolve => { - this.service.sendMessage('exit', {}).finally(() => { + void this.service.sendMessage('exit', {}).finally(() => { this.service.terminate(); resolve(); }); diff --git a/packages/rslint/src/types.ts b/packages/rslint/src/types.ts index a3e3900c..b0f50d34 100644 --- a/packages/rslint/src/types.ts +++ b/packages/rslint/src/types.ts @@ -18,7 +18,7 @@ export interface Diagnostic { filePath: string; range: Range; severity?: string; - suggestions: any[]; + suggestions: unknown[]; } export interface LintResponse { @@ -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; + sendMessage(kind: string, data: unknown): Promise; terminate(): void; } diff --git a/packages/rslint/src/worker.ts b/packages/rslint/src/worker.ts index 03b96b11..fafff6bf 100644 --- a/packages/rslint/src/worker.ts +++ b/packages/rslint/src/worker.ts @@ -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 } >(); /** @@ -42,9 +42,9 @@ async function initializeRslint(): Promise { /** * Send a message to the rslint process */ -async function sendToRslint(kind: string, data: any): Promise { +function sendToRslint(kind: string, data: unknown): Promise { 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 @@ -52,41 +52,48 @@ async function sendToRslint(kind: string, data: any): Promise { 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 { - const { id, kind, data } = event.data as IpcMessage; +async function handleMessage(event: MessageEvent): Promise { + const { id, kind, data } = event.data; try { // Ensure rslint is initialized @@ -135,7 +142,9 @@ function handleError(error: ErrorEvent): void { } // Set up event listeners -self.addEventListener('message', handleMessage); +self.addEventListener('message', (event: MessageEvent) => { + void handleMessage(event); +}); self.addEventListener('error', handleError); // Initialize the worker diff --git a/packages/utils/index.ts b/packages/utils/index.ts index 982aaf66..1b8d8d0a 100644 --- a/packages/utils/index.ts +++ b/packages/utils/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ export const AST_NODE_TYPES: any = {}; export enum AST_TOKEN_TYPES { Identifier, diff --git a/packages/vscode-extension/__tests__/runTest.ts b/packages/vscode-extension/__tests__/runTest.ts index f0b3452c..8b95d782 100644 --- a/packages/vscode-extension/__tests__/runTest.ts +++ b/packages/vscode-extension/__tests__/runTest.ts @@ -25,4 +25,4 @@ async function main() { } } -main(); +void main(); diff --git a/packages/vscode-extension/src/Extension.ts b/packages/vscode-extension/src/Extension.ts index 4a83466b..1dc5f3d2 100644 --- a/packages/vscode-extension/src/Extension.ts +++ b/packages/vscode-extension/src/Extension.ts @@ -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 { diff --git a/packages/vscode-extension/src/Rslint.ts b/packages/vscode-extension/src/Rslint.ts index d43c3785..9849f192 100644 --- a/packages/vscode-extension/src/Rslint.ts +++ b/packages/vscode-extension/src/Rslint.ts @@ -12,6 +12,7 @@ import { LanguageClientOptions, ServerOptions, State, + StateChangeEvent, Trace, } from 'vscode-languageclient/node'; import { Logger } from './logger'; @@ -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'); } @@ -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', @@ -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( diff --git a/packages/vscode-extension/src/logger.ts b/packages/vscode-extension/src/logger.ts index 462f50f1..be80ca96 100644 --- a/packages/vscode-extension/src/logger.ts +++ b/packages/vscode-extension/src/logger.ts @@ -49,11 +49,7 @@ export class Logger { this.log(LogLevel.WARN, message, ...args); } - public error( - message: string, - error?: Error | unknown, - ...args: unknown[] - ): void { + public error(message: string, error?: unknown, ...args: unknown[]): void { if (error instanceof Error) { this.log( LogLevel.ERROR, diff --git a/rslint.json b/rslint.json index 1b61cd7b..f788fa48 100644 --- a/rslint.json +++ b/rslint.json @@ -8,10 +8,12 @@ "./packages/rslint-test-tools/tests/**/*.test.ts", "packages/rslint/fixtures/**", "packages/rslint-test-tools/tests/**/*.test.ts", + "packages/rslint-test-tools/tests/**", "packages/rslint-test-tools/tests/typescript-eslint/rules/prefer-optional-chain/base-cases.ts", "packages/rslint-test-tools/tests/typescript-eslint/rules/naming-convention/cases/createTestCases.ts", "packages/rslint-test-tools/tests/typescript-eslint/areOptionsValid.ts", - "packages/rule-tester/src/index.ts" + "packages/rule-tester/src/**", + "packages/rslint/src/worker.ts" ], "languageOptions": { "parserOptions": {