打包插件

VS Code 插件体积常常随着更新越来越大,它会产生很多文件,而且还依赖各种npm包。 程序开发的最佳实践是抽象和重用,但过度拆分和庞大的代码结构产生的代价就是更大的插件体积和更慢的运行效率。加载 100 个小文件远比加载一个大文件来的慢,这就是我们更推荐打包插件的原因。 打包是将多个小的源文件打包成单个入口文件的过程。

对于 Javascript 而言,可选的构建工具非常多,比较流行的如rollup.jsparcelwebpack。大部分构建工具的概念和功能都是相似的,本节主要使用webpack打包。

使用 webpack


webpack 这个开发工具可以在npm里找到,为了获取 webpack 和它的命令行界面,打开终端然后输入:

  1. npm i --save-dev webpack webpack-cli

这行命令会先安装 webpack,然后更新你插件里的package.json中的devDependencies字段。Webpack 是一个 Javascrip 打包工具,但是大部分 VS Code 插件是用 Typescript 写的,所以你需要在 webpack 中配置ts-loader,它才能正确编译 Typescript。安装ts-loader

  1. npm i --save-dev ts-loader

配置 webpack


既然所有的工具都安装好了,我们现在可以开始配置 webpack 了。通常来说,你的项目目录中需要创建一个webpack.config.js文件,webpack 才能知道按什么规则打包你的插件。下面的配置示例是 VS Code 插件专用的,让我们来开这个头吧:

  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. //@ts-check
  6. "use strict";
  7. const path = require("path");
  8. /**@type {import('webpack').Configuration}*/
  9. const config = {
  10. target: "node", // vscode插件运行在Node.js环境中 📖 -> https://webpack.js.org/configuration/node/
  11. entry: "./src/extension.ts", // 插件的入口文件 📖 -> https://webpack.js.org/configuration/entry-context/
  12. output: {
  13. // 打包好的文件储存在'dist'文件夹中 (请参考package.json), 📖 -> https://webpack.js.org/configuration/output/
  14. path: path.resolve(__dirname, "dist"),
  15. filename: "extension.js",
  16. libraryTarget: "commonjs2",
  17. devtoolModuleFilenameTemplate: "../[resource-path]"
  18. },
  19. devtool: "source-map",
  20. externals: {
  21. vscode: "commonjs vscode" // vscode-module是热更新的临时目录,所以要排除掉。 在这里添加其他不应该被webpack打包的文件, 📖 -> https://webpack.js.org/configuration/externals/
  22. },
  23. resolve: {
  24. // 支持读取TypeScript和JavaScript文件, 📖 -> https://github.com/TypeStrong/ts-loader
  25. extensions: [".ts", ".js"]
  26. },
  27. module: {
  28. rules: [
  29. {
  30. test: /\.ts$/,
  31. exclude: /node_modules/,
  32. use: [
  33. {
  34. loader: "ts-loader"
  35. }
  36. ]
  37. }
  38. ]
  39. }
  40. };
  41. module.exports = config;

这份文件是webpack-extension中的一部分。webpack 配置最后输出的就是 JS 对象。

在上面的例子里,我们定义了如下内容:

  • traget:’node’,因为我们的插件运行在 Node.js 环境中。
  • entry:webpack 使用的入口文件。这就像是package.json中的main属性,有点不一样的是你还需要给 webpack 提供”source”—— 一般就是src/extension.ts,小心不要配置在”output”里了。webpack 可以解析 Typescript,所以我们不需要再单独执行 Typescript 编译了。
  • output配置告诉 webpack 应该把打包好的文件放在哪里,一般而言我们会放在dist文件夹里。在这个例子里,webpack 最后会产生一个dist/extension.js文件。
  • resolvemodule/rules中配置 Typescript 和 Javascript 的解析器。
  • externals即排除配置,在这里可以配置打包文件不应包含的文件和模块。vscode不需要被打包是因为它并不储存在磁盘上,它是 VS Code 热更新生成的临时文件夹。根据插件依赖的具体 node 模块,你可能需要通过这个配置优化打包文件。

运行 webpack


webpack.config.js文件创建好之后,webpack 就可以正式开始工作了。你可以从命令行中运行 webpack,不过为了避免重复工作用 npm script 会更有效率。

