mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(transformer/react-jsx): throw the pragma and pragmaFrag cannot be set when runtime is automatic error (#1196)
close: #1194 Here's a rough implementation of my idea of throwing an error.
This commit is contained in:
parent
b65094b995
commit
8c624abf9c
12 changed files with 149 additions and 46 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1855,6 +1855,7 @@ dependencies = [
|
|||
"mimalloc",
|
||||
"oxc_allocator",
|
||||
"oxc_codegen",
|
||||
"oxc_diagnostics",
|
||||
"oxc_parser",
|
||||
"oxc_semantic",
|
||||
"oxc_span",
|
||||
|
|
@ -1873,6 +1874,7 @@ dependencies = [
|
|||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
"oxc_codegen",
|
||||
"oxc_diagnostics",
|
||||
"oxc_parser",
|
||||
"oxc_semantic",
|
||||
"oxc_span",
|
||||
|
|
|
|||
|
|
@ -16,11 +16,12 @@ categories.workspace = true
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
oxc_ast = { workspace = true }
|
||||
oxc_span = { workspace = true }
|
||||
oxc_allocator = { workspace = true }
|
||||
oxc_syntax = { workspace = true }
|
||||
oxc_semantic = { workspace = true }
|
||||
oxc_ast = { workspace = true }
|
||||
oxc_span = { workspace = true }
|
||||
oxc_allocator = { workspace = true }
|
||||
oxc_syntax = { workspace = true }
|
||||
oxc_semantic = { workspace = true }
|
||||
oxc_diagnostics = { workspace = true }
|
||||
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ fn main() {
|
|||
}),
|
||||
..TransformOptions::default()
|
||||
};
|
||||
Transformer::new(&allocator, source_type, semantic, transform_options).build(program);
|
||||
Transformer::new(&allocator, source_type, semantic, transform_options).build(program).unwrap();
|
||||
|
||||
let printed = Codegen::<false>::new(source_text.len(), codegen_options).build(program);
|
||||
println!("Transformed:\n");
|
||||
println!("{printed}");
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use std::{
|
||||
cell::{Ref, RefCell, RefMut},
|
||||
mem,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use oxc_ast::AstBuilder;
|
||||
use oxc_diagnostics::Error;
|
||||
use oxc_semantic::{ScopeId, ScopeTree, Semantic, SymbolId, SymbolTable};
|
||||
use oxc_span::Atom;
|
||||
|
||||
|
|
@ -11,11 +13,12 @@ use oxc_span::Atom;
|
|||
pub struct TransformerCtx<'a> {
|
||||
pub ast: Rc<AstBuilder<'a>>,
|
||||
semantic: Rc<RefCell<Semantic<'a>>>,
|
||||
errors: Rc<RefCell<Vec<Error>>>,
|
||||
}
|
||||
|
||||
impl<'a> TransformerCtx<'a> {
|
||||
pub fn new(ast: Rc<AstBuilder<'a>>, semantic: Rc<RefCell<Semantic<'a>>>) -> Self {
|
||||
Self { ast, semantic }
|
||||
Self { ast, semantic, errors: Rc::new(RefCell::new(vec![])) }
|
||||
}
|
||||
|
||||
pub fn semantic(&self) -> Ref<'_, Semantic<'a>> {
|
||||
|
|
@ -38,4 +41,13 @@ impl<'a> TransformerCtx<'a> {
|
|||
// TODO: use the correct scope and symbol id
|
||||
self.scopes_mut().add_binding(ScopeId::new(0), name, SymbolId::new(0));
|
||||
}
|
||||
|
||||
pub fn errors(&self) -> Vec<Error> {
|
||||
mem::take(&mut self.errors.borrow_mut())
|
||||
}
|
||||
|
||||
/// Push a Transform Error
|
||||
pub fn error<T: Into<Error>>(&mut self, error: T) {
|
||||
self.errors.borrow_mut().push(error.into());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,12 @@ mod tester;
|
|||
mod typescript;
|
||||
mod utils;
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||
|
||||
use es2015::TemplateLiterals;
|
||||
use oxc_allocator::{Allocator, Vec};
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_ast::{ast::*, AstBuilder, VisitMut};
|
||||
use oxc_diagnostics::Error;
|
||||
use oxc_semantic::Semantic;
|
||||
use oxc_span::SourceType;
|
||||
|
||||
|
|
@ -44,6 +45,7 @@ pub use crate::{
|
|||
};
|
||||
|
||||
pub struct Transformer<'a> {
|
||||
ctx: TransformerCtx<'a>,
|
||||
#[allow(unused)]
|
||||
typescript: Option<TypeScript<'a>>,
|
||||
react_jsx: Option<ReactJsx<'a>>,
|
||||
|
|
@ -76,7 +78,9 @@ impl<'a> Transformer<'a> {
|
|||
Rc::clone(&ast),
|
||||
Rc::new(RefCell::new(semantic)),
|
||||
);
|
||||
|
||||
Self {
|
||||
ctx: ctx.clone(),
|
||||
// TODO: pass verbatim_module_syntax from user config
|
||||
typescript: source_type.is_typescript().then(|| TypeScript::new(Rc::clone(&ast), ctx.clone(), false)),
|
||||
regexp_flags: RegexpFlags::new(Rc::clone(&ast), &options),
|
||||
|
|
@ -91,8 +95,22 @@ impl<'a> Transformer<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn build(mut self, program: &mut Program<'a>) {
|
||||
/// # Errors
|
||||
/// Returns `Vec<Error>` if any errors were collected during the transformation.
|
||||
pub fn build(mut self, program: &mut Program<'a>) -> Result<(), Vec<Error>> {
|
||||
self.visit_program(program);
|
||||
let errors: Vec<_> = self
|
||||
.ctx
|
||||
.errors()
|
||||
.into_iter()
|
||||
.map(|e| e.with_source_code(Arc::new(self.ctx.semantic().source_text().to_string())))
|
||||
.collect();
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +126,7 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
|
|||
self.react_jsx.as_mut().map(|t| t.add_react_jsx_runtime_imports(program));
|
||||
}
|
||||
|
||||
fn visit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>) {
|
||||
fn visit_statements(&mut self, stmts: &mut oxc_allocator::Vec<'a, Statement<'a>>) {
|
||||
for stmt in stmts.iter_mut() {
|
||||
self.visit_statement(stmt);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ use std::rc::Rc;
|
|||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::{ast::*, AstBuilder};
|
||||
use oxc_diagnostics::{
|
||||
miette::{self, Diagnostic},
|
||||
thiserror::Error,
|
||||
};
|
||||
use oxc_span::{Atom, SPAN};
|
||||
use oxc_syntax::{
|
||||
identifier::{is_irregular_whitespace, is_line_terminator},
|
||||
|
|
@ -13,6 +17,11 @@ use oxc_syntax::{
|
|||
pub use self::options::{ReactJsxOptions, ReactJsxRuntime};
|
||||
use crate::context::TransformerCtx;
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[error("pragma and pragmaFrag cannot be set when runtime is automatic.")]
|
||||
#[diagnostic(severity(warning), help("Remove `pragma` and `pragmaFrag` options."))]
|
||||
struct PragmaAndPragmaFragCannotBeSet;
|
||||
|
||||
/// Transform React JSX
|
||||
///
|
||||
/// References:
|
||||
|
|
@ -20,7 +29,7 @@ use crate::context::TransformerCtx;
|
|||
/// * <https://github.com/babel/babel/tree/main/packages/babel-helper-builder-react-jsx>
|
||||
pub struct ReactJsx<'a> {
|
||||
ast: Rc<AstBuilder<'a>>,
|
||||
ctx: Rc<TransformerCtx<'a>>,
|
||||
ctx: TransformerCtx<'a>,
|
||||
options: ReactJsxOptions,
|
||||
|
||||
imports: Vec<'a, Statement<'a>>,
|
||||
|
|
@ -83,7 +92,6 @@ impl<'a> ReactJsx<'a> {
|
|||
Atom::from(format!("{}/jsx-runtime", options.import_source))
|
||||
};
|
||||
|
||||
let ctx = Rc::new(ctx);
|
||||
Self {
|
||||
ast,
|
||||
ctx,
|
||||
|
|
@ -114,6 +122,14 @@ impl<'a> ReactJsx<'a> {
|
|||
if self.options.runtime.is_classic() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.options.pragma != "React.createElement"
|
||||
|| self.options.pragma_frag != "React.Fragment"
|
||||
{
|
||||
self.ctx.error(PragmaAndPragmaFragCannotBeSet);
|
||||
return;
|
||||
}
|
||||
|
||||
let imports = self.ast.move_statement_vec(&mut self.imports);
|
||||
let index = program
|
||||
.body
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use oxc_allocator::Allocator;
|
||||
use oxc_codegen::{Codegen, CodegenOptions};
|
||||
use oxc_diagnostics::Error;
|
||||
use oxc_parser::Parser;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
use oxc_span::SourceType;
|
||||
|
|
@ -22,19 +23,19 @@ impl Tester {
|
|||
|
||||
pub fn test(&self, tests: &[(&str, &str)]) {
|
||||
for (source_text, expected) in tests {
|
||||
let transformed = self.transform(source_text);
|
||||
let transformed = self.transform(source_text).unwrap();
|
||||
let expected = self.codegen(expected);
|
||||
assert_eq!(transformed, expected, "{source_text}");
|
||||
}
|
||||
}
|
||||
|
||||
fn transform(&self, source_text: &str) -> String {
|
||||
fn transform(&self, source_text: &str) -> Result<std::string::String, std::vec::Vec<Error>> {
|
||||
let program = Parser::new(&self.allocator, source_text, self.source_type).parse().program;
|
||||
let semantic = SemanticBuilder::new(source_text, self.source_type).build(&program).semantic;
|
||||
let program = self.allocator.alloc(program);
|
||||
Transformer::new(&self.allocator, self.source_type, semantic, self.options.clone())
|
||||
.build(program);
|
||||
Codegen::<false>::new(source_text.len(), CodegenOptions).build(program)
|
||||
.build(program)
|
||||
.map(move |()| Codegen::<false>::new(source_text.len(), CodegenOptions).build(program))
|
||||
}
|
||||
|
||||
fn codegen(&self, source_text: &str) -> String {
|
||||
|
|
|
|||
|
|
@ -215,7 +215,11 @@ impl Oxc {
|
|||
let semantic = SemanticBuilder::new(source_text, source_type).build(program).semantic;
|
||||
let options =
|
||||
TransformOptions { target: TransformTarget::ES2015, ..TransformOptions::default() };
|
||||
Transformer::new(&allocator, source_type, semantic, options).build(program);
|
||||
let result =
|
||||
Transformer::new(&allocator, source_type, semantic, options).build(program);
|
||||
if let Err(errs) = result {
|
||||
self.save_diagnostics(errs);
|
||||
}
|
||||
}
|
||||
|
||||
let program = allocator.alloc(program);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ fn bench_transformer(criterion: &mut Criterion) {
|
|||
let program = allocator.alloc(program);
|
||||
let transform_options = TransformOptions::default();
|
||||
Transformer::new(&allocator, source_type, semantic, transform_options)
|
||||
.build(black_box(program));
|
||||
.build(black_box(program))
|
||||
.unwrap();
|
||||
allocator
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ oxc_semantic = { workspace = true }
|
|||
oxc_codegen = { workspace = true }
|
||||
oxc_transformer = { workspace = true }
|
||||
oxc_tasks_common = { workspace = true }
|
||||
oxc_diagnostics = { workspace = true }
|
||||
|
||||
serde_json = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Passed: 283/1113
|
||||
Passed: 263/1113
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-numeric-separator
|
||||
|
|
@ -14,7 +14,7 @@ Passed: 283/1113
|
|||
* transform-u/basic/input.js
|
||||
* transform-u/string-properties/input.js
|
||||
|
||||
# babel-plugin-transform-class-properties (7/266)
|
||||
# babel-plugin-transform-class-properties (0/266)
|
||||
* assumption-constantSuper/complex-super-class/input.js
|
||||
* assumption-constantSuper/instance-field/input.js
|
||||
* assumption-constantSuper/static-field/input.js
|
||||
|
|
@ -56,6 +56,7 @@ Passed: 283/1113
|
|||
* decorators-legacy-interop/local-define-property/input.js
|
||||
* decorators-legacy-interop/loose/input.js
|
||||
* decorators-legacy-interop/strict/input.js
|
||||
* decorators-legacy-interop/wrong-order/input.js
|
||||
* nested-class/super-call-in-decorator/input.js
|
||||
* nested-class/super-call-in-key/input.js
|
||||
* nested-class/super-property-in-accessor-key/input.js
|
||||
|
|
@ -203,6 +204,7 @@ Passed: 283/1113
|
|||
* private-loose/super-statement/input.js
|
||||
* private-loose/update/input.js
|
||||
* public/arrow-static-this-without-transform/input.js
|
||||
* public/arrow-this-without-transform/input.js
|
||||
* public/assignment/input.js
|
||||
* public/call/input.js
|
||||
* public/class-shadow-builtins/input.mjs
|
||||
|
|
@ -236,11 +238,16 @@ Passed: 283/1113
|
|||
* public/static-this/input.js
|
||||
* public/static-undefined/input.js
|
||||
* public/super-call/input.js
|
||||
* public/super-destructuring-array-pattern/input.js
|
||||
* public/super-destructuring-array-pattern-1/input.js
|
||||
* public/super-destructuring-object-pattern/input.js
|
||||
* public/super-destructuring-object-pattern-1/input.js
|
||||
* public/super-expression/input.js
|
||||
* public/super-statement/input.js
|
||||
* public/super-with-collision/input.js
|
||||
* public/update/input.js
|
||||
* public-loose/arrow-static-this-without-transform/input.js
|
||||
* public-loose/arrow-this-without-transform/input.js
|
||||
* public-loose/class-shadow-builtins/input.mjs
|
||||
* public-loose/computed/input.js
|
||||
* public-loose/constructor-collision/input.js
|
||||
|
|
@ -292,7 +299,7 @@ Passed: 283/1113
|
|||
* integration-loose/preserve-comments/input.js
|
||||
* integration-loose/super-static-block/input.js
|
||||
|
||||
# babel-plugin-transform-private-methods (7/136)
|
||||
# babel-plugin-transform-private-methods (0/136)
|
||||
* accessors/basic/input.js
|
||||
* accessors/get-only-setter/input.js
|
||||
* accessors/preserve-comments/input.js
|
||||
|
|
@ -318,8 +325,15 @@ Passed: 283/1113
|
|||
* accessors-privateFieldsAsSymbols/set-only-getter/input.js
|
||||
* accessors-privateFieldsAsSymbols/updates/input.js
|
||||
* assumption-constantSuper/private-method-super/input.js
|
||||
* duplicated-names/get-get/input.js
|
||||
* duplicated-names/get-method/input.js
|
||||
* duplicated-names/get-set/input.js
|
||||
* duplicated-names/method-get/input.js
|
||||
* duplicated-names/method-method/input.js
|
||||
* duplicated-names/method-set/input.js
|
||||
* duplicated-names/set-get/input.js
|
||||
* duplicated-names/set-method/input.js
|
||||
* duplicated-names/set-set/input.js
|
||||
* private-method/assignment/input.js
|
||||
* private-method/async/input.js
|
||||
* private-method/before-fields/input.js
|
||||
|
|
@ -493,7 +507,7 @@ Passed: 283/1113
|
|||
* export-namespace/namespace-string/input.mjs
|
||||
* export-namespace/namespace-typescript/input.mjs
|
||||
|
||||
# babel-plugin-transform-dynamic-import (2/19)
|
||||
# babel-plugin-transform-dynamic-import (0/19)
|
||||
* amd/missing-plugin/input.mjs
|
||||
* amd/module/input.mjs
|
||||
* amd/no-interop/input.js
|
||||
|
|
@ -507,6 +521,8 @@ Passed: 283/1113
|
|||
* commonjs/shadowed-require/input.js
|
||||
* commonjs/template-literal/input.js
|
||||
* commonjs/to-string/input.js
|
||||
* missing-module-transform/missing-module-transform/input.js
|
||||
* systemjs/missing-plugin/input.mjs
|
||||
* systemjs/missing-plugin-babel-7/input.mjs
|
||||
* systemjs/module/input.mjs
|
||||
* systemjs/script/input.js
|
||||
|
|
@ -516,7 +532,7 @@ Passed: 283/1113
|
|||
* assumption-noDocumentAll/transform-in-default-param/input.js
|
||||
* nullish-coalescing/transform-in-default-param/input.js
|
||||
|
||||
# babel-plugin-transform-optional-chaining (4/46)
|
||||
# babel-plugin-transform-optional-chaining (1/46)
|
||||
* assumption-noDocumentAll/assignment/input.js
|
||||
* assumption-noDocumentAll/cast-to-boolean/input.js
|
||||
* assumption-noDocumentAll/in-function-params/input.js
|
||||
|
|
@ -538,6 +554,9 @@ Passed: 283/1113
|
|||
* general/in-method-key/input.js
|
||||
* general/in-method-key-loose/input.js
|
||||
* general/in-var-destructuring/input.js
|
||||
* general/lhs-assignment/input.js
|
||||
* general/lhs-assignment-read-and-update/input.js
|
||||
* general/lhs-update/input.js
|
||||
* general/member-access/input.js
|
||||
* general/memoize/input.js
|
||||
* general/memoize-loose/input.js
|
||||
|
|
@ -726,13 +745,14 @@ Passed: 283/1113
|
|||
* loose/ignoreToPrimitiveHint/input.js
|
||||
* loose/mutableTemplateObject/input.js
|
||||
|
||||
# babel-plugin-transform-typescript (84/181)
|
||||
# babel-plugin-transform-typescript (76/181)
|
||||
* class/abstract-class-decorated/input.ts
|
||||
* class/abstract-class-decorated-method/input.ts
|
||||
* class/abstract-class-decorated-parameter/input.ts
|
||||
* class/accessor-allowDeclareFields-false/input.ts
|
||||
* class/accessor-allowDeclareFields-true/input.ts
|
||||
* class/accessor-allowDeclareFields-true-babel-7/input.ts
|
||||
* class/declare-not-enabled-babel-7/input.ts
|
||||
* class/decorated-declare-properties/input.ts
|
||||
* class/field-not-initialized-babel-7/input.ts
|
||||
* class/parameter-properties/input.ts
|
||||
|
|
@ -742,6 +762,7 @@ Passed: 283/1113
|
|||
* class/parameter-properties-with-parameters/input.ts
|
||||
* class/parameter-properties-with-super/input.ts
|
||||
* class/private-method-override-transform-private/input.ts
|
||||
* class/transform-properties-declare-wrong-order/input.ts
|
||||
* class/uninitialized-definite-with-declare-disabled-babel-7/input.ts
|
||||
* declarations/erased/input.ts
|
||||
* declarations/export-declare-enum/input.ts
|
||||
|
|
@ -764,6 +785,7 @@ Passed: 283/1113
|
|||
* exports/declared-types/input.ts
|
||||
* exports/export-const-enums/input.ts
|
||||
* exports/export-type-star-from/input.ts
|
||||
* exports/export=/input.ts
|
||||
* exports/export=-to-cjs/input.ts
|
||||
* exports/imported-types/input.ts
|
||||
* exports/imported-types-only-remove-type-imports/input.ts
|
||||
|
|
@ -782,6 +804,7 @@ Passed: 283/1113
|
|||
* imports/import-named-type-default-and-named/input.ts
|
||||
* imports/import-removed-exceptions/input.ts
|
||||
* imports/import=-declaration/input.ts
|
||||
* imports/import=-module/input.ts
|
||||
* imports/import=-module-to-cjs/input.ts
|
||||
* imports/only-remove-type-imports/input.ts
|
||||
* imports/parameter-decorators/input.ts
|
||||
|
|
@ -791,6 +814,8 @@ Passed: 283/1113
|
|||
* imports/type-only-import-specifier-3/input.ts
|
||||
* imports/type-only-import-specifier-4/input.ts
|
||||
* namespace/alias/input.ts
|
||||
* namespace/ambient-module-nested/input.ts
|
||||
* namespace/ambient-module-nested-exported/input.ts
|
||||
* namespace/canonical/input.ts
|
||||
* namespace/clobber-class/input.ts
|
||||
* namespace/clobber-enum/input.ts
|
||||
|
|
@ -803,6 +828,8 @@ Passed: 283/1113
|
|||
* namespace/module-nested/input.ts
|
||||
* namespace/module-nested-export/input.ts
|
||||
* namespace/multiple/input.ts
|
||||
* namespace/mutable-fail/input.ts
|
||||
* namespace/namespace-flag/input.ts
|
||||
* namespace/namespace-nested-module/input.ts
|
||||
* namespace/nested/input.ts
|
||||
* namespace/nested-destructuring/input.ts
|
||||
|
|
@ -825,16 +852,10 @@ Passed: 283/1113
|
|||
* regression/11061/input.mjs
|
||||
* variable-declaration/non-null-in-optional-chain/input.ts
|
||||
|
||||
# babel-plugin-transform-react-jsx (121/170)
|
||||
# babel-plugin-transform-react-jsx (128/170)
|
||||
* autoImport/after-polyfills-compiled-to-cjs/input.mjs
|
||||
* autoImport/auto-import-react-source-type-script/input.js
|
||||
* autoImport/complicated-scope-module/input.js
|
||||
* pure/false-pragma-comment-automatic-runtime/input.js
|
||||
* pure/false-pragma-option-automatic-runtime/input.js
|
||||
* pure/true-pragma-comment-automatic-runtime/input.js
|
||||
* pure/true-pragma-option-automatic-runtime/input.js
|
||||
* pure/unset-pragma-comment-automatic-runtime/input.js
|
||||
* pure/unset-pragma-option-automatic-runtime/input.js
|
||||
* react/adds-appropriate-newlines-when-using-spread-attribute-babel-7/input.js
|
||||
* react/arrow-functions/input.js
|
||||
* react/assignment-babel-7/input.js
|
||||
|
|
@ -863,7 +884,6 @@ Passed: 283/1113
|
|||
* react-automatic/should-handle-attributed-elements/input.js
|
||||
* react-automatic/should-throw-error-namespaces-if-not-flag/input.js
|
||||
* react-automatic/should-throw-when-filter-is-specified/input.js
|
||||
* react-automatic/should-warn-when-pragma-or-pragmaFrag-is-set/input.js
|
||||
* regression/issue-12478-automatic/input.js
|
||||
* regression/issue-12478-classic/input.js
|
||||
* removed-options/invalid-use-builtins-false/input.js
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::{
|
|||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_codegen::{Codegen, CodegenOptions};
|
||||
use oxc_diagnostics::Error;
|
||||
use oxc_parser::Parser;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
use oxc_span::{SourceType, VALID_EXTENSIONS};
|
||||
|
|
@ -134,7 +135,7 @@ pub trait TestCase {
|
|||
false
|
||||
}
|
||||
|
||||
fn transform(&self, path: &Path) -> String {
|
||||
fn transform(&self, path: &Path) -> Result<String, Vec<Error>> {
|
||||
let allocator = Allocator::default();
|
||||
let source_text = fs::read_to_string(path).unwrap();
|
||||
let source_type = SourceType::from_path(path).unwrap();
|
||||
|
|
@ -146,9 +147,12 @@ pub trait TestCase {
|
|||
.semantic;
|
||||
let transformed_program = allocator.alloc(ret.program);
|
||||
|
||||
Transformer::new(&allocator, source_type, semantic, self.transform_options())
|
||||
let result = Transformer::new(&allocator, source_type, semantic, self.transform_options())
|
||||
.build(transformed_program);
|
||||
Codegen::<false>::new(source_text.len(), CodegenOptions).build(transformed_program)
|
||||
|
||||
result.map(|()| {
|
||||
Codegen::<false>::new(source_text.len(), CodegenOptions).build(transformed_program)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,13 +211,28 @@ impl TestCase for ConformanceTestCase {
|
|||
.build(&ret.program)
|
||||
.semantic;
|
||||
let program = allocator.alloc(ret.program);
|
||||
Transformer::new(&allocator, source_type, semantic, self.transform_options())
|
||||
.build(program);
|
||||
let transformed_code = Codegen::<false>::new(input.len(), CodegenOptions).build(program);
|
||||
let transform_options = self.transform_options();
|
||||
let transformer =
|
||||
Transformer::new(&allocator, source_type, semantic, transform_options.clone());
|
||||
|
||||
let mut transformed_code = String::new();
|
||||
let mut actual_errors = String::new();
|
||||
let result = transformer.build(program);
|
||||
if result.is_ok() {
|
||||
transformed_code = Codegen::<false>::new(input.len(), CodegenOptions).build(program);
|
||||
} else {
|
||||
actual_errors =
|
||||
result.err().unwrap().iter().map(std::string::ToString::to_string).collect();
|
||||
}
|
||||
|
||||
let babel_options = self.options();
|
||||
|
||||
// Get output.js by using our codeg so code comparison can match.
|
||||
let output = output_path.and_then(|path| fs::read_to_string(path).ok()).map_or_else(
|
||||
|| {
|
||||
if let Some(throws) = &babel_options.throws {
|
||||
return throws.to_string();
|
||||
}
|
||||
// The transformation should be equal to input.js If output.js does not exist.
|
||||
let program = Parser::new(&allocator, &input, source_type).parse().program;
|
||||
Codegen::<false>::new(input.len(), CodegenOptions).build(&program)
|
||||
|
|
@ -225,16 +244,23 @@ impl TestCase for ConformanceTestCase {
|
|||
},
|
||||
);
|
||||
|
||||
let passed = transformed_code == output;
|
||||
let passed = transformed_code == output || actual_errors.contains(&output);
|
||||
if filtered {
|
||||
println!("Input:\n");
|
||||
println!("{input}\n");
|
||||
println!("Options:");
|
||||
println!("{:?}\n", self.transform_options());
|
||||
println!("Expected:\n");
|
||||
println!("{output}\n");
|
||||
println!("Transformed:\n");
|
||||
println!("{transformed_code}\n");
|
||||
println!("{transform_options:?}\n");
|
||||
if babel_options.throws.is_some() {
|
||||
println!("Expected Errors:\n");
|
||||
println!("{output}\n");
|
||||
println!("Actual Errors:\n");
|
||||
println!("{actual_errors}\n");
|
||||
} else {
|
||||
println!("Expected:\n");
|
||||
println!("{output}\n");
|
||||
println!("Transformed:\n");
|
||||
println!("{transformed_code}");
|
||||
}
|
||||
println!("Passed: {passed}");
|
||||
}
|
||||
passed
|
||||
|
|
@ -291,7 +317,7 @@ impl TestCase for ExecTestCase {
|
|||
}
|
||||
|
||||
fn test(&self, filtered: bool) -> bool {
|
||||
let result = self.transform(&self.path);
|
||||
let result = self.transform(&self.path).expect("Transform failed");
|
||||
let target_path = self.write_to_test_files(&result);
|
||||
let passed = Self::run_test(&target_path);
|
||||
if filtered {
|
||||
|
|
|
|||
Loading…
Reference in a new issue