【跟我学】推荐一波【写一个】自己写的插件-Auto Inspecterrors

前情提要: 痛点是我想要给一个库提点功能,或者修bug.但是不确定当前pull的代码本身有没有bug

一个常见的方式就是点开需要或者说可能修改的部分,然后vscode会有Typescript的提示,配合error lens这个插件然后设置成中文就有丝滑的体验。

  • 就像下面一样
    • 搜索 Auto Inspecterrors 然后ctrl+shift+p 输入 ASC 即可检查有错误的文件

随便写一个明显有错的代码

问题

这样的手动打开的方式会有问题,因为工程大了之后不可能手动打开所有文件,大家都知道ts的报错提示通常情况下,没有配置eslint+prettier之类的插件下,只有当你打开文件ts才会在编辑器左下角提示有多少个错误

优化

有没有一种方式可以在我打开一个项目或者pull的时候,自动帮我检查别人的代码有没有问题呢?然后可以尝试通过配置后的npm script脚本去修复一些错误?

  • 我想过自动化的修复方式在vscode里面调用一些cmd或者shell的命令
  • 于是,我和想法一拍即合

let's rocking out

首先介绍一下必要的工具

  • yo
  • vscode-genrate的插件

总之这些可以搜索引擎找到,我们不再赘述,具体可能记错cli名字了

然后通过vsc的cli可以创建一个空项目,结构如下

需要动的就2个地方一个是output出口,和input入口。

熟悉nodejs的同学都知道i/o是什么,然后我们为了方便定位用,打包的时候也开启sourcemap,反正也不大【小声嘀咕】

还有有2个vsix的文件这个是我们用特定工具生成的插件,是为了本地调试用的,算是未发布的半成品

重点就是extension.ts了

开拔

激活的主函数

ts 复制代码
export async function activate(context: vscode.ExtensionContext) {
    try {
        vscode.window.showInformationMessage('Auto-inspecting errors is about to start...');
        // 在插件激活时检查任务状态
        eslintFixTask = await getEslintFixTask();

        if (eslintFixTask) {
            // 任务存在,重新创建终端
            eslintFixTerminal = vscode.window.createTerminal({ name: 'eslint-fix', shellPath: 'npm', shellArgs: ['run', 'lint-fix'] });
            eslintFixTerminal.show();
        }

        const result = await runPluginLogic();

        if (!result) {
            // 降级处理或者其他处理方式
            console.warn('插件逻辑执行失败,插件将以降级模式继续运行。');
        }
    } catch (error) {
        vscode.window.showErrorMessage('插件遇到了一个错误,请查看控制台获取更多信息。');
        // 记录错误到插件日志
        console.error('An error occurred in the extension:', error);
    }
}

在这个里面的纯函数我们只做激活和一些校验,主要就是检查eslint的fix任务有没有开启在终端,或者已经开启了,减少开销。然后等待运行插件的主逻辑 await runPluginLogic()

主逻辑

