diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 39205923108..41dc300c95b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -651,19 +651,9 @@ jobs: - name: Run cargo test (concurrent) if: runner.os == 'Linux' && matrix.settings.crate != 'swc_ecma_minifier' shell: bash - env: - SWC_FORCE_CONCURRENT: "1" run: | ./scripts/github/test-concurrent.sh ${{ matrix.settings.crate }} - - name: Run cargo test (swc, concurrent) - shell: bash - if: matrix.settings.crate == 'swc' && runner.os == 'Linux' - env: - SWC_FORCE_CONCURRENT: "1" - run: | - cargo test -p swc --features concurrent - - name: Install cargo-hack uses: baptiste0928/cargo-install@v2 if: matrix.settings.os == 'ubuntu-latest' diff --git a/Cargo.toml b/Cargo.toml index a770257b036..7ed1b0ea5e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,10 +36,9 @@ resolver = "2" debug = true # lto = true -# Without this, printing diff consumes more than a minute. - -[profile.dev.package.pretty_assertions] +# Optimize for iteration +[profile.dev.build-override] opt-level = 3 -[profile.test.package.pretty_assertions] -opt-level = 3 +[profile.dev.package."*"] +opt-level = 3 \ No newline at end of file diff --git a/crates/swc_ecma_transforms_base/src/perf.rs b/crates/swc_ecma_transforms_base/src/perf.rs index a1fcb1659e7..fe6e9efdc14 100644 --- a/crates/swc_ecma_transforms_base/src/perf.rs +++ b/crates/swc_ecma_transforms_base/src/perf.rs @@ -49,7 +49,7 @@ where where N: Send + Sync + VisitWith, { - if nodes.len() >= threshold || option_env!("SWC_FORCE_CONCURRENT") == Some("1") { + if nodes.len() >= threshold { GLOBALS.with(|globals| { HELPERS.with(|helpers| { HANDLER.with(|handler| { @@ -107,7 +107,7 @@ where where N: Send + Sync + VisitMutWith, { - if nodes.len() >= threshold || option_env!("SWC_FORCE_CONCURRENT") == Some("1") { + if nodes.len() >= threshold { GLOBALS.with(|globals| { HELPERS.with(|helpers| { HANDLER.with(|handler| { @@ -165,7 +165,7 @@ where where N: Send + Sync + FoldWith, { - if nodes.len() >= threshold || option_env!("SWC_FORCE_CONCURRENT") == Some("1") { + if nodes.len() >= threshold { use rayon::prelude::*; let (visitor, nodes) = GLOBALS.with(|globals| { diff --git a/crates/swc_ecma_utils/src/parallel.rs b/crates/swc_ecma_utils/src/parallel.rs index 2a9bd0a1fb6..2881c4cf997 100644 --- a/crates/swc_ecma_utils/src/parallel.rs +++ b/crates/swc_ecma_utils/src/parallel.rs @@ -134,7 +134,7 @@ where I: Items, F: Send + Sync + Fn(&mut Self, usize, I::Elem), { - if nodes.len() >= threshold || option_env!("SWC_FORCE_CONCURRENT") == Some("1") { + if nodes.len() >= threshold { GLOBALS.with(|globals| { use rayon::prelude::*; diff --git a/out.txt b/out.txt deleted file mode 100644 index b1167287a8b..00000000000 --- a/out.txt +++ /dev/null @@ -1,7099 +0,0 @@ -diff --git a/bindings/binding_core_node/src/bundle.rs b/bindings/binding_core_node/src/bundle.rs -index c2378b1698..2fe29702c9 100644 ---- a/bindings/binding_core_node/src/bundle.rs -+++ b/bindings/binding_core_node/src/bundle.rs -@@ -129,16 +129,16 @@ impl Task for BundleTask { - None, - None, - true, -- codegen_target, - SourceMapsConfig::Bool(true), - // TODO - &Default::default(), - None, -- minify, - None, - true, -- false, - Default::default(), -+ swc_core::ecma::codegen::Config::default() -+ .with_target(codegen_target) -+ .with_minify(minify), - )?; - - Ok((k, output)) -diff --git a/bindings/binding_core_node/src/print.rs b/bindings/binding_core_node/src/print.rs -index 983cad30e1..720c23e57f 100644 ---- a/bindings/binding_core_node/src/print.rs -+++ b/bindings/binding_core_node/src/print.rs -@@ -40,18 +40,18 @@ impl Task for PrintTask { - None, - options.output_path.clone(), - true, -- options.config.jsc.target.unwrap_or(EsVersion::Es2020), - options - .source_maps - .clone() - .unwrap_or(SourceMapsConfig::Bool(false)), - &Default::default(), - None, -- options.config.minify.into_bool(), - None, - options.config.emit_source_map_columns.into_bool(), -- false, - Default::default(), -+ swc_core::ecma::codegen::Config::default() -+ .with_target(options.config.jsc.target.unwrap_or(EsVersion::Es2020)) -+ .with_minify(options.config.minify.into_bool()), - ) - .convert_err() - }) -@@ -102,18 +102,18 @@ pub fn print_sync(program: String, options: Buffer) -> napi::Result, m: &Module, minify: bool) -> Result - } - - let mut e = swc_ecma_codegen::Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(true), - cm, - comments: None, - wr, -diff --git a/crates/swc/benches/typescript.rs b/crates/swc/benches/typescript.rs -index cde28c9da5..b87bfc8ccb 100644 ---- a/crates/swc/benches/typescript.rs -+++ b/crates/swc/benches/typescript.rs -@@ -115,15 +115,13 @@ fn bench_codegen(b: &mut Bencher, _target: EsVersion) { - None, - None, - false, -- EsVersion::Es2020, - SourceMapsConfig::Bool(false), - &Default::default(), - None, -- false, - None, - false, -- false, - Default::default(), -+ swc_ecma_codegen::Config::default().with_target(EsVersion::Es2020), - ) - .unwrap() - })); -diff --git a/crates/swc/src/builder.rs b/crates/swc/src/builder.rs -index 45fbaa764b..e335ee6fd3 100644 ---- a/crates/swc/src/builder.rs -+++ b/crates/swc/src/builder.rs -@@ -187,8 +187,8 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> { - (true, c.config.import_interop(), c.config.ignore_dynamic) - } - Some(ModuleConfig::SystemJs(_)) -- | Some(ModuleConfig::Es6) -- | Some(ModuleConfig::NodeNext) -+ | Some(ModuleConfig::Es6(..)) -+ | Some(ModuleConfig::NodeNext(..)) - | None => (false, true.into(), true), - }; - -@@ -233,15 +233,18 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> { - should_enable(self.target, EsVersion::Es2021) - ), - Optional::new( -- compat::es2020::es2020(compat::es2020::Config { -- nullish_coalescing: compat::es2020::nullish_coalescing::Config { -- no_document_all: assumptions.no_document_all -+ compat::es2020::es2020( -+ compat::es2020::Config { -+ nullish_coalescing: compat::es2020::nullish_coalescing::Config { -+ no_document_all: assumptions.no_document_all -+ }, -+ optional_chaining: compat::es2020::optional_chaining::Config { -+ no_document_all: assumptions.no_document_all, -+ pure_getter: assumptions.pure_getters -+ } - }, -- optional_chaining: compat::es2020::optional_chaining::Config { -- no_document_all: assumptions.no_document_all, -- pure_getter: assumptions.pure_getters -- } -- }), -+ self.unresolved_mark -+ ), - should_enable(self.target, EsVersion::Es2020) - ), - Optional::new( -diff --git a/crates/swc/src/config/mod.rs b/crates/swc/src/config/mod.rs -index 38f8bae512..1a79edfdf0 100644 ---- a/crates/swc/src/config/mod.rs -+++ b/crates/swc/src/config/mod.rs -@@ -10,7 +10,7 @@ use std::{ - usize, - }; - --use anyhow::{bail, Error}; -+use anyhow::{bail, Context, Error}; - use dashmap::DashMap; - use either::Either; - use indexmap::IndexMap; -@@ -54,10 +54,13 @@ use swc_ecma_parser::{parse_file_as_expr, Syntax, TsConfig}; - use swc_ecma_transforms::{ - feature::FeatureFlag, - hygiene, modules, -- modules::{path::NodeImportResolver, rewriter::import_rewriter}, -+ modules::{path::NodeImportResolver, rewriter::import_rewriter, EsModuleConfig}, - optimization::{const_modules, json_parse, simplifier}, - pass::{noop, Optional}, -- proposals::{decorators, export_default_from, import_assertions}, -+ proposals::{ -+ decorators, explicit_resource_management::explicit_resource_management, -+ export_default_from, import_assertions, -+ }, - react::{self, default_pragma, default_pragma_frag}, - resolver, - typescript::{self, TsEnumConfig, TsImportExportAssignConfig}, -@@ -324,7 +327,7 @@ impl Options { - config: Option, - comments: Option<&'a SingleThreadedComments>, - custom_before_pass: impl FnOnce(&Program) -> P, -- ) -> Result, Error> -+ ) -> Result>, Error> - where - P: 'a + swc_ecma_visit::Fold, - { -@@ -416,7 +419,7 @@ impl Options { - - if matches!( - cfg.module, -- None | Some(ModuleConfig::Es6 | ModuleConfig::NodeNext) -+ None | Some(ModuleConfig::Es6(..) | ModuleConfig::NodeNext(..)) - ) { - c.module = true; - } -@@ -527,7 +530,7 @@ impl Options { - .as_ref() - .map(|v| match v.format.comments.clone().into_inner() { - Some(v) => v, -- None => BoolOr::Bool(false), -+ None => BoolOr::Bool(true), - }) - .unwrap_or_else(|| { - BoolOr::Data(if cfg.minify.into_bool() { -@@ -606,11 +609,11 @@ impl Options { - ); - - let import_export_assign_config = match cfg.module { -- Some(ModuleConfig::Es6) => TsImportExportAssignConfig::EsNext, -+ Some(ModuleConfig::Es6(..)) => TsImportExportAssignConfig::EsNext, - Some(ModuleConfig::CommonJs(..)) - | Some(ModuleConfig::Amd(..)) - | Some(ModuleConfig::Umd(..)) => TsImportExportAssignConfig::Preserve, -- Some(ModuleConfig::NodeNext) => TsImportExportAssignConfig::NodeNext, -+ Some(ModuleConfig::NodeNext(..)) => TsImportExportAssignConfig::NodeNext, - // TODO: should Preserve for SystemJS - _ => TsImportExportAssignConfig::Classic, - }; -@@ -664,7 +667,7 @@ impl Options { - comments.map(|v| v as _), - ); - -- let keep_import_assertions = experimental.keep_import_assertions.into_bool(); -+ let keep_import_attributes = experimental.keep_import_attributes.into_bool(); - - #[cfg(feature = "plugin")] - let plugin_transforms = { -@@ -759,88 +762,99 @@ impl Options { - noop() - }; - -- let pass = chain!( -- lint_to_fold(swc_ecma_lints::rules::all(LintParams { -- program: &program, -- lint_config: &lints, -- top_level_ctxt, -- unresolved_ctxt, -- es_version, -- source_map: cm.clone(), -- })), -- // Decorators may use type information -- Optional::new( -- match transform.decorator_version.unwrap_or_default() { -- DecoratorVersion::V202112 => { -- Either::Left(decorators(decorators::Config { -- legacy: transform.legacy_decorator.into_bool(), -- emit_metadata: transform.decorator_metadata.into_bool(), -- use_define_for_class_fields: !assumptions.set_public_class_fields, -- })) -- } -- DecoratorVersion::V202203 => { -- Either::Right( -+ let pass: Box = if experimental -+ .disable_builtin_transforms_for_internal_testing -+ .into_bool() -+ { -+ Box::new(plugin_transforms) -+ } else { -+ Box::new(chain!( -+ lint_to_fold(swc_ecma_lints::rules::all(LintParams { -+ program: &program, -+ lint_config: &lints, -+ top_level_ctxt, -+ unresolved_ctxt, -+ es_version, -+ source_map: cm.clone(), -+ })), -+ // Decorators may use type information -+ Optional::new( -+ match transform.decorator_version.unwrap_or_default() { -+ DecoratorVersion::V202112 => { -+ Either::Left(decorators(decorators::Config { -+ legacy: transform.legacy_decorator.into_bool(), -+ emit_metadata: transform.decorator_metadata.into_bool(), -+ use_define_for_class_fields: !assumptions.set_public_class_fields, -+ })) -+ } -+ DecoratorVersion::V202203 => { -+ Either::Right( - swc_ecma_transforms::proposals::decorator_2022_03::decorator_2022_03(), - ) -- } -- }, -- syntax.decorators() -- ), -- // The transform strips import assertions, so it's only enabled if -- // keep_import_assertions is false. -- Optional::new(import_assertions(), !keep_import_assertions), -- Optional::new( -- typescript::strip_with_jsx::>( -- cm.clone(), -- typescript::Config { -- pragma: Some( -- transform -- .react -- .pragma -- .clone() -- .unwrap_or_else(default_pragma) -- ), -- pragma_frag: Some( -- transform -- .react -- .pragma_frag -- .clone() -- .unwrap_or_else(default_pragma_frag) -- ), -- ts_enum_config: TsEnumConfig { -- treat_const_enum_as_enum: transform -- .treat_const_enum_as_enum -- .into_bool(), -- ts_enum_is_readonly: assumptions.ts_enum_is_readonly, -- }, -- import_export_assign_config, -- ..Default::default() -+ } - }, -- comments.map(|v| v as _), -- top_level_mark -+ syntax.decorators() - ), -- syntax.typescript() -- ), -- plugin_transforms, -- custom_before_pass(&program), -- // handle jsx -- Optional::new( -- react::react::<&dyn Comments>( -- cm.clone(), -- comments.map(|v| v as _), -- transform.react, -- top_level_mark, -- unresolved_mark -+ Optional::new( -+ explicit_resource_management(), -+ syntax.explicit_resource_management() - ), -- syntax.jsx() -- ), -- pass, -- Optional::new(jest::jest(), transform.hidden.jest.into_bool()), -- Optional::new( -- dropped_comments_preserver(comments.cloned()), -- preserve_all_comments -- ), -- ); -+ // The transform strips import assertions, so it's only enabled if -+ // keep_import_assertions is false. -+ Optional::new(import_assertions(), !keep_import_attributes), -+ Optional::new( -+ typescript::strip_with_jsx::>( -+ cm.clone(), -+ typescript::Config { -+ pragma: Some( -+ transform -+ .react -+ .pragma -+ .clone() -+ .unwrap_or_else(default_pragma) -+ ), -+ pragma_frag: Some( -+ transform -+ .react -+ .pragma_frag -+ .clone() -+ .unwrap_or_else(default_pragma_frag) -+ ), -+ ts_enum_config: TsEnumConfig { -+ treat_const_enum_as_enum: transform -+ .treat_const_enum_as_enum -+ .into_bool(), -+ ts_enum_is_readonly: assumptions.ts_enum_is_readonly, -+ }, -+ import_export_assign_config, -+ ..Default::default() -+ }, -+ comments.map(|v| v as _), -+ top_level_mark -+ ), -+ syntax.typescript() -+ ), -+ plugin_transforms, -+ custom_before_pass(&program), -+ // handle jsx -+ Optional::new( -+ react::react::<&dyn Comments>( -+ cm.clone(), -+ comments.map(|v| v as _), -+ transform.react, -+ top_level_mark, -+ unresolved_mark -+ ), -+ syntax.jsx() -+ ), -+ pass, -+ Optional::new(jest::jest(), transform.hidden.jest.into_bool()), -+ Optional::new( -+ dropped_comments_preserver(comments.cloned()), -+ preserve_all_comments -+ ), -+ )) -+ }; - - Ok(BuiltInput { - program, -@@ -859,6 +873,9 @@ impl Options { - preserve_comments, - emit_source_map_columns: cfg.emit_source_map_columns.into_bool(), - output: JscOutputConfig { charset, preamble }, -+ emit_assert_for_import_attributes: experimental -+ .emit_assert_for_import_attributes -+ .into_bool(), - }) - } - } -@@ -1231,6 +1248,9 @@ pub struct JsMinifyFormatOptions { - /// Not implemented yet. - #[serde(default, alias = "wrap_func_args")] - pub wrap_func_args: bool, -+ -+ #[serde(default)] -+ pub emit_assert_for_import_attributes: bool, - } - - fn default_comments() -> BoolOrDataConfig { -@@ -1340,6 +1360,7 @@ impl Config { - } - - /// One `BuiltConfig` per a directory with swcrc -+#[non_exhaustive] - pub struct BuiltInput { - pub program: Program, - pub pass: P, -@@ -1363,6 +1384,37 @@ pub struct BuiltInput { - pub emit_source_map_columns: bool, - - pub output: JscOutputConfig, -+ pub emit_assert_for_import_attributes: bool, -+} -+ -+impl

BuiltInput

