Rust ECMAScript兼容库swc_ecma_compat_common的使用,支持JavaScript/TypeScript语法转换和代码兼容性处理
Rust ECMAScript兼容库swc_ecma_compat_common的使用,支持JavaScript/TypeScript语法转换和代码兼容性处理
安装
在项目目录中运行以下Cargo命令:
cargo add swc_ecma_compat_common
或者在Cargo.toml中添加以下行:
swc_ecma_compat_common = "20.0.0"
使用示例
以下是一个完整的示例,展示如何使用swc_ecma_compat_common进行JavaScript/TypeScript代码转换:
use swc_common::{SourceMap, FileName};
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsConfig};
use swc_ecma_compat_common::resolver;
use swc_ecma_transforms_base::resolver as resolver_impl;
use swc_ecma_visit::FoldWith;
fn main() {
// 创建源代码映射
let cm = SourceMap::default();
// 示例TypeScript代码
let ts_code = r#"
interface User {
name: string;
age: number;
}
const greet = (user: User) => {
console.log(`Hello ${user.name}!`);
};
"#;
// 创建输入源
let fm = cm.new_source_file(FileName::Anon, ts_code.to_string());
// 配置TypeScript语法
let syntax = Syntax::Typescript(TsConfig {
tsx: false,
decorators: false,
dts: false,
no_early_errors: false,
});
// 创建词法分析器
let lexer = Lexer::new(
syntax,
Default::default(),
StringInput::from(&*fm),
None,
);
// 创建解析器
let mut parser = Parser::new_from(lexer);
// 解析代码为AST
let module = parser.parse_module().unwrap();
// 应用作用域解析器
let module = module.fold_with(&mut resolver_impl(resolver()));
// 在这里可以添加其他转换步骤...
// 输出处理后的AST
println!("{:#?}", module);
}
完整示例代码
以下是一个更完整的示例,展示如何使用swc_ecma_compat_common进行代码转换并生成输出:
use swc_common::{SourceMap, FileName, sync::Lrc};
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsConfig};
use swc_ecma_compat_common::resolver;
use swc_ecma_transforms_base::resolver as resolver_impl;
use swc_ecma_visit::FoldWith;
use swc_ecma_codegen::{text_writer::JsWriter, Emitter, Config};
fn main() {
// 1. 创建源代码映射
let cm: Lrc<SourceMap> = Default::default();
// 2. 准备TypeScript源代码
let ts_code = r#"
interface Person {
firstName: string;
lastName: string;
}
function sayHello(person: Person) {
return `Hello, ${person.firstName} ${person.lastName}!`;
}
const user = { firstName: "John", lastName: "Doe" };
console.log(sayHello(user));
"#;
// 3. 创建源文件
let fm = cm.new_source_file(FileName::Anon, ts_code.to_string());
// 4. 配置TypeScript解析选项
let syntax = Syntax::Typescript(TsConfig {
tsx: false,
decorators: true,
dts: false,
no_early_errors: false,
});
// 5. 创建词法分析器
let lexer = Lexer::new(
syntax,
Default::default(), // Es版本
StringInput::from(&*fm),
None, // 可选的注释收集器
);
// 6. 创建解析器并解析为AST
let mut parser = Parser::new_from(lexer);
let module = parser.parse_module().expect("解析失败");
// 7. 应用作用域解析器
let module = module.fold_with(&mut resolver_impl(resolver()));
// 8. 这里可以添加其他转换步骤
// 例如: module = module.fold_with(&mut some_other_transform());
// 9. 将AST转换回代码
let mut buf = vec![];
{
let mut emitter = Emitter {
cfg: Config::default(),
cm: cm.clone(),
wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
comments: None,
};
emitter.emit_module(&module).expect("代码生成失败");
}
// 10. 输出结果
let output_code = String::from_utf8(buf).expect("无效的UTF-8输出");
println!("转换后的代码:\n{}", output_code);
}
功能说明
swc_ecma_compat_common是SWC工具链中的一个核心库,主要用于:
- 提供ECMAScript兼容性处理的基础设施
- 支持JavaScript和TypeScript语法的转换
- 处理代码的作用域解析
- 提供通用的转换工具
许可证
Apache-2.0
1 回复
Rust ECMAScript兼容库swc_ecma_compat_common使用指南
概述
swc_ecma_compat_common是Rust生态中一个强大的ECMAScript兼容库,属于SWC(Speedy Web Compiler)项目的一部分。它专门用于处理JavaScript/TypeScript语法转换和代码兼容性问题,帮助开发者将现代JS/TS代码转换为兼容旧版环境的代码。
主要功能
- 语法转换:将ES6+语法转换为ES5及以下版本
- TypeScript支持:处理TypeScript特有的语法
- 代码兼容性处理:解决不同环境下的API差异问题
- 模块系统转换:支持CommonJS/ES模块互转
安装方法
在Cargo.toml中添加依赖:
[dependencies]
swc_ecma_compat_common = "0.91"
基本使用方法
1. 简单语法转换示例
use swc_ecma_compat_common::es2015;
use swc_ecma_parser::{Syntax, EsConfig};
use swc_common::{SourceMap, FileName};
let cm = SourceMap::default();
let fm = cm.new_source_file(
FileName::Anon,
"const fn = (a, b) => a + b;".into()
);
let mut parser = swc_ecma_parser::Parser::new(
Syntax::Es(EsConfig {
..Default::default()
}),
fm,
None
);
let module = parser.parse_module().unwrap();
let mut pass = es2015::arrow();
let transformed = pass.fold_module(module);
// 转换后的代码将把箭头函数转为普通函数
2. 完整的ES6转ES5示例
use swc_ecma_compat_common::{es2015, es2016, es2017, es2018, es2019, es2020};
use swc_ecma_transforms::pass::Pass;
let passes: Vec<Box<dyn Pass>> = vec![
// 处理箭头函数
Box::new(es2015::arrow()),
// 处理类
Box::new(es2015::classes()),
// 处理解构
Box::new(es2015::destructuring()),
// 处理参数默认值
Box::new(es2015::parameters()),
// 处理展开运算符
Box::new(es2015::spread()),
// 处理模板字符串
Box::new(es2015::template_literal()),
// 处理let/const
Box::new(es2015::block_scoping()),
// 处理async/await
Box::new(es2017::async_to_generator()),
];
// 对AST应用所有转换
let mut transformed = module;
for pass in passes {
transformed = pass.fold_module(transformed);
}
3. TypeScript转换示例
use swc_ecma_compat_common::typescript;
use swc_ecma_parser::{Syntax, TsConfig};
let ts_code = r#"
interface User {
name: string;
age: number;
}
class Admin implements User {
constructor(public name: string, public age: number) {}
}
"#;
let cm = SourceMap::default();
let fm = cm.new_source_file(FileName::Anon, ts_code.into());
let mut parser = swc_ecma_parser::Parser::new(
Syntax::Typescript(TsConfig {
..Default::default()
}),
fm,
None
);
let module = parser.parse_module().unwrap();
let mut pass = typescript::strip();
let transformed = pass.fold_module(module);
// 转换后的代码将移除TypeScript特有的语法
高级配置
自定义转换选项
use swc_ecma_compat_common::es2015::classes;
let class_pass = classes::classes(classes::Config {
// 保留类装饰器
decorators: true,
// 保留类属性
class_properties: true,
// 保留静态类属性
static_blocks: true,
});
处理JSX
use swc_ecma_compat_common::react;
let react_code = r#"const App = () => <div>Hello</div>;"#;
// ...解析代码...
let mut pass = react::jsx(
react::Options {
pragma: Some("React.createElement".into()),
pragma_frag: Some("React.Fragment".into()),
..Default::default()
},
cm.clone(),
);
let transformed = pass.fold_module(module);
// JSX将被转换为React.createElement调用
性能优化建议
- 批量处理:尽量一次性处理多个文件,减少重复初始化开销
- 缓存AST:如果可能,缓存解析后的AST以避免重复解析
- 并行处理:利用Rust的并行能力处理多个文件
常见问题解决
1. 如何处理特定浏览器兼容性?
use swc_ecma_compat_common::compat::browser;
let mut pass = browser::browser(
browser::Config {
targets: "> 0.5%, last 2 versions, Firefox ESR, not dead".into(),
..Default::default()
},
cm.clone(),
);
2. 如何保留源码映射(source map)?
use swc_common::GLOBALS;
use swc_ecma_codegen::{text_writer::JsWriter, Config};
let mut buf = vec![];
let mut emitter = swc_ecma_codegen::Emitter {
cfg: Config::default(),
cm: cm.clone(),
wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
};
GLOBALS.set(&Default::default(), || {
emitter.emit_module(&transformed).unwrap();
});
let code = String::from_utf8(buf).unwrap();
// code包含转换后的代码
完整示例代码
下面是一个完整的ES6转ES5的示例,包含了代码解析、转换和输出的完整流程:
use swc_common::{SourceMap, FileName, GLOBALS};
use swc_ecma_compat_common::{es2015, es2017};
use swc_ecma_parser::{Syntax, EsConfig};
use swc_ecma_transforms::pass::Pass;
use swc_ecma_codegen::{text_writer::JsWriter, Config, Emitter};
fn main() {
// 初始化源代码映射
let cm = SourceMap::default();
// 创建源代码文件
let fm = cm.new_source_file(
FileName::Anon,
r#"
const sum = (a, b) => a + b;
class Calculator {
constructor() {
this.value = 0;
}
async add(num) {
this.value += await num;
return this.value;
}
}
"#.into()
);
// 创建解析器
let mut parser = swc_ecma_parser::Parser::new(
Syntax::Es(EsConfig {
..Default::default()
}),
fm,
None
);
// 解析模块
let module = parser.parse_module().unwrap();
// 准备转换器
let passes: Vec<Box<dyn Pass>> = vec![
Box::new(es2015::arrow()), // 转换箭头函数
Box::new(es2015::classes()), // 转换类
Box::new(es2015::block_scoping()), // 转换let/const
Box::new(es2017::async_to_generator()), // 转换async/await
];
// 应用所有转换
let mut transformed = module;
for pass in passes {
transformed = pass.fold_module(transformed);
}
// 生成转换后的代码
let mut buf = vec![];
{
let mut emitter = Emitter {
cfg: Config::default(),
cm: cm.clone(),
wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
};
GLOBALS.set(&Default::default(), || {
emitter.emit_module(&transformed).unwrap();
});
}
let code = String::from_utf8(buf).unwrap();
println!("转换后的代码:\n{}", code);
}
总结
swc_ecma_compat_common提供了强大的JavaScript/TypeScript转换能力,是Rust生态中处理前端代码兼容性问题的首选工具。通过灵活的配置和丰富的转换选项,可以满足各种代码转换需求,同时保持出色的性能表现。