ist jetzt verfügbar! Lesen Sie über die neuen Funktionen und Fehlerbehebungen vom November.

Web-Erweiterungen

Visual Studio Code kann als Editor im Browser ausgeführt werden. Ein Beispiel ist die github.dev-Benutzeroberfläche, die durch Drücken von . (der Punkt-Taste) beim Durchsuchen eines Repositorys oder Pull Requests in GitHub erreicht wird. Wenn VS Code im Web verwendet wird, laufen installierte Erweiterungen in einem Erweiterungs-Host im Browser, dem sogenannten "Web Extension Host". Eine Erweiterung, die in einem Web Extension Host ausgeführt werden kann, wird als "Web-Erweiterung" bezeichnet.

Web-Erweiterungen haben die gleiche Struktur wie normale Erweiterungen, laufen aber aufgrund der unterschiedlichen Laufzeit nicht mit dem gleichen Code wie Erweiterungen, die für eine Node.js-Laufzeit geschrieben wurden. Web-Erweiterungen haben weiterhin Zugriff auf die vollständige VS Code API, aber nicht mehr auf die Node.js-APIs und das Modul-Laden. Stattdessen sind Web-Erweiterungen durch die Browser-Sandbox eingeschränkt und haben daher im Vergleich zu normalen Erweiterungen Einschränkungen.

Die Web-Erweiterungs-Laufzeit wird auch auf dem VS Code-Desktop unterstützt. Wenn Sie sich entscheiden, Ihre Erweiterung als Web-Erweiterung zu erstellen, wird sie auf VS Code für das Web (einschließlich vscode.dev und github.dev) sowie auf dem Desktop und in Diensten wie GitHub Codespaces unterstützt.

Anatomie einer Web-Erweiterung

Eine Web-Erweiterung ist wie eine normale Erweiterung strukturiert. Das Erweiterungsmanifest (package.json) definiert die Einstiegsdatei für den Quellcode der Erweiterung und deklariert die Beiträge der Erweiterung.

Bei Web-Erweiterungen wird die Haupt-Einstiegsdatei durch die browser-Eigenschaft definiert und nicht durch die main-Eigenschaft wie bei normalen Erweiterungen.

Die contributes-Eigenschaft funktioniert sowohl für Web- als auch für normale Erweiterungen gleich.

Das folgende Beispiel zeigt die package.json für eine einfache Hello-World-Erweiterung, die nur im Web Extension Host läuft (sie hat nur einen browser-Einstiegspunkt)

{
  "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"
  }
}

Hinweis: Wenn Ihre Erweiterung auf eine VS Code-Version vor 1.74 abzielt, müssen Sie onCommand:helloworld-web-sample.helloWorld explizit in activationEvents auflisten.

Erweiterungen, die nur einen main-Einstiegspunkt, aber keinen browser haben, sind keine Web-Erweiterungen. Sie werden vom Web Extension Host ignoriert und sind nicht zum Download in der Erweiterungsansicht verfügbar.

Extensions view

Erweiterungen mit nur deklarativen Beiträgen (nur contributes, kein main oder browser) können Web-Erweiterungen sein. Sie können in VS Code für das Web ohne Modifikationen durch den Erweiterungsautor installiert und ausgeführt werden. Beispiele für Erweiterungen mit deklarativen Beiträgen sind Themes, Grammatiken und Snippets.

Erweiterungen können sowohl browser- als auch main-Einstiegspunkte haben, um in Browser- und Node.js-Laufzeiten ausgeführt zu werden. Der Abschnitt Bestehende Erweiterungen auf Web-Erweiterungen aktualisieren zeigt, wie eine Erweiterung für beide Laufzeiten migriert wird.

Der Abschnitt Aktivierung von Web-Erweiterungen listet die Regeln auf, nach denen entschieden wird, ob eine Erweiterung in einem Web Extension Host geladen werden kann.

Hauptdatei einer Web-Erweiterung

