这个仓库提供一个 Rust 实现的 ECMAScript 语法兼容性分析器。它读取 JavaScript 文件,识别当前代码里实际出现的语法特性,并根据内置的 MDN Browser Compat Data 规则判断这些语法在目标运行时中是否可用。
当前定位是 syntax compatibility,不是完整的 JavaScript 运行时兼容性扫描。因此它会检测 ?.、??、class fields、ESM import/export 等语法事实;不会检测 Promise.any()、Array.prototype.at()、Object.hasOwn() 这类运行时 API。
- 分析 JavaScript 构建产物或源码文件。
- 自动发现
sourceMappingURL,并支持相邻.map文件回退。 - 支持本地文件 Source Map 和 data URI Source Map。
- 报告保留 generated 文件位置;Source Map 成功时额外提供 original source 位置。
- 兼容性数据来自手工校对后的内置静态表,当前同步来源是 MDN Browser Compat Data。
不覆盖的场景:
- 运行时 API 兼容性。
- 被构建工具转换掉的原始语法。detector 只能看到输入文件中仍然存在的语法。
- TypeScript 类型语法。输入应当是可被 JavaScript parser 解析的代码。
- HTTP Source Map、Source Map response header、index source map、多级 Source Map 链路。
当前 detector 共识别 42 种语法特性。表中的“特性标识”与
SyntaxFeatureId 枚举一一对应,是兼容性规则查询和诊断输出使用的稳定标识。
| 分类 | 特性标识 | 语法示例 |
|---|---|---|
| 运算符与表达式 | OptionalChaining |
object?.property |
| 运算符与表达式 | NullishCoalescing |
value ?? fallback |
| 运算符与表达式 | LogicalAndAssignment |
value &&= next |
| 运算符与表达式 | LogicalOrAssignment |
value ||= next |
| 运算符与表达式 | NullishCoalescingAssignment |
value ??= fallback |
| 运算符与表达式 | DynamicImport |
import("./module.js") |
| 运算符与表达式 | ImportMeta |
import.meta.url |
| 运算符与表达式 | Await |
await task |
| 运算符与表达式 | PrivateClassFieldIn |
#value in object |
| 字面量 | BigIntLiteral |
1n |
| 字面量 | TemplateLiteral |
`hello ${name}` |
| 字面量 | NumericSeparator |
1_000 |
| 函数与参数 | ArrowFunction |
value => value + 1 |
| 函数与参数 | AsyncFunction |
async function run() {} |
| 函数与参数 | GeneratorFunction |
function* values() {} |
| 函数与参数 | AsyncGeneratorFunction |
async function* values() {} |
| 函数与参数 | DefaultParameter |
function run(value = 1) {} |
| 函数与参数 | RestParameter |
function run(...values) {} |
| 方法 | MethodDefinition |
{ run() {} } 或 class C { run() {} } |
| 方法 | AsyncMethod |
{ async run() {} } |
| 方法 | AsyncGeneratorMethod |
{ async *run() {} } |
| 方法 | ShorthandObjectMethod |
{ run() {} } |
| Class | Class |
class Example {} |
| Class | PublicClassField |
class C { value = 1 } |
| Class | PrivateClassField |
class C { #value = 1 } |
| Class | PrivateClassMethod |
class C { #run() {} } |
| Class | ClassStaticInitializationBlock |
class C { static {} } |
| 控制流 | ForOf |
for (const item of items) {} |
| 控制流 | ForAwaitOf |
for await (const item of items) {} |
| 控制流 | OptionalCatchBinding |
try {} catch {} |
| 展开与解构 | Spread |
fn(...args) 或 [...items] |
| 展开与解构 | ObjectSpreadProperty |
{ ...source } |
| 展开与解构 | Destructuring |
const { value } = source |
| 展开与解构 | ArrayRestDestructuring |
const [head, ...tail] = values |
| 展开与解构 | ObjectRestDestructuring |
const { value, ...rest } = source |
| 对象字面量 | ComputedObjectPropertyName |
{ [key]: value } |
| 对象字面量 | ShorthandObjectProperty |
{ value } |
| ESM | ImportStatement |
import value from "pkg" |
| ESM | ExportStatement |
export { value } |
| ESM | ExportDefaultStatement |
export default value |
| ESM | ExportNamespaceStatement |
export * as ns from "pkg" |
| ESM | ImportAttribute |
import data from "./x.json" with { type: "json" } |
一个语法节点可能产生多条特性记录。例如 async () => {} 会同时记录
ArrowFunction 和 AsyncFunction,export default value 会同时记录
ExportStatement 和 ExportDefaultStatement。
cargo run -p ecmascript_compatibility -- <generated-js-file> <target> [target...]示例:
cargo run -p ecmascript_compatibility -- dist/app.js "chrome 60" "safari 13"CLI 会输出:
- 输入文件和解析后的 targets。
- Source Map 文件级状态。
- Unsupported、Mixed、Unknown 诊断。
- 每条诊断的 generated 位置和可用的 original source 位置。
最常用入口是 CompatAnalyzer::analyze_path:
use ecmascript_compatibility::CompatAnalyzer;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let analyzer = CompatAnalyzer::new();
let targets = analyzer.resolve_targets(["chrome 60", "safari 13"])?;
let report = analyzer.analyze_path("dist/app.js", &targets)?;
for diagnostic in report.diagnostics() {
println!(
"{:?} at {:?}",
diagnostic.feature(),
diagnostic.position()
);
}
Ok(())
}也可以运行仓库内示例:
cargo run -p ecmascript_compatibility --example analyze_file -- dist/app.js "chrome 60"更完整的 API 说明见 docs/api-usage.md。
仓库提供 napi-rs binding 包。JS 侧调用方负责决定要分析哪些文件, binding 只接收明确文件路径列表并在 native worker 中批量分析:
const { checkFileList } = require("@shined/ecma-compat");
const report = await checkFileList(["dist/app.js", "dist/chunk.js"], ["chrome 60", "safari 13"], {
cwd: process.cwd(),
});
console.log(report.counts.reportedFiles);
console.log(report.counts.diagnostics);checkFileList 返回 Promise,并会在 native worker 中并行分析文件。需要限制 worker 数时可以传 parallelism。
默认只返回有诊断的文件;需要保留空诊断文件报告时可以传 includeEmptyReports: true。
本地构建 binding:
pnpm --filter @shined/ecma-compat buildMDN 数据同步脚本只生成 SyntaxFeatureId 实际引用的条目,不把完整 JavaScript BCD 表写进源码:
node scripts/sync_mdn_bcd.js同步后需要人工检查生成表和语法特性映射是否符合当前项目边界。
详细流程见 docs/mdn-data-sync.md。
cargo fmt --all
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
pnpm --filter @shined/ecma-compat build