參加你附近的 ,瞭解 VS Code 中的 AI 輔助開發。

Web 擴充套件

Visual Studio Code 可以作為瀏覽器中的編輯器執行。一個例子是,在瀏覽 GitHub 倉庫或 Pull Request 時按下 .(句號鍵)即可訪問的 github.dev 使用者介面。當 VS Code 在 Web 中使用時,已安裝的擴充套件會在瀏覽器中的一個擴充套件主機中執行,該主機稱為“Web 擴充套件主機”。可以在 Web 擴充套件主機中執行的擴充套件稱為“Web 擴充套件”。

Web 擴充套件與常規擴充套件共享相同的結構,但由於執行時環境不同,它們的執行程式碼與為 Node.js 執行時編寫的擴充套件不同。Web 擴充套件仍然可以訪問完整的 VS Code API,但不能再訪問 Node.js API 和模組載入。相反,Web 擴充套件受到瀏覽器沙盒的限制,因此與常規擴充套件相比存在侷限性

Web 擴充套件執行時也支援 VS Code 桌面版。如果您決定將擴充套件建立為 Web 擴充套件,它將在Web 版 VS Code(包括 vscode.devgithub.dev)以及桌面版和 GitHub Codespaces 等服務中得到支援。

Web 擴充套件解剖

Web 擴充套件結構與常規擴充套件類似。擴充套件清單 (package.json) 定義了擴充套件原始碼的入口檔案並聲明瞭擴充套件貢獻。

對於 Web 擴充套件,主入口檔案browser 屬性定義,而不是像常規擴充套件那樣由 main 屬性定義。

contributes 屬性對於 Web 擴充套件和常規擴充套件的工作方式相同。

下面的示例顯示了一個簡單 hello world 擴充套件的 package.json,它只在 Web 擴充套件主機中執行(它只包含一個 browser 入口點)

{
  "name": "helloworld-web-sample",
  "displayName": "helloworld-web-sample",
  "description": "HelloWorld example for VS Code in the browser",
  "version": "0.0.1",
  "publisher": "vscode-samples",
  "repository": "https://github.com/microsoft/vscode-extension-samples/helloworld-web-sample",
  "engines": {
    "vscode": "^1.74.0"
  },
  "categories": ["Other"],
  "activationEvents": [],
  "browser": "./dist/web/extension.js",
  "contributes": {
    "commands": [
      {
        "command": "helloworld-web-sample.helloWorld",
        "title": "Hello World"
      }
    ]
  },
  "scripts": {
    "vscode:prepublish": "npm run package-web",
    "compile-web": "webpack",
    "watch-web": "webpack --watch",
    "package-web": "webpack --mode production --devtool hidden-source-map"
  },
  "devDependencies": {
    "@types/vscode": "^1.59.0",
    "ts-loader": "^9.2.2",
    "webpack": "^5.38.1",
    "webpack-cli": "^4.7.0",
    "@types/webpack-env": "^1.16.0",
    "process": "^0.11.10"
  }
}

注意:如果您的擴充套件目標是 1.74 之前的 VS Code 版本,您必須在 activationEvents 中明確列出 onCommand:helloworld-web-sample.helloWorld

只有 main 入口點而沒有 browser 的擴充套件不是 Web 擴充套件。Web 擴充套件主機將忽略它們,並且無法在擴充套件檢視中下載。

Extensions view

只有宣告式貢獻(只有 contributes,沒有 mainbrowser)的擴充套件可以是 Web 擴充套件。它們無需擴充套件作者的任何修改即可在Web 版 VS Code 中安裝和執行。具有宣告式貢獻的擴充套件示例包括主題、語法和程式碼片段。

擴充套件可以同時具有 browsermain 入口點,以便在瀏覽器和 Node.js 執行時中執行。將現有擴充套件更新為 Web 擴充套件部分展示瞭如何將擴充套件遷移到在這兩種執行時中工作。

Web 擴充套件啟用部分列出了用於決定是否可以在 Web 擴充套件主機中載入擴充套件的規則。

Web 擴充套件主檔案

