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
的插件的做者, 是真的强!