Heim  >  Artikel  >  Entwicklungswerkzeuge  >  vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt

vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt

青灯夜游
青灯夜游nach vorne
2021-12-22 19:48:163358Durchsuche

Dieser Artikel beschreibt eine Methode zum intelligenten Entfernen nicht verwendeter Variablen-Plug-Ins in vscode in Kombination mit Babel. Ich hoffe, dass es für alle hilfreich ist!

vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt

vscode ist zu einem unverzichtbaren Entwicklungstool für das Frontend geworden. Der Grund, warum vscode von Entwicklern bevorzugt wird, hat meiner Meinung nach viel mit seinem „allmächtigen“ Plug-in-System zu tun. Bei der Arbeit können wir es verwenden, um rein toolartige Plug-Ins zu entwickeln, und wir können es auch verwenden, um einige funktionale Plug-Ins zu entwickeln, die mit dem Geschäftdes Unternehmens kombiniert werden >. Hier teile ich ein Plug-in, das durch die Kombination von babel auf intelligente Weise nicht verwendete Variablen entfernen kann. Ich hoffe, es wird jeden bei der Entwicklung von vscode-Plug-ins inspirieren und unterstützen. [Empfohlenes Lernen: „vscode-Einführungs-Tutorial工具型的插件,也可以用它开发一些和公司业务相结合的功能插件。在这里我分享一个通过结合babel来实现一个能够智能移除未使用的变量插件,希望对大家开发 vscode 插件有一定的启发和帮助。【推荐学习:《vscode入门教程》】

正文

今天我们首先来熟悉一下 vscode 插件项目的搭建流程

1、使用官方提供的脚手架初始化一个项目

安装脚手架

# npm 形式
npm install -g yo generator-code
# yarn 形式
yarn global add yo generator-code

运行脚手架

# 运行脚手架
yo code

选择模板,考虑到有些开发者对 TypeScript 并不熟悉,所以我们这里选择 New Extension (JavaScript)

? What type of extension do you want to create? New Extension (JavaScript)
? What's the name of your extension? rm-unuse-var
? What's the identifier of your extension? rm-unuse-var
? What's the description of your extension? 移除未使用的变量
? Enable JavaScript type checking in 'jsconfig.json'? Yes
? Initialize a git repository? Yes
? Which package manager to use? yarn

这是我们最终生成的目录结构

vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt

我们先来运行一下这个插件试试

vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt

点击上面运行按钮,会打开一个新的 vscode 窗口,在新窗口中按下Ctrl+Shift+P输入Hello World,在窗口右下角会看到一个提示框,说明我们第一个 vscode 插件运行成功运行了。

vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt

2、自定义命令、快捷键、菜单

vscode 插件很多功能都是基于一个个命令实现的,我们可以自定义一些命令,这个命令将出现在按下Ctrl+Shift+P后的命令列表里面,同时可以给命令配置快捷键、配置资源管理器菜单、编辑器菜单、标题菜单、下拉菜单、右上角图标等。

3、如何添加命令列表

package.json 部分配置

{
  // 扩展的激活事件
  "activationEvents": ["onCommand:rm-unuse-var.helloWorld"],
  // 入口文件
  "main": "./extension.js",
  // 添加指令
  "contributes": {
    "commands": [
      {
        // 这里的值必须和activationEvents里面配置的一样
        "command": "rm-unuse-var.helloWorld",
        // 这个就是我们指令的名称,可以修改这里的值重新运行插件试试看
        "title": "Hello World"
      }
    ]
  }
}

在开发中快捷键的使用方式是最便捷的,接下来我们修改一下配置,让插件支持快捷键的方式运行。

{
  "contributes": {
    "commands": [
      {
        // 这里的值必须和activationEvents里面配置的一样
        "command": "rm-unuse-var.helloWorld",
        // 这个就是我们指令的名称,可以修改这里的值重新运行插件试试看
        "title": "Hello World"
      }
    ],
    // 快捷键绑定
    "keybindings": [
      {
        "command": "rm-unuse-var.helloWorld",
        "key": "ctrl+6",
        "mac": "cmd+6"
      }
    ]
  }
}

我们再重新运行一下,通过快捷键Ctrl+6看看我们的插件是否能够正常运行。没错就是这么简单,我们的插件已经能够支持快捷键的形式运行了。

4、叫 helloWorld 太土了,下一步我们来修改一下指令的名称

package.json

{
  "activationEvents": ["onCommand:rm-unuse-var.rm-js-var"],
  "main": "./extension.js",
  "contributes": {
    "commands": [
      {
        "command": "rm-unuse-var.rm-js-var",
        "title": "Hello World"
      }
    ],
    "keybindings": [
      {
        "command": "rm-unuse-var.rm-js-var",
        "key": "ctrl+6",
        "mac": "cmd+6"
      }
    ]
  }
}

因为我们在extension.js中注册了指令的名称,所以也要同步修改

let disposable = vscode.commands.registerCommand(
  "rm-unuse-var.rm-js-var",
  function () {
    vscode.window.showInformationMessage("Hello World from rm-unuse-var!");
  }
);

5、安装babel相关库

我们修改代码可以分为 3 个步骤

1、将代码解析成 AST 语法树 2、遍历修改 AST 语法树 3、根据修改过的 AST 语法树生成新的代码

这 3 个步骤 babel 都有对应的库来处理

  • @babel/parser生成 AST 语法树,文档地址(https://www.babeljs.cn/docs/babel-parser)

  • @babel/traverse遍历 AST 语法树,文档地址(https://www.babeljs.cn/docs/babel-traverse)

  • @babel/generator根据 AST 语法树生成代码,文档地址(https://www.babeljs.cn/docs/babel-generator)

  • @babel/types"]

    Text
  • Heute machen wir uns zunächst mit dem Erstellungsprozess des vscode-Plug-in-Projekts vertraut

1 das offizielle Gerüst

Installieren Sie das Gerüst

const say = () => {
  console.log("hello");
};

Führen Sie das Gerüst aus

function say() {
  console.log("hello");
}

Wählen Sie eine Vorlage. Da einige Entwickler mit TypeScript nicht vertraut sind, wählen wir Neue Erweiterung (JavaScript)

const t = require("@babel/types");
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generate = require("@babel/generator").default;
// 1、将代码解析成 AST 语法树
const ast = parser.parse(`const say = () => {
  console.log("hello");
};`);
// 2、遍历修改 AST 语法树
traverse(ast, {
  VariableDeclaration(path) {
    const { node } = path;
    // 写死找到第一个申明
    const declaration = node.declarations[0];
    // 定义的内容
    const init = declaration.init;
    // 判断是否是箭头函数
    if (t.isArrowFunctionExpression(init)) {
      // 将原来的表达式替换成新生成的函数
      path.replaceWith(
        t.functionDeclaration(
          declaration.id,
          init.params,
          init.body,
          init.generator,
          init.async
        )
      );
    }
  },
});
// 3、根据修改过的 AST 语法树生成新的代码
console.log(generate(ast).code);
/*
function say() {
  console.log("hello");
}
*/

Dies ist unsere endgültig generierte Verzeichnisstruktur "/>

Versuchen wir zunächst, dieses Plug-in auszuführen

vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt🎜🎜Klicken Sie oben auf die Schaltfläche „Ausführen“ und ein neues vscode-Fenster wird geöffnet. Drücken Sie im neuen Fenster Strg+Umschalt+P und geben Sie Hello World ein , sehen Sie in der unteren rechten Ecke des Fensters ein Eingabeaufforderungsfeld, das anzeigt, dass unser erstes vscode-Plug-in erfolgreich ausgeführt wurde. 🎜🎜vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt🎜

🎜2. Benutzerdefinierte Befehle, Tastenkombinationen, Menüs🎜🎜🎜Viele Funktionen des vscode-Plugins werden basierend auf Befehlen implementiert, die beim Drücken von angezeigt werden Nachdem Sie auf Strg+Umschalt+P in der Befehlsliste geklickt haben, können Sie auch Tastenkombinationen für Befehle konfigurieren, das Ressourcenmanagermenü, das Editormenü, das Titelmenü, das Dropdown-Menü, das Symbol in der oberen rechten Ecke usw. konfigurieren. 🎜

🎜3. So fügen Sie eine Befehlsliste hinzu🎜🎜🎜package.json Teilkonfiguration🎜
import * as vscode from "vscode";
// 当前打开的文件
const { activeTextEditor } = vscode.window;
// 然后通过document下的getText就能轻松获取到我们的代码了
const code = activeTextEditor.document.getText();
🎜Die Verwendung von Tastenkombinationen ist die bequemste Methode in der Entwicklung In der Konfiguration können Sie die Plug-in-Unterstützung per Tastenkombination ausführen lassen. 🎜
activeTextEditor.edit((editBuilder) => {
  editBuilder.replace(
    // 因为我们要全文件替换,所以我们需要定义一个从头到位的区间
    new vscode.Range(
      new vscode.Position(0, 0),
      new vscode.Position(activeTextEditor.document.lineCount + 1, 0)
    ),
    // 我们的新代码
    generate(ast).code
  );
});
🎜Lass es uns noch einmal ausführen und die Tastenkombination Strg+6 verwenden, um zu sehen, ob unser Plug-in normal ausgeführt werden kann. Ja, so einfach ist das. Unser Plug-in unterstützt bereits die Ausführung als Tastenkombination. 🎜

🎜4. Der Aufruf von helloWorld ist zu kitschig. Als nächstes ändern wir den Namen des Befehls 🎜🎜🎜package.json🎜
import vue from "vue";
const a = { test1: 1, test2: 2 };
const { test1, test2 } = a;
function b() {}
let c = () => {};
var d = () => {};
🎜weil wir uns in extension.js befinden <der name des befehls ist in> registriert, daher muss er gleichzeitig geändert werden🎜<pre class="brush:js;toolbar:false;">var a = 1; var b = 2; console.log(a);</pre><h3 data-id="heading-6">🎜5 Installieren Sie <code>babel-bezogene Bibliotheken🎜🎜 🎜Wir können den Code in drei Schritte unterteilen🎜🎜1. Den Code in einen AST-Syntaxbaum analysieren 2. Durchlaufen und ändern Sie den AST-Syntaxbaum 3. Generieren Sie neuen Code basierend auf dem modifizierten AST-Syntaxbaum🎜🎜Babel verfügt über entsprechende Bibliotheken, um diese drei Schritte zu verarbeiten🎜
  • 🎜 @babel/parser AST-Syntaxbaum generieren, Dokumentadresse (https://www.babeljs.cn/docs/babel-parser)🎜🎜
  • 🎜@babel/traverse AST-Syntaxbaum durchqueren, Dokumentadresse (https://www.babeljs.cn/docs/babel-traverse) 🎜🎜
  • 🎜@babel/generatorGenerieren Sie Code basierend auf dem AST-Syntaxbaum, Dokumentadresse ( https:/ /www.babeljs.cn/docs/babel-generator) 🎜🎜
  • 🎜@babel/types Werkzeugbibliothek, Dokumentadresse (https://www.babeljs.cn/docs /babel- (Typen) 🎜🎜🎜🎜🎜6. Werfen wir einen Blick auf die grundlegende Verwendung dieser Bibliotheken, z. B. die Implementierung einer Code-Implementierung, die eine es6-Pfeilfunktion in eine normale Funktion konvertiert 🎜🎜🎜Vor der Konvertierung🎜
    var a = 1;
    console.log(a);
    🎜Nach der Konvertierung🎜
    const t = require("@babel/types");
    const parser = require("@babel/parser");
    const traverse = require("@babel/traverse").default;
    const generate = require("@babel/generator").default;
    
    const ast = parser.parse(`var a = 1;
    var b = 2;
    console.log(a);`);
    
    traverse(ast, {
      VariableDeclaration(path) {
        const { node } = path;
        const { declarations } = node;
        // 此处便利可以处理 const a = 1,b = 2; 这种场景
        node.declarations = declarations.filter((declaration) => {
          const { id } = declaration;
          // const { b, c } = a;
          if (t.isObjectPattern(id)) {
            // path.scope.getBinding(name).referenced 判断变量是否被引用
            // 通过filter移除掉没有使用的变量
            id.properties = id.properties.filter((property) => {
              const binding = path.scope.getBinding(property.key.name);
              return !!binding?.referenced;
            });
            // 如果对象中所有变量都没有被应用,则该对象整个移除
            return id.properties.length > 0;
          } else {
            // const a = 1;
            const binding = path.scope.getBinding(id.name);
            return !!binding?.referenced;
          }
        });
        // 如果整个定义语句都没有被引用则整个移除
        if (node.declarations.length === 0) {
          path.remove();
        }
      },
    });
    console.log(generate(ast).code);
    🎜 Der Codeteil ist nur zu Lernzwecken fest codiert🎜rrree🎜Viele Schüler sind sicher neugierig, dass unser Ausdruck jetzt relativ einfach ist. Wenn er komplex ist, wird die Definitionsverschachtelung sehr tiefgreifend und kompliziert sein zu diesem Zeitpunkt? . Tatsächlich können Sie 🎜astexplorer.net/🎜 verwenden. Dies ist eine Website zum Online-Konvertieren von AST. Wir können zwei Fenster öffnen, den Code vor der Konvertierung in das erste Fenster einfügen und die zu konvertierende Schnittstelle in das zweite Fenster einfügen. Zu diesem Zeitpunkt können wir die Unterschiede vor und nach der Konvertierung vergleichen, um unseren Code zu implementieren. 🎜

    vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt

    vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt

    6、思考插件如何实现?

    1、获取编辑器当前打开的 js 文件的代码 2、将代码解析成 AST 语法树 3、遍历 AST 语法树,删除未使用的定义 4、根据修改过的 AST 语法树生成新的代码 5、替换当前 js 文件的代码

    其中 2、4 我们已经会了,接下来只需要看下 1、3、5 如何实现就行

    1 和 5 我们可以通过 vscode 提供的方法

    1、获取编辑器当前打开的 js 文件的代码

    import * as vscode from "vscode";
    // 当前打开的文件
    const { activeTextEditor } = vscode.window;
    // 然后通过document下的getText就能轻松获取到我们的代码了
    const code = activeTextEditor.document.getText();

    5、替换当前 js 文件的代码

    activeTextEditor.edit((editBuilder) => {
      editBuilder.replace(
        // 因为我们要全文件替换,所以我们需要定义一个从头到位的区间
        new vscode.Range(
          new vscode.Position(0, 0),
          new vscode.Position(activeTextEditor.document.lineCount + 1, 0)
        ),
        // 我们的新代码
        generate(ast).code
      );
    });

    好了接下来我们就剩核心的第 3 步了。

    3、遍历 AST 语法树,删除未使用的定义

    我们先来分析一下,未使用的定义包含了哪些?

    import vue from "vue";
    const a = { test1: 1, test2: 2 };
    const { test1, test2 } = a;
    function b() {}
    let c = () => {};
    var d = () => {};

    然后在线 ast 转换网站,复制这些内容进去看看生成的语法树结构

    vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt

    我们先来实现一个例子吧,比如把下面代码中没有用的变量移除掉

    转换前

    var a = 1;
    var b = 2;
    console.log(a);

    转换后

    var a = 1;
    console.log(a);
    • scope.getBinding(name) 获取当前所有绑定
    • scope.getBinding(name).referenced 绑定是否被引用
    • scope.getBinding(name).constantViolations 获取当前所有绑定修改
    • scope.getBinding(name).referencePaths 获取当前所有绑定路径

    代码实现

    const t = require("@babel/types");
    const parser = require("@babel/parser");
    const traverse = require("@babel/traverse").default;
    const generate = require("@babel/generator").default;
    
    const ast = parser.parse(`var a = 1;
    var b = 2;
    console.log(a);`);
    
    traverse(ast, {
      VariableDeclaration(path) {
        const { node } = path;
        const { declarations } = node;
        // 此处便利可以处理 const a = 1,b = 2; 这种场景
        node.declarations = declarations.filter((declaration) => {
          const { id } = declaration;
          // const { b, c } = a;
          if (t.isObjectPattern(id)) {
            // path.scope.getBinding(name).referenced 判断变量是否被引用
            // 通过filter移除掉没有使用的变量
            id.properties = id.properties.filter((property) => {
              const binding = path.scope.getBinding(property.key.name);
              return !!binding?.referenced;
            });
            // 如果对象中所有变量都没有被应用,则该对象整个移除
            return id.properties.length > 0;
          } else {
            // const a = 1;
            const binding = path.scope.getBinding(id.name);
            return !!binding?.referenced;
          }
        });
        // 如果整个定义语句都没有被引用则整个移除
        if (node.declarations.length === 0) {
          path.remove();
        }
      },
    });
    console.log(generate(ast).code);

    7、了解基本处理流程之后,我们就来看下最终的代码实现吧

    const t = require("@babel/types");
    const parser = require("@babel/parser");
    const traverse = require("@babel/traverse").default;
    const generate = require("@babel/generator").default;
    
    const ast = parser.parse(
      `import vue from &#39;vue&#39;;
      var a = 1;
    var b = 2;
    var { test1, test2 } = { test1: 1, test2: 2 };
    function c(){}
    function d(){}
    d();
    console.log(a, test1);`,
      {
        sourceType: "module",
      }
    );
    
    traverse(ast, {
      // 处理 const var let
      VariableDeclaration(path) {
        const { node } = path;
        const { declarations } = node;
    
        node.declarations = declarations.filter((declaration) => {
          const { id } = declaration;
          if (t.isObjectPattern(id)) {
            id.properties = id.properties.filter((property) => {
              const binding = path.scope.getBinding(property.key.name);
              return !!binding?.referenced;
            });
            return id.properties.length > 0;
          } else {
            const binding = path.scope.getBinding(id.name);
            return !!binding?.referenced;
          }
        });
    
        if (node.declarations.length === 0) {
          path.remove();
        }
      },
      // 处理 import
      ImportDeclaration(path) {
        const { node } = path;
        const { specifiers } = node;
        if (!specifiers.length) {
          return;
        }
        node.specifiers = specifiers.filter((specifier) => {
          const { local } = specifier;
          const binding = path.scope.getBinding(local.name);
          return !!binding?.referenced;
        });
        if (node.specifiers.length === 0) {
          path.remove();
        }
      },
      // 处理 function
      FunctionDeclaration(path) {
        const { node } = path;
        const { id } = node;
        const binding = path.scope.getBinding(id.name);
        if (!binding?.referenced) {
          path.remove();
        }
      },
    });
    console.log(generate(ast).code);

    8、vscode 设置我们的插件只支持 js 文件的限制

    因为我们现在实现是针对 js 文件的,所以打开其他类型的文件我们可以让我们的快捷键失效。 我们可以修改package.jsonpackage.json

    {
      "contributes": {
        "commands": [
          {
            "command": "rm-unuse-var.remove",
            "title": "Hello World"
          }
        ],
        "keybindings": [
          {
            "command": "rm-unuse-var.remove",
            "key": "ctrl+6",
            "mac": "cmd+6",
            "when": "resourceLangId == javascript"
          }
        ]
      }
    }

    9、整合到我们前面创建的项目中去

    此处省略... 相信看了上面这些介绍大家已经完全有能力自己整合了

    10、打包发布插件

    打包我们可以vsce工具

    全局安装 vsce

    # npm
    npm i vsce -g
    # yarn
    yarn global add vsce

    打包插件

    打包前先修改 README.md 文件否则会报错

    vsce package

    执行完毕之后会生成一个.vsix 文件

    如果要在本地 vscode 使用可以直接导入

    vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt

    如果要发布到市场的话,我们需要先注册账号 https://code.visualstudio.com/api/working-with-extensions/publishing-extension#publishing-extensions

    # 登录账号
    vsce login your-publisher-name
    # 发布
    vsce publish

    发布成功之后就能在我们的市场上看到了 marketplace.visualstudio.com/items?itemN… 也可以在 vscode 中搜索打我们的插件

    vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt

    总结

    到此为止,相信大家对 vscode 插件开发的基本流程已经有了了解。

    觉得文章对你有所帮助,可以点个赞 

    当然 vscode 插件还有非常多的配置没有介绍,后面如果有时间可以单独整理成一篇文章来介绍

    如果在开发过程中有问题或者其他前端技术问题也可以加我微信rjjs1221交流,或者直接在评论区回复。

    源码地址 https://github.com/taoxhsmile/rm-unuse-var

    更多关于VSCode的相关知识,请访问:vscode教程!!

Das obige ist der detaillierte Inhalt vonvscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen