From eab0e873ab1e3c50f2921d15e12d1b190bc2d60b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 18:56:07 +0000 Subject: [PATCH 1/3] Initial plan From f96afdbdfa9158c3e192e0c55713cc9eac296cf1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 19:11:45 +0000 Subject: [PATCH 2/3] Fix TypeScript 5.9.3 protocol parsing in ts-html-plugin tests Co-authored-by: arthurfiorette <47537704+arthurfiorette@users.noreply.github.com> --- .../ts-html-plugin/test/util/lang-server.ts | 52 +++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/packages/ts-html-plugin/test/util/lang-server.ts b/packages/ts-html-plugin/test/util/lang-server.ts index 46187e176..52d0aea16 100644 --- a/packages/ts-html-plugin/test/util/lang-server.ts +++ b/packages/ts-html-plugin/test/util/lang-server.ts @@ -64,6 +64,7 @@ export class TSLangServer { isClosed = false; server: ChildProcess; sequence = 0; + buffer = ''; // Add buffer for incomplete messages constructor( projectPath: string, @@ -86,27 +87,48 @@ export class TSLangServer { this.server.stdout?.on('data', (data) => { try { - const obj = JSON.parse(data.split('\n', 3)[2]); + this.buffer += data; - if (this.debug) { - console.dir(obj, { depth: 10 }); - } + // Process all complete messages in the buffer + while (true) { + // TSServer protocol: Content-Length: N\r\n\r\n{JSON}\r\n + const headerMatch = this.buffer.match(/Content-Length: (\d+)\r?\n\r?\n/); + if (!headerMatch) break; + + const contentLength = parseInt(headerMatch[1], 10); + const headerEnd = headerMatch.index! + headerMatch[0].length; + const messageEnd = headerEnd + contentLength; + + // Check if we have the complete message + if (this.buffer.length < messageEnd) break; - if (obj.success === false) { - this.errorEmitter.emit(obj.type === 'event' ? obj.event : obj.command, obj); + // Extract and parse the message + const jsonStr = this.buffer.substring(headerEnd, messageEnd); + const obj = JSON.parse(jsonStr); + + // Remove the processed message from buffer + this.buffer = this.buffer.substring(messageEnd); + + if (this.debug) { + console.dir(obj, { depth: 10 }); + } - // Error is fatal, close the server - if (!this.isClosed) { - this.isClosed = true; - this.server.stdin?.end(); + if (obj.success === false) { + this.errorEmitter.emit(obj.type === 'event' ? obj.event : obj.command, obj); + + // Error is fatal, close the server + if (!this.isClosed) { + this.isClosed = true; + this.server.stdin?.end(); + } + } else if (obj.type === 'event') { + this.responseEventEmitter.emit(obj.event, obj); + } else if (obj.type === 'response') { + this.responseCommandEmitter.emit(obj.command, obj); } - } else if (obj.type === 'event') { - this.responseEventEmitter.emit(obj.event, obj); - } else if (obj.type === 'response') { - this.responseCommandEmitter.emit(obj.command, obj); } } catch (error) { - console.error(data); + console.error(this.buffer); this.exitPromise.reject(error); } }); From eff90c9c717a068f10cbe8daa481635aa84882ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 19:13:50 +0000 Subject: [PATCH 3/3] Refactor message processing into separate method for better code quality Co-authored-by: arthurfiorette <47537704+arthurfiorette@users.noreply.github.com> --- .../ts-html-plugin/test/util/lang-server.ts | 83 ++++++++++--------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/packages/ts-html-plugin/test/util/lang-server.ts b/packages/ts-html-plugin/test/util/lang-server.ts index 52d0aea16..591e77aa1 100644 --- a/packages/ts-html-plugin/test/util/lang-server.ts +++ b/packages/ts-html-plugin/test/util/lang-server.ts @@ -88,45 +88,7 @@ export class TSLangServer { this.server.stdout?.on('data', (data) => { try { this.buffer += data; - - // Process all complete messages in the buffer - while (true) { - // TSServer protocol: Content-Length: N\r\n\r\n{JSON}\r\n - const headerMatch = this.buffer.match(/Content-Length: (\d+)\r?\n\r?\n/); - if (!headerMatch) break; - - const contentLength = parseInt(headerMatch[1], 10); - const headerEnd = headerMatch.index! + headerMatch[0].length; - const messageEnd = headerEnd + contentLength; - - // Check if we have the complete message - if (this.buffer.length < messageEnd) break; - - // Extract and parse the message - const jsonStr = this.buffer.substring(headerEnd, messageEnd); - const obj = JSON.parse(jsonStr); - - // Remove the processed message from buffer - this.buffer = this.buffer.substring(messageEnd); - - if (this.debug) { - console.dir(obj, { depth: 10 }); - } - - if (obj.success === false) { - this.errorEmitter.emit(obj.type === 'event' ? obj.event : obj.command, obj); - - // Error is fatal, close the server - if (!this.isClosed) { - this.isClosed = true; - this.server.stdin?.end(); - } - } else if (obj.type === 'event') { - this.responseEventEmitter.emit(obj.event, obj); - } else if (obj.type === 'response') { - this.responseCommandEmitter.emit(obj.command, obj); - } - } + this.#processMessages(); } catch (error) { console.error(this.buffer); this.exitPromise.reject(error); @@ -134,6 +96,49 @@ export class TSLangServer { }); } + #processMessages() { + // Process all complete messages in the buffer + let headerMatch = this.buffer.match(/Content-Length: (\d+)\r?\n\r?\n/); + + while (headerMatch && headerMatch.index !== undefined) { + // TSServer protocol: Content-Length: N\r\n\r\n{JSON}\r\n + const contentLength = parseInt(headerMatch[1], 10); + const headerEnd = headerMatch.index + headerMatch[0].length; + const messageEnd = headerEnd + contentLength; + + // Check if we have the complete message + if (this.buffer.length < messageEnd) break; + + // Extract and parse the message + const jsonStr = this.buffer.substring(headerEnd, messageEnd); + const obj = JSON.parse(jsonStr); + + // Remove the processed message from buffer + this.buffer = this.buffer.substring(messageEnd); + + if (this.debug) { + console.dir(obj, { depth: 10 }); + } + + if (obj.success === false) { + this.errorEmitter.emit(obj.type === 'event' ? obj.event : obj.command, obj); + + // Error is fatal, close the server + if (!this.isClosed) { + this.isClosed = true; + this.server.stdin?.end(); + } + } else if (obj.type === 'event') { + this.responseEventEmitter.emit(obj.event, obj); + } else if (obj.type === 'response') { + this.responseCommandEmitter.emit(obj.command, obj); + } + + // Check for next message + headerMatch = this.buffer.match(/Content-Length: (\d+)\r?\n\r?\n/); + } + } + /** Opens the project, sends diagnostics request and returns the response */ async openWithDiagnostics(content: TemplateStringsArray, ...args: any[]) { const fileContent = `${TEST_HELPERS}\n${String.raw(content, ...args).trim()}`;