Web 擴充套件的主檔案由 browser 屬性定義。該指令碼在 Web 擴充套件主機中的 瀏覽器 WebWorker 環境中執行。它受到瀏覽器 Worker 沙盒的限制,並且與在 Node.js 執行時中執行的常規擴充套件相比存在侷限性。

  • 不支援匯入或要求其他模組。importScripts 也不可用。因此,程式碼必須打包成單個檔案。
  • VS Code API 可以透過 require('vscode') 模式載入。這將有效,因為存在一個 require 的 shim,但此 shim 不能用於載入其他擴充套件檔案或額外的 Node 模組。它只適用於 require('vscode')
  • Node.js 全域性變數和庫,如 processossetImmediatepathutilurl 在執行時不可用。但是,它們可以透過 webpack 等工具新增。webpack 配置部分解釋瞭如何實現這一點。
  • 開啟的工作區或資料夾位於虛擬檔案系統上。訪問工作區檔案需要透過 VS Code 檔案系統 API (vscode.workspace.fs)。
  • 擴充套件上下文位置 (ExtensionContext.extensionUri) 和儲存位置 (ExtensionContext.storageUri, globalStorageUri) 也位於虛擬檔案系統上,需要透過 vscode.workspace.fs 訪問。
  • 對於訪問 Web 資源,必須使用 Fetch API。訪問的資源需要支援 跨域資源共享 (CORS)
  • 無法建立子程序或執行可執行檔案。但是,可以透過 Worker API 建立 Web Worker。這用於執行語言伺服器,如Web 擴充套件中的語言伺服器協議部分所述。
  • 與常規擴充套件一樣,擴充套件的 activate/deactivate 函式需要透過 exports.activate = ... 模式匯出。

開發 Web 擴充套件

值得慶幸的是,TypeScript 和 webpack 等工具可以隱藏許多瀏覽器執行時限制,並允許您以與常規擴充套件相同的方式編寫 Web 擴充套件。Web 擴充套件和常規擴充套件通常可以從相同的原始碼生成。

例如,由 yo code 生成器建立的 Hello Web 擴充套件只在構建指令碼上有所不同。您可以透過使用除錯: 選擇並啟動除錯命令可用的啟動配置,像傳統 Node.js 擴充套件一樣執行和除錯生成的擴充套件。

建立 Web 擴充套件

要搭建新的 Web 擴充套件,請使用 yo code 並選擇新建 Web 擴充套件。請確保安裝了最新版本的 generator-code (>= generator-code@1.6)。要更新生成器和 yo,請執行 npm i -g yo generator-code

建立的擴充套件包括擴充套件的原始碼(顯示 hello world 通知命令)、package.json 清單檔案以及 webpack 或 esbuild 配置檔案。

為了簡單起見,我們假設您使用 webpack 作為打包器。在文章的末尾,我們還將解釋選擇 esbuild 時有什麼不同。

  • src/web/extension.ts 是擴充套件的入口原始碼檔案。它與常規 hello 擴充套件相同。
  • package.json 是擴充套件清單。
    • 它使用 browser 屬性指向入口檔案。
    • 它提供了指令碼:compile-webwatch-webpackage-web 用於編譯、監視和打包。
  • webpack.config.js 是 webpack 配置檔案,它將擴充套件原始碼編譯並打包成單個檔案。
  • .vscode/launch.json 包含在帶有 Web 擴充套件主機的 VS Code 桌面版中執行 Web 擴充套件和測試的啟動配置(不再需要設定 extensions.webWorker)。
  • .vscode/task.json 包含啟動配置使用的構建任務。它使用 npm run watch-web 並依賴於 webpack 特定的 ts-webpack-watch 問題匹配器。
  • .vscode/extensions.json 包含提供問題匹配器的擴充套件。要使啟動配置正常工作,需要安裝這些擴充套件。
  • tsconfig.json 定義了與 webworker 執行時匹配的編譯選項。

helloworld-web-sample 中的原始碼與生成器建立的類似。

Webpack 配置

webpack 配置檔案由 yo code 自動生成。它將擴充套件的原始碼打包成一個 JavaScript 檔案,以便載入到 Web 擴充套件主機中。

稍後我們將解釋如何使用 esbuild 作為打包器,但現在我們從 webpack 開始。

