babel 現在已成爲每個現代前端項目的標配, 有了它,咱們能夠肆無忌憚的使用 stage-xxxx
的語法, 加強咱們的生產力前端
咱們經過寫一個 支持 arr[-1]
的 babel插件 來加深理解node
如今咱們有以下的一個數組 arr
, 咱們想獲取數組的最後一個下標,因爲 js
不支持 [-1]
的操做,因此咱們須要轉換成 arr[arr.length - 1]
git
const arr = [1,2,3]
const value = arr[-1]
// ↓ 想要轉換成
const value = arr[arr.length - 1]
複製代碼
換做之前的我, 反手就是粗暴的 正則解析 去替換, 固然正規一點仍是老老實實用 AST(Abstract Syntax Tree)github
站在巨人的肩膀上 咱們能夠 使用 在線的 AST 生成工具 astexplorer.net/ 能夠看到咱們剛纔寫的兩行代碼對應的 語法樹express
固然你也可使用 @babel/core
來生成 語法樹 獲得的結果是一致的數組
import babel from '@babel/core' const code = ` const arr = [1,2,3] const value = arr[-1] ` babel.transform(code,{},(result)=>{ console.log(result.ast) }) 複製代碼
關於 babel 插件的詳細介紹, 能夠參考 這篇文章 Babel 插件有啥用?bash
const babel = require('@babel/core') const t = require('@babel/types') const visitor = { MemberExpression(path) { const node = path.node } } module.exports = (babel) => { return { visitor } } 複製代碼
@babel/types
主要幫助咱們判斷 當前節點是什麼類型, 十分強大, 根據觀察生成 的 AST
, arr[-1]
是一個 MemberExpression
節點, 因此咱們只須要 將對應的節點 替換掉便可babel
想要將 arr[-1]
轉換成 arr[arr.length - 1]
, 咱們須要知道2點markdown
數組的名字 => arr
數組的下標而且是一個數字 => -1
獲取 數組的名字工具
if (node.object && t.isIdentifier(node.object)) { const arrName = node.object.name } 複製代碼
獲取 數組的下標, 應該知足 是一個表達式, 而且參數是一個數字
let arrIndex let operator if(node.property && t.isUnaryExpression(node.property)){ if( node.property.prefix && node.property.operator && node.property.argument && t.isNumericLiteral(node.property.argument) ) { arrIndex = node.property.argument.value operator = node.property.operator } } 複製代碼
最後拿到了咱們想要的參數, 組裝一下, 替換當前節點便可
const result = `${arrName}[${arrName}.length ${operator} ${arrIndex}]` path.replaceWithSourceString(result) 複製代碼
import { transform } from '@babel/core' import babelArrayLastValuePlugin from '../src/index' describe('test array last value babel plugin', () => { beforeEach(() => { String.prototype.trimAll = function () { return this.replace(/\s/g, "") } }) it('test expression', () => { const _code = ` const arr = [1,2,3]; const v = arr[-1]; ` const { code } = transform(_code, { plugins: [babelArrayLastValuePlugin] }) expect(code.trimAll()).toEqual(`const arr = [1,2,3];const v = arr[arr.length - 1];`.trimAll()) }) 複製代碼
在實際場景中 可能還要直接賦值的狀況
arr[-1] = 4
複製代碼
或者使用 lodash
的 get
的狀況
get(arr,"[0]") 複製代碼
這樣對應的 AST
會有變化, 還須要處理對應的節點
yarn add babel-plugin-array-last-index
複製代碼
// .babelrc { "plugins": [ "array-last-index" ], } 複製代碼
這是我第一次嘗試寫一個玩具插件,雖然實際做用不大, 不過仍是學到很多, 也由衷的佩服那些 寫 正兒八經 babel
的插件的做者, 是真的強!