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特性的降级转换:

  1. 异步函数(async/await)转换为生成器函数
  2. 对象展开运算符(…)转换为Object.assign调用
  3. 正则表达式命名捕获组
  4. 正则表达式后行断言
  5. 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特性的降级转换:

  1. 异步函数(async/await)转换为生成器函数
  2. 对象展开运算符(…)转换为Object.assign调用
  3. 正则表达式命名捕获组
  4. 正则表达式后行断言
  5. 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);
        }
    }
}

代码说明

  1. 初始化阶段:

    • 创建SourceMap用于跟踪源代码位置
    • 准备包含ES2018特性的示例代码
  2. 解析阶段:

    • 使用Lexer进行词法分析
    • 使用Parser生成AST抽象语法树
  3. 转换阶段:

    • 使用resolver_with_mark处理作用域
    • 使用es2018()转换器将ES2018特性降级为ES5
  4. 代码生成阶段:

    • 使用Emitter将转换后的AST生成JavaScript代码
    • 配置代码生成选项(缩进、换行等)
  5. 输出结果:

    • 将生成的ES5代码输出到控制台

处理的主要ES2018特性

  1. async/await:

    • 转换为基于Promise的等效实现
  2. 对象展开运算符:

    • 转换为Object.assign()调用
  3. 异步迭代:

    • 转换为生成器函数的实现
  4. Promise.finally:

    • 转换为等效的then/catch链

高级用法建议

  1. 批量处理:
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();
    
    // 处理输出...
}
  1. 自定义转换管道:
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代码,适用于需要支持旧浏览器的项目。

回到顶部