Die Hauptdatei der Web-Erweiterung wird durch die browser-Eigenschaft definiert. Das Skript läuft im Web Extension Host in einer Browser WebWorker-Umgebung. Es ist durch die Browser-Worker-Sandbox eingeschränkt und hat Einschränkungen im Vergleich zu normalen Erweiterungen, die in einer Node.js-Laufzeit laufen.

  • Das Importieren oder Anfordern anderer Module wird nicht unterstützt. importScripts ist ebenfalls nicht verfügbar. Folglich muss der Code zu einer einzigen Datei verpackt werden.
  • Die VS Code API kann über das Muster require('vscode') geladen werden. Dies funktioniert, da es eine Shim für require gibt. Diese Shim kann jedoch nicht verwendet werden, um zusätzliche Erweiterungsdateien oder zusätzliche Node-Module zu laden. Sie funktioniert nur mit require('vscode').
  • Node.js-Globale und Bibliotheken wie process, os, setImmediate, path, util, url sind zur Laufzeit nicht verfügbar. Sie können jedoch mit Tools wie webpack hinzugefügt werden. Der Abschnitt webpack-Konfiguration erklärt, wie dies geschieht.
  • Der geöffnete Workspace oder Ordner befindet sich auf einem virtuellen Dateisystem. Der Zugriff auf Workspace-Dateien muss über die VS Code Dateisystem-API erfolgen, die unter vscode.workspace.fs zugänglich ist.
  • Erweiterungskontext-Speicherorte (ExtensionContext.extensionUri) und Speicherorte (ExtensionContext.storageUri, globalStorageUri) befinden sich ebenfalls auf einem virtuellen Dateisystem und müssen über vscode.workspace.fs aufgerufen werden.
  • Für den Zugriff auf Web-Ressourcen muss die Fetch-API verwendet werden. Zugegriffene Ressourcen müssen Cross-Origin Resource Sharing (CORS) unterstützen.
  • Das Erstellen von Kindprozessen oder das Ausführen von ausführbaren Dateien ist nicht möglich. Web-Worker können jedoch über die Worker-API erstellt werden. Dies wird für die Ausführung von Sprachservern verwendet, wie im Abschnitt Language Server Protocol in Web-Erweiterungen beschrieben.
  • Wie bei normalen Erweiterungen müssen die activate/deactivate-Funktionen der Erweiterung über das Muster exports.activate = ... exportiert werden.

Entwickeln einer Web-Erweiterung

Glücklicherweise können Tools wie TypeScript und webpack viele der Einschränkungen der Browser-Laufzeit verbergen und es Ihnen ermöglichen, Web-Erweiterungen genauso zu schreiben wie normale Erweiterungen. Sowohl eine Web-Erweiterung als auch eine normale Erweiterung können oft aus demselben Quellcode generiert werden.

Zum Beispiel unterscheidet sich die von yo code Generator erstellte Hello Web Extension nur in den Build-Skripten. Sie können die generierte Erweiterung genau wie herkömmliche Node.js-Erweiterungen ausführen und debuggen, indem Sie die bereitgestellten Startkonfigurationen verwenden, die über den Befehl Debuggen: Starten und Auswählen des Debuggings zugänglich sind.

Erstellen einer Web-Erweiterung

Um eine neue Web-Erweiterung zu erstellen, verwenden Sie yo code und wählen Sie New Web Extension. Stellen Sie sicher, dass Sie die neueste Version von generator-code (>= generator-code@1.6) installiert haben. Um den Generator und yo zu aktualisieren, führen Sie npm i -g yo generator-code aus.

Die erstellte Erweiterung besteht aus dem Quellcode der Erweiterung (ein Befehl, der eine Hello-World-Benachrichtigung anzeigt), der package.json-Manifestdatei und einer webpack- oder esbuild-Konfigurationsdatei.

Um die Dinge einfach zu halten, gehen wir davon aus, dass Sie webpack als Bundler verwenden. Am Ende des Artikels erklären wir auch, was sich bei der Wahl von esbuild unterscheidet.

  • src/web/extension.ts ist die Einstiegsdatei für den Quellcode der Erweiterung. Sie ist identisch mit der normalen Hello-Erweiterung.
  • package.json ist das Erweiterungsmanifest.
    • Es verweist mit der browser-Eigenschaft auf die Einstiegsdatei.
    • Es stellt Skripte bereit: compile-web, watch-web und package-web zum Kompilieren, Beobachten und Verpacken.
  • webpack.config.js ist die webpack-Konfigurationsdatei, die die Erweiterungsquellen kompiliert und zu einer einzigen Datei bündelt.
  • .vscode/launch.json enthält die Startkonfigurationen, die die Web-Erweiterung und die Tests im VS Code-Desktop mit einem Web Extension Host ausführen (die Einstellung extensions.webWorker ist nicht mehr erforderlich).
  • .vscode/task.json enthält die von der Startkonfiguration verwendete Build-Aufgabe. Sie verwendet npm run watch-web und hängt vom webpack-spezifischen ts-webpack-watch-Problem-Matcher ab.
  • .vscode/extensions.json enthält die Erweiterungen, die die Problem-Matcher bereitstellen. Diese Erweiterungen müssen installiert sein, damit die Startkonfigurationen funktionieren.
  • tsconfig.json definiert die Compile-Optionen, die mit der webworker-Laufzeit übereinstimmen.