webpack.config.js

const path = require('path');
const webpack = require('webpack');

/** @typedef {import('webpack').Configuration} WebpackConfig **/
/** @type WebpackConfig */
const webExtensionConfig = {
  mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
  target: 'webworker', // extensions run in a webworker context
  entry: {
    extension: './src/web/extension.ts', // source of the web extension main file
    'test/suite/index': './src/web/test/suite/index.ts' // source of the web extension test runner
  },
  output: {
    filename: '[name].js',
    path: path.join(__dirname, './dist/web'),
    libraryTarget: 'commonjs',
    devtoolModuleFilenameTemplate: '../../[resource-path]'
  },
  resolve: {
    mainFields: ['browser', 'module', 'main'], // look for `browser` entry point in imported node modules
    extensions: ['.ts', '.js'], // support ts-files and js-files
    alias: {
      // provides alternate implementation for node module and source files
    },
    fallback: {
      // Webpack 5 no longer polyfills Node.js core modules automatically.
      // see https://webpack.js.org/configuration/resolve/#resolvefallback
      // for the list of Node.js core module polyfills.
      assert: require.resolve('assert')
    }
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'ts-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new webpack.ProvidePlugin({
      process: 'process/browser' // provide a shim for the global `process` variable
    })
  ],
  externals: {
    vscode: 'commonjs vscode' // ignored because it doesn't exist
  },
  performance: {
    hints: false
  },
  devtool: 'nosources-source-map' // create a source map that points to the original source file
};
module.exports = [webExtensionConfig];

webpack.config.js 的一些重要欄位是

  • entry 欄位包含擴充套件和測試套件的主要入口點。
    • 您可能需要調整此路徑以正確指向擴充套件的入口點。
    • 對於現有擴充套件,您可以首先將此路徑指向您當前用於 package.jsonmain 的檔案。
    • 如果您不想打包測試,可以省略測試套件欄位。
  • output 欄位指示編譯檔案將位於何處。
    • [name] 將被 entry 中使用的鍵替換。因此,在生成的配置檔案中,它將生成 dist/web/extension.jsdist/web/test/suite/index.js
  • target 欄位指示編譯後的 JavaScript 檔案將執行的環境型別。對於 Web 擴充套件,您希望此值為 webworker
  • resolve 欄位包含為在瀏覽器中不起作用的 Node 庫新增別名和回退的功能。
    • 如果您正在使用像 path 這樣的庫,您可以指定如何在 Web 編譯上下文中解析 path。例如,您可以指向專案中定義 path 的檔案,例如 path: path.resolve(__dirname, 'src/my-path-implementation-for-web.js')。或者您可以使用名為 path-browserify 的 Browserify Node 打包版本庫,並指定 path: require.resolve('path-browserify')
    • 請參閱 webpack resolve.fallback 以獲取 Node.js 核心模組 polyfill 的列表。
  • plugins 部分使用 DefinePlugin 外掛來 polyfill 全域性變數,例如 process Node.js 全域性變數。

測試您的 Web 擴充套件

目前有三種方法可以在釋出到市場之前測試 Web 擴充套件。

  • 使用在桌面上執行的 VS Code,並使用 --extensionDevelopmentKind=web 選項在 VS Code 中執行的 Web 擴充套件主機中執行您的 Web 擴充套件。
  • 使用 @vscode/test-web Node 模組開啟一個包含 Web 版 VS Code(包括您的擴充套件)的瀏覽器,該瀏覽器由本地伺服器提供服務。
  • 側載您的擴充套件到 vscode.dev 以在實際環境中檢視您的擴充套件。

在桌面上執行的 VS Code 中測試您的 Web 擴充套件

為了利用現有的 VS Code 擴充套件開發體驗,在桌面上執行的 VS Code 支援執行 Web 擴充套件主機以及常規 Node.js 擴充套件主機。

使用新建 Web 擴充套件生成器提供的 pwa-extensionhost 啟動配置

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Run Web Extension in VS Code",
      "type": "pwa-extensionHost",
      "debugWebWorkerHost": true,
      "request": "launch",
      "args": [
        "--extensionDevelopmentPath=${workspaceFolder}",
        "--extensionDevelopmentKind=web"
      ],
      "outFiles": ["${workspaceFolder}/dist/web/**/*.js"],
      "preLaunchTask": "npm: watch-web"
    }
  ]
}

