Web 擴充功能
Visual Studio Code 可以在瀏覽器中作為編輯器執行。其中一個範例是 github.dev
使用者介面,當您在 GitHub 中瀏覽存放庫或提取要求時,按下 .
(句點鍵) 即可到達。當 VS Code 在 Web 中使用時,已安裝的擴充功能會在瀏覽器中的擴充功能主機中執行,稱為「Web 擴充功能主機」。可以在 Web 擴充功能主機中執行的擴充功能稱為「Web 擴充功能」。
Web 擴充功能與一般擴充功能具有相同的結構,但由於執行階段不同,因此不會與為 Node.js 執行階段編寫的擴充功能執行相同的程式碼。Web 擴充功能仍然可以存取完整的 VS Code API,但無法再存取 Node.js API 和模組載入。相反地,Web 擴充功能受到瀏覽器沙箱的限制,因此與一般擴充功能相比,具有限制。
VS Code 桌面版也支援 Web 擴充功能執行階段。如果您決定將擴充功能建立為 Web 擴充功能,則 VS Code for the Web (包括 vscode.dev
和 github.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 擴充功能主機忽略,並且無法在「擴充功能」檢視中下載。
僅具有宣告式貢獻 (僅 contributes
,沒有 main
或 browser
) 的擴充功能可以是 Web 擴充功能。它們可以安裝並在 VS Code for the Web 中執行,而無需擴充功能作者進行任何修改。具有宣告式貢獻的擴充功能範例包括佈景主題、文法和程式碼片段。
擴充功能可以同時具有 browser
和 main
進入點,以便在瀏覽器和 Node.js 執行階段中執行。將現有擴充功能更新為 Web 擴充功能章節說明如何移轉擴充功能以在兩個執行階段中運作。
Web 擴充功能啟用章節列出了用於決定是否可以在 Web 擴充功能主機中載入擴充功能的規則。
Web 擴充功能主要檔案
Web 擴充功能的主要檔案由 browser
屬性定義。指令碼在 瀏覽器 WebWorker 環境中的 Web 擴充功能主機中執行。它受到瀏覽器 Worker 沙箱的限制,並且與在 Node.js 執行階段中執行的正常擴充功能相比,具有限制。
- 不支援匯入或要求其他模組。
importScripts
也無法使用。因此,程式碼必須封裝到單一檔案中。 - 可以透過模式
require('vscode')
載入 VS Code API。這會有效,因為require
有一個 shim,但這個 shim 無法用於載入其他擴充功能檔案或其他 Node 模組。它僅適用於require('vscode')
。 - Node.js 全域變數和程式庫 (例如
process
、os
、setImmediate
、path
、util
、url
) 在執行階段不可用。但是,可以使用 webpack 等工具新增它們。webpack 組態章節說明了如何完成此操作。 - 已開啟的工作區或資料夾位於虛擬檔案系統上。對工作區檔案的存取需要透過 VS Code 檔案系統 API 進行,該 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 Extension
僅在建置指令碼中有所不同。您可以像使用提供的啟動組態 (可使用偵錯:選取並開始偵錯命令存取) 一樣,執行和偵錯產生的擴充功能,就像傳統的 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-web
、watch-web
和package-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 開始。
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.dev.org.tw/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.json
的main
的檔案開始。 - 如果您不想封裝測試,則可以省略測試套件欄位。
output
欄位指示編譯後的檔案將位於何處。[name]
將由entry
中使用的金鑰取代。因此,在產生的組態檔案中,它將產生dist/web/extension.js
和dist/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 擴充功能發佈到 Marketplace 之前進行測試。
- 使用在桌面上執行的 VS Code,並使用
--extensionDevelopmentKind=web
選項,以在 VS Code 中執行的 Web 擴充功能主機中執行您的 Web 擴充功能。 - 使用 @vscode/test-web Node 模組開啟包含 VS Code for the Web 的瀏覽器,包括您的擴充功能,從本機伺服器提供。
- 側載您的擴充功能到 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
,可以從命令列開啟 VS Code for the Web
- 它將 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 (預設值)、firefox 或 webkit |
--extensionDevelopmentPath | 指向要包含在內的開發中擴充功能的路徑。 |
--extensionTestsPath | 要執行的測試模組的路徑。 |
--permission | 授予已開啟瀏覽器的權限:例如 clipboard-read 、clipboard-write 。請參閱 選項完整清單。可以多次提供引數。 |
--folder-uri | 要在其上開啟 VS Code 的工作區 URI。當提供 folderPath 時會忽略 |
--extensionPath | 指向包含要包含在內的其他擴充功能的資料夾的路徑。 可以多次提供引數。 |
folderPath | 要在其上開啟 VS Code 的本機資料夾。 資料夾內容將作為虛擬檔案系統提供,並作為工作區開啟。 |
VS Code 的 Web 位元會下載到資料夾 .vscode-test-web
。您想要將其新增至您的 .gitignore
檔案。
在 vscode.dev 中測試您的 Web 擴充功能
在您發佈擴充功能以供所有人用於 VS Code for the Web 之前,您可以驗證您的擴充功能在實際的 vscode.dev 環境中的行為方式。
若要在 vscode.dev 上查看您的擴充功能,您首先需要從您的機器託管它,以便 vscode.dev 可以下載並執行。
首先,您需要安裝 mkcert
。
然後,將 localhost.pem
和 localhost-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://localhost: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://localhost:5000
,然後選取安裝。
檢查記錄
您可以檢查瀏覽器開發人員工具的控制台中的記錄,以查看來自擴充功能的任何錯誤、狀態和記錄。
您可能會看到來自 vscode.dev 本身的其他記錄。此外,您無法輕鬆設定中斷點,也無法查看擴充功能的原始碼。這些限制使得在 vscode.dev 中偵錯不是最愉快的體驗,因此我們建議在側載到 vscode.dev 之前使用前兩個選項進行測試。側載是在發佈擴充功能之前進行最終健全性檢查的好方法。
Web 擴充功能測試
Web 擴充功能測試受到支援,並且可以實作得與一般擴充功能測試類似。請參閱測試擴充功能文章以了解擴充功能測試的基本結構。
@vscode/test-web Node 模組相當於 @vscode/test-electron (先前名為 vscode-test
)。它允許您從命令列在 Chromium、Firefox 和 Safari 上執行擴充功能測試。
該公用程式執行以下步驟
- 從本機 Web 伺服器啟動 VS Code for the Web 編輯器。
- 開啟指定的瀏覽器。
- 執行提供的測試執行器指令碼。
您可以在持續建置中執行測試,以確保擴充功能在所有瀏覽器上都能運作。
測試執行器指令碼在 Web 擴充功能主機上執行,其限制與 Web 擴充功能主要檔案相同
- 所有檔案都捆綁到單一檔案中。它應包含測試執行器 (例如 Mocha) 和所有測試 (通常為
*.test.ts
)。 - 僅支援
require('vscode')
。
由 yo code
Web 擴充功能產生器建立的 webpack 組態具有測試區段。它預期測試執行器指令碼位於 ./src/web/test/suite/index.ts
。提供的 測試執行器指令碼 使用 Web 版本的 Mocha,並包含 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 作為範例。
- 新增 測試您的 Web 擴充功能章節中所示的
launch.json
和tasks.json
檔案。 - 在 webpack 組態檔案中,將輸入檔案設定為現有的 Node.js 主要檔案,或為 Web 擴充功能建立新的主要檔案。
- 在
package.json
中,新增browser
和scripts
屬性,如Web 擴充功能結構章節所示。 - 執行
npm run compile-web
以叫用 webpack 並查看需要進行哪些工作才能使您的擴充功能在 Web 中執行。
為了確保可以盡可能多地重複使用原始碼,以下是一些技巧
- 若要 Polyfill Node.js 核心模組 (例如
path
),請將項目新增至 resolve.fallback。 - 若要提供 Node.js 全域變數 (例如
process
),請使用 DefinePlugin 外掛程式。 - 使用在瀏覽器和 Node 執行階段中都能運作的 Node 模組。Node 模組可以透過定義
browser
和main
進入點來做到這一點。Webpack 將自動使用與其目標相符的進入點。執行此操作的 Node 模組範例包括 request-light 和 @vscode/l10n。 - 若要為 Node 模組或原始碼檔案提供替代實作,請使用 resolve.alias。
- 將您的程式碼分成瀏覽器部分、Node.js 部分和共用部分。在共用部分中,僅使用在瀏覽器和 Node.js 執行階段中都能運作的程式碼。為在 Node.js 和瀏覽器中具有不同實作的功能建立抽象。
- 注意
path
、URI.file
、context.extensionPath
、rootPath
、uri.fsPath
的用法。這些在虛擬工作區 (非檔案系統) 中無法運作,因為它們在 VS Code for the Web 中使用。請改為將 URI 與URI.parse
、context.extensionUri
搭配使用。vscode-uri Node 模組提供joinPath
、dirName
、baseName
、extName
、resolvePath
。 - 注意
fs
的用法。將其替換為使用 vscodeworkspace.fs
。
當您的擴充功能在 Web 中執行時,提供較少的功能是可以接受的。使用 when 子句內容 來控制哪些命令、檢視和工作在 Web 上的虛擬工作區中可用或隱藏。
- 使用
virtualWorkspace
內容變數來找出目前的工作區是否為非檔案系統工作區。 - 使用
resourceScheme
來檢查目前的資源是否為file
資源。 - 如果存在平台 Shell,則使用
shellExecutionSupported
。 - 實作替代命令處理常式,以顯示對話方塊來解釋為何命令不適用。
WebWorker 可以用作分支處理程序的替代方案。我們已更新多個語言伺服器以作為 Web 擴充功能執行,包括內建的 JSON、CSS 和 HTML 語言伺服器。語言伺服器協定章節在下面提供了更多詳細資訊。
瀏覽器執行階段環境僅支援 JavaScript 和 WebAssembly 的執行。以其他程式語言編寫的程式庫需要交叉編譯,例如,有工具可以將 C/C++ 和 Rust 編譯為 WebAssembly。vscode-anycode 擴充功能,例如,使用 tree-sitter,它是編譯為 WebAssembly 的 C/C++ 程式碼。
Web 擴充功能中的語言伺服器協定
vscode-languageserver-node 是 語言伺服器協定 (LSP) 的實作,它用作語言伺服器實作的基礎,例如 JSON、CSS 和 HTML。
自 3.16.0 起,用戶端和伺服器現在也提供瀏覽器實作。伺服器可以在 Web Worker 中執行,並且連線基於 WebWorker 的 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
進入點,並且沒有以下任何貢獻點:localizations
、debuggers
、terminal
、typescriptServerPlugins
。
如果擴充功能想要提供也適用於 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 執行階段提供)。
- 為
process
和buffer
建立 Polyfill - 使用 esbuildProblemMatcherPlugin 外掛程式來報告阻止捆綁器完成的錯誤。此外掛程式以
esbuild
問題比對器偵測到的格式發出錯誤,該比對器也需要作為擴充功能安裝。 - 使用 testBundlePlugin 來實作參考所有測試檔案和 mocha 測試執行器
mochaTestRunner.js
的測試主要檔案 (extensionTests.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:esbuild
和 watch-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);
}
});
}