-+where -+ P: swc_ecma_visit::Fold, -+{ -+ pub fn with_pass(self, map: impl FnOnce(P) -> N) -> BuiltInput -+ where -+ N: swc_ecma_visit::Fold, -+ { -+ BuiltInput { -+ program: self.program, -+ pass: map(self.pass), -+ syntax: self.syntax, -+ target: self.target, -+ minify: self.minify, -+ external_helpers: self.external_helpers, -+ source_maps: self.source_maps, -+ input_source_map: self.input_source_map, -+ is_module: self.is_module, -+ output_path: self.output_path, -+ source_file_name: self.source_file_name, -+ preserve_comments: self.preserve_comments, -+ inline_sources_content: self.inline_sources_content, -+ comments: self.comments, -+ emit_source_map_columns: self.emit_source_map_columns, -+ output: self.output, -+ emit_assert_for_import_attributes: self.emit_assert_for_import_attributes, -+ } -+ } - } - - /// `jsc` in `.swcrc`. -@@ -1446,7 +1498,10 @@ pub struct JscExperimental { - pub plugins: Option>, - /// If true, keeps import assertions in the output. - #[serde(default)] -- pub keep_import_assertions: BoolConfig, -+ pub keep_import_attributes: BoolConfig, -+ -+ #[serde(default)] -+ pub emit_assert_for_import_attributes: BoolConfig, - /// Location where swc may stores its intermediate cache. - /// Currently this is only being used for wasm plugin's bytecache. - /// Path should be absolute directory, which will be created if not exist. -@@ -1454,6 +1509,9 @@ pub struct JscExperimental { - /// and will not be considered as breaking changes. - #[serde(default)] - pub cache_root: Option, -+ -+ #[serde(default)] -+ pub disable_builtin_transforms_for_internal_testing: BoolConfig, - } - - #[derive(Debug, Clone, Copy, Serialize, Deserialize)] -@@ -1513,9 +1571,9 @@ pub enum ModuleConfig { - #[serde(rename = "systemjs")] - SystemJs(modules::system_js::Config), - #[serde(rename = "es6")] -- Es6, -+ Es6(EsModuleConfig), - #[serde(rename = "nodenext")] -- NodeNext, -+ NodeNext(EsModuleConfig), - } - - impl ModuleConfig { -@@ -1538,11 +1596,20 @@ impl ModuleConfig { - let skip_resolver = base_url.as_os_str().is_empty() && paths.is_empty(); - - match config { -- None | Some(ModuleConfig::Es6) | Some(ModuleConfig::NodeNext) => { -+ None => { -+ if skip_resolver { -+ Box::new(noop()) -+ } else { -+ let resolver = build_resolver(base_url, paths, false); -+ -+ Box::new(import_rewriter(base, resolver)) -+ } -+ } -+ Some(ModuleConfig::Es6(config)) | Some(ModuleConfig::NodeNext(config)) => { - if skip_resolver { - Box::new(noop()) - } else { -- let resolver = build_resolver(base_url, paths); -+ let resolver = build_resolver(base_url, paths, config.resolve_fully); - - Box::new(import_rewriter(base, resolver)) - } -@@ -1556,7 +1623,7 @@ impl ModuleConfig { - comments, - )) - } else { -- let resolver = build_resolver(base_url, paths); -+ let resolver = build_resolver(base_url, paths, config.resolve_fully); - Box::new(modules::common_js::common_js_with_resolver( - resolver, - base, -@@ -1577,7 +1644,7 @@ impl ModuleConfig { - comments, - )) - } else { -- let resolver = build_resolver(base_url, paths); -+ let resolver = build_resolver(base_url, paths, config.config.resolve_fully); - - Box::new(modules::umd::umd_with_resolver( - cm, -@@ -1599,7 +1666,7 @@ impl ModuleConfig { - comments, - )) - } else { -- let resolver = build_resolver(base_url, paths); -+ let resolver = build_resolver(base_url, paths, config.config.resolve_fully); - - Box::new(modules::amd::amd_with_resolver( - resolver, -@@ -1615,7 +1682,7 @@ impl ModuleConfig { - if skip_resolver { - Box::new(modules::system_js::system_js(unresolved_mark, config)) - } else { -- let resolver = build_resolver(base_url, paths); -+ let resolver = build_resolver(base_url, paths, config.resolve_fully); - - Box::new(modules::system_js::system_js_with_resolver( - resolver, -@@ -1941,27 +2008,51 @@ fn default_env_name() -> String { - } - } - --fn build_resolver(base_url: PathBuf, paths: CompiledPaths) -> Box { -- static CACHE: Lazy> = -+fn build_resolver( -+ mut base_url: PathBuf, -+ paths: CompiledPaths, -+ resolve_fully: bool, -+) -> Box { -+ static CACHE: Lazy> = - Lazy::new(Default::default); - -- if let Some(cached) = CACHE.get(&(base_url.clone(), paths.clone())) { -+ // On Windows, we need to normalize path as UNC path. -+ if cfg!(target_os = "windows") { -+ base_url = base_url -+ .canonicalize() -+ .with_context(|| { -+ format!( -+ "failed to canonicalize jsc.baseUrl(`{}`)\nThis is required on Windows \ -+ because of UNC path.", -+ base_url.display() -+ ) -+ }) -+ .unwrap(); -+ } -+ -+ if let Some(cached) = CACHE.get(&(base_url.clone(), paths.clone(), resolve_fully)) { - return Box::new((*cached).clone()); - } - - let r = { - let r = TsConfigResolver::new( -- NodeModulesResolver::new(Default::default(), Default::default(), true), -+ NodeModulesResolver::without_node_modules(Default::default(), Default::default(), true), - base_url.clone(), - paths.clone(), - ); - let r = CachingResolver::new(40, r); - -- let r = NodeImportResolver::with_base_dir(r, Some(base_url.clone())); -+ let r = NodeImportResolver::with_config( -+ r, -+ swc_ecma_transforms::modules::path::Config { -+ base_dir: Some(base_url.clone()), -+ resolve_fully, -+ }, -+ ); - Arc::new(r) - }; - -- CACHE.insert((base_url, paths), r.clone()); -+ CACHE.insert((base_url, paths, resolve_fully), r.clone()); - - Box::new(r) - } -diff --git a/crates/swc/src/lib.rs b/crates/swc/src/lib.rs -index 00782b1b30..9843855f58 100644 ---- a/crates/swc/src/lib.rs -+++ b/crates/swc/src/lib.rs -@@ -193,7 +193,7 @@ pub mod resolver { - preserve_symlinks: bool, - ) -> CachingResolver> { - let r = TsConfigResolver::new( -- NodeModulesResolver::new(target_env, alias, preserve_symlinks), -+ NodeModulesResolver::without_node_modules(target_env, alias, preserve_symlinks), - base_url, - paths, - ); -@@ -501,15 +501,13 @@ impl Compiler { - source_file_name: Option<&str>, - output_path: Option, - inline_sources_content: bool, -- target: EsVersion, - source_map: SourceMapsConfig, - source_map_names: &AHashMap, - orig: Option<&sourcemap::SourceMap>, -- minify: bool, - comments: Option<&dyn Comments>, - emit_source_map_columns: bool, -- ascii_only: bool, - preamble: &str, -+ codegen_config: swc_ecma_codegen::Config, - ) -> Result - where - T: Node + VisitWith, -@@ -535,17 +533,12 @@ impl Compiler { - w.preamble(preamble).unwrap(); - let mut wr = Box::new(w) as Box; - -- if minify { -+ if codegen_config.minify { - wr = Box::new(swc_ecma_codegen::text_writer::omit_trailing_semi(wr)); - } - - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- target, -- ascii_only, -- ..Default::default() -- }, -+ cfg: codegen_config, - comments, - cm: self.cm.clone(), - wr, -@@ -681,10 +674,10 @@ impl SourceMapGenConfig for SwcSourceMapConfig<'_> { - } - - fn skip(&self, f: &FileName) -> bool { -- if let FileName::Custom(s) = f { -- s.starts_with('<') -- } else { -- false -+ match f { -+ FileName::Internal(..) => true, -+ FileName::Custom(s) => s.starts_with('<'), -+ _ => false, - } - } - } -@@ -733,7 +726,7 @@ impl Compiler { - } - } - -- #[tracing::instrument(level = "info", skip_all)] -+ #[tracing::instrument(skip_all)] - pub fn read_config(&self, opts: &Options, name: &FileName) -> Result, Error> { - static CUR_DIR: Lazy = Lazy::new(|| { - if cfg!(target_arch = "wasm32") { -@@ -845,7 +838,7 @@ impl Compiler { - /// This method handles merging of config. - /// - /// This method does **not** parse module. -- #[tracing::instrument(level = "info", skip_all)] -+ #[tracing::instrument(skip_all)] - pub fn parse_js_as_input<'a, P>( - &'a self, - fm: Lrc, -@@ -908,7 +901,7 @@ impl Compiler { - }) - } - -- #[tracing::instrument(level = "info", skip_all)] -+ #[tracing::instrument(skip_all)] - pub fn transform( - &self, - handler: &Handler, -@@ -936,7 +929,7 @@ impl Compiler { - /// - /// This means, you can use `noop_visit_type`, `noop_fold_type` and - /// `noop_visit_mut_type` in your visitor to reduce the binary size. -- #[tracing::instrument(level = "info", skip_all)] -+ #[tracing::instrument(skip_all)] - pub fn process_js_with_custom_pass( - &self, - fm: Arc, -@@ -970,26 +963,9 @@ impl Compiler { - } - }; - -- let pass = chain!(config.pass, custom_after_pass(&config.program)); -- -- let config = BuiltInput { -- program: config.program, -- pass, -- syntax: config.syntax, -- target: config.target, -- minify: config.minify, -- external_helpers: config.external_helpers, -- source_maps: config.source_maps, -- input_source_map: config.input_source_map, -- is_module: config.is_module, -- output_path: config.output_path, -- source_file_name: config.source_file_name, -- preserve_comments: config.preserve_comments, -- inline_sources_content: config.inline_sources_content, -- comments: config.comments, -- emit_source_map_columns: config.emit_source_map_columns, -- output: config.output, -- }; -+ let after_pass = custom_after_pass(&config.program); -+ -+ let config = config.with_pass(|pass| chain!(pass, after_pass)); - - let orig = if config.source_maps.enabled() { - self.get_orig_src_map(&fm, &config.input_source_map, false)? -@@ -997,11 +973,11 @@ impl Compiler { - None - }; - -- self.process_js_inner(handler, orig.as_ref(), config) -+ self.apply_transforms(handler, orig.as_ref(), config) - }) - } - -- #[tracing::instrument(level = "info", skip(self, handler, opts))] -+ #[tracing::instrument(skip(self, handler, opts))] - pub fn process_js_file( - &self, - fm: Arc, -@@ -1019,7 +995,7 @@ impl Compiler { - ) - } - -- #[tracing::instrument(level = "info", skip_all)] -+ #[tracing::instrument(skip_all)] - pub fn minify( - &self, - fm: Arc, -@@ -1110,10 +1086,10 @@ impl Compiler { - jsx: true, - decorators: true, - decorators_before_export: true, -- import_assertions: true, -+ import_attributes: true, - ..Default::default() - }), -- IsModule::Bool(true), -+ IsModule::Bool(opts.module), - Some(&comments), - ) - .context("failed to parse input file")?; -@@ -1170,15 +1146,19 @@ impl Compiler { - Some(&fm.name.to_string()), - opts.output_path.clone().map(From::from), - opts.inline_sources_content, -- target, - source_map, - &source_map_names, - orig.as_ref(), -- true, - Some(&comments), - opts.emit_source_map_columns, -- opts.format.ascii_only, - &opts.format.preamble, -+ swc_ecma_codegen::Config::default() -+ .with_target(target) -+ .with_minify(true) -+ .with_ascii_only(opts.format.ascii_only) -+ .with_emit_assert_for_import_attributes( -+ opts.format.emit_assert_for_import_attributes, -+ ), - ) - }) - } -@@ -1186,7 +1166,7 @@ impl Compiler { - /// You can use custom pass with this method. - /// - /// There exists a [PassBuilder] to help building custom passes. -- #[tracing::instrument(level = "info", skip_all)] -+ #[tracing::instrument(skip_all)] - pub fn process_js( - &self, - handler: &Handler, -@@ -1207,8 +1187,8 @@ impl Compiler { - ) - } - -- #[tracing::instrument(level = "info", skip_all)] -- fn process_js_inner( -+ #[tracing::instrument(name = "swc::Compiler::apply_transforms", skip_all)] -+ fn apply_transforms( - &self, - handler: &Handler, - orig: Option<&sourcemap::SourceMap>, -@@ -1245,19 +1225,25 @@ impl Compiler { - config.source_file_name.as_deref(), - config.output_path, - config.inline_sources_content, -- config.target, - config.source_maps, - &source_map_names, - orig, -- config.minify, - config.comments.as_ref().map(|v| v as _), - config.emit_source_map_columns, -- config -- .output -- .charset -- .map(|v| matches!(v, OutputCharset::Ascii)) -- .unwrap_or(false), - &config.output.preamble, -+ swc_ecma_codegen::Config::default() -+ .with_target(config.target) -+ .with_minify(config.minify) -+ .with_ascii_only( -+ config -+ .output -+ .charset -+ .map(|v| matches!(v, OutputCharset::Ascii)) -+ .unwrap_or(false), -+ ) -+ .with_emit_assert_for_import_attributes( -+ config.emit_assert_for_import_attributes, -+ ), - ) - }) - } -@@ -1281,7 +1267,7 @@ fn find_swcrc(path: &Path, root: &Path, root_mode: RootMode) -> Option - None - } - --#[tracing::instrument(level = "info", skip_all)] -+#[tracing::instrument(skip_all)] - fn load_swcrc(path: &Path) -> Result { - let content = read_to_string(path).context("failed to read config (.swcrc) file")?; - -diff --git a/crates/swc/tests/exec.rs b/crates/swc/tests/exec.rs -index 6fa8403871..a21cbfe6cd 100644 ---- a/crates/swc/tests/exec.rs -+++ b/crates/swc/tests/exec.rs -@@ -194,7 +194,7 @@ fn create_matrix(entry: &Path) -> Vec { - ..Default::default() - }, - module: if entry.extension().unwrap() == "mjs" { -- Some(ModuleConfig::Es6) -+ Some(ModuleConfig::Es6(Default::default())) - } else { - Some(ModuleConfig::CommonJs(Default::default())) - }, -diff --git a/crates/swc/tests/projects.rs b/crates/swc/tests/projects.rs -index 2a2594d519..e49497cf37 100644 ---- a/crates/swc/tests/projects.rs -+++ b/crates/swc/tests/projects.rs -@@ -7,8 +7,8 @@ use anyhow::Context; - use rayon::prelude::*; - use swc::{ - config::{ -- BuiltInput, Config, FileMatcher, JsMinifyOptions, JscConfig, ModuleConfig, Options, -- SourceMapsConfig, TransformConfig, -+ Config, FileMatcher, JsMinifyOptions, JscConfig, ModuleConfig, Options, SourceMapsConfig, -+ TransformConfig, - }, - try_with_handler, BoolOrDataConfig, Compiler, TransformOutput, - }; -@@ -719,24 +719,7 @@ fn should_visit() { - - dbg!(config.syntax); - -- let config = BuiltInput { -- program: config.program, -- pass: chain!(Panicking, config.pass), -- syntax: config.syntax, -- target: config.target, -- minify: config.minify, -- external_helpers: config.external_helpers, -- source_maps: config.source_maps, -- input_source_map: config.input_source_map, -- is_module: config.is_module, -- output_path: config.output_path, -- source_file_name: config.source_file_name, -- preserve_comments: config.preserve_comments, -- inline_sources_content: config.inline_sources_content, -- comments: config.comments, -- emit_source_map_columns: config.emit_source_map_columns, -- output: config.output, -- }; -+ let config = config.with_pass(|pass| chain!(Panicking, pass)); - - if config.minify { - let preserve_excl = |_: &BytePos, vc: &mut Vec| -> bool { -@@ -760,16 +743,16 @@ fn should_visit() { - None, - config.output_path, - config.inline_sources_content, -- config.target, - config.source_maps, - &Default::default(), - None, - // TODO: figure out sourcemaps -- config.minify, - Some(&comments), - config.emit_source_map_columns, -- false, - Default::default(), -+ swc_ecma_codegen::Config::default() -+ .with_target(config.target) -+ .with_minify(config.minify), - ) - .unwrap() - .code) -@@ -1108,6 +1091,7 @@ fn issue_7513_2() { - fm, - handler, - &JsMinifyOptions { -+ module: true, - compress: BoolOrDataConfig::from_bool(true), - mangle: BoolOrDataConfig::from_obj(MangleOptions { - props: None, -diff --git a/crates/swc/tests/tsc.rs b/crates/swc/tests/tsc.rs -index ecf12920d7..7f2818b953 100644 ---- a/crates/swc/tests/tsc.rs -+++ b/crates/swc/tests/tsc.rs -@@ -315,8 +315,8 @@ fn matrix(input: &Path) -> Vec { - Self::Umd => ModuleConfig::Umd(Default::default()), - Self::Amd => ModuleConfig::Amd(Default::default()), - Self::SystemJs => ModuleConfig::SystemJs(Default::default()), -- Self::Es6 => ModuleConfig::Es6, -- Self::NodeNext => ModuleConfig::NodeNext, -+ Self::Es6 => ModuleConfig::Es6(Default::default()), -+ Self::NodeNext => ModuleConfig::NodeNext(Default::default()), - } - } - } -diff --git a/crates/swc_bundler/examples/bundle.rs b/crates/swc_bundler/examples/bundle.rs -index 8e1b606f15..8bbd9d6c02 100644 ---- a/crates/swc_bundler/examples/bundle.rs -+++ b/crates/swc_bundler/examples/bundle.rs -@@ -42,10 +42,7 @@ fn print_bundles(cm: Lrc, modules: Vec, minify: bool) { - { - let wr = JsWriter::new(cm.clone(), "\n", &mut buf, None); - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(true), - cm: cm.clone(), - comments: None, - wr: if minify { -diff --git a/crates/swc_bundler/examples/path.rs b/crates/swc_bundler/examples/path.rs -index 48ad850d84..7857b6902b 100644 ---- a/crates/swc_bundler/examples/path.rs -+++ b/crates/swc_bundler/examples/path.rs -@@ -46,10 +46,7 @@ fn main() { - - let wr = stdout(); - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify: false, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default(), - cm: cm.clone(), - comments: None, - wr: Box::new(JsWriter::new(cm, "\n", wr.lock(), None)), -diff --git a/crates/swc_bundler/src/bundler/chunk/computed_key.rs b/crates/swc_bundler/src/bundler/chunk/computed_key.rs -index 27ea5f3f70..660d1140a3 100644 ---- a/crates/swc_bundler/src/bundler/chunk/computed_key.rs -+++ b/crates/swc_bundler/src/bundler/chunk/computed_key.rs -@@ -84,7 +84,7 @@ where - specifiers: vec![specifier], - src: None, - type_only: false, -- asserts: None, -+ with: None, - })), - )); - } -diff --git a/crates/swc_bundler/src/bundler/chunk/merge.rs b/crates/swc_bundler/src/bundler/chunk/merge.rs -index d8c08cb833..281725d4b8 100644 ---- a/crates/swc_bundler/src/bundler/chunk/merge.rs -+++ b/crates/swc_bundler/src/bundler/chunk/merge.rs -@@ -768,7 +768,7 @@ where - specifiers: vec![specifier], - src: None, - type_only: false, -- asserts: None, -+ with: None, - }, - ))); - } -@@ -816,7 +816,7 @@ where - specifiers: vec![specifier], - src: None, - type_only: false, -- asserts: None, -+ with: None, - }, - ))); - } -@@ -883,7 +883,7 @@ where - .collect(), - src: None, - type_only: false, -- asserts: None, -+ with: None, - })); - extra.push(export); - continue; -@@ -925,7 +925,7 @@ where - specifiers: vec![specifier], - src: None, - type_only: false, -- asserts: None, -+ with: None, - }, - ))); - } -@@ -1140,7 +1140,7 @@ where - span: ns.span, - specifiers: vec![specifier], - src: None, -- asserts: None, -+ with: None, - type_only: false, - }), - )); -diff --git a/crates/swc_bundler/src/bundler/import/mod.rs b/crates/swc_bundler/src/bundler/import/mod.rs -index 7c3a56c98f..be8ab29a2c 100644 ---- a/crates/swc_bundler/src/bundler/import/mod.rs -+++ b/crates/swc_bundler/src/bundler/import/mod.rs -@@ -250,7 +250,7 @@ where - specifiers: vec![], - src: Box::new(src.clone()), - type_only: false, -- asserts: None, -+ with: None, - }; - - if self.top_level { -@@ -657,7 +657,7 @@ where - .collect(), - src: Box::new(src), - type_only: false, -- asserts: None, -+ with: None, - }; - - // if self.top_level { -diff --git a/crates/swc_bundler/src/bundler/load.rs b/crates/swc_bundler/src/bundler/load.rs -index 19edeb4a91..a50f6d0f2b 100644 ---- a/crates/swc_bundler/src/bundler/load.rs -+++ b/crates/swc_bundler/src/bundler/load.rs -@@ -304,7 +304,7 @@ where - specifiers: vec![], - src: Box::new(src), - type_only: false, -- asserts: None, -+ with: None, - }, - true, - false, -diff --git a/crates/swc_bundler/src/debug/mod.rs b/crates/swc_bundler/src/debug/mod.rs -index 372fbcd5a6..eca3c6ae12 100644 ---- a/crates/swc_bundler/src/debug/mod.rs -+++ b/crates/swc_bundler/src/debug/mod.rs -@@ -19,10 +19,7 @@ pub(crate) fn print_hygiene(event: &str, cm: &Lrc, t: &Module) { - - writeln!(w, "==================== @ {} ====================", event).unwrap(); - Emitter { -- cfg: swc_ecma_codegen::Config { -- minify: false, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default(), - cm: cm.clone(), - comments: None, - wr: Box::new(JsWriter::new(cm.clone(), "\n", &mut w, None)), -diff --git a/crates/swc_bundler/tests/deno.rs b/crates/swc_bundler/tests/deno.rs -index 4431ad4eed..ad593a30c7 100644 ---- a/crates/swc_bundler/tests/deno.rs -+++ b/crates/swc_bundler/tests/deno.rs -@@ -1085,10 +1085,7 @@ fn bundle(url: &str, minify: bool) -> String { - } - - Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(minify), - cm: cm.clone(), - comments: None, - wr, -diff --git a/crates/swc_common/src/input.rs b/crates/swc_common/src/input.rs -index 246a98b1d9..b08277c4ca 100644 ---- a/crates/swc_common/src/input.rs -+++ b/crates/swc_common/src/input.rs -@@ -46,7 +46,10 @@ impl<'a> StringInput<'a> { - - #[inline] - pub fn bump_bytes(&mut self, n: usize) { -- self.reset_to(self.last_pos + BytePos(n as u32)); -+ unsafe { -+ // Safety: We only proceed, not go back. -+ self.reset_to(self.last_pos + BytePos(n as u32)); -+ } - } - } - -@@ -78,7 +81,7 @@ impl<'a> Input for StringInput<'a> { - } - - #[inline] -- fn bump(&mut self) { -+ unsafe fn bump(&mut self) { - if let Some((i, c)) = self.iter.next() { - self.last_pos = self.start_pos_of_iter + BytePos((i + c.len_utf8()) as u32); - } else { -@@ -115,7 +118,7 @@ impl<'a> Input for StringInput<'a> { - } - - #[inline] -- fn slice(&mut self, start: BytePos, end: BytePos) -> &str { -+ unsafe fn slice(&mut self, start: BytePos, end: BytePos) -> &str { - debug_assert!(start <= end, "Cannot slice {:?}..{:?}", start, end); - let s = self.orig; - -@@ -184,7 +187,7 @@ impl<'a> Input for StringInput<'a> { - } - - #[inline] -- fn reset_to(&mut self, to: BytePos) { -+ unsafe fn reset_to(&mut self, to: BytePos) { - let orig = self.orig; - let idx = (to - self.orig_start).0 as usize; - -@@ -197,12 +200,12 @@ impl<'a> Input for StringInput<'a> { - - #[inline] - fn is_byte(&mut self, c: u8) -> bool { -- if self.iter.as_str().is_empty() { -- false -- } else { -- // Safety: We checked that `self.iter.as_str().len() > 0` -- unsafe { *self.iter.as_str().as_bytes().get_unchecked(0) == c } -- } -+ self.iter -+ .as_str() -+ .as_bytes() -+ .first() -+ .map(|b| *b == c) -+ .unwrap_or(false) - } - - #[inline] -@@ -233,7 +236,12 @@ pub trait Input: Clone { - fn cur(&mut self) -> Option; - fn peek(&mut self) -> Option; - fn peek_ahead(&mut self) -> Option; -- fn bump(&mut self); -+ -+ /// # Safety -+ /// -+ /// This should be called only when `cur()` returns `Some`. i.e. -+ /// when the Input is not empty. -+ unsafe fn bump(&mut self); - - /// Returns [None] if it's end of input **or** current character is not an - /// ascii character. -@@ -253,7 +261,11 @@ pub trait Input: Clone { - - fn last_pos(&self) -> BytePos; - -- fn slice(&mut self, start: BytePos, end: BytePos) -> &str; -+ /// # Safety -+ /// -+ /// - start should be less than or equal to end. -+ /// - start and end should be in the valid range of input. -+ unsafe fn slice(&mut self, start: BytePos, end: BytePos) -> &str; - - /// Takes items from stream, testing each one with predicate. returns the - /// range of items which passed predicate. -@@ -266,7 +278,10 @@ pub trait Input: Clone { - where - F: FnMut(char) -> bool; - -- fn reset_to(&mut self, to: BytePos); -+ /// # Safety -+ /// -+ /// - `to` be in the valid range of input. -+ unsafe fn reset_to(&mut self, to: BytePos); - - /// Implementors can override the method to make it faster. - /// -@@ -291,7 +306,10 @@ pub trait Input: Clone { - #[inline] - fn eat_byte(&mut self, c: u8) -> bool { - if self.is_byte(c) { -- self.bump(); -+ unsafe { -+ // Safety: We are sure that the input is not empty -+ self.bump(); -+ } - true - } else { - false -@@ -319,13 +337,13 @@ mod tests { - #[test] - fn src_input_slice_1() { - with_test_sess("foo/d", |mut i| { -- assert_eq!(i.slice(BytePos(1), BytePos(2)), "f"); -+ assert_eq!(unsafe { i.slice(BytePos(1), BytePos(2)) }, "f"); - assert_eq!(i.last_pos, BytePos(2)); - assert_eq!(i.start_pos_of_iter, BytePos(2)); - assert_eq!(i.cur(), Some('o')); - -- assert_eq!(i.slice(BytePos(2), BytePos(4)), "oo"); -- assert_eq!(i.slice(BytePos(1), BytePos(4)), "foo"); -+ assert_eq!(unsafe { i.slice(BytePos(2), BytePos(4)) }, "oo"); -+ assert_eq!(unsafe { i.slice(BytePos(1), BytePos(4)) }, "foo"); - assert_eq!(i.last_pos, BytePos(4)); - assert_eq!(i.start_pos_of_iter, BytePos(4)); - assert_eq!(i.cur(), Some('/')); -@@ -335,11 +353,11 @@ mod tests { - #[test] - fn src_input_reset_to_1() { - with_test_sess("load", |mut i| { -- assert_eq!(i.slice(BytePos(1), BytePos(3)), "lo"); -+ assert_eq!(unsafe { i.slice(BytePos(1), BytePos(3)) }, "lo"); - assert_eq!(i.last_pos, BytePos(3)); - assert_eq!(i.start_pos_of_iter, BytePos(3)); - assert_eq!(i.cur(), Some('a')); -- i.reset_to(BytePos(1)); -+ unsafe { i.reset_to(BytePos(1)) }; - - assert_eq!(i.cur(), Some('l')); - assert_eq!(i.last_pos, BytePos(1)); -@@ -360,11 +378,15 @@ mod tests { - assert_eq!(i.start_pos_of_iter, BytePos(4)); - assert_eq!(i.cur(), Some('/')); - -- i.bump(); -+ unsafe { -+ i.bump(); -+ } - assert_eq!(i.last_pos, BytePos(5)); - assert_eq!(i.cur(), Some('d')); - -- i.bump(); -+ unsafe { -+ i.bump(); -+ } - assert_eq!(i.last_pos, BytePos(6)); - assert_eq!(i.cur(), None); - }); -diff --git a/crates/swc_common/src/source_map.rs b/crates/swc_common/src/source_map.rs -index 1b8c97f8aa..cc121bee11 100644 ---- a/crates/swc_common/src/source_map.rs -+++ b/crates/swc_common/src/source_map.rs -@@ -1266,6 +1266,9 @@ impl SourceMap { - Some(ref f) if f.start_pos <= pos && pos < f.end_pos => f, - _ => { - f = self.lookup_source_file(pos); -+ if config.skip(&f.name) { -+ continue; -+ } - src_id = builder.add_source(&config.file_name_to_source(&f.name)); - - inline_sources_content = config.inline_sources_content(&f.name); -@@ -1447,8 +1450,9 @@ pub trait SourceMapGenConfig { - true - } - -- fn skip(&self, _f: &FileName) -> bool { -- false -+ /// By default, we skip internal files. -+ fn skip(&self, f: &FileName) -> bool { -+ matches!(f, FileName::Internal(..)) - } - } - -diff --git a/crates/swc_css_ast/src/at_rule.rs b/crates/swc_css_ast/src/at_rule.rs -index 612454ccab..460ad0b509 100644 ---- a/crates/swc_css_ast/src/at_rule.rs -+++ b/crates/swc_css_ast/src/at_rule.rs -@@ -4,8 +4,9 @@ use swc_atoms::{Atom, JsWord}; - use swc_common::{ast_node, util::take::Take, EqIgnoreSpan, Span}; - - use crate::{ -- CustomIdent, CustomPropertyName, DashedIdent, Declaration, Dimension, FamilyName, Function, -- Ident, ListOfComponentValues, Number, Percentage, Ratio, SelectorList, SimpleBlock, Str, Url, -+ CustomIdent, CustomPropertyName, DashedIdent, Declaration, Dimension, FamilyName, -+ ForgivingSelectorList, Function, Ident, ListOfComponentValues, Number, Percentage, Ratio, -+ SelectorList, SimpleBlock, Str, Url, - }; - - #[ast_node("AtRule")] -@@ -84,6 +85,18 @@ pub enum AtRulePrelude { - ContainerPrelude(ContainerCondition), - #[tag("CustomMedia")] - CustomMediaPrelude(CustomMediaQuery), -+ #[tag("ScopeRange")] -+ ScopePrelude(ScopeRange), -+} -+ -+#[ast_node("ScopeRange")] -+#[derive(Eq, Hash, EqIgnoreSpan)] -+pub struct ScopeRange { -+ pub span: Span, -+ /// https://drafts.csswg.org/css-cascade-6/#typedef-scope-start -+ pub scope_start: Option, -+ /// https://drafts.csswg.org/css-cascade-6/#typedef-scope-end -+ pub scope_end: Option, - } - - #[ast_node] -diff --git a/crates/swc_css_codegen/src/lib.rs b/crates/swc_css_codegen/src/lib.rs -index f3ff1339ca..dd4bf349ae 100644 ---- a/crates/swc_css_codegen/src/lib.rs -+++ b/crates/swc_css_codegen/src/lib.rs -@@ -324,6 +324,9 @@ where - n - ) - } -+ AtRulePrelude::ScopePrelude(n) => { -+ emit!(self, n); -+ } - } - } - -@@ -2459,6 +2462,23 @@ where - } - } - -+ #[emitter] -+ fn emit_scope_range(&mut self, n: &ScopeRange) -> Result { -+ if let Some(start) = &n.scope_start { -+ formatting_space!(self); -+ write_raw!(self, "("); -+ emit!(self, start); -+ write_raw!(self, ")"); -+ } -+ if let Some(end) = &n.scope_end { -+ write_raw!(self, " to"); -+ space!(self); -+ write_raw!(self, "("); -+ emit!(self, end); -+ write_raw!(self, ")"); -+ } -+ } -+ - fn emit_list_pseudo_element_selector_children( - &mut self, - nodes: &[PseudoElementSelectorChildren], -diff --git a/crates/swc_css_modules/src/lib.rs b/crates/swc_css_modules/src/lib.rs -index 81f4da7056..49678021c4 100644 ---- a/crates/swc_css_modules/src/lib.rs -+++ b/crates/swc_css_modules/src/lib.rs -@@ -204,13 +204,13 @@ where - n.visit_mut_children_with(self); - - if let QualifiedRulePrelude::SelectorList(sel) = &n.prelude { -- // -- if sel.children.len() == 1 && sel.children[0].children.len() == 1 { -- if let ComplexSelectorChildren::CompoundSelector(sel) = &sel.children[0].children[0] -- { -- if sel.subclass_selectors.len() == 1 { -- if let SubclassSelector::Class(class_sel) = &sel.subclass_selectors[0] { -- if let Some(composes) = self.data.composes_for_current.take() { -+ let composes = self.data.composes_for_current.take(); -+ -+ for child in &sel.children { -+ if let ComplexSelectorChildren::CompoundSelector(sel) = &child.children[0] { -+ for subclass_sel in &sel.subclass_selectors { -+ if let SubclassSelector::Class(class_sel) = &subclass_sel { -+ if let Some(composes) = &composes { - let key = self - .data - .renamed_to_orig -@@ -218,7 +218,27 @@ where - .cloned(); - - if let Some(key) = key { -- self.result.renamed.entry(key).or_default().extend(composes); -+ let mut renamed = self.result.renamed.clone(); -+ let class_names = self.result.renamed.entry(key).or_default(); -+ -+ class_names.extend(composes.clone()); -+ -+ for composed_class_name in composes.iter() { -+ if let CssClassName::Local { name } = composed_class_name { -+ if let Some(original_class_name) = -+ self.data.renamed_to_orig.get(&name.value) -+ { -+ class_names.extend( -+ renamed -+ .entry(original_class_name.clone()) -+ .or_default() -+ .split_at(1) -+ .1 -+ .to_vec(), -+ ); -+ } -+ } -+ } - } - } - } -diff --git a/crates/swc_css_parser/src/error.rs b/crates/swc_css_parser/src/error.rs -index 56b0857860..6ebdf85e4c 100644 ---- a/crates/swc_css_parser/src/error.rs -+++ b/crates/swc_css_parser/src/error.rs -@@ -71,6 +71,7 @@ impl Error { - ErrorKind::InvalidKeyframesName(s) => { - format!("{} is not valid name for keyframes", s).into() - } -+ ErrorKind::InvalidScopeAtRule => "Invalid @scope at-rule".into(), - } - } - -@@ -115,6 +116,7 @@ pub enum ErrorKind { - InvalidAnPlusBMicrosyntax, - InvalidCustomIdent(JsWord), - InvalidKeyframesName(&'static str), -+ InvalidScopeAtRule, - - UnknownAtRuleNotTerminated, - } -diff --git a/crates/swc_css_parser/src/lexer/mod.rs b/crates/swc_css_parser/src/lexer/mod.rs -index 3fb292d6f4..ad05725ceb 100644 ---- a/crates/swc_css_parser/src/lexer/mod.rs -+++ b/crates/swc_css_parser/src/lexer/mod.rs -@@ -139,7 +139,10 @@ where - } - - fn reset(&mut self, state: &Self::State) { -- self.input.reset_to(state.pos); -+ unsafe { -+ // Safety: state.pos is created from a valid position. -+ self.input.reset_to(state.pos); -+ } - } - - fn take_errors(&mut self) -> Vec { -@@ -199,7 +202,10 @@ where - self.cur_pos = self.input.last_pos(); - - if cur.is_some() { -- self.input.bump(); -+ unsafe { -+ // Safety: cur is Some -+ self.input.bump(); -+ } - } - - cur -@@ -207,7 +213,11 @@ where - - #[inline(always)] - fn reconsume(&mut self) { -- self.input.reset_to(self.cur_pos); -+ unsafe { -+ // Safety: self.cur_pos is a position generated by self.input, meaning it is -+ // valid. -+ self.input.reset_to(self.cur_pos); -+ } - } - - #[cold] -diff --git a/crates/swc_css_parser/src/macros.rs b/crates/swc_css_parser/src/macros.rs -index 8aca0cdfe9..4a8f21bb09 100644 ---- a/crates/swc_css_parser/src/macros.rs -+++ b/crates/swc_css_parser/src/macros.rs -@@ -146,4 +146,8 @@ macro_rules! tok { - (">") => { - swc_css_ast::Token::Delim { value: '>' } - }; -+ -+ ("to") => { -+ swc_css_ast::Token::Ident { value: js_word!("to"), .. } -+ }; - } -diff --git a/crates/swc_css_parser/src/parser/at_rules/mod.rs b/crates/swc_css_parser/src/parser/at_rules/mod.rs -index 46f25c7a8e..fee6245cc5 100644 ---- a/crates/swc_css_parser/src/parser/at_rules/mod.rs -+++ b/crates/swc_css_parser/src/parser/at_rules/mod.rs -@@ -424,6 +424,15 @@ where - - None - } -+ js_word!("scope") => { -+ self.input.skip_ws(); -+ -+ let prelude = AtRulePrelude::ScopePrelude(self.parse()?); -+ -+ self.input.skip_ws(); -+ -+ Some(prelude) -+ } - _ => { - return Err(Error::new(Default::default(), ErrorKind::Ignore)); - } -@@ -756,6 +765,13 @@ where - - rule_list - } -+ js_word!("scope") => { -+ let rule_list = self.parse_as::>()?; -+ let rule_list: Vec = -+ rule_list.into_iter().map(ComponentValue::from).collect(); -+ -+ rule_list -+ } - _ => { - return Err(Error::new(Default::default(), ErrorKind::Ignore)); - } -@@ -2625,3 +2641,67 @@ where - }) - } - } -+ -+impl Parse for Parser -+where -+ I: ParserInput, -+{ -+ fn parse(&mut self) -> PResult { -+ let span = self.input.cur_span(); -+ -+ if is!(self, EOF) { -+ return Ok(ScopeRange { -+ span: span!(self, span.lo), -+ scope_start: None, -+ scope_end: None, -+ }); -+ } -+ -+ match cur!(self) { -+ tok!("(") => { -+ bump!(self); -+ let start = self.parse()?; -+ expect!(self, ")"); -+ self.input.skip_ws(); -+ -+ let end = if is!(self, EOF) { -+ None -+ } else if is_case_insensitive_ident!(self, "to") { -+ bump!(self); -+ self.input.skip_ws(); -+ expect!(self, "("); -+ let result = self.parse()?; -+ expect!(self, ")"); -+ Some(result) -+ } else { -+ None -+ }; -+ -+ Ok(ScopeRange { -+ span: span!(self, span.lo), -+ scope_start: Some(start), -+ scope_end: end, -+ }) -+ } -+ _ => { -+ if is_case_insensitive_ident!(self, "to") { -+ bump!(self); -+ -+ self.input.skip_ws(); -+ -+ expect!(self, "("); -+ let end = self.parse()?; -+ expect!(self, ")"); -+ -+ return Ok(ScopeRange { -+ span: span!(self, span.lo), -+ scope_start: None, -+ scope_end: Some(end), -+ }); -+ } -+ -+ return Err(Error::new(span, ErrorKind::InvalidScopeAtRule)); -+ } -+ } -+ } -+} -diff --git a/crates/swc_css_visit/src/lib.rs b/crates/swc_css_visit/src/lib.rs -index 672907ad44..e0bbf0ce63 100644 ---- a/crates/swc_css_visit/src/lib.rs -+++ b/crates/swc_css_visit/src/lib.rs -@@ -641,6 +641,13 @@ define!({ - LayerPrelude(LayerPrelude), - ContainerPrelude(ContainerCondition), - CustomMediaPrelude(CustomMediaQuery), -+ ScopePrelude(ScopeRange), -+ } -+ -+ pub struct ScopeRange { -+ pub span: Span, -+ pub scope_start: Option, -+ pub scope_end: Option, - } - - pub struct ListOfComponentValues { -diff --git a/crates/swc_ecma_ast/src/module_decl.rs b/crates/swc_ecma_ast/src/module_decl.rs -index 2de8963636..368299b140 100644 ---- a/crates/swc_ecma_ast/src/module_decl.rs -+++ b/crates/swc_ecma_ast/src/module_decl.rs -@@ -84,7 +84,7 @@ pub struct ImportDecl { - pub type_only: bool, - - #[cfg_attr(feature = "serde-impl", serde(default))] -- pub asserts: Option>, -+ pub with: Option>, - } - - impl Take for ImportDecl { -@@ -94,7 +94,7 @@ impl Take for ImportDecl { - specifiers: Take::dummy(), - src: Take::dummy(), - type_only: Default::default(), -- asserts: Take::dummy(), -+ with: Take::dummy(), - } - } - } -@@ -113,7 +113,7 @@ pub struct ExportAll { - pub type_only: bool, - - #[cfg_attr(feature = "serde-impl", serde(default))] -- pub asserts: Option>, -+ pub with: Option>, - } - - impl Take for ExportAll { -@@ -122,7 +122,7 @@ impl Take for ExportAll { - span: DUMMY_SP, - src: Take::dummy(), - type_only: Default::default(), -- asserts: Take::dummy(), -+ with: Take::dummy(), - } - } - } -@@ -144,7 +144,7 @@ pub struct NamedExport { - pub type_only: bool, - - #[cfg_attr(feature = "serde-impl", serde(default))] -- pub asserts: Option>, -+ pub with: Option>, - } - - impl Take for NamedExport { -@@ -154,7 +154,7 @@ impl Take for NamedExport { - specifiers: Take::dummy(), - src: Take::dummy(), - type_only: Default::default(), -- asserts: Take::dummy(), -+ with: Take::dummy(), - } - } - } -diff --git a/crates/swc_ecma_codegen/src/config.rs b/crates/swc_ecma_codegen/src/config.rs -index 204d77a041..90ff3f84d4 100644 ---- a/crates/swc_ecma_codegen/src/config.rs -+++ b/crates/swc_ecma_codegen/src/config.rs -@@ -5,6 +5,7 @@ use swc_ecma_ast::EsVersion; - #[derive(Debug, Clone, Copy)] - #[cfg_attr(feature = "serde-impl", derive(Serialize, Deserialize))] - #[cfg_attr(feature = "serde-impl", serde(rename_all = "camelCase"))] -+#[non_exhaustive] - pub struct Config { - /// The target runtime environment. - /// -@@ -32,6 +33,9 @@ pub struct Config { - /// Defaults to `false`. - #[cfg_attr(feature = "serde-impl", serde(default))] - pub omit_last_semi: bool, -+ -+ #[cfg_attr(feature = "serde-impl", serde(default))] -+ pub emit_assert_for_import_attributes: bool, - } - - impl Default for Config { -@@ -41,6 +45,37 @@ impl Default for Config { - minify: false, - ascii_only: false, - omit_last_semi: false, -+ emit_assert_for_import_attributes: false, - } - } - } -+ -+impl Config { -+ pub fn with_target(mut self, target: EsVersion) -> Self { -+ self.target = target; -+ self -+ } -+ -+ pub fn with_minify(mut self, minify: bool) -> Self { -+ self.minify = minify; -+ self -+ } -+ -+ pub fn with_ascii_only(mut self, ascii_only: bool) -> Self { -+ self.ascii_only = ascii_only; -+ self -+ } -+ -+ pub fn with_omit_last_semi(mut self, omit_last_semi: bool) -> Self { -+ self.omit_last_semi = omit_last_semi; -+ self -+ } -+ -+ pub fn with_emit_assert_for_import_attributes( -+ mut self, -+ emit_assert_for_import_attributes: bool, -+ ) -> Self { -+ self.emit_assert_for_import_attributes = emit_assert_for_import_attributes; -+ self -+ } -+} -diff --git a/crates/swc_ecma_codegen/src/lib.rs b/crates/swc_ecma_codegen/src/lib.rs -index 72a7dc387a..a1522134f8 100644 ---- a/crates/swc_ecma_codegen/src/lib.rs -+++ b/crates/swc_ecma_codegen/src/lib.rs -@@ -308,11 +308,15 @@ where - - emit!(n.src); - -- if let Some(asserts) = &n.asserts { -+ if let Some(with) = &n.with { - formatting_space!(); -- keyword!("assert"); -+ if self.cfg.emit_assert_for_import_attributes { -+ keyword!("assert"); -+ } else { -+ keyword!("with") -+ }; - formatting_space!(); -- emit!(asserts); -+ emit!(with); - } - - semi!(); -@@ -451,11 +455,15 @@ where - formatting_space!(); - emit!(src); - -- if let Some(asserts) = &node.asserts { -+ if let Some(with) = &node.with { - formatting_space!(); -- keyword!("assert"); -+ if self.cfg.emit_assert_for_import_attributes { -+ keyword!("assert"); -+ } else { -+ keyword!("with") -+ }; - formatting_space!(); -- emit!(asserts); -+ emit!(with); - } - } - semi!(); -@@ -477,11 +485,15 @@ where - formatting_space!(); - emit!(node.src); - -- if let Some(asserts) = &node.asserts { -+ if let Some(with) = &node.with { - formatting_space!(); -- keyword!("assert"); -+ if self.cfg.emit_assert_for_import_attributes { -+ keyword!("assert"); -+ } else { -+ keyword!("with") -+ }; - formatting_space!(); -- emit!(asserts); -+ emit!(with); - } - - semi!(); -@@ -1879,12 +1891,23 @@ where - } - - if let Some(ref arg) = node.arg { -- if !node.delegate && arg.starts_with_alpha_num() { -+ let need_paren = node -+ .arg -+ .as_deref() -+ .map(|expr| self.has_leading_comment(expr)) -+ .unwrap_or(false); -+ if need_paren { -+ punct!("(") -+ } else if !node.delegate && arg.starts_with_alpha_num() { - space!() - } else { - formatting_space!() - } -+ - emit!(node.arg); -+ if need_paren { -+ punct!(")") -+ } - } - } - -@@ -2743,6 +2766,8 @@ where - #[emitter] - #[tracing::instrument(skip_all)] - fn emit_expr_stmt(&mut self, e: &ExprStmt) -> Result { -+ self.emit_leading_comments_of_span(e.span, false)?; -+ - emit!(e.expr); - - semi!(); -diff --git a/crates/swc_ecma_codegen/tests/fixture.rs b/crates/swc_ecma_codegen/tests/fixture.rs -index d162af295e..aba025d528 100644 ---- a/crates/swc_ecma_codegen/tests/fixture.rs -+++ b/crates/swc_ecma_codegen/tests/fixture.rs -@@ -49,10 +49,7 @@ fn run(input: &Path, minify: bool) { - } - - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(minify), - cm, - comments: None, - wr, -diff --git a/crates/swc_ecma_codegen/tests/sourcemap.rs b/crates/swc_ecma_codegen/tests/sourcemap.rs -index b8d2e7af2d..dd8358b06d 100644 ---- a/crates/swc_ecma_codegen/tests/sourcemap.rs -+++ b/crates/swc_ecma_codegen/tests/sourcemap.rs -@@ -322,12 +322,10 @@ fn identity(entry: PathBuf) { - wr = Box::new(swc_ecma_codegen::text_writer::omit_trailing_semi(wr)); - - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify: true, -- target: EsVersion::Es5, -- ascii_only: true, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default() -+ .with_minify(true) -+ .with_ascii_only(true) -+ .with_target(EsVersion::Es5), - cm: cm.clone(), - wr, - comments: None, -diff --git a/crates/swc_ecma_codegen/tests/test262.rs b/crates/swc_ecma_codegen/tests/test262.rs -index 8e0b270283..b8e60b4e4a 100644 ---- a/crates/swc_ecma_codegen/tests/test262.rs -+++ b/crates/swc_ecma_codegen/tests/test262.rs -@@ -136,11 +136,9 @@ fn do_test(entry: &Path, minify: bool) { - } - - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- target: EsVersion::Es5, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default() -+ .with_minify(minify) -+ .with_target(EsVersion::Es5), - cm, - wr, - comments: if minify { None } else { Some(&comments) }, -diff --git a/crates/swc_ecma_dep_graph/src/lib.rs b/crates/swc_ecma_dep_graph/src/lib.rs -index db84d697fa..e94780e9a5 100644 ---- a/crates/swc_ecma_dep_graph/src/lib.rs -+++ b/crates/swc_ecma_dep_graph/src/lib.rs -@@ -41,7 +41,7 @@ pub enum ImportAssertion { - } - - #[derive(Clone, Debug, Eq, PartialEq)] --pub enum ImportAssertions { -+pub enum ImportAttributes { - /// There was no import assertions object literal. - None, - /// The set of assertion keys could not be statically analyzed. -@@ -51,16 +51,16 @@ pub enum ImportAssertions { - Known(HashMap), - } - --impl Default for ImportAssertions { -+impl Default for ImportAttributes { - fn default() -> Self { -- ImportAssertions::None -+ ImportAttributes::None - } - } - --impl ImportAssertions { -+impl ImportAttributes { - pub fn get(&self, key: &str) -> Option<&String> { - match self { -- ImportAssertions::Known(map) => match map.get(key) { -+ ImportAttributes::Known(map) => match map.get(key) { - Some(ImportAssertion::Known(value)) => Some(value), - _ => None, - }, -@@ -84,7 +84,7 @@ pub struct DependencyDescriptor { - /// The span of the specifier. - pub specifier_span: Span, - /// Import assertions for this dependency. -- pub import_assertions: ImportAssertions, -+ pub import_attributes: ImportAttributes, - } - - struct DependencyCollector<'a> { -@@ -110,7 +110,7 @@ impl<'a> Visit for DependencyCollector<'a> { - } else { - DependencyKind::Import - }; -- let import_assertions = parse_import_assertions(node.asserts.as_deref()); -+ let import_attributes = parse_import_attributes(node.with.as_deref()); - self.items.push(DependencyDescriptor { - kind, - is_dynamic: false, -@@ -118,7 +118,7 @@ impl<'a> Visit for DependencyCollector<'a> { - span: node.span, - specifier, - specifier_span: node.src.span, -- import_assertions, -+ import_attributes, - }); - } - -@@ -131,7 +131,7 @@ impl<'a> Visit for DependencyCollector<'a> { - } else { - DependencyKind::Export - }; -- let import_assertions = parse_import_assertions(node.asserts.as_deref()); -+ let import_attributes = parse_import_attributes(node.with.as_deref()); - self.items.push(DependencyDescriptor { - kind, - is_dynamic: false, -@@ -139,7 +139,7 @@ impl<'a> Visit for DependencyCollector<'a> { - span: node.span, - specifier, - specifier_span: src.span, -- import_assertions, -+ import_attributes, - }); - } - } -@@ -152,7 +152,7 @@ impl<'a> Visit for DependencyCollector<'a> { - } else { - DependencyKind::Export - }; -- let import_assertions = parse_import_assertions(node.asserts.as_deref()); -+ let import_attributes = parse_import_attributes(node.with.as_deref()); - self.items.push(DependencyDescriptor { - kind, - is_dynamic: false, -@@ -160,7 +160,7 @@ impl<'a> Visit for DependencyCollector<'a> { - span: node.span, - specifier, - specifier_span: node.src.span, -- import_assertions, -+ import_attributes, - }); - } - -@@ -175,8 +175,9 @@ impl<'a> Visit for DependencyCollector<'a> { - span: node.span, - specifier, - specifier_span: node.arg.span, -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }); -+ node.visit_children_with(self); - } - - fn visit_module_items(&mut self, items: &[ast::ModuleItem]) { -@@ -230,7 +231,7 @@ impl<'a> Visit for DependencyCollector<'a> { - span: node.span, - specifier, - specifier_span: str_.span, -- import_assertions: dynamic_import_assertions, -+ import_attributes: dynamic_import_assertions, - }); - } - } -@@ -259,7 +260,7 @@ impl<'a> Visit for DependencyCollector<'a> { - span: node.span, - specifier, - specifier_span: expr.span, -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }); - } - } -@@ -268,13 +269,13 @@ impl<'a> Visit for DependencyCollector<'a> { - /// Parses import assertions into a hashmap. According to proposal the values - /// can only be strings (https://github.com/tc39/proposal-import-assertions#should-more-than-just-strings-be-supported-as-attribute-values) - /// and thus non-string values are skipped. --fn parse_import_assertions(asserts: Option<&ast::ObjectLit>) -> ImportAssertions { -- let asserts = match asserts { -- Some(asserts) => asserts, -- None => return ImportAssertions::None, -+fn parse_import_attributes(attrs: Option<&ast::ObjectLit>) -> ImportAttributes { -+ let attrs = match attrs { -+ Some(with) => with, -+ None => return ImportAttributes::None, - }; - let mut import_assertions = HashMap::new(); -- for prop in asserts.props.iter() { -+ for prop in attrs.props.iter() { - if let ast::PropOrSpread::Prop(prop) = prop { - if let ast::Prop::KeyValue(key_value) = &**prop { - let maybe_key = match &key_value.key { -@@ -292,23 +293,23 @@ fn parse_import_assertions(asserts: Option<&ast::ObjectLit>) -> ImportAssertions - } - } - } -- ImportAssertions::Known(import_assertions) -+ ImportAttributes::Known(import_assertions) - } - - /// Parses import assertions from the second arg of a dynamic import. --fn parse_dynamic_import_assertions(arg: Option<&ast::ExprOrSpread>) -> ImportAssertions { -+fn parse_dynamic_import_assertions(arg: Option<&ast::ExprOrSpread>) -> ImportAttributes { - let arg = match arg { - Some(arg) => arg, -- None => return ImportAssertions::None, -+ None => return ImportAttributes::None, - }; - - if arg.spread.is_some() { -- return ImportAssertions::Unknown; -+ return ImportAttributes::Unknown; - } - - let object_lit = match &*arg.expr { - ast::Expr::Object(object_lit) => object_lit, -- _ => return ImportAssertions::Unknown, -+ _ => return ImportAttributes::Unknown, - }; - - let mut assertions_map = HashMap::new(); -@@ -317,37 +318,37 @@ fn parse_dynamic_import_assertions(arg: Option<&ast::ExprOrSpread>) -> ImportAss - for prop in object_lit.props.iter() { - let prop = match prop { - ast::PropOrSpread::Prop(prop) => prop, -- _ => return ImportAssertions::Unknown, -+ _ => return ImportAttributes::Unknown, - }; - let key_value = match &**prop { - ast::Prop::KeyValue(key_value) => key_value, -- _ => return ImportAssertions::Unknown, -+ _ => return ImportAttributes::Unknown, - }; - let key = match &key_value.key { - ast::PropName::Str(key) => key.value.to_string(), - ast::PropName::Ident(ident) => ident.sym.to_string(), -- _ => return ImportAssertions::Unknown, -+ _ => return ImportAttributes::Unknown, - }; -- if key == "assert" { -+ if key == "assert" || key == "with" { - had_assert_key = true; - let assertions_lit = match &*key_value.value { - ast::Expr::Object(assertions_lit) => assertions_lit, -- _ => return ImportAssertions::Unknown, -+ _ => return ImportAttributes::Unknown, - }; - - for prop in assertions_lit.props.iter() { - let prop = match prop { - ast::PropOrSpread::Prop(prop) => prop, -- _ => return ImportAssertions::Unknown, -+ _ => return ImportAttributes::Unknown, - }; - let key_value = match &**prop { - ast::Prop::KeyValue(key_value) => key_value, -- _ => return ImportAssertions::Unknown, -+ _ => return ImportAttributes::Unknown, - }; - let key = match &key_value.key { - ast::PropName::Str(key) => key.value.to_string(), - ast::PropName::Ident(ident) => ident.sym.to_string(), -- _ => return ImportAssertions::Unknown, -+ _ => return ImportAttributes::Unknown, - }; - if let ast::Expr::Lit(value_lit) = &*key_value.value { - assertions_map.insert( -@@ -366,9 +367,9 @@ fn parse_dynamic_import_assertions(arg: Option<&ast::ExprOrSpread>) -> ImportAss - } - - if had_assert_key { -- ImportAssertions::Known(assertions_map) -+ ImportAttributes::Known(assertions_map) - } else { -- ImportAssertions::None -+ ImportAttributes::None - } - } - -@@ -477,7 +478,7 @@ try { - span: Span::new(BytePos(1), BytePos(34), Default::default()), - specifier: JsWord::from("./test.ts"), - specifier_span: Span::new(BytePos(22), BytePos(33), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::ImportType, -@@ -490,7 +491,7 @@ try { - span: Span::new(BytePos(48), BytePos(86), Default::default()), - specifier: JsWord::from("./foo.d.ts"), - specifier_span: Span::new(BytePos(73), BytePos(85), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::Export, -@@ -503,7 +504,7 @@ try { - span: Span::new(BytePos(115), BytePos(149), Default::default()), - specifier: JsWord::from("./buzz.ts"), - specifier_span: Span::new(BytePos(137), BytePos(148), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::ExportType, -@@ -523,7 +524,7 @@ try { - span: Span::new(BytePos(181), BytePos(221), Default::default()), - specifier: JsWord::from("./fizz.d.ts"), - specifier_span: Span::new(BytePos(207), BytePos(220), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::Require, -@@ -532,7 +533,7 @@ try { - span: Span::new(BytePos(239), BytePos(254), Default::default()), - specifier: JsWord::from("path"), - specifier_span: Span::new(BytePos(247), BytePos(253), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -541,7 +542,7 @@ try { - span: Span::new(BytePos(274), BytePos(293), Default::default()), - specifier: JsWord::from("./foo1.ts"), - specifier_span: Span::new(BytePos(281), BytePos(292), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -550,7 +551,7 @@ try { - span: Span::new(BytePos(324), BytePos(342), Default::default()), - specifier: JsWord::from("./foo.ts"), - specifier_span: Span::new(BytePos(331), BytePos(341), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::Require, -@@ -559,7 +560,7 @@ try { - span: Span::new(BytePos(395), BytePos(418), Default::default()), - specifier: JsWord::from("some_package"), - specifier_span: Span::new(BytePos(403), BytePos(417), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::ImportEquals, -@@ -568,7 +569,7 @@ try { - span: Span::new(BytePos(449), BytePos(491), Default::default()), - specifier: JsWord::from("some_package_foo"), - specifier_span: Span::new(BytePos(471), BytePos(489), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::ImportType, -@@ -577,7 +578,7 @@ try { - span: Span::new(BytePos(492), BytePos(547), Default::default()), - specifier: JsWord::from("some_package_foo_type"), - specifier_span: Span::new(BytePos(522), BytePos(545), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::ExportEquals, -@@ -586,7 +587,7 @@ try { - span: Span::new(BytePos(548), BytePos(597), Default::default()), - specifier: JsWord::from("some_package_bar"), - specifier_span: Span::new(BytePos(577), BytePos(595), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::Require, -@@ -595,7 +596,7 @@ try { - span: Span::new(BytePos(612), BytePos(651), Default::default()), - specifier: JsWord::from("some_package_resolve"), - specifier_span: Span::new(BytePos(628), BytePos(650), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::Require, -@@ -604,7 +605,7 @@ try { - span: Span::new(BytePos(676), BytePos(719), Default::default()), - specifier: JsWord::from("some_package_resolve_foo"), - specifier_span: Span::new(BytePos(692), BytePos(718), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - ] - ); -@@ -630,7 +631,7 @@ const d9 = await import("./d9.json", { assert: { type: "json", ...bar } }); - const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" } }); - "#; - let (module, comments) = helper("test.ts", source).unwrap(); -- let expected_assertions1 = ImportAssertions::Known({ -+ let expected_assertions1 = ImportAttributes::Known({ - let mut map = HashMap::new(); - map.insert( - "type".to_string(), -@@ -638,7 +639,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - ); - map - }); -- let expected_assertions2 = ImportAssertions::Known({ -+ let expected_assertions2 = ImportAttributes::Known({ - let mut map = HashMap::new(); - map.insert( - "type".to_string(), -@@ -646,7 +647,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - ); - map - }); -- let dynamic_expected_assertions2 = ImportAssertions::Known({ -+ let dynamic_expected_assertions2 = ImportAttributes::Known({ - let mut map = HashMap::new(); - map.insert( - "type".to_string(), -@@ -666,7 +667,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(1), BytePos(66), Default::default()), - specifier: JsWord::from("./test.ts"), - specifier_span: Span::new(BytePos(22), BytePos(33), Default::default()), -- import_assertions: expected_assertions1.clone(), -+ import_attributes: expected_assertions1.clone(), - }, - DependencyDescriptor { - kind: DependencyKind::Export, -@@ -675,7 +676,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(67), BytePos(125), Default::default()), - specifier: JsWord::from("./test.ts"), - specifier_span: Span::new(BytePos(81), BytePos(92), Default::default()), -- import_assertions: expected_assertions1, -+ import_attributes: expected_assertions1, - }, - DependencyDescriptor { - kind: DependencyKind::Export, -@@ -684,7 +685,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(126), BytePos(186), Default::default()), - specifier: JsWord::from("./test.json"), - specifier_span: Span::new(BytePos(146), BytePos(159), Default::default()), -- import_assertions: expected_assertions2.clone(), -+ import_attributes: expected_assertions2.clone(), - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -693,7 +694,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(187), BytePos(240), Default::default()), - specifier: JsWord::from("./foo.json"), - specifier_span: Span::new(BytePos(203), BytePos(215), Default::default()), -- import_assertions: expected_assertions2, -+ import_attributes: expected_assertions2, - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -702,7 +703,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(260), BytePos(313), Default::default()), - specifier: JsWord::from("./fizz.json"), - specifier_span: Span::new(BytePos(267), BytePos(280), Default::default()), -- import_assertions: dynamic_expected_assertions2.clone(), -+ import_attributes: dynamic_expected_assertions2.clone(), - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -711,7 +712,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(334), BytePos(387), Default::default()), - specifier: JsWord::from("./buzz.json"), - specifier_span: Span::new(BytePos(341), BytePos(354), Default::default()), -- import_assertions: dynamic_expected_assertions2, -+ import_attributes: dynamic_expected_assertions2, - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -720,7 +721,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(406), BytePos(425), Default::default()), - specifier: JsWord::from("./d1.json"), - specifier_span: Span::new(BytePos(413), BytePos(424), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -729,7 +730,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(444), BytePos(467), Default::default()), - specifier: JsWord::from("./d2.json"), - specifier_span: Span::new(BytePos(451), BytePos(462), Default::default()), -- import_assertions: Default::default(), -+ import_attributes: Default::default(), - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -738,7 +739,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(486), BytePos(510), Default::default()), - specifier: JsWord::from("./d3.json"), - specifier_span: Span::new(BytePos(493), BytePos(504), Default::default()), -- import_assertions: ImportAssertions::Unknown, -+ import_attributes: ImportAttributes::Unknown, - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -747,7 +748,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(529), BytePos(564), Default::default()), - specifier: JsWord::from("./d4.json"), - specifier_span: Span::new(BytePos(536), BytePos(547), Default::default()), -- import_assertions: ImportAssertions::Known(HashMap::new()), -+ import_attributes: ImportAttributes::Known(HashMap::new()), - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -756,7 +757,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(583), BytePos(619), Default::default()), - specifier: JsWord::from("./d5.json"), - specifier_span: Span::new(BytePos(590), BytePos(601), Default::default()), -- import_assertions: ImportAssertions::Unknown, -+ import_attributes: ImportAttributes::Unknown, - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -765,7 +766,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(638), BytePos(681), Default::default()), - specifier: JsWord::from("./d6.json"), - specifier_span: Span::new(BytePos(645), BytePos(656), Default::default()), -- import_assertions: ImportAssertions::Unknown, -+ import_attributes: ImportAttributes::Unknown, - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -774,7 +775,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(700), BytePos(754), Default::default()), - specifier: JsWord::from("./d7.json"), - specifier_span: Span::new(BytePos(707), BytePos(718), Default::default()), -- import_assertions: ImportAssertions::Unknown, -+ import_attributes: ImportAttributes::Unknown, - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -783,7 +784,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(773), BytePos(819), Default::default()), - specifier: JsWord::from("./d8.json"), - specifier_span: Span::new(BytePos(780), BytePos(791), Default::default()), -- import_assertions: ImportAssertions::Known({ -+ import_attributes: ImportAttributes::Known({ - let mut map = HashMap::new(); - map.insert("type".to_string(), ImportAssertion::Unknown); - map -@@ -796,7 +797,7 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(838), BytePos(895), Default::default()), - specifier: JsWord::from("./d9.json"), - specifier_span: Span::new(BytePos(845), BytePos(856), Default::default()), -- import_assertions: ImportAssertions::Unknown, -+ import_attributes: ImportAttributes::Unknown, - }, - DependencyDescriptor { - kind: DependencyKind::Import, -@@ -805,9 +806,52 @@ const d10 = await import("./d10.json", { assert: { type: "json", ["type"]: "bad" - span: Span::new(BytePos(915), BytePos(982), Default::default()), - specifier: JsWord::from("./d10.json"), - specifier_span: Span::new(BytePos(922), BytePos(934), Default::default()), -- import_assertions: ImportAssertions::Unknown, -+ import_attributes: ImportAttributes::Unknown, - }, - ] - ); - } -+ -+ #[test] -+ fn ts_import_object_lit_property() { -+ let source = r#" -+export declare const SomeValue: typeof Core & import("./a.d.ts").Constructor<{ -+ paginate: import("./b.d.ts").PaginateInterface; -+} & import("./c.d.ts").RestEndpointMethods>; -+"#; -+ let (module, comments) = helper("test.ts", source).unwrap(); -+ let dependencies = analyze_dependencies(&module, &comments); -+ assert_eq!( -+ dependencies, -+ vec![ -+ DependencyDescriptor { -+ kind: DependencyKind::ImportType, -+ is_dynamic: false, -+ leading_comments: Vec::new(), -+ span: Span::new(BytePos(48), BytePos(176), Default::default()), -+ specifier: JsWord::from("./a.d.ts"), -+ specifier_span: Span::new(BytePos(55), BytePos(65), Default::default()), -+ import_attributes: ImportAttributes::None, -+ }, -+ DependencyDescriptor { -+ kind: DependencyKind::ImportType, -+ is_dynamic: false, -+ leading_comments: Vec::new(), -+ span: Span::new(BytePos(95), BytePos(131), Default::default()), -+ specifier: JsWord::from("./b.d.ts"), -+ specifier_span: Span::new(BytePos(102), BytePos(112), Default::default()), -+ import_attributes: ImportAttributes::None, -+ }, -+ DependencyDescriptor { -+ kind: DependencyKind::ImportType, -+ is_dynamic: false, -+ leading_comments: Vec::new(), -+ span: Span::new(BytePos(137), BytePos(175), Default::default()), -+ specifier: JsWord::from("./c.d.ts"), -+ specifier_span: Span::new(BytePos(144), BytePos(154), Default::default()), -+ import_attributes: ImportAttributes::None, -+ } -+ ] -+ ); -+ } - } -diff --git a/crates/swc_ecma_loader/src/resolvers/node.rs b/crates/swc_ecma_loader/src/resolvers/node.rs -index 94a3468f51..80ade412a9 100644 ---- a/crates/swc_ecma_loader/src/resolvers/node.rs -+++ b/crates/swc_ecma_loader/src/resolvers/node.rs -@@ -103,6 +103,7 @@ pub struct NodeModulesResolver { - alias: AHashMap, - // if true do not resolve symlink - preserve_symlinks: bool, -+ ignore_node_modules: bool, - } - - static EXTENSIONS: &[&str] = &["ts", "tsx", "js", "jsx", "json", "node"]; -@@ -118,6 +119,21 @@ impl NodeModulesResolver { - target_env, - alias, - preserve_symlinks, -+ ignore_node_modules: false, -+ } -+ } -+ -+ /// Create a node modules resolver which does not care about `node_modules` -+ pub fn without_node_modules( -+ target_env: TargetEnv, -+ alias: AHashMap, -+ preserve_symlinks: bool, -+ ) -> Self { -+ Self { -+ target_env, -+ alias, -+ preserve_symlinks, -+ ignore_node_modules: true, - } - } - -@@ -369,6 +385,10 @@ impl NodeModulesResolver { - base_dir: &Path, - target: &str, - ) -> Result, Error> { -+ if self.ignore_node_modules { -+ return Ok(None); -+ } -+ - let absolute_path = to_absolute_path(base_dir)?; - let mut path = Some(&*absolute_path); - while let Some(dir) = path { -@@ -394,7 +414,7 @@ impl NodeModulesResolver { - impl Resolve for NodeModulesResolver { - fn resolve(&self, base: &FileName, target: &str) -> Result { - debug!( -- "Resolve {} from {:#?} for {:#?}", -+ "Resolving {} from {:#?} for {:#?}", - target, base, self.target_env - ); - -diff --git a/crates/swc_ecma_loader/src/resolvers/tsc.rs b/crates/swc_ecma_loader/src/resolvers/tsc.rs -index 995afaf87a..357c5550f0 100644 ---- a/crates/swc_ecma_loader/src/resolvers/tsc.rs -+++ b/crates/swc_ecma_loader/src/resolvers/tsc.rs -@@ -1,8 +1,8 @@ --use std::path::{Component, PathBuf}; -+use std::path::{Component, Path, PathBuf}; - - use anyhow::{bail, Context, Error}; - use swc_common::FileName; --use tracing::{debug, info, trace, Level}; -+use tracing::{debug, info, trace, warn, Level}; - - use crate::resolve::Resolve; - -@@ -99,6 +99,58 @@ where - paths, - } - } -+ -+ fn invoke_inner_resolver( -+ &self, -+ base: &FileName, -+ module_specifier: &str, -+ ) -> Result { -+ let res = self.inner.resolve(base, module_specifier).with_context(|| { -+ format!( -+ "failed to resolve `{module_specifier}` from `{base}` using inner \ -+ resolver\nbase_url={}", -+ self.base_url_filename -+ ) -+ }); -+ -+ match res { -+ Ok(resolved) => { -+ info!( -+ "Resolved `{}` as `{}` from `{}`", -+ module_specifier, resolved, base -+ ); -+ -+ let is_base_in_node_modules = if let FileName::Real(v) = base { -+ v.components().any(|c| match c { -+ Component::Normal(v) => v == "node_modules", -+ _ => false, -+ }) -+ } else { -+ false -+ }; -+ let is_target_in_node_modules = if let FileName::Real(v) = &resolved { -+ v.components().any(|c| match c { -+ Component::Normal(v) => v == "node_modules", -+ _ => false, -+ }) -+ } else { -+ false -+ }; -+ -+ // If node_modules is in path, we should return module specifier. -+ if !is_base_in_node_modules && is_target_in_node_modules { -+ return Ok(FileName::Real(module_specifier.into())); -+ } -+ -+ Ok(resolved) -+ } -+ -+ Err(err) => { -+ warn!("{:?}", err); -+ Err(err) -+ } -+ } -+ } - } - - impl Resolve for TsConfigResolver -@@ -110,7 +162,7 @@ where - Some( - tracing::span!( - Level::ERROR, -- "tsc.resolve", -+ "TsConfigResolver::resolve", - base_url = tracing::field::display(self.base_url.display()), - base = tracing::field::display(base), - src = tracing::field::display(module_specifier), -@@ -127,30 +179,29 @@ where - || module_specifier.starts_with("../")) - { - return self -- .inner -- .resolve(base, module_specifier) -+ .invoke_inner_resolver(base, module_specifier) - .context("not processed by tsc resolver because it's relative import"); - } - -- if cfg!(debug_assertions) { -- debug!("non-relative import"); -- } -- - if let FileName::Real(v) = base { - if v.components().any(|c| match c { - Component::Normal(v) => v == "node_modules", - _ => false, - }) { -- return self.inner.resolve(base, module_specifier).context( -+ return self.invoke_inner_resolver(base, module_specifier).context( - "not processed by tsc resolver because base module is in node_modules", - ); - } - } - -+ info!("Checking `jsc.paths`"); -+ - // https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping - for (from, to) in &self.paths { - match from { - Pattern::Wildcard { prefix } => { -+ debug!("Checking `{}` in `jsc.paths`", prefix); -+ - let extra = module_specifier.strip_prefix(prefix); - let extra = match extra { - Some(v) => v, -@@ -163,35 +214,53 @@ where - }; - - if cfg!(debug_assertions) { -- trace!("extra = {}", extra); -+ debug!("Extra: `{}`", extra); - } - - let mut errors = vec![]; - for target in to { - let mut replaced = target.replace('*', extra); -- let rel = format!("./{}", replaced); - -- let res = self.inner.resolve(base, &rel).with_context(|| { -- format!( -- "failed to resolve `{}`, which is expanded from `{}`", -- replaced, module_specifier -+ let _tracing = if cfg!(debug_assertions) { -+ Some( -+ tracing::span!( -+ Level::ERROR, -+ "TsConfigResolver::resolve::jsc.paths", -+ replaced = tracing::field::display(&replaced), -+ ) -+ .entered(), - ) -- }); -+ } else { -+ None -+ }; -+ -+ let relative = format!("./{}", replaced); -+ -+ let res = self -+ .invoke_inner_resolver(base, module_specifier) -+ .or_else(|_| { -+ self.invoke_inner_resolver(&self.base_url_filename, &relative) -+ }) -+ .or_else(|_| { -+ self.invoke_inner_resolver(&self.base_url_filename, &replaced) -+ }); - - errors.push(match res { -- Ok(v) => return Ok(v), -+ Ok(resolved) => return Ok(resolved), - Err(err) => err, - }); - - if cfg!(target_os = "windows") { -- if replaced.starts_with("./") { -- replaced = replaced[2..].to_string(); -- } - replaced = replaced.replace('/', "\\"); - } - - if to.len() == 1 { -- return Ok(FileName::Real(self.base_url.join(replaced))); -+ info!( -+ "Using `{}` for `{}` because the length of the jsc.paths entry is \ -+ 1", -+ replaced, module_specifier -+ ); -+ return Ok(FileName::Real(replaced.into())); - } - } - -@@ -204,33 +273,29 @@ where - } - Pattern::Exact(from) => { - // Should be exactly matched -- if module_specifier == from { -- let replaced = self.base_url.join(&to[0]); -- if replaced.exists() { -- return Ok(FileName::Real(replaced)); -- } -+ if module_specifier != from { -+ continue; -+ } - -- return self -- .inner -- .resolve(base, &format!("./{}", &to[0])) -- .with_context(|| { -- format!( -- "tried to resolve `{}` because `{}` was exactly matched", -- to[0], from -- ) -- }); -+ let tp = Path::new(&to[0]); -+ if tp.is_absolute() { -+ return Ok(FileName::Real(tp.into())); - } -+ -+ if let Ok(res) = self.resolve(&self.base_url_filename, &format!("./{}", &to[0])) -+ { -+ return Ok(res); -+ } -+ -+ return Ok(FileName::Real(self.base_url.join(&to[0]))); - } - } - } - -- if let Ok(v) = self -- .inner -- .resolve(&self.base_url_filename, module_specifier) -- { -+ if let Ok(v) = self.invoke_inner_resolver(&self.base_url_filename, module_specifier) { - return Ok(v); - } - -- self.inner.resolve(base, module_specifier) -+ self.invoke_inner_resolver(base, module_specifier) - } - } -diff --git a/crates/swc_ecma_loader/tests/tsc_resolver.rs b/crates/swc_ecma_loader/tests/tsc_resolver.rs -index 220563e77e..5ff4cd0c68 100644 ---- a/crates/swc_ecma_loader/tests/tsc_resolver.rs -+++ b/crates/swc_ecma_loader/tests/tsc_resolver.rs -@@ -38,14 +38,8 @@ fn exact() { - } - - { -- let err = r -- .resolve(&FileName::Anon, "unrelated") -+ r.resolve(&FileName::Anon, "unrelated") - .expect_err("should not touch error"); -- -- assert!( -- err.source().is_none(), -- "should not touch error if src is not related" -- ); - } - } - -diff --git a/crates/swc_ecma_minifier/benches/full.rs b/crates/swc_ecma_minifier/benches/full.rs -index f0c0935256..17eba4f4e7 100644 ---- a/crates/swc_ecma_minifier/benches/full.rs -+++ b/crates/swc_ecma_minifier/benches/full.rs -@@ -112,10 +112,7 @@ fn print(cm: Lrc, nodes: &[N], minify: boo - - { - let mut emitter = swc_ecma_codegen::Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(minify), - cm: cm.clone(), - comments: None, - wr: Box::new(JsWriter::new(cm, "\n", &mut buf, None)), -diff --git a/crates/swc_ecma_minifier/examples/compress.rs b/crates/swc_ecma_minifier/examples/compress.rs -index f141fc2345..705ac90247 100644 ---- a/crates/swc_ecma_minifier/examples/compress.rs -+++ b/crates/swc_ecma_minifier/examples/compress.rs -@@ -75,10 +75,7 @@ fn print(cm: Lrc, nodes: &[N], minify: boo - - { - let mut emitter = swc_ecma_codegen::Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(minify), - cm: cm.clone(), - comments: None, - wr: Box::new(JsWriter::new(cm, "\n", &mut buf, None)), -diff --git a/crates/swc_ecma_minifier/examples/minifier.rs b/crates/swc_ecma_minifier/examples/minifier.rs -index 73708705cb..395f6ebdd2 100644 ---- a/crates/swc_ecma_minifier/examples/minifier.rs -+++ b/crates/swc_ecma_minifier/examples/minifier.rs -@@ -76,10 +76,7 @@ fn print(cm: Lrc, nodes: &[N], minify: boo - - { - let mut emitter = swc_ecma_codegen::Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(minify), - cm: cm.clone(), - comments: None, - wr: omit_trailing_semi(JsWriter::new(cm, "\n", &mut buf, None)), -diff --git a/crates/swc_ecma_minifier/examples/minify-all.rs b/crates/swc_ecma_minifier/examples/minify-all.rs -index a770bc11b0..6a94bbcecc 100644 ---- a/crates/swc_ecma_minifier/examples/minify-all.rs -+++ b/crates/swc_ecma_minifier/examples/minify-all.rs -@@ -122,10 +122,7 @@ fn print(cm: Lrc, nodes: &[N], minify: boo - - { - let mut emitter = swc_ecma_codegen::Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(minify), - cm: cm.clone(), - comments: None, - wr: Box::new(JsWriter::new(cm, "\n", &mut buf, None)), -diff --git a/crates/swc_ecma_minifier/src/compress/optimize/dead_code.rs b/crates/swc_ecma_minifier/src/compress/optimize/dead_code.rs -index 79f94304d1..614a2fc5a4 100644 ---- a/crates/swc_ecma_minifier/src/compress/optimize/dead_code.rs -+++ b/crates/swc_ecma_minifier/src/compress/optimize/dead_code.rs -@@ -47,7 +47,6 @@ impl Optimizer<'_> { - - // We only handle identifiers on lhs for now. - if let Some(lhs) = assign.left.as_ident() { -- // - if self - .data - .vars -diff --git a/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs b/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs -index d671f5ef38..77aa659d64 100644 ---- a/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs -+++ b/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs -@@ -40,7 +40,7 @@ impl Optimizer<'_> { - - let usage = self.data.vars.get(&obj.to_id())?; - -- if usage.reassigned() { -+ if usage.reassigned { - return None; - } - -diff --git a/crates/swc_ecma_minifier/src/compress/optimize/iife.rs b/crates/swc_ecma_minifier/src/compress/optimize/iife.rs -index 21063b5f71..7b7eeece51 100644 ---- a/crates/swc_ecma_minifier/src/compress/optimize/iife.rs -+++ b/crates/swc_ecma_minifier/src/compress/optimize/iife.rs -@@ -181,7 +181,7 @@ impl Optimizer<'_> { - match &mut **param { - Pat::Ident(param) => { - if let Some(usage) = self.data.vars.get(¶m.to_id()) { -- if usage.reassigned() { -+ if usage.reassigned { - continue; - } - if usage.ref_count != 1 { -@@ -222,7 +222,7 @@ impl Optimizer<'_> { - Pat::Rest(rest_pat) => { - if let Pat::Ident(param_id) = &*rest_pat.arg { - if let Some(usage) = self.data.vars.get(¶m_id.to_id()) { -- if usage.reassigned() -+ if usage.reassigned - || usage.ref_count != 1 - || !usage.has_property_access - { -@@ -906,7 +906,7 @@ impl Optimizer<'_> { - if let Some(arg) = arg { - if let Some(usage) = self.data.vars.get(&orig_params[idx].to_id()) { - if usage.ref_count == 1 -- && !usage.reassigned() -+ && !usage.reassigned - && !usage.has_property_mutation - && matches!( - &*arg, -diff --git a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs -index 2e12bdb081..0bd90c936e 100644 ---- a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs -+++ b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs -@@ -98,9 +98,7 @@ impl Optimizer<'_> { - // - // TODO: Allow `length` in usage.accessed_props - if usage.declared -- && !usage.reassigned() -- && !usage.mutated -- && !usage.has_property_mutation -+ && !usage.mutated() - && usage.accessed_props.is_empty() - && !usage.is_infected() - && is_inline_enabled -@@ -153,7 +151,7 @@ impl Optimizer<'_> { - } - } - -- if !usage.reassigned() { -+ if !usage.reassigned { - match init { - Expr::Fn(..) | Expr::Arrow(..) => { - self.typeofs.insert(ident.to_id(), js_word!("function")); -@@ -165,7 +163,7 @@ impl Optimizer<'_> { - } - } - -- if !usage.mutated { -+ if !usage.mutated() { - self.mode.store(ident.to_id(), &*init); - } - -@@ -177,7 +175,8 @@ impl Optimizer<'_> { - // new variant is added for multi inline, think carefully - if is_inline_enabled - && usage.declared_count == 1 -- && usage.can_inline_var() -+ && usage.assign_count == 0 -+ && (!usage.has_property_mutation || !usage.reassigned) - && match init { - Expr::Ident(Ident { - sym: js_word!("eval"), -@@ -188,7 +187,7 @@ impl Optimizer<'_> { - if !usage.assigned_fn_local { - false - } else if let Some(u) = self.data.vars.get(&id.to_id()) { -- let mut should_inline = !u.reassigned() && u.declared; -+ let mut should_inline = !u.reassigned && u.declared; - - should_inline &= - // Function declarations are hoisted -@@ -322,8 +321,8 @@ impl Optimizer<'_> { - && is_inline_enabled - && usage.declared - && may_remove -- && !usage.reassigned() -- && (usage.can_inline_var() || usage.is_mutated_only_by_one_call()) -+ && !usage.reassigned -+ && usage.assign_count == 0 - && ref_count == 1 - { - match init { -@@ -371,7 +370,7 @@ impl Optimizer<'_> { - continue; - } - if let Some(v_usage) = self.data.vars.get(&id) { -- if v_usage.reassigned() { -+ if v_usage.reassigned { - return; - } - } else { -@@ -388,7 +387,7 @@ impl Optimizer<'_> { - continue; - } - if let Some(v_usage) = self.data.vars.get(&id) { -- if v_usage.reassigned() { -+ if v_usage.reassigned { - return; - } - } else { -@@ -400,7 +399,7 @@ impl Optimizer<'_> { - Expr::Object(..) if self.options.pristine_globals => { - for id in idents_used_by_ignoring_nested(init) { - if let Some(v_usage) = self.data.vars.get(&id) { -- if v_usage.reassigned() { -+ if v_usage.reassigned { - return; - } - } -@@ -413,7 +412,7 @@ impl Optimizer<'_> { - } - - if let Some(init_usage) = self.data.vars.get(&id.to_id()) { -- if init_usage.reassigned() || !init_usage.declared { -+ if init_usage.reassigned || !init_usage.declared { - return; - } - } -@@ -422,7 +421,7 @@ impl Optimizer<'_> { - _ => { - for id in idents_used_by(init) { - if let Some(v_usage) = self.data.vars.get(&id) { -- if v_usage.reassigned() || v_usage.has_property_mutation { -+ if v_usage.reassigned || v_usage.has_property_mutation { - return; - } - } -@@ -534,7 +533,7 @@ impl Optimizer<'_> { - } - - if let Some(usage) = self.data.vars.get(&i.to_id()) { -- if !usage.reassigned() { -+ if !usage.reassigned { - trace_op!("typeofs: Storing typeof `{}{:?}`", i.sym, i.span.ctxt); - match &*decl { - Decl::Fn(..) => { -@@ -601,10 +600,10 @@ impl Optimizer<'_> { - return; - } - -- if usage.reassigned() || usage.inline_prevented { -+ if usage.reassigned || usage.inline_prevented { - log_abort!( - "inline: [x] reassigned = {}, inline_prevented = {}", -- usage.reassigned(), -+ usage.reassigned, - usage.inline_prevented - ); - return; -diff --git a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs -index cabd31c354..f9f5ce88db 100644 ---- a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs -+++ b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs -@@ -318,6 +318,10 @@ impl From<&Function> for FnMetadata { - - impl Optimizer<'_> { - fn may_remove_ident(&self, id: &Ident) -> bool { -+ if self.ctx.is_exported { -+ return false; -+ } -+ - if id.span.ctxt != self.marks.top_level_ctxt { - return true; - } -@@ -842,7 +846,7 @@ impl Optimizer<'_> { - if let Expr::Ident(callee) = &**callee { - if self.options.reduce_vars && self.options.side_effects { - if let Some(usage) = self.data.vars.get(&callee.to_id()) { -- if !usage.reassigned() && usage.pure_fn { -+ if !usage.reassigned && usage.pure_fn { - self.changed = true; - report_change!("Reducing function call to a variable"); - -diff --git a/crates/swc_ecma_minifier/src/compress/optimize/props.rs b/crates/swc_ecma_minifier/src/compress/optimize/props.rs -index fd8fcba54b..fc529c4b5a 100644 ---- a/crates/swc_ecma_minifier/src/compress/optimize/props.rs -+++ b/crates/swc_ecma_minifier/src/compress/optimize/props.rs -@@ -29,13 +29,11 @@ impl Optimizer<'_> { - .vars - .get(&name.to_id()) - .map(|v| { -- !v.mutated -- && v.mutation_by_call_count == 0 -+ !v.mutated() - && !v.used_as_ref - && !v.used_as_arg - && !v.used_in_cond - && (!v.is_fn_local || !self.mode.should_be_very_correct()) -- && !v.reassigned() - && !v.is_infected() - }) - .unwrap_or(false) -@@ -153,8 +151,7 @@ impl Optimizer<'_> { - .map(|v| { - v.ref_count == 1 - && v.has_property_access -- && !v.mutated -- && v.mutation_by_call_count == 0 -+ && !v.mutated() - && v.is_fn_local - && !v.executed_multiple_time - && !v.used_as_arg -diff --git a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs -index bf24de9006..aa2d29ec36 100644 ---- a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs -+++ b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs -@@ -1469,7 +1469,7 @@ impl Optimizer<'_> { - } - _ => a.may_have_side_effects(&self.expr_ctx), - }; -- if has_side_effect && !usgae.is_fn_local && (usgae.exported || usgae.reassigned()) { -+ if has_side_effect && !usgae.is_fn_local && (usgae.exported || usgae.reassigned) { - log_abort!("a (expr) has side effect"); - return false; - } -@@ -1478,7 +1478,7 @@ impl Optimizer<'_> { - if let Some(init) = &a.init { - if init.may_have_side_effects(&self.expr_ctx) - && !usgae.is_fn_local -- && (usgae.exported || usgae.reassigned()) -+ && (usgae.exported || usgae.reassigned) - { - log_abort!("a (var) init has side effect"); - return false; -@@ -2210,7 +2210,7 @@ impl Optimizer<'_> { - } - - // We can remove this variable same as unused pass -- if !usage.reassigned() -+ if !usage.reassigned - && usage.usage_count == 1 - && usage.declared - && !usage.used_recursively -@@ -2239,7 +2239,7 @@ impl Optimizer<'_> { - _ => false, - }; - -- if usage.ref_count != 1 || usage.reassigned() || !usage.is_fn_local { -+ if usage.ref_count != 1 || usage.reassigned || !usage.is_fn_local { - if is_lit { - can_take_init = false - } else { -@@ -2271,7 +2271,7 @@ impl Optimizer<'_> { - - Mergable::FnDecl(a) => { - if let Some(usage) = self.data.vars.get(&a.ident.to_id()) { -- if usage.ref_count != 1 || usage.reassigned() || !usage.is_fn_local { -+ if usage.ref_count != 1 || usage.reassigned || !usage.is_fn_local { - return Ok(false); - } - -diff --git a/crates/swc_ecma_minifier/src/compress/optimize/unused.rs b/crates/swc_ecma_minifier/src/compress/optimize/unused.rs -index 75c5ca591b..6d113adcbf 100644 ---- a/crates/swc_ecma_minifier/src/compress/optimize/unused.rs -+++ b/crates/swc_ecma_minifier/src/compress/optimize/unused.rs -@@ -150,7 +150,7 @@ impl Optimizer<'_> { - if let Some(v) = self.data.vars.get(&i.to_id()).cloned() { - if v.ref_count == 0 - && v.usage_count == 0 -- && !v.reassigned() -+ && !v.reassigned - && !v.has_property_mutation - && !v.declared_as_catch_param - { -@@ -215,10 +215,7 @@ impl Optimizer<'_> { - return true; - } - -- if !usage.mutated -- && !usage.reassigned() -- && usage.no_side_effect_for_member_access -- { -+ if !usage.mutated() && usage.no_side_effect_for_member_access { - return false; - } - } -diff --git a/crates/swc_ecma_minifier/src/pass/merge_exports.rs b/crates/swc_ecma_minifier/src/pass/merge_exports.rs -index 6b9958083b..7e9b0bf5c5 100644 ---- a/crates/swc_ecma_minifier/src/pass/merge_exports.rs -+++ b/crates/swc_ecma_minifier/src/pass/merge_exports.rs -@@ -48,7 +48,7 @@ impl VisitMut for Merger { - specifiers: self.specifiers.take(), - span: DUMMY_SP, - type_only: Default::default(), -- asserts: Default::default(), -+ with: Default::default(), - }, - ))); - } -@@ -61,7 +61,7 @@ impl VisitMut for Merger { - specifiers: Default::default(), - span: DUMMY_SP, - type_only: Default::default(), -- asserts: Default::default(), -+ with: Default::default(), - }, - ))); - } -diff --git a/crates/swc_ecma_minifier/src/program_data.rs b/crates/swc_ecma_minifier/src/program_data.rs -index e258732abe..b43191105a 100644 ---- a/crates/swc_ecma_minifier/src/program_data.rs -+++ b/crates/swc_ecma_minifier/src/program_data.rs -@@ -65,7 +65,6 @@ pub(crate) struct VarUsageInfo { - pub(crate) declared_as_for_init: bool, - - pub(crate) assign_count: u32, -- pub(crate) mutation_by_call_count: u32, - - /// The number of direct and indirect reference to this identifier. - /// ## Things to note -@@ -74,9 +73,7 @@ pub(crate) struct VarUsageInfo { - pub(crate) usage_count: u32, - - /// The variable itself is assigned after reference. -- reassigned: bool, -- /// The variable itself or a property of it is modified. -- pub(crate) mutated: bool, -+ pub(crate) reassigned: bool, - - pub(crate) has_property_access: bool, - pub(crate) has_property_mutation: bool, -@@ -139,10 +136,8 @@ impl Default for VarUsageInfo { - declared_as_fn_expr: Default::default(), - declared_as_for_init: Default::default(), - assign_count: Default::default(), -- mutation_by_call_count: Default::default(), - usage_count: Default::default(), - reassigned: Default::default(), -- mutated: Default::default(), - has_property_access: Default::default(), - has_property_mutation: Default::default(), - exported: Default::default(), -@@ -170,31 +165,23 @@ impl Default for VarUsageInfo { - } - - impl VarUsageInfo { -- pub(crate) fn is_mutated_only_by_one_call(&self) -> bool { -- self.assign_count == 0 && self.mutation_by_call_count == 1 -- } -- - pub(crate) fn is_infected(&self) -> bool { - !self.infects_to.is_empty() - } - -- pub(crate) fn reassigned(&self) -> bool { -- self.reassigned -- || (u32::from(self.var_initialized) -- + u32::from(self.declared_as_catch_param) -- + u32::from(self.declared_as_fn_param) -- + self.assign_count) -- > 1 -- } -- -- pub(crate) fn can_inline_var(&self) -> bool { -- !self.mutated || (self.assign_count == 0 && !self.reassigned()) -+ /// The variable itself or a property of it is modified. -+ pub(crate) fn mutated(&self) -> bool { -+ self.assign_count > 0 || self.has_property_mutation - } - - pub(crate) fn can_inline_fn_once(&self) -> bool { - self.callee_count > 0 - || !self.executed_multiple_time && (self.is_fn_local || !self.used_in_non_child_fn) - } -+ -+ fn initialized(&self) -> bool { -+ self.var_initialized || self.declared_as_fn_param || self.declared_as_catch_param -+ } - } - - impl Storage for ProgramData { -@@ -235,6 +222,8 @@ impl Storage for ProgramData { - || (var_info.var_initialized && !e.get().var_initialized); - - if var_info.var_initialized { -+ // If it is inited in some other child scope and also inited in current -+ // scope - if e.get().var_initialized || e.get().ref_count > 0 { - e.get_mut().assign_count += 1; - e.get_mut().reassigned = true; -@@ -247,6 +236,8 @@ impl Storage for ProgramData { - // If it is inited in some other child scope, but referenced in - // current child scope - if !inited && e.get().var_initialized && var_info.ref_count > 0 { -+ e.get_mut().var_initialized = false; -+ e.get_mut().assign_count += 1; - e.get_mut().reassigned = true - } - } -@@ -255,7 +246,11 @@ impl Storage for ProgramData { - - e.get_mut().reassigned |= var_info.reassigned; - -- e.get_mut().mutated |= var_info.mutated; -+ if var_info.assign_count > 0 { -+ if e.get().initialized() { -+ e.get_mut().reassigned = true -+ } -+ } - - e.get_mut().has_property_access |= var_info.has_property_access; - e.get_mut().has_property_mutation |= var_info.has_property_mutation; -@@ -275,7 +270,6 @@ impl Storage for ProgramData { - e.get_mut().executed_multiple_time |= var_info.executed_multiple_time; - e.get_mut().used_in_cond |= var_info.used_in_cond; - e.get_mut().assign_count += var_info.assign_count; -- e.get_mut().mutation_by_call_count += var_info.mutation_by_call_count; - e.get_mut().usage_count += var_info.usage_count; - - e.get_mut().infects_to.extend(var_info.infects_to); -@@ -314,7 +308,7 @@ impl Storage for ProgramData { - } - } - ScopeKind::Block => { -- if var_info.used_in_non_child_fn { -+ if e.get().used_in_non_child_fn { - e.get_mut().is_fn_local = false; - e.get_mut().used_in_non_child_fn = true; - } -@@ -354,13 +348,13 @@ impl Storage for ProgramData { - let v = self.vars.entry(i.to_id()).or_default(); - v.is_top_level |= ctx.is_top_level; - -- if has_init && (v.declared || v.var_initialized) { -+ // assigned or declared before this declaration -+ if has_init && (v.declared || v.var_initialized || v.assign_count > 0) { - #[cfg(feature = "debug")] - { - tracing::trace!("declare_decl(`{}`): Already declared", i); - } - -- v.mutated = true; - v.reassigned = true; - v.assign_count += 1; - } -@@ -396,7 +390,7 @@ impl Storage for ProgramData { - self.initialized_vars.truncate(len) - } - -- fn mark_property_mutattion(&mut self, id: Id, ctx: Ctx) { -+ fn mark_property_mutation(&mut self, id: Id, ctx: Ctx) { - let e = self.vars.entry(id).or_default(); - e.has_property_mutation = true; - -@@ -482,14 +476,6 @@ impl VarDataLike for VarUsageInfo { - *self.accessed_props.entry(name).or_default() += 1; - } - -- fn mark_mutated(&mut self) { -- self.mutated = true; -- } -- -- fn mark_reassigned(&mut self) { -- self.reassigned = true; -- } -- - fn mark_used_as_ref(&mut self) { - self.used_as_ref = true; - } -@@ -612,46 +598,45 @@ impl ProgramData { - - let call_may_mutate = ctx.in_call_arg_of == Some(CalleeKind::Unknown); - -- // Passing object as a argument is possibly modification. -- e.mutated |= is_modify || (call_may_mutate && ctx.is_exact_arg); -- - e.executed_multiple_time |= ctx.executed_multiple_time; - e.used_in_cond |= ctx.in_cond; - - if is_modify && ctx.is_exact_reassignment { - if is_first { -+ if e.assign_count > 0 || e.initialized() { -+ e.reassigned = true -+ } -+ - e.assign_count += 1; -+ -+ if !ctx.is_op_assign { -+ if e.ref_count == 1 -+ && ctx.in_assign_lhs -+ && e.var_kind != Some(VarDeclKind::Const) -+ && !inited -+ { -+ self.initialized_vars.insert(i.clone()); -+ e.assign_count -= 1; -+ e.var_initialized = true; -+ } else { -+ e.reassigned = true -+ } -+ } - } - - if ctx.is_op_assign { - e.usage_count += 1; -- } else if is_first { -- if e.ref_count == 1 -- && ctx.in_assign_lhs -- && e.var_kind != Some(VarDeclKind::Const) -- && !inited -- { -- self.initialized_vars.insert(i.clone()); -- e.assign_count -= 1; -- e.var_initialized = true; -- } else { -- e.reassigned = true -- } - } - - for other in e.infects_to.clone() { - self.report(other.0, ctx, true, dejavu) - } - } else { -- if call_may_mutate && ctx.is_exact_arg { -- e.mutation_by_call_count += 1; -- } -- - e.usage_count += 1; - } - - if call_may_mutate && ctx.is_exact_arg { -- self.mark_property_mutattion(i, ctx) -+ self.mark_property_mutation(i, ctx) - } - } - } -diff --git a/crates/swc_ecma_minifier/src/util/base54.rs b/crates/swc_ecma_minifier/src/util/base54.rs -index 7b0820a6f8..d98dfa98b9 100644 ---- a/crates/swc_ecma_minifier/src/util/base54.rs -+++ b/crates/swc_ecma_minifier/src/util/base54.rs -@@ -227,12 +227,9 @@ impl CharFreq { - - { - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- target: EsVersion::latest(), -- ascii_only: false, -- minify: true, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default() -+ .with_target(EsVersion::latest()) -+ .with_minify(true), - cm, - comments: None, - wr: &mut freq, -diff --git a/crates/swc_ecma_minifier/tests/compress.rs b/crates/swc_ecma_minifier/tests/compress.rs -index c3ba509b84..1ac887200a 100644 ---- a/crates/swc_ecma_minifier/tests/compress.rs -+++ b/crates/swc_ecma_minifier/tests/compress.rs -@@ -630,10 +630,7 @@ fn print( - } - - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(minify), - cm, - comments: None, - wr, -diff --git a/crates/swc_ecma_minifier/tests/exec.rs b/crates/swc_ecma_minifier/tests/exec.rs -index 98fab543f6..a02d73b4c2 100644 ---- a/crates/swc_ecma_minifier/tests/exec.rs -+++ b/crates/swc_ecma_minifier/tests/exec.rs -@@ -77,10 +77,7 @@ fn print( - } - - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(minify), - cm, - comments: None, - wr, -diff --git a/crates/swc_ecma_minifier/tests/mangle.rs b/crates/swc_ecma_minifier/tests/mangle.rs -index 480f48248e..140f3c16d3 100644 ---- a/crates/swc_ecma_minifier/tests/mangle.rs -+++ b/crates/swc_ecma_minifier/tests/mangle.rs -@@ -33,10 +33,7 @@ fn print(cm: Lrc, m: &Module, minify: bool) -> String { - } - - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(minify), - cm, - comments: None, - wr, -diff --git a/crates/swc_ecma_minifier/tests/terser_exec.rs b/crates/swc_ecma_minifier/tests/terser_exec.rs -index dd8d2ed84a..df809bf1af 100644 ---- a/crates/swc_ecma_minifier/tests/terser_exec.rs -+++ b/crates/swc_ecma_minifier/tests/terser_exec.rs -@@ -304,10 +304,7 @@ fn print( - } - - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default().with_minify(minify), - cm, - comments: None, - wr, -diff --git a/crates/swc_ecma_parser/src/lexer/jsx.rs b/crates/swc_ecma_parser/src/lexer/jsx.rs -index ea64b0c188..e10f0870e6 100644 ---- a/crates/swc_ecma_parser/src/lexer/jsx.rs -+++ b/crates/swc_ecma_parser/src/lexer/jsx.rs -@@ -33,12 +33,18 @@ impl<'a> Lexer<'a> { - // - if cur_pos == self.state.start { - if cur == '<' && self.state.is_expr_allowed { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() was Some('<') -+ self.input.bump(); -+ } - return Ok(Token::JSXTagStart).map(Some); - } - return self.read_token(); - } -- out.push_str(self.input.slice(chunk_start, cur_pos)); -+ out.push_str(unsafe { -+ // Safety: We already checked for the range -+ self.input.slice(chunk_start, cur_pos) -+ }); - - return Ok(Token::JSXText { - raw: Atom::new(out), -@@ -52,7 +58,10 @@ impl<'a> Lexer<'a> { - candidate_list: vec!["`{'>'}`", "`>`"], - }, - ); -- self.input.bump() -+ unsafe { -+ // Safety: cur() was Some('>') -+ self.input.bump() -+ } - } - '}' => { - self.emit_error( -@@ -61,10 +70,16 @@ impl<'a> Lexer<'a> { - candidate_list: vec!["`{'}'}`", "`}`"], - }, - ); -- self.input.bump() -+ unsafe { -+ // Safety: cur() was Some('}') -+ self.input.bump() -+ } - } - '&' => { -- out.push_str(self.input.slice(chunk_start, cur_pos)); -+ out.push_str(unsafe { -+ // Safety: We already checked for the range -+ self.input.slice(chunk_start, cur_pos) -+ }); - - let jsx_entity = self.read_jsx_entity()?; - -@@ -74,14 +89,20 @@ impl<'a> Lexer<'a> { - - _ => { - if cur.is_line_terminator() { -- out.push_str(self.input.slice(chunk_start, cur_pos)); -+ out.push_str(unsafe { -+ // Safety: We already checked for the range -+ self.input.slice(chunk_start, cur_pos) -+ }); - match self.read_jsx_new_line(true)? { - Either::Left(s) => out.push_str(s), - Either::Right(c) => out.push(c), - } - chunk_start = cur_pos; - } else { -- self.input.bump() -+ unsafe { -+ // Safety: cur() was Some(c) -+ self.input.bump() -+ } - } - } - } -@@ -113,7 +134,10 @@ impl<'a> Lexer<'a> { - - let c = self.input.cur(); - debug_assert_eq!(c, Some('&')); -- self.input.bump(); -+ unsafe { -+ // Safety: cur() was Some('&') -+ self.input.bump(); -+ } - - let start_pos = self.input.cur_pos(); - -@@ -122,7 +146,10 @@ impl<'a> Lexer<'a> { - Some(c) => c, - None => break, - }; -- self.input.bump(); -+ unsafe { -+ // Safety: cur() was Some(c) -+ self.input.bump(); -+ } - - if c == ';' { - if let Some(stripped) = s.strip_prefix('#') { -@@ -147,7 +174,10 @@ impl<'a> Lexer<'a> { - s.push(c) - } - -- self.input.reset_to(start_pos); -+ unsafe { -+ // Safety: start_pos is a valid position because we got it from self.input -+ self.input.reset_to(start_pos); -+ } - - Ok(('&', "&".to_string())) - } -@@ -159,10 +189,16 @@ impl<'a> Lexer<'a> { - debug_assert!(self.syntax.jsx()); - - let ch = self.input.cur().unwrap(); -- self.input.bump(); -+ unsafe { -+ // Safety: cur() was Some(ch) -+ self.input.bump(); -+ } - - let out = if ch == '\r' && self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() was Some('\n') -+ self.input.bump(); -+ } - Either::Left(if normalize_crlf { "\n" } else { "\r\n" }) - } else { - Either::Right(ch) -@@ -181,7 +217,10 @@ impl<'a> Lexer<'a> { - - raw.push(quote); - -- self.input.bump(); // `quote` -+ unsafe { -+ // Safety: cur() was Some(quote) -+ self.input.bump(); // `quote` -+ } - - let mut out = String::new(); - let mut chunk_start = self.input.cur_pos(); -@@ -199,7 +238,10 @@ impl<'a> Lexer<'a> { - let cur_pos = self.input.cur_pos(); - - if ch == '\\' { -- let value = self.input.slice(chunk_start, cur_pos); -+ let value = unsafe { -+ // Safety: We already checked for the range -+ self.input.slice(chunk_start, cur_pos) -+ }; - - out.push_str(value); - out.push('\\'); -@@ -218,7 +260,10 @@ impl<'a> Lexer<'a> { - } - - if ch == '&' { -- let value = self.input.slice(chunk_start, cur_pos); -+ let value = unsafe { -+ // Safety: We already checked for the range -+ self.input.slice(chunk_start, cur_pos) -+ }; - - out.push_str(value); - raw.push_str(value); -@@ -230,7 +275,10 @@ impl<'a> Lexer<'a> { - - chunk_start = self.input.cur_pos(); - } else if ch.is_line_terminator() { -- let value = self.input.slice(chunk_start, cur_pos); -+ let value = unsafe { -+ // Safety: We already checked for the range -+ self.input.slice(chunk_start, cur_pos) -+ }; - - out.push_str(value); - raw.push_str(value); -@@ -248,12 +296,18 @@ impl<'a> Lexer<'a> { - - chunk_start = cur_pos + BytePos(ch.len_utf8() as _); - } else { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() was Some(ch) -+ self.input.bump(); -+ } - } - } - - let cur_pos = self.input.cur_pos(); -- let value = self.input.slice(chunk_start, cur_pos); -+ let value = unsafe { -+ // Safety: We already checked for the range -+ self.input.slice(chunk_start, cur_pos) -+ }; - - out.push_str(value); - raw.push_str(value); -@@ -261,7 +315,10 @@ impl<'a> Lexer<'a> { - // it might be at the end of the file when - // the string literal is unterminated - if self.input.peek_ahead().is_some() { -- self.input.bump(); -+ unsafe { -+ // Safety: We called peek_ahead() which means cur() was Some -+ self.input.bump(); -+ } - } - - raw.push(quote); -diff --git a/crates/swc_ecma_parser/src/lexer/mod.rs b/crates/swc_ecma_parser/src/lexer/mod.rs -index 387389f9b1..992a8b2c21 100644 ---- a/crates/swc_ecma_parser/src/lexer/mod.rs -+++ b/crates/swc_ecma_parser/src/lexer/mod.rs -@@ -201,7 +201,10 @@ impl<'a> Lexer<'a> { - fn read_token_number_sign(&mut self) -> LexResult> { - debug_assert!(self.cur().is_some()); - -- self.input.bump(); // '#' -+ unsafe { -+ // Safety: cur() is Some('#') -+ self.input.bump(); // '#' -+ } - - // `#` can also be a part of shebangs, however they should have been - // handled by `read_shebang()` -@@ -221,7 +224,10 @@ impl<'a> Lexer<'a> { - let next = match self.input.peek() { - Some(next) => next, - None => { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some(',') -+ self.input.bump(); -+ } - return Ok(tok!('.')); - } - }; -@@ -232,11 +238,19 @@ impl<'a> Lexer<'a> { - }); - } - -- self.input.bump(); // 1st `.` -+ unsafe { -+ // Safety: cur() is Some -+ // 1st `.` -+ self.input.bump(); -+ } - - if next == '.' && self.input.peek() == Some('.') { -- self.input.bump(); // 2nd `.` -- self.input.bump(); // 3rd `.` -+ unsafe { -+ // Safety: peek() was Some -+ -+ self.input.bump(); // 2nd `.` -+ self.input.bump(); // 3rd `.` -+ } - - return Ok(tok!("...")); - } -@@ -251,16 +265,26 @@ impl<'a> Lexer<'a> { - fn read_token_question_mark(&mut self) -> LexResult { - match self.input.peek() { - Some('?') => { -- self.input.bump(); -- self.input.bump(); -- if self.input.cur() == Some('=') { -+ unsafe { -+ // Safety: peek() was some -+ self.input.bump(); - self.input.bump(); -+ } -+ if self.input.cur() == Some('=') { -+ unsafe { -+ // Safety: cur() was some -+ self.input.bump(); -+ } -+ - return Ok(tok!("??=")); - } - Ok(tok!("??")) - } - _ => { -- self.input.bump(); -+ unsafe { -+ // Safety: peek() is callable only if cur() is Some -+ self.input.bump(); -+ } - Ok(tok!('?')) - } - } -@@ -271,7 +295,10 @@ impl<'a> Lexer<'a> { - /// This is extracted as a method to reduce size of `read_token`. - #[inline(never)] - fn read_token_colon(&mut self) -> LexResult { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some(':') -+ self.input.bump(); -+ } - Ok(tok!(':')) - } - -@@ -308,7 +335,10 @@ impl<'a> Lexer<'a> { - let had_line_break_before_last = self.had_line_break_before_last(); - let start = self.cur_pos(); - -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some(c as char) -+ self.input.bump(); -+ } - let token = if c == b'&' { BitAnd } else { BitOr }; - - // '|=', '&=' -@@ -322,10 +352,16 @@ impl<'a> Lexer<'a> { - - // '||', '&&' - if self.input.cur() == Some(c as char) { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some(c) -+ self.input.bump(); -+ } - - if self.input.cur() == Some('=') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('=') -+ self.input.bump(); -+ } - return Ok(AssignOp(match token { - BitAnd => op!("&&="), - BitOr => op!("||="), -@@ -359,7 +395,10 @@ impl<'a> Lexer<'a> { - #[inline(never)] - fn read_token_mul_mod(&mut self, c: u8) -> LexResult { - let is_mul = c == b'*'; -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some(c) -+ self.input.bump(); -+ } - let mut token = if is_mul { BinOp(Mul) } else { BinOp(Mod) }; - - // check for ** -@@ -521,7 +560,10 @@ impl<'a> Lexer<'a> { - } - }; - -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some(c) if this method is called. -+ self.input.bump(); -+ } - - Ok(Some(vec![c.into()])) - } -@@ -529,11 +571,17 @@ impl<'a> Lexer<'a> { - fn read_token_plus_minus(&mut self, c: u8) -> LexResult> { - let start = self.cur_pos(); - -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some(c), if this method is called. -+ self.input.bump(); -+ } - - // '++', '--' - Ok(Some(if self.input.cur() == Some(c as char) { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some(c) -+ self.input.bump(); -+ } - - // Handle --> - if self.state.had_line_break && c == b'-' && self.eat(b'>') { -@@ -559,7 +607,10 @@ impl<'a> Lexer<'a> { - let start = self.cur_pos(); - let had_line_break_before_last = self.had_line_break_before_last(); - -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some(c) if this method is called. -+ self.input.bump(); -+ } - - Ok(Some(if self.input.eat_byte(b'=') { - // "==" -@@ -911,7 +962,10 @@ impl<'a> Lexer<'a> { - chars.push(c.into()); - } - _ => { -- self.input.reset_to(state); -+ unsafe { -+ // Safety: state is valid position because we got it from cur_pos() -+ self.input.reset_to(state); -+ } - - chars.push(Char::from('\\')); - chars.push(Char::from('u')); -@@ -1029,7 +1083,10 @@ impl<'a> Lexer<'a> { - - /// Expects current char to be '/' - fn read_regexp(&mut self, start: BytePos) -> LexResult { -- self.input.reset_to(start); -+ unsafe { -+ // Safety: start is valid position, and cur() is Some('/') -+ self.input.reset_to(start); -+ } - - debug_assert_eq!(self.cur(), Some('/')); - -@@ -1104,8 +1161,12 @@ impl<'a> Lexer<'a> { - if self.input.cur() != Some('#') || self.input.peek() != Some('!') { - return Ok(None); - } -- self.input.bump(); -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('#') -+ self.input.bump(); -+ // Safety: cur() is Some('!') -+ self.input.bump(); -+ } - let s = self.input.uncons_while(|c| !c.is_line_terminator()); - Ok(Some(Atom::new(s))) - } -diff --git a/crates/swc_ecma_parser/src/lexer/number.rs b/crates/swc_ecma_parser/src/lexer/number.rs -index 67194ca496..e2df37e195 100644 ---- a/crates/swc_ecma_parser/src/lexer/number.rs -+++ b/crates/swc_ecma_parser/src/lexer/number.rs -@@ -491,7 +491,10 @@ impl<'a> Lexer<'a> { - } - - // Ignore this _ character -- self.input.bump(); -+ unsafe { -+ // Safety: cur() returns Some(c) where c is a valid char -+ self.input.bump(); -+ } - raw.push(c); - - continue; -diff --git a/crates/swc_ecma_parser/src/lexer/state.rs b/crates/swc_ecma_parser/src/lexer/state.rs -index c632be1df7..06062d18ef 100644 ---- a/crates/swc_ecma_parser/src/lexer/state.rs -+++ b/crates/swc_ecma_parser/src/lexer/state.rs -@@ -286,7 +286,10 @@ impl<'a> Iterator for Lexer<'a> { - } - - if c == '>' { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('>') -+ self.input.bump(); -+ } - return Ok(Some(Token::JSXTagEnd)); - } - -@@ -301,7 +304,10 @@ impl<'a> Iterator for Lexer<'a> { - let had_line_break_before_last = self.had_line_break_before_last(); - let cur_pos = self.input.cur_pos(); - -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('<') -+ self.input.bump(); -+ } - - if had_line_break_before_last && self.is_str("<<<<<< ") { - let span = Span::new(cur_pos, cur_pos + BytePos(7), Default::default()); -diff --git a/crates/swc_ecma_parser/src/lexer/table.rs b/crates/swc_ecma_parser/src/lexer/table.rs -index 2e7072e6d5..155c783aa0 100644 ---- a/crates/swc_ecma_parser/src/lexer/table.rs -+++ b/crates/swc_ecma_parser/src/lexer/table.rs -@@ -51,7 +51,10 @@ const ERR: ByteHandler = Some(|lexer| { - }; - - let start = lexer.cur_pos(); -- lexer.input.bump(); -+ unsafe { -+ // Safety: Byte handler is only called for non-last chracters -+ lexer.input.bump(); -+ } - lexer.error_span(pos_span(start), SyntaxError::UnexpectedChar { c })? - }); - -@@ -89,7 +92,10 @@ const UNI: ByteHandler = Some(|lexer| { - } - - let start = lexer.cur_pos(); -- lexer.input.bump(); -+ unsafe { -+ // Safety: Byte handler is only called for non-last chracters -+ lexer.input.bump(); -+ } - lexer.error_span(pos_span(start), SyntaxError::UnexpectedChar { c })? - }); - -diff --git a/crates/swc_ecma_parser/src/lexer/util.rs b/crates/swc_ecma_parser/src/lexer/util.rs -index d31fddc594..694c0b81c2 100644 ---- a/crates/swc_ecma_parser/src/lexer/util.rs -+++ b/crates/swc_ecma_parser/src/lexer/util.rs -@@ -69,7 +69,10 @@ impl<'a> Lexer<'a> { - - #[inline(always)] - pub(super) fn bump(&mut self) { -- self.input.bump() -+ unsafe { -+ // Safety: Actually this is not safe but this is an internal method. -+ self.input.bump() -+ } - } - - #[inline(always)] -@@ -246,7 +249,10 @@ impl<'a> Lexer<'a> { - let end = self.cur_pos(); - - if let Some(comments) = self.comments_buffer.as_mut() { -- let s = self.input.slice(slice_start, end); -+ let s = unsafe { -+ // Safety: We know that the start and the end are valid -+ self.input.slice(slice_start, end) -+ }; - let cmt = Comment { - kind: CommentKind::Line, - span: Span::new(start, end, SyntaxContext::empty()), -@@ -264,7 +270,10 @@ impl<'a> Lexer<'a> { - } - } - -- self.input.reset_to(end); -+ unsafe { -+ // Safety: We got end from self.input -+ self.input.reset_to(end); -+ } - } - - /// Expects current char to be '/' and next char to be '*'. -@@ -302,7 +311,10 @@ impl<'a> Lexer<'a> { - } - - if let Some(comments) = self.comments_buffer.as_mut() { -- let src = self.input.slice(slice_start, end); -+ let src = unsafe { -+ // Safety: We got slice_start and end from self.input so those are valid. -+ self.input.slice(slice_start, end) -+ }; - let s = &src[..src.len() - 2]; - let cmt = Comment { - kind: CommentKind::Block, -diff --git a/crates/swc_ecma_parser/src/lib.rs b/crates/swc_ecma_parser/src/lib.rs -index fe50ea65dc..415dc49606 100644 ---- a/crates/swc_ecma_parser/src/lib.rs -+++ b/crates/swc_ecma_parser/src/lib.rs -@@ -175,11 +175,11 @@ impl Syntax { - } - } - -- pub fn import_assertions(self) -> bool { -+ pub fn import_attributes(self) -> bool { - match self { - Syntax::Es(EsConfig { -- import_assertions, .. -- }) => import_assertions, -+ import_attributes, .. -+ }) => import_attributes, - Syntax::Typescript(_) => true, - } - } -@@ -282,9 +282,12 @@ impl Syntax { - } - } - -- fn using_decl(&self) -> bool { -+ pub fn explicit_resource_management(&self) -> bool { - match self { -- Syntax::Es(EsConfig { using_decl, .. }) => *using_decl, -+ Syntax::Es(EsConfig { -+ explicit_resource_management: using_decl, -+ .. -+ }) => *using_decl, - Syntax::Typescript(_) => true, - } - } -@@ -341,8 +344,8 @@ pub struct EsConfig { - pub export_default_from: bool, - - /// Stage 3. -- #[serde(default)] -- pub import_assertions: bool, -+ #[serde(default, alias = "importAssertions")] -+ pub import_attributes: bool, - - #[serde(default, rename = "allowSuperOutsideMethod")] - pub allow_super_outside_method: bool, -@@ -354,7 +357,7 @@ pub struct EsConfig { - pub auto_accessors: bool, - - #[serde(default)] -- pub using_decl: bool, -+ pub explicit_resource_management: bool, - } - - /// Syntactic context. -diff --git a/crates/swc_ecma_parser/src/parser/expr.rs b/crates/swc_ecma_parser/src/parser/expr.rs -index f6d6d4dfac..b077d891f5 100644 ---- a/crates/swc_ecma_parser/src/parser/expr.rs -+++ b/crates/swc_ecma_parser/src/parser/expr.rs -@@ -740,7 +740,7 @@ impl Parser { - expect!(p, ','); - // Handle trailing comma. - if is!(p, ')') { -- if is_dynamic_import && !p.input.syntax().import_assertions() { -+ if is_dynamic_import && !p.input.syntax().import_attributes() { - syntax_error!( - p, - span!(p, start), -diff --git a/crates/swc_ecma_parser/src/parser/stmt.rs b/crates/swc_ecma_parser/src/parser/stmt.rs -index 273d447843..00f0c164cd 100644 ---- a/crates/swc_ecma_parser/src/parser/stmt.rs -+++ b/crates/swc_ecma_parser/src/parser/stmt.rs -@@ -816,7 +816,7 @@ impl<'a, I: Tokens> Parser { - decls.push(self.parse_var_declarator(false, VarDeclKind::Var)?); - } - -- if !self.syntax().using_decl() { -+ if !self.syntax().explicit_resource_management() { - self.emit_err(span!(self, start), SyntaxError::UsingDeclNotEnabled); - } - -@@ -1272,7 +1272,7 @@ impl<'a, I: Tokens> Parser { - let start = cur_pos!(self); - let init = self.include_in_expr(false).parse_for_head_prefix()?; - -- let is_using_decl = self.input.syntax().using_decl() -+ let is_using_decl = self.input.syntax().explicit_resource_management() - && match *init { - Expr::Ident(Ident { - sym: js_word!("using"), -@@ -2434,7 +2434,7 @@ export default function waitUntil(callback, options = {}) { - test_parser( - src, - Syntax::Es(EsConfig { -- import_assertions: true, -+ import_attributes: true, - ..Default::default() - }), - |p| p.parse_expr(), -diff --git a/crates/swc_ecma_parser/src/parser/stmt/module_item.rs b/crates/swc_ecma_parser/src/parser/stmt/module_item.rs -index d2b2003f2d..9ddc999e36 100644 ---- a/crates/swc_ecma_parser/src/parser/stmt/module_item.rs -+++ b/crates/swc_ecma_parser/src/parser/stmt/module_item.rs -@@ -63,9 +63,9 @@ impl Parser { - _ => unreachable!(), - }; - let _ = cur!(self, false); -- let asserts = if self.input.syntax().import_assertions() -+ let with = if self.input.syntax().import_attributes() - && !self.input.had_line_break_before_cur() -- && eat!(self, "assert") -+ && (eat!(self, "assert") || eat!(self, "with")) - { - match *self.parse_object::>()? { - Expr::Object(v) => Some(Box::new(v)), -@@ -80,7 +80,7 @@ impl Parser { - src, - specifiers: vec![], - type_only: false, -- asserts, -+ with, - })) - .map(ModuleItem::from); - } -@@ -157,9 +157,9 @@ impl Parser { - }; - - let _ = cur!(self, false); -- let asserts = if self.input.syntax().import_assertions() -+ let with = if self.input.syntax().import_attributes() - && !self.input.had_line_break_before_cur() -- && eat!(self, "assert") -+ && (eat!(self, "assert") || eat!(self, "with")) - { - match *self.parse_object::>()? { - Expr::Object(v) => Some(Box::new(v)), -@@ -176,7 +176,7 @@ impl Parser { - specifiers, - src, - type_only, -- asserts, -+ with, - })) - .map(ModuleItem::from) - } -@@ -570,12 +570,12 @@ impl Parser { - assert_and_bump!(self, '*'); - - // improve error message for `export * from foo` -- let (src, asserts) = self.parse_from_clause_and_semi()?; -+ let (src, with) = self.parse_from_clause_and_semi()?; - return Ok(ModuleDecl::ExportAll(ExportAll { - span: span!(self, start), - src, - type_only, -- asserts, -+ with, - })); - } - -@@ -617,13 +617,13 @@ impl Parser { - - if has_default || has_ns { - if is!(self, "from") { -- let (src, asserts) = self.parse_from_clause_and_semi()?; -+ let (src, with) = self.parse_from_clause_and_semi()?; - return Ok(ModuleDecl::ExportNamed(NamedExport { - span: span!(self, start), - specifiers, - src: Some(src), - type_only, -- asserts, -+ with, - })); - } else if !self.input.syntax().export_default_from() { - // emit error -@@ -667,7 +667,7 @@ impl Parser { - } - None - }; -- let (src, asserts) = match opt { -+ let (src, with) = match opt { - Some(v) => (Some(v.0), v.1), - None => (None, None), - }; -@@ -676,7 +676,7 @@ impl Parser { - specifiers, - src, - type_only, -- asserts, -+ with, - })); - }; - -@@ -793,7 +793,7 @@ impl Parser { - }) - } - -- /// Parses `from 'foo.js' assert {};` -+ /// Parses `from 'foo.js' with {};` or `from 'foo.js' assert {};` - fn parse_from_clause_and_semi(&mut self) -> PResult<(Box, Option>)> { - expect!(self, "from"); - -@@ -810,9 +810,9 @@ impl Parser { - _ => unexpected!(self, "a string literal"), - }; - let _ = cur!(self, false); -- let asserts = if self.input.syntax().import_assertions() -+ let with = if self.input.syntax().import_attributes() - && !self.input.had_line_break_before_cur() -- && eat!(self, "assert") -+ && (eat!(self, "assert") || eat!(self, "with")) - { - match *self.parse_object::>()? { - Expr::Object(v) => Some(Box::new(v)), -@@ -822,7 +822,7 @@ impl Parser { - None - }; - expect!(self, ';'); -- Ok((src, asserts)) -+ Ok((src, with)) - } - } - -diff --git a/crates/swc_ecma_parser/tests/comments.rs b/crates/swc_ecma_parser/tests/comments.rs -index 397571d2f8..65b7f9b198 100644 ---- a/crates/swc_ecma_parser/tests/comments.rs -+++ b/crates/swc_ecma_parser/tests/comments.rs -@@ -29,7 +29,7 @@ fn test(input: PathBuf) { - decorators: true, - decorators_before_export: false, - export_default_from: true, -- import_assertions: true, -+ import_attributes: true, - ..Default::default() - }), - "ts" | "tsx" => Syntax::Typescript(TsConfig { -diff --git a/crates/swc_ecma_parser/tests/errors.rs b/crates/swc_ecma_parser/tests/errors.rs -index ff68922564..973b5c9bd1 100644 ---- a/crates/swc_ecma_parser/tests/errors.rs -+++ b/crates/swc_ecma_parser/tests/errors.rs -@@ -47,7 +47,7 @@ where - } else { - ::swc_ecma_parser::Syntax::Es(::swc_ecma_parser::EsConfig { - jsx: is_jsx, -- using_decl: true, -+ explicit_resource_management: true, - ..Default::default() - }) - }; -diff --git a/crates/swc_ecma_parser/tests/js.rs b/crates/swc_ecma_parser/tests/js.rs -index 507c532da4..a7de707d50 100644 ---- a/crates/swc_ecma_parser/tests/js.rs -+++ b/crates/swc_ecma_parser/tests/js.rs -@@ -91,7 +91,8 @@ where - - let lexer = Lexer::new( - Syntax::Es(EsConfig { -- using_decl: true, -+ explicit_resource_management: true, -+ import_attributes: true, - ..Default::default() - }), - EsVersion::Es2015, -diff --git a/crates/swc_ecma_preset_env/src/lib.rs b/crates/swc_ecma_preset_env/src/lib.rs -index 4b14862b72..826b7f4a50 100644 ---- a/crates/swc_ecma_preset_env/src/lib.rs -+++ b/crates/swc_ecma_preset_env/src/lib.rs -@@ -173,10 +173,13 @@ where - let pass = add!( - pass, - OptionalChaining, -- es2020::optional_chaining(es2020::optional_chaining::Config { -- no_document_all: loose || assumptions.no_document_all, -- pure_getter: loose || assumptions.pure_getters -- }) -+ es2020::optional_chaining( -+ es2020::optional_chaining::Config { -+ no_document_all: loose || assumptions.no_document_all, -+ pure_getter: loose || assumptions.pure_getters -+ }, -+ global_mark -+ ) - ); - - // ES2019 -@@ -452,7 +455,7 @@ impl VisitMut for Polyfills { - } - .into(), - type_only: false, -- asserts: None, -+ with: None, - })) - }), - ); -@@ -470,7 +473,7 @@ impl VisitMut for Polyfills { - } - .into(), - type_only: false, -- asserts: None, -+ with: None, - })) - }), - ); -diff --git a/crates/swc_ecma_preset_env/tests/test.rs b/crates/swc_ecma_preset_env/tests/test.rs -index f4bf64eb8c..1379699a01 100644 ---- a/crates/swc_ecma_preset_env/tests/test.rs -+++ b/crates/swc_ecma_preset_env/tests/test.rs -@@ -159,10 +159,7 @@ fn exec(c: PresetConfig, dir: PathBuf) -> Result<(), Error> { - let mut buf = vec![]; - { - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify: false, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default(), - comments: None, - cm: cm.clone(), - wr: Box::new(swc_ecma_codegen::text_writer::JsWriter::new( -diff --git a/crates/swc_ecma_quote_macros/src/ast/module_decl.rs b/crates/swc_ecma_quote_macros/src/ast/module_decl.rs -index 56503a53c0..17b2ebcfa1 100644 ---- a/crates/swc_ecma_quote_macros/src/ast/module_decl.rs -+++ b/crates/swc_ecma_quote_macros/src/ast/module_decl.rs -@@ -15,12 +15,12 @@ impl_enum!( - ] - ); - --impl_struct!(ImportDecl, [span, specifiers, src, type_only, asserts]); -+impl_struct!(ImportDecl, [span, specifiers, src, type_only, with]); - impl_struct!(ExportDecl, [span, decl]); - impl_struct!(ExportDefaultDecl, [span, decl]); - impl_struct!(ExportDefaultExpr, [span, expr]); --impl_struct!(ExportAll, [span, src, asserts]); --impl_struct!(NamedExport, [span, specifiers, src, type_only, asserts]); -+impl_struct!(ExportAll, [span, src, with]); -+impl_struct!(NamedExport, [span, specifiers, src, type_only, with]); - - impl_enum!(ImportSpecifier, [Named, Default, Namespace]); - -diff --git a/crates/swc_ecma_transforms_base/src/helpers/mod.rs b/crates/swc_ecma_transforms_base/src/helpers/mod.rs -index 29e8f5160f..c29b1fe4f4 100644 ---- a/crates/swc_ecma_transforms_base/src/helpers/mod.rs -+++ b/crates/swc_ecma_transforms_base/src/helpers/mod.rs -@@ -86,7 +86,7 @@ macro_rules! add_import_to { - span: DUMMY_SP, - specifiers: vec![s], - src: Box::new(src), -- asserts: Default::default(), -+ with: Default::default(), - type_only: Default::default(), - }))) - } -diff --git a/crates/swc_ecma_transforms_base/src/rename/ops.rs b/crates/swc_ecma_transforms_base/src/rename/ops.rs -index 6ab3af91ec..c2e35bf962 100644 ---- a/crates/swc_ecma_transforms_base/src/rename/ops.rs -+++ b/crates/swc_ecma_transforms_base/src/rename/ops.rs -@@ -223,7 +223,7 @@ impl<'a> VisitMut for Operator<'a> { - })], - src: None, - type_only: false, -- asserts: None, -+ with: None, - }, - ))); - }; -@@ -343,7 +343,7 @@ impl<'a> VisitMut for Operator<'a> { - specifiers: renamed, - src: None, - type_only: false, -- asserts: None, -+ with: None, - }, - ))); - } -diff --git a/crates/swc_ecma_transforms_base/src/resolver/mod.rs b/crates/swc_ecma_transforms_base/src/resolver/mod.rs -index c947a89cc5..d20afdff4e 100644 ---- a/crates/swc_ecma_transforms_base/src/resolver/mod.rs -+++ b/crates/swc_ecma_transforms_base/src/resolver/mod.rs -@@ -1,5 +1,5 @@ - use rustc_hash::FxHashSet; --use swc_atoms::{js_word, JsWord}; -+use swc_atoms::JsWord; - use swc_common::{ - collections::{AHashMap, AHashSet}, - Mark, Span, SyntaxContext, -@@ -269,11 +269,6 @@ impl<'a> Resolver<'a> { - } - - fn mark_for_ref_inner(&self, sym: &JsWord, stop_an_fn_scope: bool) -> Option { -- // NaN always points the globals -- if *sym == js_word!("NaN") { -- return Some(self.config.unresolved_mark); -- } -- - if self.config.handle_types && self.in_type { - let mut mark = self.current.mark; - let mut scope = Some(&self.current); -@@ -307,7 +302,16 @@ impl<'a> Resolver<'a> { - if mark == Mark::root() { - return None; - } -- return Some(mark); -+ -+ return match &**sym { -+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#value_properties -+ "undefined" | "NaN" | "Infinity" | "globalThis" -+ if mark == self.config.top_level_mark => -+ { -+ Some(self.config.unresolved_mark) -+ } -+ _ => Some(mark), -+ }; - } - - if cur.kind == ScopeKind::Fn && stop_an_fn_scope { -diff --git a/crates/swc_ecma_transforms_base/tests/fixer_test262.rs b/crates/swc_ecma_transforms_base/tests/fixer_test262.rs -index 60822e4bb2..4afd9b651f 100644 ---- a/crates/swc_ecma_transforms_base/tests/fixer_test262.rs -+++ b/crates/swc_ecma_transforms_base/tests/fixer_test262.rs -@@ -175,10 +175,7 @@ fn identity_tests(tests: &mut Vec) -> Result<(), io::Error> { - - { - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify: false, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default(), - cm: cm.clone(), - wr: Box::new(swc_ecma_codegen::text_writer::JsWriter::new( - cm.clone(), -@@ -189,10 +186,7 @@ fn identity_tests(tests: &mut Vec) -> Result<(), io::Error> { - comments: None, - }; - let mut expected_emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify: false, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default(), - cm: cm.clone(), - wr: Box::new(swc_ecma_codegen::text_writer::JsWriter::new( - cm, "\n", &mut wr2, None, -diff --git a/crates/swc_ecma_transforms_compat/src/es2015/block_scoping/mod.rs b/crates/swc_ecma_transforms_compat/src/es2015/block_scoping/mod.rs -index 4c80ceefd3..ce18711f7e 100644 ---- a/crates/swc_ecma_transforms_compat/src/es2015/block_scoping/mod.rs -+++ b/crates/swc_ecma_transforms_compat/src/es2015/block_scoping/mod.rs -@@ -431,13 +431,6 @@ impl VisitMut for BlockScoping { - self.handle_capture_of_vars(&mut node.body); - } - -- fn visit_mut_while_stmt(&mut self, node: &mut WhileStmt) { -- self.visit_mut_with_scope(ScopeKind::new_loop(), &mut node.body); -- -- node.test.visit_mut_with(self); -- self.handle_capture_of_vars(&mut node.body); -- } -- - fn visit_mut_for_in_stmt(&mut self, node: &mut ForInStmt) { - let blockifyed = self.blockify_for_stmt_body(&mut node.body); - let lexical_var = if let ForHead::VarDecl(decl) = &node.left { -@@ -544,6 +537,14 @@ impl VisitMut for BlockScoping { - self.visit_mut_stmt_like(n); - } - -+ fn visit_mut_switch_case(&mut self, n: &mut SwitchCase) { -+ let old_vars = self.vars.take(); -+ -+ n.visit_mut_children_with(self); -+ -+ self.vars = old_vars; -+ } -+ - fn visit_mut_var_decl(&mut self, var: &mut VarDecl) { - let old = self.var_decl_kind; - self.var_decl_kind = var.kind; -@@ -569,6 +570,13 @@ impl VisitMut for BlockScoping { - } - } - } -+ -+ fn visit_mut_while_stmt(&mut self, node: &mut WhileStmt) { -+ self.visit_mut_with_scope(ScopeKind::new_loop(), &mut node.body); -+ -+ node.test.visit_mut_with(self); -+ self.handle_capture_of_vars(&mut node.body); -+ } - } - - impl BlockScoping { -diff --git a/crates/swc_ecma_transforms_compat/src/es2015/classes/mod.rs b/crates/swc_ecma_transforms_compat/src/es2015/classes/mod.rs -index dc27b327ce..e9660266cc 100644 ---- a/crates/swc_ecma_transforms_compat/src/es2015/classes/mod.rs -+++ b/crates/swc_ecma_transforms_compat/src/es2015/classes/mod.rs -@@ -154,7 +154,7 @@ where - .into()], - src: None, - type_only: false, -- asserts: None, -+ with: None, - }, - )) { - Ok(t) => t, -diff --git a/crates/swc_ecma_transforms_compat/src/es2015/for_of.rs b/crates/swc_ecma_transforms_compat/src/es2015/for_of.rs -index b509443934..d7ddb6be38 100644 ---- a/crates/swc_ecma_transforms_compat/src/es2015/for_of.rs -+++ b/crates/swc_ecma_transforms_compat/src/es2015/for_of.rs -@@ -670,7 +670,7 @@ impl VisitMut for ForOf { - *s = self.fold_for_stmt(Some(label.clone()), stmt.take()); - } - _ => { -- body.visit_mut_children_with(self); -+ body.visit_mut_with(self); - } - } - } -diff --git a/crates/swc_ecma_transforms_compat/src/es2015/generator.rs b/crates/swc_ecma_transforms_compat/src/es2015/generator.rs -index a6da0916a0..a409203a50 100644 ---- a/crates/swc_ecma_transforms_compat/src/es2015/generator.rs -+++ b/crates/swc_ecma_transforms_compat/src/es2015/generator.rs -@@ -572,72 +572,70 @@ impl VisitMut for Generator { - e.visit_mut_children_with(self); - } - -- Expr::Assign(node) => { -- if contains_yield(&node.right) { -- match node.left.as_expr_mut() { -- Some(Expr::Member(left)) => { -- match &mut left.prop { -- MemberProp::Ident(..) | MemberProp::PrivateName(..) => { -- // a.b = yield; -- // -- // [intermediate] -- // .local _a -- // _a = a; -- // .yield resumeLabel -- // .mark resumeLabel -- // _a.b = %sent%; -- -- left.obj.visit_mut_with(self); -- let obj = self.cache_expression(left.obj.take()); -- -- left.obj = Box::new(Expr::Ident(obj)); -- } -- MemberProp::Computed(prop) => { -- // [source] -- // a[b] = yield; -- // -- // [intermediate] -- // .local _a, _b -- // _a = a; -- // _b = b; -- // .yield resumeLabel -- // .mark resumeLabel -- // _a[_b] = %sent%; -- let prop_span = prop.span; -- -- left.obj.visit_mut_with(self); -- let obj = self.cache_expression(left.obj.take()); -- -- prop.visit_mut_with(self); -- let prop = self.cache_expression(prop.expr.take()); -- -- left.obj = Box::new(Expr::Ident(obj)); -- left.prop = MemberProp::Computed(ComputedPropName { -- span: prop_span, -- expr: Box::new(Expr::Ident(prop)), -- }); -- } -+ Expr::Assign(node) if contains_yield(&node.right) => { -+ match node.left.as_expr_mut() { -+ Some(Expr::Member(left)) => { -+ match &mut left.prop { -+ MemberProp::Ident(..) | MemberProp::PrivateName(..) => { -+ // a.b = yield; -+ // -+ // [intermediate] -+ // .local _a -+ // _a = a; -+ // .yield resumeLabel -+ // .mark resumeLabel -+ // _a.b = %sent%; -+ -+ left.obj.visit_mut_with(self); -+ let obj = self.cache_expression(left.obj.take()); -+ -+ left.obj = Box::new(Expr::Ident(obj)); -+ } -+ MemberProp::Computed(prop) => { -+ // [source] -+ // a[b] = yield; -+ // -+ // [intermediate] -+ // .local _a, _b -+ // _a = a; -+ // _b = b; -+ // .yield resumeLabel -+ // .mark resumeLabel -+ // _a[_b] = %sent%; -+ let prop_span = prop.span; -+ -+ left.obj.visit_mut_with(self); -+ let obj = self.cache_expression(left.obj.take()); -+ -+ prop.visit_mut_with(self); -+ let prop = self.cache_expression(prop.expr.take()); -+ -+ left.obj = Box::new(Expr::Ident(obj)); -+ left.prop = MemberProp::Computed(ComputedPropName { -+ span: prop_span, -+ expr: Box::new(Expr::Ident(prop)), -+ }); - } -- // [source] -- } -- _ => { -- node.left.visit_mut_with(self); - } -+ // [source] -+ } -+ _ => { -+ node.left.visit_mut_with(self); - } -- if node.op != op!("=") { -- let left_of_right = self.cache_expression(node.left.take().expect_expr()); -+ } -+ if node.op != op!("=") { -+ let left_of_right = self.cache_expression(node.left.take().expect_expr()); - -- node.right.visit_mut_with(self); -+ node.right.visit_mut_with(self); - -- *e = Expr::Assign(AssignExpr { -- span: node.right.span(), -- op: node.op, -- left: left_of_right.into(), -- right: node.right.take(), -- }); -- } else { -- node.right.visit_mut_with(self); -- } -+ *e = Expr::Assign(AssignExpr { -+ span: node.right.span(), -+ op: node.op, -+ left: left_of_right.into(), -+ right: node.right.take(), -+ }); -+ } else { -+ node.right.visit_mut_with(self); - } - } - -diff --git a/crates/swc_ecma_transforms_compat/src/es2018/object_rest.rs b/crates/swc_ecma_transforms_compat/src/es2018/object_rest.rs -index 269d987bd6..39e6fc1253 100644 ---- a/crates/swc_ecma_transforms_compat/src/es2018/object_rest.rs -+++ b/crates/swc_ecma_transforms_compat/src/es2018/object_rest.rs -@@ -291,7 +291,7 @@ impl VisitMut for ObjectRest { - specifiers, - src: None, - type_only: false, -- asserts: None, -+ with: None, - }; - - var_decl.visit_mut_with(self); -diff --git a/crates/swc_ecma_transforms_compat/src/es2020/export_namespace_from.rs b/crates/swc_ecma_transforms_compat/src/es2020/export_namespace_from.rs -index 3ca62e97c3..98ea8e3f57 100644 ---- a/crates/swc_ecma_transforms_compat/src/es2020/export_namespace_from.rs -+++ b/crates/swc_ecma_transforms_compat/src/es2020/export_namespace_from.rs -@@ -40,7 +40,7 @@ impl VisitMut for ExportNamespaceFrom { - specifiers, - src: Some(src), - type_only: false, -- asserts, -+ with, - })) if specifiers.iter().any(|s| s.is_namespace()) => { - let mut origin_specifiers = vec![]; - -@@ -79,7 +79,7 @@ impl VisitMut for ExportNamespaceFrom { - specifiers: import_specifiers, - src: src.clone(), - type_only: false, -- asserts: asserts.clone(), -+ with: with.clone(), - }))); - - stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( -@@ -88,7 +88,7 @@ impl VisitMut for ExportNamespaceFrom { - specifiers: export_specifiers, - src: None, - type_only: false, -- asserts: None, -+ with: None, - }, - ))); - -@@ -99,7 +99,7 @@ impl VisitMut for ExportNamespaceFrom { - specifiers: origin_specifiers, - src: Some(src), - type_only: false, -- asserts, -+ with, - }, - ))); - } -diff --git a/crates/swc_ecma_transforms_compat/src/es2020/mod.rs b/crates/swc_ecma_transforms_compat/src/es2020/mod.rs -index 39d5c67409..c116b3c6de 100644 ---- a/crates/swc_ecma_transforms_compat/src/es2020/mod.rs -+++ b/crates/swc_ecma_transforms_compat/src/es2020/mod.rs -@@ -1,5 +1,5 @@ - use serde::Deserialize; --use swc_common::chain; -+use swc_common::{chain, Mark}; - use swc_ecma_visit::Fold; - - pub use self::{ -@@ -11,10 +11,10 @@ mod export_namespace_from; - pub mod nullish_coalescing; - pub mod optional_chaining; - --pub fn es2020(config: Config) -> impl Fold { -+pub fn es2020(config: Config, unresolved_mark: Mark) -> impl Fold { - chain!( - nullish_coalescing(config.nullish_coalescing), -- optional_chaining(config.optional_chaining), -+ optional_chaining(config.optional_chaining, unresolved_mark), - export_namespace_from(), - ) - } -diff --git a/crates/swc_ecma_transforms_compat/src/es2020/optional_chaining.rs b/crates/swc_ecma_transforms_compat/src/es2020/optional_chaining.rs -index 1ce54d2184..034907e4a2 100644 ---- a/crates/swc_ecma_transforms_compat/src/es2020/optional_chaining.rs -+++ b/crates/swc_ecma_transforms_compat/src/es2020/optional_chaining.rs -@@ -1,16 +1,17 @@ - use std::mem; - - use serde::Deserialize; --use swc_common::{util::take::Take, DUMMY_SP}; -+use swc_common::{util::take::Take, Mark, SyntaxContext, DUMMY_SP}; - use swc_ecma_ast::*; - use swc_ecma_utils::{ - alias_ident_for, prepend_stmt, quote_ident, undefined, ExprFactory, StmtLike, - }; - use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}; - --pub fn optional_chaining(c: Config) -> impl Fold + VisitMut { -+pub fn optional_chaining(c: Config, unresolved_mark: Mark) -> impl Fold + VisitMut { - as_folder(OptChaining { - c, -+ unresolved: SyntaxContext::empty().apply_mark(unresolved_mark), - ..Default::default() - }) - } -@@ -18,6 +19,7 @@ pub fn optional_chaining(c: Config) -> impl Fold + VisitMut { - #[derive(Default)] - struct OptChaining { - vars: Vec, -+ unresolved: SyntaxContext, - c: Config, - } - -@@ -63,7 +65,7 @@ impl VisitMut for OptChaining { - // foo?.bar -> foo == null ? void 0 : foo.bar - Expr::OptChain(v) => { - let data = self.gather(v.take(), vec![]); -- *e = self.construct(data, false, self.c.no_document_all); -+ *e = self.construct(data, false); - } - - Expr::Unary(UnaryExpr { -@@ -75,7 +77,7 @@ impl VisitMut for OptChaining { - // delete foo?.bar -> foo == null ? true : delete foo.bar - Expr::OptChain(v) => { - let data = self.gather(v.take(), vec![]); -- *e = self.construct(data, true, self.c.no_document_all); -+ *e = self.construct(data, true); - } - _ => e.visit_mut_children_with(self), - } -@@ -145,12 +147,27 @@ impl VisitMut for OptChaining { - } - } - -+#[derive(Debug, Clone)] -+enum Memo { -+ Cache(Ident), -+ Raw(Box), -+} -+ -+impl Memo { -+ fn into_expr(self) -> Expr { -+ match self { -+ Memo::Cache(i) => Expr::Ident(i), -+ Memo::Raw(e) => *e, -+ } -+ } -+} -+ - #[derive(Debug)] - enum Gathering { - Call(CallExpr), - Member(MemberExpr), -- OptCall(CallExpr, Ident), -- OptMember(MemberExpr, Ident), -+ OptCall(CallExpr, Memo), -+ OptMember(MemberExpr, Memo), - } - - impl OptChaining { -@@ -181,7 +198,7 @@ impl OptChaining { - next = m.obj.take(); - m.prop.visit_mut_with(self); - chain.push(if optional { -- Gathering::OptMember(m.take(), self.memoize(&next)) -+ Gathering::OptMember(m.take(), self.memoize(&next, false)) - } else { - Gathering::Member(m.take()) - }); -@@ -192,7 +209,7 @@ impl OptChaining { - c.args.visit_mut_with(self); - // I don't know why c is an OptCall instead of a CallExpr. - chain.push(if optional { -- Gathering::OptCall(c.take().into(), self.memoize(&next)) -+ Gathering::OptCall(c.take().into(), self.memoize(&next, true)) - } else { - Gathering::Call(c.take().into()) - }); -@@ -213,12 +230,7 @@ impl OptChaining { - - /// Constructs a rightward nested conditional expression out of our - /// flattened chain. -- fn construct( -- &mut self, -- data: (Expr, usize, Vec), -- is_delete: bool, -- no_document_all: bool, -- ) -> Expr { -+ fn construct(&mut self, data: (Expr, usize, Vec), is_delete: bool) -> Expr { - let (mut current, count, chain) = data; - - // Stores partially constructed CondExprs for us to assemble later on. -@@ -260,16 +272,22 @@ impl OptChaining { - Expr::Member(m) => { - call = true; - let this = ctx.unwrap_or_else(|| { -- let this = self.memoize(&m.obj); -- m.obj = Box::new(Expr::Assign(AssignExpr { -- span: DUMMY_SP, -- op: op!("="), -- left: this.clone().into(), -- right: m.obj.take(), -- })); -- this -+ let this = self.memoize(&m.obj, true); -+ -+ match &this { -+ Memo::Cache(i) => { -+ m.obj = Box::new(Expr::Assign(AssignExpr { -+ span: DUMMY_SP, -+ op: op!("="), -+ left: i.clone().into(), -+ right: m.obj.take(), -+ })); -+ this -+ } -+ Memo::Raw(_) => this, -+ } - }); -- c.args.insert(0, this.as_arg()); -+ c.args.insert(0, this.into_expr().as_arg()); - } - Expr::SuperProp(s) => { - call = true; -@@ -280,7 +298,7 @@ impl OptChaining { - - committed_cond.push(CondExpr { - span: DUMMY_SP, -- test: init_and_eq_null_or_undefined(&memo, current, no_document_all), -+ test: init_and_eq_null_or_undefined(&memo, current, self.c.no_document_all), - cons: if is_delete { - true.into() - } else { -@@ -289,9 +307,11 @@ impl OptChaining { - alt: Take::dummy(), - }); - c.callee = if call { -- memo.make_member(quote_ident!("call")).as_callee() -+ memo.into_expr() -+ .make_member(quote_ident!("call")) -+ .as_callee() - } else { -- memo.as_callee() -+ memo.into_expr().as_callee() - }; - ctx = None; - Expr::Call(c) -@@ -299,7 +319,7 @@ impl OptChaining { - Gathering::OptMember(mut m, memo) => { - committed_cond.push(CondExpr { - span: DUMMY_SP, -- test: init_and_eq_null_or_undefined(&memo, current, no_document_all), -+ test: init_and_eq_null_or_undefined(&memo, current, self.c.no_document_all), - cons: if is_delete { - true.into() - } else { -@@ -308,7 +328,7 @@ impl OptChaining { - alt: Take::dummy(), - }); - ctx = Some(memo.clone()); -- m.obj = memo.into(); -+ m.obj = memo.into_expr().into(); - Expr::Member(m) - } - }; -@@ -331,15 +351,41 @@ impl OptChaining { - current - } - -- fn memoize(&mut self, expr: &Expr) -> Ident { -- let memo = alias_ident_for(expr, "_this"); -- self.vars.push(VarDeclarator { -- span: DUMMY_SP, -- name: memo.clone().into(), -- init: None, -- definite: false, -- }); -- memo -+ fn should_memo(&self, expr: &Expr, is_call: bool) -> bool { -+ fn is_simple_member(e: &Expr) -> bool { -+ match e { -+ Expr::Ident(_) => true, -+ Expr::SuperProp(s) if !s.prop.is_computed() => true, -+ Expr::Member(m) if !m.prop.is_computed() => is_simple_member(&m.obj), -+ _ => false, -+ } -+ } -+ -+ match expr { -+ Expr::Ident(i) if i.span.ctxt != self.unresolved => false, -+ _ => { -+ if is_call && self.c.pure_getter { -+ !is_simple_member(expr) -+ } else { -+ true -+ } -+ } -+ } -+ } -+ -+ fn memoize(&mut self, expr: &Expr, is_call: bool) -> Memo { -+ if self.should_memo(expr, is_call) { -+ let memo = alias_ident_for(expr, "_this"); -+ self.vars.push(VarDeclarator { -+ span: DUMMY_SP, -+ name: memo.clone().into(), -+ init: None, -+ definite: false, -+ }); -+ Memo::Cache(memo) -+ } else { -+ Memo::Raw(Box::new(expr.to_owned())) -+ } - } - - fn visit_mut_stmt_like(&mut self, stmts: &mut Vec) -@@ -371,13 +417,16 @@ impl OptChaining { - } - } - --fn init_and_eq_null_or_undefined(i: &Ident, init: Expr, no_document_all: bool) -> Box { -- let lhs = Box::new(Expr::Assign(AssignExpr { -- span: DUMMY_SP, -- op: op!("="), -- left: PatOrExpr::Pat(i.clone().into()), -- right: Box::new(init), -- })); -+fn init_and_eq_null_or_undefined(i: &Memo, init: Expr, no_document_all: bool) -> Box { -+ let lhs = match i { -+ Memo::Cache(i) => Box::new(Expr::Assign(AssignExpr { -+ span: DUMMY_SP, -+ op: op!("="), -+ left: PatOrExpr::Pat(i.clone().into()), -+ right: Box::new(init), -+ })), -+ Memo::Raw(e) => e.to_owned(), -+ }; - - if no_document_all { - return Box::new(Expr::Bin(BinExpr { -@@ -395,9 +444,14 @@ fn init_and_eq_null_or_undefined(i: &Ident, init: Expr, no_document_all: bool) - - right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), - })); - -+ let left_expr = match i { -+ Memo::Cache(i) => Box::new(i.clone().into()), -+ Memo::Raw(e) => e.to_owned(), -+ }; -+ - let void_cmp = Box::new(Expr::Bin(BinExpr { - span: DUMMY_SP, -- left: Box::new(Expr::Ident(i.clone())), -+ left: left_expr, - op: op!("==="), - right: undefined(DUMMY_SP), - })); -diff --git a/crates/swc_ecma_transforms_compat/src/es2022/class_properties/mod.rs b/crates/swc_ecma_transforms_compat/src/es2022/class_properties/mod.rs -index 5ba50de0c4..c83ebc2316 100644 ---- a/crates/swc_ecma_transforms_compat/src/es2022/class_properties/mod.rs -+++ b/crates/swc_ecma_transforms_compat/src/es2022/class_properties/mod.rs -@@ -365,7 +365,7 @@ impl ClassProperties { - .into()], - src: None, - type_only: false, -- asserts: None, -+ with: None, - }, - )) { - Ok(t) => t, -diff --git a/crates/swc_ecma_transforms_compat/src/es2022/static_blocks.rs b/crates/swc_ecma_transforms_compat/src/es2022/static_blocks.rs -index 5a35cbbf4f..ef47681494 100644 ---- a/crates/swc_ecma_transforms_compat/src/es2022/static_blocks.rs -+++ b/crates/swc_ecma_transforms_compat/src/es2022/static_blocks.rs -@@ -17,11 +17,40 @@ pub fn static_blocks(mark: Mark) -> impl Fold + VisitMut { - impl ClassStaticBlock { - fn visit_mut_static_block( - &mut self, -- static_block: StaticBlock, -+ mut static_block: StaticBlock, - private_id: JsWord, - ) -> PrivateProp { -+ let mut stmts = static_block.body.stmts.take(); -+ let span = static_block.span; -+ -+ // We special-case the single expression case to avoid the iife, since it's -+ // common. -+ let value = if stmts.len() == 1 && stmts[0].is_expr() { -+ stmts[0].take().expr().map(|expr_stmt| expr_stmt.expr) -+ } else { -+ static_block.body.stmts = stmts; -+ -+ let expr = Expr::Call(CallExpr { -+ span: DUMMY_SP, -+ callee: ArrowExpr { -+ span: DUMMY_SP, -+ params: Vec::new(), -+ is_async: false, -+ is_generator: false, -+ type_params: None, -+ return_type: None, -+ body: Box::new(BlockStmtOrExpr::BlockStmt(static_block.body)), -+ } -+ .as_callee(), -+ args: Vec::new(), -+ type_args: None, -+ }); -+ -+ Some(Box::new(expr)) -+ }; -+ - PrivateProp { -- span: DUMMY_SP.apply_mark(self.mark), -+ span: span.apply_mark(self.mark), - is_static: true, - is_optional: false, - is_override: false, -@@ -37,21 +66,7 @@ impl ClassStaticBlock { - optional: false, - }, - }, -- value: Some(Box::new(Expr::Call(CallExpr { -- span: DUMMY_SP, -- callee: ArrowExpr { -- span: DUMMY_SP, -- params: Vec::new(), -- is_async: false, -- is_generator: false, -- type_params: None, -- return_type: None, -- body: Box::new(BlockStmtOrExpr::BlockStmt(static_block.body)), -- } -- .as_callee(), -- args: Vec::new(), -- type_args: None, -- }))), -+ value, - definite: false, - } - } -diff --git a/crates/swc_ecma_transforms_compat/src/es3/reserved_word.rs b/crates/swc_ecma_transforms_compat/src/es3/reserved_word.rs -index 7df077e4b3..c1f8dbd679 100644 ---- a/crates/swc_ecma_transforms_compat/src/es3/reserved_word.rs -+++ b/crates/swc_ecma_transforms_compat/src/es3/reserved_word.rs -@@ -72,7 +72,7 @@ impl VisitMut for ReservedWord { - specifiers: extra_exports, - src: None, - type_only: false, -- asserts: None, -+ with: None, - } - .into(), - ); -diff --git a/crates/swc_ecma_transforms_compat/tests/es2015_generator.rs b/crates/swc_ecma_transforms_compat/tests/es2015_generator.rs -index 1b7a8f5499..4a29cfb36d 100644 ---- a/crates/swc_ecma_transforms_compat/tests/es2015_generator.rs -+++ b/crates/swc_ecma_transforms_compat/tests/es2015_generator.rs -@@ -1937,3 +1937,47 @@ test_exec!( - await res; - " - ); -+ -+test!( -+ Syntax::default(), -+ |_| { -+ let mark = Mark::fresh(Mark::root()); -+ es2015::(mark, None, Default::default()) -+ }, -+ issue_7809, -+ r#" -+ function a(fn) { -+ return _a.apply(this, arguments); -+ } -+ function _a() { -+ _a = _async_to_generator(function*(fn) { -+ (yield fn()).a = 1; -+ }); -+ return _a.apply(this, arguments); -+ } -+ "#, -+ r#" -+ function a(fn) { -+ return _a.apply(this, arguments); -+ } -+ function _a() { -+ _a = _async_to_generator(function(fn) { -+ return _ts_generator(this, function(_state) { -+ switch(_state.label){ -+ case 0: -+ return [ -+ 4, -+ fn() -+ ]; -+ case 1: -+ _state.sent().a = 1; -+ return [ -+ 2 -+ ]; -+ } -+ }); -+ }); -+ return _a.apply(this, arguments); -+ } -+ "# -+); -diff --git a/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs b/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs -index 63b6d96ca4..5f8161c5d8 100644 ---- a/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs -+++ b/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs -@@ -1,12 +1,19 @@ - use std::{fs::read_to_string, path::PathBuf}; - -+use swc_common::{chain, Mark}; - use swc_ecma_parser::Syntax; -+use swc_ecma_transforms_base::resolver; - use swc_ecma_transforms_compat::es2020::{optional_chaining, optional_chaining::Config}; - use swc_ecma_transforms_testing::{compare_stdout, test, test_exec, test_fixture}; - use swc_ecma_visit::Fold; - - fn tr(c: Config) -> impl Fold { -- optional_chaining(c) -+ let unresolved_mark = Mark::new(); -+ let top_level_mark = Mark::new(); -+ chain!( -+ resolver(unresolved_mark, top_level_mark, false), -+ optional_chaining(c, unresolved_mark) -+ ) - } - - fn syntax() -> Syntax { -@@ -264,10 +271,18 @@ fn exec(input: PathBuf) { - compare_stdout( - Default::default(), - |_| { -- optional_chaining(Config { -- no_document_all: true, -- ..Default::default() -- }) -+ let unresolved_mark = Mark::new(); -+ let top_level_mark = Mark::new(); -+ chain!( -+ resolver(unresolved_mark, top_level_mark, false), -+ optional_chaining( -+ Config { -+ no_document_all: true, -+ ..Default::default() -+ }, -+ Mark::new(), -+ ) -+ ) - }, - &src, - ); -@@ -279,7 +294,14 @@ fn fixture(input: PathBuf) { - - test_fixture( - Default::default(), -- &|_| optional_chaining(Default::default()), -+ &|_| { -+ let unresolved_mark = Mark::new(); -+ let top_level_mark = Mark::new(); -+ chain!( -+ resolver(unresolved_mark, top_level_mark, false), -+ optional_chaining(Default::default(), unresolved_mark) -+ ) -+ }, - &input, - &output, - Default::default(), -@@ -293,10 +315,18 @@ fn fixture_loose(input: PathBuf) { - test_fixture( - Default::default(), - &|_| { -- optional_chaining(Config { -- no_document_all: true, -- pure_getter: true, -- }) -+ let unresolved_mark = Mark::new(); -+ let top_level_mark = Mark::new(); -+ chain!( -+ resolver(unresolved_mark, top_level_mark, false), -+ optional_chaining( -+ Config { -+ no_document_all: true, -+ pure_getter: true, -+ }, -+ Mark::new(), -+ ) -+ ) - }, - &input, - &output, -diff --git a/crates/swc_ecma_transforms_macros/src/parallel.rs b/crates/swc_ecma_transforms_macros/src/parallel.rs -index 006ee04400..2a0da6eddb 100644 ---- a/crates/swc_ecma_transforms_macros/src/parallel.rs -+++ b/crates/swc_ecma_transforms_macros/src/parallel.rs -@@ -1,3 +1,5 @@ -+#![allow(non_snake_case)] -+ - use pmutil::q; - use proc_macro2::{Span, TokenStream}; - use syn::{Expr, Ident, ImplItem, ImplItemFn, ItemImpl, Meta, Type}; -@@ -29,11 +31,9 @@ pub fn expand(attr: TokenStream, mut item: ItemImpl) -> ItemImpl { - mode, - "module_items", - explode, -- 100, -- ))); -- item.items.push(ImplItem::Fn(make_par_visit_method( -- mode, "stmts", explode, 100, - ))); -+ item.items -+ .push(ImplItem::Fn(make_par_visit_method(mode, "stmts", explode))); - - item - } -@@ -85,20 +85,15 @@ fn explode_hook_method_name(explode: bool, suffix: &str) -> Option { - } - } - --fn make_par_visit_method(mode: Mode, suffix: &str, explode: bool, threshold: usize) -> ImplItemFn { -+fn make_par_visit_method(mode: Mode, suffix: &str, explode: bool) -> ImplItemFn { - let method_name = Ident::new(&format!("{}_{}", mode.prefix(), suffix), Span::call_site()); - let hook = post_visit_hook(mode, suffix); - let explode_method_name = explode_hook_method_name(explode, suffix); - -- let threshold = q!(Vars { threshold }, { -- (swc_ecma_transforms_base::perf::cpu_count() * threshold) -- }); -- - match (mode, explode_method_name) { - (Mode::Fold, Some(explode_method_name)) => q!( - Vars { - NodeType: node_type(suffix), -- threshold, - method_name, - hook, - explode_method_name, -@@ -109,63 +104,6 @@ fn make_par_visit_method(mode: Mode, suffix: &str, explode: bool, threshold: usi - use swc_ecma_transforms_base::perf::{ParExplode, Parallel}; - use swc_ecma_visit::FoldWith; - -- #[cfg(feature = "rayon")] -- if nodes.len() >= threshold || option_env!("SWC_FORCE_CONCURRENT") == Some("1") -- { -- use rayon::prelude::*; -- -- let (visitor, mut nodes) = ::swc_common::GLOBALS.with(|globals| { -- swc_ecma_transforms_base::helpers::HELPERS.with(|helpers| { -- HANDLER.with(|handler| { -- nodes -- .into_par_iter() -- .map(|node| { -- ::swc_common::GLOBALS.set(&globals, || { -- swc_ecma_transforms_base::helpers::HELPERS.set( -- helpers, -- || { -- HANDLER.set(handler, || { -- let mut visitor = -- Parallel::create(&*self); -- let node = node.fold_with(&mut visitor); -- let mut nodes = Vec::with_capacity(4); -- -- ParExplode::explode_method_name( -- &mut visitor, -- &mut nodes, -- ); -- -- nodes.push(node); -- -- (visitor, nodes) -- }) -- }, -- ) -- }) -- }) -- .reduce( -- || (Parallel::create(&*self), vec![]), -- |mut a, b| { -- Parallel::merge(&mut a.0, b.0); -- -- a.1.extend(b.1); -- -- a -- }, -- ) -- }) -- }) -- }); -- -- Parallel::merge(self, visitor); -- -- { -- hook; -- } -- -- return nodes; -- } -- - let mut buf = Vec::with_capacity(nodes.len()); - - for node in nodes { -@@ -189,7 +127,6 @@ fn make_par_visit_method(mode: Mode, suffix: &str, explode: bool, threshold: usi - (Mode::Fold, None) => q!( - Vars { - NodeType: node_type(suffix), -- threshold, - method_name, - hook, - }, -@@ -199,65 +136,6 @@ fn make_par_visit_method(mode: Mode, suffix: &str, explode: bool, threshold: usi - use swc_ecma_transforms_base::perf::Parallel; - use swc_ecma_visit::FoldWith; - -- #[cfg(feature = "rayon")] -- if nodes.len() >= threshold || option_env!("SWC_FORCE_CONCURRENT") == Some("1") -- { -- use rayon::prelude::*; -- -- let (visitor, mut nodes) = ::swc_common::GLOBALS.with(|globals| { -- swc_ecma_transforms_base::helpers::HELPERS.with(|helpers| { -- HANDLER.with(|handler| { -- nodes -- .into_par_iter() -- .map(|node| { -- ::swc_common::GLOBALS.set(&globals, || { -- swc_ecma_transforms_base::helpers::HELPERS.set( -- helpers, -- || { -- HANDLER.set(handler, || { -- let mut visitor = -- Parallel::create(&*self); -- let node = node.fold_with(&mut visitor); -- -- (visitor, node) -- }) -- }, -- ) -- }) -- }) -- .fold( -- || (Parallel::create(&*self), vec![]), -- |mut a, b| { -- Parallel::merge(&mut a.0, b.0); -- -- a.1.push(b.1); -- -- a -- }, -- ) -- .reduce( -- || (Parallel::create(&*self), vec![]), -- |mut a, b| { -- Parallel::merge(&mut a.0, b.0); -- -- a.1.extend(b.1); -- -- a -- }, -- ) -- }) -- }) -- }); -- -- Parallel::merge(self, visitor); -- -- { -- hook; -- } -- -- return nodes; -- } -- - let mut nodes = nodes.fold_children_with(self); - { - hook; -@@ -272,7 +150,6 @@ fn make_par_visit_method(mode: Mode, suffix: &str, explode: bool, threshold: usi - (Mode::VisitMut, Some(explode_method_name)) => q!( - Vars { - NodeType: node_type(suffix), -- threshold, - method_name, - hook, - explode_method_name -@@ -285,66 +162,6 @@ fn make_par_visit_method(mode: Mode, suffix: &str, explode: bool, threshold: usi - use swc_ecma_transforms_base::perf::{ParExplode, Parallel}; - use swc_ecma_visit::VisitMutWith; - -- #[cfg(feature = "rayon")] -- if nodes.len() >= threshold || option_env!("SWC_FORCE_CONCURRENT") == Some("1") -- { -- ::swc_common::GLOBALS.with(|globals| { -- swc_ecma_transforms_base::helpers::HELPERS.with(|helpers| { -- HANDLER.with(|handler| { -- use rayon::prelude::*; -- -- let (visitor, new_nodes) = take(nodes) -- .into_par_iter() -- .map(|mut node| { -- ::swc_common::GLOBALS.set(&globals, || { -- swc_ecma_transforms_base::helpers::HELPERS.set( -- helpers, -- || { -- HANDLER.set(handler, || { -- let mut visitor = -- Parallel::create(&*self); -- node.visit_mut_with(&mut visitor); -- -- let mut nodes = Vec::with_capacity(4); -- -- ParExplode::explode_method_name( -- &mut visitor, -- &mut nodes, -- ); -- -- nodes.push(node); -- -- (visitor, nodes) -- }) -- }, -- ) -- }) -- }) -- .reduce( -- || (Parallel::create(&*self), vec![]), -- |mut a, b| { -- Parallel::merge(&mut a.0, b.0); -- -- a.1.extend(b.1); -- -- a -- }, -- ); -- -- Parallel::merge(self, visitor); -- -- { -- hook; -- } -- -- *nodes = new_nodes; -- }) -- }) -- }); -- -- return; -- } -- - let mut buf = Vec::with_capacity(nodes.len()); - - for mut node in take(nodes) { -@@ -367,7 +184,6 @@ fn make_par_visit_method(mode: Mode, suffix: &str, explode: bool, threshold: usi - (Mode::VisitMut, None) => q!( - Vars { - NodeType: node_type(suffix), -- threshold, - method_name, - hook, - }, -@@ -377,53 +193,6 @@ fn make_par_visit_method(mode: Mode, suffix: &str, explode: bool, threshold: usi - use swc_ecma_transforms_base::perf::Parallel; - use swc_ecma_visit::VisitMutWith; - -- #[cfg(feature = "rayon")] -- if nodes.len() >= threshold || option_env!("SWC_FORCE_CONCURRENT") == Some("1") -- { -- ::swc_common::GLOBALS.with(|globals| { -- swc_ecma_transforms_base::helpers::HELPERS.with(|helpers| { -- HANDLER.with(|handler| { -- use rayon::prelude::*; -- -- let visitor = nodes -- .into_par_iter() -- .map(|node| { -- ::swc_common::GLOBALS.set(&globals, || { -- swc_ecma_transforms_base::helpers::HELPERS.set( -- helpers, -- || { -- HANDLER.set(handler, || { -- let mut visitor = -- Parallel::create(&*self); -- node.visit_mut_with(&mut visitor); -- -- visitor -- }) -- }, -- ) -- }) -- }) -- .reduce( -- || Parallel::create(&*self), -- |mut a, b| { -- Parallel::merge(&mut a, b); -- -- a -- }, -- ); -- -- Parallel::merge(self, visitor); -- -- { -- hook; -- } -- }) -- }) -- }); -- -- return; -- } -- - nodes.visit_mut_children_with(self); - { - hook; -diff --git a/crates/swc_ecma_transforms_module/src/lib.rs b/crates/swc_ecma_transforms_module/src/lib.rs -index 6c73f690dd..bfc8b3869f 100644 ---- a/crates/swc_ecma_transforms_module/src/lib.rs -+++ b/crates/swc_ecma_transforms_module/src/lib.rs -@@ -3,6 +3,8 @@ - #![allow(clippy::needless_lifetimes)] - #![allow(clippy::vec_box)] - -+use serde::{Deserialize, Serialize}; -+ - pub use self::{amd::amd, common_js::common_js, system_js::system_js, umd::umd}; - - #[macro_use] -@@ -16,3 +18,10 @@ pub mod path; - pub mod rewriter; - pub mod system_js; - pub mod umd; -+ -+#[derive(Debug, Default, Clone, Serialize, Deserialize)] -+#[serde(deny_unknown_fields, rename_all = "camelCase")] -+pub struct EsModuleConfig { -+ #[serde(default)] -+ pub resolve_fully: bool, -+} -diff --git a/crates/swc_ecma_transforms_module/src/path.rs b/crates/swc_ecma_transforms_module/src/path.rs -index 7f10d7e527..7c33109ef7 100644 ---- a/crates/swc_ecma_transforms_module/src/path.rs -+++ b/crates/swc_ecma_transforms_module/src/path.rs -@@ -14,7 +14,7 @@ use swc_common::{FileName, Mark, Span, DUMMY_SP}; - use swc_ecma_ast::*; - use swc_ecma_loader::resolve::Resolve; - use swc_ecma_utils::{quote_ident, ExprFactory}; --use tracing::{debug, trace, warn, Level}; -+use tracing::{debug, info, warn, Level}; - - pub(crate) enum Resolver { - Real { -@@ -86,21 +86,38 @@ where - R: Resolve, - { - resolver: R, -- base_dir: Option, -+ config: Config, -+} -+ -+#[derive(Debug, Clone, Default)] -+pub struct Config { -+ pub base_dir: Option, -+ pub resolve_fully: bool, - } - - impl NodeImportResolver - where - R: Resolve, - { -- #[deprecated(note = "Use `with_base_dir`")] -+ #[deprecated(note = "Use `with_config`")] - pub fn new(resolver: R) -> Self { -- Self::with_base_dir(resolver, None) -+ Self::with_config(resolver, Default::default()) - } - -+ #[deprecated(note = "Use `with_config`")] - pub fn with_base_dir(resolver: R, base_dir: Option) -> Self { -+ Self::with_config( -+ resolver, -+ Config { -+ base_dir, -+ ..Default::default() -+ }, -+ ) -+ } -+ -+ pub fn with_config(resolver: R, config: Config) -> Self { - #[cfg(not(target_arch = "wasm32"))] -- if let Some(base_dir) = &base_dir { -+ if let Some(base_dir) = &config.base_dir { - assert!( - base_dir.is_absolute(), - "base_dir(`{}`) must be absolute. Please ensure that `jsc.baseUrl` is specified \ -@@ -115,43 +132,69 @@ where - ); - } - -- Self { resolver, base_dir } -+ Self { resolver, config } - } - } - --impl ImportResolver for NodeImportResolver -+impl NodeImportResolver - where - R: Resolve, - { -- fn resolve_import(&self, base: &FileName, module_specifier: &str) -> Result { -- fn to_specifier(target_path: &str, orig_ext: Option<&str>) -> JsWord { -- let mut p = PathBuf::from(target_path); -+ fn to_specifier(&self, mut target_path: PathBuf, orig_filename: Option<&str>) -> JsWord { -+ debug!( -+ "Creating a specifier for `{}` with original filename `{:?}`", -+ target_path.display(), -+ orig_filename -+ ); - -- if cfg!(debug_assertions) { -- trace!("to_specifier({target_path}): orig_ext={:?}", orig_ext); -- } -+ if let Some(orig_filename) = orig_filename { -+ let is_resolved_as_index = if let Some(stem) = target_path.file_stem() { -+ stem == "index" -+ } else { -+ false -+ }; - -- if let Some(orig_ext) = orig_ext { -- let use_orig = if let Some(ext) = p.extension() { -- ext == "ts" || ext == "tsx" -+ let is_resolved_as_ts = if let Some(ext) = target_path.extension() { -+ ext == "ts" || ext == "tsx" -+ } else { -+ false -+ }; -+ -+ let is_exact = if let Some(filename) = target_path.file_name() { -+ filename == orig_filename -+ } else { -+ false -+ }; -+ -+ if !is_resolved_as_index && !is_exact { -+ target_path.set_file_name(orig_filename); -+ } else if is_resolved_as_ts && is_exact { -+ if let Some(ext) = Path::new(orig_filename).extension() { -+ target_path.set_extension(ext); - } else { -- false -- }; -- -- if use_orig { -- if matches!(orig_ext, "js" | "mjs" | "cjs" | "jsx") { -- p.set_extension(orig_ext); -- } else { -- p.set_extension(""); -- } -+ target_path.set_extension("js"); -+ } -+ } else if self.config.resolve_fully && is_resolved_as_ts { -+ target_path.set_extension("js"); -+ } else if is_resolved_as_ts && is_resolved_as_index { -+ if orig_filename == "index" { -+ target_path.set_extension(""); -+ } else { -+ target_path.pop(); - } -- } else { -- p.set_extension(""); - } -+ } else { -+ target_path.set_extension(""); -+ } - -- p.display().to_string().into() -+ if cfg!(target_os = "windows") { -+ target_path.display().to_string().replace('\\', "/").into() -+ } else { -+ target_path.display().to_string().into() - } -+ } - -+ fn try_resolve_import(&self, base: &FileName, module_specifier: &str) -> Result { - let _tracing = if cfg!(debug_assertions) { - Some( - tracing::span!( -@@ -166,17 +209,7 @@ where - None - }; - -- if cfg!(debug_assertions) { -- debug!("invoking resolver"); -- } -- -- let orig_ext = module_specifier.split('/').last().and_then(|s| { -- if s.contains('.') { -- s.split('.').last() -- } else { -- None -- } -- }); -+ let orig_filename = module_specifier.split('/').last(); - - let target = self.resolver.resolve(base, module_specifier); - let target = match target { -@@ -187,9 +220,18 @@ where - } - }; - -+ info!("Resolved to {}", target); -+ - let mut target = match target { -- FileName::Real(v) => v, -- FileName::Custom(s) => return Ok(to_specifier(&s, orig_ext)), -+ FileName::Real(v) => { -+ // @nestjs/common should be preserved as a whole -+ if v.starts_with(".") || v.starts_with("..") || v.is_absolute() { -+ v -+ } else { -+ return Ok(self.to_specifier(v, orig_filename)); -+ } -+ } -+ FileName::Custom(s) => return Ok(self.to_specifier(s.into(), orig_filename)), - _ => { - unreachable!( - "Node path provider does not support using `{:?}` as a target file name", -@@ -215,10 +257,16 @@ where - }; - - if base.is_absolute() != target.is_absolute() { -- base = Cow::Owned(absolute_path(self.base_dir.as_deref(), &base)?); -- target = absolute_path(self.base_dir.as_deref(), &target)?; -+ base = Cow::Owned(absolute_path(self.config.base_dir.as_deref(), &base)?); -+ target = absolute_path(self.config.base_dir.as_deref(), &target)?; - } - -+ debug!( -+ "Comparing values (after normalizing absoluteness)\nbase={}\ntarget={}", -+ base.display(), -+ target.display() -+ ); -+ - let rel_path = diff_paths( - &target, - match base.parent() { -@@ -229,9 +277,11 @@ where - - let rel_path = match rel_path { - Some(v) => v, -- None => return Ok(to_specifier(&target.display().to_string(), orig_ext)), -+ None => return Ok(self.to_specifier(target, orig_filename)), - }; - -+ debug!("Relative path: {}", rel_path.display()); -+ - { - // Check for `node_modules`. - -@@ -256,11 +306,21 @@ where - } else { - Cow::Owned(format!("./{}", s)) - }; -- if cfg!(target_os = "windows") { -- Ok(to_specifier(&s.replace('\\', "/"), orig_ext)) -- } else { -- Ok(to_specifier(&s, orig_ext)) -- } -+ -+ Ok(self.to_specifier(s.into_owned().into(), orig_filename)) -+ } -+} -+ -+impl ImportResolver for NodeImportResolver -+where -+ R: Resolve, -+{ -+ fn resolve_import(&self, base: &FileName, module_specifier: &str) -> Result { -+ self.try_resolve_import(base, module_specifier) -+ .or_else(|err| { -+ warn!("Failed to resolve import: {}", err); -+ Ok(module_specifier.into()) -+ }) - } - } - -diff --git a/crates/swc_ecma_transforms_module/src/system_js.rs b/crates/swc_ecma_transforms_module/src/system_js.rs -index 520eb06b7d..758e8c524e 100644 ---- a/crates/swc_ecma_transforms_module/src/system_js.rs -+++ b/crates/swc_ecma_transforms_module/src/system_js.rs -@@ -17,6 +17,9 @@ use crate::{ - pub struct Config { - #[serde(default)] - pub allow_top_level_this: bool, -+ -+ #[serde(default)] -+ pub resolve_fully: bool, - } - - struct SystemJs { -diff --git a/crates/swc_ecma_transforms_module/src/umd/config.rs b/crates/swc_ecma_transforms_module/src/umd/config.rs -index 3fb840eef0..1564b7283b 100644 ---- a/crates/swc_ecma_transforms_module/src/umd/config.rs -+++ b/crates/swc_ecma_transforms_module/src/umd/config.rs -@@ -29,8 +29,10 @@ impl Config { - .into_iter() - .map(|(k, v)| { - let parse = |s| { -- let fm = cm -- .new_source_file(FileName::Custom(format!("", s)), s); -+ let fm = cm.new_source_file( -+ FileName::Internal(format!("", s)), -+ s, -+ ); - - parse_file_as_expr( - &fm, -diff --git a/crates/swc_ecma_transforms_module/src/util.rs b/crates/swc_ecma_transforms_module/src/util.rs -index ee607d1b95..823f9a2ef0 100644 ---- a/crates/swc_ecma_transforms_module/src/util.rs -+++ b/crates/swc_ecma_transforms_module/src/util.rs -@@ -38,6 +38,9 @@ pub struct Config { - pub ignore_dynamic: bool, - #[serde(default)] - pub preserve_import_meta: bool, -+ -+ #[serde(default)] -+ pub resolve_fully: bool, - } - - impl Default for Config { -@@ -52,6 +55,7 @@ impl Default for Config { - no_interop: false, - ignore_dynamic: false, - preserve_import_meta: false, -+ resolve_fully: false, - } - } - } -diff --git a/crates/swc_ecma_transforms_module/tests/path_node.rs b/crates/swc_ecma_transforms_module/tests/path_node.rs -index 6ce2a4388f..32c80dc58e 100644 ---- a/crates/swc_ecma_transforms_module/tests/path_node.rs -+++ b/crates/swc_ecma_transforms_module/tests/path_node.rs -@@ -84,24 +84,23 @@ fn issue_4730() { - - type JscPathsProvider = NodeImportResolver>; - --fn paths_resolver( -- base_url: impl AsRef, -- rules: Vec<(String, Vec)>, --) -> JscPathsProvider { -- let base_url = base_url -- .as_ref() -+fn paths_resolver(base_dir: &Path, rules: Vec<(String, Vec)>) -> JscPathsProvider { -+ let base_dir = base_dir - .to_path_buf() - .canonicalize() - .expect("failed to canonicalize"); -- dbg!(&base_url); -+ dbg!(&base_dir); - -- NodeImportResolver::with_base_dir( -+ NodeImportResolver::with_config( - TsConfigResolver::new( - NodeModulesResolver::new(swc_ecma_loader::TargetEnv::Node, Default::default(), true), -- base_url.clone(), -+ base_dir.clone(), - rules, - ), -- Some(base_url), -+ swc_ecma_transforms_module::path::Config { -+ base_dir: Some(base_dir), -+ resolve_fully: false, -+ }, - ) - } - -@@ -134,7 +133,7 @@ fn fixture(input_dir: PathBuf) { - let rules = config.paths.clone().into_iter().collect(); - - let resolver = -- paths_resolver(config.base_url.clone().unwrap_or(input_dir.clone()), rules); -+ paths_resolver(&config.base_url.clone().unwrap_or(input_dir.clone()), rules); - - import_rewriter(FileName::Real(index_path.clone()), resolver) - }, -diff --git a/crates/swc_ecma_transforms_module/tests/system_js.rs b/crates/swc_ecma_transforms_module/tests/system_js.rs -index 40aeae243f..998178ab3c 100644 ---- a/crates/swc_ecma_transforms_module/tests/system_js.rs -+++ b/crates/swc_ecma_transforms_module/tests/system_js.rs -@@ -45,7 +45,8 @@ test!( - |tester| tr( - tester, - Config { -- allow_top_level_this: true -+ allow_top_level_this: true, -+ ..Default::default() - } - ), - allow_top_level_this_true, -@@ -67,7 +68,8 @@ test!( - |tester| tr( - tester, - Config { -- allow_top_level_this: false -+ allow_top_level_this: false, -+ ..Default::default() - } - ), - iife, -@@ -94,7 +96,8 @@ test!( - |tester| tr( - tester, - Config { -- allow_top_level_this: false -+ allow_top_level_this: false, -+ ..Default::default() - } - ), - top_level_this_false_class, -@@ -133,7 +136,8 @@ test!( - |tester| tr( - tester, - Config { -- allow_top_level_this: false -+ allow_top_level_this: false, -+ ..Default::default() - } - ), - allow_top_level_this_false, -diff --git a/crates/swc_ecma_transforms_optimization/src/const_modules.rs b/crates/swc_ecma_transforms_optimization/src/const_modules.rs -index 5e578e07c1..ed71111e62 100644 ---- a/crates/swc_ecma_transforms_optimization/src/const_modules.rs -+++ b/crates/swc_ecma_transforms_optimization/src/const_modules.rs -@@ -46,7 +46,10 @@ pub fn const_modules( - fn parse_option(cm: &SourceMap, name: &str, src: String) -> Arc { - static CACHE: Lazy, ARandomState>> = Lazy::new(DashMap::default); - -- let fm = cm.new_source_file(FileName::Custom(format!("", name)), src); -+ let fm = cm.new_source_file( -+ FileName::Internal(format!("", name)), -+ src, -+ ); - if let Some(expr) = CACHE.get(&**fm.src) { - return expr.clone(); - } -diff --git a/crates/swc_ecma_transforms_optimization/src/inline_globals.rs b/crates/swc_ecma_transforms_optimization/src/inline_globals.rs -index 434ff49600..6e67a0afd4 100644 ---- a/crates/swc_ecma_transforms_optimization/src/inline_globals.rs -+++ b/crates/swc_ecma_transforms_optimization/src/inline_globals.rs -@@ -4,9 +4,8 @@ use swc_common::{ - sync::Lrc, - }; - use swc_ecma_ast::*; --use swc_ecma_transforms_base::perf::Parallel; --use swc_ecma_transforms_macros::parallel; --use swc_ecma_utils::{collect_decls, NodeIgnoringSpan}; -+use swc_ecma_transforms_base::perf::{ParVisitMut, Parallel}; -+use swc_ecma_utils::{collect_decls, parallel::cpu_count, NodeIgnoringSpan}; - use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}; - - /// The key will be compared using [EqIgnoreSpan::eq_ignore_span], and matched -@@ -63,7 +62,6 @@ impl Parallel for InlineGlobals { - fn merge(&mut self, _: Self) {} - } - --#[parallel] - impl VisitMut for InlineGlobals { - noop_visit_mut_type!(); - -@@ -208,6 +206,22 @@ impl VisitMut for InlineGlobals { - - script.visit_mut_children_with(self); - } -+ -+ fn visit_mut_prop_or_spreads(&mut self, n: &mut Vec) { -+ self.visit_mut_par(cpu_count() * 8, n); -+ } -+ -+ fn visit_mut_expr_or_spreads(&mut self, n: &mut Vec) { -+ self.visit_mut_par(cpu_count() * 8, n); -+ } -+ -+ fn visit_mut_opt_vec_expr_or_spreads(&mut self, n: &mut Vec>) { -+ self.visit_mut_par(cpu_count() * 8, n); -+ } -+ -+ fn visit_mut_exprs(&mut self, n: &mut Vec>) { -+ self.visit_mut_par(cpu_count() * 8, n); -+ } - } - - #[cfg(test)] -diff --git a/crates/swc_ecma_transforms_optimization/src/json_parse.rs b/crates/swc_ecma_transforms_optimization/src/json_parse.rs -index 05e15cd2bc..2306961b8f 100644 ---- a/crates/swc_ecma_transforms_optimization/src/json_parse.rs -+++ b/crates/swc_ecma_transforms_optimization/src/json_parse.rs -@@ -4,7 +4,6 @@ use serde_json::Value; - use swc_common::{util::take::Take, Spanned, DUMMY_SP}; - use swc_ecma_ast::*; - use swc_ecma_transforms_base::perf::Parallel; --use swc_ecma_transforms_macros::parallel; - use swc_ecma_utils::{calc_literal_cost, member_expr, ExprFactory}; - use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}; - -@@ -54,7 +53,6 @@ impl Default for JsonParse { - } - } - --#[parallel] - impl VisitMut for JsonParse { - noop_visit_mut_type!(); - -diff --git a/crates/swc_ecma_transforms_proposal/src/decorator_2022_03.rs b/crates/swc_ecma_transforms_proposal/src/decorator_2022_03.rs -index b08bb7522c..9545436f92 100644 ---- a/crates/swc_ecma_transforms_proposal/src/decorator_2022_03.rs -+++ b/crates/swc_ecma_transforms_proposal/src/decorator_2022_03.rs -@@ -1505,7 +1505,7 @@ impl VisitMut for Decorator202203 { - specifiers: self.extra_exports.take(), - src: None, - type_only: false, -- asserts: None, -+ with: None, - }, - ))); - } -diff --git a/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs b/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs -index 1f19f83fa4..cabada213e 100644 ---- a/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs -+++ b/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs -@@ -526,7 +526,7 @@ impl VisitMut for TscDecorator { - specifiers: self.exports.take(), - src: None, - type_only: Default::default(), -- asserts: Default::default(), -+ with: Default::default(), - }, - ))); - } -diff --git a/crates/swc_ecma_transforms_proposal/src/decorators/mod.rs b/crates/swc_ecma_transforms_proposal/src/decorators/mod.rs -index 692c4d7021..026effac83 100644 ---- a/crates/swc_ecma_transforms_proposal/src/decorators/mod.rs -+++ b/crates/swc_ecma_transforms_proposal/src/decorators/mod.rs -@@ -212,7 +212,7 @@ impl Fold for Decorators { - .into()], - src: None, - type_only: false, -- asserts: None, -+ with: None, - }, - ))); - }}; -diff --git a/crates/swc_ecma_transforms_proposal/src/explicit_resource_management.rs b/crates/swc_ecma_transforms_proposal/src/explicit_resource_management.rs -index 344975aaeb..e860053898 100644 ---- a/crates/swc_ecma_transforms_proposal/src/explicit_resource_management.rs -+++ b/crates/swc_ecma_transforms_proposal/src/explicit_resource_management.rs -@@ -113,7 +113,7 @@ impl ExplicitResourceManagement { - })], - src: None, - type_only: Default::default(), -- asserts: None, -+ with: None, - })) - .unwrap(), - ); -@@ -152,7 +152,7 @@ impl ExplicitResourceManagement { - })], - src: None, - type_only: Default::default(), -- asserts: None, -+ with: None, - })) - .unwrap(), - ); -diff --git a/crates/swc_ecma_transforms_proposal/src/export_default_from.rs b/crates/swc_ecma_transforms_proposal/src/export_default_from.rs -index a310918f3f..6718a6d9ac 100644 ---- a/crates/swc_ecma_transforms_proposal/src/export_default_from.rs -+++ b/crates/swc_ecma_transforms_proposal/src/export_default_from.rs -@@ -39,7 +39,7 @@ impl VisitMut for ExportDefaultFrom { - specifiers, - src: Some(src), - type_only: false, -- asserts, -+ with, - })) if specifiers.iter().any(|s| s.is_default()) => { - let mut origin_specifiers = vec![]; - -@@ -79,7 +79,7 @@ impl VisitMut for ExportDefaultFrom { - specifiers: export_specifiers, - src: Some(src.clone()), - type_only: false, -- asserts: None, -+ with: None, - }, - ))); - -@@ -90,7 +90,7 @@ impl VisitMut for ExportDefaultFrom { - specifiers: origin_specifiers, - src: Some(src), - type_only: false, -- asserts, -+ with, - }, - ))); - } -diff --git a/crates/swc_ecma_transforms_proposal/src/import_assertions.rs b/crates/swc_ecma_transforms_proposal/src/import_assertions.rs -index 78aecc0575..5612a312f1 100644 ---- a/crates/swc_ecma_transforms_proposal/src/import_assertions.rs -+++ b/crates/swc_ecma_transforms_proposal/src/import_assertions.rs -@@ -1,23 +1,27 @@ - use swc_ecma_ast::{ExportAll, ImportDecl, NamedExport}; - use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut}; - --pub fn import_assertions() -> impl VisitMut + Fold { -+#[deprecated(note = "Please use `import_assertions` instead")] -+pub use self::import_attributes as import_assertions; -+ -+pub fn import_attributes() -> impl VisitMut + Fold { - as_folder(ImportAssertions) - } -+ - struct ImportAssertions; - - impl VisitMut for ImportAssertions { - noop_visit_mut_type!(); - - fn visit_mut_import_decl(&mut self, n: &mut ImportDecl) { -- n.asserts = None; -+ n.with = None; - } - - fn visit_mut_export_all(&mut self, n: &mut ExportAll) { -- n.asserts = None; -+ n.with = None; - } - - fn visit_mut_named_export(&mut self, n: &mut NamedExport) { -- n.asserts = None; -+ n.with = None; - } - } -diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit_resource_management.rs b/crates/swc_ecma_transforms_proposal/tests/explicit_resource_management.rs -index e546b724f4..df568a7209 100644 ---- a/crates/swc_ecma_transforms_proposal/tests/explicit_resource_management.rs -+++ b/crates/swc_ecma_transforms_proposal/tests/explicit_resource_management.rs -@@ -12,7 +12,7 @@ fn exec(input: PathBuf) { - exec_tr( - "explicit-resource-management", - Syntax::Es(EsConfig { -- using_decl: true, -+ explicit_resource_management: true, - ..Default::default() - }), - |_| { -@@ -42,7 +42,7 @@ fn run_fixture(input: PathBuf) { - - test_fixture( - Syntax::Es(EsConfig { -- using_decl: true, -+ explicit_resource_management: true, - ..Default::default() - }), - &|_t| { -diff --git a/crates/swc_ecma_transforms_proposal/tests/import_assertions.rs b/crates/swc_ecma_transforms_proposal/tests/import_assertions.rs -index e4165c0b35..b4b79c980d 100644 ---- a/crates/swc_ecma_transforms_proposal/tests/import_assertions.rs -+++ b/crates/swc_ecma_transforms_proposal/tests/import_assertions.rs -@@ -9,7 +9,7 @@ fn tr() -> impl Fold { - - fn syntax() -> Syntax { - Syntax::Es(EsConfig { -- import_assertions: true, -+ import_attributes: true, - ..Default::default() - }) - } -diff --git a/crates/swc_ecma_transforms_react/src/jsx/mod.rs b/crates/swc_ecma_transforms_react/src/jsx/mod.rs -index 2fd57a788a..ea53f552cd 100644 ---- a/crates/swc_ecma_transforms_react/src/jsx/mod.rs -+++ b/crates/swc_ecma_transforms_react/src/jsx/mod.rs -@@ -118,7 +118,7 @@ pub fn parse_expr_for_jsx( - src: String, - top_level_mark: Mark, - ) -> Arc> { -- let fm = cm.new_source_file(FileName::Custom(format!("", name)), src); -+ let fm = cm.new_source_file(FileName::Internal(format!("", name)), src); - - parse_file_as_expr( - &fm, -@@ -1059,7 +1059,7 @@ where - } - .into(), - type_only: Default::default(), -- asserts: Default::default(), -+ with: Default::default(), - })), - ) - }); -diff --git a/crates/swc_ecma_transforms_react/src/jsx/tests.rs b/crates/swc_ecma_transforms_react/src/jsx/tests.rs -index 7ab15dd9f0..acf91be34d 100644 ---- a/crates/swc_ecma_transforms_react/src/jsx/tests.rs -+++ b/crates/swc_ecma_transforms_react/src/jsx/tests.rs -@@ -1499,12 +1499,9 @@ fn test_script(src: &str, output: &Path, options: Options) { - let mut buf = vec![]; - - let mut emitter = Emitter { -- cfg: Config { -- target: Default::default(), -- ascii_only: true, -- minify: false, -- omit_last_semi: true, -- }, -+ cfg: Config::default() -+ .with_ascii_only(true) -+ .with_omit_last_semi(true), - cm: tester.cm.clone(), - wr: Box::new(swc_ecma_codegen::text_writer::JsWriter::new( - tester.cm.clone(), -diff --git a/crates/swc_ecma_transforms_typescript/benches/compat.rs b/crates/swc_ecma_transforms_typescript/benches/compat.rs -index 9b4e2ca9e7..705cb879ac 100644 ---- a/crates/swc_ecma_transforms_typescript/benches/compat.rs -+++ b/crates/swc_ecma_transforms_typescript/benches/compat.rs -@@ -152,7 +152,7 @@ fn es2020_nullish_coalescing(b: &mut Bencher) { - - fn es2020_optional_chaining(b: &mut Bencher) { - run(b, || { -- swc_ecma_transforms_compat::es2020::optional_chaining(Default::default()) -+ swc_ecma_transforms_compat::es2020::optional_chaining(Default::default(), Mark::new()) - }); - } - -diff --git a/crates/swc_ecma_transforms_typescript/examples/ts_to_js.rs b/crates/swc_ecma_transforms_typescript/examples/ts_to_js.rs -index 7edfc18cad..680d3ab0e0 100644 ---- a/crates/swc_ecma_transforms_typescript/examples/ts_to_js.rs -+++ b/crates/swc_ecma_transforms_typescript/examples/ts_to_js.rs -@@ -80,10 +80,7 @@ fn main() { - let mut buf = vec![]; - { - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify: false, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default(), - cm: cm.clone(), - comments: Some(&comments), - wr: JsWriter::new(cm.clone(), "\n", &mut buf, None), -diff --git a/crates/swc_ecma_transforms_typescript/src/import_export_assign.rs b/crates/swc_ecma_transforms_typescript/src/import_export_assign.rs -index 25f8b58c8b..5e9a3acc20 100644 ---- a/crates/swc_ecma_transforms_typescript/src/import_export_assign.rs -+++ b/crates/swc_ecma_transforms_typescript/src/import_export_assign.rs -@@ -56,7 +56,7 @@ impl VisitMut for ImportExportAssign { - .into()], - src: Box::new(quote_str!("module")), - type_only: false, -- asserts: None, -+ with: None, - }) - .into(), - ); -@@ -160,7 +160,7 @@ impl VisitMut for ImportExportAssign { - .into()], - src: None, - type_only: false, -- asserts: None, -+ with: None, - } - .into(), - )) -diff --git a/crates/swc_ecma_transforms_typescript/src/strip.rs b/crates/swc_ecma_transforms_typescript/src/strip.rs -index 6c7419892f..97bbf218cc 100644 ---- a/crates/swc_ecma_transforms_typescript/src/strip.rs -+++ b/crates/swc_ecma_transforms_typescript/src/strip.rs -@@ -2205,7 +2205,7 @@ where - specifiers: vec![], - src: None, - type_only: false, -- asserts: None, -+ with: None, - }, - ))) - } -diff --git a/crates/swc_ecma_transforms_typescript/tests/strip.rs b/crates/swc_ecma_transforms_typescript/tests/strip.rs -index 549a7ee9a6..08326e3ff0 100644 ---- a/crates/swc_ecma_transforms_typescript/tests/strip.rs -+++ b/crates/swc_ecma_transforms_typescript/tests/strip.rs -@@ -729,7 +729,7 @@ test!( - constructor(a) { - } - } -- (()=>{ A.b = 'foo'; })();" -+ A.b = 'foo';" - ); - - test!( -@@ -3146,7 +3146,20 @@ test!( - decorators: true, - ..Default::default() - }), -- |_| chain!(tr(), optional_chaining(Default::default())), -+ |_| { -+ let unresolved_mark = Mark::new(); -+ let top_level_mark = Mark::new(); -+ let config = strip::Config { -+ no_empty_export: true, -+ ..Default::default() -+ }; -+ chain!( -+ Optional::new(decorators(Default::default()), false,), -+ resolver(unresolved_mark, top_level_mark, true), -+ strip_with_config(config, top_level_mark), -+ optional_chaining(Default::default(), unresolved_mark) -+ ) -+ }, - issue_1149_1, - " - const tmp = tt?.map((t: any) => t).join((v: any) => v); -@@ -3668,9 +3681,7 @@ to!( - prop = (console.log(1), 'a'); - prop1 = (console.log(2), 'b'); - })(); -- (()=>{ -- A[prop1] = 2; -- })(); -+ A[prop1] = 2; - " - ); - -@@ -3696,9 +3707,7 @@ to!( - prop = (console.log(1), 'a'); - prop1 = (console.log(2), 'b'); - })(); -- (()=>{ -- A[prop1] = 2; -- })(); -+ A[prop1] = 2; - " - ); - -@@ -3769,7 +3778,7 @@ to!( - " - var _class; - const A = (_class = class {}, -- (()=>{ _class.a = 1; })(), -+ _class.a = 1, - _class); - " - ); -@@ -4144,9 +4153,9 @@ to!( - var _TestClass; - var _class; - let TestClass = _class = someClassDecorator((_class = (_TestClass = class TestClass { -- }, (()=>{ _TestClass.Something = 'hello'; })(), (()=>{ _TestClass.SomeProperties = { -+ }, _TestClass.Something = 'hello', _TestClass.SomeProperties = { - firstProp: _TestClass.Something -- };})(), _TestClass)) || _class) || _class; -+ }, _TestClass)) || _class) || _class; - function someClassDecorator(c) { - return c; - } -@@ -4210,7 +4219,7 @@ class Foo { - const identifier = 'bar'; - class Foo { - } --(()=>{ Foo.identifier = 5; })(); -+Foo.identifier = 5; - " - ); - -diff --git a/crates/swc_ecma_transforms_typescript/tests/strip_correctness.rs b/crates/swc_ecma_transforms_typescript/tests/strip_correctness.rs -index fe22e0b1e6..21f42d1011 100644 ---- a/crates/swc_ecma_transforms_typescript/tests/strip_correctness.rs -+++ b/crates/swc_ecma_transforms_typescript/tests/strip_correctness.rs -@@ -188,10 +188,7 @@ fn identity(entry: PathBuf) { - - { - let mut emitter = Emitter { -- cfg: swc_ecma_codegen::Config { -- minify: false, -- ..Default::default() -- }, -+ cfg: swc_ecma_codegen::Config::default(), - cm: cm.clone(), - wr: Box::new(swc_ecma_codegen::text_writer::JsWriter::new( - cm.clone(), -@@ -239,7 +236,7 @@ fn identity(entry: PathBuf) { - decorators: true, - decorators_before_export: true, - export_default_from: true, -- import_assertions: true, -+ import_attributes: true, - allow_super_outside_method: true, - ..Default::default() - }), -diff --git a/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs b/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs -index a8fa1cdbd8..c4fa2462bc 100644 ---- a/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs -+++ b/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs -@@ -919,8 +919,8 @@ where - v.add_accessed_property(prop.sym.clone()); - } - -- if self.ctx.in_assign_lhs || self.ctx.is_delete_arg { -- self.data.mark_property_mutattion(obj.to_id(), self.ctx) -+ if self.ctx.in_assign_lhs || self.ctx.in_update_arg || self.ctx.is_delete_arg { -+ self.data.mark_property_mutation(obj.to_id(), self.ctx) - } - }) - } -diff --git a/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs b/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs -index c15bcfdb41..3abb2c422e 100644 ---- a/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs -+++ b/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs -@@ -30,7 +30,7 @@ pub trait Storage: Sized + Default { - fn get_initialized_cnt(&self) -> usize; - fn truncate_initialized_cnt(&mut self, len: usize); - -- fn mark_property_mutattion(&mut self, id: Id, ctx: Ctx); -+ fn mark_property_mutation(&mut self, id: Id, ctx: Ctx); - } - - pub trait ScopeDataLike: Sized + Default + Clone { -@@ -65,10 +65,6 @@ pub trait VarDataLike: Sized { - - fn add_accessed_property(&mut self, name: JsWord); - -- fn mark_mutated(&mut self); -- -- fn mark_reassigned(&mut self); -- - fn mark_used_as_ref(&mut self); - - fn add_infects_to(&mut self, other: Access); -diff --git a/crates/swc_ecma_visit/src/lib.rs b/crates/swc_ecma_visit/src/lib.rs -index 346d91364e..788cd818c4 100644 ---- a/crates/swc_ecma_visit/src/lib.rs -+++ b/crates/swc_ecma_visit/src/lib.rs -@@ -1095,20 +1095,20 @@ define!({ - pub specifiers: Vec, - pub src: Box, - pub type_only: bool, -- pub asserts: Option>, -+ pub with: Option>, - } - pub struct ExportAll { - pub span: Span, - pub src: Box, - pub type_only: bool, -- pub asserts: Option>, -+ pub with: Option>, - } - pub struct NamedExport { - pub span: Span, - pub specifiers: Vec, - pub src: Option>, - pub type_only: bool, -- pub asserts: Option>, -+ pub with: Option>, - } - pub struct ExportDefaultDecl { - pub span: Span, -diff --git a/crates/swc_estree_ast/src/module.rs b/crates/swc_estree_ast/src/module.rs -index 70fd9ba1ff..54e75758e7 100644 ---- a/crates/swc_estree_ast/src/module.rs -+++ b/crates/swc_estree_ast/src/module.rs -@@ -143,7 +143,7 @@ pub struct ExportAllDeclaration { - pub base: BaseNode, - pub source: StringLiteral, - #[serde(default)] -- pub assertions: Option>, -+ pub with: Option>, - #[serde(default)] - pub export_kind: Option, - } -@@ -192,7 +192,7 @@ pub struct ExportNamedDeclaration { - #[serde(default)] - pub source: Option, - #[serde(default)] -- pub assertions: Option>, -+ pub with: Option>, - #[serde(default)] - pub export_kind: Option, - } -@@ -270,7 +270,7 @@ pub struct ImportDeclaration { - pub specifiers: Vec, - pub source: StringLiteral, - #[serde(default)] -- pub assertions: Option>, -+ pub with: Option>, - #[serde(default)] - pub import_kind: Option, - } -diff --git a/crates/swc_estree_compat/benches/babelify.rs b/crates/swc_estree_compat/benches/babelify.rs -index 7b0b94f68c..5076f7615e 100644 ---- a/crates/swc_estree_compat/benches/babelify.rs -+++ b/crates/swc_estree_compat/benches/babelify.rs -@@ -55,7 +55,7 @@ fn babelify_only(b: &mut Bencher) { - module - .fold_with(&mut resolver(unresolved_mark, top_level_mark, true)) - .fold_with(&mut typescript::strip(top_level_mark)) -- .fold_with(&mut es2020(Default::default())) -+ .fold_with(&mut es2020(Default::default(), unresolved_mark)) - }); - - b.iter(|| { -diff --git a/crates/swc_estree_compat/src/babelify/module_decl.rs b/crates/swc_estree_compat/src/babelify/module_decl.rs -index 4d49dc8c37..91fea257b3 100644 ---- a/crates/swc_estree_compat/src/babelify/module_decl.rs -+++ b/crates/swc_estree_compat/src/babelify/module_decl.rs -@@ -90,13 +90,13 @@ impl Babelify for ExportDecl { - declaration: Some(Box::alloc().init(self.decl.babelify(ctx))), - specifiers: Default::default(), - source: Default::default(), -- assertions: Default::default(), -+ with: Default::default(), - export_kind: Default::default(), - } - } - } - --fn convert_import_asserts( -+fn convert_import_attrs( - asserts: Option>, - ctx: &Context, - ) -> Option> { -@@ -161,7 +161,7 @@ impl Babelify for ImportDecl { - base: ctx.base(self.span), - specifiers: self.specifiers.babelify(ctx), - source: self.src.babelify(ctx), -- assertions: convert_import_asserts(self.asserts, ctx), -+ with: convert_import_attrs(self.with, ctx), - import_kind: if self.type_only { - Some(ImportKind::Type) - } else { -@@ -178,7 +178,7 @@ impl Babelify for ExportAll { - ExportAllDeclaration { - base: ctx.base(self.span), - source: self.src.babelify(ctx), -- assertions: convert_import_asserts(self.asserts, ctx), -+ with: convert_import_attrs(self.with, ctx), - export_kind: if self.type_only { - Some(ExportKind::Type) - } else { -@@ -197,7 +197,7 @@ impl Babelify for NamedExport { - declaration: Default::default(), - specifiers: self.specifiers.babelify(ctx), - source: self.src.map(|s| s.babelify(ctx)), -- assertions: convert_import_asserts(self.asserts, ctx), -+ with: convert_import_attrs(self.with, ctx), - export_kind: if self.type_only { - Some(ExportKind::Type) - } else { -diff --git a/crates/swc_estree_compat/src/swcify/stmt.rs b/crates/swc_estree_compat/src/swcify/stmt.rs -index 80bae36d8e..4b806296ec 100644 ---- a/crates/swc_estree_compat/src/swcify/stmt.rs -+++ b/crates/swc_estree_compat/src/swcify/stmt.rs -@@ -436,8 +436,8 @@ impl Swcify for ExportAllDeclaration { - span: ctx.span(&self.base), - src: self.source.swcify(ctx).into(), - type_only: self.export_kind == Some(ExportKind::Type), -- asserts: self -- .assertions -+ with: self -+ .with - .swcify(ctx) - .map(|props| { - props -@@ -528,8 +528,8 @@ impl Swcify for ExportNamedDeclaration { - specifiers: self.specifiers.swcify(ctx), - src: self.source.swcify(ctx).map(Box::new), - type_only: false, -- asserts: self -- .assertions -+ with: self -+ .with - .swcify(ctx) - .map(|props| { - props -@@ -625,8 +625,8 @@ impl Swcify for ImportDeclaration { - specifiers: self.specifiers.swcify(ctx), - src: self.source.swcify(ctx).into(), - type_only: false, -- asserts: self -- .assertions -+ with: self -+ .with - .swcify(ctx) - .map(|props| { - props -@@ -779,7 +779,7 @@ impl Swcify for DeclareExportAllDeclaration { - span: ctx.span(&self.base), - src: self.source.swcify(ctx).into(), - type_only: self.export_kind == Some(ExportKind::Type), -- asserts: Default::default(), -+ with: Default::default(), - } - } - } -diff --git a/crates/swc_html_minifier/src/lib.rs b/crates/swc_html_minifier/src/lib.rs -index bca0f876bf..4a83a7d9aa 100644 ---- a/crates/swc_html_minifier/src/lib.rs -+++ b/crates/swc_html_minifier/src/lib.rs -@@ -2114,12 +2114,7 @@ impl Minifier<'_> { - )) as Box; - - let mut emitter = swc_ecma_codegen::Emitter { -- cfg: swc_ecma_codegen::Config { -- target, -- minify: false, -- ascii_only: false, -- omit_last_semi: false, -- }, -+ cfg: swc_ecma_codegen::Config::default().with_target(target), - cm, - comments: Some(&comments), - wr, -diff --git a/crates/swc_html_parser/src/lexer/mod.rs b/crates/swc_html_parser/src/lexer/mod.rs -index eb03d36877..f63a2eec62 100644 ---- a/crates/swc_html_parser/src/lexer/mod.rs -+++ b/crates/swc_html_parser/src/lexer/mod.rs -@@ -152,7 +152,10 @@ where - // A leading Byte Order Mark (BOM) causes the character encoding argument to be - // ignored and will itself be skipped. - if lexer.input.is_at_start() && lexer.input.cur() == Some('\u{feff}') { -- lexer.input.bump(); -+ unsafe { -+ // Safety: We know that the current character is '\u{feff}'. -+ lexer.input.bump(); -+ } - } - - lexer -@@ -240,13 +243,19 @@ where - self.cur_pos = self.input.cur_pos(); - - if self.cur.is_some() { -- self.input.bump(); -+ unsafe { -+ // Safety: self.cur is Some() -+ self.input.bump(); -+ } - } - } - - #[inline(always)] - fn reconsume(&mut self) { -- self.input.reset_to(self.cur_pos); -+ unsafe { -+ // Safety: self.cur_pos is valid position because we got it from self.input -+ self.input.reset_to(self.cur_pos); -+ } - } - - #[inline(always)] -@@ -401,7 +410,10 @@ where - sub_buf.push(c); - - if self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('\n') -+ self.input.bump(); -+ } - - sub_buf.push('\n'); - } -@@ -466,7 +478,10 @@ where - sub_buf.push(c); - - if self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('\n') -+ self.input.bump(); -+ } - - sub_buf.push('\n'); - } -@@ -497,7 +512,10 @@ where - sub_buf.push(c); - - if self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('\n') -+ self.input.bump(); -+ } - - sub_buf.push('\n'); - } -@@ -795,7 +813,10 @@ where - sub_buf.push('\r'); - - if self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('\n') -+ self.input.bump(); -+ } - - sub_buf.push('\n'); - } -@@ -826,7 +847,10 @@ where - sub_buf.push(c); - - if self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('\n') -+ self.input.bump(); -+ } - - sub_buf.push('\n'); - } -@@ -955,7 +979,10 @@ where - sub_buf.push(c); - - if self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('\n') -+ self.input.bump(); -+ } - - sub_buf.push('\n'); - } -@@ -1022,8 +1049,10 @@ where - buf.push(c); - - if self.input.cur() == Some('\n') { -- self.input.bump(); -- -+ unsafe { -+ // Safety: cur() is Some('\n') -+ self.input.bump(); -+ } - buf.push('\n'); - } - -@@ -2861,7 +2890,10 @@ where - lexer.state = State::BogusComment; - lexer.cur_pos = cur_pos; - // We don't validate input here because we reset position -- lexer.input.reset_to(cur_pos); -+ unsafe { -+ // Safety: We reset position to the previous one -+ lexer.input.reset_to(cur_pos); -+ } - }; - - // If the next few characters are: -@@ -3536,7 +3568,11 @@ where - _ => { - buf.clear(); - self.cur_pos = cur_pos; -- self.input.reset_to(cur_pos); -+ unsafe { -+ // Safety: We got cur_pos from self.input.cur_pos() above, so -+ // it's a valid position. -+ self.input.reset_to(cur_pos); -+ } - self.emit_error( - ErrorKind::InvalidCharacterSequenceAfterDoctypeName, - ); -@@ -4393,10 +4429,16 @@ where - - if entity.is_some() { - self.cur_pos = entity_cur_pos.unwrap(); -- self.input.reset_to(entity_cur_pos.unwrap()); -+ unsafe { -+ // Safety: We got entity_cur_pos from the input, so it's valid -+ self.input.reset_to(entity_cur_pos.unwrap()); -+ } - } else { - self.cur_pos = initial_cur_pos; -- self.input.reset_to(initial_cur_pos); -+ unsafe { -+ // Safety: We got initial_cur_pos from the input, so it's valid -+ self.input.reset_to(initial_cur_pos); -+ } - } - - let is_last_semicolon = self.temporary_buffer.ends_with(';'); -@@ -4814,7 +4856,10 @@ where - #[inline(always)] - fn skip_whitespaces(&mut self, c: char) { - if c == '\r' && self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some -+ self.input.bump(); -+ } - } - } - } -diff --git a/crates/swc_node_bundler/tests/fixture.rs b/crates/swc_node_bundler/tests/fixture.rs -index 9ecac943b3..237f37a622 100644 ---- a/crates/swc_node_bundler/tests/fixture.rs -+++ b/crates/swc_node_bundler/tests/fixture.rs -@@ -91,15 +91,13 @@ fn pass(input_dir: PathBuf) { - None, - None, - false, -- EsVersion::Es2020, - SourceMapsConfig::Bool(false), - &Default::default(), - None, -- false, - Some(&comments), - false, -- false, - Default::default(), -+ swc_ecma_codegen::Config::default().with_target(EsVersion::Es2020), - ) - .expect("failed to print?") - .code; -diff --git a/crates/swc_plugin_runner/src/wasix_runtime.rs b/crates/swc_plugin_runner/src/wasix_runtime.rs -index 09e8d0b54e..1fa2fb936a 100644 ---- a/crates/swc_plugin_runner/src/wasix_runtime.rs -+++ b/crates/swc_plugin_runner/src/wasix_runtime.rs -@@ -1,3 +1,5 @@ -+#![allow(unused)] -+ - use std::{path::PathBuf, sync::Arc}; - - use parking_lot::Mutex; -diff --git a/crates/swc_xml_parser/src/lexer/mod.rs b/crates/swc_xml_parser/src/lexer/mod.rs -index 1b259f65ad..85eb3e0659 100644 ---- a/crates/swc_xml_parser/src/lexer/mod.rs -+++ b/crates/swc_xml_parser/src/lexer/mod.rs -@@ -171,7 +171,10 @@ where - // A leading Byte Order Mark (BOM) causes the character encoding argument to be - // ignored and will itself be skipped. - if lexer.input.is_at_start() && lexer.input.cur() == Some('\u{feff}') { -- lexer.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('\u{feff}') -+ lexer.input.bump(); -+ } - } - - lexer -@@ -247,13 +250,19 @@ where - self.cur_pos = self.input.cur_pos(); - - if self.cur.is_some() { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some(c) -+ self.input.bump(); -+ } - } - } - - #[inline(always)] - fn reconsume(&mut self) { -- self.input.reset_to(self.cur_pos); -+ unsafe { -+ // Safety: We got cur_pos from self.input -+ self.input.reset_to(self.cur_pos); -+ } - } - - #[inline(always)] -@@ -299,7 +308,10 @@ where - let anything_else = |lexer: &mut Lexer| { - lexer.emit_error(ErrorKind::InvalidEntityCharacter); - lexer.cur_pos = cur_pos; -- lexer.input.reset_to(cur_pos); -+ unsafe { -+ // Safety: We got cur_post from self.input -+ lexer.input.reset_to(cur_pos); -+ } - }; - - // This section defines how to consume a character reference, optionally with an -@@ -318,7 +330,10 @@ where - Some(c) if self.additional_allowed_character == Some(c) => { - self.emit_error(ErrorKind::InvalidEntityCharacter); - self.cur_pos = cur_pos; -- self.input.reset_to(cur_pos); -+ unsafe { -+ // Safety: We got cur_post from self.input -+ self.input.reset_to(cur_pos); -+ } - } - Some('l') => match self.consume_next_char() { - Some('t') => { -@@ -467,7 +482,10 @@ where - if characters.is_empty() { - // TODO - self.cur_pos = cur_pos; -- self.input.reset_to(cur_pos); -+ unsafe { -+ // Safety: We got cur_post from self.input -+ self.input.reset_to(cur_pos); -+ } - - return None; - } -@@ -553,7 +571,10 @@ where - raw.push(c); - - if self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('\n') -+ self.input.bump(); -+ } - - raw.push('\n'); - } -@@ -873,7 +894,10 @@ where - raw_c.push(c); - - if self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('\n') -+ self.input.bump(); -+ } - - raw_c.push('\n'); - } -@@ -937,7 +961,10 @@ where - raw.push(c); - - if self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('\n') -+ self.input.bump(); -+ } - - raw.push('\n'); - } -@@ -1236,7 +1263,10 @@ where - lexer.state = State::BogusComment; - lexer.cur_pos = cur_pos; - // We don't validate input here because we reset position -- lexer.input.reset_to(cur_pos); -+ unsafe { -+ // Safety: cur_pos is in the range of input -+ lexer.input.reset_to(cur_pos); -+ } - }; - - // If the next few characters are: -@@ -2464,7 +2494,10 @@ where - } - _ => { - self.cur_pos = cur_pos; -- self.input.reset_to(cur_pos); -+ unsafe { -+ // Safety: We got cur_pos from self.input.cur_pos() -+ self.input.reset_to(cur_pos); -+ } - self.emit_error( - ErrorKind::InvalidCharacterSequenceAfterDoctypeName, - ); -@@ -3074,7 +3107,10 @@ where - #[inline(always)] - fn skip_next_lf(&mut self, c: char) { - if c == '\r' && self.input.cur() == Some('\n') { -- self.input.bump(); -+ unsafe { -+ // Safety: cur() is Some('\n') -+ self.input.bump(); -+ } - } - } - }