您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關vscode+babel然后開發一個智能移除未使用變量的插件,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
vscode 已經成為前端不可缺失的開發工具之一,之所以 vscode 能夠獲得開發者的青睞,我想和它“無所不能”的插件體系有很大一部分關系。在工作中我們能用它來開發純工具型
的插件,也可以用它開發一些和公司業務相結合
的功能插件。在這里我分享一個通過結合babel
來實現一個能夠智能移除未使用的變量
插件。
今天我們首先來熟悉一下 vscode 插件項目的搭建流程
安裝腳手架
# 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 窗口,在新窗口中按下Ctrl+Shift+P
輸入Hello World
,在窗口右下角會看到一個提示框,說明我們第一個 vscode 插件運行成功運行了。
vscode 插件很多功能都是基于一個個命令實現的,我們可以自定義一些命令,這個命令將出現在按下Ctrl+Shift+P
后的命令列表里面,同時可以給命令配置快捷鍵、配置資源管理器菜單、編輯器菜單、標題菜單、下拉菜單、右上角圖標等。
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
看看我們的插件是否能夠正常運行。沒錯就是這么簡單,我們的插件已經能夠支持快捷鍵的形式運行了。
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!"); } );
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
工具庫,文檔地址(https://www.babeljs.cn/docs/babel-types)
轉換前
const say = () => { console.log("hello"); };
轉換后
function say() { console.log("hello"); }
代碼實現,代碼部分寫死僅供學習參考
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"); } */
很多同學肯定好奇現在我們的表達式比較簡單還好,如果復雜的話定義嵌套會非常深和復雜,這個時候應該怎么知道去替換哪個節點?。其實這里可以借助astexplorer.net/ 這是一個在線轉換 AST 的網站。我們可以打開兩個窗口,把轉換前的代碼放到第一個窗口,把需要轉換的接口放到第二個窗口。這個時候我們就可以對比一下轉換前后的差異,來實現我們的代碼了。
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 轉換網站,復制這些內容進去看看生成的語法樹結構
我們先來實現一個例子吧,比如把下面代碼中沒有用的變量移除掉
轉換前
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);
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 'vue'; 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);
因為我們現在實現是針對 js 文件的,所以打開其他類型的文件我們可以讓我們的快捷鍵失效。
我們可以修改package.json
package.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" } ] } }
此處省略... 相信看了上面這些介紹大家已經完全有能力自己整合了
打包我們可以vsce
工具
全局安裝 vsce
# npm npm i vsce -g # yarn yarn global add vsce
打包插件
打包前先修改 README.md 文件否則會報錯
vsce package
執行完畢之后會生成一個.vsix 文件
如果要在本地 vscode 使用可以直接導入
如果要發布到市場的話,我們需要先注冊賬號 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然后開發一個智能移除未使用變量的插件,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。