套件化擴充功能
套件化 Visual Studio Code 擴充功能的第一個原因是確保它適用於在任何平台上使用 VS Code 的每個人。只有套件化的擴充功能才能在 VS Code 的 Web 環境中使用,例如 github.dev 和 vscode.dev。當 VS Code 在瀏覽器中執行時,它只能為您的擴充功能載入一個檔案,因此擴充功能程式碼需要套件化為單一的網頁友善 JavaScript 檔案。這也適用於 筆記本輸出轉譯器,其中 VS Code 也只會為您的轉譯器擴充功能載入一個檔案。
此外,擴充功能的大小和複雜性可能會快速成長。它們可能是以多個原始碼檔案撰寫,並依賴來自 npm 的模組。分解和重複使用是開發的最佳實務,但在安裝和執行擴充功能時會產生代價。載入 100 個小型檔案比載入一個大型檔案慢得多。這就是我們建議套件化的原因。套件化是將多個小型原始碼檔案組合成單一檔案的過程。
對於 JavaScript,有不同的套件化工具可用。熱門的工具包括 rollup.js、Parcel、esbuild 和 webpack。
使用 esbuild
esbuild
是一個快速的 JavaScript 套件化工具,設定簡單。若要取得 esbuild,請開啟終端機並輸入
npm i --save-dev esbuild
執行 esbuild
您可以從命令列執行 esbuild,但為了減少重複並啟用問題報告,使用建置指令碼 esbuild.js
會很有幫助
const esbuild = require('esbuild');
const production = process.argv.includes('--production');
const watch = process.argv.includes('--watch');
async function main() {
const ctx = await esbuild.context({
entryPoints: ['src/extension.ts'],
bundle: true,
format: 'cjs',
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: 'node',
outfile: 'dist/extension.js',
external: ['vscode'],
logLevel: 'warning',
plugins: [
/* add to the end of plugins array */
esbuildProblemMatcherPlugin
]
});
if (watch) {
await ctx.watch();
} else {
await ctx.rebuild();
await ctx.dispose();
}
}
/**
* @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/extension.ts
中的程式碼套件化為單一檔案dist/extension.js
。 - 如果傳遞了
--production
旗標,則縮減程式碼。 - 除非傳遞了
--production
旗標,否則產生來源地圖。 - 從套件中排除 'vscode' 模組 (因為它是由 VS Code 執行階段提供的)。
- 將
- 使用 esbuildProblemMatcherPlugin 外掛程式來報告阻止套件化工具完成的錯誤。此外掛程式會以
esbuild
問題比對器偵測到的格式發出錯誤,該問題比對器也需要作為擴充功能安裝。 - 如果傳遞了
--watch
旗標,它會開始監看原始碼檔案的變更,並在偵測到變更時重建套件。
esbuild 可以直接與 TypeScript 檔案搭配使用。但是,esbuild 只會移除所有類型宣告,而不會執行任何類型檢查。只會報告語法錯誤,並可能導致 esbuild 失敗。
因此,我們分開執行 TypeScript 編譯器 (tsc
) 來檢查類型,但不發出任何程式碼 (旗標 --noEmit
)。
package.json
中的 scripts
區段現在看起來像這樣
"scripts": {
"compile": "npm run check-types && node esbuild.js",
"check-types": "tsc --noEmit",
"watch": "npm-run-all -p watch:*",
"watch:esbuild": "node esbuild.js --watch",
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
"vscode:prepublish": "npm run package",
"package": "npm run check-types && node esbuild.js --production"
}
npm-run-all
是一個節點模組,可平行執行名稱符合給定字首的指令碼。對我們而言,它會執行 watch:esbuild
和 watch:tsc
指令碼。您需要將 npm-run-all
新增至 package.json
中的 devDependencies
區段。
compile
和 watch
指令碼用於開發,它們會產生具有來源地圖的套件檔案。package
指令碼由 vscode:prepublish
指令碼使用,後者由 VS Code 套件和發佈工具 vsce
使用,並在發佈擴充功能之前執行。將 --production
旗標傳遞至 esbuild 指令碼會使其壓縮程式碼並建立小型套件,但也會使偵錯變得困難,因此在開發期間會使用其他旗標。若要執行上述指令碼,請開啟終端機並輸入 npm run watch
,或從命令面板中選取 工作: 執行工作 (⇧⌘P (Windows、Linux Ctrl+Shift+P))。
如果您以下列方式設定 .vscode/tasks.json
,您將為每個監看工作取得個別的終端機。
{
"version": "2.0.0",
"tasks": [
{
"label": "watch",
"dependsOn": ["npm: watch:tsc", "npm: watch:esbuild"],
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "npm",
"script": "watch:esbuild",
"group": "build",
"problemMatcher": "$esbuild-watch",
"isBackground": true,
"label": "npm: watch:esbuild",
"presentation": {
"group": "watch",
"reveal": "never"
}
},
{
"type": "npm",
"script": "watch:tsc",
"group": "build",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"label": "npm: watch:tsc",
"presentation": {
"group": "watch",
"reveal": "never"
}
}
]
}
此監看工作依賴擴充功能 connor4312.esbuild-problem-matchers
進行問題比對,您需要安裝該擴充功能,工作才能在問題檢視中報告問題。需要安裝此擴充功能才能完成啟動。
為了不忘記這一點,請將 .vscode/extensions.json
檔案新增至工作區
{
"recommendations": ["connor4312.esbuild-problem-matchers"]
}
最後,您會想要更新 .vscodeignore
檔案,以便將編譯的檔案包含在發佈的擴充功能中。請查看 發佈 區段以取得更多詳細資訊。
跳至 測試 區段以繼續閱讀。
使用 webpack
Webpack 是一個開發工具,可從 npm 取得。若要取得 webpack 及其命令列介面,請開啟終端機並輸入
npm i --save-dev webpack webpack-cli
這將安裝 webpack 並更新您擴充功能的 package.json
檔案,以在 devDependencies
中包含 webpack。
Webpack 是一個 JavaScript 套件化工具,但許多 VS Code 擴充功能是以 TypeScript 撰寫,且僅編譯為 JavaScript。如果您的擴充功能使用 TypeScript,您可以使用載入器 ts-loader
,以便 webpack 可以理解 TypeScript。使用以下命令安裝 ts-loader
npm i --save-dev ts-loader
所有檔案都可在 webpack-extension 範例中取得。
設定 webpack
安裝所有工具後,現在可以設定 webpack。依照慣例,webpack.config.js
檔案包含設定,以指示 webpack 套件化您的擴充功能。以下範例設定適用於 VS Code 擴充功能,應可提供良好的起點
//@ts-check
'use strict';
const path = require('path');
const webpack = require('webpack');
/**@type {import('webpack').Configuration}*/
const config = {
target: 'webworker', // vscode extensions run in webworker context for VS Code web 📖 -> https://webpack.dev.org.tw/configuration/target/#target
entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.dev.org.tw/configuration/entry-context/
output: {
// the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.dev.org.tw/configuration/output/
path: path.resolve(__dirname, 'dist'),
filename: 'extension.js',
libraryTarget: 'commonjs2',
devtoolModuleFilenameTemplate: '../[resource-path]'
},
devtool: 'source-map',
externals: {
vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.dev.org.tw/configuration/externals/
},
resolve: {
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
mainFields: ['browser', 'module', 'main'], // look for `browser` entry point in imported node modules
extensions: ['.ts', '.js'],
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.
}
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader'
}
]
}
]
}
};
module.exports = config;
該檔案作為 webpack-extension 範例的一部分 提供。Webpack 設定檔案是正常的 JavaScript 模組,必須匯出設定物件。
在上述範例中,定義了以下內容
target
指出您的擴充功能將執行的內容。我們建議使用webworker
,讓您的擴充功能可在 VS Code 網頁版和 VS Code 桌面版中運作。- webpack 應使用的進入點。這與
package.json
中的main
屬性類似,不同之處在於您為 webpack 提供「來源」進入點 (通常是src/extension.ts
),而不是「輸出」進入點。webpack 套件化工具理解 TypeScript,因此單獨的 TypeScript 編譯步驟是多餘的。 output
設定會告知 webpack 將產生的套件檔案放在哪裡。依照慣例,那是dist
資料夾。在此範例中,webpack 將產生dist/extension.js
檔案。resolve
和module/rules
設定用於支援 TypeScript 和 JavaScript 輸入檔案。externals
設定用於宣告排除項目,例如不應包含在套件中的檔案和模組。vscode
模組不應套件化,因為它在磁碟上不存在,而是在需要時由 VS Code 即時建立。根據擴充功能使用的節點模組,可能需要更多排除項目。
最後,您會想要更新 .vscodeignore
檔案,以便將編譯的檔案包含在發佈的擴充功能中。請查看 發佈 區段以取得更多詳細資訊。
執行 webpack
建立 webpack.config.js
檔案後,即可叫用 webpack。您可以從命令列執行 webpack,但為了減少重複,使用 npm 指令碼會很有幫助。
將這些項目合併到 package.json
中的 scripts
區段
"scripts": {
"compile": "webpack --mode development",
"watch": "webpack --mode development --watch",
"vscode:prepublish": "npm run package",
"package": "webpack --mode production --devtool hidden-source-map",
},
compile
和 watch
指令碼用於開發,它們會產生套件檔案。vscode:prepublish
由 VS Code 套件和發佈工具 vsce
使用,並在發佈擴充功能之前執行。差異在於 mode,它控制最佳化的程度。使用 production
會產生最小的套件,但也需要較長的時間,因此會使用 development
。若要執行上述指令碼,請開啟終端機並輸入 npm run compile
,或從命令面板中選取 工作: 執行工作 (⇧⌘P (Windows、Linux Ctrl+Shift+P))。
執行擴充功能
在您可以執行擴充功能之前,package.json
中的 main
屬性必須指向套件,以上述設定而言,它是 "./dist/extension"
。進行該變更後,現在可以執行和測試擴充功能。
測試
擴充功能作者通常會為其擴充功能原始碼撰寫單元測試。在正確的架構分層下,擴充功能原始碼不依賴測試,webpack 和 esbuild 產生的套件不應包含任何測試程式碼。若要執行單元測試,只需要簡單的編譯即可。
將這些項目合併到 package.json
中的 scripts
區段
"scripts": {
"compile-tests": "tsc -p . --outDir out",
"pretest": "npm run compile-tests",
"test": "vscode-test"
}
compile-tests
指令碼使用 TypeScript 編譯器將擴充功能編譯到 out
資料夾中。有了可用的中繼 JavaScript,以下用於 launch.json
的程式碼片段就足以執行測試。
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test"
],
"outFiles": ["${workspaceFolder}/out/test/**/*.js"],
"preLaunchTask": "npm: compile-tests"
}
此用於執行測試的設定與未套件化的擴充功能相同。沒有理由套件化單元測試,因為它們不是擴充功能發佈部分的一部分。
發佈
在發佈之前,您應該更新 .vscodeignore
檔案。現在套件化到 dist/extension.js
檔案中的所有內容都可以排除,通常是 out
資料夾 (如果您尚未刪除) 以及最重要的 node_modules
資料夾。
典型的 .vscodeignore
檔案看起來像這樣
.vscode
node_modules
out/
src/
tsconfig.json
webpack.config.js
esbuild.js
移轉現有的擴充功能
移轉現有的擴充功能以使用 esbuild 或 webpack 很簡單,並且與上述入門指南類似。透過此 提取要求,VS Code 的參考檢視採用 webpack 的真實範例。
您可以在那裡看到
- 新增
esbuild
resp.webpack
、webpack-cli
和ts-loader
作為devDependencies
。 - 更新 npm 指令碼以使用如上所示的套件化工具
- 更新工作設定
tasks.json
檔案。 - 新增和調整
esbuild.js
或webpack.config.js
建置檔案。 - 更新
.vscodeignore
以排除node_modules
和中繼輸出檔案。 - 享受安裝和載入速度更快的擴充功能!
疑難排解
縮減
在 production
模式中套件化也會執行程式碼縮減。縮減會藉由移除空白字元和註解,以及將變數和函式名稱變更為醜陋但簡短的名稱來壓縮原始碼。使用 Function.prototype.name
的原始碼運作方式不同,因此您可能必須停用縮減。
webpack 重要相依性
執行 webpack 時,您可能會遇到類似 Critical dependencies: the request of a dependency is an expression 的警告。必須認真對待此類警告,而且您的套件可能無法運作。此訊息表示 webpack 無法靜態判斷如何套件化某些相依性。這通常是由動態 require
陳述式所造成,例如 require(someDynamicVariable)
。
若要解決此警告,您應該執行以下操作
- 嘗試使相依性靜態化,以便可以套件化。
- 透過
externals
設定排除該相依性。另請確定這些 JavaScript 檔案未從封裝的擴充功能中排除,方法是在.vscodeignore
中使用否定 glob 模式,例如!node_modules/mySpecialModule
。