开发一个vscode扩展插件

这里有官方文档

初始化插件项目

vscode扩展插件本质上也是一个 前端项目!

安装脚手架,并初始化项目

npm i --g yo generator-code
yo code

这个时候,脚手架会提示你:“选择 ts 还是 js、用什么么构建工具等等”,按照如下选项即可(比如我这里创建一个 Run Tsx的扩展)

# ? What type of extension do you want to create? New Extension (TypeScript)
# ? What's the name of your extension?  Run Tsx
### Press <Enter> to choose default for all options below ###

# ? What's the identifier of your extension?  run-tsx
# ? What's the description of your extension? LEAVE BLANK
# ? Initialize a git repository? Y
# ? Which bundler to use? unbundled
# ? Which package manager to use? npm

# ? Do you want to open the new folder with Visual Studio Code? Open with `code`

立即启动和运行,按 F5 打开一个加载了扩展的新窗口。这个窗口就已经安装了你的插件!

核心文件介绍

src/extension.ts 这是扩展的核心文件!

// 'vscode' 模块包含 VS Code 扩展性 API
// 导入模块并在下面的代码中使用别名 vscode 引用它
import * as vscode from 'vscode';

// 当您的扩展被激活时调用此方法
// 您的扩展在第一次执行命令时被激活
export function activate(context: vscode.ExtensionContext) {

	// ---开发插件的时候,会输出在 “调试控制台”---
	// 使用控制台输出诊断信息 (console.log) 和错误 (console.error)
	// 这行代码只会在扩展激活时执行一次
	console.log('恭喜,您的扩展 "run-tsx" 现在已激活!');

	// ---注册运行文件命令---
	// 命令已在 package.json 文件中定义
	// 现在使用 registerCommand 提供命令的实现
	// commandId 参数必须与 package.json 中的 command 字段匹配
	const helloWorldDisposable = vscode.commands.registerCommand('run-tsx.helloWorld', () => {
		// 您在此处放置的代码将在每次执行命令时执行
		// 向用户显示消息框
		vscode.window.showInformationMessage('Hello World from Run Tsx!');
	});

	context.subscriptions.push(helloWorldDisposable);
}

// 当您的扩展被停用时调用此方法
export function deactivate() {}

以上,即是 便是为 vscode定义和注册了一个命令,命令 id 为 helloWorldDisposable
然后我们把这个命令绑定到右键菜单,这样在选择文件后,右键就可以调用这个命令执行。

在 package.json中定义

{
  "name": "run-tsx",
  // ...
  "contributes": {
    "commands": [
      {
        "command": "run-tsx.helloWorld",
        "title": "运行Run Tsx的helloWorld命令"
      }
    ],
    "menus": {
      "explorer/context": [ // 右键文件列表的时候
        {
          "command": "run-tsx.helloWorld", 
          "when": "resourceExtname == .ts || resourceExtname == .tsx",
          "group": "navigation@1"
        }
      ],
      "editor/context": [ // 右键文本内容的时候
        {
          "command": "run-tsx.helloWorld", 
          "when": "resourceExtname == .ts || resourceExtname == .tsx",
          "group": "navigation@1"
        }
      ]
    }
  },
}

学习定义命令

调用在终端完成执行

 // 注册运行命令
const runInTerminalDisposable = vscode.commands.registerCommand(
  "run-tsx.runInTerminal",
  (uri: vscode.Uri) => {
    if (uri && uri.fsPath) {
      const filePath = uri.fsPath;
      const fileName = vscode.workspace.asRelativePath(filePath);

      // 创建或获取终端
      const terminal = vscode.window.createTerminal("Run TSX");
      terminal.show();

      let command = "";
      // 根据文件扩展名选择运行命令
      const isTsOrTsxFile =
        filePath.endsWith(".tsx") || filePath.endsWith(".ts");
      if (isTsOrTsxFile) {
        command = `npx tsx "${filePath}"`;
      }

      terminal.sendText(command);
      vscode.window.showInformationMessage(`在终端中运行: ${fileName}`);
    } else {
      vscode.window.showErrorMessage("无法获取文件路径");
    }
  }
);

在输出通道来完成执行