ts 复制代码
async function runPluginLogic(): Promise<boolean> {
    vscode.commands.registerCommand('auto-inspect-errors.AutoInspectErrors', async () => {
            vscode.window.showInformationMessage('Auto-inspecting errors is about to registing...');
            console.log('AutoInspectPlugin :>> ', 'the plugin is started');
            // 获取当前工作区
            const workspaceFolders  = vscode.workspace.workspaceFolders;

            if (!workspaceFolders) {
                vscode.window.showErrorMessage('No workspace folder opened.');
                return;
            }
            const workspaceFolder = workspaceFolders[0].uri.fsPath;

            // 获取用户配置
            const config = vscode.workspace.getConfiguration('autoInspectErrors');
            const excludedPatterns: string[] = config.get('excludedPatterns', []);
            const customErrorRules: string[] = config.get('customErrorRules', []);

            // 构建.gitignore文件路径
            const gitIgnorePath = path.join(workspaceFolder, '.gitignore');

            try {
                const gitIgnoreContent = await fs.promises.readFile(gitIgnorePath, 'utf-8');

                const ignoreRules = gitIgnoreContent
                    .split('\n')
                    .map(line => line.trim())
                    .filter(line => line.length > 0 && !line.startsWith('#'))
                    .map(line => new vscode.RelativePattern(workspaceFolder, line));

                // 添加用户配置的排除规则
                excludedPatterns.forEach(pattern => {
                    ignoreRules.push(new vscode.RelativePattern(workspaceFolder, pattern));
                });

                // 添加用户配置的自定义错误规则
                const customErrorRegex = new RegExp(customErrorRules.join('|'));


                // 添加新的排除规则,排除 node_modules 目录
                ignoreRules.push(new vscode.RelativePattern(workspaceFolder, 'node_modules/**'));

                // 使用异步 vscode.workspace.findFiles 替代同步 vscode.workspace.findFiles
                const nonIgnoredFiles = await vscode.workspace.findFiles('**/*.{ts,tsx,vue,js,json,css}', '**/node_modules/**', 1000);

                const matchingFiles = nonIgnoredFiles.filter(file => {
                    const filePath = path.relative(workspaceFolder, file.fsPath);

                    return ignoreRules.every(rule => {
                        const relativePath = path.relative(workspaceFolder, rule.base);
                        const rulePath = path.join(relativePath, rule.pattern);

                        return !filePath.startsWith(rulePath);
                    });
                });

                // 使用 Promise.all 并行处理异步操作
                const openedFiles = await Promise.all(matchingFiles.map(async file => {
                    const res  = await checkFixAndCloseFile(file,customErrorRegex);
                    if (res) return res
                    return undefined; // 返回 undefined 表示没有打开的文件
                }));

                // 过滤掉 undefined,以获得实际打开的文件数量
                const actualOpenedFiles = openedFiles.filter(file => file !== undefined);

                // 获取当前组件的行数
                vscode.window.onDidChangeActiveTextEditor(async (editor) => {
                    if (editor) {
                        const res = await getCurrentFileLineContent()
                        if (res > 500) {
                            await splitFiles(editor.document)
                        }
                    }
                })

                //
                vscode.workspace.onDidChangeTextDocument(async (event) => {
                    if (event) {
                        const res = await getCurrentFileLineContent()
                        if (res > 500) {
                            await splitFiles(event.document)
                        }
                    }
                })

                vscode.window.showInformationMessage(`Found ${actualOpenedFiles.length} problematic TypeScript file(s).`);
            } catch (error: any) {
                vscode.window.showErrorMessage(`Error reading .gitignore: ${error.message}`);
            }
    });
    return true; // 表示逻辑执行成功
}

首先需要执行注册一个命令,因为插件太多,我们没法等待所有插件完成之后再自动加载我们的插件,所以只能手动触发插件,vscode.commands.registerCommand

  • AutoInspectErrors 名字就是这个了。
  1. 获取工作区、目录的当前的激活的第一个编辑器
  2. 获取用户配置
    • 排除检查错误的规则,比如有console的不算错误,或者es5的indexOf不算错误
    • 包含的错误规则, 比如有console和es5的indexOf
    • 排除git忽略的文件
      • 获取git文件
      • 添加用户忽略的排除规则
      • 添加用户配置的自定义错误规则
      • 排除 node_modules 目录
      • 异步的找包含的扩展名文件中的所有文件
  3. 拿到所有已经实际打开的有错误的文件
  4. 执行修复并且关闭已修复的文件
    • checkFixAndCloseFile
      • 检查自定义错误规则
      • 获取 vscode 自身的报错
      • 执行 ESLint 自动修复
        1. 自己ctrl+c取消
        2. 没有配置对应的eslint规则在依赖中
        3. eslint运行中手动删除,也提示用户重新运行命令
        4. 要配置npm script
  5. 组件超过500行提示拆分
    • 正在做

全部代码

ts 复制代码
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';

let eslintFixTask: vscode.Task | undefined;
let eslintFixTerminal: vscode.Terminal | undefined;
let lineCount: number = 0;


export async function activate(context: vscode.ExtensionContext) {
    try {
        vscode.window.showInformationMessage('Auto-inspecting errors is about to start...');
        // 在插件激活时检查任务状态
        eslintFixTask = await getEslintFixTask();

        if (eslintFixTask) {
            // 任务存在,重新创建终端
            eslintFixTerminal = vscode.window.createTerminal({ name: 'eslint-fix', shellPath: 'npm', shellArgs: ['run', 'lint-fix'] });
            eslintFixTerminal.show();
        }

        const result = await runPluginLogic();

        if (!result) {
            // 降级处理或者其他处理方式
            console.warn('插件逻辑执行失败,插件将以降级模式继续运行。');
        }
    } catch (error) {
        vscode.window.showErrorMessage('插件遇到了一个错误,请查看控制台获取更多信息。');
        // 记录错误到插件日志
        console.error('An error occurred in the extension:', error);
    }
}

