本站点文档内容均翻译自code.visualstudio.com,仅供个人学习,如有差异请以官网为准。

网页扩展

Visual Studio Code 可以在浏览器中作为编辑器运行。一个例子是github.dev用户界面通过按压到达输入:.(句点键)在 GitHub 上浏览仓库或拉取请求时。当 VS Code 在网络上使用时,已安装的扩展在浏览器中的扩展主机上运行,称为“网络扩展主机”。可以在网络扩展主机上运行的扩展称为“网络扩展”。

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

网页扩展运行时也支持 VS Code 桌面版。如果您决定将扩展创建为网页扩展,则它将支持 VS Code for the Web(包括 vscode.devgithub.dev) 以及在桌面和像 GitHub Codespaces 这样的服务中。

网页扩展解剖学

一个网页扩展的结构与常规扩展相同。扩展清单 (package.json) 定义了扩展源代码的入口文件并声明扩展贡献。

对于网页扩展,主入口文件浏览器财产,并非由主要属性与常规扩展相同。

贡献属性对网页和常规扩展的工作方式相同。

下面的示例显示了package.json对于一个简单的Hello World扩展,该扩展仅在Web扩展主机中运行(它只有浏览器入口点):

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

注意:如果您的扩展针对的是 VS Code 1.74 之前的版本,则必须明确列出onCommand: helloworld-web-sample.helloWorld激活事件输入:.

扩展名仅有一个主要入口点,但没有浏览器不是网络扩展。它们被网络扩展主机忽略,并且无法在扩展视图中下载。

扩展视图

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

扩展可以同时拥有两者浏览器主要 入口点以在浏览器和 Node.js 运行时中运行。 更新现有的扩展为 Web 扩展 部分展示了如何将扩展迁移到两个运行时中都能工作。

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

网页扩展主文件

网页扩展的主文件由定义浏览器 属性。脚本在浏览器扩展主机中的浏览器Web Worker环境中运行。它受浏览器工作程序沙盒的限制,并且与在Node.js运行时中运行的普通扩展相比具有局限性。

  • 不支持导入或引用其他模块。导入脚本无法以其他形式提供。因此,代码必须被打包成一个文件。
  • VS Code API可以通过模式加载require('vscode')这将有效,因为有一个垫片要求,但是这个补丁不能用来加载额外的扩展文件或额外的节点模块。它只适用于require('vscode')输入:.
  • Node.js 全局和库,例如处理操作系统设置立即执行路径实用对不起,我无法处理这个请求。 在运行时不可用。然而,可以使用像webpack这样的工具来添加它们。 webpack配置 部分解释了如何做到这一点。
  • 打开的工作区或文件夹位于虚拟文件系统上。访问工作区文件需要通过 VS Code 文件系统 API,该 API 可在 vscode.workspace.fs输入:.
  • 扩展上下文 位置 (扩展上下文扩展URI) 和 存储位置 (扩展上下文存储路径全局存储路径) 也是在虚拟文件系统上,并需要通过vscode.workspace.fs输入:.
  • 要访问网络资源,必须使用获取 API。访问的资源需要支持跨域资源共享 (CORS)
  • 无法创建子进程或运行可执行文件。然而,可以通过Worker API 创建 web 工作线程。这用于在网页扩展中的语言服务器协议部分中运行语言服务器。
  • 与常规扩展一样,扩展的激活/停用函数需要通过模式导出exports.activate = ...输入:.

开发一个网页扩展

幸运的是,像 TypeScript 和 webpack 这样的工具可以隐藏许多浏览器运行时的限制,使您能够以与普通扩展相同的方式编写 Web 扩展。通常,可以从相同的源代码生成 Web 扩展和普通扩展。

例如,你好,网页扩展由...创建你的代码 生成器仅在构建脚本中有所不同。您可以通过使用提供的 launch 配置(通过调试:选择并开始调试命令访问)来运行和调试生成的扩展,就像传统的 Node.js 扩展一样。

创建一个网页扩展

要构建一个新的网页扩展,请使用你的代码 并选择 新的 Web 扩展。确保安装了最新版本的 generator-code(>= generator-code@1.6)。要更新 generator 和 yo,请运行 npm i -g yo generator-code输入:.

创建的扩展包括扩展的源代码(显示“Hello World”通知的命令)以及package.json清单文件,以及一个webpack或esbuild配置文件。