// 运行 TypeScript 文件的辅助函数
function runTypeScriptFile(filePath: string) {
  // 创建输出通道来显示运行结果
  const outputChannel = vscode.window.createOutputChannel("Run TSX");
  outputChannel.show();
  outputChannel.appendLine(`正在运行文件: ${filePath}`);

  // 创建终端来执行命令
  const terminal = vscode.window.createTerminal("Run TSX");
 //   terminal.show(); // 显示执行的终端窗口

  // 根据文件扩展名选择合适的运行器
  const ext = path.extname(filePath);
  let command: string;
  if (ext === ".tsx" || ext === ".ts") {
    // 对于 TSX 文件,使用 tsx 运行器
    command = `npx tsx "${filePath}"`;
  } else {
    vscode.window.showErrorMessage("不支持的文件类型");
    return;
  }
  terminal.sendText(command);
  outputChannel.appendLine(`执行命令: ${command}`);
}

const runFileDisposable = vscode.commands.registerCommand("run-tsx.runFile",(uri: vscode.Uri) => {
	if (uri && uri.fsPath) {
	// 获取文件路径
	const filePath = uri.fsPath;
	const fileName = vscode.workspace.asRelativePath(filePath);

	// 显示信息消息
	vscode.window.showInformationMessage(`正在运行文件: ${fileName}`);

	// 这里可以添加实际的运行逻辑
	// 例如使用 ts-node 或其他工具运行 TypeScript 文件
	runTypeScriptFile(filePath);
	} else {
	vscode.window.showErrorMessage("无法获取文件路径");
	}
});

优化版本:以上代码在执行的没有任何输出,也不知道任务是否结束,对于用户来说,跟卡了一样 无响应,这显然不合理。我们可以 child_process + 输出通道 的方式来解决,

function runTypeScriptFile(filePath: string) {
  // 创建输出通道来显示运行结果
  const outputChannel = vscode.window.createOutputChannel("Run TSX");
  outputChannel.show();
  outputChannel.clear(); // 清空之前的输出

  outputChannel.appendLine(`正在运行文件: ${filePath}`);
  outputChannel.appendLine(`执行时间: ${new Date().toLocaleString()}`);
  outputChannel.appendLine('=' .repeat(60));
  outputChannel.appendLine('正在启动执行...');

  // 根据文件扩展名选择合适的运行器
  const ext = path.extname(filePath);
  let command: string;

  if (ext === ".tsx" || ext === ".ts") {
    command = `npx tsx "${filePath}"`;
  } else {
    vscode.window.showErrorMessage("不支持的文件类型");
    return;
  }

  outputChannel.appendLine(`执行命令: ${command}`);
  outputChannel.appendLine('');

  // 使用 child_process 执行命令并实时显示输出
  const process = spawn('npx', ['tsx', filePath], {
    cwd: path.dirname(filePath),
    shell: true
  });

  // 监听标准输出
  process.stdout.on('data', (data: Buffer) => {
    const output = data.toString();
    outputChannel.append(output);
  });

  // 监听错误输出
  process.stderr.on('data', (data: Buffer) => {
    const error = data.toString();
    outputChannel.append(`[错误] ${error}`);
  });

  // 监听进程结束
  process.on('close', (code: number) => {
    outputChannel.appendLine('');
    outputChannel.appendLine('=' .repeat(60));
    if (code === 0) {
      outputChannel.appendLine(`✅ 执行完成! 退出代码: ${code}`);
      vscode.window.showInformationMessage('文件执行成功!');
    } else {
      outputChannel.appendLine(`❌ 执行失败! 退出代码: ${code}`);
      vscode.window.showErrorMessage(`文件执行失败,退出代码: ${code}`);
    }
    outputChannel.appendLine(`结束时间: ${new Date().toLocaleString()}`);
  });

  // 监听进程错误
  process.on('error', (error: Error) => {
    outputChannel.appendLine('');
    outputChannel.appendLine('=' .repeat(60));
    outputChannel.appendLine(`❌ 进程错误: ${error.message}`);
    vscode.window.showErrorMessage(`执行错误: ${error.message}`);
  });
}

但代码会多很多,还有一种方案是利用VS Code 任务系统,这种比较常见

