Rust的ECMAScript 2018兼容转换库swc_ecma_compat_es2018的使用,实现现代JavaScript到ES5的高效降级编译
Rust的ECMAScript 2018兼容转换库swc_ecma_compat_es2018的使用,实现现代JavaScript到ES5的高效降级编译
安装
在项目目录中运行以下Cargo命令:
cargo add swc_ecma_compat_es2018
或者在Cargo.toml中添加以下行:
swc_ecma_compat_es2018 = "25.0.0"
使用示例
以下是一个完整的示例,展示如何使用swc_ecma_compat_es2018将ES2018代码转换为ES5兼容代码:
use swc_common::{FileName, SourceMap};
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax};
use swc_ecma_transforms::compat::es2018;
use swc_ecma_visit::FoldWith;
fn main() {
// 创建源代码映射
let cm = SourceMap::default();
// 示例ES2018代码 - 包含async/await和对象展开运算符
let code = r#"
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return { ...data, status: 'processed' };
}
"#;
// 创建输入源
let fm = cm.new_source_file(FileName::Anon, code.to_string());
let input = StringInput::from(&*fm);
// 创建词法分析器和解析器
let lexer = Lexer::new(
Syntax::Es(Default::default()),
Default::default(),
input,
None,
);
let mut parser = Parser::new_from(lexer);
// 解析为AST
let module = parser.parse_module().unwrap();
// 应用ES2018到ES5的转换
let module = module.fold_with(&mut es2018());
// 打印转换后的代码
let output = swc_ecma_codegen::Emitter {
cfg: Default::default(),
cm: cm.clone(),
comments: None,
wr: Box::new(std::io::stdout()),
};
output.emit_module(&module).unwrap();
}
功能说明
swc_ecma_compat_es2018库提供了以下ES2018特性的降级转换:
- 异步函数(async/await)转换为生成器函数
- 对象展开运算符(…)转换为Object.assign调用
- 正则表达式命名捕获组
- 正则表达式后行断言
- Promise.prototype.finally
完整示例代码
// 引入必要的模块
use swc_common::{FileName, SourceMap};
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax};
use swc_ecma_transforms::compat::es2018;
use swc_ecma_visit::FoldWith;
use std::io;
fn main() -> io::Result<()> {
// 1. 初始化源代码映射
let cm = SourceMap::default();
// 2. 准备要转换的ES2018代码
let es2018_code = r#"
// 包含多个ES2018特性的示例代码
const user = { name: 'Alice', age: 30 };
const extendedUser = { ...user, role: 'admin' };
async function getUserData() {
try {
const res = await fetch('/api/user');
return await res.json();
} finally {
console.log('Request completed');
}
}
// 正则表达式命名捕获组
const re = /(?<year>\d{4})-(?<month>\d{2})/;
"#;
// 3. 创建源代码文件
let fm = cm.new_source_file(FileName::Anon, es2018_code.to_string());
// 4. 创建词法分析器
let lexer = Lexer::new(
Syntax::Es(Default::default()), // 使用ECMAScript语法
Default::default(), // 使用默认的ECMAScript版本
StringInput::from(&*fm), // 源代码输入
None, // 不使用注释
);
// 5. 创建解析器并生成AST
let mut parser = Parser::new_from(lexer);
let module = parser.parse_module()
.expect("Failed to parse module");
// 6. 应用ES2018到ES5的转换
let transformed_module = module.fold_with(&mut es2018());
// 7. 配置代码生成器
let mut buf = Vec::new();
{
let mut emitter = swc_ecma_codegen::Emitter {
cfg: Default::default(), // 默认配置
cm: cm.clone(), // 源代码映射
comments: None, // 不保留注释
wr: Box::new(&mut buf), // 输出到缓冲区
};
// 8. 生成转换后的代码
emitter.emit_module(&transformed_module)
.expect("Failed to emit module");
}
// 9. 输出转换结果
let es5_code = String::from_utf8(buf)
.expect("Invalid UTF-8 output");
println!("转换后的ES5代码:\n{}", es5_code);
Ok(())
}
功能说明
swc_ecma_compat_es2018库提供了以下ES2018特性的降级转换:
- 异步函数(async/await)转换为生成器函数
- 对象展开运算符(…)转换为Object.assign调用
- 正则表达式命名捕获组
- 正则表达式后行断言
- Promise.prototype.finally
许可证
Apache-2.0
1 回复
Rust的swc_ecma_compat_es2018库:实现JavaScript ES2018到ES5的高效降级编译
介绍
swc_ecma_compat_es2018
是Rust生态中SWC(Super-fast Web Compiler)工具链的一部分,专门用于将符合ECMAScript 2018(ES9)标准的JavaScript代码转换为兼容ES5的代码。这个库提供了高效的AST转换能力,能够处理ES2018引入的新特性并将其降级为广泛兼容的ES5语法。
主要功能
- 将ES2018特性转换为ES5兼容代码
- 支持异步/await语法转换
- 处理对象展开运算符
- 转换正则表达式命名捕获组
- 处理Promise.finally等新API
- 保持源码映射(source map)支持
完整示例代码
// 引入必要的依赖
use swc_common::{sync::Lrc, SourceMap, FilePathMapping};
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, EsConfig};
use swc_ecma_compat_es2018::es2018;
use swc_ecma_transforms_base::resolver::resolver_with_mark;
use swc_ecma_visit::FoldWith;
use swc_ecma_codegen::{Emitter, Config};
fn main() {
// 1. 初始化源代码映射
let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
// 2. 准备要转换的ES2018代码
let code = r#"
// 使用ES2018特性的示例代码
const fetchUserData = async (userId) => {
try {
const response = await fetch(`/api/users/${userId}`);
const { name, age, ...details } = await response.json();
// 使用对象展开运算符
const userProfile = {
name,
...details,
timestamp: new Date()
};
return userProfile;
} catch (error) {
console.error('Fetch error:', error);
throw error;
} finally {
console.log('Fetch operation completed');
}
};
// 异步迭代示例
async function processItems(items) {
for await (const item of items) {
console.log('Processing item:', item);
}
}
"#;
// 3. 创建词法分析器
let lexer = Lexer::new(
Syntax::Es(EsConfig {
// 启用所有ECMAScript特性
..Default::default()
}),
Default::default(), // ECMAScript版本
StringInput::from(code), // 输入代码
None, // 可选注释
);
// 4. 创建解析器并生成AST
let mut parser = Parser::new_from(lexer);
let module = match parser.parse_module() {
Ok(module) => module,
Err(err) => {
eprintln!("解析错误: {}", err);
return;
}
};
// 5. 应用转换器
// 5.1 先应用作用域解析器
let module = module.fold_with(&mut resolver_with_mark(0));
// 5.2 应用ES2018到ES5的转换
let module = module.fold_with(&mut es2018());
// 6. 生成转换后的代码
let mut output = vec![];
{
let mut emitter = Emitter {
cfg: Config {
// 代码生成配置
..Default::default()
},
cm: cm.clone(), // 源代码映射
comments: None, // 可选注释
wr: Box::new(swc_ecma_codegen::text_writer::JsWriter::new(
cm.clone(),
"\n", // 换行符
&mut output, // 输出缓冲区
None, // 可选源映射配置
)),
};
if let Err(err) = emitter.emit_module(&module) {
eprintln!("代码生成错误: {}", err);
return;
}
}
// 7. 输出结果
match String::from_utf8(output) {
Ok(transformed_code) => {
println!("转换后的ES5代码:");
println!("{}", transformed_code);
}
Err(err) => {
eprintln!("UTF-8转换错误: {}", err);
}
}
}
代码说明
-
初始化阶段:
- 创建
SourceMap
用于跟踪源代码位置 - 准备包含ES2018特性的示例代码
- 创建
-
解析阶段:
- 使用
Lexer
进行词法分析 - 使用
Parser
生成AST抽象语法树
- 使用
-
转换阶段:
- 使用
resolver_with_mark
处理作用域 - 使用
es2018()
转换器将ES2018特性降级为ES5
- 使用
-
代码生成阶段:
- 使用
Emitter
将转换后的AST生成JavaScript代码 - 配置代码生成选项(缩进、换行等)
- 使用
-
输出结果:
- 将生成的ES5代码输出到控制台
处理的主要ES2018特性
-
async/await:
- 转换为基于Promise的等效实现
-
对象展开运算符:
- 转换为
Object.assign()
调用
- 转换为
-
异步迭代:
- 转换为生成器函数的实现
-
Promise.finally:
- 转换为等效的then/catch链
高级用法建议
- 批量处理:
use std::path::Path;
use swc::config::{IsModule, Options};
use swc::Compiler;
let compiler = Compiler::new(Arc::new(SourceMap::new(FilePathMapping::empty())));
let options = Options {
// 配置转换选项
..Default::default()
};
let files = vec![
Path::new("src/file1.js"),
Path::new("src/file2.js"),
// 更多文件...
];
for file in files {
let fm = compiler.cm.load_file(file).unwrap();
let program = compiler.parse_js(
fm,
&options,
).unwrap();
let output = compiler.transform(
program,
&options,
).unwrap();
// 处理输出...
}
- 自定义转换管道:
use swc_ecma_compat_es2018::es2018;
use swc_ecma_compat_es2015::es2015;
use swc_ecma_transforms_base::resolver;
let module = module
.fold_with(&mut resolver())
.fold_with(&mut es2018(
swc_ecma_compat_es2018::Config {
// 自定义配置
..Default::default()
}
))
.fold_with(&mut es2015());
这个完整示例展示了如何使用swc_ecma_compat_es2018
将现代JavaScript代码转换为广泛兼容的ES5代码,适用于需要支持旧浏览器的项目。