捆绑扩展

捆绑 Visual Studio Code 扩展的第一个原因是确保它适用于在任何平台上使用 VS Code 的每个人。只有捆绑的扩展才能在 VS Code for Web 环境(例如github.devvscode.dev )中使用。当 VS Code 在浏览器中运行时,它只能为您的扩展加载一个文件,因此需要将扩展​​代码捆绑到一个 Web 友好的 JavaScript 文件中。这也适用于笔记本输出渲染器,其中 VS Code 也只会为渲染器扩展加载一个文件。

此外,扩展的大小和复杂性会快速增长。它们可以在多个源文件中编写,并依赖于npm中的模块。分解和重用是开发最佳实践,但在安装和运行扩展时它们会付出代价。加载 100 个小文件比加载 1 个大文件慢得多。这就是我们建议捆绑的原因。捆绑是将多个小源文件组合成一个文件的过程。

对于 JavaScript,可以使用不同的捆绑器。流行的是rollup.jsParcelesbuildwebpack

使用 esbuild

esbuild是一个配置简单的快速捆绑器。要获取 esbuild,请打开终端并输入:

npm i --save-dev esbuild

有关使用 esbuild 的完整扩展的示例,请查看test-adapter-converter

运行 esbuild

您可以从命令行运行 esbuild,但为了减少重复,使用 npm 脚本会很有帮助。

将这些条目合并到scripts以下部分package.json

"scripts": {
    "vscode:prepublish": "npm run esbuild-base -- --minify",
    "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node",
    "esbuild": "npm run esbuild-base -- --sourcemap",
    "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch",
    "test-compile": "tsc -p ./"
},

esbuild脚本esbuild-watch用于开发,它们生成捆绑文件。vscode:prepublish由 VS Code 打包和发布工具使用,vsce并在发布扩展之前运行。传递--minify标志和 no--sourcemap会压缩代码并创建一个小包,但也会使调试变得困难,因此在开发过程中使用其他标志。要运行上述脚本,请打开终端并键入npm run esbuild或选择任务:从命令面板运行任务 ( ⇧⌘P (Windows、Linux Ctrl+Shift+P ) )。

最后,您需要更新.vscodeignore文件,以便编译的文件包含在已发布的扩展中。查看发布部分了解更多详细信息。

跳至测试部分继续阅读。

使用网页包

Webpack 是一个可从npm获取的开发工具。要获取 webpack 及其命令行界面,请打开终端并输入:

npm i --save-dev webpack webpack-cli

这将安装 webpack 并更新您的扩展package.json文件以将 webpack 包含在devDependencies.

Webpack 是一个 JavaScript 捆绑器,但许多 VS Code 扩展都是用 TypeScript 编写的,并且仅编译为 JavaScript。如果您的扩展使用 TypeScript,您可以使用 loader ts-loader,以便 webpack 可以理解 TypeScript。使用以下命令进行安装ts-loader

npm i --save-dev ts-loader

配置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.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;

该文件作为webpack-extension示例的一部分提供。Webpack 配置文件是必须导出配置对象的普通 JavaScript 模块。

在上面的示例中,定义了以下内容:

  • 指示target您的扩展将运行的上下文。我们建议使用webworker,以便您的扩展可以在 VS Code Web 版和 VS Code 桌面版中运行。
  • webpack 应使用的入口点。main这与中的属性类似,package.json只是您为 webpack 提供了“源”入口点(通常为 )src/extension.ts,而不是“输出”入口点。webpack 捆绑程序可以理解 TypeScript,因此单独的 TypeScript 编译步骤是多余的。
  • output配置告诉 webpack 将生成的包文件放置在哪里。按照惯例,这就是dist文件夹。在此示例中,webpack 将生成一个dist/extension.js文件。
  • 和配置用于支持 TypeScript 和 JavaScript 输入文件resolvemodule/rules
  • 配置externals用于声明排除项,例如不应包含在捆绑包中的文件和模块。该vscode模块不应捆绑,因为它不存在于磁盘上,而是在需要时由 VS Code 即时创建。根据扩展使用的节点模块,可能需要更多排除。