function runTypeScriptFile(filePath: string) {
  const fileName = path.basename(filePath);

  // 使用最简单可靠的方案:npx tsx
  // 如果用户没有安装 tsx,npx 会自动下载
  const execution = new vscode.ShellExecution(
    `npx tsx "${filePath}"`,
    {
      cwd: path.dirname(filePath),
    }
  );

  // 创建任务定义 - 描述任务的基本信息
  const taskDefinition: vscode.TaskDefinition = {
    type: "shell",                   // 任务类型: "shell"=在shell中执行命令
    label: `运行 ${fileName}`,        // 任务标签: 在任务面板中显示的名称
  };

  // 创建任务
  const task = new vscode.Task(
    taskDefinition,              // 第1个参数: 任务定义对象,包含 type 和 label
    vscode.TaskScope.Workspace,  // 第2个参数: 任务作用域 (Workspace=工作区级别, Global=全局级别, 或特定的WorkspaceFolder)
    `运行 ${fileName}`,          // 第3个参数: 任务名称,显示在任务列表中
    "run-tsx",                   // 第4个参数: 任务来源/提供者,通常是扩展名称
    execution,                   // 第5个参数: 任务执行对象,定义如何执行任务 (ShellExecution 或 ProcessExecution)
    "$tsc"                       // 第6个参数: 问题匹配器,用于解析输出中的错误/警告 ($tsc=TypeScript编译器的错误格式)
  );

  // 执行任务
  vscode.tasks.executeTask(task);
}

我该选哪种方式?

以上定义命令有两种方式,那我到底选择哪一种呢?
第一种简单,但是一般都会选择第二种!😭

两个命令都是在终端中运行的! 真正的区别是:

第一种是:
✅ 终端运行 + ✅ 输出通道记录
既在终端执行命令,又在输出通道显示日志信息
用户可以在两个地方看到信息:
终端:看到实际的命令执行和结果
输出通道:看到扩展的日志记录

第二种是:
✅ 仅终端运行
只在终端执行命令
用户只能在终端看到执行结果

构建

构建使用官方工具 vscenpm i --D @vscode/vsce ,并且在 package.json增加如下

"vsce": {
  "baseImagesUrl": "https://my.custom/base/images/url",
  "dependencies": true,
  "yarn": false
},
"icon": "images/icon.png"

最后执行构建命令即可

npx @vscode/vsce package

VS Code 扩展的打包流程是:

  1. 编译阶段: TypeScript 编译到 out/ 目录
  2. 打包阶段: vsce package 基于 out/ 目录和 package.json 创建 .vsix 文件

发布

要想发布到市场,必须先登录(没有账号的 这里创建),npx @vscode/vsce login 你的用户id,根据提示输入你的 Personal Access Token 即可登录!
如果你没有令牌,需要创建,去 Azure DevOps 创建账号再创建令牌(好奇为啥没有在统一平台,而是分两个? 还是 npm 登录发布 好用)

最后在 package.json 里增加 publisher字段 后,执行 npx @vscode/vsce publish 即可!

发布完成既可以 在 你的 包管理页面 看到
image

其它

vsc-extension-quickstart

vsc-extension-quickstart.md的中文翻译

# 欢迎使用您的 VS Code 扩展

## 文件夹内容

* 此文件夹包含扩展所需的所有文件。
* `package.json` - 这是清单文件,您在其中声明扩展和命令。
  * 示例插件注册一个命令并定义其标题和命令名称。有了这些信息,VS Code 可以在命令面板中显示该命令。它还不需要加载插件。
* `src/extension.ts` - 这是主文件,您将在其中提供命令的实现。
  * 该文件导出一个函数 `activate`,它在扩展第一次激活时被调用(在这种情况下是通过执行命令)。在 `activate` 函数内部,我们调用 `registerCommand`。
  * 我们将包含命令实现的函数作为第二个参数传递给 `registerCommand`。

## 立即启动和运行

* 按 `F5` 打开一个加载了扩展的新窗口。
* 通过按下(`Ctrl+Shift+P` 或 Mac 上的 `Cmd+Shift+P`)并输入 `Hello World` 从命令面板运行您的命令。
* 在 `src/extension.ts` 中的代码中设置断点来调试您的扩展。
* 在调试控制台中查找扩展的输出。

## 进行更改

* 在 `src/extension.ts` 中更改代码后,您可以从调试工具栏重新启动扩展。
* 您也可以重新加载(`Ctrl+R` 或 Mac 上的 `Cmd+R`)带有扩展的 VS Code 窗口来加载您的更改。

## 探索 API

* 当您打开文件 `node_modules/@types/vscode/index.d.ts` 时,可以查看我们完整的 API 集合。

## 运行测试

