Astring
🌳 一个小巧快速的 JavaScript 代码生成器,支持符合 ESTree 规范的 AST。
🎁 查看 在线演示。
主要特性
- 可生成高达 13 版本 (2022) 的 JavaScript 代码,以及 已完成的提案。
- 适用于符合 ESTree 规范的 AST,如 Meriyah 或 Acorn 生成的 AST。
- 可通过自定义 AST 节点处理器进行扩展。
- 速度显著快于 Bublé(最高 5 倍)、Escodegen(最高 10 倍)、Babel(最高 50 倍)、UglifyJS(最高 125 倍)和 Prettier(最高 380 倍)。
- 支持使用 Source Map 生成源映射。
- 支持使用 Astravel 生成注释。
- 无依赖,体积小(压缩后约 16 KB,gzip 压缩后约 4 KB)。
- 可在 🦕 Deno 上运行。
安装
:warning: Astring 依赖
String.prototype.repeat(amount)
和String.prototype.endsWith(string)
。如果运行 Astring 的环境未定义这些方法,请使用string.prototype.repeat
、string.prototype.endsWith
或babel-polyfill
。
使用 Node Package Manager 安装:
npm install astring
或使用 JSR 安装:
deno add @davidbonnet/astring
或者,克隆此仓库并安装开发依赖以构建模块文件:
git clone https://github.com/davidbonnet/astring.git
cd astring
npm install
导入
从 Deno 的第三方模块仓库 导入:
const { generate } = await import('https://deno.land/x/astring/src/astring.js')
使用 JavaScript 6 模块:
import { generate } from 'astring'
使用 CommonJS:
const { generate } = require('astring')
dist/astring.min.js
提供了一个可在浏览器中使用的压缩版 Astring。该模块暴露了一个全局变量 astring
:
<script src="astring.min.js" type="text/javascript"></script>
<script type="text/javascript">
var generate = astring.generate
</script>
API
astring
模块暴露以下属性:
generate(node: object, options: object): string | object
返回一个表示提供的 AST node
渲染代码的字符串。但是,如果在 options
中提供了 output
流,它会写入该流并返回该流。
options
包括:
indent
:用于缩进的字符串(默认为"␣␣"
)lineEnd
:用于行尾的字符串(默认为"\n"
)startingIndentLevel
:起始缩进级别(默认为0
)comments
:如果为true
,则生成注释(默认为false
)output
:用于写入渲染代码的输出流(默认为null
)generator
:自定义代码生成器(默认为GENERATOR
)sourceMap
:源映射生成器(默认为null
)expressionsPrecedence
:节点类型及其优先级的自定义映射(默认为EXPRESSIONS_PRECEDENCE
)
GENERATOR: object
可用于 扩展 Astring 的基础生成器。
EXPRESSIONS_PRECEDENCE: object
节点类型及其优先级的映射,用于让生成器知道何时使用括号。
NEEDS_PARENTHESES: number
始终触发括号使用的默认优先级。
baseGenerator: object
:warning: 已弃用,请使用
GENERATOR
代替。
基准测试
生成代码
每秒操作次数,用于从预解析的 AST 生成每个示例代码:
代码示例(长度) | escodegen | astring | uglify | babel | prettier |
---|---|---|---|---|---|
微小代码 (11) | 1,257,527 | 7,185,642 | 129,467 | 156,184 | 333 |
全部 (8532) | 1,366 | 8,008 | 0 | 346 | 64 |
解析和生成代码
每秒操作次数,用于解析和生成每个示例代码:
代码示例(长度) | acorn + astring | meriyah + astring | buble | sucrase |
---|---|---|---|---|
微小代码 (11) | 92,578 | 864,665 | 25,911 | 575,370 |
全部 (8532) | 706 | 1,425 | 132 | 1,403 |
示例
以下示例使用 JavaScript 5 编写,并以 CommonJS 方式导入 Astring。
生成代码
此示例使用 Acorn,这是一个极快的 JavaScript AST 生成器,因此是 Astring 的完美搭配。
// 确保已导入 acorn 和 astring 模块
// 设置示例代码
var code = 'let answer = 4 + 7 * 5 + 3;\n'
// 将其解析为 AST
var ast = acorn.parse(code, { ecmaVersion: 6 })
// 将其格式化为代码字符串
var formattedCode = astring.generate(ast)
// 检查结果
console.log(code === formattedCode ? '成功!' : '出错了…')
生成源映射
此示例使用 Source Map 模块中的源映射生成器。
// 确保已导入 acorn、sourceMap 和 astring 模块
var code = 'function add(a, b) { return a + b; }\n'
var ast = acorn.parse(code, {
ecmaVersion: 6,
sourceType: 'module',
// 需要位置信息以使源映射生成器工作
locations: true,
})
// 创建空的源映射生成器
var map = new sourceMap.SourceMapGenerator({
// 必须设置源文件名,将用于映射
file: 'script.js',
})
var formattedCode = generate(ast, {
// 启用源映射
sourceMap: map,
})
// 显示生成的源映射
console.log(map.toString())
使用可写流
这个 Node 示例展示了如何使用可写流获取渲染后的代码。
// 确保已导入 acorn 和 astring 模块
// 设置示例代码
var code = 'let answer = 4 + 7 * 5 + 3;\n'
// 将其解析为 AST
var ast = acorn.parse(code, { ecmaVersion: 6 })
// 格式化并将结果写入 stdout
var stream = astring.generate(ast, {
output: process.stdout,
})
// 返回值是输出流
console.log('stream 是否等于 process.stdout?', stream === process.stdout)
生成注释
Astring 支持注释生成,前提是这些注释存储在 AST 节点上。为此,本示例使用 Astravel,这是一个快速的 AST 遍历器和修改器。
// 确保已导入 acorn、astravel 和 astring 模块
// 设置示例代码
var code =
[
'// 计算宇宙终极问题的答案',
'let answer = 4 + 7 * 5 + 3;',
'// 显示结果',
'console.log(answer);',
].join('\n') + '\n'
// 将其解析为 AST 并获取注释列表
var comments = []
var ast = acorn.parse(code, {
ecmaVersion: 6,
locations: true,
onComment: comments,
})
// 将注释附加到 AST 节点
astravel.attachComments(ast, comments)
// 将其格式化为代码字符串
var formattedCode = astring.generate(ast, {
comments: true,
})
// 检查结果
console.log(code === formattedCode ? '成功!' : '出错了…')
扩展
通过更新或传递自定义代码 generator
,可以轻松扩展 Astring。代码 generator
由节点名称和函数的映射组成,这些函数接受两个参数:node
和 state
。node
指向要从中生成代码的节点,state
暴露了接受生成的代码字符串的 write
方法。
此示例展示了如何支持 await
关键字,它是 异步函数提案 的一部分。相应的 AwaitExpression
节点基于 这个建议的定义。
// 确保已导入 astring 模块,并且已定义 Object.assign
// 创建一个继承自 Astring 基础生成器的自定义生成器
var customGenerator = Object.assign({}, astring.GENERATOR, {
AwaitExpression: function (node, state) {
state.write('await ')
var argument = node.argument
if (argument != null) {
this[argument.type](argument, state)
}
},
})
// 以某种方式获取自定义 AST(注意,这个 AST 并非来自有效代码)
var ast = {
type: 'AwaitExpression',
argument: {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: 'callable',
},
arguments: [],
},
}
// 格式化
var code = astring.generate(ast, {
generator: customGenerator,
})
// 检查结果
console.log(
code === 'await callable();\n' ? '成功!' : '出错了…',
)
命令行界面
bin/astring
实用工具可用于转换 JavaScript 代码的 JSON 格式 ESTree 兼容 AST。它接受以下参数:
-i
,--indent
:用作缩进的字符串(默认为"␣␣"
)-l
,--line-end
:用于行尾的字符串(默认为"\n"
)-s
,--starting-indent-level
:起始缩进级别(默认为0
)-h
,--help
:打印使用说明并退出-v
,--version
:打印包版本并退出
该实用工具从提供的文件列表或在未提供文件时从 stdin
读取 AST,并打印生成的代码。
示例
与前面的示例一样,这些示例使用 Acorn 获取 JSON 格式的 AST。此命令将 Acorn 从 script.js
文件输出的 AST 通过管道传递给 Astring,并将格式化后的 JavaScript 代码写入 result.js
文件:
acorn --ecma6 script.js | astring > result.js
此命令执行相同操作,但从中间文件读取 AST:
acorn --ecma6 script.js > ast.json
astring ast.json > result.js
此命令从 stdin
读取 JavaScript 6 代码,并输出美化版本:
cat | acorn --ecma