Babel 还原不直观的编码字符串或数值

需求

还原不直观的编码字符串或数值,使其一目了然

代码样例(encode.js)

// 二进制整数
var b = 0b11;
// 八进制整数
var o = 0o7;
// 十六进制整数
var x = 0x23;
// \u \x 字符串
const u = 'Hello\u{000A}\u0009!\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xb8\x96\xe7\x95\x8c';

处理后代码(decode.js)

// 二进制整数
var b = 3;
// 八进制整数
var o = 7;
// 十六进制整数
var x = 35;
// \u \x 字符串
const u = "Hello\n\t!你好世界";

思路

StringLiteralNumericLiteral 节点中, value 为直观形式,extra 中包含不直观(编码)形式

查阅 @babel/types 文档发现对于 StringLiteralNumericLiteral 节点 extra 为非必须属性

去除 extra 属性后发现,js 代码中相应值变为直观形式

编写 babel 插件

直接上完整插件代码

// decrypt.js
const fs = require('fs');
var util = require('util');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const types = require('@babel/types');
const generator = require('@babel/generator').default;

// 程序启动时间
var time_start = new Date().getTime()
// 读取文件
process.argv.length > 2 ? encode_file = process.argv[2] : encode_file = 'encode.js';
process.argv.length > 3 ? decode_file = process.argv[3] : decode_file = 'decode.js';

let jscode = fs.readFileSync(encode_file, { encoding: 'utf-8' });
console.log(util.format('Reading the file [%s] is complete.', encode_file))
// 转换为 ast 树
let ast = parser.parse(jscode);

const visitor =
{
  NumericLiteral(path) {
    let node = path.node;
    if (node.extra && /^0[obx]/i.test(node.extra.raw)) {
      node.extra = undefined;
    }
  },
  StringLiteral(path) {
    let node = path.node;
    if (node.extra && /\\[ux]/gi.test(node.extra.raw)) {
      try {
        node_value = decodeURIComponent(escape(node.value));
      } catch (error) {
        node_value = node.value;
      };
      path.replaceWith(types.stringLiteral(node_value));
      path.node.extra = { 'raw': JSON.stringify(node_value), 'rawValue': node_value };
    }
  }
}

//调用插件,处理待处理 js ast 树
traverse(ast, visitor);
console.log('AST traverse completed.')

// 生成处理后的 js
let { code } = generator(ast);
console.log('AST generator completed.')
fs.writeFile(decode_file, code, (err) => { });
console.log(util.format('The javascript code in [%s] has been processed.', encode_file))
console.log(util.format('The processing result has been saved to [%s].', decode_file))
// 程序结束时间
var time_end = new Date().getTime()
console.log(util.format('The program runs to completion, time-consuming: %s s', (time_end - time_start) / 1000))

注意:\x 编码的字符串若是中文去仅除 extra 属性,其仍然会表现为乱码
故在此处插件尝试进行了解码

推荐阅读

参考

Table of Contents