From 78a7df43a399dc1e00579270431b58ebd7f30fa8 Mon Sep 17 00:00:00 2001 From: Darien Pardinas Diaz Date: Sun, 1 Dec 2024 17:55:14 -0500 Subject: [PATCH 1/3] Add custom error message when an error is triggered when invoking the external formatter via HTTP request. --- src/services/formatter.ts | 12 +++++++++++- src/services/requestProcessor.ts | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/services/formatter.ts b/src/services/formatter.ts index b481c14..9295107 100644 --- a/src/services/formatter.ts +++ b/src/services/formatter.ts @@ -3,13 +3,18 @@ import * as http from "http"; import * as https from "https"; let formatterRegistration: vscode.Disposable; +const defaultErrorMessage = + "Error sending request to the external formatter. Make sure the formatter HTTP endpoint is up and running."; export async function registerExternalFormatter( formatterEndpoint: string, languages: string[], - httpMethod: string + httpMethod: string, + onErrorMessage: string ) { languages = languages || (await vscode.languages.getLanguages()); httpMethod = httpMethod || "POST"; + onErrorMessage = onErrorMessage || defaultErrorMessage; + if (formatterRegistration) { formatterRegistration.dispose(); } @@ -55,6 +60,11 @@ export async function registerExternalFormatter( ); }); }); + req.on("error", (err) => { + req.destroy(); + accept([]); // no edits + vscode.window.showErrorMessage(`${onErrorMessage} - ${err}`, "OK"); + }); req.write(payload); req.end(); }); diff --git a/src/services/requestProcessor.ts b/src/services/requestProcessor.ts index 9af1b03..d1f5b05 100644 --- a/src/services/requestProcessor.ts +++ b/src/services/requestProcessor.ts @@ -143,7 +143,7 @@ export async function processRemoteControlRequest(command: string, args: any[]): } if (command === "custom.registerExternalFormatter") { - return await registerExternalFormatter(args[0], args[1], args[2]); + return await registerExternalFormatter(args[0], args[1], args[2], args[3]); } // try to run an arbitrary command with the arguments provided as is From aa3bec41b464317b696d3b14ac39b6bf4a407d82 Mon Sep 17 00:00:00 2001 From: Darien Pardinas Diaz Date: Mon, 2 Dec 2024 09:35:17 -0500 Subject: [PATCH 2/3] Formatted all code with prettier --- package.json | 3 ++- src/extension.ts | 10 ++++---- src/services/formatter.ts | 8 +++---- src/services/logger.ts | 2 +- src/services/quickPick.ts | 2 +- src/test/suite/extension.test.ts | 18 +++++++++----- src/test/suite/sendPostRequest.ts | 8 +++---- webpack.config.js | 40 +++++++++++++++---------------- 8 files changed, 49 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 1a8b509..22a302e 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,8 @@ "test-watch": "tsc -watch -p ./", "pretest": "npm run test-compile && npm run lint", "lint": "eslint src --ext ts", - "test": "xvfb-run -a node ./out/test/runTest.js" + "test": "xvfb-run -a node ./out/test/runTest.js", + "format": "npx prettier --write './**/*.{js,jsx,mjs,cjs,ts,tsx}'" }, "devDependencies": { "@types/glob": "^7.1.6", diff --git a/src/extension.ts b/src/extension.ts index 807edc0..8b27a6a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -40,7 +40,7 @@ const startHttpServer = async ( context: vscode.ExtensionContext, host: string, port: number, - fallbackPorts: number[] + fallbackPorts: number[], ): Promise => { let isInUse = false; if (port) { @@ -127,7 +127,7 @@ const startHttpServer = async ( function getDefaultPortForWorkspace(): number { const identifier = vscode.workspace.workspaceFile ? vscode.workspace.workspaceFile.toString() - : vscode.workspace.workspaceFolders?.map(f => f.uri.toString()).join(""); + : vscode.workspace.workspaceFolders?.map((f) => f.uri.toString()).join(""); if (!identifier) { return 37100; } @@ -135,7 +135,7 @@ function getDefaultPortForWorkspace(): number { a = (a << 5) - a + b.charCodeAt(0); return a & a; }, 0); - const port = 37100 + (Math.abs(hash) % (65535-37100)); + const port = 37100 + (Math.abs(hash) % (65535 - 37100)); return port; } @@ -149,7 +149,7 @@ function httpPortToPid(context: vscode.ExtensionContext, port: number): string { function killPreviousVscodeProcessIfUsingTcpPort( context: vscode.ExtensionContext, - port: number | undefined + port: number | undefined, ) { Logger.info(`Checking if we need to kill previous process using port = ${port}`); if (!port) { @@ -188,7 +188,7 @@ function setupRestControl(context: vscode.ExtensionContext) { context, "127.0.0.1", port, - (fallbackPorts || []).filter((p: number) => p !== port) + (fallbackPorts || []).filter((p: number) => p !== port), ); Logger.info("VSCode REST Control is now active!"); } else { diff --git a/src/services/formatter.ts b/src/services/formatter.ts index 9295107..127cd48 100644 --- a/src/services/formatter.ts +++ b/src/services/formatter.ts @@ -9,7 +9,7 @@ export async function registerExternalFormatter( formatterEndpoint: string, languages: string[], httpMethod: string, - onErrorMessage: string + onErrorMessage: string, ) { languages = languages || (await vscode.languages.getLanguages()); httpMethod = httpMethod || "POST"; @@ -21,7 +21,7 @@ export async function registerExternalFormatter( const url = new URL(formatterEndpoint); formatterRegistration = vscode.languages.registerDocumentFormattingEditProvider(languages, { provideDocumentFormattingEdits( - doc: vscode.TextDocument + doc: vscode.TextDocument, ): vscode.ProviderResult { const payload = JSON.stringify({ file: doc.fileName, @@ -40,7 +40,7 @@ export async function registerExternalFormatter( }; const range = new vscode.Range( doc.lineAt(0).range.start, - doc.lineAt(doc.lineCount - 1).range.end + doc.lineAt(doc.lineCount - 1).range.end, ); return new Promise((accept, reject) => { const httpModule = url.protocol.startsWith("https") ? https : http; @@ -56,7 +56,7 @@ export async function registerExternalFormatter( accept([]); // no edits vscode.window.showErrorMessage( `Failed to format document with custom formatter: ${err}`, - "OK" + "OK", ); }); }); diff --git a/src/services/logger.ts b/src/services/logger.ts index 2d2456e..dc0c9c5 100644 --- a/src/services/logger.ts +++ b/src/services/logger.ts @@ -22,7 +22,7 @@ export class Logger { } Logger.channel?.appendLine( - `["${type}" - ${new Date().getHours()}:${new Date().getMinutes()}] ${message}` + `["${type}" - ${new Date().getHours()}:${new Date().getMinutes()}] ${message}`, ); } diff --git a/src/services/quickPick.ts b/src/services/quickPick.ts index d04ad0e..6a7b804 100644 --- a/src/services/quickPick.ts +++ b/src/services/quickPick.ts @@ -22,7 +22,7 @@ export async function quickPick(data: any) { picker.onDidHide(() => { resolve(picker.selectedItems); disposable.dispose(); - }) + }), ); for (const item of picker.items) { if (item.label === defaultLabel) { diff --git a/src/test/suite/extension.test.ts b/src/test/suite/extension.test.ts index e40a955..33b6235 100644 --- a/src/test/suite/extension.test.ts +++ b/src/test/suite/extension.test.ts @@ -6,12 +6,14 @@ import { EXTENSION_ID } from "../../extension"; import { makeRequest } from "./sendPostRequest"; suite("Extension Test Suite", () => { - suiteSetup(async () => { }); + suiteSetup(async () => {}); - suiteTeardown(async () => { }); + suiteTeardown(async () => {}); test("check can list extensions", async () => { - const extensionIds: string[] = (await makeRequest("custom.listInstalledExtensions")) as string[]; + const extensionIds: string[] = (await makeRequest( + "custom.listInstalledExtensions", + )) as string[]; assert(extensionIds.includes(EXTENSION_ID)); }).timeout(5000); @@ -19,12 +21,17 @@ suite("Extension Test Suite", () => { const workspaceFolders = (await makeRequest("custom.workspaceFolders")) as string[]; assert(workspaceFolders.length === 1); const ws = workspaceFolders[0] as any; - assert(ws.name === 'workspace1'); + assert(ws.name === "workspace1"); assert(ws.index === 0); assert(ws.uri.startsWith("file://")); assert(ws.uri.endsWith("/workspace1")); - const workspaceFile = await makeRequest("custom.workspaceFile", undefined, undefined, false) as any; + const workspaceFile = (await makeRequest( + "custom.workspaceFile", + undefined, + undefined, + false, + )) as any; assert(workspaceFile === null); // no workspace file }); @@ -32,5 +39,4 @@ suite("Extension Test Suite", () => { const commands: string[] = (await makeRequest("custom.getCommands")) as string[]; assert(commands.length > 100); }); - }); diff --git a/src/test/suite/sendPostRequest.ts b/src/test/suite/sendPostRequest.ts index 5392851..3922064 100644 --- a/src/test/suite/sendPostRequest.ts +++ b/src/test/suite/sendPostRequest.ts @@ -5,18 +5,18 @@ export async function makeRequest( command: string, args: any[] = [], port: number = 0, - urlEncoded = true + urlEncoded = true, ) { return new Promise((resolve, reject) => { const path = urlEncoded ? `/?command=${command}&args=${encodeURIComponent(JSON.stringify(args))}` - : '/'; + : "/"; const req = http.request( { method: "POST", hostname: "localhost", port: port || getListeningPort(), - path: path + path: path, }, (res) => { const chunks: any[] = []; @@ -25,7 +25,7 @@ export async function makeRequest( const resBody = Buffer.concat(chunks); resolve(JSON.parse(resBody.toString())); }); - } + }, ); req.on("error", reject); if (!urlEncoded) { diff --git a/webpack.config.js b/webpack.config.js index ec5c0a4..7cfe026 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,30 +1,30 @@ //@ts-check -'use strict'; +"use strict"; -const path = require('path'); +const path = require("path"); /**@type {import('webpack').Configuration}*/ const config = { - target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ - mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') + target: "node", // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ + mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production') - entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ + entry: "./src/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ output: { // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ - path: path.resolve(__dirname, 'out'), - filename: 'extension.js', - libraryTarget: 'commonjs2' + path: path.resolve(__dirname, "out"), + filename: "extension.js", + libraryTarget: "commonjs2", }, - devtool: 'nosources-source-map', + devtool: "nosources-source-map", externals: { - bufferutil: 'bufferutil', - 'utf-8-validate': 'commonjs utf-8-validate', - vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ + bufferutil: "bufferutil", + "utf-8-validate": "commonjs utf-8-validate", + vscode: "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ }, resolve: { // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader - extensions: ['.ts', '.js'] + extensions: [".ts", ".js"], }, module: { rules: [ @@ -33,11 +33,11 @@ const config = { exclude: /node_modules/, use: [ { - loader: 'ts-loader' - } - ] - } - ] - } + loader: "ts-loader", + }, + ], + }, + ], + }, }; -module.exports = config; \ No newline at end of file +module.exports = config; From d95cfb968206a579885b713967f3f93efc7e30f9 Mon Sep 17 00:00:00 2001 From: Darien Pardinas Diaz Date: Mon, 2 Dec 2024 10:00:36 -0500 Subject: [PATCH 3/3] Added linter check to CICD Added one unit test for custom.eval --- .github/workflows/cicd.yml | 3 + CHANGELOG.md | 5 + package-lock.json | 260 ++++++++++++------------------- package.json | 4 +- src/extension.ts | 47 +++--- src/services/formatter.ts | 2 + src/services/requestProcessor.ts | 5 +- src/test/suite/extension.test.ts | 20 ++- 8 files changed, 155 insertions(+), 191 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 3f29e08..49d3d30 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -21,6 +21,9 @@ jobs: - name: Install npm dependencies run: npm install + - name: Run compile, formatting and linting checks + run: npm run pretest + - name: Build and run automation tests run: npm run test diff --git a/CHANGELOG.md b/CHANGELOG.md index bc427fb..f6cf1b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## [0.0.15] + +- Added custom error message when registering an external formatter via `custom.registerExternalFormatter`. +- Improved unit tests and linter checks (added code formatting check) + ## [0.0.14] - Added `custom.registerExternalFormatter` to support external formatters that send code snippets to a HTTP endpoint. diff --git a/package-lock.json b/package-lock.json index 1a753bf..fd13108 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-rest-control", - "version": "0.0.4", + "version": "0.0.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-rest-control", - "version": "0.0.4", + "version": "0.0.15", "license": "MIT", "dependencies": { "tcp-port-used": "^1.0.2" @@ -37,37 +37,25 @@ } }, "node_modules/@azure/abort-controller": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", - "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", - "dev": true, - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@azure/core-auth": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.2.tgz", - "integrity": "sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", "dev": true, "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-util": "^1.1.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@azure/core-auth/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "node_modules/@azure/core-auth": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz", + "integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==", "dev": true, "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.11.0", "tslib": "^2.6.2" }, "engines": { @@ -92,28 +80,16 @@ "node": ">=18.0.0" } }, - "node_modules/@azure/core-client/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", - "dev": true, - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@azure/core-rest-pipeline": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.16.0.tgz", - "integrity": "sha512-CeuTvsXxCUmEuxH5g/aceuSl6w2EugvNHKAtKKVdiX915EjJJxAwfzNNWZreNnbxHZ2fi0zaM6wwS23x2JVqSQ==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.1.tgz", + "integrity": "sha512-/wS73UEDrxroUEVywEm7J0p2c+IIiVxyfigCGfsKvCxxCET4V/Hef2aURqltrXMRjNmdmt5IuOgIpl8f6xdO5A==", "dev": true, "dependencies": { "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.4.0", + "@azure/core-auth": "^1.8.0", "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.9.0", + "@azure/core-util": "^1.11.0", "@azure/logger": "^1.0.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", @@ -123,18 +99,6 @@ "node": ">=18.0.0" } }, - "node_modules/@azure/core-rest-pipeline/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", - "dev": true, - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@azure/core-rest-pipeline/node_modules/agent-base": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", @@ -186,9 +150,9 @@ } }, "node_modules/@azure/core-util": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.0.tgz", - "integrity": "sha512-AfalUQ1ZppaKuxPPMsFEUdX6GZPB3d9paR9d/TTL7Ow2De8cJaC7ibi7kWVlFAVPCYo31OcnGymc0R89DX8Oaw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.11.0.tgz", + "integrity": "sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==", "dev": true, "dependencies": { "@azure/abort-controller": "^2.0.0", @@ -198,33 +162,21 @@ "node": ">=18.0.0" } }, - "node_modules/@azure/core-util/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", - "dev": true, - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@azure/identity": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.2.0.tgz", - "integrity": "sha512-ve3aYv79qXOJ8wRxQ5jO0eIz2DZ4o0TyME4m4vlGV5YyePddVZ+pFMzusAMODNAflYAAv1cBIhKnd4xytmXyig==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.5.0.tgz", + "integrity": "sha512-EknvVmtBuSIic47xkOqyNabAme0RYTw52BTMz8eBgU1ysTyMrD1uOoM+JdS0J/4Yfp98IBT3osqq3BfwSaNaGQ==", "dev": true, "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.5.0", - "@azure/core-client": "^1.4.0", - "@azure/core-rest-pipeline": "^1.1.0", + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.3.0", + "@azure/core-util": "^1.11.0", "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^3.11.1", - "@azure/msal-node": "^2.6.6", + "@azure/msal-browser": "^3.26.1", + "@azure/msal-node": "^2.15.0", "events": "^3.0.0", "jws": "^4.0.0", "open": "^8.0.0", @@ -248,33 +200,33 @@ } }, "node_modules/@azure/msal-browser": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.14.0.tgz", - "integrity": "sha512-Un85LhOoecJ3HDTS3Uv3UWnXC9/43ZSO+Kc+anSqpZvcEt58SiO/3DuVCAe1A3I5UIBYJNMgTmZPGXQ0MVYrwA==", + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.27.0.tgz", + "integrity": "sha512-+b4ZKSD8+vslCtVRVetkegEhOFMLP3rxDWJY212ct+2r6jVg6OSQKc1Qz3kCoXo0FgwaXkb+76TMZfpHp8QtgA==", "dev": true, "dependencies": { - "@azure/msal-common": "14.10.0" + "@azure/msal-common": "14.16.0" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "14.10.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.10.0.tgz", - "integrity": "sha512-Zk6DPDz7e1wPgLoLgAp0349Yay9RvcjPM5We/ehuenDNsz/t9QEFI7tRoHpp/e47I4p20XE3FiDlhKwAo3utDA==", + "version": "14.16.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", + "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", "dev": true, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-node": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.8.1.tgz", - "integrity": "sha512-VcZZM+5VvCWRBTOF7SxMKaxrz+EXjntx2u5AQe7QE06e6FuPJElGBrImgNgCh5QmFaNCfVFO+3qNR7UoFD/Gfw==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.2.tgz", + "integrity": "sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==", "dev": true, "dependencies": { - "@azure/msal-common": "14.10.0", + "@azure/msal-common": "14.16.0", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, @@ -617,9 +569,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "node_modules/@types/glob": { @@ -1375,12 +1327,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1393,9 +1345,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -1412,10 +1364,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -1536,9 +1488,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001620", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz", - "integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==", + "version": "1.0.30001685", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001685.tgz", + "integrity": "sha512-e/kJN1EMyHQzgcMEEgoo+YTCO1NGCmIYHk5Qk8jT6AazWemS5QFKJ5ShCJlH3GZrNIdZofcNCEwZqbMjjKzmnA==", "dev": true, "funding": [ { @@ -1751,9 +1703,9 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -2001,9 +1953,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.774", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.774.tgz", - "integrity": "sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==", + "version": "1.5.67", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz", + "integrity": "sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==", "dev": true }, "node_modules/emoji-regex": { @@ -2032,9 +1984,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", - "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -2121,9 +2073,9 @@ "dev": true }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "engines": { "node": ">=6" @@ -2539,9 +2491,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -3796,12 +3748,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -4131,9 +4083,9 @@ "optional": true }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "node_modules/normalize-path": { @@ -4383,9 +4335,9 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "node_modules/picomatch": { @@ -5650,9 +5602,9 @@ "dev": true }, "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -5669,8 +5621,8 @@ } ], "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -5743,21 +5695,20 @@ } }, "node_modules/webpack": { - "version": "5.91.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", - "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "version": "5.96.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", + "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "dev": true, "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.21.10", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -5869,9 +5820,9 @@ } }, "node_modules/webpack/node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -5880,15 +5831,6 @@ "node": ">=0.4.0" } }, - "node_modules/webpack/node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6033,9 +5975,9 @@ "dev": true }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" diff --git a/package.json b/package.json index 22a302e..0f20717 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "This extension allows you to remotely control Visual Studio Code via a REST endpoint, taking automation to the next level.", "publisher": "dpar39", "license": "MIT", - "version": "0.0.14", + "version": "0.0.15", "engines": { "vscode": "^1.55.0" }, @@ -73,7 +73,7 @@ "test-compile": "tsc -p ./", "test-watch": "tsc -watch -p ./", "pretest": "npm run test-compile && npm run lint", - "lint": "eslint src --ext ts", + "lint": "eslint src --ext ts && npx prettier --check './**/*.{js,ts}'", "test": "xvfb-run -a node ./out/test/runTest.js", "format": "npx prettier --write './**/*.{js,jsx,mjs,cjs,ts,tsx}'" }, diff --git a/src/extension.ts b/src/extension.ts index 8b27a6a..2b36184 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -60,51 +60,50 @@ const startHttpServer = async ( } } - const endBadRequest = (err: any, res: ServerResponse) => { - res.statusCode = 400; - const errStringJson = JSON.stringify(err, Object.getOwnPropertyNames(err)); + const sendResponse = (res: ServerResponse, body: string, statusCode: number = 200) => { + res.statusCode = statusCode; res.setHeader("Access-Control-Allow-Origin", "*"); - res.write(errStringJson); + res.setHeader("Content-Type", "application/json"); + res.write(body); res.end(); + }; + + const endBadRequest = (err: any, res: ServerResponse) => { + const errStringJson = JSON.stringify(err, Object.getOwnPropertyNames(err)); + sendResponse(res, errStringJson, 400); Logger.error(errStringJson); }; const processRequest = (cmd: string, args: string[], res: ServerResponse) => { processRemoteControlRequest(cmd, args) - .then((data) => { - res.setHeader("Content-Type", "application/json"); - res.setHeader("Access-Control-Allow-Origin", "*"); - res.write(JSON.stringify(data || null)); - res.end(); - }) + .then((data) => sendResponse(res, JSON.stringify(data || null))) .catch((err) => endBadRequest(err, res)); }; const requestHandler = (req: IncomingMessage, res: ServerResponse) => { - let body = ""; - let controlCommand: any = {}; + let command: string | null; + let args: any[] = []; if (req.url && req.url.indexOf("?") >= 0) { const url = new URL(req.url, `http://${req.headers.host}/`); const queryParams = new URLSearchParams(url.search); - try { - const cmd = queryParams.get("command"); - const args = queryParams.has("args") - ? JSON.parse(decodeURIComponent(queryParams.get("args")!)) - : []; - Logger.info(`Remote request command=${cmd}, args=${args}`); - processRequest(cmd!, args, res); - } catch (err) { - endBadRequest(err, res); + command = queryParams.get("command"); + if (queryParams.has("args")) { + args = JSON.parse(decodeURIComponent(queryParams.get("args")!)); } } - + let body = ""; req.on("data", (chunk) => { body += chunk; }); req.on("end", () => { Logger.info(`Remote request payload: ${body}`); - const reqData = body ? JSON.parse(body) : controlCommand; - processRequest(reqData.command, reqData.args || [], res); + if (body) { + const reqData = JSON.parse(body); + command = reqData.command || command; + args = reqData.args || args; + } + Logger.info(`Remote Control request with command=${command}, args=${args}`); + processRequest(command!, args, res); }); }; // Start the HTTP server diff --git a/src/services/formatter.ts b/src/services/formatter.ts index 127cd48..7b56631 100644 --- a/src/services/formatter.ts +++ b/src/services/formatter.ts @@ -34,7 +34,9 @@ export async function registerExternalFormatter( path: url.pathname, method: httpMethod, headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention "Content-Type": "application/json", + // eslint-disable-next-line @typescript-eslint/naming-convention "Content-Length": Buffer.byteLength(payload), }, }; diff --git a/src/services/requestProcessor.ts b/src/services/requestProcessor.ts index d1f5b05..d235615 100644 --- a/src/services/requestProcessor.ts +++ b/src/services/requestProcessor.ts @@ -58,8 +58,7 @@ export async function processRemoteControlRequest(command: string, args: any[]): } if (command === "custom.eval") { - eval(args[0]); - return; + return eval(args[0]); } if (command === "custom.listInstalledExtensions") { @@ -125,7 +124,7 @@ export async function processRemoteControlRequest(command: string, args: any[]): } let uri = null; if (!path.isAbsolute(filePath)) { - let candidates = await vscode.workspace.findFiles(args[0]); + let candidates = await vscode.workspace.findFiles(filePath); if (candidates.length === 1) { uri = candidates[0]; } diff --git a/src/test/suite/extension.test.ts b/src/test/suite/extension.test.ts index 33b6235..9a929f1 100644 --- a/src/test/suite/extension.test.ts +++ b/src/test/suite/extension.test.ts @@ -1,5 +1,6 @@ import * as assert from "assert"; import { EXTENSION_ID } from "../../extension"; +import * as fs from "fs"; // You can import and use all API from the 'vscode' module // as well as import your extension to test it @@ -18,9 +19,9 @@ suite("Extension Test Suite", () => { }).timeout(5000); test("get workspace folders", async () => { - const workspaceFolders = (await makeRequest("custom.workspaceFolders")) as string[]; + const workspaceFolders = (await makeRequest("custom.workspaceFolders")) as any[]; assert(workspaceFolders.length === 1); - const ws = workspaceFolders[0] as any; + const ws = workspaceFolders[0]; assert(ws.name === "workspace1"); assert(ws.index === 0); assert(ws.uri.startsWith("file://")); @@ -35,8 +36,21 @@ suite("Extension Test Suite", () => { assert(workspaceFile === null); // no workspace file }); - test("get all commands registred in vscode", async () => { + test("get all commands registered in vscode", async () => { const commands: string[] = (await makeRequest("custom.getCommands")) as string[]; assert(commands.length > 100); }); + + test("test can open document and get its content", async () => { + const xx = await makeRequest("custom.goToFileLineCharacter", ["demo.py:17:28"]); + const content: string = (await makeRequest("custom.eval", [ + "vscode.window.activeTextEditor?.document.getText()", + ])) as string; + const workspaceFolders = (await makeRequest("custom.workspaceFolders")) as any[]; + const workspaceAbsPath = workspaceFolders[0].uri.slice("file://".length); + const expectedContent = fs.readFileSync(workspaceAbsPath + "/demo.py", { + encoding: "utf-8", + }); + assert(content === expectedContent); + }); });