Der Quellcode im helloworld-web-sample ähnelt dem, was vom Generator erstellt wird.

Webpack-Konfiguration

Die webpack-Konfigurationsdatei wird automatisch von yo code generiert. Sie bündelt den Quellcode Ihrer Erweiterung zu einer einzigen JavaScript-Datei, die im Web Extension Host geladen wird.

Später erklären wir, wie esbuild als Bundler verwendet wird, aber vorerst beginnen wir mit 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];

Einige wichtige Felder von webpack.config.js sind

  • Das Feld entry enthält den Haupteinstiegspunkt Ihrer Erweiterung und der Test-Suite.
    • Sie müssen diesen Pfad möglicherweise anpassen, um auf den Einstiegspunkt Ihrer Erweiterung zu verweisen.
    • Für eine bestehende Erweiterung können Sie damit beginnen, diesen Pfad auf die Datei zu setzen, die Sie derzeit für main Ihrer package.json verwenden.
    • Wenn Sie Ihre Tests nicht verpacken möchten, können Sie das Feld für die Test-Suite weglassen.
  • Das Feld output gibt an, wo sich die kompilierte Datei befinden wird.
    • [name] wird durch den in entry verwendeten Schlüssel ersetzt. In der generierten Konfigurationsdatei werden also dist/web/extension.js und dist/web/test/suite/index.js erstellt.
  • Das Feld target gibt an, welche Art von Umgebung die kompilierte JavaScript-Datei ausführen wird. Für Web-Erweiterungen möchten Sie dies webworker haben.
  • Das Feld resolve enthält die Möglichkeit, Aliase und Fallbacks für Node-Bibliotheken hinzuzufügen, die im Browser nicht funktionieren.
    • Wenn Sie eine Bibliothek wie path verwenden, können Sie angeben, wie path in einem Web-kompilierten Kontext aufgelöst wird. Sie können zum Beispiel auf eine Datei im Projekt verweisen, die path mit path: path.resolve(__dirname, 'src/my-path-implementation-for-web.js') definiert. Oder Sie können die Browserify-Node-Version der Bibliothek namens path-browserify verwenden und path: require.resolve('path-browserify') angeben.
    • Siehe webpack resolve.fallback für die Liste der Node.js Core-Modul-Polyfills.
  • Der Abschnitt plugins verwendet das DefinePlugin-Plugin, um Globale wie das Node.js-Global process zu polyfillen.

Testen Sie Ihre Web-Erweiterung

Derzeit gibt es drei Möglichkeiten, eine Web-Erweiterung zu testen, bevor sie im Marketplace veröffentlicht wird.

  • Verwenden Sie VS Code, das auf dem Desktop mit der Option --extensionDevelopmentKind=web ausgeführt wird, um Ihre Web-Erweiterung in einem Web Extension Host auszuführen, der in VS Code läuft.
  • Verwenden Sie das @vscode/test-web Node-Modul, um einen Browser zu öffnen, der VS Code für das Web mit Ihrer Erweiterung enthält und von einem lokalen Server bereitgestellt wird.
  • Sideloaden Sie Ihre Erweiterung in vscode.dev, um Ihre Erweiterung in der tatsächlichen Umgebung zu sehen.

Testen Sie Ihre Web-Erweiterung in VS Code, das auf dem Desktop läuft

Um die bestehende VS Code-Erweiterungsentwicklungsumgebung nutzen zu können, unterstützt VS Code auf dem Desktop die Ausführung eines Web Extension Hosts neben dem regulären Node.js Extension Host.

Verwenden Sie die pwa-extensionhost-Startkonfiguration, die vom New Web Extension-Generator bereitgestellt wird