它使用任務 npm: watch-web 透過呼叫 npm run watch-web 來編譯擴充套件。該任務應位於 tasks.json

{
  "version": "2.0.0",
  "tasks": [
    {
      "type": "npm",
      "script": "watch-web",
      "group": "build",
      "isBackground": true,
      "problemMatcher": ["$ts-webpack-watch"]
    }
  ]
}

$ts-webpack-watch 是一個問題匹配器,可以解析 webpack 工具的輸出。它由 TypeScript + Webpack 問題匹配器擴充套件提供。

在啟動的擴充套件開發主機例項中,Web 擴充套件將可用並在 Web 擴充套件主機中執行。執行 Hello World 命令以啟用擴充套件。

開啟執行擴充套件檢視(命令:開發者: 顯示執行擴充套件)以檢視哪些擴充套件在 Web 擴充套件主機中執行。

使用 @vscode/test-web 在瀏覽器中測試您的 Web 擴充套件

@vscode/test-web Node 模組提供了 CLI 和 API,用於在瀏覽器中測試 Web 擴充套件。

該 Node 模組貢獻了一個 npm 二進位制檔案 vscode-test-web,可以從命令列開啟 Web 版 VS Code

  • 它將 VS Code 的 Web 部分下載到 .vscode-test-web
  • localhost:3000 上啟動本地伺服器。
  • 開啟瀏覽器(Chromium、Firefox 或 Webkit)。

您可以從命令列執行它

npx @vscode/test-web --extensionDevelopmentPath=$extensionFolderPath $testDataPath

或者更好的是,將 @vscode/test-web 新增為擴充套件的開發依賴項並在指令碼中呼叫它

  "devDependencies": {
    "@vscode/test-web": "*"
  },
  "scripts": {
    "open-in-browser": "vscode-test-web --extensionDevelopmentPath=. ."
  }

檢視 @vscode/test-web README 以獲取更多 CLI 選項

選項 引數說明
--browserType 要啟動的瀏覽器:chromium(預設)、firefoxwebkit
--extensionDevelopmentPath 指向要包含的正在開發的擴充套件的路徑。
--extensionTestsPath 要執行的測試模組的路徑。
--permission 授予開啟的瀏覽器的許可權:例如 clipboard-readclipboard-write
請參閱選項完整列表。引數可以提供多次。
--folder-uri 開啟 VS Code 的工作區 URI。如果提供了 folderPath,則忽略。
--extensionPath 指向包含要包含的其他擴充套件的資料夾的路徑。
引數可以提供多次。
folderPath 要開啟 VS Code 的本地資料夾。
資料夾內容將作為虛擬檔案系統可用並作為工作區開啟。

VS Code 的 Web 部分被下載到 .vscode-test-web 資料夾。您希望將其新增到您的 .gitignore 檔案中。

在 vscode.dev 中測試您的 Web 擴充套件

在將您的擴充套件釋出供所有人在 Web 版 VS Code 上使用之前,您可以驗證您的擴充套件在實際的 vscode.dev 環境中表現如何。

要在 vscode.dev 上檢視您的擴充套件,您首先需要從您的機器上託管它,以便 vscode.dev 下載並執行。

首先,您需要安裝 mkcert

