fix(swc): Fix order of custom passes (#2367)

swc_ecma_transform_react:
 - Accept `top_level_mark`.

swc:
 - Fix order of `custom_before_pass`.
This commit is contained in:
Donny/강동윤 2021-10-07 15:13:21 +09:00 committed by GitHub
parent f0be833f14
commit edc4cb432e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 268 additions and 171 deletions

16
Cargo.lock generated
View File

@ -2305,7 +2305,7 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "swc"
version = "0.63.1"
version = "0.64.0"
dependencies = [
"ahash",
"anyhow",
@ -2410,7 +2410,7 @@ dependencies = [
[[package]]
name = "swc_bundler"
version = "0.65.0"
version = "0.66.0"
dependencies = [
"ahash",
"anyhow",
@ -2650,7 +2650,7 @@ dependencies = [
[[package]]
name = "swc_ecma_minifier"
version = "0.34.2"
version = "0.35.0"
dependencies = [
"ansi_term 0.12.1",
"anyhow",
@ -2708,7 +2708,7 @@ dependencies = [
[[package]]
name = "swc_ecma_preset_env"
version = "0.49.0"
version = "0.50.0"
dependencies = [
"dashmap",
"indexmap",
@ -2734,7 +2734,7 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms"
version = "0.78.0"
version = "0.79.0"
dependencies = [
"pretty_assertions 0.6.1",
"sourcemap",
@ -2851,7 +2851,7 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_optimization"
version = "0.48.0"
version = "0.49.0"
dependencies = [
"dashmap",
"indexmap",
@ -2903,7 +2903,7 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_react"
version = "0.45.0"
version = "0.46.0"
dependencies = [
"base64 0.13.0",
"dashmap",
@ -2995,7 +2995,7 @@ dependencies = [
[[package]]
name = "swc_ecmascript"
version = "0.70.0"
version = "0.71.0"
dependencies = [
"swc_ecma_ast",
"swc_ecma_codegen",

View File

@ -20,7 +20,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc"
repository = "https://github.com/swc-project/swc.git"
version = "0.63.1"
version = "0.64.0"
[lib]
name = "swc"
@ -52,16 +52,16 @@ serde = {version = "1", features = ["derive"]}
serde_json = "1"
sourcemap = "6"
swc_atoms = {version = "0.2", path = "./atoms"}
swc_bundler = {version = "0.65.0", path = "./bundler"}
swc_bundler = {version = "0.66.0", path = "./bundler"}
swc_common = {version = "0.13.0", path = "./common", features = ["sourcemap", "concurrent"]}
swc_ecma_ast = {version = "0.54.0", path = "./ecmascript/ast"}
swc_ecma_codegen = {version = "0.74.0", path = "./ecmascript/codegen"}
swc_ecma_ext_transforms = {version = "0.31.0", path = "./ecmascript/ext-transforms"}
swc_ecma_loader = {version = "0.20.0", path = "./ecmascript/loader", features = ["lru", "node", "tsc"]}
swc_ecma_minifier = {version = "0.34.0", path = "./ecmascript/minifier"}
swc_ecma_minifier = {version = "0.35.0", path = "./ecmascript/minifier"}
swc_ecma_parser = {version = "0.73.0", path = "./ecmascript/parser"}
swc_ecma_preset_env = {version = "0.49.0", path = "./ecmascript/preset-env"}
swc_ecma_transforms = {version = "0.78.0", path = "./ecmascript/transforms", features = [
swc_ecma_preset_env = {version = "0.50.0", path = "./ecmascript/preset-env"}
swc_ecma_transforms = {version = "0.79.0", path = "./ecmascript/transforms", features = [
"compat",
"module",
"optimization",
@ -72,7 +72,7 @@ swc_ecma_transforms = {version = "0.78.0", path = "./ecmascript/transforms", fea
swc_ecma_transforms_base = {version = "0.35.0", path = "./ecmascript/transforms/base"}
swc_ecma_utils = {version = "0.46.0", path = "./ecmascript/utils"}
swc_ecma_visit = {version = "0.40.0", path = "./ecmascript/visit"}
swc_ecmascript = {version = "0.70.0", path = "./ecmascript"}
swc_ecmascript = {version = "0.71.0", path = "./ecmascript"}
swc_visit = {version = "0.2.3", path = "./visit"}
tracing = "0.1.28"

View File

@ -9,7 +9,7 @@ include = ["Cargo.toml", "build.rs", "src/**/*.rs", "src/**/*.js"]
license = "Apache-2.0/MIT"
name = "swc_bundler"
repository = "https://github.com/swc-project/swc.git"
version = "0.65.0"
version = "0.66.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
@ -36,7 +36,7 @@ swc_ecma_ast = {version = "0.54.0", path = "../ecmascript/ast"}
swc_ecma_codegen = {version = "0.74.0", path = "../ecmascript/codegen"}
swc_ecma_loader = {version = "0.20.0", path = "../ecmascript/loader"}
swc_ecma_parser = {version = "0.73.0", path = "../ecmascript/parser"}
swc_ecma_transforms = {version = "0.78.0", path = "../ecmascript/transforms", features = ["optimization"]}
swc_ecma_transforms = {version = "0.79.0", path = "../ecmascript/transforms", features = ["optimization"]}
swc_ecma_utils = {version = "0.46.0", path = "../ecmascript/utils"}
swc_ecma_visit = {version = "0.40.0", path = "../ecmascript/visit"}
tracing = "0.1.28"
@ -46,7 +46,7 @@ hex = "0.4"
ntest = "0.7.2"
reqwest = {version = "0.11.4", features = ["blocking"]}
sha-1 = "0.9"
swc_ecma_transforms = {version = "0.78.0", path = "../ecmascript/transforms", features = ["react", "typescript"]}
swc_ecma_transforms = {version = "0.79.0", path = "../ecmascript/transforms", features = ["react", "typescript"]}
tempfile = "3.1.0"
testing = {version = "0.14.0", path = "../testing"}
url = "2.1.1"

View File

@ -12,7 +12,7 @@ use swc_common::{
comments::SingleThreadedComments,
errors::{ColorConfig, Handler},
sync::Lrc,
FileName, SourceMap,
FileName, Mark, SourceMap,
};
use swc_ecma_parser::{lexer::Lexer, JscTarget, Parser, StringInput, Syntax, TsConfig};
use swc_ecma_transforms::{react, typescript::strip};
@ -83,6 +83,8 @@ impl Load for Loader {
fn load(&self, f: &FileName) -> Result<ModuleData, Error> {
eprintln!("load: {}", f);
let top_level_mark = Mark::fresh(Mark::root());
let tsx;
let fm = match f {
FileName::Real(path) => {
@ -129,6 +131,7 @@ impl Load for Loader {
self.cm.clone(),
None,
Default::default(),
top_level_mark,
));
Ok(ModuleData {

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecmascript"
repository = "https://github.com/swc-project/swc.git"
version = "0.70.0"
version = "0.71.0"
[package.metadata.docs.rs]
all-features = true
@ -35,10 +35,10 @@ typescript = ["typescript-parser", "swc_ecma_transforms/typescript"]
swc_ecma_ast = {version = "0.54.0", path = "./ast"}
swc_ecma_codegen = {version = "0.74.0", path = "./codegen", optional = true}
swc_ecma_dep_graph = {version = "0.42.0", path = "./dep-graph", optional = true}
swc_ecma_minifier = {version = "0.34.0", path = "./minifier", optional = true}
swc_ecma_minifier = {version = "0.35.0", path = "./minifier", optional = true}
swc_ecma_parser = {version = "0.73.0", path = "./parser", optional = true, default-features = false}
swc_ecma_preset_env = {version = "0.49.0", path = "./preset-env", optional = true}
swc_ecma_transforms = {version = "0.78.0", path = "./transforms", optional = true}
swc_ecma_preset_env = {version = "0.50.0", path = "./preset-env", optional = true}
swc_ecma_transforms = {version = "0.79.0", path = "./transforms", optional = true}
swc_ecma_utils = {version = "0.46.0", path = "./utils", optional = true}
swc_ecma_visit = {version = "0.40.0", path = "./visit", optional = true}

View File

@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "src/lists/*.json"]
license = "Apache-2.0/MIT"
name = "swc_ecma_minifier"
repository = "https://github.com/swc-project/swc.git"
version = "0.34.2"
version = "0.35.0"
[features]
debug = ["backtrace"]
@ -29,7 +29,7 @@ swc_common = {version = "0.13.0", path = "../../common"}
swc_ecma_ast = {version = "0.54.0", path = "../ast"}
swc_ecma_codegen = {version = "0.74.0", path = "../codegen"}
swc_ecma_parser = {version = "0.73.0", path = "../parser"}
swc_ecma_transforms = {version = "0.78.0", path = "../transforms/", features = ["optimization"]}
swc_ecma_transforms = {version = "0.79.0", path = "../transforms/", features = ["optimization"]}
swc_ecma_transforms_base = {version = "0.35.0", path = "../transforms/base"}
swc_ecma_utils = {version = "0.46.0", path = "../utils"}
swc_ecma_visit = {version = "0.40.0", path = "../visit"}

View File

@ -5,7 +5,7 @@ documentation = "https://rustdoc.swc.rs/swc_ecma_preset_env/"
edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecma_preset_env"
version = "0.49.0"
version = "0.50.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -22,7 +22,7 @@ string_enum = {version = "0.3.1", path = "../../macros/string_enum"}
swc_atoms = {version = "0.2", path = "../../atoms"}
swc_common = {version = "0.13.0", path = "../../common"}
swc_ecma_ast = {version = "0.54.0", path = "../ast"}
swc_ecma_transforms = {version = "0.78.0", path = "../transforms", features = ["compat", "proposal"]}
swc_ecma_transforms = {version = "0.79.0", path = "../transforms", features = ["compat", "proposal"]}
swc_ecma_utils = {version = "0.46.0", path = "../utils"}
swc_ecma_visit = {version = "0.40.0", path = "../visit"}
walkdir = "2"

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecma_transforms"
repository = "https://github.com/swc-project/swc.git"
version = "0.78.0"
version = "0.79.0"
[package.metadata.docs.rs]
all-features = true
@ -28,9 +28,9 @@ swc_ecma_parser = {version = "0.73.0", path = "../parser"}
swc_ecma_transforms_base = {version = "0.35.0", path = "./base"}
swc_ecma_transforms_compat = {version = "0.40.0", path = "./compat", optional = true}
swc_ecma_transforms_module = {version = "0.44.0", path = "./module", optional = true}
swc_ecma_transforms_optimization = {version = "0.48.0", path = "./optimization", optional = true}
swc_ecma_transforms_optimization = {version = "0.49.0", path = "./optimization", optional = true}
swc_ecma_transforms_proposal = {version = "0.44.0", path = "./proposal", optional = true}
swc_ecma_transforms_react = {version = "0.45.0", path = "./react", optional = true}
swc_ecma_transforms_react = {version = "0.46.0", path = "./react", optional = true}
swc_ecma_transforms_typescript = {version = "0.46.0", path = "./typescript", optional = true}
swc_ecma_utils = {version = "0.46.0", path = "../utils"}
swc_ecma_visit = {version = "0.40.0", path = "../visit"}

View File

@ -14,12 +14,12 @@ use swc_atoms::{js_word, JsWord};
use swc_common::{
collections::{AHashMap, AHashSet},
util::take::Take,
FileName, Mark, Span, SyntaxContext, DUMMY_SP,
FileName, Mark, Span, DUMMY_SP,
};
use swc_ecma_ast::*;
use swc_ecma_utils::{
ident::IdentLike, member_expr, private_ident, quote_ident, quote_str, undefined,
DestructuringFinder, ExprFactory,
DestructuringFinder, ExprFactory, Id,
};
use swc_ecma_visit::{Fold, FoldWith, VisitWith};
@ -115,10 +115,10 @@ pub struct Scope {
///
/// - `import foo from 'bar';`
/// -> `{foo: ('bar', default)}`
pub(crate) idents: FxHashMap<(JsWord, SyntaxContext), (JsWord, JsWord)>,
pub(crate) idents: FxHashMap<Id, (JsWord, JsWord)>,
/// Declared variables except const.
pub(crate) declared_vars: Vec<(JsWord, SyntaxContext)>,
pub(crate) declared_vars: Vec<Id>,
/// Maps of exported variables.
///
@ -129,7 +129,7 @@ pub struct Scope {
///
/// - `export { a as b }`
/// -> `{ a: [b] }`
pub(crate) exported_vars: AHashMap<(JsWord, SyntaxContext), Vec<(JsWord, SyntaxContext)>>,
pub(crate) exported_vars: AHashMap<Id, Vec<Id>>,
/// This is required to handle
/// `export * from 'foo';`

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecma_transforms_optimization"
repository = "https://github.com/swc-project/swc.git"
version = "0.48.0"
version = "0.49.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
@ -29,7 +29,7 @@ tracing = "0.1.28"
swc_ecma_transforms_compat = {version = "0.40.0", path = "../compat"}
swc_ecma_transforms_module = {version = "0.44.0", path = "../module"}
swc_ecma_transforms_proposal = {version = "0.44.0", path = "../proposal"}
swc_ecma_transforms_react = {version = "0.45.0", path = "../react"}
swc_ecma_transforms_react = {version = "0.46.0", path = "../react"}
swc_ecma_transforms_testing = {version = "0.36.0", path = "../testing"}
swc_ecma_transforms_typescript = {version = "0.46.0", path = "../typescript"}
testing = {version = "0.14.0", path = "../../../testing"}

View File

@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs"]
license = "Apache-2.0/MIT"
name = "swc_ecma_transforms_react"
repository = "https://github.com/swc-project/swc.git"
version = "0.45.0"
version = "0.46.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -1,4 +1,5 @@
use self::static_check::should_use_create_element;
use crate::refresh::options::{deserialize_refresh, RefreshOptions};
use dashmap::DashMap;
use once_cell::sync::Lazy;
use regex::Regex;
@ -11,7 +12,7 @@ use swc_common::{
iter::IdentifyLast,
sync::Lrc,
util::take::Take,
FileName, SourceMap, Spanned, DUMMY_SP,
FileName, Mark, SourceMap, Spanned, DUMMY_SP,
};
use swc_ecma_ast::*;
use swc_ecma_parser::{Parser, StringInput, Syntax};
@ -21,8 +22,6 @@ use swc_ecma_utils::{
};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
use crate::refresh::options::{deserialize_refresh, RefreshOptions};
mod static_check;
#[cfg(test)]
mod tests;
@ -116,15 +115,21 @@ fn default_throw_if_namespace() -> bool {
true
}
fn parse_classic_option(cm: &SourceMap, name: &str, src: String) -> Box<Expr> {
static CACHE: Lazy<DashMap<String, Box<Expr>>> = Lazy::new(|| DashMap::with_capacity(2));
fn parse_classic_option(
cm: &SourceMap,
name: &str,
src: String,
top_level_mark: Mark,
) -> Box<Expr> {
static CACHE: Lazy<DashMap<(String, Mark), Box<Expr>>> =
Lazy::new(|| DashMap::with_capacity(2));
let fm = cm.new_source_file(FileName::Custom(format!("<jsx-config-{}.js>", name)), src);
if let Some(expr) = CACHE.get(&**fm.src) {
if let Some(expr) = CACHE.get(&((*fm.src).clone(), top_level_mark)) {
return expr.clone();
}
let expr = Parser::new(Syntax::default(), StringInput::from(&*fm), None)
let mut expr = Parser::new(Syntax::default(), StringInput::from(&*fm), None)
.parse_expr()
.map_err(|e| {
if HANDLER.is_set() {
@ -139,20 +144,46 @@ fn parse_classic_option(cm: &SourceMap, name: &str, src: String) -> Box<Expr> {
)
});
CACHE.insert((*fm.src).clone(), expr.clone());
apply_mark(&mut expr, top_level_mark);
CACHE.insert(((*fm.src).clone(), top_level_mark), expr.clone());
expr
}
fn apply_mark(e: &mut Expr, mark: Mark) {
match e {
Expr::Ident(i) => {
i.span = i.span.apply_mark(mark);
}
Expr::Member(MemberExpr {
obj: ExprOrSuper::Expr(obj),
..
}) => {
apply_mark(&mut **obj, mark);
}
_ => {}
}
}
/// `@babel/plugin-transform-react-jsx`
///
/// Turn JSX into React function calls
pub fn jsx<C>(cm: Lrc<SourceMap>, comments: Option<C>, options: Options) -> impl Fold + VisitMut
///
///
/// `top_level_mark` should be [Mark] passed to
/// [swc_ecma_transforms_base::resolver::resolver_with_mark].
pub fn jsx<C>(
cm: Lrc<SourceMap>,
comments: Option<C>,
options: Options,
top_level_mark: Mark,
) -> impl Fold + VisitMut
where
C: Comments,
{
as_folder(Jsx {
cm: cm.clone(),
top_level_mark,
next: options.next,
runtime: options.runtime.unwrap_or_default(),
import_source: options.import_source.into(),
@ -161,11 +192,16 @@ where
import_fragment: None,
import_create_element: None,
pragma: ExprOrSuper::Expr(parse_classic_option(&cm, "pragma", options.pragma)),
pragma: ExprOrSuper::Expr(parse_classic_option(
&cm,
"pragma",
options.pragma,
top_level_mark,
)),
comments,
pragma_frag: ExprOrSpread {
spread: None,
expr: parse_classic_option(&cm, "pragmaFrag", options.pragma_frag),
expr: parse_classic_option(&cm, "pragmaFrag", options.pragma_frag, top_level_mark),
},
use_builtins: options.use_builtins,
use_spread: options.use_spread,
@ -180,6 +216,8 @@ where
{
cm: Lrc<SourceMap>,
top_level_mark: Mark,
next: bool,
runtime: Runtime,
/// For automatic runtime.
@ -750,7 +788,12 @@ where
let src = line.replace("@jsxFrag", "").trim().to_string();
self.pragma_frag = ExprOrSpread {
expr: parse_classic_option(&self.cm, "module-jsx-pragma-frag", src),
expr: parse_classic_option(
&self.cm,
"module-jsx-pragma-frag",
src,
self.top_level_mark,
),
spread: None,
};
} else if line.starts_with("@jsx ") {
@ -772,6 +815,7 @@ where
&self.cm,
"module-jsx-pragma",
src,
self.top_level_mark,
));
}
}

View File

@ -5,6 +5,7 @@ use crate::display_name;
use std::path::PathBuf;
use swc_common::{chain, Mark};
use swc_ecma_parser::EsConfig;
use swc_ecma_transforms_base::resolver::resolver_with_mark;
use swc_ecma_transforms_compat::{
es2015::{arrow, classes},
es3::property_literals,
@ -12,9 +13,15 @@ use swc_ecma_transforms_compat::{
use swc_ecma_transforms_module::common_js::common_js;
use swc_ecma_transforms_testing::{parse_options, test, test_fixture, Tester};
fn tr(t: &mut Tester, options: Options) -> impl Fold {
fn tr(t: &mut Tester, options: Options, top_level_mark: Mark) -> impl Fold {
chain!(
jsx(t.cm.clone(), Some(t.comments.clone()), options),
resolver_with_mark(top_level_mark),
jsx(
t.cm.clone(),
Some(t.comments.clone()),
options,
top_level_mark
),
display_name(),
classes(Some(t.comments.clone())),
arrow(),
@ -45,6 +52,8 @@ fn true_by_default() -> bool {
}
fn fixture_tr(t: &mut Tester, mut options: FixtureOptions) -> impl Fold {
let top_level_mark = Mark::fresh(Mark::root());
options.options.next = options.babel_8_breaking || options.options.runtime.is_some();
if !options.babel_8_breaking && options.options.runtime.is_none() {
@ -53,7 +62,12 @@ fn fixture_tr(t: &mut Tester, mut options: FixtureOptions) -> impl Fold {
options.options.use_builtins |= options.use_builtins;
chain!(
jsx(t.cm.clone(), Some(t.comments.clone()), options.options),
jsx(
t.cm.clone(),
Some(t.comments.clone()),
options.options,
top_level_mark
),
display_name(),
)
}
@ -62,7 +76,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_add_appropriate_newlines,
r#"
<Component
@ -81,7 +95,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_arrow_functions,
r#"
var foo = function () {
@ -113,7 +127,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_concatenates_adjacent_string_literals,
r#"
var x =
@ -150,7 +164,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_display_name_assignment_expression,
r#"var Component;
Component = React.createClass({
@ -173,7 +187,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_display_name_export_default,
r#"
export default React.createClass({
@ -197,7 +211,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_display_name_if_missing,
r#"
var Whateva = React.createClass({
@ -235,7 +249,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_display_name_object_declaration,
r#"
exports = {
@ -261,7 +275,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_display_name_property_assignment,
r#"
exports.Component = React.createClass({
@ -285,7 +299,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_display_name_variable_declaration,
r#"
var Component = React.createClass({
@ -309,7 +323,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_dont_coerce_expression_containers,
r#"
<Text>
@ -332,7 +346,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_honor_custom_jsx_comment_if_jsx_pragma_option_set,
r#"/** @jsx dom */
@ -356,7 +370,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_honor_custom_jsx_comment,
r#"
/** @jsx dom */
@ -388,7 +402,8 @@ test!(
Options {
pragma: "dom".into(),
..Default::default()
}
},
Mark::fresh(Mark::root())
),
react_honor_custom_jsx_pragma_option,
r#"
@ -412,7 +427,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_jsx_with_retainlines_option,
r#"var div = <div>test</div>;"#,
r#"var div = React.createElement("div", null, "test");"#
@ -423,7 +438,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_jsx_without_retainlines_option,
r#"var div = <div>test</div>;"#,
r#"var div = React.createElement("div", null, "test");"#
@ -436,7 +451,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_optimisation_react_constant_elements,
r#"
class App extends React.Component {
@ -500,7 +515,10 @@ test!(
jsx: true,
..Default::default()
}),
|t| chain!(tr(t, Default::default()), property_literals()),
|t| chain!(
tr(t, Default::default(), Mark::fresh(Mark::root())),
property_literals(),
),
react_should_add_quotes_es3,
r#"var es3 = <F aaa new const var default foo-bar/>;"#,
r#"
@ -520,7 +538,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_allow_constructor_as_prop,
r#"<Component constructor="foo" />;"#,
r#"
@ -535,7 +553,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_allow_deeper_js_namespacing,
r#"<Namespace.DeepNamespace.Component />;"#,
r#"React.createElement(Namespace.DeepNamespace.Component, null);"#
@ -546,7 +564,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_allow_elements_as_attributes,
r#"<div attr=<div /> />"#,
r#"
@ -560,7 +578,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_allow_js_namespacing,
r#"<Namespace.Component />;"#,
r#"React.createElement(Namespace.Component, null);"#
@ -571,7 +589,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_allow_nested_fragments,
r#"
<div>
@ -606,7 +624,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_allow_no_pragmafrag_if_frag_unused,
r#"
/** @jsx dom */
@ -624,7 +642,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_allow_pragmafrag_and_frag,
r#"
/** @jsx dom */
@ -645,7 +663,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_avoid_wrapping_in_extra_parens_if_not_needed,
r#"
var x = <div>
@ -677,7 +695,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_convert_simple_tags,
r#"var x = <div></div>;"#,
r#"var x = React.createElement("div", null);"#
@ -688,7 +706,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_convert_simple_text,
r#"var x = <div>text</div>;"#,
r#"var x = React.createElement("div", null, "text");"#
@ -699,7 +717,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_escape_xhtml_jsxattribute,
r#"
<div id="wôw" />;
@ -725,7 +743,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_escape_xhtml_jsxtext_1,
r#"
<div>wow</div>;
@ -756,7 +774,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_escape_xhtml_jsxtext_2,
r#"
<div>this should not parse as unicode: \u00a0</div>;
@ -772,7 +790,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_escape_unicode_chars_in_attribute,
r#"<Bla title="Ú"/>"#,
r#"React.createElement(Bla, {
@ -787,7 +805,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_escape_xhtml_jsxtext_3,
r#"
<div>this should parse as nbsp: </div>;
@ -802,7 +820,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_handle_attributed_elements,
r#"
var HelloMessage = React.createClass({
@ -838,7 +856,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_handle_has_own_property_correctly,
r#"<hasOwnProperty>testing</hasOwnProperty>;"#,
r#"React.createElement("hasOwnProperty", null, "testing");"#
@ -849,7 +867,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_have_correct_comma_in_nested_children,
r#"
var x = <div>
@ -873,7 +891,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_insert_commas_after_expressions_before_whitespace,
r#"
var x =
@ -908,7 +926,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_not_add_quotes_to_identifier_names,
r#"var e = <F aaa new const var default foo-bar/>;"#,
r#"
@ -928,7 +946,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_not_mangle_expressioncontainer_attribute_values,
r#"<button data-value={"a value\n with\nnewlines\n and spaces"}>Button</button>;"#,
r#"
@ -943,7 +961,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_not_strip_nbsp_even_coupled_with_other_whitespace,
r#"<div>&nbsp; </div>;"#,
r#"React.createElement("div", null, "\xA0 ");"#,
@ -955,7 +973,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_not_strip_tags_with_a_single_child_of_nbsp,
r#"<div>&nbsp;</div>;"#,
r#"React.createElement("div", null, "\xA0");"#,
@ -968,7 +986,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_properly_handle_comments_between_props,
r#"
var x = (
@ -999,7 +1017,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_quote_jsx_attributes,
r#"<button data-value='a value'>Button</button>;"#,
r#"
@ -1021,6 +1039,7 @@ test!(
throw_if_namespace: false,
..Default::default()
},
Mark::fresh(Mark::root())
),
react_should_support_xml_namespaces_if_flag,
r#"<f:image n:attr />;"#,
@ -1034,7 +1053,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_should_transform_known_hyphenated_tags,
r#"<font-face />;"#,
r#"React.createElement("font-face", null);"#
@ -1045,7 +1064,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_wraps_props_in_react_spread_for_first_spread_attributes,
r#"
<Component { ... x } y
@ -1064,7 +1083,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_wraps_props_in_react_spread_for_last_spread_attributes,
r#"<Component y={2} z { ... x } />"#,
r#"
@ -1080,7 +1099,7 @@ test!(
jsx: true,
..Default::default()
}),
|t| tr(t, Default::default()),
|t| tr(t, Default::default(), Mark::fresh(Mark::root())),
react_wraps_props_in_react_spread_for_middle_spread_attributes,
r#"<Component y={2} { ... x } z />"#,
r#"
@ -1102,6 +1121,7 @@ test!(
use_builtins: true,
..Default::default()
},
Mark::fresh(Mark::root())
),
use_builtins_assignment,
r#"var div = <Component {...props} foo="bar" />"#,
@ -1123,6 +1143,7 @@ test!(
use_spread: true,
..Default::default()
},
Mark::fresh(Mark::root())
),
use_spread_assignment,
r#"<Component y={2} { ...x } z />"#,
@ -1141,6 +1162,7 @@ test!(
use_builtins: true,
..Default::default()
},
Mark::fresh(Mark::root())
),
issue_229,
"const a = <>test</>
@ -1154,16 +1176,20 @@ test!(
jsx: true,
..Default::default()
}),
|t| chain!(
tr(
t,
Options {
use_builtins: true,
..Default::default()
}
),
common_js(Mark::fresh(Mark::root()), Default::default(), None)
),
|t| {
let top_level_mark = Mark::fresh(Mark::root());
chain!(
tr(
t,
Options {
use_builtins: true,
..Default::default()
},
top_level_mark
),
common_js(top_level_mark, Default::default(), None)
)
},
issue_351,
"import React from 'react';
@ -1183,7 +1209,8 @@ test!(
Options {
use_builtins: true,
..Default::default()
}
},
Mark::fresh(Mark::root())
),
issue_481,
"<span> {foo}</span>;",
@ -1196,16 +1223,20 @@ test!(
jsx: true,
..Default::default()
}),
|t| chain!(
tr(
t,
Options {
use_builtins: true,
..Default::default()
}
),
common_js(Mark::fresh(Mark::root()), Default::default(), None)
),
|t| {
let top_level_mark = Mark::fresh(Mark::root());
chain!(
tr(
t,
Options {
use_builtins: true,
..Default::default()
},
top_level_mark
),
common_js(Mark::fresh(Mark::root()), Default::default(), None)
)
},
issue_517,
"import React from 'react';
<div style='white-space: pre'>Hello World</div>;",
@ -1234,7 +1265,8 @@ test!(
Options {
use_builtins: true,
..Default::default()
}
},
Mark::fresh(Mark::root())
),
issue_542,
"let page = <p>Click <em>New melody</em> listen to a randomly generated melody</p>",
@ -1250,10 +1282,19 @@ test!(
jsx: true,
..Default::default()
}),
|t| chain!(
classes(Some(t.comments.clone())),
jsx(t.cm.clone(), Some(t.comments.clone()), Default::default())
),
|t| {
let top_level_mark = Mark::fresh(Mark::root());
chain!(
classes(Some(t.comments.clone())),
jsx(
t.cm.clone(),
Some(t.comments.clone()),
Default::default(),
top_level_mark
)
)
},
regression_2775,
r#"
import React, {Component} from 'react';

View File

@ -7,7 +7,7 @@ pub use self::{
refresh::{options::RefreshOptions, refresh},
};
use std::mem;
use swc_common::{chain, comments::Comments, sync::Lrc, SourceMap};
use swc_common::{chain, comments::Comments, sync::Lrc, Mark, SourceMap};
use swc_ecma_visit::Fold;
mod display_name;
@ -20,7 +20,16 @@ mod refresh;
/// `@babel/preset-react`
///
/// Preset for all React plugins.
pub fn react<C>(cm: Lrc<SourceMap>, comments: Option<C>, mut options: Options) -> impl Fold
///
///
/// `top_level_mark` should be [Mark] passed to
/// [swc_ecma_transforms_base::resolver::resolver_with_mark].
pub fn react<C>(
cm: Lrc<SourceMap>,
comments: Option<C>,
mut options: Options,
top_level_mark: Mark,
) -> impl Fold
where
C: Comments + Clone,
{
@ -32,7 +41,7 @@ where
jsx_src(development, cm.clone()),
jsx_self(development),
refresh(development, refresh_options, cm.clone(), comments.clone()),
jsx(cm.clone(), comments.clone(), options),
jsx(cm.clone(), comments.clone(), options, top_level_mark),
display_name(),
pure_annotations(comments),
)

View File

@ -1,5 +1,5 @@
use super::*;
use swc_common::{comments::SingleThreadedComments, sync::Lrc, FileName, SourceMap};
use swc_common::{comments::SingleThreadedComments, sync::Lrc, FileName, Mark, SourceMap};
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
use swc_ecma_parser::{Parser, StringInput};
use swc_ecma_transforms_base::resolver;
@ -63,6 +63,8 @@ fn emit(
fn run_test(input: &str, expected: &str) {
Tester::run(|tester| {
let top_level_mark = Mark::fresh(Mark::root());
let (actual, actual_sm, actual_comments) = parse(tester, input)?;
let actual = actual
.fold_with(&mut resolver::resolver())
@ -70,6 +72,7 @@ fn run_test(input: &str, expected: &str) {
actual_sm.clone(),
Some(&actual_comments),
Default::default(),
top_level_mark,
));
let actual_src = emit(actual_sm, actual_comments, &actual);

View File

@ -14,14 +14,8 @@ use swc_ecma_ast::{EsVersion, Module};
use swc_ecma_minifier::option::MinifyOptions;
use swc_ecma_parser::Syntax;
use swc_ecma_transforms::{
compat, fixer, helpers, hygiene,
hygiene::hygiene_with_config,
modules,
modules::util::Scope,
optimization::const_modules,
pass::Optional,
proposals::{import_assertions, private_in_object},
typescript,
compat, fixer, helpers, hygiene, hygiene::hygiene_with_config, modules, modules::util::Scope,
optimization::const_modules, pass::Optional, proposals::private_in_object,
};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, VisitMut};
@ -32,7 +26,7 @@ pub struct PassBuilder<'a, 'b, P: swc_ecma_visit::Fold> {
env: Option<swc_ecma_preset_env::Config>,
pass: P,
/// [Mark] for top level bindings and unresolved identifier references.
global_mark: Mark,
top_level_mark: Mark,
target: JscTarget,
loose: bool,
hygiene: Option<hygiene::Config>,
@ -46,7 +40,7 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
cm: &'a Arc<SourceMap>,
handler: &'b Handler,
loose: bool,
global_mark: Mark,
top_level_mark: Mark,
pass: P,
) -> Self {
PassBuilder {
@ -54,7 +48,7 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
handler,
env: None,
pass,
global_mark,
top_level_mark,
target: JscTarget::Es5,
loose,
hygiene: Some(Default::default()),
@ -74,7 +68,7 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
handler: self.handler,
env: self.env,
pass,
global_mark: self.global_mark,
top_level_mark: self.top_level_mark,
target: self.target,
loose: self.loose,
hygiene: self.hygiene,
@ -154,7 +148,6 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
syntax: Syntax,
module: Option<ModuleConfig>,
comments: Option<&'cmt SwcComments>,
custom_before_pass: impl 'cmt + swc_ecma_visit::Fold,
) -> impl 'cmt + swc_ecma_visit::Fold
where
P: 'cmt,
@ -168,17 +161,13 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
// compat
let compat_pass = if let Some(env) = self.env {
Either::Left(chain!(
import_assertions(),
Optional::new(typescript::strip(), syntax.typescript()),
custom_before_pass,
swc_ecma_preset_env::preset_env(self.global_mark, comments.clone(), env)
Either::Left(swc_ecma_preset_env::preset_env(
self.top_level_mark,
comments.clone(),
env,
))
} else {
Either::Right(chain!(
import_assertions(),
Optional::new(typescript::strip(), syntax.typescript()),
custom_before_pass,
Optional::new(
compat::es2021::es2021(),
should_enable(self.target, JscTarget::Es2021)
@ -205,7 +194,7 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
),
Optional::new(
compat::es2015(
self.global_mark,
self.top_level_mark,
comments.clone(),
compat::es2015::Config {
for_of: compat::es2015::for_of::Config {
@ -244,7 +233,7 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
base_url,
paths,
base,
self.global_mark,
self.top_level_mark,
module,
Rc::clone(&module_scope)
),
@ -252,7 +241,7 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
options: self.minify,
cm: self.cm.clone(),
comments: comments.cloned(),
top_level_mark: self.global_mark,
top_level_mark: self.top_level_mark,
}),
Optional::new(
hygiene_with_config(self.hygiene.clone().unwrap_or_default()),

View File

@ -36,7 +36,7 @@ use swc_ecma_transforms::{
modules::{hoist::import_hoister, path::NodeImportResolver, util::Scope},
optimization::{const_modules, inline_globals, json_parse, simplifier},
pass::{noop, Optional},
proposals::{decorators, export_default_from},
proposals::{decorators, export_default_from, import_assertions},
react, resolver_with_mark, typescript,
};
use swc_ecma_visit::Fold;
@ -249,21 +249,6 @@ impl Options {
.unwrap_or_else(|| Mark::fresh(Mark::root()));
let pass = chain!(
// handle jsx
Optional::new(
react::react(cm.clone(), comments.clone(), transform.react),
syntax.jsx()
),
// Decorators may use type information
Optional::new(
decorators(decorators::Config {
legacy: transform.legacy_decorator,
emit_metadata: transform.decorator_metadata,
}),
syntax.decorators()
),
Optional::new(typescript::strip(), syntax.typescript()),
resolver_with_mark(top_level_mark),
const_modules,
optimization,
Optional::new(export_default_from(), syntax.export_default_from()),
@ -289,10 +274,34 @@ impl Options {
syntax,
config.module,
comments,
custom_before_pass,
);
let pass = chain!(pass, Optional::new(jest::jest(), transform.hidden.jest));
let pass = chain!(
// Decorators may use type information
Optional::new(
decorators(decorators::Config {
legacy: transform.legacy_decorator,
emit_metadata: transform.decorator_metadata,
}),
syntax.decorators()
),
import_assertions(),
Optional::new(typescript::strip(), syntax.typescript()),
resolver_with_mark(top_level_mark),
custom_before_pass,
// handle jsx
Optional::new(
react::react(
cm.clone(),
comments.clone(),
transform.react,
top_level_mark
),
syntax.jsx()
),
pass,
Optional::new(jest::jest(), transform.hidden.jest)
);
BuiltConfig {
minify: config.minify,

View File

@ -1,4 +1,3 @@
import React from "react";
export var HelloWorld = function() {
return(/*#__PURE__*/ React.createElement("div", {
title: "您好SWC"