{
  "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"
    }
  ]
}

Sie verwendet die Aufgabe npm: watch-web, um die Erweiterung durch Aufruf von npm run watch-web zu kompilieren. Diese Aufgabe wird in tasks.json erwartet.

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

$ts-webpack-watch ist ein Problem-Matcher, der die Ausgabe des webpack-Tools parsen kann. Er wird von der TypeScript + Webpack Problem Matchers-Erweiterung bereitgestellt.

In der gestarteten Instanz des Extension Development Host ist die Web-Erweiterung verfügbar und läuft in einem Web Extension Host. Führen Sie den Befehl Hello World aus, um die Erweiterung zu aktivieren.

Öffnen Sie die Ansicht Running Extensions (Befehl: Developer: Show Running Extensions), um zu sehen, welche Erweiterungen im Web Extension Host laufen.

Testen Sie Ihre Web-Erweiterung im Browser mit @vscode/test-web

Das @vscode/test-web Node-Modul bietet eine CLI und eine API, um eine Web-Erweiterung im Browser zu testen.

Das Node-Modul stellt ein npm-Binary vscode-test-web bereit, das VS Code für das Web von der Befehlszeile aus öffnen kann.

  • Es lädt die Web-Bits von VS Code in den Ordner .vscode-test-web herunter.
  • Startet einen lokalen Server unter localhost:3000.
  • Öffnet einen Browser (Chromium, Firefox oder Webkit).

Sie können es von der Befehlszeile ausführen

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

Oder besser, fügen Sie @vscode/test-web als Entwicklungsabhängigkeit zu Ihrer Erweiterung hinzu und rufen Sie es in einem Skript auf.

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

Weitere CLI-Optionen finden Sie in der @vscode/test-web README.

Option Argument Beschreibung
--browserType Der zu startende Browser: chromium (Standard), firefox oder webkit.
--extensionDevelopmentPath Ein Pfad, der auf eine in Entwicklung befindliche Erweiterung verweist, die einbezogen werden soll.
--extensionTestsPath Ein Pfad zu einem Testmodul, das ausgeführt werden soll.
--permission Dem geöffneten Browser gewährte Berechtigung: z. B. clipboard-read, clipboard-write.
Siehe vollständige Liste der Optionen. Das Argument kann mehrmals angegeben werden.
--folder-uri URI des Workspace, auf dem VS Code geöffnet werden soll. Wird ignoriert, wenn folderPath angegeben ist.
--extensionPath Ein Pfad, der auf einen Ordner mit zusätzlichen Erweiterungen verweist, die einbezogen werden sollen.
Das Argument kann mehrmals angegeben werden.
folderPath Ein lokaler Ordner, auf dem VS Code geöffnet werden soll.
Der Ordnerinhalt wird als virtuelles Dateisystem verfügbar sein und als Workspace geöffnet.

Die Web-Bits von VS Code werden in einen Ordner .vscode-test-web heruntergeladen. Diesen Ordner sollten Sie zu Ihrer .gitignore-Datei hinzufügen.

Testen Sie Ihre Web-Erweiterung in vscode.dev

Bevor Sie Ihre Erweiterung für alle zur Verwendung in VS Code für das Web veröffentlichen, können Sie überprüfen, wie sich Ihre Erweiterung in der tatsächlichen vscode.dev-Umgebung verhält.

Um Ihre Erweiterung auf vscode.dev anzuzeigen, müssen Sie sie zuerst von Ihrem Rechner hosten, damit vscode.dev sie herunterladen und ausführen kann.

Zuerst müssen Sie mkcert installieren.

Generieren Sie dann die Dateien localhost.pem und localhost-key.pem in einem Ort, an dem Sie sie nicht verlieren (z. B. $HOME/certs).

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

Starten Sie dann von Ihrem Erweiterungs-Pfad aus einen HTTP-Server, indem Sie npx serve ausführen.

$ 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!               │
   │                                                    │
   └────────────────────────────────────────────────────┘

Öffnen Sie schließlich vscode.dev, führen Sie Developer: Install Extension From Location... aus der Befehlspalette (⇧⌘P (Windows, Linux Ctrl+Shift+P)) aus, fügen Sie die obige URL, z. B. https://:5000, ein und wählen Sie Install.

Überprüfen Sie die Protokolle

