网络扩展

Visual Studio Code 可以作为浏览器中的编辑器运行。一个示例是在 GitHub 中浏览存储库或 Pull 请求时github.dev按(句点键)即可到达的用户界面。.当 VS Code 在 Web 中使用时,安装的扩展将在浏览器中的扩展主机(称为“Web 扩展主机”)中运行。可以在 Web 扩展主机中运行的扩展称为“Web 扩展”。

Web 扩展与常规扩展共享相同的结构,但考虑到不同的运行时,不要使用与为 Node.js 运行时编写的扩展相同的代码来运行。Web 扩展仍然可以访问完整的 VS Code API,但不再可以访问 Node.js API 和模块加载。相反,Web 扩展受到浏览器沙箱的限制,因此与普通扩展相比具有局限性。

VS Code 桌面也支持 Web 扩展运行时。如果您决定将扩展创建为 Web 扩展,则VS Code for Web(包括vscode.devgithub.dev)以及桌面和GitHub Codespaces等服务将支持该扩展。

Web 扩展剖析

Web 扩展的结构与常规扩展类似。扩展清单 ( package.json) 定义扩展源代码的入口文件并声明扩展贡献。

对于 Web 扩展,主条目文件browser属性定义,而不是main像常规扩展那样由属性定义。

contributes属性对于 Web 扩展和常规扩展的工作方式相同。

下面的示例显示了package.json一个简单的 hello world 扩展,该扩展仅在 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 版本,则必须onCommand:helloworld-web-sample.helloWorldactivationEvents.

只有main入口点但没有入口点的扩展browser不是 Web 扩展。它们会被 Web 扩展主机忽略,并且无法在扩展视图中下载。

扩展视图

仅具有声明性贡献(仅contributes、无mainbrowser)的扩展可以是 Web 扩展。它们可以在VS Code for the Web中安装和运行,无需扩展作者进行任何修改。具有声明性贡献的扩展示例包括主题、语法和片段。

扩展可以同时具有browsermain入口点,以便在浏览器和 Node.js 运行时中运行。将现有扩展更新为 Web 扩展部分展示了如何迁移扩展以在两个运行时中工作。

Web扩展启用部分列出了用于决定扩展是否可以加载到 Web 扩展主机中的规则。

Web 扩展主文件

