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.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 擴充套件。它們無需擴充套件作者的任何修改即可在Web 版 VS Code 中安裝和執行。具有宣告式貢獻的擴充套件示例包括主題、語法和程式碼片段。
擴充套件可以同時具有 browser
和 main
入口點,以便在瀏覽器和 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 全域性變數和庫,如
process
、os
、setImmediate
、path
、util
、url
在執行時不可用。但是,它們可以透過 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-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.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.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 擴充套件。
- 使用在桌面上執行的 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 (預設)、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 擴充套件
在將您的擴充套件釋出供所有人在 Web 版 VS Code 上使用之前,您可以驗證您的擴充套件在實際的 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://: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 上從命令列執行擴充套件測試。
該實用程式執行以下步驟
- 從本地 Web 伺服器啟動 Web 版 VS Code 編輯器。
- 開啟指定的瀏覽器。
- 執行提供的測試執行器指令碼。
您可以在持續構建中執行測試,以確保擴充套件在所有瀏覽器上都能正常工作。
測試執行器指令碼在 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.json
和tasks.json
檔案,如測試您的 Web 擴充套件部分所示。 - 在 webpack 配置檔案中,將輸入檔案設定為現有的 Node.js 主檔案,或者為 Web 擴充套件建立新的主檔案。
- 在
package.json
中,新增browser
和scripts
屬性,如Web 擴充套件解剖部分所示。 - 執行
npm run compile-web
以呼叫 webpack,並檢視需要哪些工作才能使您的擴充套件在 Web 中執行。
為了確保儘可能多的原始碼可以重用,這裡有一些技術
- 要對 Node.js 核心模組(例如
path
)進行 polyfill,請在 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
的用法。它們在虛擬工作區(非檔案系統)中無法工作,因為它們在 Web 版 VS Code 中使用。請改用帶有URI.parse
、context.extensionUri
的 URI。vscode-uri Node 模組提供了joinPath
、dirName
、baseName
、extName
、resolvePath
。 - 注意
fs
的用法。將其替換為使用 vscodeworkspace.fs
。
當您的擴充套件在 Web 中執行時,提供較少的功能是可以的。使用when 子句上下文來控制在 Web 上的虛擬工作區中執行時哪些命令、檢視和任務可用或隱藏。
- 使用
virtualWorkspace
上下文變數來判斷當前工作區是否為非檔案系統工作區。 - 使用
resourceScheme
檢查當前資源是否為file
資源。 - 如果存在平臺 shell,請使用
shellExecutionSupported
。 - 實現替代命令處理程式,顯示一個對話方塊來解釋為什麼該命令不適用。
WebWorker 可以用作 fork 程序的替代方案。我們已經更新了多個語言伺服器以作為 Web 擴充套件執行,包括內建的 JSON、CSS 和 HTML 語言伺服器。下面的語言伺服器協議部分提供了更多詳細資訊。
瀏覽器執行時環境僅支援 JavaScript 和 WebAssembly 的執行。用其他程式語言編寫的庫需要交叉編譯,例如有工具可以將 C/C++ 和 Rust 編譯為 WebAssembly。vscode-anycode 擴充套件,例如,使用 tree-sitter,它是由 C/C++ 程式碼編譯而成的 WebAssembly。
Web 擴充套件中的語言伺服器協議
vscode-languageserver-node 是 語言伺服器協議 (LSP) 的實現,它用作 JSON、CSS 和 HTML 等語言伺服器實現的基礎。
自 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
入口點,也沒有以下貢獻點: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
建立 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: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);
}
});
}