async function runPluginLogic(): Promise<boolean> {
    vscode.commands.registerCommand('auto-inspect-errors.AutoInspectErrors', async () => {
            vscode.window.showInformationMessage('Auto-inspecting errors is about to registing...');
            console.log('AutoInspectPlugin :>> ', 'the plugin is started');
            // 获取当前工作区
            const workspaceFolders  = vscode.workspace.workspaceFolders;

            if (!workspaceFolders) {
                vscode.window.showErrorMessage('No workspace folder opened.');
                return;
            }
            const workspaceFolder = workspaceFolders[0].uri.fsPath;

            // 获取用户配置
            const config = vscode.workspace.getConfiguration('autoInspectErrors');
            const excludedPatterns: string[] = config.get('excludedPatterns', []);
            const customErrorRules: string[] = config.get('customErrorRules', []);

            // 构建.gitignore文件路径
            const gitIgnorePath = path.join(workspaceFolder, '.gitignore');

            try {
                const gitIgnoreContent = await fs.promises.readFile(gitIgnorePath, 'utf-8');

                const ignoreRules = gitIgnoreContent
                    .split('\n')
                    .map(line => line.trim())
                    .filter(line => line.length > 0 && !line.startsWith('#'))
                    .map(line => new vscode.RelativePattern(workspaceFolder, line));

                // 添加用户配置的排除规则
                excludedPatterns.forEach(pattern => {
                    ignoreRules.push(new vscode.RelativePattern(workspaceFolder, pattern));
                });

                // 添加用户配置的自定义错误规则
                const customErrorRegex = new RegExp(customErrorRules.join('|'));


                // 添加新的排除规则,排除 node_modules 目录
                ignoreRules.push(new vscode.RelativePattern(workspaceFolder, 'node_modules/**'));

                // 使用异步 vscode.workspace.findFiles 替代同步 vscode.workspace.findFiles
                const nonIgnoredFiles = await vscode.workspace.findFiles('**/*.{ts,tsx,vue,js,json,css}', '**/node_modules/**', 1000);

                const matchingFiles = nonIgnoredFiles.filter(file => {
                    const filePath = path.relative(workspaceFolder, file.fsPath);

                    return ignoreRules.every(rule => {
                        const relativePath = path.relative(workspaceFolder, rule.base);
                        const rulePath = path.join(relativePath, rule.pattern);

                        return !filePath.startsWith(rulePath);
                    });
                });

                // 使用 Promise.all 并行处理异步操作
                const openedFiles = await Promise.all(matchingFiles.map(async file => {
                    const res  = await checkFixAndCloseFile(file,customErrorRegex);
                    if (res) return res
                    return undefined; // 返回 undefined 表示没有打开的文件
                }));

                // 过滤掉 undefined,以获得实际打开的文件数量
                const actualOpenedFiles = openedFiles.filter(file => file !== undefined);

                // 获取当前组件的行数
                vscode.window.onDidChangeActiveTextEditor(async (editor) => {
                    if (editor) {
                        const res = await getCurrentFileLineContent()
                        if (res > 500) {
                            await splitFiles(editor.document)
                        }
                    }
                })

                //
                vscode.workspace.onDidChangeTextDocument(async (event) => {
                    if (event) {
                        const res = await getCurrentFileLineContent()
                        if (res > 500) {
                            await splitFiles(event.document)
                        }
                    }
                })

                vscode.window.showInformationMessage(`Found ${actualOpenedFiles.length} problematic TypeScript file(s).`);
            } catch (error: any) {
                vscode.window.showErrorMessage(`Error reading .gitignore: ${error.message}`);
            }
    });
    return true; // 表示逻辑执行成功
}

const executeEslintFix = async (): Promise<boolean> => {

    let terminal: vscode.Terminal | undefined;

    try {
        // 配置任务
        const task: vscode.Task = new vscode.Task(
            { type: 'shell' },
            vscode.TaskScope.Workspace,
            'eslint-fix',
            'ESLint Fix',
            new vscode.ShellExecution('npm run lint-fix')
        );

        // 执行任务并等待结果
        const execution = await vscode.tasks.executeTask(task);

        // 提前检查任务是否启动成功
        if (!execution || !execution.task) {
            vscode.window.showErrorMessage('Failed to start eslint-fix task. Check your configuration of package.json. like: "lint-fix": "eslint-fix ."');
            return false;
        }

        return new Promise<boolean>((resolve) => {
            // 监听任务结束事件
            const onDidEndTask = vscode.tasks.onDidEndTaskProcess((e) => {
                if (e.execution === execution) {
                    let message = '';
                    let isSuccess = false;

                    // 判断任务状态
                    switch (e.exitCode) {
                        case 0:
                            // 任务成功完成
                            message = 'Auto-fix task completed successfully.';
                            isSuccess = true;
                            break;
                        case 1:
                            // 任务失败
                            message = 'Auto-fix task failed. Check the terminal for details.';
                            isSuccess = true;
                            break;
                        default:
                            // 任务被取消
                            message = 'Auto-fix task was canceled.';
                            break;
                    }

                    // 显示提示消息
                    vscode.window.showInformationMessage(message);

                    // 移除监听
                    onDidEndTask.dispose();

                    // 返回结果
                    resolve(isSuccess);
                }
            });

            // 监听终端关闭事件
            const onDidCloseTerminal = vscode.window.onDidCloseTerminal((closedTerminal) => {
                if (closedTerminal === terminal) {
                    // 用户手动关闭了终端
                    vscode.window.showErrorMessage('Auto-fix task terminated. Please run the plugin again.');

                    // 移除监听
                    onDidCloseTerminal.dispose();

                    resolve(false);
                }
            });

            // 监听用户中断任务(Ctrl+C)的情况
            const onDidTerminateTask = vscode.tasks.onDidEndTaskProcess((terminatedTask) => {
                if (terminatedTask.execution === execution) {
                    // 任务被中断
                    vscode.window.showErrorMessage('Auto-fix task terminated. Please run the plugin again.');

                    // 移除监听
                    onDidTerminateTask.dispose();

                    resolve(false);
                }
            });

        });
    } catch (error: any) {
        console.error(`Error: ${error.message}`);
        return false;
    }
};