Sie können die Protokolle in der Konsole der Entwicklertools Ihres Browsers überprüfen, um Fehler, Status und Protokolle Ihrer Erweiterung zu sehen.

Sie sehen möglicherweise andere Protokolle von vscode.dev selbst. Außerdem können Sie nicht einfach Breakpoints setzen oder den Quellcode Ihrer Erweiterung einsehen. Diese Einschränkungen machen das Debuggen in vscode.dev nicht zur angenehmsten Erfahrung. Daher empfehlen wir die ersten beiden Optionen für Tests, bevor Sie auf vscode.dev sideloaden. Sideloading ist ein guter abschließender Funktionstest vor der Veröffentlichung Ihrer Erweiterung.

Web-Erweiterungstests

Web-Erweiterungstests werden unterstützt und können ähnlich wie normale Erweiterungstests implementiert werden. Lesen Sie den Artikel Erweiterungen testen, um die grundlegende Struktur von Erweiterungstests kennenzulernen.

Das @vscode/test-web Node-Modul ist das Äquivalent zu @vscode/test-electron (früher vscode-test genannt). Es ermöglicht Ihnen, Erweiterungstests von der Befehlszeile auf Chromium, Firefox und Safari auszuführen.

Das Dienstprogramm führt die folgenden Schritte aus:

  1. Startet einen VS Code for the Web-Editor von einem lokalen Webserver.
  2. Öffnet den angegebenen Browser.
  3. Führt das bereitgestellte Test-Runner-Skript aus.

Sie können die Tests in kontinuierlichen Builds ausführen, um sicherzustellen, dass die Erweiterung in allen Browsern funktioniert.

Das Test-Runner-Skript läuft im Web Extension Host mit denselben Einschränkungen wie die Hauptdatei der Web-Erweiterung.

  • Alle Dateien werden zu einer einzigen Datei gebündelt. Sie sollte den Test-Runner (z. B. Mocha) und alle Tests (typischerweise *.test.ts) enthalten.
  • Nur require('vscode') wird unterstützt.

Die von yo code für Web-Erweiterungen generierte webpack-Konfiguration hat einen Abschnitt für Tests. Sie erwartet das Test-Runner-Skript unter ./src/web/test/suite/index.ts. Das bereitgestellte Test-Runner-Skript verwendet die Web-Version von Mocha und enthält webpack-spezifische Syntax, um alle Testdateien zu importieren.

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);
    }
  });
}

Um den Web-Test von der Befehlszeile auszuführen, fügen Sie Ihrer package.json Folgendes hinzu und führen Sie ihn mit npm test aus.

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

Um VS Code auf einem Ordner mit Testdaten zu öffnen, übergeben Sie einen lokalen Ordnerpfad (folderPath) als letzten Parameter.

Verwenden Sie die Startkonfiguration Extension Tests in VS Code, um Erweiterungstests in VS Code (Insiders) Desktop auszuführen (und zu debuggen).

{
  "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"
    }
  ]
}

Veröffentlichen einer Web-Erweiterung

Web-Erweiterungen werden zusammen mit anderen Erweiterungen auf dem Marketplace gehostet.

Stellen Sie sicher, dass Sie die neueste Version von vsce verwenden, um Ihre Erweiterung zu veröffentlichen. vsce kennzeichnet alle Erweiterungen, die Web-Erweiterungen sind. Dazu verwendet vsce die Regeln, die im Abschnitt Aktivierung von Web-Erweiterungen aufgeführt sind.

Bestehende Erweiterungen auf Web-Erweiterungen aktualisieren

Erweiterung ohne Code

Erweiterungen, die keinen Code enthalten, sondern nur Beitragspunkte (z. B. Themes, Snippets und einfache Spracherweiterungen), benötigen keine Änderungen. Sie können in einem Web Extension Host ausgeführt und aus der Erweiterungsansicht installiert werden.

Eine erneute Veröffentlichung ist nicht notwendig, aber wenn Sie eine neue Version der Erweiterung veröffentlichen, stellen Sie sicher, dass Sie die aktuellste Version von vsce verwenden.

Migrieren von Erweiterungen mit Code

Erweiterungen mit Quellcode (definiert durch die main-Eigenschaft) müssen eine Hauptdatei der Web-Erweiterung bereitstellen und die browser-Eigenschaft in package.json setzen.