Web 扩展的主文件由属性定义browser该脚本在Browser WebWorker环境中的 Web 扩展主机中运行。它受到浏览器工作沙箱的限制,并且与 Node.js 运行时中运行的普通扩展相比具有局限性。

  • 不支持导入或要求其他模块。importScripts也不可用。因此,代码必须打包到单个文件中。
  • VS Code API可以通过模式加载require('vscode')。这将起作用,因为有一个垫片require,但该垫片不能用于加载其他扩展文件或其他节点模块。它仅适用于require('vscode').
  • Node.js 全局变量和库(例如processossetImmediate、 、pathutilurl在运行时不可用。但是,可以使用 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 = ...

开发网络扩展

值得庆幸的是,像 TypeScript 和 webpack 这样的工具可以隐藏许多浏览器运行时限制,并允许您像常规扩展一样编写 Web 扩展。Web 扩展和常规扩展通常可以从相同的源代码生成。

例如,生成器Hello Web Extension创建的仅在构建脚本上有所不同。您可以像传统 Node.js 扩展一样运行和调试生成的扩展,方法是使用提供的启动配置(可使用“调试:选择并开始调试”命令访问)。yo code

创建网络扩展

要构建新的 Web 扩展,请使用yo code并选择New Web Extension。确保安装了最新版本的生成器代码(>=生成器代码@1.6)。要更新生成器,请运行npm i -g yo generator-code.

创建的扩展由扩展的源代码(显示 hello world 通知的命令)、清单package.json文件和 webpack 配置文件组成。

  • src/web/extension.ts是扩展的入口源代码文件。它与常规的 hello 扩展相同。
  • package.json是扩展清单。
    • 它指向使用browser属性的入口文件。
    • 它提供了脚本:compile-webwatch-webpackage-web来编译、监视和打包。
  • webpack.config.js是 webpack 配置文件,它将扩展源编译并捆绑到单个文件中。
  • .vscode/launch.json包含运行 Web 扩展的启动配置以及使用 Web 扩展主机在 VS Code 桌面中进行的测试(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 扩展主机中。

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];

一些重要的领域webpack.config.js是:

  • entry字段包含扩展和测试套件的主要入口点。
    • 您可能需要调整此路径以适当地指向扩展的入口点。
    • 对于现有扩展,您可以首先将此路径指向您当前正在使用mainpackage.json.
    • 如果您不想打包测试,则可以省略测试套件字段。
  • output字段指示编译文件的位置。
    • [name]将被替换为 中使用的密钥entry。所以在生成的配置文件中,它会产生dist/web/extension.jsdist/web/test/suite/index.js
  • target字段指示编译后的 JavaScript 文件将运行的环境类型。对于网络扩展,您希望它是webworker.
  • resolve字段包含为在浏览器中不起作用的节点库添加别名和后备的功能。
    • 如果您使用像 之类的库path,则可以指定如何path在 Web 编译上下文中解析。例如,您可以指向项目中定义pathpath: path.resolve(__dirname, 'src/my-path-implementation-for-web.js'). path-browserify或者您可以使用名为并指定的Browserify 节点打包版本的库path: require.resolve('path-browserify')
    • 有关 Node.js 核心模块 polyfill 的列表,请参阅webpackresolve.fallback 。
  • plugins节使用DefinePlugin 插件来填充全局变量,例如processNode.js 全局变量。

测试您的网络扩展

目前,在将 Web 扩展发布到 Marketplace 之前,可以通过三种方法对其进行测试。

  • 使用在桌面上运行的 VS Code,并可选择--extensionDevelopmentKind=web在 VS Code 中运行的 Web 扩展主机中运行 Web 扩展。
  • 使用@vscode/test-web节点模块打开包含 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 Problem Matchers扩展提供。

在启动的扩展开发主机实例中,Web 扩展将可用并在 Web 扩展主机中运行。运行Hello World命令以激活扩展。

打开正在运行的扩展视图(命令:开发人员:显示正在运行的扩展)以查看哪些扩展正在 Web 扩展主机中运行。

使用 @vscode/test-web 在浏览器中测试您的 Web 扩展

@vscode/test-web节点模块提供了 CLI 和 API 来在浏览器中测试 Web 扩展。

Node 模块提供了一个 npm 二进制文件vscode-test-web,可以从命令行打开 VS Code for the Web:

  • 它将 VS Code 的网页下载到.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 自述文件以获取更多 CLI 选项:

选项 参数说明
--browserType 要启动的浏览器:(chromium默认),firefoxwebkit
--extensionDevelopmentPath 指向要包含的正在开发的扩展的路径。
--extensionTestsPath 要运行的测试模块的路径。
--permission 授予打开的浏览器的权限:例如clipboard-readclipboard-write
查看完整的选项列表。参数可以多次提供。
--folder-uri 用于打开 VS Code 的工作区的 URI。folderPath提供时被忽略
--extensionPath 指向包含要包含的其他扩展的文件夹的路径。
参数可以多次提供。
folderPath 用于打开 VS Code 的本地文件夹。
文件夹内容将作为虚拟文件系统提供并作为工作区打开。

VS Code 的 Web 部分被下载到一个文件夹中.vscode-test-web。您想将其添加到您的.gitignore文件中。

在 vscode.dev 中测试您的 Web 扩展

在发布扩展以供所有人在 VS Code 网页版 上使用之前,您可以验证扩展在实际vscode.dev环境中的行为方式。

要在 vscode.dev 上查看您的扩展,您首先需要从您的计算机托管它,以便 vscode.dev 下载并运行。

首先,您需要安装mkcert.

然后,将localhost.pemlocalhost-key.pem文件生成到不会丢失的位置(例如$HOME/certs):

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

然后,从扩展程序的路径中,通过运行以下命令启动 HTTP 服务器npx serve

$ 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 ) )运行Developer: Install Extension From Location... ,在示例中粘贴上面的 URL,然后选择Installhttps://localhost:5000

检查日志

您可以在浏览器的开发人员工具控制台中检查日志,以查看扩展程序中的任何错误、状态和日志。

您可能会看到 vscode.dev 本身的其他日志。此外,您无法轻松设置断点,也无法查看扩展的源代码。这些限制使得在 vscode.dev 中进行调试并不是最愉快的体验,因此我们建议在侧载到 vscode.dev 之前使用前两个选项进行测试。在发布扩展之前,旁加载是一个很好的最终健全性检查。

网络扩展测试

支持 Web 扩展测试,并且可以像常规扩展测试一样实现。请参阅测试扩展文章以了解扩展测试的基本结构。

@vscode/test-web节点模块相当于@vscode/test- Electron(以前名为vscode-test)。它允许您在 Chromium、Firefox 和 Safari 上从命令行运行扩展测试。

该实用程序执行以下步骤:

  1. 从本地 Web 服务器启动 Web 编辑器的 VS Code。
  2. 打开指定的浏览器。
  3. 运行提供的测试运行程序脚本。