为了使事情更简单,我们假设你使用webpack作为打包者。在文章的最后,我们还将解释选择时有何不同。esbuild输入:.

  • src/web/extension.ts是扩展的入口源代码文件。它与普通的 hello 扩展相同。
  • package.json是扩展程序清单。
    • 它使用 指向入口文件浏览器财产。
    • 它提供了脚本:编译网页观看网页包-网页编译、观看和打包。
  • webpack.config.js是用于编译和打包扩展源文件到一个文件的webpack配置文件。
  • .vscode/launch.json包含在 VS Code 桌面中运行 web 扩展和测试的启动配置,带有 web 扩展主机 (设置扩展.web工人不再需要。
  • .vscode/task.json包含用于启动配置的构建任务。它使用npm 运行 watch-web并且取决于webpack的特定ts-webpack-watch问题匹配器。
  • .vscode/extensions.json包含提供问题匹配器的扩展。这些扩展需要安装才能使启动配置正常工作。
  • tsconfig.json定义了编译选项匹配网页工作者运行时。

helloworld-web-sample中的源代码与生成器创建的类似。

Webpack 配置

webpack 配置文件由自动生成你的代码它将您的扩展源代码打包成一个JavaScript文件,以加载到网络扩展主机中。

稍后我们会解释如何使用 esbuild 作为打包器,但现在我们从 webpack 开始。

webpack.config.js

```plaintext
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' // 创建一个指向原始源文件的源映射
};
module.exports = [webExtensionConfig];

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

  • 条目field 包含进入你的扩展和测试套件的主要入口点。
    • 您可能需要调整此路径,以正确地指向您的扩展的入口点。
    • 对于现有的扩展,您可以从将此路径指向您当前使用的文件开始主要你的package.json输入:.
    • 如果您不想打包您的测试,可以省略测试套件字段。
  • 输出字段指示编译后的文件将位于何处。
    • [名字]将被用于的密钥替换条目所以,在生成的配置文件中,它将产生dist/web/extension.jsdist/web/test/suite/index.js输入:.
  • 目标字段指示编译后的JavaScript文件将运行的环境类型。对于网页扩展,您希望这是网页工作者输入:.
  • 解决field 包含为无法在浏览器中运行的节点库添加别名和备用功能的能力。
    • 如果你使用像路径,您可以指定如何解决路径在网页编译上下文中。例如,您可以指向项目中定义的一个文件。路径路径: path.resolve(__dirname, 'src/my-path-implementation-for-web.js'). 或者你可以使用通过 Browserify 包装的库的节点版本,叫做路径浏览器ify并指定路径: require.resolve('path-browserify')输入:.
    • webpack resolve.fallback获取Node.js核心模块补丁的列表。
  • 插件 section 使用 DefinePlugin 插件 来填充全局变量,例如 处理Node.js 全局。

测试您的网页扩展

目前有三种方法可以在发布到市场之前测试网络扩展。

  • 在桌面上使用 VS Code 运行--extensionDevelopmentKind=网页选项在 VS Code 中运行的 web extension host 中运行您的 web 扩展。
  • 使用 @vscode/test-web 节点模块打开一个包含 VS Code for Web 和你的扩展的浏览器,由本地服务器提供。
  • 侧载你的扩展到vscode.dev以在实际环境中看到你的扩展。

在桌面运行的 VS Code 中测试您的网络扩展

为了使用现有的 VS Code 扩展开发体验,桌面版 VS Code 支持同时运行一个网页扩展主机和常规的 Node.js 扩展主机。

使用pwa-扩展主机新网页扩展 生成器提供的启动配置:

{
  "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 运行 watch-web. 那项任务预计在任务.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 问题匹配器 扩展提供的。

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

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

在浏览器中使用 @vscode/test-web 测试你的网页扩展

@vscode/test-web node 模块提供了一个 CLI 和 API,用于在浏览器中测试一个 web 扩展。

该节点模块贡献了一个npm二进制文件vscode测试网页可以在命令行中打开 VS Code for the Web:

  • 它下载 VS Code 的网络组件到.vscode-test-web输入:.
  • 启动本地服务器于本地主机:3000输入:.
  • 打开浏览器(Chromium、Firefox 或 Webkit)。

你可以在命令行中运行它:

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

或者更好的方法是添加@vscode/测试网页将其作为扩展的开发依赖项,并在脚本中调用它:

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

查看@vscode/test-web 仓库以获取更多 CLI 选项:

选项 参数描述
--浏览器类型 要启动的浏览器:(默认),火狐浏览器webkit
--扩展开发路径 一个指向正在开发的扩展路径,以包括。
--扩展测试路径 一个用于运行测试模块的路径。
--权限 允许打开的浏览器:例如剪贴板读取剪贴板写入.
查看完整选项列表。参数可以多次提供。
--文件夹-URI 打开 VS Code 的工作区的 URI。当 文件夹路径提供
--扩展路径 一个指向包含附加扩展的文件夹的路径。
参数可以多次提供。
文件夹路径 一个本地文件夹,用于在VS Code上打开。
文件夹内容将作为虚拟文件系统提供,并作为工作区打开。

VS Code 的网络组件被下载到一个文件夹中.vscode-test-web你想将这个添加到你的.gitignore文件。

在vscode.dev测试你的网页扩展

在您将扩展发布给所有人使用之前,您可以验证您的扩展在实际的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: 2.196秒内安装78

   ┌────────────────────────────────────────────────────┐
   │                                                    │
   │   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)),粘贴上面的URL,https://localhost:5000 在示例中,并选择 安装.

查看日志

您可以在浏览器开发者工具的控制台中查看扩展的错误、状态和日志。

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

网页扩展测试

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

@vscode/test-web node 模块等同于@vscode/test-electron(以前名为vscode测试它允许您在 Chromium、Firefox 和 Safari 上通过命令行运行扩展测试。

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

  1. 启动一个 VS Code 网页编辑器,来自本地网页服务器。
  2. 打开指定的浏览器。
  3. 运行提供的测试运行脚本。

您可以在持续构建中运行测试,以确保扩展在所有浏览器上都能正常工作。

测试运行器脚本在与网络扩展主文件相同限制的网络扩展主机上运行:

  • 所有文件被打包成一个文件。它应该包含测试运行器(例如,Mocha)和所有测试(通常*.test.ts)。
  • require('vscode')得到支持。

这个webpack配置是通过你的代码web extension generator 有一个测试部分。它期望测试运行器脚本位于./src/web/test/suite/index.ts提供的测试运行器脚本使用了Mocha的网络版本,并包含用于导入所有测试文件的webpack特定语法。

require('mocha/mocha'); // 导入 mocha web 版本

导出 函数 运行(): Promise<void> {
  返回 new Promise((c, e) => {
    mocha.设置({
      用户界面: 'tdd',
      报告器: 未定义
    });

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

要从命令行运行网页测试,请将以下内容添加到你的package.json并用它运行npm 测试输入:.

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

要打开包含测试数据的文件夹的 VS Code,请传递一个本地文件夹路径 (文件夹路径) 作为最后一个参数。

要在 VS Code (Insiders) 桌面版中运行(和调试)扩展测试,请使用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"
    }
  ]
}

发布一个网页扩展

网页扩展存储在市场中,与其他扩展一起。

确保使用最新版本的vsce发布您的扩展。vsce将所有扩展标记为 web 扩展。为此vsce 正在使用 网络扩展启用 部分中列出的规则。

将现有扩展更新为 Web 扩展

无代码扩展

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

重新发布不是必须的,但在发布扩展的新版本时,请确保使用最新的版本vsce输入:.

使用代码迁移扩展

带有源代码的扩展(由主要 属性)需要提供一个 网络扩展主文件 并设置 浏览器物业在package.json输入:.

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

  • 添加一个webpack配置文件,如 webpack配置 所示。如果你已经有一个用于Node.js扩展代码的webpack文件,可以在其中添加一个web部分。查看 vscode-css-formatter 作为一个例子。
  • 添加launch.json任务.json 文件如 测试您的网络扩展 部分所示。
  • 在webpack配置文件中,将输入文件设置为现有的Node.js主文件,或者为Web扩展创建一个新的主文件。
  • package.json,添加一个浏览器脚本 属性如 网页扩展解剖 所示。
  • 执行npm 运行 compile-web调用webpack并查看需要在哪里进行工作以使您的扩展在网页中运行。

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

  • 要填充Node.js核心模块,例如路径,添加一条记录到resolve.fallback
  • 提供一个Node.js全局变量,例如处理 使用 DefinePlugin 插件
  • 使用在浏览器和 node 运行时都能正常工作的 node 模块。Node 模块可以通过同时定义来实现这一点 浏览器主要 入口点。Webpack 会自动使用与目标匹配的那个。执行此操作的 node 模块示例有 request-light@vscode/l10n
  • 要为节点模块或源文件提供替代实现,请使用resolve.alias
  • 将代码分为浏览器部分、Node.js部分和通用部分。通常,只使用在浏览器和Node.js运行时都能运行的代码。为在Node.js和浏览器中具有不同实现的功能创建抽象。
  • 请注意以下用法路径URI.文件上下文.扩展路径根路径uri.fsPath这些将无法在虚拟工作区(非文件系统)中使用,因为它们在 VS Code Web 中使用。相反,请使用带有 的 URIsURI.parse上下文.扩展uri。The vscode-uri 节点模块提供 连接路径目录名基础名称扩展名解析路径输入:.
  • 请注意以下用法输入:fs使用vscode替换工作区文件系统输入:.

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

  • 使用虚拟工作区上下文变量以确定当前工作区是否为非文件系统工作区。
  • 使用资源方案检查当前资源是否为文件资源。
  • 使用外壳执行支持如果有平台外壳。
  • 实现替代命令处理程序,显示对话框解释为什么该命令不适用。

WebWorkers 可以用作进程 fork 的替代方案。我们已经更新了几个语言服务器以作为网页扩展运行,包括内置的 JSONCSSHTML 语言服务器。下面的 语言服务器协议 部分提供了更多详细信息。

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

网页扩展中的语言服务器协议

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

自3.16.0起,客户端和服务器现在也提供了一个浏览器实现。服务器可以在Web Worker中运行,连接基于Web Workers。发布消息协议。

浏览器的客户端可以在'vscode-languageclient/browser'找到:

导入 { LanguageClient }  `vscode-languageclient/browser`;

服务器在vscode-languageserver/浏览器输入:.

这个lsp-web-extension-sample展示了如何实现。

网页扩展启用

VS Code 会自动将扩展视为网页扩展,如果:

  • 扩展程序清单package.json) 有浏览器入口点。
  • 扩展程序清单没有主要入口点且不符合以下贡献点:本地化调试器终端类型脚本服务器插件输入:.

如果一个扩展希望提供一个调试器或终端,该扩展在网页扩展主机中也能工作,那么一个浏览器入口点需要定义。

使用 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输入:.
    • 如果需要,请压缩代码--生产旗帜已通过。
    • 除非生成源映射--生产旗帜已通过。
    • 从包中排除'vscode'模块(因为它是由VS Code运行时提供的)。
    • 创建polyfills用于处理缓冲区
    • 使用esbuildProblemMatcherPlugin插件来报告阻止打包器完成的错误。此插件以一种被检测的格式发出错误。esbuild问题匹配器也需要安装为扩展。
    • 使用testBundlePlugin来实现一个测试主文件extensionTests.js) 引用了所有测试文件和 mocha 测试运行器mochaTestRunner.js
  • 如果--观察当传递该标志时,它开始监控源文件的变化,并在检测到变化时重新构建包。

esbuild可以直接处理TypeScript文件。然而,esbuild只是简单地去除所有的类型声明,而不进行任何类型检查。 只报告语法错误,这可能会导致esbuild失败。

出于这个原因,我们单独运行TypeScript编译器 (翻译结果:tsc) 检查类型,但不生成任何代码(标志--不输出)。

脚本部分在package.json现在看起来是那样

  "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",
    "测试": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. --extensionTestsPath=dist/web/test/suite/extensionTests.js",
    "在浏览器中运行": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. ."
  }

npm运行所有是一个节点模块,可以并行运行与给定前缀匹配的脚本。对我们来说,它运行的是watch-web:esbuildwatch-web:tsc脚本。你需要添加npm运行所有开发依赖项部分在package.json输入:.

以下任务.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": "编译网页",
      "问题匹配器": ["$tsc", "$esbuild"]
    }
  ]
}

这是mochaTestRunner.js在 esbuild 构建脚本中引用:

// 为浏览器导入 mocha,定义全局变量 `mocha`。
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);
    }
  });
}

样本