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工具链中的一个核心库,主要用于:

  1. 提供ECMAScript兼容性处理的基础设施
  2. 支持JavaScript和TypeScript语法的转换
  3. 处理代码的作用域解析
  4. 提供通用的转换工具

许可证

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调用

性能优化建议

  1. 批量处理:尽量一次性处理多个文件,减少重复初始化开销
  2. 缓存AST:如果可能,缓存解析后的AST以避免重复解析
  3. 并行处理:利用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生态中处理前端代码兼容性问题的首选工具。通过灵活的配置和丰富的转换选项,可以满足各种代码转换需求,同时保持出色的性能表现。

回到顶部