然後,將 localhost.pemlocalhost-key.pem 檔案生成到一個您不會丟失它們的位置(例如 $HOME/certs

$ mkdir -p $HOME/certs
$ cd $HOME/certs
$ mkcert -install
$ mkcert localhost

然後,從您的擴充套件路徑中,透過執行 npx serve 啟動一個 HTTP 伺服器

$ npx serve --cors -l 5000 --ssl-cert $HOME/certs/localhost.pem --ssl-key $HOME/certs/localhost-key.pem
npx: installed 78 in 2.196s

   ┌────────────────────────────────────────────────────┐
   │                                                    │
   │   Serving!                                         │
   │                                                    │
   │   - Local:            https://:5000       │
   │   - On Your Network:  https://172.19.255.26:5000   │
   │                                                    │
   │   Copied local address to clipboard!               │
   │                                                    │
   └────────────────────────────────────────────────────┘

最後,開啟 vscode.dev,從命令面板 (⇧⌘P (Windows, Linux Ctrl+Shift+P)) 執行開發者: 從位置安裝擴充套件...,貼上上面的 URL(例如 https://:5000),然後選擇安裝

檢查日誌

您可以檢查瀏覽器開發者工具控制檯中的日誌,以檢視擴充套件中的任何錯誤、狀態和日誌。

您可能會看到來自 vscode.dev 本身的其他日誌。此外,您無法輕鬆設定斷點,也無法檢視擴充套件的原始碼。這些限制使得在 vscode.dev 中除錯並不是最愉快的體驗,因此我們建議在側載到 vscode.dev 之前使用前兩種測試選項。側載是釋出擴充套件之前的良好最終健全性檢查。

Web 擴充套件測試

支援 Web 擴充套件測試,並且可以類似於常規擴充套件測試實現。請參閱測試擴充套件文章以瞭解擴充套件測試的基本結構。

@vscode/test-web Node 模組等效於 @vscode/test-electron(以前名為 vscode-test)。它允許您在 Chromium、Firefox 和 Safari 上從命令列執行擴充套件測試。

該實用程式執行以下步驟

  1. 從本地 Web 伺服器啟動 Web 版 VS Code 編輯器。
  2. 開啟指定的瀏覽器。
  3. 執行提供的測試執行器指令碼。

您可以在持續構建中執行測試,以確保擴充套件在所有瀏覽器上都能正常工作。

測試執行器指令碼在 Web 擴充套件主機上執行,其限制與Web 擴充套件主檔案相同

  • 所有檔案都打包成一個檔案。它應該包含測試執行器(例如 Mocha)和所有測試(通常是 *.test.ts)。
  • 只支援 require('vscode')

yo code Web 擴充套件生成器建立的 webpack 配置包含一個測試部分。它期望測試執行器指令碼位於 ./src/web/test/suite/index.ts。提供的 測試執行器指令碼使用 Mocha 的 Web 版本,幷包含 webpack 特定的語法來匯入所有測試檔案。

require('mocha/mocha'); // import the mocha web build

export function run(): Promise<void> {
  return new Promise((c, e) => {
    mocha.setup({
      ui: 'tdd',
      reporter: undefined
    });

    // bundles all files in the current directory matching `*.test`
    const importAll = (r: __WebpackModuleApi.RequireContext) => r.keys().forEach(r);
    importAll(require.context('.', true, /\.test$/));

    try {
      // Run the mocha test
      mocha.run(failures => {
        if (failures > 0) {
          e(new Error(`${failures} tests failed.`));
        } else {
          c();
        }
      });
    } catch (err) {
      console.error(err);
      e(err);
    }
  });
}

要從命令列執行 Web 測試,請將以下內容新增到您的 package.json 並使用 npm test 執行它。

  "devDependencies": {
    "@vscode/test-web": "*"
  },
  "scripts": {
    "test": "vscode-test-web --extensionDevelopmentPath=. --extensionTestsPath=dist/web/test/suite/index.js"
  }

要在帶有測試資料的資料夾上開啟 VS Code,請將本地資料夾路徑 (folderPath) 作為最後一個引數傳遞。

要在 VS Code (Insiders) 桌面版中執行(和除錯)擴充套件測試,請使用 Extension Tests in VS Code 啟動配置

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Extension Tests in VS Code",
      "type": "extensionHost",
      "debugWebWorkerHost": true,
      "request": "launch",
      "args": [
        "--extensionDevelopmentPath=${workspaceFolder}",
        "--extensionDevelopmentKind=web",
        "--extensionTestsPath=${workspaceFolder}/dist/web/test/suite/index"
      ],
      "outFiles": ["${workspaceFolder}/dist/web/**/*.js"],
      "preLaunchTask": "npm: watch-web"
    }
  ]
}

釋出 Web 擴充套件

Web 擴充套件與其他擴充套件一起託管在 Marketplace 上。