const checkFixAndCloseFile = async (file: vscode.Uri,customErrorRegex: RegExp) => {
    try {
        const document = await vscode.workspace.openTextDocument(file);

        // 检查自定义错误规则
        const fileContent = document.getText();
        const hasCustomErrors = customErrorRegex.test(fileContent);

        // 获取 vscode 自身的报错
        const hasVSCodeDiagnostics = vscode.languages.getDiagnostics(document.uri).length > 0;

        if (hasVSCodeDiagnostics || hasCustomErrors) {
            await vscode.window.showTextDocument(document, { preview: false });

            // 执行 ESLint 自动修复
            const isExcuteEslintFix = await executeEslintFix();
            /*
                1. 自己ctrl+c取消
                2. 没有配置对应的eslint规则在依赖中
                3. eslint运行中手动删除,也提示用户重新运行命令
            */

            if (isExcuteEslintFix) {
                // 执行之后要去看有没有文件有问题
                if (document && (hasVSCodeDiagnostics || hasCustomErrors)) {
                    await vscode.window.showTextDocument(document, { preview: false });
                } else {
                    vscode.commands.executeCommand('workbench.action.closeActiveEditor');
                }
            }
            return document
        } else {
            vscode.window.showInformationMessage('eslint fix script is aready done, please modifed details manually');
            return document;
        }
    } catch (error) {
        console.error("An error occurred:", error);
        vscode.window.showErrorMessage('ESLint fix failed. Check the terminal for details.');
    }
};

async function getEslintFixTask(): Promise<vscode.Task | undefined> {
    const eslintFixTaskName = 'eslint-fix';

    // 获取所有任务
    const tasks = await vscode.tasks.fetchTasks();

    // 查找 eslint-fix 任务
    const eslintFixTask = tasks.find(task => task.name === eslintFixTaskName);

    return eslintFixTask;
}

async function getCurrentFileLineContent(): Promise<number>{
    const activeTextEditor = vscode.window.activeTextEditor;

    if (activeTextEditor) {
        const document = activeTextEditor.document;
        if (document && (document.fileName.endsWith('.vue') || document.fileName.endsWith('.tsx'))) {
            const text = document.getText();
            lineCount = text.split(/\r\n|\r|\n/).length;
        }
    }

    return lineCount;
}

const splitFiles = async (document:  vscode.TextDocument) => {
    console.log('File is being split...');
}

export function deactivate() { }

具体的配置都在插件的主页写的清楚,有问题和建议 欢迎评论

相关推荐
陈随易14 分钟前
Postman 被曝自动往 Claude Code 和 Codex 装插件
前端·后端·程序员
云-缩影2 小时前
拆解 AI 图像生成应用的后端架构:ECS、OSS 和大模型 API 怎么配合
java·spring boot·后端
Dream-Y.ocean2 小时前
鸿蒙PC平台宅匠 3D 室内设计适配实战:基于 Electron 壳方案的 Three.js 双渲染引擎与参数化户型编辑器开发
javascript·3d·harmonyos
考虑考虑3 小时前
Posrgresql恢复.backup文件
数据库·后端·postgresql
Csvn3 小时前
性能监控
前端
Highcharts.js3 小时前
Highcharts Gantt使用符号的自定义数据标签开发甘特图示例
javascript·甘特图·highcharts·图表开发·实战代码·响应式图表
无奈何杨3 小时前
风控决策系统决策流,策略模式,权重、顺序、投票、最坏
后端
Csvn4 小时前
构建工具
前端
FRing034 小时前
vue2干货总结3(computed计算属性、watch监听器)
开发语言·前端·vue.js·vue2
Patrick_Wilson5 小时前
Go 为什么需要 defer?从函数怎么收尾说起
后端·go·编程语言