From f40f59bd707a9d21d8eb41e42b5c6a1c95f0bb7e Mon Sep 17 00:00:00 2001 From: melbourne2991 <4619280+melbourne2991@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:25:46 +1100 Subject: [PATCH] fix(es/typescript): Fix panic on invalid jsx pragma (#8513) **Description:** Currently a jsx pragma with an invalid js identifier (eg, with dashes: `@jsx bad-pragma` causes a panic. This PR prevents a panic and will ignore an invalid pragma in a comment. (There may be an argument for showing an error or warning instead, but given a jsx pragma is still valid ES I'm not sure whether that makes sense?) [Original (Deno) issue here](https://github.com/denoland/deno/issues/21927) --- .../src/typescript.rs | 26 +++++++++---------- .../tests/strip.rs/ts_jsx_bad_pragma.js | 1 + .../tests/strip.rs | 24 +++++++++++++++++ 3 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 crates/swc_ecma_transforms_typescript/tests/__swc_snapshots__/tests/strip.rs/ts_jsx_bad_pragma.js diff --git a/crates/swc_ecma_transforms_typescript/src/typescript.rs b/crates/swc_ecma_transforms_typescript/src/typescript.rs index 4b0d15f71695..4c4c43351858 100644 --- a/crates/swc_ecma_transforms_typescript/src/typescript.rs +++ b/crates/swc_ecma_transforms_typescript/src/typescript.rs @@ -122,14 +122,12 @@ where /// Get an [Id] which will used by expression. /// /// For `React#1.createElement`, this returns `React#1`. -fn id_for_jsx(e: &Expr) -> Id { +fn id_for_jsx(e: &Expr) -> Option { match e { - Expr::Ident(i) => i.to_id(), - Expr::Member(MemberExpr { obj, .. }) => id_for_jsx(obj), - Expr::Lit(Lit::Null(..)) => ("null".into(), Default::default()), - _ => { - panic!("failed to determine top-level Id for jsx expression") - } + Expr::Ident(i) => Some(i.to_id()), + Expr::Member(MemberExpr { obj, .. }) => Some(id_for_jsx(obj)).flatten(), + Expr::Lit(Lit::Null(..)) => Some(("null".into(), Default::default())), + _ => None, } } @@ -175,8 +173,8 @@ where self.top_level_mark, ); - let pragma_id = id_for_jsx(&pragma); - let pragma_frag_id = id_for_jsx(&pragma_frag); + let pragma_id = id_for_jsx(&pragma).unwrap(); + let pragma_frag_id = id_for_jsx(&pragma_frag).unwrap(); self.id_usage.insert(pragma_id); self.id_usage.insert(pragma_frag_id); @@ -199,13 +197,15 @@ where }); if let Some(pragma) = pragma { - let pragma_id = id_for_jsx(&pragma); - self.id_usage.insert(pragma_id); + if let Some(pragma_id) = id_for_jsx(&pragma) { + self.id_usage.insert(pragma_id); + } } if let Some(pragma_frag) = pragma_frag { - let pragma_frag_id = id_for_jsx(&pragma_frag); - self.id_usage.insert(pragma_frag_id); + if let Some(pragma_frag_id) = id_for_jsx(&pragma_frag) { + self.id_usage.insert(pragma_frag_id); + } } } } diff --git a/crates/swc_ecma_transforms_typescript/tests/__swc_snapshots__/tests/strip.rs/ts_jsx_bad_pragma.js b/crates/swc_ecma_transforms_typescript/tests/__swc_snapshots__/tests/strip.rs/ts_jsx_bad_pragma.js new file mode 100644 index 000000000000..01582554515c --- /dev/null +++ b/crates/swc_ecma_transforms_typescript/tests/__swc_snapshots__/tests/strip.rs/ts_jsx_bad_pragma.js @@ -0,0 +1 @@ +/** @jsx bad-pragma */ diff --git a/crates/swc_ecma_transforms_typescript/tests/strip.rs b/crates/swc_ecma_transforms_typescript/tests/strip.rs index 8f548df1f597..4a1d8fb4ca1b 100644 --- a/crates/swc_ecma_transforms_typescript/tests/strip.rs +++ b/crates/swc_ecma_transforms_typescript/tests/strip.rs @@ -2797,3 +2797,27 @@ test!( console.log(I.A); "# ); + +test!( + Syntax::Typescript(TsConfig::default()), + |t| { + let unresolved_mark = Mark::new(); + let top_level_mark = Mark::new(); + + chain!( + resolver(unresolved_mark, top_level_mark, false), + tsx( + t.cm.clone(), + typescript::Config { + verbatim_module_syntax: false, + ..Default::default() + }, + TsxConfig::default(), + t.comments.clone(), + top_level_mark, + ) + ) + }, + ts_jsx_bad_pragma, + r#"/** @jsx bad-pragma */"# +);