Skip to content

Commit

Permalink
Merge branch 'main' into feat/jest-no-test-return-statement
Browse files Browse the repository at this point in the history
  • Loading branch information
eryue0220 authored Jan 11, 2024
2 parents 7445e85 + d4acd14 commit e979f0c
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 9 deletions.
13 changes: 10 additions & 3 deletions crates/oxc_linter/src/rules/react/jsx_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ fn is_in_array_or_iter<'a, 'b>(

let mut is_outside_containing_function = false;
let mut is_explicit_return = false;
let mut is_arrow_expr_statement = false;

loop {
let Some(parent) = ctx.nodes().parent_node(node.id()) else {
Expand All @@ -92,7 +91,7 @@ fn is_in_array_or_iter<'a, 'b>(

match parent.kind() {
AstKind::ArrowExpression(arrow_expr) => {
is_arrow_expr_statement = matches!(
let is_arrow_expr_statement = matches!(
arrow_expr.body.statements.first(),
Some(Statement::ExpressionStatement(_))
);
Expand Down Expand Up @@ -126,7 +125,7 @@ fn is_in_array_or_iter<'a, 'b>(
is_outside_containing_function = true;
}
AstKind::ArrayExpression(_) => {
if is_arrow_expr_statement {
if is_outside_containing_function {
return None;
}

Expand Down Expand Up @@ -392,6 +391,14 @@ fn test() {
(Component) => <div><Component /></div>
];
",
r"
MyStory.decorators = [
(Component) => {
const store = useMyStore();
return <Provider store={store}><Component /></Provider>;
}
];
",
];

let fail = vec![
Expand Down
10 changes: 8 additions & 2 deletions crates/oxc_semantic/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,16 +183,22 @@ impl ScopeTree {
expr.gather(&mut |part| parts.push(part));
let name = parts.join("$");
let name = name.trim_start_matches('_');
self.generate_uid(name)
}

// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L495>
pub fn generate_uid(&self, name: &str) -> Atom {
for i in 0.. {
let name = Self::generate_uid(name, i);
let name = Self::internal_generate_uid(name, i);
if !self.has_binding(ScopeId::new(0), &name) {
return name;
}
}
unreachable!()
}

fn generate_uid(name: &str, i: i32) -> Atom {
// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L523>
fn internal_generate_uid(name: &str, i: i32) -> Atom {
Atom::from(if i > 1 { format!("_{name}{i}") } else { format!("_{name}") })
}
}
2 changes: 2 additions & 0 deletions crates/oxc_transformer/src/es2015/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ mod arrow_functions;
mod duplicate_keys;
mod function_name;
mod instanceof;
mod new_target;
mod shorthand_properties;
mod template_literals;

pub use arrow_functions::{ArrowFunctions, ArrowFunctionsOptions};
pub use duplicate_keys::DuplicateKeys;
pub use function_name::FunctionName;
pub use instanceof::Instanceof;
pub use new_target::NewTarget;
pub use shorthand_properties::ShorthandProperties;
pub use template_literals::TemplateLiterals;
133 changes: 133 additions & 0 deletions crates/oxc_transformer/src/es2015/new_target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use crate::{context::TransformerCtx, TransformOptions, TransformTarget};
use oxc_allocator::Vec;
use oxc_ast::{ast::*, AstBuilder, AstKind, VisitMut};
use oxc_diagnostics::miette;
use oxc_span::{Atom, Span, SPAN};
use oxc_syntax::operator::BinaryOperator;
use std::rc::Rc;

/// ES2015: New Target
///
/// References:
/// * <https://babel.dev/docs/babel-plugin-transform-template-new-target>
/// * <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-new-target>
pub struct NewTarget<'a> {
ast: Rc<AstBuilder<'a>>,
ctx: TransformerCtx<'a>,
kinds: Vec<'a, NewTargetKind>,
}

#[derive(Debug)]
enum NewTargetKind {
Method,
Constructor,
Function(Option<Atom>),
}

impl<'a> VisitMut<'a> for NewTarget<'a> {
fn enter_node(&mut self, kind: AstKind<'a>) {
if let Some(kind) = self.get_kind(kind) {
self.kinds.push(kind);
}
}

fn leave_node(&mut self, kind: AstKind<'a>) {
if self.get_kind(kind).is_some() {
self.kinds.pop();
}
}
}

impl<'a> NewTarget<'a> {
pub fn new(
ast: Rc<AstBuilder<'a>>,
ctx: TransformerCtx<'a>,
options: &TransformOptions,
) -> Option<Self> {
let kinds = ast.new_vec();
(options.target < TransformTarget::ES2015 || options.new_target).then(|| Self {
ast,
ctx,
kinds,
})
}

fn get_kind(&self, kind: AstKind<'a>) -> Option<NewTargetKind> {
match kind {
AstKind::MethodDefinition(def) => match def.kind {
MethodDefinitionKind::Get
| MethodDefinitionKind::Set
| MethodDefinitionKind::Method => Some(NewTargetKind::Method),
MethodDefinitionKind::Constructor => Some(NewTargetKind::Constructor),
},
AstKind::ObjectProperty(property) => property.method.then_some(NewTargetKind::Method),
AstKind::Function(function) => {
// oxc visitor `MethodDefinitionKind` will enter `Function` node, here need to exclude it
if let Some(kind) = self.kinds.last() {
if !matches!(kind, NewTargetKind::Function(_)) {
return None;
}
}
function.id.as_ref().map(|id| NewTargetKind::Function(Some(id.name.clone())))
}
_ => None,
}
}

fn create_constructor_expr(&self, span: Span) -> Expression<'a> {
self.ast.static_member_expression(
span,
self.ast.this_expression(span),
IdentifierName { span, name: "constructor".into() },
false,
)
}

pub fn transform_expression<'b>(&mut self, expr: &'b mut Expression<'a>) {
if let Expression::MetaProperty(meta) = expr {
if meta.meta.name == "new" && meta.property.name == "target" {
if let Some(kind) = self.kinds.last() {
match kind {
NewTargetKind::Constructor => {
*expr = self.create_constructor_expr(meta.span);
}
NewTargetKind::Method => {
*expr = self.ast.void_0();
}
NewTargetKind::Function(name) => {
// TODO packages/babel-helper-create-class-features-plugin/src/fields.ts#L192 unshadow
// It will mutate previous ast node, it is difficult at now.
let id = name
.clone()
.unwrap_or_else(|| self.ctx.scopes().generate_uid("target"));
let test = self.ast.binary_expression(
SPAN,
self.ast.this_expression(SPAN),
BinaryOperator::Instanceof,
self.ast.identifier_reference_expression(IdentifierReference::new(
SPAN, id,
)),
);
let consequent = self.ast.static_member_expression(
SPAN,
self.ast.this_expression(SPAN),
IdentifierName { span: SPAN, name: "constructor".into() },
false,
);
*expr = self.ast.conditional_expression(
meta.span,
test,
consequent,
self.ast.void_0(),
);
}
}
} else {
self.ctx.error(miette::Error::msg(
"new.target must be under a (non-arrow) function or a class.",
));
}
}
}
}
}
17 changes: 15 additions & 2 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use std::{cell::RefCell, rc::Rc, sync::Arc};

use es2015::TemplateLiterals;
use oxc_allocator::Allocator;
use oxc_ast::{ast::*, AstBuilder, VisitMut};
use oxc_ast::{ast::*, AstBuilder, AstKind, VisitMut};
use oxc_diagnostics::Error;
use oxc_semantic::Semantic;
use oxc_span::SourceType;
Expand Down Expand Up @@ -69,6 +69,7 @@ pub struct Transformer<'a> {
es2015_template_literals: Option<TemplateLiterals<'a>>,
es2015_duplicate_keys: Option<DuplicateKeys<'a>>,
es2015_instanceof: Option<Instanceof<'a>>,
es2015_new_target: Option<NewTarget<'a>>,
es3_property_literal: Option<PropertyLiteral<'a>>,
}

Expand Down Expand Up @@ -108,6 +109,7 @@ impl<'a> Transformer<'a> {
es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options),
es2015_duplicate_keys: DuplicateKeys::new(Rc::clone(&ast), &options),
es2015_instanceof: Instanceof::new(Rc::clone(&ast), ctx.clone(), &options),
es2015_new_target: NewTarget::new(Rc::clone(&ast),ctx.clone(), &options),
// other
es3_property_literal: PropertyLiteral::new(Rc::clone(&ast), &options),
react_jsx: ReactJsx::new(Rc::clone(&ast), ctx.clone(), options)
Expand All @@ -134,6 +136,14 @@ impl<'a> Transformer<'a> {
}

impl<'a> VisitMut<'a> for Transformer<'a> {
fn enter_node(&mut self, kind: oxc_ast::AstKind<'a>) {
self.es2015_new_target.as_mut().map(|t| t.enter_node(kind));
}

fn leave_node(&mut self, kind: oxc_ast::AstKind<'a>) {
self.es2015_new_target.as_mut().map(|t| t.leave_node(kind));
}

fn visit_program(&mut self, program: &mut Program<'a>) {
for directive in program.directives.iter_mut() {
self.visit_directive(directive);
Expand Down Expand Up @@ -184,6 +194,7 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
self.es2015_instanceof.as_mut().map(|t| t.transform_expression(expr));
self.es2016_exponentiation_operator.as_mut().map(|t| t.transform_expression(expr));
self.es2015_template_literals.as_mut().map(|t| t.transform_expression(expr));
self.es2015_new_target.as_mut().map(|t| t.transform_expression(expr));

self.visit_expression_match(expr);
}
Expand All @@ -210,12 +221,14 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
self.es2015_shorthand_properties.as_mut().map(|t| t.transform_object_property(prop));
self.es3_property_literal.as_mut().map(|t| t.transform_object_property(prop));

let kind = AstKind::ObjectProperty(self.alloc(prop));
self.enter_node(kind);
self.visit_property_key(&mut prop.key);
self.visit_expression(&mut prop.value);

if let Some(init) = &mut prop.init {
self.visit_expression(init);
}
self.leave_node(kind);
}

fn visit_class_body(&mut self, class_body: &mut ClassBody<'a>) {
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_transformer/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub struct TransformOptions {
pub property_literals: bool,
pub babel_8_breaking: Option<bool>,
pub instanceof: bool,
pub new_target: bool,
}

/// See <https://www.typescriptlang.org/tsconfig#target>
Expand Down
10 changes: 9 additions & 1 deletion tasks/transform_conformance/babel.snap.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Passed: 295/1171
Passed: 297/1179

# All Passed:
* babel-plugin-transform-numeric-separator
Expand Down Expand Up @@ -824,6 +824,14 @@ Passed: 295/1171
# babel-plugin-transform-duplicate-keys (7/8)
* combination/dupes/input.js

# babel-plugin-transform-new-target (2/8)
* general/arrow/input.js
* general/class-properties/input.js
* general/class-properties-loose/input.js
* general/function/input.js
* general/function-duplicate-name/input.js
* general/object/input.js

# babel-plugin-transform-typescript (66/158)
* class/abstract-class-decorated/input.ts
* class/abstract-class-decorated-method/input.ts
Expand Down
6 changes: 5 additions & 1 deletion tasks/transform_conformance/babel_exec.snap.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Passed: 379/445
Passed: 386/454

# All Passed:
* babel-plugin-transform-class-static-block
Expand Down Expand Up @@ -98,3 +98,7 @@ Passed: 379/445
# babel-plugin-transform-instanceof (0/1)
* instanceof/instanceof/exec.js

# babel-plugin-transform-new-target (7/9)
* general/class-properties/exec.js
* general/class-properties-loose/exec.js

1 change: 1 addition & 0 deletions tasks/transform_conformance/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const CASES: &[&str] = &[
"babel-plugin-transform-template-literals",
"babel-plugin-transform-duplicate-keys",
"babel-plugin-transform-instanceof",
"babel-plugin-transform-new-target",
// ES3
"babel-plugin-transform-property-literals",
// TypeScript
Expand Down
1 change: 1 addition & 0 deletions tasks/transform_conformance/src/test_case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ pub trait TestCase {
template_literals: options.get_plugin("transform-template-literals").is_some(),
property_literals: options.get_plugin("transform-property-literals").is_some(),
duplicate_keys: options.get_plugin("transform-duplicate-keys").is_some(),
new_target: options.get_plugin("transform-new-target").is_some(),
}
}

Expand Down

0 comments on commit e979f0c

Please sign in to comment.