Skip to content

Commit 2e1f1a2

Browse files
committed
feat: parser support jsx
1 parent 59a0c65 commit 2e1f1a2

File tree

19 files changed

+516
-22
lines changed

19 files changed

+516
-22
lines changed

crates/node_binding/napi-binding.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2333,6 +2333,11 @@ export interface RawJavascriptParserOptions {
23332333
* @experimental
23342334
*/
23352335
typeReexportsPresence?: string
2336+
/**
2337+
* This option is experimental in Rspack only and subject to change or be removed anytime.
2338+
* @experimental
2339+
*/
2340+
jsx?: boolean
23362341
}
23372342

23382343
export interface RawJsonGeneratorOptions {

crates/rspack/src/builder/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,6 +1716,7 @@ impl ModuleOptionsBuilder {
17161716
import_dynamic: Some(true),
17171717
commonjs_magic_comments: Some(false),
17181718
inline_const: Some(false),
1719+
jsx: Some(false),
17191720
..Default::default()
17201721
}),
17211722
);

crates/rspack/tests/snapshots/defaults__default_options.snap

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,6 +1444,9 @@ CompilerOptions {
14441444
inline_const: Some(
14451445
false,
14461446
),
1447+
jsx: Some(
1448+
false,
1449+
),
14471450
},
14481451
),
14491452
},

crates/rspack_binding_api/src/raw_options/raw_module/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ pub struct RawJavascriptParserOptions {
298298
/// This option is experimental in Rspack only and subject to change or be removed anytime.
299299
/// @experimental
300300
pub type_reexports_presence: Option<String>,
301+
/// This option is experimental in Rspack only and subject to change or be removed anytime.
302+
/// @experimental
303+
pub jsx: Option<bool>,
301304
}
302305

303306
impl From<RawJavascriptParserOptions> for JavascriptParserOptions {
@@ -344,6 +347,7 @@ impl From<RawJavascriptParserOptions> for JavascriptParserOptions {
344347
import_dynamic: value.import_dynamic,
345348
commonjs_magic_comments: value.commonjs_magic_comments,
346349
inline_const: value.inline_const,
350+
jsx: value.jsx,
347351
}
348352
}
349353
}

crates/rspack_core/src/concatenated_module.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use swc_core::{
3030
ecma::{
3131
ast::{EsVersion, Program},
3232
atoms::Atom,
33-
parser::{Syntax, parse_file_as_module},
33+
parser::{EsSyntax, Syntax, parse_file_as_module},
3434
transforms::base::resolver,
3535
},
3636
};
@@ -47,13 +47,13 @@ use crate::{
4747
IdentCollector, InitFragment, InitFragmentStage, LibIdentOptions,
4848
MaybeDynamicTargetExportInfoHashKey, Module, ModuleArgument, ModuleGraph,
4949
ModuleGraphCacheArtifact, ModuleGraphConnection, ModuleIdentifier, ModuleLayer,
50-
ModuleStaticCacheArtifact, ModuleType, NAMESPACE_OBJECT_EXPORT, PrefetchExportsInfoMode, Resolve,
51-
RuntimeCondition, RuntimeGlobals, RuntimeSpec, SourceType, UsageState, UsedName, UsedNameItem,
52-
define_es_module_flag_statement, escape_identifier, filter_runtime, get_runtime_key,
53-
impl_source_map_config, merge_runtime_condition, merge_runtime_condition_non_false,
54-
module_update_hash, property_access, property_name, reserved_names::RESERVED_NAMES,
55-
returning_function, runtime_condition_expression, subtract_runtime_condition,
56-
to_identifier_with_escaped, to_normal_comment,
50+
ModuleStaticCacheArtifact, ModuleType, NAMESPACE_OBJECT_EXPORT, ParserOptions,
51+
PrefetchExportsInfoMode, Resolve, RuntimeCondition, RuntimeGlobals, RuntimeSpec, SourceType,
52+
UsageState, UsedName, UsedNameItem, define_es_module_flag_statement, escape_identifier,
53+
filter_runtime, get_runtime_key, impl_source_map_config, merge_runtime_condition,
54+
merge_runtime_condition_non_false, module_update_hash, property_access, property_name,
55+
reserved_names::RESERVED_NAMES, returning_function, runtime_condition_expression,
56+
subtract_runtime_condition, to_identifier_with_escaped, to_normal_comment,
5757
};
5858