* 安装 [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner)
* 通过 **Tasks: Run Task** 命令运行 "watch" 任务。确保此任务正在运行,否则可能无法发现测试。
* 从活动栏打开测试视图并点击 "Run Test" 按钮,或使用快捷键 `Ctrl/Cmd + ; A`
* 在测试结果视图中查看测试结果的输出。
* 对 `src/test/extension.test.ts` 进行更改或在 `test` 文件夹内创建新的测试文件。
  * 提供的测试运行器只会考虑匹配名称模式 `**.test.ts` 的文件。
  * 您可以在 `test` 文件夹内创建文件夹,以任何您想要的方式组织您的测试。

## 进一步发展

* [遵循 UX 指南](https://code.visualstudio.com/api/ux-guidelines/overview) 创建与 VS Code 原生界面和模式无缝集成的扩展。
* 通过 [打包您的扩展](https://code.visualstudio.com/api/working-with-extensions/bundling-extension) 来减少扩展大小并改善启动时间。
* 在 VS Code 扩展市场上 [发布您的扩展](https://code.visualstudio.com/api/working-with-extensions/publishing-extension)。
* 通过设置 [持续集成](https://code.visualstudio.com/api/working-with-extensions/continuous-integration) 来自动化构建。
* 集成到 [问题报告](https://code.visualstudio.com/api/get-started/wrapping-up#issue-reporting) 流程中,以获取用户报告的问题和功能请求。

构建提示

It seems the README.md still contains template text. Make sure to edit the README.md file before you package or publish your extension.
意识是 README.md 你要自己写,不能将模板内容打进去!修改如下即可

# Run TSX - VS Code Extension

🚀 一键运行 TypeScript 和 TSX 文件的 VS Code 扩展

## 功能特性

- **右键运行**: 在文件资源管理器或编辑器中右键点击 `.ts` 或 `.tsx` 文件,选择"运行 TS/TSX 文件"即可执行
- **任务集成**: 使用 VS Code 内置任务系统,提供完整的执行反馈和错误处理
- **智能检测**: 自动识别 TypeScript 和 TSX 文件类型
- **内置依赖**: 扩展内置 `tsx` 运行器,无需用户项目安装额外依赖
- **实时输出**: 在任务终端中查看实时执行结果和错误信息

## 使用方法

### 方式一:右键菜单
1. 在文件资源管理器中找到您的 `.ts` 或 `.tsx` 文件
2. 右键点击文件
3. 选择 "运行 TS/TSX 文件"

### 方式二:编辑器右键
1. 打开 `.ts` 或 `.tsx` 文件
2. 在编辑器中右键
3. 选择 "运行 TS/TSX 文件"

WARNING LICENSE, LICENSE.md, or LICENSE.txt not found
意思是需要一个许可声明文件,在根目录创建一个 LICENSE即可!

MIT License

Copyright (c) [year] [your-name]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

扩展依赖三方

比如这里 我直接把 tsx 打进扩展内部,这样用户在使用这个扩展的时候,就不用在ta 自己安装 tsx 这个依赖了

function runTypeScriptFile(filePath: string, context: vscode.ExtensionContext) {
  const fileName = path.basename(filePath);
  // 使用扩展上下文获取正确的扩展路径
  const extensionPath = context.extensionPath;

  console.log("扩展路径:", extensionPath);
  console.log("当前工作目录:", process.cwd());
  console.log("__dirname:", __dirname);

  // 在打包后的扩展中,node_modules 与 extension.js 在同一级别
  // 所以从 extensionPath 直接访问 node_modules
  let tsxPath = path.join(
    extensionPath,
    "node_modules",
    "tsx",
    "dist",
    "cli.mjs"
  );
  const command = `node "${tsxPath}" "${filePath}"`;
  const execution = new vscode.ShellExecution(command, {
    cwd: path.dirname(filePath),
  });

  // 创建任务定义 - 描述任务的基本信息
  const taskDefinition: vscode.TaskDefinition = {
    type: "shell", // 任务类型: "shell"=在shell中执行命令
    label: `运行 ${fileName}`, // 任务标签: 在任务面板中显示的名称
  };

  // 创建任务
  const task = new vscode.Task(
    taskDefinition,
    vscode.TaskScope.Workspace,
    `运行 ${fileName}`,
    "run-tsx",
    execution,
    "$tsc"
  );

  // 执行任务
  vscode.tasks.executeTask(task);
}
posted @ 2025-08-23 14:48  丁少华  阅读(105)  评论(0)    收藏  举报