Verwenden Sie diese Schritte, um Ihren Erweiterungscode für die Browserumgebung neu zu kompilieren:

  • Fügen Sie eine webpack-Konfigurationsdatei hinzu, wie im Abschnitt webpack-Konfiguration gezeigt. Wenn Sie bereits eine webpack-Datei für Ihren Node.js-Erweiterungscode haben, können Sie einen neuen Abschnitt für Web hinzufügen. Sehen Sie sich vscode-css-formatter als Beispiel an.
  • Fügen Sie die Dateien launch.json und tasks.json hinzu, wie im Abschnitt Testen Sie Ihre Web-Erweiterung gezeigt.
  • Setzen Sie in der webpack-Konfigurationsdatei die Eingabedatei auf die bestehende Node.js-Hauptdatei oder erstellen Sie eine neue Hauptdatei für die Web-Erweiterung.
  • Fügen Sie in package.json die Eigenschaften browser und scripts hinzu, wie im Abschnitt Anatomie einer Web-Erweiterung gezeigt.
  • Führen Sie npm run compile-web aus, um webpack aufzurufen und zu sehen, wo Arbeiten erforderlich sind, damit Ihre Erweiterung im Web läuft.

Um sicherzustellen, dass so viel Quellcode wie möglich wiederverwendet werden kann, hier einige Techniken:

  • Um ein Node.js-Kernmodul wie path zu polyfillen, fügen Sie einen Eintrag zu resolve.fallback hinzu.
  • Um ein Node.js-Global wie process bereitzustellen, verwenden Sie das DefinePlugin-Plugin.
  • Verwenden Sie Node-Module, die sowohl in der Browser- als auch in der Node-Laufzeit funktionieren. Node-Module können dies tun, indem sie sowohl browser- als auch main-Einstiegspunkte definieren. Webpack verwendet automatisch denjenigen, der seinem Ziel entspricht. Beispiele für Node-Module, die dies tun, sind request-light und @vscode/l10n.
  • Um eine alternative Implementierung für ein Node-Modul oder eine Quelldatei bereitzustellen, verwenden Sie resolve.alias.
  • Trennen Sie Ihren Code in einen Browser-Teil, einen Node.js-Teil und einen gemeinsamen Teil. Verwenden Sie im gemeinsamen Teil nur Code, der sowohl im Browser als auch in der Node.js-Laufzeit funktioniert. Erstellen Sie Abstraktionen für Funktionalität, die in Node.js und im Browser unterschiedliche Implementierungen hat.
  • Achten Sie auf die Verwendung von path, URI.file, context.extensionPath, rootPath, uri.fsPath. Diese funktionieren nicht mit virtuellen Workspaces (nicht-Dateisystem), wie sie in VS Code für das Web verwendet werden. Verwenden Sie stattdessen URIs mit URI.parse, context.extensionUri. Das Node-Modul vscode-uri bietet joinPath, dirName, baseName, extName, resolvePath.
  • Achten Sie auf die Verwendung von fs. Ersetzen Sie dies durch die Verwendung von VS Code workspace.fs.

Es ist in Ordnung, weniger Funktionalität bereitzustellen, wenn Ihre Erweiterung im Web ausgeführt wird. Verwenden Sie When-Clause-Kontexte, um zu steuern, welche Befehle, Ansichten und Aufgaben verfügbar oder ausgeblendet werden, wenn Sie in einem virtuellen Workspace im Web laufen.

  • Verwenden Sie die Kontextvariable virtualWorkspace, um festzustellen, ob der aktuelle Workspace ein Nicht-Dateisystem-Workspace ist.
  • Verwenden Sie resourceScheme, um zu prüfen, ob die aktuelle Ressource eine file-Ressource ist.
  • Verwenden Sie shellExecutionSupported, wenn eine Plattform-Shell vorhanden ist.
  • Implementieren Sie alternative Befehlshandler, die einen Dialog anzeigen, um zu erklären, warum der Befehl nicht anwendbar ist.

WebWorkers können als Alternative zur Prozess-Forschung verwendet werden. Wir haben mehrere Sprachserver aktualisiert, um als Web-Erweiterungen zu laufen, einschließlich der integrierten JSON-, CSS- und HTML-Sprachserver. Der Abschnitt Language Server Protocol unten enthält weitere Details.