請確保使用最新版本的 vsce 釋出您的擴充套件。vsce 標記所有是 Web 擴充套件的擴充套件。為此,vsce 使用Web 擴充套件啟用部分中列出的規則。

將現有擴充套件更新為 Web 擴充套件

無程式碼擴充套件

沒有程式碼,只包含貢獻點(例如,主題、程式碼片段和基本語言擴充套件)的擴充套件不需要任何修改。它們可以在 Web 擴充套件主機中執行,並可以從擴充套件檢視中安裝。

重新發布不是必需的,但當釋出新版本的擴充套件時,請確保使用最新版本的 vsce

遷移帶有程式碼的擴充套件

具有原始碼(由 main 屬性定義)的擴充套件需要提供Web 擴充套件主檔案並在 package.json 中設定 browser 屬性。

請按照以下步驟為瀏覽器環境重新編譯您的擴充套件程式碼

  • 新增一個 webpack 配置檔案,如webpack 配置部分所示。如果您已經有一個用於 Node.js 擴充套件程式碼的 webpack 檔案,您可以為 Web 新增一個新部分。檢視 vscode-css-formatter 作為示例。
  • 新增 launch.jsontasks.json 檔案,如測試您的 Web 擴充套件部分所示。
  • 在 webpack 配置檔案中,將輸入檔案設定為現有的 Node.js 主檔案,或者為 Web 擴充套件建立新的主檔案。
  • package.json 中,新增 browserscripts 屬性,如Web 擴充套件解剖部分所示。
  • 執行 npm run compile-web 以呼叫 webpack,並檢視需要哪些工作才能使您的擴充套件在 Web 中執行。

為了確保儘可能多的原始碼可以重用,這裡有一些技術

  • 要對 Node.js 核心模組(例如 path)進行 polyfill,請在 resolve.fallback 中新增一個條目。
  • 要提供 Node.js 全域性變數(例如 process),請使用 DefinePlugin 外掛
  • 使用在瀏覽器和 Node 執行時中都有效的 Node 模組。Node 模組可以透過定義 browsermain 入口點來實現這一點。Webpack 將自動使用與其目標匹配的那個。這樣做的一些 Node 模組示例是 request-light@vscode/l10n
  • 要為 Node 模組或原始檔提供備用實現,請使用 resolve.alias
  • 將您的程式碼分為瀏覽器部分、Node.js 部分和通用部分。在通用部分中,只使用在瀏覽器和 Node.js 執行時中都有效的程式碼。為在 Node.js 和瀏覽器中具有不同實現的功能建立抽象。
  • 注意 pathURI.filecontext.extensionPathrootPathuri.fsPath 的用法。它們在虛擬工作區(非檔案系統)中無法工作,因為它們在 Web 版 VS Code 中使用。請改用帶有 URI.parsecontext.extensionUri 的 URI。vscode-uri Node 模組提供了 joinPathdirNamebaseNameextNameresolvePath
  • 注意 fs 的用法。將其替換為使用 vscode workspace.fs

當您的擴充套件在 Web 中執行時,提供較少的功能是可以的。使用when 子句上下文來控制在 Web 上的虛擬工作區中執行時哪些命令、檢視和任務可用或隱藏。

  • 使用 virtualWorkspace 上下文變數來判斷當前工作區是否為非檔案系統工作區。
  • 使用 resourceScheme 檢查當前資源是否為 file 資源。
  • 如果存在平臺 shell,請使用 shellExecutionSupported
  • 實現替代命令處理程式,顯示一個對話方塊來解釋為什麼該命令不適用。

WebWorker 可以用作 fork 程序的替代方案。我們已經更新了多個語言伺服器以作為 Web 擴充套件執行,包括內建的 JSONCSSHTML 語言伺服器。下面的語言伺服器協議部分提供了更多詳細資訊。

瀏覽器執行時環境僅支援 JavaScript 和 WebAssembly 的執行。用其他程式語言編寫的庫需要交叉編譯,例如有工具可以將 C/C++Rust 編譯為 WebAssembly。vscode-anycode 擴充套件,例如,使用 tree-sitter,它是由 C/C++ 程式碼編譯而成的 WebAssembly。