最后,您需要更新.vscodeignore文件,以便编译的文件包含在已发布的扩展中。查看发布部分了解更多详细信息。

运行 webpack

创建文件后webpack.config.js,就可以调用 webpack 了。您可以从命令行运行 webpack,但为了减少重复,使用 npm 脚本会很有帮助。

将这些条目合并到scripts以下部分package.json

"scripts": {
    "vscode:prepublish": "npm run package",
    "webpack": "webpack --mode development",
    "webpack-dev": "webpack --mode development --watch",
    "package": "webpack --mode production --devtool hidden-source-map",
    "test-compile": "tsc -p ./"
},

webpack脚本webpack-dev用于开发,它们生成捆绑文件。vscode:prepublish由 VS Code 打包和发布工具使用,vsce并在发布扩展之前运行。区别在于模式和控制优化级别的不同。使用production会产生最小的包,但也需要更长的时间,因此development使用 else。要运行上述脚本,请打开终端并键入npm run webpack或选择任务:从命令面板运行任务 ( ⇧⌘P (Windows、Linux Ctrl+Shift+P ) )。

运行扩展

在运行扩展之前,main中的属性package.json必须指向捆绑包,对于上面的配置,该属性是"./dist/extension"。通过这一更改,现在可以执行和测试扩展。

测试

扩展作者经常为其扩展源代码编写单元测试。通过正确的架构分层,扩展源代码不依赖于测试,webpack 生成的包不应包含任何测试代码。要运行单元测试,只需要简单的编译。在示例中,有一个test-compile脚本,它使用 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: test-compile"
}

用于运行测试的配置与非 webpacked 扩展相同。没有理由进行 webpack 单元测试,因为它们不是扩展的已发布部分的一部分。

出版

在发布之前,您应该更新该.vscodeignore文件。现在捆绑到文件中的所有内容都dist/extension.js可以排除,通常是out文件夹(如果您尚未删除它),最重要的是文件node_modules夹。

典型的.vscodeignore文件如下所示:

.vscode
node_modules
out/
src/
tsconfig.json
webpack.config.js

迁移现有扩展

迁移现有扩展以​​使用 webpack 很简单,类似于上面的入门指南。采用 webpack 的真实示例是通过此拉取请求的VS Code 的引用视图。

在那里你可以看到:

  • 添加webpackwebpack-clits-loader作为devDependencies
  • 更新npm脚本,以便使用webpack进行开发。
  • 更新调试器配置launch.json文件。
  • 添加并调整webpack.config.js配置文件。
  • 更新.vscodeignore以排除node_modules和中间输出文件。
  • 享受安装和加载速度更快的扩展!

故障排除

缩小化

模式下的捆绑production还会执行代码缩减。缩小通过删除空格和注释以及将变量和函数名称更改为难看但简短的名称来压缩源代码。使用的源代码Function.prototype.name工作方式不同,因此您可能必须禁用缩小。

webpack 关键依赖项

运行 webpack 时,您可能会遇到类似Critical dependency: the request of a dependency is an expression 的警告。必须认真对待此类警告,否则您的捆绑包可能无法工作。该消息意味着 webpack 无法静态确定如何捆绑某些依赖项。这通常是由动态require语句引起的,例如require(someDynamicVariable).

要解决该警告,您应该:

  • 尝试使依赖项静态化,以便可以将其捆绑。
  • 通过配置排除该依赖项externals。还要确保这些 JavaScript 文件不会从打包的扩展中排除,例如在 中使用否定的glob 模式.vscodeignore!node_modules/mySpecialModule

下一步

  • 扩展市场- 了解有关 VS Code 公共扩展市场的更多信息。
  • 测试扩展- 将测试添加到您的扩展项目中以确保高质量。
  • 持续集成- 了解如何在 Azure Pipelines 上运行扩展 CI 构建。