5959
type ExportsDefinitionArgs = Vec<(String, String)>;
@@ -1993,10 +1993,24 @@ impl ConcatenatedModule {
19931993
let comments = SwcComments::default();
19941994
let mut module_info = concatenation_scope.current_module;
19951995

1996+
let jsx = module
1997+
.as_ref()
1998+
.as_normal_module()
1999+
.and_then(|normal_module| normal_module.get_parser_options())
2000+
.and_then(|options: &ParserOptions| {
2001+
options
2002+
.get_javascript()
2003+
.and_then(|js_options| js_options.jsx)
2004+
})
2005+
.unwrap_or(false);
2006+
19962007
let mut errors = vec![];
19972008
let program = match parse_file_as_module(
19982009
&fm,
1999-
Syntax::default(),
2010+
Syntax::Es(EsSyntax {
2011+
jsx,
2012+
..Default::default()
2013+
}),
20002014
EsVersion::EsNext,
20012015
Some(&comments),
20022016
&mut errors,

crates/rspack_core/src/options/module.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ pub struct JavascriptParserOptions {
284284
pub import_dynamic: Option<bool>,
285285
pub commonjs_magic_comments: Option<bool>,
286286
pub inline_const: Option<bool>,
287+
pub jsx: Option<bool>,
287288
}
288289

289290
#[cacheable]

crates/rspack_plugin_javascript/src/parser_and_generator/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,15 @@ impl ParserAndGenerator for JavaScriptParserAndGenerator {
179179
);
180180
let comments = SwcComments::default();
181181
let target = ast::EsVersion::EsNext;
182+
183+
let jsx = module_parser_options
184+
.and_then(|options| options.get_javascript())
185+
.and_then(|options| options.jsx)
186+
.unwrap_or(false);
187+
182188
let parser_lexer = Lexer::new(
183189
Syntax::Es(EsSyntax {
190+
jsx,
184191
allow_return_outside_function: matches!(
185192
module_type,
186193
ModuleType::JsDynamic | ModuleType::JsAuto

crates/rspack_plugin_javascript/src/visitors/dependency/parser/walk.rs

Lines changed: 126 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ use swc_core::{
77
ArrayLit, ArrayPat, ArrowExpr, AssignExpr, AssignPat, AssignTarget, AssignTargetPat, AwaitExpr,
88
BinExpr, BlockStmt, BlockStmtOrExpr, CallExpr, Callee, CatchClause, Class, ClassExpr,
99
ClassMember, CondExpr, DefaultDecl, DoWhileStmt, ExportDefaultDecl, Expr, ExprOrSpread,
10-
ExprStmt, FnExpr, ForHead, ForInStmt, ForOfStmt, ForStmt, Function, GetterProp, Ident, IfStmt,
11-
KeyValueProp, LabeledStmt, MemberExpr, MemberProp, MetaPropExpr, ModuleDecl, ModuleItem,
12-
NewExpr, ObjectLit, ObjectPat, ObjectPatProp, OptCall, OptChainExpr, Param, Pat, Prop,
13-
PropName, PropOrSpread, RestPat, ReturnStmt, SeqExpr, SetterProp, SimpleAssignTarget, Stmt,
14-
SwitchCase, SwitchStmt, TaggedTpl, ThisExpr, ThrowStmt, Tpl, TryStmt, UnaryExpr, UnaryOp,
10+
ExprStmt, FnExpr, ForHead, ForInStmt, ForOfStmt, ForStmt, Function, GetterProp, Ident,
11+
IdentName, IfStmt, JSXAttr, JSXAttrOrSpread, JSXAttrValue, JSXElement, JSXElementChild,
12+
JSXElementName, JSXExpr, JSXExprContainer, JSXFragment, JSXMemberExpr, JSXNamespacedName,
13+
JSXObject, KeyValueProp, LabeledStmt, MemberExpr, MemberProp, MetaPropExpr, ModuleDecl,
14+
ModuleItem, NewExpr, ObjectLit, ObjectPat, ObjectPatProp, OptCall, OptChainExpr, Param, Pat,
15+
Prop, PropName, PropOrSpread, RestPat, ReturnStmt, SeqExpr, SetterProp, SimpleAssignTarget,
16+
Stmt, SwitchCase, SwitchStmt, TaggedTpl, ThisExpr, ThrowStmt, Tpl, TryStmt, UnaryExpr, UnaryOp,
1517
UpdateExpr, VarDeclOrExpr, WhileStmt, WithStmt, YieldExpr,
1618
},
1719
};
@@ -431,12 +433,18 @@ impl JavascriptParser<'_> {
431433
Expr::Update(expr) => self.walk_update_expression(expr),
432434
Expr::Yield(expr) => self.walk_yield_expression(expr),
433435
Expr::SuperProp(_) | Expr::Lit(_) | Expr::PrivateName(_) | Expr::Invalid(_) => (),
436+
Expr::JSXMember(_) | Expr::JSXNamespacedName(_) | Expr::JSXEmpty(_) => {
437+
self.ensure_jsx_enabled();
438+
}
439+
Expr::JSXElement(element) => {
440+
self.ensure_jsx_enabled();
441+
self.walk_jsx_element(element);
442+
}
443+
Expr::JSXFragment(fragment) => {
444+
self.ensure_jsx_enabled();
445+
self.walk_jsx_fragment(fragment);
446+
}
434447
Expr::Paren(_)
435-
| Expr::JSXMember(_)
436-
| Expr::JSXNamespacedName(_)
437-
| Expr::JSXEmpty(_)
438-
| Expr::JSXElement(_)
439-
| Expr::JSXFragment(_)
440448
| Expr::TsTypeAssertion(_)
441449
| Expr::TsConstAssertion(_)
442450
| Expr::TsNonNull(_)
@@ -516,6 +524,114 @@ impl JavascriptParser<'_> {
516524
}
517525
}
518526

527+
fn ensure_jsx_enabled(&self) {
528+
if !self.javascript_options.jsx.unwrap_or_default() {
529+
unreachable!();
530+
}
531+
}
532+
533+
fn walk_jsx_element(&mut self, element: &JSXElement) {
534+
self.walk_jsx_element_name(&element.opening.name);
535+
if element.opening.type_args.is_some() {
536+
// JSX type arguments only exist in TSX; this walker assumes pure JSX input.
537+
unreachable!();
538+
}
539+
for attr in &element.opening.attrs {
540+
self.walk_jsx_attr_or_spread(attr);
541+
}
542+
for child in &element.children {
543+
self.walk_jsx_child(child);
544+
}
545+
if let Some(closing) = &element.closing {
546+
self.walk_jsx_element_name(&closing.name);
547+
}
548+
}
549+
550+
fn walk_jsx_fragment(&mut self, fragment: &JSXFragment) {
551+
for child in &fragment.children {
552+
self.walk_jsx_child(child);
553+
}
554+
}
555+
556+
fn walk_jsx_child(&mut self, child: &JSXElementChild) {
557+
match child {
558+
JSXElementChild::JSXElement(element) => self.walk_jsx_element(element),
559+
JSXElementChild::JSXFragment(fragment) => self.walk_jsx_fragment(fragment),
560+
JSXElementChild::JSXExprContainer(container) => self.walk_jsx_expr_container(container),
561+
JSXElementChild::JSXSpreadChild(spread) => self.walk_expression(&spread.expr),
562+
JSXElementChild::JSXText(_) => (),
563+
}
564+
}
565+
566+
fn walk_jsx_expr_container(&mut self, container: &JSXExprContainer) {
567+
match &container.expr {
568+
JSXExpr::Expr(expr) => self.walk_expression(expr),
569+
JSXExpr::JSXEmptyExpr(_) => (),
570+
}
571+
}
572+
573+
fn walk_jsx_attr_or_spread(&mut self, attr: &JSXAttrOrSpread) {
574+
match attr {
575+
JSXAttrOrSpread::JSXAttr(attr) => self.walk_jsx_attr(attr),
576+
JSXAttrOrSpread::SpreadElement(spread) => self.walk_expression(&spread.expr),
577+
}
578+
}
579+
580+
fn walk_jsx_attr(&mut self, attr: &JSXAttr) {
581+
if let Some(value) = &attr.value {
582+
self.walk_jsx_attr_value(value);
583+
}
584+
}
585+
586+
fn walk_jsx_attr_value(&mut self, value: &JSXAttrValue) {
587+
match value {
588+
JSXAttrValue::Lit(_) => (),
589+
JSXAttrValue::JSXExprContainer(container) => self.walk_jsx_expr_container(container),
590+
JSXAttrValue::JSXElement(element) => self.walk_jsx_element(element),
591+
JSXAttrValue::JSXFragment(fragment) => self.walk_jsx_fragment(fragment),
592+
}
593+
}
594+
595+
fn walk_jsx_element_name(&mut self, name: &JSXElementName) {
596+
match name {
597+
JSXElementName::Ident(ident) => self.walk_identifier(ident),
598+
JSXElementName::JSXMemberExpr(member) => self.walk_jsx_member_expr(member),
599+
JSXElementName::JSXNamespacedName(namespaced) => self.walk_jsx_namespaced_name(namespaced),
600+
}
601+
}
602+
603+
fn walk_jsx_member_expr(&mut self, member: &JSXMemberExpr) {
604+
let member_expr = Self::jsx_member_expr_to_member_expr(member);
605+
self.walk_member_expression(&member_expr);
606+
}
607+
608+
fn jsx_member_expr_to_member_expr(member: &JSXMemberExpr) -> MemberExpr {
609+
MemberExpr {
610+
span: member.span,
611+
obj: Box::new(Self::jsx_object_to_expr(&member.obj)),
612+
prop: MemberProp::Ident(member.prop.clone()),
613+
}
614+
}
615+
616+
fn jsx_object_to_expr(obj: &JSXObject) -> Expr {
617+
match obj {
618+
JSXObject::Ident(ident) => Expr::Ident(ident.clone()),
619+
JSXObject::JSXMemberExpr(member) => {
620+
Expr::Member(Self::jsx_member_expr_to_member_expr(member))
621+
}
622+
}
623+
}
624+
625+
fn walk_jsx_namespaced_name(&mut self, name: &JSXNamespacedName) {
626+
self.walk_ident_name(&name.ns);
627+
self.walk_ident_name(&name.name);
628+
}
629+
630+
fn walk_ident_name(&mut self, name: &IdentName) {
631+
let ident: Ident = name.clone().into();
632+
self.walk_identifier(&ident);
633+
}
634+
519635
fn walk_object_expression(&mut self, expr: &ObjectLit) {
520636
for prop in &expr.props {
521637
self.walk_property_or_spread(prop);

packages/rspack/etc/core.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3380,6 +3380,7 @@ export type JavascriptParserOptions = {
33803380
commonjsMagicComments?: boolean;
33813381
inlineConst?: boolean;
33823382
typeReexportsPresence?: "no-tolerant" | "tolerant" | "tolerant-no-check";
3383+
jsx?: boolean;
33833384
};
33843385

33853386
// @public (undocumented)

packages/rspack/src/config/adapter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,8 @@ function getRawJavascriptParserOptions(
586586
importDynamic: parser.importDynamic,
587587
commonjsMagicComments: parser.commonjsMagicComments,
588588
inlineConst: parser.inlineConst,
589-
typeReexportsPresence: parser.typeReexportsPresence
589+
typeReexportsPresence: parser.typeReexportsPresence,
590+
jsx: parser.jsx
590591
};
591592
}
592593

0 commit comments

Comments
 (0)