From c99e75d837ac1e37793209a08c6a1e8915627806 Mon Sep 17 00:00:00 2001 From: Aasim Khan Date: Wed, 8 Oct 2025 13:46:05 -0700 Subject: [PATCH 1/2] adding new lint rule to prevent webviews from using extension code --- eslint/custom-rules/banned-imports.js | 33 +++++++++++++++++++++++---- eslint/custom-rules/package.json | 2 +- yarn.lock | 2 +- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/eslint/custom-rules/banned-imports.js b/eslint/custom-rules/banned-imports.js index 452c559ac5..bc7752b142 100644 --- a/eslint/custom-rules/banned-imports.js +++ b/eslint/custom-rules/banned-imports.js @@ -1,5 +1,6 @@ "use strict"; const path = require("path"); +const module = require("module"); function toPosix(p) { return p.split(path.sep).join("/"); // turns "\" → "/" @@ -9,8 +10,7 @@ module.exports = { meta: { type: "suggestion", docs: { - description: - "Prevent importing from reactviews directory except from within reactviews", + description: "Enforce import boundaries between extension, webview, and shared code", category: "Imports", recommended: false, }, @@ -30,7 +30,7 @@ module.exports = { const isFileInSharedInterfaces = /\/src\/sharedInterfaces\//.test(filePath); const isImportFromVscodeClient = /vscode-languageclient/.test(importSource); - // 1️⃣ importing *reactviews* from outside reactviews + // Importing *reactviews* from outside reactviews if (isImportFromReactviews && !isFileInReactviews) { context.report({ node, @@ -39,7 +39,7 @@ module.exports = { }); } - // 2️⃣ importing *vscode-languageclient* inside forbidden folders + // Importing *vscode-languageclient* inside forbidden folders if ((isFileInReactviews || isFileInSharedInterfaces) && isImportFromVscodeClient) { context.report({ node, @@ -47,6 +47,31 @@ module.exports = { "Use 'vscode-jsonrpc/browser' instead of 'vscode-languageclient' inside reactviews or sharedInterfaces", }); } + + // Importing extension code (non-sharedInterfaces src/) from webview (reactviews) + if (isFileInReactviews) { + // Check if import is a relative path starting with ../ or ../../ etc + if (importSource.startsWith("..")) { + // Resolve the import path relative to the current file + const currentDir = path.dirname(filePath); + const resolvedImport = toPosix(path.resolve(currentDir, importSource)); + + // Check if the resolved import is in src/ but not in sharedInterfaces/ + const isImportInSrc = /\/src\//.test(resolvedImport); + const isImportInSharedInterfaces = /\/src\/sharedInterfaces\//.test( + resolvedImport, + ); + const isImportInReactviews = /\/src\/reactviews\//.test(resolvedImport); + + if (isImportInSrc && !isImportInSharedInterfaces && !isImportInReactviews) { + context.report({ + node, + message: + "Webview code (reactviews) cannot import extension code. Only imports from 'sharedInterfaces' are allowed.", + }); + } + } + } }, }; }, diff --git a/eslint/custom-rules/package.json b/eslint/custom-rules/package.json index 7f565748d6..5c668feb32 100644 --- a/eslint/custom-rules/package.json +++ b/eslint/custom-rules/package.json @@ -1,5 +1,5 @@ { "name": "eslint-plugin-custom-eslint-rules", - "version": "1.0.1", + "version": "1.0.2", "main": "index.js" } diff --git a/yarn.lock b/yarn.lock index 4b3858afa8..e2c9bcc25d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4340,7 +4340,7 @@ eslint-config-prettier@^9.1.0: integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== "eslint-plugin-custom-eslint-rules@file:eslint/custom-rules": - version "1.0.1" + version "1.0.2" eslint-plugin-jsdoc@^50.2.2: version "50.2.3" From acebbfa35715805e9f926498ed370ff54d4a8a9c Mon Sep 17 00:00:00 2001 From: Aasim Khan Date: Wed, 8 Oct 2025 14:06:51 -0700 Subject: [PATCH 2/2] remove unused --- eslint/custom-rules/banned-imports.js | 1 - 1 file changed, 1 deletion(-) diff --git a/eslint/custom-rules/banned-imports.js b/eslint/custom-rules/banned-imports.js index bc7752b142..cfb27744f7 100644 --- a/eslint/custom-rules/banned-imports.js +++ b/eslint/custom-rules/banned-imports.js @@ -1,6 +1,5 @@ "use strict"; const path = require("path"); -const module = require("module"); function toPosix(p) { return p.split(path.sep).join("/"); // turns "\" → "/"