如何开发VS code插件

270 阅读4分钟

参考官方示例:github.com/microsoft/v…

1. 生成项目结构

1.1. 安装必要的工具

  • Yeoman(yo) 通用脚手架工具,用于生成项目结构,它支持生成多种项目目录结构
  • generator-codeyo配合,用于生成 VS Code 插件的模板
npm install -g yo generator-code

1.2. 生成代码

yo code

1.2.1. 以下是我的选择,供参考

1.2.2. 生成完成,按照提示在vs code下载对应插件

2. 目录结构

2.1. 介绍

flutter-project-forge/    VS Code 工作区的配置文件夹
├── .vscode/
    ├── extensions.json  刚安装的插件的配置文件(esbuild-problem-matchers)
    ├── launch.json      调试的配置文件
    ├── settings.json    覆盖vs code本地的settings.json
    ├── tasks.json       配置项目的任务,如打包任务等。
├── src/
    ├── test/             测试文件
        ├── extension.test.ts
    ├── extension.ts     主入口,用于定义激活函数和命令注册。
├── .gitignore
├── .npmrc
├── .vscode-test.mjs      测试
├── .vscodeignore         插件发布包排除的文件,比如test文件、调试配置、.git 文件夹等
├── CHANGELOG.md          记录插件的更新日志,显示每个版本的改动和新增功能
├── esbuild.js            Esbuild 的配置脚本
├── eslint.config.mjs
├── package.json          插件的数据和依赖管理
├── README.md
├── tsconfig.json
└── vsc-extension-quickstart.md     VS Code 插件开发的快速入门指南,包含基础开发流程的说明。

2.2. package.json

{
  "name": "flutter-project-forge", // 插件的唯一包名
  "displayName": "Flutter Project Forge",  // 插件显示在插件市场的名称
  "description": "Automate code generation in a Flutter project.",
  "version": "0.0.1",
  "engines": {
    "vscode": "^1.95.0"
  },
  "categories": [
    "Other"
  ],
  "activationEvents": [],  // 插件激活的触发事件(如命令启动)
  "main": "./dist/extension.js",
  "contributes": {    // 插件的扩展点(如注册命令)。
    "commands": [
      {
        "command": "flutter-project-forge.helloWorld", // 注册命令,与extension.ts中一致
        "title": "Hello World"
      }
    ],
    "menus": {       // 让命令出现在资源管理器右键菜单
      "explorer/context": [   // 表示“文件资源管理器(Explorer)”的右键菜单
        {
          "command": "flutter-project-forge.helloWorld",
          "group": "Forge@1"    // 代表优先级,@ 后面的数字越小,越靠前
        }
      ]
    }
  },
  "scripts": {
    "vscode:prepublish": "pnpm run package",
    "compile": "pnpm run check-types && pnpm run lint && node esbuild.js",
    "watch": "npm-run-all -p watch:*",
    "watch:esbuild": "node esbuild.js --watch",
    "watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
    "package": "pnpm run check-types && pnpm run lint && node esbuild.js --production",
    "compile-tests": "tsc -p . --outDir out",
    "watch-tests": "tsc -p . -w --outDir out",
    "pretest": "pnpm run compile-tests && pnpm run compile && pnpm run lint",
    "check-types": "tsc --noEmit",
    "lint": "eslint src",
    "test": "vscode-test"
  },
  "devDependencies": {
    "@types/vscode": "^1.95.0",
    "@types/mocha": "^10.0.9",
    "@types/node": "20.x",
    "@typescript-eslint/eslint-plugin": "^8.10.0",
    "@typescript-eslint/parser": "^8.7.0",
    "eslint": "^9.13.0",
    "esbuild": "^0.24.0",
    "npm-run-all": "^4.1.5",
    "typescript": "^5.6.3",
    "@vscode/test-cli": "^0.0.10",
    "@vscode/test-electron": "^2.4.1"
  }
}

3. 运行调试

3.1. 启动项目

  • 点击左侧的调试运行

  • 此时会打开一个新的vscode窗口

  • 可能会有报错,这是我本地的两个插件报错

  • 我们可以禁用其他本地的插件,在lunch.json中增加--disable-extensions
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Run Extension",
       // ...
      "args": [
        "--disable-extensions", // 增加这一行
        "--extensionDevelopmentPath=${workspaceFolder}",
      ],
      // ...
    }
  ]
}
  • 不会报错了