Web 擴充套件中的語言伺服器協議

vscode-languageserver-node語言伺服器協議 (LSP) 的實現,它用作 JSONCSSHTML 等語言伺服器實現的基礎。

自 3.16.0 版本起,客戶端和伺服器現在也提供瀏覽器實現。伺服器可以在 Web Worker 中執行,連線基於 Web Worker 的 postMessage 協議。

瀏覽器的客戶端可以在 'vscode-languageclient/browser' 找到

import { LanguageClient } from `vscode-languageclient/browser`;

伺服器在 vscode-languageserver/browser

lsp-web-extension-sample 展示了這是如何工作的。

Web 擴充套件啟用

如果滿足以下條件,VS Code 會自動將擴充套件視為 Web 擴充套件

  • 擴充套件清單 (package.json) 具有 browser 入口點。
  • 擴充套件清單沒有 main 入口點,也沒有以下貢獻點:localizationsdebuggersterminaltypescriptServerPlugins

如果擴充套件想要提供一個在 Web 擴充套件主機中也有效的偵錯程式或終端,則需要定義一個 browser 入口點。

使用 ESBuild

如果您想使用 esbuild 而不是 webpack,請執行以下操作

新增 esbuild.js 構建指令碼

const esbuild = require('esbuild');
const glob = require('glob');
const path = require('path');
const polyfill = require('@esbuild-plugins/node-globals-polyfill');

const production = process.argv.includes('--production');
const watch = process.argv.includes('--watch');

async function main() {
  const ctx = await esbuild.context({
    entryPoints: ['src/web/extension.ts', 'src/web/test/suite/extensionTests.ts'],
    bundle: true,
    format: 'cjs',
    minify: production,
    sourcemap: !production,
    sourcesContent: false,
    platform: 'browser',
    outdir: 'dist/web',
    external: ['vscode'],
    logLevel: 'warning',
    // Node.js global to browser globalThis
    define: {
      global: 'globalThis'
    },

    plugins: [
      polyfill.NodeGlobalsPolyfillPlugin({
        process: true,
        buffer: true
      }),
      testBundlePlugin,
      esbuildProblemMatcherPlugin /* add to the end of plugins array */
    ]
  });
  if (watch) {
    await ctx.watch();
  } else {
    await ctx.rebuild();
    await ctx.dispose();
  }
}

/**
 * For web extension, all tests, including the test runner, need to be bundled into
 * a single module that has a exported `run` function .
 * This plugin bundles implements a virtual file extensionTests.ts that bundles all these together.
 * @type {import('esbuild').Plugin}
 */
const testBundlePlugin = {
  name: 'testBundlePlugin',
  setup(build) {
    build.onResolve({ filter: /[\/\\]extensionTests\.ts$/ }, args => {
      if (args.kind === 'entry-point') {
        return { path: path.resolve(args.path) };
      }
    });
    build.onLoad({ filter: /[\/\\]extensionTests\.ts$/ }, async args => {
      const testsRoot = path.join(__dirname, 'src/web/test/suite');
      const files = await glob.glob('*.test.{ts,tsx}', { cwd: testsRoot, posix: true });
      return {
        contents:
          `export { run } from './mochaTestRunner.ts';` +
          files.map(f => `import('./${f}');`).join(''),
        watchDirs: files.map(f => path.dirname(path.resolve(testsRoot, f))),
        watchFiles: files.map(f => path.resolve(testsRoot, f))
      };
    });
  }
};

/**
 * This plugin hooks into the build process to print errors in a format that the problem matcher in
 * Visual Studio Code can understand.
 * @type {import('esbuild').Plugin}
 */
const esbuildProblemMatcherPlugin = {
  name: 'esbuild-problem-matcher',

  setup(build) {
    build.onStart(() => {
      console.log('[watch] build started');
    });
    build.onEnd(result => {
      result.errors.forEach(({ text, location }) => {
        console.error(`✘ [ERROR] ${text}`);
        if (location == null) return;
        console.error(`    ${location.file}:${location.line}:${location.column}:`);
      });
      console.log('[watch] build finished');
    });
  }
};

main().catch(e => {
  console.error(e);
  process.exit(1);
});

