diff --git a/eslint/custom-rules/banned-imports.js b/eslint/custom-rules/banned-imports.js index 452c559ac5..cfb27744f7 100644 --- a/eslint/custom-rules/banned-imports.js +++ b/eslint/custom-rules/banned-imports.js @@ -9,8 +9,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 +29,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 +38,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 +46,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"