3.2. 使用插件

  • command + shift + p,输入hello world
  • 因为package.json中定义的是hello world

  • 正常弹出弹窗

3.3. 运行流程分析

  • 思考:我们在点击运行调试时发生了什么?

3.3.1. 执行launch.json中的配置

  • 根据.vscode/launch.json中的配置的name,执行对应的命令
  • launch中的preLaunchTask,表示运行前执行某个任务,任务配置在task.json

3.3.2. 执行任务

  • preLaunchTask的取值是task.json中的label字段, "${defaultBuildTask}"表示是默认task,也就是task中"isDefault": true的字段

  • 然后执行任务中的dependsOn

.vscode/tasks.json

"dependsOn": [
    "npm: watch:tsc",
    "npm: watch:esbuild"
],
  • 也就是package.json中的任务,

    • 编译ts,--noEmit:检查类型和语法,不生成 .js 文件。
    • node esbuild.js 使用esbuild进行打包
    • --watch 启用监听模式
 "scripts": {
    // ...
    "watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
    "watch:esbuild": "node esbuild.js --watch",
 }

3.3.3. 生成dist

  • 通过以上步骤,便会生成dist目录,完成打包
  • 最后打开vscode新窗口,成功启动调试模式

3.3.4. 在新窗口中执行命令

  • package.json 的 title
"contributes": {
    "commands": [
      {
        "command": "flutter-project-forge.helloWorld",
        "title": "Hello World"
      }
    ]
  },
  • 根据package.json的command,执行src/extension.ts中的vscode.commands.registerCommand的方法

  • 然后弹出弹窗, src/extension.ts
vscode.window.showInformationMessage('Hello World ');

3.3.5. 其他语法参考vscode官方示例

GitHub - microsoft/vscode-extension-samples: Sample code illustrating the VS Code extension API.

4. 打包上架

  • 下载vsce,用于打包和发布
npm install -g vsce

4.1. 打包

vsce package # 打包
  • 打包时可能遇到npm error问题,是由于依赖包导致

  • 由于我使用的pnpm,导致依赖问题,删除node_module,和lock文件,执行npm i (不使用pnpm)

4.2. 使用vsce进行发布

4.2.1. 修改package的publisher

  • icon不要使用svg
"publisher": "JewelYi",
"icon": "logo.png",

这里建议 name id都使用和package中的publisher保持一致,不要有空格,包括组织。否则后续可能token验证不过

  • 注册publisher

Azure DevOps Services | Sign In

4.2.2. 注册token

Azure DevOps Services | Microsoft Azure

  • 新建组织,名字与publisher保持一致

4.2.3. 创建token

name与publisher尽量一致

  • 复制创建好的token

4.2.4. 发布

  • 等待一会儿后就可以搜索到插件了
vsce publish
  • 自动更新版本号发布
vsce publish patch #更新最后一位
vsce publish minor #更新中间一位版本号
vsce publish major #更新首位版本号

  • 插件后台也能看到

5. 规范配置(可选)

规范代码提交,以及CHANGELOG自动生成

5.1. pre-commit

  • 安装husky
npm i husky -D
npx husky init

添加需要在pre-commit前执行的命令

.husky/pre-commit

npm run lint

5.2. commit规范检查

  • 安装代码检查工具
npm i @commitlint/cli @commitlint/config-conventional -D
  • 添加检查到对应的husky文件

  • 通过执行命令

npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
  • 或直接添加

  • .husky/commit-msg

npx --no-install commitlint --edit
  • 根目录下配置commit规范文件

  • commitlint.config.mjs

const types = ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'release', 'chore', 'revert', 'ci'];

export default {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-empty': [2, 'never'], // type cannot be empty
    'type-enum': [2, 'always', types], // type must be one of the predefined types
    'type-case': [2, 'always', 'lower-case'], // type must be in lowercase
    'scope-case': [0, 'never'], // disable case checking for scope
    'subject-empty': [2, 'never'], // subject cannot be empty
    'subject-case': [0, 'never'], // disable case checking for subject
    'header-max-length': [2, 'always', 100], // header length must not exceed 88 characters
  },
};

5.3. 自动生成changelog文件

  • 下载自动生成工具
npm i conventional-changelog-cli -D
  • 执行自动生成

  • -r 0表示从0开始生成(可以去掉)

npx conventional-changelog -p -i CHANGELOG.md -s -r 0