Erweiterungen bündeln
Der erste Grund, Ihre Visual Studio Code-Erweiterung zu bündeln, ist die Gewährleistung ihrer Funktionalität für alle Benutzer von VS Code auf jeder Plattform. Nur gebündelte Erweiterungen können in VS Code für Web-Umgebungen wie github.dev und vscode.dev verwendet werden. Wenn VS Code im Browser ausgeführt wird, kann es nur eine Datei für Ihre Erweiterung laden, daher muss der Erweiterungscode zu einer einzigen, webfreundlichen JavaScript-Datei gebündelt werden. Dies gilt auch für Notebook-Ausgaberenderer, bei denen VS Code ebenfalls nur eine Datei für Ihre Renderer-Erweiterung lädt.
Darüber hinaus können Erweiterungen schnell an Größe und Komplexität gewinnen. Sie können in mehreren Quelldateien verfasst und von Modulen aus npm abhängen. Dekomposition und Wiederverwendung sind bewährte Entwicklungspraktiken, haben aber bei der Installation und Ausführung von Erweiterungen ihren Preis. Das Laden von 100 kleinen Dateien ist wesentlich langsamer als das Laden einer großen Datei. Deshalb empfehlen wir das Bündeln. Bündeln ist der Prozess, bei dem mehrere kleine Quelldateien zu einer einzigen Datei zusammengefasst werden.
Für JavaScript stehen verschiedene Bundler zur Verfügung. Beliebte sind rollup.js, Parcel, esbuild und webpack.
Verwendung von esbuild
esbuild ist ein schneller JavaScript-Bundler, der einfach zu konfigurieren ist. Um esbuild zu erhalten, öffnen Sie das Terminal und geben Sie ein
npm i --save-dev esbuild
esbuild ausführen
Sie können esbuild von der Befehlszeile ausführen, aber um Wiederholungen zu reduzieren und die Fehlerberichterstattung zu ermöglichen, ist die Verwendung eines Build-Skripts, esbuild.js, hilfreich.
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);
});
Das Build-Skript führt Folgendes aus
- Es erstellt einen Build-Kontext mit esbuild. Der Kontext wird konfiguriert, um
- Den Code in
src/extension.tsin eine einzige Dateidist/extension.jszu bündeln. - Den Code zu minimieren, wenn das Flag
--productionübergeben wurde. - Quellkarten zu generieren, es sei denn, das Flag
--productionwurde übergeben. - Das Modul 'vscode' vom Bundle auszuschließen (da es von der VS Code-Laufzeitumgebung bereitgestellt wird).
- Den Code in
- Das esbuildProblemMatcherPlugin zu verwenden, um Fehler zu melden, die den Bundler daran gehindert haben, abzuschließen. Dieses Plugin gibt die Fehler in einem Format aus, das vom
esbuild-Problem-Matcher erkannt wird, der ebenfalls als Erweiterung installiert werden muss. - Wenn das Flag
--watchübergeben wurde, beginnt es mit der Überwachung der Quelldateien auf Änderungen und baut das Bundle neu auf, wenn eine Änderung erkannt wird.
esbuild kann direkt mit TypeScript-Dateien arbeiten. esbuild entfernt jedoch einfach alle Typdeklarationen, ohne Typüberprüfungen durchzuführen. Nur Syntaxfehler werden gemeldet und können dazu führen, dass esbuild fehlschlägt.
Aus diesem Grund führen wir separat den TypeScript-Compiler (tsc) aus, um die Typen zu überprüfen, aber ohne Code auszugeben (Flag --noEmit).
Der Abschnitt scripts in package.json sieht nun so aus
"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 ist ein Node-Modul, das Skripte parallel ausführt, deren Namen mit einem bestimmten Präfix übereinstimmen. Für uns führt es die Skripte watch:esbuild und watch:tsc aus. Sie müssen npm-run-all zu den devDependencies in package.json hinzufügen.
Die Skripte compile und watch sind für die Entwicklung gedacht und erstellen die Bundle-Datei mit Quellkarten. Das Skript package wird vom Skript vscode:prepublish verwendet, das von vsce, dem Pack- und Veröffentlichungstool von VS Code, verwendet wird und vor der Veröffentlichung einer Erweiterung ausgeführt wird. Das Übergeben des Flags --production an das esbuild-Skript führt dazu, dass der Code komprimiert und ein kleines Bundle erstellt wird, aber das Debugging erschwert, daher werden während der Entwicklung andere Flags verwendet. Um die obigen Skripte auszuführen, öffnen Sie ein Terminal und geben Sie npm run watch ein oder wählen Sie Tasks: Run Task aus der Befehlspalette (⇧⌘P (Windows, Linux Ctrl+Shift+P)).
Wenn Sie .vscode/tasks.json wie folgt konfigurieren, erhalten Sie für jede Watch-Aufgabe ein separates Terminal.
{
"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"
}
}
]
}
Diese Watch-Aufgabe hängt von der Erweiterung connor4312.esbuild-problem-matchers für die Problembehandlung ab, die Sie installieren müssen, damit die Aufgabe Probleme in der Problemansicht meldet. Diese Erweiterung muss installiert sein, damit der Start abgeschlossen werden kann.
Um dies nicht zu vergessen, fügen Sie eine Datei .vscode/extensions.json zum Arbeitsbereich hinzu
{
"recommendations": ["connor4312.esbuild-problem-matchers"]
}
Schließlich möchten Sie Ihre Datei .vscodeignore aktualisieren, damit kompilierte Dateien in die veröffentlichte Erweiterung aufgenommen werden. Weitere Details finden Sie im Abschnitt Veröffentlichung.
Springen Sie zum Abschnitt Tests, um weiterzulesen.
Verwendung von webpack
Webpack ist ein Entwicklungswerkzeug, das von npm verfügbar ist. Um webpack und seine Befehlszeilenschnittstelle zu erhalten, öffnen Sie das Terminal und geben Sie ein
npm i --save-dev webpack webpack-cli
Dadurch werden webpack installiert und Ihre Erweiterungsdatei package.json aktualisiert, um webpack in die devDependencies aufzunehmen.
Webpack ist ein JavaScript-Bundler, aber viele VS Code-Erweiterungen sind in TypeScript geschrieben und werden nur zu JavaScript kompiliert. Wenn Ihre Erweiterung TypeScript verwendet, können Sie den Loader ts-loader verwenden, damit webpack TypeScript versteht. Verwenden Sie Folgendes, um ts-loader zu installieren
npm i --save-dev ts-loader
Alle Dateien sind im Beispiel webpack-extension verfügbar.
Webpack konfigurieren
Nach der Installation aller Tools kann webpack nun konfiguriert werden. Konventionell enthält eine Datei webpack.config.js die Konfiguration, um webpack anzuweisen, Ihre Erweiterung zu bündeln. Die folgende Beispielkonfiguration ist für VS Code-Erweiterungen gedacht und sollte einen guten Ausgangspunkt bieten.
//@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.js.org/configuration/target/#target
entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
output: {
// the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/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.js.org/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.js.org/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;
Die Datei ist als Teil des Beispiels webpack-extension verfügbar. Webpack-Konfigurationsdateien sind normale JavaScript-Module, die ein Konfigurationsobjekt exportieren müssen.
Im obigen Beispiel werden folgende Punkte definiert
- Das
targetgibt den Kontext an, in dem Ihre Erweiterung ausgeführt wird. Wir empfehlen die Verwendung vonwebworker, damit Ihre Erweiterung sowohl in VS Code für Web als auch in VS Code Desktop-Versionen funktioniert. - Der Einstiegspunkt, den webpack verwenden soll. Dies ist ähnlich wie die Eigenschaft
maininpackage.json, außer dass Sie webpack einen "Source"-Einstiegspunkt bereitstellen, normalerweisesrc/extension.ts, und keinen "Output"-Einstiegspunkt. Der webpack-Bundler versteht TypeScript, sodass ein separater TypeScript-Kompilierungsschritt überflüssig ist. - Die
output-Konfiguration teilt webpack mit, wo die generierte Bundle-Datei platziert werden soll. Konventionell ist dies der Ordnerdist. In diesem Beispiel erstellt webpack eine Dateidist/extension.js. - Die Konfigurationen
resolveundmodule/rulesdienen zur Unterstützung von TypeScript- und JavaScript-Eingabedateien. - Die Konfiguration
externalswird verwendet, um Ausschlüsse zu deklarieren, z. B. Dateien und Module, die nicht in das Bundle aufgenommen werden sollen. Das Modulvscodesollte nicht gebündelt werden, da es nicht auf der Festplatte vorhanden ist, sondern von VS Code bei Bedarf im laufenden Betrieb erstellt wird. Abhängig von den von einer Erweiterung verwendeten Node-Modulen können weitere Ausschlüsse erforderlich sein.
Schließlich möchten Sie Ihre Datei .vscodeignore aktualisieren, damit kompilierte Dateien in die veröffentlichte Erweiterung aufgenommen werden. Weitere Details finden Sie im Abschnitt Veröffentlichung.
Webpack ausführen
Mit der erstellten Datei webpack.config.js kann webpack aufgerufen werden. Sie können webpack von der Befehlszeile aus ausführen, aber um Wiederholungen zu reduzieren, ist die Verwendung von npm-Skripten hilfreich.
Fügen Sie diese Einträge zum Abschnitt scripts in package.json zusammen
"scripts": {
"compile": "webpack --mode development",
"watch": "webpack --mode development --watch",
"vscode:prepublish": "npm run package",
"package": "webpack --mode production --devtool hidden-source-map",
},
Die Skripte compile und watch sind für die Entwicklung und erstellen die Bundle-Datei. vscode:prepublish wird von vsce, dem Pack- und Veröffentlichungstool von VS Code, verwendet und vor der Veröffentlichung einer Erweiterung ausgeführt. Der Unterschied liegt im Modus, der den Optimierungsgrad steuert. Die Verwendung von production liefert das kleinste Bundle, dauert aber auch länger, daher wird stattdessen development verwendet. Um die obigen Skripte auszuführen, öffnen Sie ein Terminal und geben Sie npm run compile ein oder wählen Sie Tasks: Run Task aus der Befehlspalette (⇧⌘P (Windows, Linux Ctrl+Shift+P)).
Erweiterung ausführen
Bevor Sie die Erweiterung ausführen können, muss die Eigenschaft main in package.json auf das Bundle verweisen, das für die obige Konfiguration "./dist/extension" ist. Mit dieser Änderung kann die Erweiterung nun ausgeführt und getestet werden.
Tests
Erweiterungsautoren schreiben oft Unit-Tests für ihren Erweiterungsquellcode. Mit der richtigen architektonischen Schichtung, bei der der Erweiterungsquellcode nicht von Tests abhängt, sollte das von webpack und esbuild erstellte Bundle keinen Testcode enthalten. Zum Ausführen von Unit-Tests ist nur eine einfache Kompilierung erforderlich.
Fügen Sie diese Einträge zum Abschnitt scripts in package.json zusammen
"scripts": {
"compile-tests": "tsc -p . --outDir out",
"pretest": "npm run compile-tests",
"test": "vscode-test"
}
Das Skript compile-tests verwendet den TypeScript-Compiler, um die Erweiterung in den Ordner out zu kompilieren. Mit diesem zwischenzeitlich verfügbaren JavaScript ist der folgende Ausschnitt für launch.json ausreichend, um Tests auszuführen.
{
"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"
}
Diese Konfiguration zum Ausführen von Tests ist für nicht gebündelte Erweiterungen gleich. Es gibt keinen Grund, Unit-Tests zu bündeln, da sie nicht Teil des veröffentlichten Teils einer Erweiterung sind.
Veröffentlichung
Vor der Veröffentlichung sollten Sie die Datei .vscodeignore aktualisieren. Alles, was jetzt in die Datei dist/extension.js gebündelt ist, kann ausgeschlossen werden, normalerweise der Ordner out (falls Sie ihn noch nicht gelöscht haben) und am wichtigsten der Ordner node_modules.
Eine typische Datei .vscodeignore sieht so aus
.vscode
node_modules
out/
src/
tsconfig.json
webpack.config.js
esbuild.js
Vorhandene Erweiterung migrieren
Die Migration einer vorhandenen Erweiterung zur Verwendung von esbuild oder webpack ist einfach und ähnelt dem obigen Leitfaden für den Einstieg. Ein realweltliches Beispiel, das webpack übernommen hat, ist die Referenzansicht von VS Code durch diesen Pull Request.
Dort sehen Sie
esbuildbzw.webpack,webpack-cliundts-loaderalsdevDependencieshinzufügen.- npm-Skripte aktualisieren, um die Bundler wie oben gezeigt zu verwenden.
- Die Task-Konfiguration
tasks.jsonaktualisieren. - Die Build-Datei
esbuild.jsoderwebpack.config.jshinzufügen und optimieren. .vscodeignoreaktualisieren, umnode_modulesund Zwischenausgabedateien auszuschließen.- Genießen Sie eine Erweiterung, die viel schneller installiert und geladen wird!
Fehlerbehebung
Minimierung
Das Bündeln im production-Modus führt auch eine Code-Minimierung durch. Minifizierung komprimiert Quellcode, indem Leerzeichen und Kommentare entfernt und Variablennamen und Funktionsnamen in etwas Hässliches, aber Kurzes umgewandelt werden. Quellcode, der Function.prototype.name verwendet, funktioniert anders und daher müssen Sie möglicherweise die Minifizierung deaktivieren.
Webpack kritische Abhängigkeiten
Wenn Sie webpack ausführen, stoßen Sie möglicherweise auf eine Warnung wie Kritische Abhängigkeiten: Die Anforderung einer Abhängigkeit ist ein Ausdruck. Solche Warnungen müssen ernst genommen werden und Ihr Bundle wird wahrscheinlich nicht funktionieren. Die Meldung bedeutet, dass webpack statisch nicht ermitteln kann, wie eine bestimmte Abhängigkeit gebündelt werden soll. Dies wird normalerweise durch eine dynamische require-Anweisung verursacht, z. B. require(someDynamicVariable).
Um die Warnung zu beheben, sollten Sie entweder
- Versuchen, die Abhängigkeit statisch zu machen, damit sie gebündelt werden kann.
- Diese Abhängigkeit über die
externals-Konfiguration ausschließen. Stellen Sie außerdem sicher, dass diese JavaScript-Dateien nicht aus der gepackten Erweiterung ausgeschlossen werden, indem Sie ein negiertes Glob-Muster in.vscodeignoreverwenden, z. B.!node_modules/mySpecialModule.
Nächste Schritte
- Extension Marketplace - Erfahren Sie mehr über den öffentlichen Extension Marketplace von VS Code.
- Erweiterungen testen - Fügen Sie Ihrem Erweiterungsprojekt Tests hinzu, um eine hohe Qualität zu gewährleisten.
- Continuous Integration - Erfahren Sie, wie Sie CI-Builds für Erweiterungen auf Azure Pipelines ausführen.