構建指令碼執行以下操作

  • 它使用 esbuild 建立一個構建上下文。上下文配置為
    • src/web/extension.ts 中的程式碼打包成一個檔案 dist/web/extension.js
    • 將所有測試,包括測試執行器 (mocha) 打包成一個檔案 dist/web/test/suite/extensionTests.js
    • 如果傳入 --production 標誌,則壓縮程式碼。
    • 除非傳入 --production 標誌,否則生成源對映。
    • 將“vscode”模組從包中排除(因為它由 VS Code 執行時提供)。
    • processbuffer 建立 polyfills
    • 使用 esbuildProblemMatcherPlugin 外掛報告阻止打包器完成的錯誤。此外掛以一種格式發出錯誤,該格式可由 esbuild 問題匹配器檢測到,該匹配器也需要作為擴充套件安裝。
    • 使用 testBundlePlugin 實現一個測試主檔案 (extensionTests.js),該檔案引用所有測試檔案和 mocha 測試執行器 mochaTestRunner.js
  • 如果傳入 --watch 標誌,它會開始監視原始檔的更改,並在檢測到更改時重新構建包。

esbuild 可以直接處理 TypeScript 檔案。但是,esbuild 只會剝離所有型別宣告而不進行任何型別檢查。只會報告語法錯誤,並可能導致 esbuild 失敗。

因此,我們單獨執行 TypeScript 編譯器 (tsc) 來檢查型別,但不會發出任何程式碼 (標誌 --noEmit)。

package.json 中的 scripts 部分現在看起來像這樣

  "scripts": {
    "vscode:prepublish": "npm run package-web",
    "compile-web": "npm run check-types && node esbuild.js",
    "watch-web": "npm-run-all -p watch-web:*",
    "watch-web:esbuild": "node esbuild.js --watch",
    "watch-web:tsc": "tsc --noEmit --watch --project tsconfig.json",
    "package-web": "npm run check-types && node esbuild.js --production",
    "check-types": "tsc --noEmit",
    "pretest": "npm run compile-web",
    "test": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. --extensionTestsPath=dist/web/test/suite/extensionTests.js",
    "run-in-browser": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. ."
  }

npm-run-all 是一個 Node 模組,它並行執行名稱與給定字首匹配的指令碼。對於我們來說,它執行 watch-web:esbuildwatch-web:tsc 指令碼。您需要將 npm-run-all 新增到 package.json 中的 devDependencies 部分。

以下 tasks.json 檔案為每個監視任務提供了單獨的終端

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "watch-web",
      "dependsOn": ["npm: watch-web:tsc", "npm: watch-web:esbuild"],
      "presentation": {
        "reveal": "never"
      },
      "group": {
        "kind": "build",
        "isDefault": true
      },
      "runOptions": {
        "runOn": "folderOpen"
      }
    },
    {
      "type": "npm",
      "script": "watch-web:esbuild",
      "group": "build",
      "problemMatcher": "$esbuild-watch",
      "isBackground": true,
      "label": "npm: watch-web:esbuild",
      "presentation": {
        "group": "watch",
        "reveal": "never"
      }
    },
    {
      "type": "npm",
      "script": "watch-web:tsc",
      "group": "build",
      "problemMatcher": "$tsc-watch",
      "isBackground": true,
      "label": "npm: watch-web:tsc",
      "presentation": {
        "group": "watch",
        "reveal": "never"
      }
    },
    {
      "label": "compile",
      "type": "npm",
      "script": "compile-web",
      "problemMatcher": ["$tsc", "$esbuild"]
    }
  ]
}

這是 esbuild 構建指令碼中引用的 mochaTestRunner.js

// Imports mocha for the browser, defining the `mocha` global.
import 'mocha/mocha';

mocha.setup({
  ui: 'tdd',
  reporter: undefined
});

export function run(): Promise<void> {
  return new Promise((c, e) => {
    try {
      // Run the mocha test
      mocha.run(failures => {
        if (failures > 0) {
          e(new Error(`${failures} tests failed.`));
        } else {
          c();
        }
      });
    } catch (err) {
      console.error(err);
      e(err);
    }
  });
}

示例