将下列脚本复制到package.jsonscripts中去:

  1. "scripts": {
  2. "vscode:prepublish": "webpack --mode production",
  3. "compile": "webpack --mode none",
  4. "watch": "webpack --mode none --watch",
  5. },

compilewatch脚本是开发时使用的,它们会产生构建文件。vscode:prepublishvsce使用的,vsce是 VS Code 的打包和发布工具,你需要在发布插件之前运行这个命令。webpack 中的mode是控制优化级别的配置项,如果你使用production字段,那么就会打包出最小的构建文件,但是也会耗费更多时间,所以我们开发中使用none。想要运行上述脚本,我们可以打开终端(命令行)输入npm run compile或者从命令面板Ctrl+Shift+P)中使用运行任务来开始。

运行插件


运行插件之前,你需要将package.json中的main属性指向到构建文件上,也就是我们上面提到的"./dist/extension",改好之后我们就可以运行和测试插件了。关于调试配置,请注意更新launch.json中的outFiles属性。

测试


插件开发者一般都会给插件源代码进行单元测试,但是有了完备的底层架构支持,插件的源代码可以不依赖测试,webpack 产生的构建文件中也不应该包含任何测试代码。如果需要运行单元测试,只需要简单地编译就好了。在上面的例子里,我们有一个test-compile脚本,它会把调用 Typescript 编译器将插件编译至out目录中。这样一来我们就有了 JS 文件,再使用下面的launch.json就足够应付测试了。

  1. {
  2. "name": "Extension Tests",
  3. "type": "extensionHost",
  4. "request": "launch",
  5. "runtimeExecutable": "${execPath}",
  6. "args": [
  7. "--extensionDevelopmentPath=${workspaceFolder}",
  8. "--extensionTestsPath=${workspaceFolder}/out/test"
  9. ],
  10. "outFiles": ["${workspaceFolder}/out/test/**/*.js"],
  11. "preLaunchTask": "npm: test-compile"
  12. }

这个测试配置对于非 webpack 打包的插件来说也是可以使用的。我们没必要将单元测试打包起来,因为它们不应包含在我们发布的插件里。

发布


发布前你需要更新.vscodeignore文件。现在所有东西都打包到了dist/extension.js文件中,所以应该排除这个文件还有out文件夹(怕你漏了,特此提醒),以及最重要的node_modules文件夹。

一般来说,.vsignore文件应该是这样的:

  1. .vscode
  2. node_modules
  3. out/
  4. src/
  5. tsconfig.json
  6. webpack.config.js

迁移插件


用 webpack 迁移现有的插件是很容易的,整个过程就像我们上面的指南一样。真实的例子如 VS Code 的 References 视图就是从这个pull request应用了 webpack 而来的。

在里面,你可以看到:

  • devDependencies中添加webpackwebpack-clits-loader
  • 更新 npm 脚本以便开发时使用 webpack
  • 更新调试配置文件launch.json
  • 添加和修改webpack.config.js
  • 更新.vscodeignore排除node_modules和其他开发时产生的临时文件
  • 开始享受体积更小、安装更快的插件!

疑难解答


压缩

使用production模式会执行代码压缩,它会去除源代码中的空格和注释,并把变量名和函数名进行替换——混淆和压缩。不过形如Function.prototype.name的代码不会压缩。

webpack critical dependencies

当你运行 webpack 时,你可能会碰到像Critical dependencies: the request of a dependency is an expression字样的警告。这些警告必须立即处理,一般来说会影响到打包过程。这句话意味着 webpack 不能静态分析某些依赖,一般是由动态使用require导致的,比如require(someDynamicVariable)

想要处理这类警告,你需要:

  • 将需要打包的部分变成静态的依赖。
  • 通过externals排除这部分依赖,但是注意它们的 Javascript 文件还是应该保留在我们打包的插件里,在.vscodeignore中使用 glob 模式,比如!node_modules/mySpecialModule

下一步

  • 插件市场 - 学习更多 VS Code 插件市场的有关内容。
  • 测试插件 - 测试插件,提高项目质量。
  • 持续集成 - 使用 Azure Pipeline 运行插件的 CI 构建。