Die Browser-Laufzeitumgebung unterstützt nur die Ausführung von JavaScript und WebAssembly. Bibliotheken, die in anderen Programmiersprachen geschrieben sind, müssen cross-kompiliert werden. Es gibt zum Beispiel Werkzeuge, um C/C++ und Rust nach WebAssembly zu kompilieren. Die vscode-anycode-Erweiterung verwendet zum Beispiel tree-sitter, das ein in WebAssembly kompilierter C/C++-Code ist.

Language Server Protocol in Web-Erweiterungen

vscode-languageserver-node ist eine Implementierung des Language Server Protocol (LSP), die als Grundlage für Implementierungen von Sprachservern wie JSON, CSS und HTML verwendet wird.

Seit Version 3.16.0 bieten der Client und der Server auch eine Browser-Implementierung. Der Server kann in einem Web-Worker ausgeführt werden und die Verbindung basiert auf dem postMessage-Protokoll von Web-Workern.

Der Client für den Browser ist unter 'vscode-languageclient/browser' zu finden.

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

Der Server unter vscode-languageserver/browser.

Das lsp-web-extension-sample zeigt, wie das funktioniert.

Aktivierung von Web-Erweiterungen

VS Code behandelt eine Erweiterung automatisch als Web-Erweiterung, wenn

  • Das Erweiterungsmanifest (package.json) über einen browser-Einstiegspunkt verfügt.
  • Das Erweiterungsmanifest keinen main-Einstiegspunkt und keine der folgenden Beitragsarten hat: localizations, debuggers, terminal, typescriptServerPlugins.

Wenn eine Erweiterung einen Debugger oder ein Terminal bereitstellen möchte, das auch im Web Extension Host funktioniert, muss ein browser-Einstiegspunkt definiert werden.

Verwenden von ESBuild

Wenn Sie esbuild anstelle von webpack verwenden möchten, gehen Sie wie folgt vor:

Fügen Sie ein esbuild.js Build-Skript hinzu.

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);
});

Das Build-Skript führt folgende Aktionen aus:

  • Es erstellt einen Build-Kontext mit esbuild. Der Kontext ist konfiguriert, um
    • Den Code in src/web/extension.ts in eine einzelne Datei dist/web/extension.js zu bündeln.
    • Alle Tests, einschließlich des Test-Runners (mocha), in eine einzelne Datei dist/web/test/suite/extensionTests.js zu bündeln.
    • Den Code zu minifizieren, wenn das Flag --production übergeben wurde.
    • Source Maps zu generieren, es sei denn, das Flag --production wurde übergeben.
    • Das Modul 'vscode' vom Bundle auszuschließen (da es von der VS Code-Laufzeit bereitgestellt wird).
    • Polyfills für process und buffer zu erstellen.
    • Das esbuildProblemMatcherPlugin-Plugin zu verwenden, um Fehler zu melden, die das Abschließen des Bundlers verhindert haben. Dieses Plugin gibt die Fehler in einem Format aus, das vom esbuild-Problem-Matcher erkannt wird, der ebenfalls als Erweiterung installiert werden muss.
    • Das testBundlePlugin zu verwenden, um eine Test-Hauptdatei (extensionTests.js) zu implementieren, die auf alle Testdateien und den mocha-Test-Runner mochaTestRunner.js verweist.
  • Wenn das Flag --watch übergeben wurde, werden die Quelldateien auf Änderungen überwacht und das Bundle neu erstellt, sobald eine Änderung erkannt wird.

esbuild kann direkt mit TypeScript-Dateien arbeiten. Allerdings entfernt esbuild 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, jedoch ohne Code auszugeben (Flag --noEmit).

Der Abschnitt scripts in package.json sieht jetzt so aus:

  "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 ist ein Node-Modul, das Skripte parallel ausführt, deren Namen einem bestimmten Präfix entsprechen. Für uns führt es die Skripte watch-web:esbuild und watch-web:tsc aus. Sie müssen npm-run-all zur Sektion devDependencies in package.json hinzufügen.

Die folgenden tasks.json-Dateien bieten separate Terminals für jede Watch-Aufgabe.

{
  "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"]
    }
  ]
}

Dies ist der mochaTestRunner.js, auf den im esbuild-Build-Skript verwiesen wird.

// 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);
    }
  });
}

Beispiele

© . This site is unofficial and not affiliated with Microsoft.