swc/out.txt
2023-09-24 13:59:06 +09:00

7100 lines
277 KiB
Plaintext

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<TransformOut
None,
options.output_path,
true,
- codegen_target,
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(codegen_target)
+ .with_minify(options.config.minify.into_bool()),
)
.convert_err()
})
diff --git a/crates/binding_macros/src/wasm.rs b/crates/binding_macros/src/wasm.rs
index cfb25ab747..8aef150de0 100644
--- a/crates/binding_macros/src/wasm.rs
+++ b/crates/binding_macros/src/wasm.rs
@@ -248,17 +248,17 @@ macro_rules! build_print_sync {
None,
None,
true,
- opts.codegen_target().unwrap_or($crate::wasm::EsVersion::Es2020),
opts.source_maps
.clone()
.unwrap_or($crate::wasm::SourceMapsConfig::Bool(false)),
&Default::default(),
None,
- opts.config.minify.into(),
None,
opts.config.emit_source_map_columns.into_bool(),
- false,
Default::default(),
+ swc_core::ecma::codegen::Config::default()
+ .with_target(opts.codegen_target().unwrap_or($crate::wasm::EsVersion::Es2020))
+ .with_minify(opts.config.minify.into())
),"failed to print code")?;
serde_wasm_bindgen::to_value(&s)
diff --git a/crates/dbg-swc/src/util/mod.rs b/crates/dbg-swc/src/util/mod.rs
index 8acfd2d05a..00f0bb3033 100644
--- a/crates/dbg-swc/src/util/mod.rs
+++ b/crates/dbg-swc/src/util/mod.rs
@@ -89,10 +89,7 @@ pub fn print_js(cm: Arc<SourceMap>, m: &Module, minify: bool) -> Result<String>
}
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<Config>,
comments: Option<&'a SingleThreadedComments>,
custom_before_pass: impl FnOnce(&Program) -> P,
- ) -> Result<BuiltInput<impl 'a + swc_ecma_visit::Fold>, Error>
+ ) -> Result<BuiltInput<Box<dyn 'a + Fold>>, 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<dyn Fold> = 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::<Option<&dyn Comments>>(
- 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::<Option<&dyn Comments>>(
+ 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<JsMinifyCommentOption> {
@@ -1340,6 +1360,7 @@ impl Config {
}
/// One `BuiltConfig` per a directory with swcrc
+#[non_exhaustive]
pub struct BuiltInput<P: swc_ecma_visit::Fold> {
pub program: Program,
pub pass: P,
@@ -1363,6 +1384,37 @@ pub struct BuiltInput<P: swc_ecma_visit::Fold> {
pub emit_source_map_columns: bool,
pub output: JscOutputConfig,
+ pub emit_assert_for_import_attributes: bool,
+}
+
+impl<P> BuiltInput<P>
+where
+ P: swc_ecma_visit::Fold,
+{
+ pub fn with_pass<N>(self, map: impl FnOnce(P) -> N) -> BuiltInput<N>
+ 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<Vec<PluginConfig>>,
/// If true, keeps import assertions in the output.
#[serde(default)]
- pub keep_import_assertions: BoolConfig<false>,
+ pub keep_import_attributes: BoolConfig<false>,
+
+ #[serde(default)]
+ pub emit_assert_for_import_attributes: BoolConfig<false>,
/// 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<String>,
+
+ #[serde(default)]
+ pub disable_builtin_transforms_for_internal_testing: BoolConfig<false>,
}
#[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<SwcImportResolver> {
- static CACHE: Lazy<DashMap<(PathBuf, CompiledPaths), SwcImportResolver, ARandomState>> =
+fn build_resolver(
+ mut base_url: PathBuf,
+ paths: CompiledPaths,
+ resolve_fully: bool,
+) -> Box<SwcImportResolver> {
+ static CACHE: Lazy<DashMap<(PathBuf, CompiledPaths, bool), SwcImportResolver, ARandomState>> =
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<TsConfigResolver<NodeModulesResolver>> {
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<PathBuf>,
inline_sources_content: bool,
- target: EsVersion,
source_map: SourceMapsConfig,
source_map_names: &AHashMap<BytePos, JsWord>,
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<TransformOutput, Error>
where
T: Node + VisitWith<IdentCollector>,
@@ -535,17 +533,12 @@ impl Compiler {
w.preamble(preamble).unwrap();
let mut wr = Box::new(w) as Box<dyn WriteJs>;
- 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<Option<Config>, Error> {
static CUR_DIR: Lazy<PathBuf> = 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<SourceFile>,
@@ -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<P1, P2>(
&self,
fm: Arc<SourceFile>,
@@ -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<SourceFile>,
@@ -1019,7 +995,7 @@ impl Compiler {
)
}
- #[tracing::instrument(level = "info", skip_all)]
+ #[tracing::instrument(skip_all)]
pub fn minify(
&self,
fm: Arc<SourceFile>,
@@ -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<PathBuf>
None
}
-#[tracing::instrument(level = "info", skip_all)]
+#[tracing::instrument(skip_all)]
fn load_swcrc(path: &Path) -> Result<Rc, Error> {
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<Options> {
..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<Comment>| -> 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<TestUnitData> {
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<SourceMap>, modules: Vec<Bundle>, 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<SourceMap>, 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<char>;
fn peek(&mut self) -> Option<char>;
fn peek_ahead(&mut self) -> Option<char>;
- 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<ForgivingSelectorList>,
+ /// https://drafts.csswg.org/css-cascade-6/#typedef-scope-end
+ pub scope_end: Option<ForgivingSelectorList>,
}
#[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<Error> {
@@ -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::<Vec<Rule>>()?;
+ let rule_list: Vec<ComponentValue> =
+ rule_list.into_iter().map(ComponentValue::from).collect();
+
+ rule_list
+ }
_ => {
return Err(Error::new(Default::default(), ErrorKind::Ignore));
}
@@ -2625,3 +2641,67 @@ where
})
}
}
+
+impl<I> Parse<ScopeRange> for Parser<I>
+where
+ I: ParserInput,
+{
+ fn parse(&mut self) -> PResult<ScopeRange> {
+ 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<ForgivingSelectorList>,
+ pub scope_end: Option<ForgivingSelectorList>,
}
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<Box<ObjectLit>>,
+ pub with: Option<Box<ObjectLit>>,
}
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<Box<ObjectLit>>,
+ pub with: Option<Box<ObjectLit>>,
}
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<Box<ObjectLit>>,
+ pub with: Option<Box<ObjectLit>>,
}
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<String, ImportAssertion>),
}
-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<String, String>,
// 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<String, String>,
+ 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<Option<PathBuf>, 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<FileName, Error> {
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<FileName, Error> {
+ 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<R> Resolve for TsConfigResolver<R>
@@ -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<N: swc_ecma_codegen::Node>(cm: Lrc<SourceMap>, 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<N: swc_ecma_codegen::Node>(cm: Lrc<SourceMap>, 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<N: swc_ecma_codegen::Node>(cm: Lrc<SourceMap>, 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<N: swc_ecma_codegen::Node>(cm: Lrc<SourceMap>, 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(&param.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(&param_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<N: swc_ecma_codegen::Node>(
}
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<N: swc_ecma_codegen::Node>(
}
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<SourceMap>, 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<N: swc_ecma_codegen::Node>(
}
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!["`{'>'}`", "`&gt;`"],
},
);
- 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!["`{'}'}`", "`&rbrace;`"],
},
);
- 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<Option<Token>> {
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<Token> {
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<Token> {
- 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<Token> {
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<Option<Token>> {
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<Token> {
- 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<I: Tokens> Parser<I> {
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<I> {
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<I> {
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<I: Tokens> Parser<I> {
_ => 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::<Box<Expr>>()? {
Expr::Object(v) => Some(Box::new(v)),
@@ -80,7 +80,7 @@ impl<I: Tokens> Parser<I> {
src,
specifiers: vec![],
type_only: false,
- asserts,
+ with,
}))
.map(ModuleItem::from);
}
@@ -157,9 +157,9 @@ impl<I: Tokens> Parser<I> {
};
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::<Box<Expr>>()? {
Expr::Object(v) => Some(Box::new(v)),
@@ -176,7 +176,7 @@ impl<I: Tokens> Parser<I> {
specifiers,
src,
type_only,
- asserts,
+ with,
}))
.map(ModuleItem::from)
}
@@ -570,12 +570,12 @@ impl<I: Tokens> Parser<I> {
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<I: Tokens> Parser<I> {
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<I: Tokens> Parser<I> {
}
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<I: Tokens> Parser<I> {
specifiers,
src,
type_only,
- asserts,
+ with,
}));
};
@@ -793,7 +793,7 @@ impl<I: Tokens> Parser<I> {
})
}
- /// 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<Str>, Option<Box<ObjectLit>>)> {
expect!(self, "from");
@@ -810,9 +810,9 @@ impl<I: Tokens> Parser<I> {
_ => 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::<Box<Expr>>()? {
Expr::Object(v) => Some(Box::new(v)),
@@ -822,7 +822,7 @@ impl<I: Tokens> Parser<I> {
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<Mark> {
- // 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<TestDescAndFn>) -> 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<TestDescAndFn>) -> 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<VarDeclarator>,
+ 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<Expr>),
+}
+
+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<Gathering>),
- is_delete: bool,
- no_document_all: bool,
- ) -> Expr {
+ fn construct(&mut self, data: (Expr, usize, Vec<Gathering>), 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<T>(&mut self, stmts: &mut Vec<T>)
@@ -371,13 +417,16 @@ impl OptChaining {
}
}
-fn init_and_eq_null_or_undefined(i: &Ident, init: Expr, no_document_all: bool) -> Box<Expr> {
- 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<Expr> {
+ 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<C: Comments> ClassProperties<C> {
.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::<SingleThreadedComments>(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<Ident> {
}
}
-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<PathBuf>,
+ config: Config,
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct Config {
+ pub base_dir: Option<PathBuf>,
+ pub resolve_fully: bool,
}
impl<R> NodeImportResolver<R>
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<PathBuf>) -> 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<R> ImportResolver for NodeImportResolver<R>
+impl<R> NodeImportResolver<R>
where
R: Resolve,
{
- fn resolve_import(&self, base: &FileName, module_specifier: &str) -> Result<JsWord, Error> {
- 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<JsWord, Error> {
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<R> ImportResolver for NodeImportResolver<R>
+where
+ R: Resolve,
+{
+ fn resolve_import(&self, base: &FileName, module_specifier: &str) -> Result<JsWord, Error> {
+ 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!("<umd-config-{}.js>", s)), s);
+ let fm = cm.new_source_file(
+ FileName::Internal(format!("<umd-config-{}.js>", 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<TsConfigResolver<NodeModulesResolver>>;
-fn paths_resolver(
- base_url: impl AsRef<Path>,
- rules: Vec<(String, Vec<String>)>,
-) -> JscPathsProvider {
- let base_url = base_url
- .as_ref()
+fn paths_resolver(base_dir: &Path, rules: Vec<(String, Vec<String>)>) -> 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<Expr> {
static CACHE: Lazy<DashMap<String, Arc<Expr>, ARandomState>> = Lazy::new(DashMap::default);
- let fm = cm.new_source_file(FileName::Custom(format!("<const-module-{}.js>", name)), src);
+ let fm = cm.new_source_file(
+ FileName::Internal(format!("<const-module-{}.js>", 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<PropOrSpread>) {
+ self.visit_mut_par(cpu_count() * 8, n);
+ }
+
+ fn visit_mut_expr_or_spreads(&mut self, n: &mut Vec<ExprOrSpread>) {
+ self.visit_mut_par(cpu_count() * 8, n);
+ }
+
+ fn visit_mut_opt_vec_expr_or_spreads(&mut self, n: &mut Vec<Option<ExprOrSpread>>) {
+ self.visit_mut_par(cpu_count() * 8, n);
+ }
+
+ fn visit_mut_exprs(&mut self, n: &mut Vec<Box<Expr>>) {
+ 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<Box<Expr>> {
- let fm = cm.new_source_file(FileName::Custom(format!("<jsx-config-{}.js>", name)), src);
+ let fm = cm.new_source_file(FileName::Internal(format!("<jsx-config-{}.js>", 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<ImportSpecifier>,
pub src: Box<Str>,
pub type_only: bool,
- pub asserts: Option<Box<ObjectLit>>,
+ pub with: Option<Box<ObjectLit>>,
}
pub struct ExportAll {
pub span: Span,
pub src: Box<Str>,
pub type_only: bool,
- pub asserts: Option<Box<ObjectLit>>,
+ pub with: Option<Box<ObjectLit>>,
}
pub struct NamedExport {
pub span: Span,
pub specifiers: Vec<ExportSpecifier>,
pub src: Option<Box<Str>>,
pub type_only: bool,
- pub asserts: Option<Box<ObjectLit>>,
+ pub with: Option<Box<ObjectLit>>,
}
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<Vec<ImportAttribute>>,
+ pub with: Option<Vec<ImportAttribute>>,
#[serde(default)]
pub export_kind: Option<ExportKind>,
}
@@ -192,7 +192,7 @@ pub struct ExportNamedDeclaration {
#[serde(default)]
pub source: Option<StringLiteral>,
#[serde(default)]
- pub assertions: Option<Vec<ImportAttribute>>,
+ pub with: Option<Vec<ImportAttribute>>,
#[serde(default)]
pub export_kind: Option<ExportKind>,
}
@@ -270,7 +270,7 @@ pub struct ImportDeclaration {
pub specifiers: Vec<ImportSpecifierType>,
pub source: StringLiteral,
#[serde(default)]
- pub assertions: Option<Vec<ImportAttribute>>,
+ pub with: Option<Vec<ImportAttribute>>,
#[serde(default)]
pub import_kind: Option<ImportKind>,
}
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<Box<ObjectLit>>,
ctx: &Context,
) -> Option<Vec<ImportAttribute>> {
@@ -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<dyn swc_ecma_codegen::text_writer::WriteJs>;
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<I>| {
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();
+ }
}
}
}