您可以在连续构建中运行测试,以确保扩展程序适用于所有浏览器。

测试运行器脚本在 Web 扩展主机上运行,​​具有与Web 扩展主文件相同的限制:

  • 所有文件都捆绑到一个文件中。它应该包含测试运行程序(例如,Mocha)和所有测试(通常*.test.ts)。
  • require('vscode')支持。

由 Web 扩展生成器创建的Webpack配置yo code有一个用于测试的部分。它期望测试运行器脚本位于./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 扩展与其他扩展一起托管在Marketplace上。

确保使用最新版本vsce来发布您的扩展。vsce标记所有属于 Web 扩展的扩展。为此,请使用Web 扩展启用vsce部分中列出的规则。

将现有扩展更新为 Web 扩展

无代码扩展

没有代码、只有贡献点的扩展(例如主题、片段和基本语言扩展)不需要任何修改。它们可以在 Web 扩展主机中运行,并且可以从扩展视图安装。

不需要重新发布,但是在发布新版本的扩展时,请确保使用最新版本的vsce.

使用代码迁移扩展

具有源代码(由main属性定义)的扩展需要提供一个Web 扩展主文件browserpackage.json.

使用以下步骤为浏览器环境重新编译扩展代码:

  • 添加 webpack 配置文件,如webpack 配置部分所示。如果您已经有 Node.js 扩展代码的 webpack 文件,则可以为 web 添加一个新部分。查看vscode-css-formatter作为示例。
  • 添加launch.json和文件,如测试您的 Web 扩展tasks.json部分所示。
  • 在 webpack 配置文件中,将输入文件设置为现有的 Node.js 主文件或为 Web 扩展创建一个新的主文件。
  • 在 中package.json,添加 abrowser和属性,如Web 扩展剖析scripts部分所示。
  • 运行npm run compile-web以调用 webpack 并查看需要在哪里工作才能使您的扩展在网络中运行。

为了确保尽可能多的源代码可以被重用,这里有一些技巧:

  • 要填充 Node.js 核心模块(例如 ) ,请向resolve.fallbackpath添加一个条目。
  • 要提供 Node.js 全局,例如process使用DefinePlugin 插件
  • 使用可在浏览器和节点运行时运行的节点模块。节点模块可以通过定义browsermain入口点来做到这一点。Webpack 将自动使用与其目标匹配的那个。执行此操作的节点模块的示例是request-lightvscode-nls
  • 要为节点模块或源文件提供替代实现,请使用resolve.alias
  • 将代码分为浏览器部分、Node.js 部分和公共部分。通常,仅使用可在浏览器和 Node.js 运行时运行的代码。为在 Node.js 和浏览器中具有不同实现的功能创建抽象。
  • 注意path, URI.file, context.extensionPath,的用法rootPathuri.fsPath。这些不适用于虚拟工作区(非文件系统),因为它们在 VS Code for the Web 中使用。而是使用带有URI.parse,的 URI context.extensionUrivscode-uri节点模块提供joinPath, dirName, baseName, extName, resolvePath
  • 留意 的用法fs。使用 vscode 替换workspace.fs

当您的扩展程序在网络中运行时,提供较少的功能是可以的。使用when 子句上下文来控制哪些命令、视图和任务在 Web 虚拟工作区中运行时可用或隐藏。

  • 使用virtualWorkspace上下文变量来查明当前工作区是否是非文件系统工作区。
  • 用于resourceScheme检查当前资源是否为file资源。
  • shellExecutionSupported如果存在平台 shell,则使用。
  • 实现替代命令处理程序,显示一个对话框来解释该命令不适用的原因。

WebWorkers 可以用作分叉流程的替代方案。我们更新了多种语言服务器以作为 Web 扩展运行,包括内置的JSONCSSHTML语言服务器。下面的语言服务器协议部分提供了更多详细信息。

浏览器运行环境仅支持 JavaScript 和WebAssembly的执行。用其他编程语言编写的库需要交叉编译,例如有工具将C/C++Rust编译为 WebAssembly。例如, vscode-anycode 扩展使用tree - sitter,它是编译为 WebAssembly 的 C/C++ 代码。

Web 扩展中的语言服务器协议

vscode-languageserver-node是语言服务器协议(LSP)的实现,用作JSONCSSHTML等语言服务器实现的基础。

从3.16.0开始,客户端和服务器现在也提供了浏览器实现。服务器可以在 Web Worker 中运行,并且连接基于 WebWorkerspostMessage协议。

浏览器的客户端可以在“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需要定义一个入口点。

样品