diff --git a/.github/actions/clone-submodules/action.yml b/.github/actions/clone-submodules/action.yml index 48d4656c8dc1da..32e7b8c1d4ba5b 100644 --- a/.github/actions/clone-submodules/action.yml +++ b/.github/actions/clone-submodules/action.yml @@ -10,7 +10,7 @@ runs: show-progress: false repository: tc39/test262 path: tasks/coverage/test262 - ref: dc0082c5ea347e5ecb585c1d7ebf4555aa429528 + ref: c4317b0cb578d3fe7940f65b27162638efb9b34d - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: diff --git a/Cargo.lock b/Cargo.lock index b0446737852f18..c67658cd8e130e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1384,7 +1384,7 @@ checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" [[package]] name = "oxc" -version = "0.42.0" +version = "0.43.0" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1446,7 +1446,7 @@ dependencies = [ [[package]] name = "oxc_allocator" -version = "0.42.0" +version = "0.43.0" dependencies = [ "allocator-api2", "bumpalo", @@ -1457,7 +1457,7 @@ dependencies = [ [[package]] name = "oxc_ast" -version = "0.42.0" +version = "0.43.0" dependencies = [ "bitflags 2.6.0", "cow-utils", @@ -1475,7 +1475,7 @@ dependencies = [ [[package]] name = "oxc_ast_macros" -version = "0.42.0" +version = "0.43.0" dependencies = [ "proc-macro2", "quote", @@ -1523,7 +1523,7 @@ dependencies = [ [[package]] name = "oxc_cfg" -version = "0.42.0" +version = "0.43.0" dependencies = [ "bitflags 2.6.0", "itertools", @@ -1536,7 +1536,7 @@ dependencies = [ [[package]] name = "oxc_codegen" -version = "0.42.0" +version = "0.43.0" dependencies = [ "assert-unchecked", "base64", @@ -1597,7 +1597,7 @@ dependencies = [ [[package]] name = "oxc_data_structures" -version = "0.42.0" +version = "0.43.0" dependencies = [ "assert-unchecked", "ropey", @@ -1605,7 +1605,7 @@ dependencies = [ [[package]] name = "oxc_diagnostics" -version = "0.42.0" +version = "0.43.0" dependencies = [ "oxc-miette", "rustc-hash", @@ -1613,7 +1613,7 @@ dependencies = [ [[package]] name = "oxc_ecmascript" -version = "0.42.0" +version = "0.43.0" dependencies = [ "num-bigint", "num-traits", @@ -1624,7 +1624,7 @@ dependencies = [ [[package]] name = "oxc_estree" -version = "0.42.0" +version = "0.43.0" dependencies = [ "serde", ] @@ -1641,7 +1641,7 @@ dependencies = [ [[package]] name = "oxc_isolated_declarations" -version = "0.42.0" +version = "0.43.0" dependencies = [ "bitflags 2.6.0", "insta", @@ -1740,7 +1740,7 @@ dependencies = [ [[package]] name = "oxc_mangler" -version = "0.42.0" +version = "0.43.0" dependencies = [ "itertools", "oxc_ast", @@ -1751,7 +1751,7 @@ dependencies = [ [[package]] name = "oxc_minifier" -version = "0.42.0" +version = "0.43.0" dependencies = [ "cow-utils", "insta", @@ -1799,7 +1799,7 @@ dependencies = [ [[package]] name = "oxc_napi" -version = "0.42.0" +version = "0.43.0" dependencies = [ "napi", "napi-derive", @@ -1808,7 +1808,7 @@ dependencies = [ [[package]] name = "oxc_parser" -version = "0.42.0" +version = "0.43.0" dependencies = [ "assert-unchecked", "bitflags 2.6.0", @@ -1889,7 +1889,7 @@ dependencies = [ [[package]] name = "oxc_regular_expression" -version = "0.42.0" +version = "0.43.0" dependencies = [ "oxc_allocator", "oxc_ast_macros", @@ -1923,7 +1923,7 @@ dependencies = [ [[package]] name = "oxc_semantic" -version = "0.42.0" +version = "0.43.0" dependencies = [ "assert-unchecked", "hashbrown 0.15.2", @@ -1964,7 +1964,7 @@ dependencies = [ [[package]] name = "oxc_span" -version = "0.42.0" +version = "0.43.0" dependencies = [ "compact_str", "oxc-miette", @@ -1977,7 +1977,7 @@ dependencies = [ [[package]] name = "oxc_syntax" -version = "0.42.0" +version = "0.43.0" dependencies = [ "assert-unchecked", "bitflags 2.6.0", @@ -2035,7 +2035,7 @@ dependencies = [ [[package]] name = "oxc_transform_napi" -version = "0.42.0" +version = "0.43.0" dependencies = [ "napi", "napi-build", @@ -2048,7 +2048,7 @@ dependencies = [ [[package]] name = "oxc_transformer" -version = "0.42.0" +version = "0.43.0" dependencies = [ "base64", "compact_str", @@ -2081,7 +2081,7 @@ dependencies = [ [[package]] name = "oxc_traverse" -version = "0.42.0" +version = "0.43.0" dependencies = [ "compact_str", "itoa", diff --git a/Cargo.toml b/Cargo.toml index d0af07feddeaa3..8c41dff0112930 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,28 +78,28 @@ doc_lazy_continuation = "allow" # FIXME [workspace.dependencies] # publish = true -oxc = { version = "0.42.0", path = "crates/oxc" } -oxc_allocator = { version = "0.42.0", path = "crates/oxc_allocator" } -oxc_ast = { version = "0.42.0", path = "crates/oxc_ast" } -oxc_ast_macros = { version = "0.42.0", path = "crates/oxc_ast_macros" } -oxc_cfg = { version = "0.42.0", path = "crates/oxc_cfg" } -oxc_codegen = { version = "0.42.0", path = "crates/oxc_codegen" } -oxc_data_structures = { version = "0.42.0", path = "crates/oxc_data_structures" } -oxc_diagnostics = { version = "0.42.0", path = "crates/oxc_diagnostics" } -oxc_ecmascript = { version = "0.42.0", path = "crates/oxc_ecmascript" } -oxc_estree = { version = "0.42.0", path = "crates/oxc_estree" } -oxc_isolated_declarations = { version = "0.42.0", path = "crates/oxc_isolated_declarations" } -oxc_mangler = { version = "0.42.0", path = "crates/oxc_mangler" } -oxc_minifier = { version = "0.42.0", path = "crates/oxc_minifier" } -oxc_napi = { version = "0.42.0", path = "crates/oxc_napi" } -oxc_parser = { version = "0.42.0", path = "crates/oxc_parser" } -oxc_regular_expression = { version = "0.42.0", path = "crates/oxc_regular_expression" } -oxc_semantic = { version = "0.42.0", path = "crates/oxc_semantic" } -oxc_span = { version = "0.42.0", path = "crates/oxc_span" } -oxc_syntax = { version = "0.42.0", path = "crates/oxc_syntax" } -oxc_transform_napi = { version = "0.42.0", path = "napi/transform" } -oxc_transformer = { version = "0.42.0", path = "crates/oxc_transformer" } -oxc_traverse = { version = "0.42.0", path = "crates/oxc_traverse" } +oxc = { version = "0.43.0", path = "crates/oxc" } +oxc_allocator = { version = "0.43.0", path = "crates/oxc_allocator" } +oxc_ast = { version = "0.43.0", path = "crates/oxc_ast" } +oxc_ast_macros = { version = "0.43.0", path = "crates/oxc_ast_macros" } +oxc_cfg = { version = "0.43.0", path = "crates/oxc_cfg" } +oxc_codegen = { version = "0.43.0", path = "crates/oxc_codegen" } +oxc_data_structures = { version = "0.43.0", path = "crates/oxc_data_structures" } +oxc_diagnostics = { version = "0.43.0", path = "crates/oxc_diagnostics" } +oxc_ecmascript = { version = "0.43.0", path = "crates/oxc_ecmascript" } +oxc_estree = { version = "0.43.0", path = "crates/oxc_estree" } +oxc_isolated_declarations = { version = "0.43.0", path = "crates/oxc_isolated_declarations" } +oxc_mangler = { version = "0.43.0", path = "crates/oxc_mangler" } +oxc_minifier = { version = "0.43.0", path = "crates/oxc_minifier" } +oxc_napi = { version = "0.43.0", path = "crates/oxc_napi" } +oxc_parser = { version = "0.43.0", path = "crates/oxc_parser" } +oxc_regular_expression = { version = "0.43.0", path = "crates/oxc_regular_expression" } +oxc_semantic = { version = "0.43.0", path = "crates/oxc_semantic" } +oxc_span = { version = "0.43.0", path = "crates/oxc_span" } +oxc_syntax = { version = "0.43.0", path = "crates/oxc_syntax" } +oxc_transform_napi = { version = "0.43.0", path = "napi/transform" } +oxc_transformer = { version = "0.43.0", path = "crates/oxc_transformer" } +oxc_traverse = { version = "0.43.0", path = "crates/oxc_traverse" } # publish = false oxc_linter = { path = "crates/oxc_linter" } diff --git a/crates/oxc/Cargo.toml b/crates/oxc/Cargo.toml index 0fdb9f195bc574..8b72f7782a1206 100644 --- a/crates/oxc/Cargo.toml +++ b/crates/oxc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_allocator/CHANGELOG.md b/crates/oxc_allocator/CHANGELOG.md index 2e22a97bdc1134..20ac24f2031914 100644 --- a/crates/oxc_allocator/CHANGELOG.md +++ b/crates/oxc_allocator/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.43.0] - 2024-12-21 + +### Features + +- 75b775c allocator: `Vec::into_string` (#8017) (overlookmotel) +- 8547e02 ast: Implement `allocator_api2` for `Allocator` (#8043) (Boshen) + +### Performance + +- 414e828 semantic: Allocate symbol data in Allocator (#8012) (Boshen) + ## [0.39.0] - 2024-12-04 ### Bug Fixes diff --git a/crates/oxc_allocator/Cargo.toml b/crates/oxc_allocator/Cargo.toml index 734ba63d3ba49b..7949cdcd32a3e2 100644 --- a/crates/oxc_allocator/Cargo.toml +++ b/crates/oxc_allocator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_allocator" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_ast/CHANGELOG.md b/crates/oxc_ast/CHANGELOG.md index c521fff529c9d0..3a82de3741aa81 100644 --- a/crates/oxc_ast/CHANGELOG.md +++ b/crates/oxc_ast/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.43.0] - 2024-12-21 + +### Features + +- 63a95e4 ast: Add `AstBulder::move_property_key` (#7998) (overlookmotel) + +### Performance + +- c0dd3f8 ast: `move_expression` and `move_statement` produce dummy with no span (#7995) (overlookmotel) + +### Documentation + +- df5c341 ast: Improve docs for `AstBuilder::move_*` methods (#7994) (overlookmotel) + ## [0.42.0] - 2024-12-18 ### Features diff --git a/crates/oxc_ast/Cargo.toml b/crates/oxc_ast/Cargo.toml index 6c3b41bd24a8ba..bef862fbe6fd73 100644 --- a/crates/oxc_ast/Cargo.toml +++ b/crates/oxc_ast/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_ast" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_ast_macros/Cargo.toml b/crates/oxc_ast_macros/Cargo.toml index d8054434d685c1..ec2058aa62b357 100644 --- a/crates/oxc_ast_macros/Cargo.toml +++ b/crates/oxc_ast_macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_ast_macros" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_cfg/Cargo.toml b/crates/oxc_cfg/Cargo.toml index 6901dedffce760..2f98dc83cfb528 100644 --- a/crates/oxc_cfg/Cargo.toml +++ b/crates/oxc_cfg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_cfg" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_codegen/CHANGELOG.md b/crates/oxc_codegen/CHANGELOG.md index ba44e446b4af1f..09385c7ead2098 100644 --- a/crates/oxc_codegen/CHANGELOG.md +++ b/crates/oxc_codegen/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.43.0] - 2024-12-21 + +### Performance + +- 862838f codegen: Remove useless to_owned (#8014) (Dunqing) + ## [0.42.0] - 2024-12-18 ### Bug Fixes diff --git a/crates/oxc_codegen/Cargo.toml b/crates/oxc_codegen/Cargo.toml index 2fe0b8457fd147..8f589f63126cfe 100644 --- a/crates/oxc_codegen/Cargo.toml +++ b/crates/oxc_codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_codegen" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_data_structures/Cargo.toml b/crates/oxc_data_structures/Cargo.toml index 660e6962a3e130..68cc03592a3f36 100644 --- a/crates/oxc_data_structures/Cargo.toml +++ b/crates/oxc_data_structures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_data_structures" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_diagnostics/Cargo.toml b/crates/oxc_diagnostics/Cargo.toml index 02d7ac904113fd..066668e3fa2afa 100644 --- a/crates/oxc_diagnostics/Cargo.toml +++ b/crates/oxc_diagnostics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_diagnostics" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_ecmascript/Cargo.toml b/crates/oxc_ecmascript/Cargo.toml index 25e687b7610738..9ad61b109d81ca 100644 --- a/crates/oxc_ecmascript/Cargo.toml +++ b/crates/oxc_ecmascript/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_ecmascript" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_estree/Cargo.toml b/crates/oxc_estree/Cargo.toml index 0c63a065bc270d..b374b90e9cefa8 100644 --- a/crates/oxc_estree/Cargo.toml +++ b/crates/oxc_estree/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_estree" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_isolated_declarations/Cargo.toml b/crates/oxc_isolated_declarations/Cargo.toml index 682c2d7bf8fe62..fdaa95e0e87963 100644 --- a/crates/oxc_isolated_declarations/Cargo.toml +++ b/crates/oxc_isolated_declarations/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_isolated_declarations" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_linter/src/config/flat.rs b/crates/oxc_linter/src/config/config_store.rs similarity index 65% rename from crates/oxc_linter/src/config/flat.rs rename to crates/oxc_linter/src/config/config_store.rs index bece45963f3485..1fa756bd668c0e 100644 --- a/crates/oxc_linter/src/config/flat.rs +++ b/crates/oxc_linter/src/config/config_store.rs @@ -1,21 +1,9 @@ -use std::{ - hash::{BuildHasher, Hash, Hasher}, - path::Path, - sync::Arc, -}; +use std::{path::Path, sync::Arc}; -use dashmap::DashMap; -use rustc_hash::{FxBuildHasher, FxHashSet}; +use rustc_hash::FxHashSet; -use crate::{rules::RULES, LintPlugins, RuleWithSeverity}; - -use super::{ - overrides::{OverrideId, OxlintOverrides}, - LintConfig, -}; - -type AppliedOverrideHash = u64; -type FxDashMap = DashMap; +use super::{overrides::OxlintOverrides, LintConfig, LintPlugins}; +use crate::{rules::RULES, RuleWithSeverity}; // TODO: support `categories` et. al. in overrides. #[derive(Debug)] @@ -31,24 +19,21 @@ impl Clone for ResolvedLinterState { } } -/// Keeps track of a list of config deltas, lazily applying them to a base config as requested by -/// [`ConfigStore::resolve`]. This struct is [`Sync`] + [`Send`] since the linter runs on each file -/// in parallel. #[derive(Debug)] -pub struct ConfigStore { - // TODO: flatten base config + overrides into a single "flat" config. Similar idea to ESLint's - // flat configs, but we would still support v8 configs. Doing this could open the door to - // supporting flat configs (e.g. eslint.config.js). Still need to figure out how this plays - // with nested configs. - /// Resolved override cache. The key is a hash of each override's ID that matched the list of - /// file globs in order to avoid re-allocating the same set of rules multiple times. - cache: FxDashMap, - /// "root" level configuration. In the future this may just be the first entry in `overrides`. +struct Config { + /// The basic linter state for this configuration. base: ResolvedLinterState, - /// Config deltas applied to `base`. + + /// An optional set of overrides to apply to the base state depending on the file being linted. overrides: OxlintOverrides, } +/// Resolves a lint configuration for a given file, by applying overrides based on the file's path. +#[derive(Debug)] +pub struct ConfigStore { + base: Config, +} + impl ConfigStore { pub fn new( base_rules: Vec, @@ -59,78 +44,54 @@ impl ConfigStore { rules: Arc::from(base_rules.into_boxed_slice()), config: Arc::new(base_config), }; - // best-best case: no overrides are provided & config is initialized with 0 capacity best - // case: each file matches only a single override, so we only need `overrides.len()` - // capacity worst case: files match more than one override. In the most ridiculous case, we - // could end up needing (overrides.len() ** 2) capacity. I don't really want to - // pre-allocate that much space unconditionally. Better to re-alloc if we end up needing - // it. - let cache = FxDashMap::with_capacity_and_hasher(overrides.len(), FxBuildHasher); - - Self { cache, base, overrides } + Self { base: Config { base, overrides } } } pub fn number_of_rules(&self) -> usize { - self.base.rules.len() + self.base.base.rules.len() } pub fn rules(&self) -> &Arc<[RuleWithSeverity]> { - &self.base.rules + &self.base.base.rules } pub(crate) fn resolve(&self, path: &Path) -> ResolvedLinterState { - if self.overrides.is_empty() { - return self.base.clone(); - } - - let mut overrides_to_apply: Vec = Vec::new(); - let mut hasher = FxBuildHasher.build_hasher(); - - // Compute the path of the file relative to the configuration file for glob matching. Globs should match - // relative to the location of the configuration file. - // - path: /some/path/like/this/to/file.js - // - config_path: /some/path/like/.oxlintrc.json - // => relative_path: this/to/file.js - // TODO: Handle nested configuration file paths. - let relative_path = if let Some(config_path) = &self.base.config.path { - if let Some(parent) = config_path.parent() { - path.strip_prefix(parent).unwrap_or(path) - } else { - path - } - } else { - path - }; + // TODO: based on the `path` provided, resolve the configuration file to use. + let resolved_config = &self.base; + Self::apply_overrides(resolved_config, path) + } - for (id, override_config) in self.overrides.iter_enumerated() { - if override_config.files.is_match(relative_path) { - overrides_to_apply.push(id); - id.hash(&mut hasher); - } + fn apply_overrides(config: &Config, path: &Path) -> ResolvedLinterState { + if config.overrides.is_empty() { + return config.base.clone(); } - if overrides_to_apply.is_empty() { - return self.base.clone(); - } + let relative_path = config + .base + .config + .path + .as_ref() + .and_then(|config_path| { + config_path.parent().map(|parent| path.strip_prefix(parent).unwrap_or(path)) + }) + .unwrap_or(path); - let key = hasher.finish(); - self.cache - .entry(key) - .or_insert_with(|| self.apply_overrides(&overrides_to_apply)) - .value() - .clone() - } + let overrides_to_apply = + config.overrides.iter().filter(|config| config.files.is_match(relative_path)); - /// NOTE: this function must not borrow any entries from `self.cache` or DashMap will deadlock. - fn apply_overrides(&self, override_ids: &[OverrideId]) -> ResolvedLinterState { - let mut plugins = self.base.config.plugins; + let mut overrides_to_apply = overrides_to_apply.peekable(); + + if overrides_to_apply.peek().is_none() { + return config.base.clone(); + } + let mut plugins = config.base.config.plugins; let all_rules = RULES .iter() .filter(|rule| plugins.contains(LintPlugins::from(rule.plugin_name()))) .cloned() .collect::>(); - let mut rules = self + let mut rules = config .base .rules .iter() @@ -138,23 +99,21 @@ impl ConfigStore { .cloned() .collect::>(); - let overrides = override_ids.iter().map(|id| &self.overrides[*id]); - for override_config in overrides { + for override_config in overrides_to_apply { if !override_config.rules.is_empty() { override_config.rules.override_rules(&mut rules, &all_rules); } - // Append the override's plugins to the base list of enabled plugins. if let Some(override_plugins) = override_config.plugins { plugins |= override_plugins; } } let rules = rules.into_iter().collect::>(); - let config = if plugins == self.base.config.plugins { - Arc::clone(&self.base.config) + let config = if plugins == config.base.config.plugins { + Arc::clone(&config.base.config) } else { - let mut config = (*self.base.config.as_ref()).clone(); + let mut config = (*config.base.config).clone(); config.plugins = plugins; Arc::new(config) @@ -309,7 +268,7 @@ mod test { }]); let store = ConfigStore::new(vec![], base_config, overrides); - assert_eq!(store.base.config.plugins, LintPlugins::IMPORT); + assert_eq!(store.base.base.config.plugins, LintPlugins::IMPORT); let app = store.resolve("other.mjs".as_ref()).config; assert_eq!(app.plugins, LintPlugins::IMPORT); diff --git a/crates/oxc_linter/src/config/mod.rs b/crates/oxc_linter/src/config/mod.rs index 03228e26b649ae..7ab4bf56bf6dce 100644 --- a/crates/oxc_linter/src/config/mod.rs +++ b/crates/oxc_linter/src/config/mod.rs @@ -1,17 +1,17 @@ use std::path::PathBuf; mod categories; +mod config_store; mod env; -mod flat; mod globals; mod overrides; mod oxlintrc; mod plugins; mod rules; mod settings; +pub use config_store::ConfigStore; +pub(crate) use config_store::ResolvedLinterState; pub use env::OxlintEnv; -pub use flat::ConfigStore; -pub(crate) use flat::ResolvedLinterState; pub use globals::OxlintGlobals; pub use overrides::OxlintOverrides; pub use oxlintrc::Oxlintrc; diff --git a/crates/oxc_linter/src/module_graph_visitor.rs b/crates/oxc_linter/src/module_graph_visitor.rs index 3d6bb7214e77ad..e6e2cee4304d8f 100644 --- a/crates/oxc_linter/src/module_graph_visitor.rs +++ b/crates/oxc_linter/src/module_graph_visitor.rs @@ -200,29 +200,29 @@ impl ModuleGraphVisitor { } }; } - for module_record_ref in &module_record.loaded_modules { + for pair in module_record.loaded_modules.read().unwrap().iter() { if self.depth > self.max_depth { return VisitFoldWhile::Stop(accumulator.into_inner()); } - let path = &module_record_ref.resolved_absolute_path; + let path = &pair.1.resolved_absolute_path; if !self.traversed.insert(path.clone()) { continue; } - if !filter(module_record_ref.pair(), module_record) { + if !filter(pair, module_record) { continue; } self.depth += 1; - event(ModuleGraphVisitorEvent::Enter, module_record_ref.pair(), module_record); - enter(module_record_ref.pair(), module_record); + event(ModuleGraphVisitorEvent::Enter, pair, module_record); + enter(pair, module_record); - accumulate!(fold(accumulator.into_inner(), module_record_ref.pair(), module_record)); + accumulate!(fold(accumulator.into_inner(), pair, module_record)); accumulate!(self.filter_fold_recursive( accumulator, - module_record_ref.value(), + pair.1, filter, fold, event, @@ -230,8 +230,8 @@ impl ModuleGraphVisitor { leave )); - event(ModuleGraphVisitorEvent::Leave, module_record_ref.pair(), module_record); - leave(module_record_ref.pair(), module_record); + event(ModuleGraphVisitorEvent::Leave, pair, module_record); + leave(pair, module_record); self.depth -= 1; } diff --git a/crates/oxc_linter/src/module_record.rs b/crates/oxc_linter/src/module_record.rs index 439b7c5524494d..996032d5fc104c 100644 --- a/crates/oxc_linter/src/module_record.rs +++ b/crates/oxc_linter/src/module_record.rs @@ -3,18 +3,15 @@ use std::{ fmt, path::{Path, PathBuf}, - sync::Arc, + sync::{Arc, OnceLock, RwLock}, }; -use dashmap::DashMap; -use rustc_hash::{FxBuildHasher, FxHashMap}; +use rustc_hash::FxHashMap; use oxc_semantic::Semantic; use oxc_span::{CompactStr, Span}; pub use oxc_syntax::module_record::RequestedModule; -type FxDashMap = DashMap; - /// ESM Module Record /// /// All data inside this data structure are for ESM, no commonjs data is allowed. @@ -49,7 +46,7 @@ pub struct ModuleRecord { /// /// Note that Oxc does not support cross-file analysis, so this map will be empty after /// [`ModuleRecord`] is created. You must link the module records yourself. - pub loaded_modules: FxDashMap>, + pub loaded_modules: RwLock>>, /// `[[ImportEntries]]` /// @@ -81,7 +78,7 @@ pub struct ModuleRecord { /// Reexported bindings from `export * from 'specifier'` /// Keyed by resolved path - pub exported_bindings_from_star_export: FxDashMap>, + exported_bindings_from_star_export: OnceLock>>, /// `export default name` /// ^^^^^^^ span @@ -93,8 +90,10 @@ impl fmt::Debug for ModuleRecord { // recursively formatting loaded modules can crash when the module graph is cyclic let loaded_modules = self .loaded_modules + .read() + .unwrap() .iter() - .map(|entry| (entry.key().to_string())) + .map(|(key, _)| (key.to_string())) .reduce(|acc, key| format!("{acc}, {key}")) .unwrap_or_default(); let loaded_modules = format!("{{ {loaded_modules} }}"); @@ -492,4 +491,37 @@ impl ModuleRecord { ..ModuleRecord::default() } } + + pub(crate) fn exported_bindings_from_star_export( + &self, + ) -> &FxHashMap> { + self.exported_bindings_from_star_export.get_or_init(|| { + let mut exported_bindings_from_star_export: FxHashMap> = + FxHashMap::default(); + let loaded_modules = self.loaded_modules.read().unwrap(); + for export_entry in &self.star_export_entries { + let Some(module_request) = &export_entry.module_request else { + continue; + }; + let Some(remote_module_record) = loaded_modules.get(module_request.name()) else { + continue; + }; + // Append both remote `bindings` and `exported_bindings_from_star_export` + let remote_exported_bindings_from_star_export = remote_module_record + .exported_bindings_from_star_export() + .iter() + .flat_map(|(_, value)| value.clone()); + let remote_bindings = remote_module_record + .exported_bindings + .keys() + .cloned() + .chain(remote_exported_bindings_from_star_export); + exported_bindings_from_star_export + .entry(remote_module_record.resolved_absolute_path.clone()) + .or_default() + .extend(remote_bindings); + } + exported_bindings_from_star_export + }) + } } diff --git a/crates/oxc_linter/src/rules/eslint/no_global_assign.rs b/crates/oxc_linter/src/rules/eslint/no_global_assign.rs index 9ade4716f3bf7a..d65b80fc332b1e 100644 --- a/crates/oxc_linter/src/rules/eslint/no_global_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_global_assign.rs @@ -63,7 +63,7 @@ impl Rule for NoGlobalAssign { for &reference_id in reference_id_list { let reference = symbol_table.get_reference(reference_id); if reference.is_write() - && !self.excludes.contains(name) + && !self.excludes.iter().any(|n| n == name) && ctx.env_contains_var(name) { ctx.diagnostic(no_global_assign_diagnostic( diff --git a/crates/oxc_linter/src/rules/import/default.rs b/crates/oxc_linter/src/rules/import/default.rs index 4aced42629320c..aca5812cc23ba1 100644 --- a/crates/oxc_linter/src/rules/import/default.rs +++ b/crates/oxc_linter/src/rules/import/default.rs @@ -53,19 +53,20 @@ declare_oxc_lint!( impl Rule for Default { fn run_once(&self, ctx: &LintContext<'_>) { let module_record = ctx.module_record(); + let loaded_modules = module_record.loaded_modules.read().unwrap(); for import_entry in &module_record.import_entries { let ImportImportName::Default(default_span) = import_entry.import_name else { continue; }; let specifier = import_entry.module_request.name(); - let Some(remote_module_record_ref) = module_record.loaded_modules.get(specifier) else { + let Some(remote_module_record) = loaded_modules.get(specifier) else { continue; }; - if !remote_module_record_ref.has_module_syntax { + if !remote_module_record.has_module_syntax { continue; } - if !remote_module_record_ref + if !remote_module_record .resolved_absolute_path .extension() .and_then(|ext| ext.to_str()) @@ -73,8 +74,8 @@ impl Rule for Default { { continue; } - if remote_module_record_ref.export_default.is_none() - && !remote_module_record_ref.exported_bindings.contains_key("default") + if remote_module_record.export_default.is_none() + && !remote_module_record.exported_bindings.contains_key("default") { ctx.diagnostic(default_diagnostic(specifier, default_span)); } diff --git a/crates/oxc_linter/src/rules/import/export.rs b/crates/oxc_linter/src/rules/import/export.rs index 96f87fb7d31ec0..908ef15956408d 100644 --- a/crates/oxc_linter/src/rules/import/export.rs +++ b/crates/oxc_linter/src/rules/import/export.rs @@ -55,6 +55,7 @@ impl Rule for Export { let mut all_export_names = FxHashMap::default(); let mut visited = FxHashSet::default(); + let loaded_modules = module_record.loaded_modules.read().unwrap(); module_record.star_export_entries.iter().for_each(|star_export_entry| { if star_export_entry.is_type { return; @@ -64,17 +65,11 @@ impl Rule for Export { let Some(module_request) = &star_export_entry.module_request else { return; }; - let Some(remote_module_record_ref) = - module_record.loaded_modules.get(module_request.name()) - else { + let Some(remote_module_record) = loaded_modules.get(module_request.name()) else { return; }; - walk_exported_recursive( - remote_module_record_ref.value(), - &mut export_names, - &mut visited, - ); + walk_exported_recursive(remote_module_record, &mut export_names, &mut visited); if export_names.is_empty() { ctx.diagnostic(no_named_export(module_request.name(), module_request.span())); @@ -126,16 +121,15 @@ fn walk_exported_recursive( for name in module_record.exported_bindings.keys() { result.insert(name.clone()); } + let loaded_modules = module_record.loaded_modules.read().unwrap(); for export_entry in &module_record.star_export_entries { let Some(module_request) = &export_entry.module_request else { continue; }; - let Some(remote_module_record_ref) = - module_record.loaded_modules.get(module_request.name()) - else { + let Some(remote_module_record) = loaded_modules.get(module_request.name()) else { continue; }; - walk_exported_recursive(remote_module_record_ref.value(), result, visited); + walk_exported_recursive(remote_module_record, result, visited); } } diff --git a/crates/oxc_linter/src/rules/import/named.rs b/crates/oxc_linter/src/rules/import/named.rs index 829d039e68694b..72033bf0680385 100644 --- a/crates/oxc_linter/src/rules/import/named.rs +++ b/crates/oxc_linter/src/rules/import/named.rs @@ -91,6 +91,7 @@ impl Rule for Named { let module_record = ctx.module_record(); + let loaded_modules = module_record.loaded_modules.read().unwrap(); for import_entry in &module_record.import_entries { // Get named import let ImportImportName::Name(import_name) = &import_entry.import_name else { @@ -98,10 +99,9 @@ impl Rule for Named { }; let specifier = import_entry.module_request.name(); // Get remote module record - let Some(remote_module_record_ref) = module_record.loaded_modules.get(specifier) else { + let Some(remote_module_record) = loaded_modules.get(specifier) else { continue; }; - let remote_module_record = remote_module_record_ref.value(); if !remote_module_record.has_module_syntax { continue; } @@ -117,9 +117,9 @@ impl Rule for Named { } // check re-export if remote_module_record - .exported_bindings_from_star_export + .exported_bindings_from_star_export() .iter() - .any(|entry| entry.value().contains(&import_name.name)) + .any(|(_, value)| value.contains(&import_name.name)) { continue; } @@ -127,6 +127,7 @@ impl Rule for Named { ctx.diagnostic(named_diagnostic(name, specifier, import_span)); } + let loaded_modules = module_record.loaded_modules.read().unwrap(); for export_entry in &module_record.indirect_export_entries { let Some(module_request) = &export_entry.module_request else { continue; @@ -136,10 +137,9 @@ impl Rule for Named { }; let specifier = module_request.name(); // Get remote module record - let Some(remote_module_record_ref) = module_record.loaded_modules.get(specifier) else { + let Some(remote_module_record) = loaded_modules.get(specifier) else { continue; }; - let remote_module_record = remote_module_record_ref.value(); if !remote_module_record.has_module_syntax { continue; } diff --git a/crates/oxc_linter/src/rules/import/namespace.rs b/crates/oxc_linter/src/rules/import/namespace.rs index 6cc933984d640a..5e84692fd8fde0 100644 --- a/crates/oxc_linter/src/rules/import/namespace.rs +++ b/crates/oxc_linter/src/rules/import/namespace.rs @@ -123,32 +123,33 @@ impl Rule for Namespace { return; } + let loaded_modules = module_record.loaded_modules.read().unwrap(); + for entry in &module_record.import_entries { let (source, module) = match &entry.import_name { ImportImportName::NamespaceObject => { let source = entry.module_request.name(); - if let Some(module) = module_record.loaded_modules.get(source) { - (source.to_string(), Arc::clone(module.value())) + if let Some(module) = loaded_modules.get(source) { + (source.to_string(), Arc::clone(module)) } else { return; } } ImportImportName::Name(name) => { - let Some(loaded_module) = - module_record.loaded_modules.get(entry.module_request.name()) + let Some(loaded_module) = loaded_modules.get(entry.module_request.name()) else { return; }; - let Some(source) = get_module_request_name(name.name(), &loaded_module) else { + let Some(source) = get_module_request_name(name.name(), loaded_module) else { return; }; - let Some(loaded_module) = &loaded_module.loaded_modules.get(source.as_str()) - else { + let loaded_module = loaded_module.loaded_modules.read().unwrap(); + let Some(loaded_module) = loaded_module.get(source.as_str()) else { return; }; - (source, Arc::clone(loaded_module.value())) + (source, Arc::clone(loaded_module)) } ImportImportName::Default(_) => { // TODO: Hard to confirm if it's a namespace object @@ -275,10 +276,11 @@ fn check_deep_namespace_for_node( if let Some(module_source) = get_module_request_name(name, module) { let parent_node = ctx.nodes().parent_node(node.id())?; - let module_record = module.loaded_modules.get(module_source.as_str())?; + let loaded_modules = module.loaded_modules.read().unwrap(); + let module_record = loaded_modules.get(module_source.as_str())?; let mut namespaces = namespaces.to_owned(); namespaces.push(name.into()); - check_deep_namespace_for_node(parent_node, source, &namespaces, module_record.value(), ctx); + check_deep_namespace_for_node(parent_node, source, &namespaces, module_record, ctx); } else { check_binding_exported( name, @@ -313,11 +315,13 @@ fn check_deep_namespace_for_object_pattern( if let Some(module_source) = get_module_request_name(&name, module) { let mut next_namespaces = namespaces.to_owned(); next_namespaces.push(name.to_string()); + + let loaded_modules = module.loaded_modules.read().unwrap(); check_deep_namespace_for_object_pattern( pattern, source, next_namespaces.as_slice(), - module.loaded_modules.get(module_source.as_str()).unwrap().value(), + loaded_modules.get(module_source.as_str()).unwrap(), ctx, ); continue; @@ -352,9 +356,9 @@ fn check_binding_exported( if module.exported_bindings.contains_key(name) || (name == "default" && module.export_default.is_some()) || module - .exported_bindings_from_star_export + .exported_bindings_from_star_export() .iter() - .any(|entry| entry.value().iter().any(|s| s.as_str() == name)) + .any(|(_, value)| value.iter().any(|s| s.as_str() == name)) { return; } diff --git a/crates/oxc_linter/src/rules/import/no_duplicates.rs b/crates/oxc_linter/src/rules/import/no_duplicates.rs index b538290a92e317..f083f5abf41452 100644 --- a/crates/oxc_linter/src/rules/import/no_duplicates.rs +++ b/crates/oxc_linter/src/rules/import/no_duplicates.rs @@ -91,11 +91,12 @@ impl Rule for NoDuplicates { fn run_once(&self, ctx: &LintContext<'_>) { let module_record = ctx.module_record(); + let loaded_modules = module_record.loaded_modules.read().unwrap(); let groups = module_record .requested_modules .iter() .map(|(source, requested_modules)| { - let resolved_absolute_path = module_record.loaded_modules.get(source).map_or_else( + let resolved_absolute_path = loaded_modules.get(source).map_or_else( || source.to_string(), |module| module.resolved_absolute_path.to_string_lossy().to_string(), ); diff --git a/crates/oxc_linter/src/rules/import/no_named_as_default.rs b/crates/oxc_linter/src/rules/import/no_named_as_default.rs index 4206fa1ef0ec3e..9300b19a609af4 100644 --- a/crates/oxc_linter/src/rules/import/no_named_as_default.rs +++ b/crates/oxc_linter/src/rules/import/no_named_as_default.rs @@ -66,12 +66,13 @@ impl Rule for NoNamedAsDefault { }; let specifier = import_entry.module_request.name(); - let Some(remote_module_record_ref) = module_record.loaded_modules.get(specifier) else { + let remote_module_record = module_record.loaded_modules.read().unwrap(); + let Some(remote_module_record) = remote_module_record.get(specifier) else { continue; }; let import_name = import_entry.local_name.name(); - if remote_module_record_ref.exported_bindings.contains_key(import_name) { + if remote_module_record.exported_bindings.contains_key(import_name) { ctx.diagnostic(no_named_as_default_diagnostic( *import_span, import_name, diff --git a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs index 6dcd58add23218..4c680996729977 100644 --- a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs +++ b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use oxc_ast::{ ast::{BindingPatternKind, Expression, IdentifierReference, MemberExpression}, AstKind, @@ -82,11 +84,12 @@ impl Rule for NoNamedAsDefaultMember { }; let specifier = import_entry.module_request.name(); - let Some(remote_module_record_ref) = module_record.loaded_modules.get(specifier) else { + let remote_module_record = module_record.loaded_modules.read().unwrap(); + let Some(remote_module_record) = remote_module_record.get(specifier) else { continue; }; - if remote_module_record_ref.exported_bindings.is_empty() { + if remote_module_record.exported_bindings.is_empty() { continue; } @@ -95,8 +98,10 @@ impl Rule for NoNamedAsDefaultMember { return; }; - has_members_map - .insert(symbol_id, (remote_module_record_ref, import_entry.module_request.clone())); + has_members_map.insert( + symbol_id, + (Arc::clone(remote_module_record), import_entry.module_request.clone()), + ); } if has_members_map.is_empty() { diff --git a/crates/oxc_linter/src/rules/import/no_self_import.rs b/crates/oxc_linter/src/rules/import/no_self_import.rs index 576381cb0a804b..c991f9985154cd 100644 --- a/crates/oxc_linter/src/rules/import/no_self_import.rs +++ b/crates/oxc_linter/src/rules/import/no_self_import.rs @@ -45,10 +45,11 @@ impl Rule for NoSelfImport { let module_record = ctx.module_record(); let resolved_absolute_path = &module_record.resolved_absolute_path; for (request, requested_modules) in &module_record.requested_modules { - let Some(remote_module_record_ref) = module_record.loaded_modules.get(request) else { + let remote_module_record = module_record.loaded_modules.read().unwrap(); + let Some(remote_module_record) = remote_module_record.get(request) else { continue; }; - if remote_module_record_ref.value().resolved_absolute_path == *resolved_absolute_path { + if remote_module_record.resolved_absolute_path == *resolved_absolute_path { for requested_module in requested_modules { ctx.diagnostic(no_self_import_diagnostic(requested_module.span)); } diff --git a/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs b/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs index 7085d04c7ac813..8eda042bd714cc 100644 --- a/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs +++ b/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs @@ -49,7 +49,7 @@ impl Rule for NoJasmineGlobals { .scopes() .root_unresolved_references() .iter() - .filter(|(key, _)| NON_JASMINE_PROPERTY_NAMES.contains(&key.as_str())); + .filter(|(key, _)| NON_JASMINE_PROPERTY_NAMES.contains(key)); for (name, reference_ids) in jasmine_references { for &reference_id in reference_ids { diff --git a/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs b/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs index f6cea20a837a5d..aa3c71014ebdb1 100644 --- a/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs +++ b/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs @@ -95,8 +95,10 @@ impl Rule for NoBarrelFile { let mut total: usize = 0; for module_request in module_requests { - if let Some(remote_module) = module_record.loaded_modules.get(module_request.name()) { - if let Some(count) = count_loaded_modules(remote_module.value()) { + if let Some(remote_module) = + module_record.loaded_modules.read().unwrap().get(module_request.name()) + { + if let Some(count) = count_loaded_modules(remote_module) { total += count; labels.push(module_request.span().label(format!("{count} modules"))); } @@ -111,7 +113,7 @@ impl Rule for NoBarrelFile { } fn count_loaded_modules(module_record: &ModuleRecord) -> Option { - if module_record.loaded_modules.is_empty() { + if module_record.loaded_modules.read().unwrap().is_empty() { return None; } Some( diff --git a/crates/oxc_linter/src/service/runtime.rs b/crates/oxc_linter/src/service/runtime.rs index 8014df9f582fcf..62b4060c4f8961 100644 --- a/crates/oxc_linter/src/service/runtime.rs +++ b/crates/oxc_linter/src/service/runtime.rs @@ -255,6 +255,8 @@ impl Runtime { if let ModuleState::Resolved(target_module_record) = target_ref.value() { module_record .loaded_modules + .write() + .unwrap() .insert(specifier.clone(), Arc::clone(target_module_record)); } }; @@ -262,35 +264,6 @@ impl Runtime { // The thread is blocked here until all dependent modules are resolved. - // Resolve and append `star_export_bindings` - for export_entry in &module_record.star_export_entries { - let Some(remote_module_record_ref) = - export_entry.module_request.as_ref().and_then(|module_request| { - module_record.loaded_modules.get(module_request.name()) - }) - else { - continue; - }; - let remote_module_record = remote_module_record_ref.value(); - // Append both remote `bindings` and `exported_bindings_from_star_export` - let remote_exported_bindings_from_star_export = remote_module_record - .exported_bindings_from_star_export - .iter() - .flat_map(|r| r.value().clone()); - let remote_bindings = remote_module_record - .exported_bindings - .keys() - .cloned() - .chain(remote_exported_bindings_from_star_export) - .collect::>(); - module_record - .exported_bindings_from_star_export - .entry(remote_module_record.resolved_absolute_path.clone()) - .or_default() - .value_mut() - .extend(remote_bindings); - } - // Stop if the current module is not marked for lint. if !self.paths.contains(path) { return vec![]; diff --git a/crates/oxc_linter/src/utils/jest.rs b/crates/oxc_linter/src/utils/jest.rs index 22b1dc363b48b3..9ae9b6a6b1e374 100644 --- a/crates/oxc_linter/src/utils/jest.rs +++ b/crates/oxc_linter/src/utils/jest.rs @@ -250,7 +250,7 @@ fn collect_ids_referenced_to_global<'c>( .scopes() .root_unresolved_references() .iter() - .filter(|(name, _)| JEST_METHOD_NAMES.contains(name.as_str())) + .filter(|(name, _)| JEST_METHOD_NAMES.contains(name)) .flat_map(|(_, reference_ids)| reference_ids.iter().copied()) } diff --git a/crates/oxc_mangler/CHANGELOG.md b/crates/oxc_mangler/CHANGELOG.md index fd6bea9528a95b..8a100a56d7461d 100644 --- a/crates/oxc_mangler/CHANGELOG.md +++ b/crates/oxc_mangler/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.43.0] - 2024-12-21 + +### Performance + +- 414e828 semantic: Allocate symbol data in Allocator (#8012) (Boshen) + +### Refactor + +- 02f968d semantic: Change `Bindings` to a plain `FxHashMap` (#8019) (Boshen) + ## [0.42.0] - 2024-12-18 ### Features diff --git a/crates/oxc_mangler/Cargo.toml b/crates/oxc_mangler/Cargo.toml index 11ec430babde1f..6119fd9aa6bcd3 100644 --- a/crates/oxc_mangler/Cargo.toml +++ b/crates/oxc_mangler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_mangler" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_minifier/Cargo.toml b/crates/oxc_minifier/Cargo.toml index b0bb8d9e29c77d..f5a3ef76a2c1a4 100644 --- a/crates/oxc_minifier/Cargo.toml +++ b/crates/oxc_minifier/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_minifier" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs b/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs index 5ec9c3e9f66bc1..da3431eb95ab65 100644 --- a/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs +++ b/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs @@ -20,7 +20,7 @@ impl<'a> CompressorPass<'a> for CollapseVariableDeclarations { } impl<'a> Traverse<'a> for CollapseVariableDeclarations { - fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { + fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { self.join_vars(stmts, ctx); } } diff --git a/crates/oxc_minifier/src/ast_passes/mod.rs b/crates/oxc_minifier/src/ast_passes/mod.rs index 4d31e84337aa5d..9d61688fb0f206 100644 --- a/crates/oxc_minifier/src/ast_passes/mod.rs +++ b/crates/oxc_minifier/src/ast_passes/mod.rs @@ -22,6 +22,8 @@ pub use peephole_substitute_alternate_syntax::PeepholeSubstituteAlternateSyntax; pub use remove_syntax::RemoveSyntax; pub use statement_fusion::StatementFusion; +use crate::CompressOptions; + pub trait CompressorPass<'a>: Traverse<'a> { fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>); } @@ -56,8 +58,8 @@ impl<'a> CompressorPass<'a> for CollapsePass { } impl<'a> Traverse<'a> for CollapsePass { - fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { - self.x1_collapse_variable_declarations.enter_statements(stmts, ctx); + fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { + self.x1_collapse_variable_declarations.exit_statements(stmts, ctx); } } @@ -132,10 +134,6 @@ impl<'a> CompressorPass<'a> for LatePeepholeOptimizations { } impl<'a> Traverse<'a> for LatePeepholeOptimizations { - fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { - self.x1_peephole_remove_dead_code.enter_statement(stmt, ctx); - } - fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { self.x1_peephole_remove_dead_code.exit_statement(stmt, ctx); self.x2_peephole_minimize_conditions.exit_statement(stmt, ctx); @@ -162,23 +160,19 @@ impl<'a> Traverse<'a> for LatePeepholeOptimizations { self.x3_peephole_substitute_alternate_syntax.exit_return_statement(stmt, ctx); } - fn enter_variable_declaration( + fn exit_variable_declaration( &mut self, decl: &mut VariableDeclaration<'a>, ctx: &mut TraverseCtx<'a>, ) { - self.x3_peephole_substitute_alternate_syntax.enter_variable_declaration(decl, ctx); - } - - fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - self.x3_peephole_substitute_alternate_syntax.enter_expression(expr, ctx); - self.x4_peephole_replace_known_methods.enter_expression(expr, ctx); + self.x3_peephole_substitute_alternate_syntax.exit_variable_declaration(decl, ctx); } fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { self.x1_peephole_remove_dead_code.exit_expression(expr, ctx); self.x2_peephole_minimize_conditions.exit_expression(expr, ctx); self.x3_peephole_substitute_alternate_syntax.exit_expression(expr, ctx); + self.x4_peephole_replace_known_methods.exit_expression(expr, ctx); self.x5_peephole_fold_constants.exit_expression(expr, ctx); } @@ -189,14 +183,6 @@ impl<'a> Traverse<'a> for LatePeepholeOptimizations { fn exit_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { self.x3_peephole_substitute_alternate_syntax.exit_call_expression(expr, ctx); } - - fn enter_binary_expression( - &mut self, - expr: &mut BinaryExpression<'a>, - ctx: &mut TraverseCtx<'a>, - ) { - self.x3_peephole_substitute_alternate_syntax.enter_binary_expression(expr, ctx); - } } // See `createPeepholeOptimizationsPass` @@ -230,10 +216,6 @@ impl<'a> CompressorPass<'a> for PeepholeOptimizations { } impl<'a> Traverse<'a> for PeepholeOptimizations { - fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { - self.x5_peephole_remove_dead_code.enter_statement(stmt, ctx); - } - fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { self.x2_peephole_minimize_conditions.exit_statement(stmt, ctx); self.x5_peephole_remove_dead_code.exit_statement(stmt, ctx); @@ -251,22 +233,18 @@ impl<'a> Traverse<'a> for PeepholeOptimizations { self.x3_peephole_substitute_alternate_syntax.exit_return_statement(stmt, ctx); } - fn enter_variable_declaration( + fn exit_variable_declaration( &mut self, decl: &mut VariableDeclaration<'a>, ctx: &mut TraverseCtx<'a>, ) { - self.x3_peephole_substitute_alternate_syntax.enter_variable_declaration(decl, ctx); - } - - fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - self.x3_peephole_substitute_alternate_syntax.enter_expression(expr, ctx); - self.x4_peephole_replace_known_methods.enter_expression(expr, ctx); + self.x3_peephole_substitute_alternate_syntax.exit_variable_declaration(decl, ctx); } fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { self.x2_peephole_minimize_conditions.exit_expression(expr, ctx); self.x3_peephole_substitute_alternate_syntax.exit_expression(expr, ctx); + self.x4_peephole_replace_known_methods.exit_expression(expr, ctx); self.x5_peephole_remove_dead_code.exit_expression(expr, ctx); self.x6_peephole_fold_constants.exit_expression(expr, ctx); } @@ -278,12 +256,46 @@ impl<'a> Traverse<'a> for PeepholeOptimizations { fn exit_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { self.x3_peephole_substitute_alternate_syntax.exit_call_expression(expr, ctx); } +} - fn enter_binary_expression( - &mut self, - expr: &mut BinaryExpression<'a>, - ctx: &mut TraverseCtx<'a>, - ) { - self.x3_peephole_substitute_alternate_syntax.enter_binary_expression(expr, ctx); +pub struct DeadCodeElimination { + x0_remove_syntax: RemoveSyntax, + x1_peephole_fold_constants: PeepholeFoldConstants, + x2_peephole_remove_dead_code: PeepholeRemoveDeadCode, +} + +impl DeadCodeElimination { + pub fn new() -> Self { + Self { + x0_remove_syntax: RemoveSyntax::new(CompressOptions::all_false()), + x1_peephole_fold_constants: PeepholeFoldConstants::new(), + x2_peephole_remove_dead_code: PeepholeRemoveDeadCode::new(), + } + } +} + +impl<'a> CompressorPass<'a> for DeadCodeElimination { + fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { + traverse_mut_with_ctx(self, program, ctx); + } +} + +impl<'a> Traverse<'a> for DeadCodeElimination { + fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + self.x2_peephole_remove_dead_code.exit_statement(stmt, ctx); + } + + fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { + self.x2_peephole_remove_dead_code.exit_program(program, ctx); + } + + fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { + self.x2_peephole_remove_dead_code.exit_statements(stmts, ctx); + } + + fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + self.x0_remove_syntax.exit_expression(expr, ctx); + self.x1_peephole_fold_constants.exit_expression(expr, ctx); + self.x2_peephole_remove_dead_code.exit_expression(expr, ctx); } } diff --git a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs index 55a1dd10f45958..b80a526a509dea 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs @@ -24,7 +24,8 @@ impl<'a> CompressorPass<'a> for PeepholeRemoveDeadCode { } impl<'a> Traverse<'a> for PeepholeRemoveDeadCode { - fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + self.compress_block(stmt, Ctx(ctx)); let ctx = Ctx(ctx); if let Some(new_stmt) = match stmt { Statement::IfStatement(if_stmt) => self.try_fold_if(if_stmt, ctx), @@ -40,10 +41,6 @@ impl<'a> Traverse<'a> for PeepholeRemoveDeadCode { } } - fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { - self.compress_block(stmt, Ctx(ctx)); - } - fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { if stmts.iter().any(|stmt| matches!(stmt, Statement::EmptyStatement(_))) { stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_))); @@ -159,22 +156,26 @@ impl<'a, 'b> PeepholeRemoveDeadCode { ctx: Ctx<'a, 'b>, ) -> Option> { // Descend and remove `else` blocks first. - if let Some(Statement::IfStatement(alternate)) = &mut if_stmt.alternate { - if let Some(new_stmt) = self.try_fold_if(alternate, ctx) { - if matches!(new_stmt, Statement::EmptyStatement(_)) { - if_stmt.alternate = None; + match &mut if_stmt.alternate { + Some(Statement::IfStatement(alternate)) => { + if let Some(new_stmt) = self.try_fold_if(alternate, ctx) { + if matches!(new_stmt, Statement::EmptyStatement(_)) { + if_stmt.alternate = None; + } else { + if_stmt.alternate = Some(new_stmt); + } self.changed = true; - } else { - if_stmt.alternate = Some(new_stmt); } } + Some(Statement::EmptyStatement(_)) => { + if_stmt.alternate = None; + self.changed = true; + } + _ => {} } match ctx.get_boolean_value(&if_stmt.test) { - Some(true) => { - // self.changed = true; - Some(ctx.ast.move_statement(&mut if_stmt.consequent)) - } + Some(true) => Some(ctx.ast.move_statement(&mut if_stmt.consequent)), Some(false) => { Some(if let Some(alternate) = &mut if_stmt.alternate { ctx.ast.move_statement(alternate) @@ -186,7 +187,6 @@ impl<'a, 'b> PeepholeRemoveDeadCode { .get_variable_declaration_statement() .unwrap_or_else(|| ctx.ast.statement_empty(SPAN)) }) - // self.changed = true; } None => None, } diff --git a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs index 8da3dfe56a8ab7..df7fc773325f64 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs @@ -22,7 +22,7 @@ impl<'a> CompressorPass<'a> for PeepholeReplaceKnownMethods { } impl<'a> Traverse<'a> for PeepholeReplaceKnownMethods { - fn enter_expression(&mut self, node: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + fn exit_expression(&mut self, node: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { self.try_fold_known_string_methods(node, ctx); } } diff --git a/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs b/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs index 5e44eb731a6a64..4887eafcaed91a 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs @@ -44,7 +44,7 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { self.compress_return_statement(stmt); } - fn enter_variable_declaration( + fn exit_variable_declaration( &mut self, decl: &mut VariableDeclaration<'a>, ctx: &mut TraverseCtx<'a>, @@ -77,18 +77,25 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { self.in_define_export = false; } - fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { let ctx = Ctx(ctx); - if let Expression::AssignmentExpression(assignment_expr) = expr { - if let Some(new_expr) = Self::try_compress_assignment_expression(assignment_expr, ctx) { - *expr = new_expr; - self.changed = true; + match expr { + Expression::AssignmentExpression(assignment_expr) => { + if let Some(new_expr) = + Self::try_compress_assignment_expression(assignment_expr, ctx) + { + *expr = new_expr; + self.changed = true; + } + } + Expression::LogicalExpression(logical_expr) => { + if let Some(new_expr) = Self::try_compress_is_null_or_undefined(logical_expr, ctx) { + *expr = new_expr; + self.changed = true; + } } + _ => {} } - } - - fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - let ctx = Ctx(ctx); self.try_compress_boolean(expr, ctx); self.try_compress_undefined(expr, ctx); match expr { @@ -138,17 +145,12 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { } } } + Expression::BinaryExpression(expr) => { + self.compress_typeof_undefined(expr, ctx); + } _ => {} } } - - fn enter_binary_expression( - &mut self, - expr: &mut BinaryExpression<'a>, - ctx: &mut TraverseCtx<'a>, - ) { - self.compress_typeof_undefined(expr, Ctx(ctx)); - } } impl<'a, 'b> PeepholeSubstituteAlternateSyntax { @@ -274,6 +276,123 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax { self.changed = true; } + /// Compress `foo === null || foo === undefined` into `foo == null`. + /// + /// `foo === null || foo === undefined` => `foo == null` + /// `foo !== null && foo !== undefined` => `foo != null` + /// + /// This compression assumes that `document.all` is a normal object. + /// If that assumption does not hold, this compression is not allowed. + /// - `document.all === null || document.all === undefined` is `false` + /// - `document.all == null` is `true` + fn try_compress_is_null_or_undefined( + expr: &mut LogicalExpression<'a>, + ctx: Ctx<'a, 'b>, + ) -> Option> { + let op = expr.operator; + if !matches!(op, LogicalOperator::Or | LogicalOperator::And) { + return None; + } + #[allow(clippy::match_wildcard_for_single_variants)] + let target_ops = match op { + LogicalOperator::Or => (BinaryOperator::StrictEquality, BinaryOperator::Equality), + LogicalOperator::And => (BinaryOperator::StrictInequality, BinaryOperator::Inequality), + _ => unreachable!(), + }; + + if let Some(new_expr) = Self::try_compress_is_null_or_undefined_for_left_and_right( + &expr.left, + &expr.right, + expr.span, + target_ops, + ctx, + ) { + return Some(new_expr); + } + + let Expression::LogicalExpression(left) = &mut expr.left else { + return None; + }; + if left.operator != op { + return None; + } + + Self::try_compress_is_null_or_undefined_for_left_and_right( + &left.right, + &expr.right, + Span::new(left.right.span().start, expr.span.end), + target_ops, + ctx, + ) + .map(|new_expr| { + ctx.ast.expression_logical( + expr.span, + ctx.ast.move_expression(&mut left.left), + expr.operator, + new_expr, + ) + }) + } + + fn try_compress_is_null_or_undefined_for_left_and_right( + left: &Expression<'a>, + right: &Expression<'a>, + span: Span, + (find_op, replace_op): (BinaryOperator, BinaryOperator), + ctx: Ctx<'a, 'b>, + ) -> Option> { + let pair = Self::commutative_pair( + (&left, &right), + |a| { + if let Expression::BinaryExpression(op) = a { + if op.operator == find_op { + return Self::commutative_pair( + (&op.left, &op.right), + |a_a| a_a.is_null().then_some(a_a.span()), + |a_b| { + if let Expression::Identifier(id) = a_b { + Some((a_b.span(), (*id).clone())) + } else { + None + } + }, + ); + } + } + None + }, + |b| { + if let Expression::BinaryExpression(op) = b { + if op.operator == find_op { + return Self::commutative_pair( + (&op.left, &op.right), + |b_a| b_a.evaluate_to_undefined().then_some(()), + |b_b| { + if let Expression::Identifier(id) = b_b { + Some((*id).clone()) + } else { + None + } + }, + ) + .map(|v| v.1); + } + } + None + }, + ); + let ((null_expr_span, (left_id_expr_span, left_id_ref)), right_id_ref) = pair?; + if left_id_ref.name != right_id_ref.name { + return None; + } + + let left_id_expr = + ctx.ast.expression_identifier_reference(left_id_expr_span, left_id_ref.name); + let null_expr = ctx.ast.expression_null_literal(null_expr_span); + + Some(ctx.ast.expression_binary(span, left_id_expr, replace_op, null_expr)) + } + fn commutative_pair( pair: (&A, &A), check_a: F, @@ -978,4 +1097,24 @@ mod test { test("const foo = () => { return 'baz' }", "const foo = () => 'baz'"); test_same("const foo = () => { foo; return 'baz' }"); } + + #[test] + fn test_fold_is_null_or_undefined() { + test("foo === null || foo === undefined", "foo == null"); + test("foo === undefined || foo === null", "foo == null"); + test("foo === null || foo === void 0", "foo == null"); + test("foo === null || foo === void 0 || foo === 1", "foo == null || foo === 1"); + test("foo === 1 || foo === null || foo === void 0", "foo === 1 || foo == null"); + test_same("foo === void 0 || bar === null"); + test_same("foo !== 1 && foo === void 0 || foo === null"); + test_same("foo.a === void 0 || foo.a === null"); // cannot be folded because accessing foo.a might have a side effect + + test("foo !== null && foo !== undefined", "foo != null"); + test("foo !== undefined && foo !== null", "foo != null"); + test("foo !== null && foo !== void 0", "foo != null"); + test("foo !== null && foo !== void 0 && foo !== 1", "foo != null && foo !== 1"); + test("foo !== 1 && foo !== null && foo !== void 0", "foo !== 1 && foo != null"); + test("foo !== 1 || foo !== void 0 && foo !== null", "foo !== 1 || foo != null"); + test_same("foo !== void 0 && bar !== null"); + } } diff --git a/crates/oxc_minifier/src/ast_passes/remove_syntax.rs b/crates/oxc_minifier/src/ast_passes/remove_syntax.rs index 4d4063b1d202b4..c72189134a13d5 100644 --- a/crates/oxc_minifier/src/ast_passes/remove_syntax.rs +++ b/crates/oxc_minifier/src/ast_passes/remove_syntax.rs @@ -21,7 +21,7 @@ impl<'a> CompressorPass<'a> for RemoveSyntax { } impl<'a> Traverse<'a> for RemoveSyntax { - fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) { + fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) { stmts.retain(|stmt| { !(matches!(stmt, Statement::EmptyStatement(_)) || self.drop_debugger(stmt) @@ -29,11 +29,8 @@ impl<'a> Traverse<'a> for RemoveSyntax { }); } - fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - self.compress_console(expr, ctx); - } - fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + self.compress_console(expr, ctx); Self::strip_parenthesized_expression(expr, ctx); } @@ -114,7 +111,7 @@ mod test { #[test] fn drop_console() { - test("console.log()", ""); + test("console.log()", "void 0;\n"); } #[test] diff --git a/crates/oxc_minifier/src/compressor.rs b/crates/oxc_minifier/src/compressor.rs index dc1c6def1d8a38..174062ee151369 100644 --- a/crates/oxc_minifier/src/compressor.rs +++ b/crates/oxc_minifier/src/compressor.rs @@ -5,8 +5,8 @@ use oxc_traverse::ReusableTraverseCtx; use crate::{ ast_passes::{ - CollapsePass, LatePeepholeOptimizations, PeepholeFoldConstants, PeepholeOptimizations, - PeepholeRemoveDeadCode, RemoveSyntax, + CollapsePass, DeadCodeElimination, LatePeepholeOptimizations, PeepholeOptimizations, + RemoveSyntax, }, CompressOptions, CompressorPass, }; @@ -44,10 +44,7 @@ impl<'a> Compressor<'a> { pub fn dead_code_elimination(self, program: &mut Program<'a>) { let (symbols, scopes) = SemanticBuilder::new().build(program).semantic.into_symbol_table_and_scope_tree(); - let mut ctx = ReusableTraverseCtx::new(scopes, symbols, self.allocator); - RemoveSyntax::new(self.options).build(program, &mut ctx); - PeepholeFoldConstants::new().build(program, &mut ctx); - PeepholeRemoveDeadCode::new().build(program, &mut ctx); + self.dead_code_elimination_with_symbols_and_scopes(symbols, scopes, program); } pub fn dead_code_elimination_with_symbols_and_scopes( @@ -57,8 +54,6 @@ impl<'a> Compressor<'a> { program: &mut Program<'a>, ) { let mut ctx = ReusableTraverseCtx::new(scopes, symbols, self.allocator); - RemoveSyntax::new(self.options).build(program, &mut ctx); - PeepholeFoldConstants::new().build(program, &mut ctx); - PeepholeRemoveDeadCode::new().build(program, &mut ctx); + DeadCodeElimination::new().build(program, &mut ctx); } } diff --git a/crates/oxc_minifier/src/lib.rs b/crates/oxc_minifier/src/lib.rs index 67f92ea9fe22a1..f523cb4813ee21 100644 --- a/crates/oxc_minifier/src/lib.rs +++ b/crates/oxc_minifier/src/lib.rs @@ -13,9 +13,10 @@ use oxc_allocator::Allocator; use oxc_ast::ast::Program; use oxc_mangler::Mangler; -pub use crate::{ast_passes::CompressorPass, compressor::Compressor, options::CompressOptions}; pub use oxc_mangler::MangleOptions; +pub use crate::{ast_passes::CompressorPass, compressor::Compressor, options::CompressOptions}; + #[derive(Debug, Clone, Copy)] pub struct MinifierOptions { pub mangle: Option, diff --git a/crates/oxc_napi/Cargo.toml b/crates/oxc_napi/Cargo.toml index 3df095ea9891ad..4cff316a4c8f20 100644 --- a/crates/oxc_napi/Cargo.toml +++ b/crates/oxc_napi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_napi" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_parser/Cargo.toml b/crates/oxc_parser/Cargo.toml index 4badc9f3f0f346..01c66f867f4365 100644 --- a/crates/oxc_parser/Cargo.toml +++ b/crates/oxc_parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_parser" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index 2ef147e0515d3a..275cbf6657986d 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -60,9 +60,7 @@ impl<'a> ParserImpl<'a> { let mut phase = None; match self.cur_kind() { Kind::Source => { - let peek_kind = self.peek_kind(); - // Allow `import source from 'mod'` - if peek_kind.is_binding_identifier() && peek_kind != Kind::From { + if self.peek_kind().is_binding_identifier() && self.nth_kind(2) == Kind::From { self.bump_any(); phase = Some(ImportPhase::Source); } diff --git a/crates/oxc_regular_expression/Cargo.toml b/crates/oxc_regular_expression/Cargo.toml index b8b20234589c72..4842f7566696f6 100644 --- a/crates/oxc_regular_expression/Cargo.toml +++ b/crates/oxc_regular_expression/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_regular_expression" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_semantic/CHANGELOG.md b/crates/oxc_semantic/CHANGELOG.md index 37a2129e47c844..d11a6c9918c353 100644 --- a/crates/oxc_semantic/CHANGELOG.md +++ b/crates/oxc_semantic/CHANGELOG.md @@ -4,6 +4,35 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.43.0] - 2024-12-21 + +- ed75e42 semantic: [**BREAKING**] Make SymbolTable fields `pub(crate)` instead of `pub` (#7999) (Boshen) + +### Features + +- 8547e02 ast: Implement `allocator_api2` for `Allocator` (#8043) (Boshen) + +### Performance + +- 2736657 semantic: Allocate `UnresolvedReferences` in allocator (#8046) (Boshen) +- 2e8872c semantic: Allocate child scope in allocator (#8045) (Boshen) +- 414e828 semantic: Allocate symbol data in Allocator (#8012) (Boshen) +- 7aebed0 semantic: Allocate `Bindings` in allocator (#8021) (Boshen) + +### Refactor + +- f1adf9f semantic: `ScopeTree::rename_binding` remove old binding first (#8020) (overlookmotel) +- 02f968d semantic: Change `Bindings` to a plain `FxHashMap` (#8019) (Boshen) +- e7476a1 semantic: Remove `serialize` (#8015) (Boshen) +- 1cf7b83 semantic: Simplify handling namespace stack (#7987) (Dunqing) +- 48cb52b semantic: Remove resetting `current_reference_flags` in visit functions (#7986) (Dunqing) +- 3250a47 semantic: Remove unused current_symbol_flags (#7985) (Dunqing) +- efe96ec semantic: Use `Stack` for function stack node ids (#7984) (Dunqing) + +### Testing + +- bcb33c0 semantic: Add a test for catch parameters reference (#7988) (Dunqing) + ## [0.42.0] - 2024-12-18 - c071494 semantic: [**BREAKING**] Remove `SymbolTable::rename` method (#7868) (overlookmotel) diff --git a/crates/oxc_semantic/Cargo.toml b/crates/oxc_semantic/Cargo.toml index 9158d130690e86..4d77bab61bb95f 100644 --- a/crates/oxc_semantic/Cargo.toml +++ b/crates/oxc_semantic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_semantic" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index edd6875b2c8db8..b1959b9f839f9f 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -279,12 +279,9 @@ impl<'a> SemanticBuilder<'a> { if self.check_syntax_error && !self.source_type.is_typescript() { checker::check_unresolved_exports(&self); } - self.scope.root_unresolved_references = self - .unresolved_references - .into_root() - .into_iter() - .map(|(k, v)| (k.into(), v)) - .collect(); + self.scope.set_root_unresolved_references( + self.unresolved_references.into_root().into_iter().map(|(k, v)| (k.as_str(), v)), + ); let jsdoc = if self.build_jsdoc { self.jsdoc.build() } else { JSDocFinder::default() }; diff --git a/crates/oxc_semantic/src/scope.rs b/crates/oxc_semantic/src/scope.rs index 0b6144e41c3a87..808898db5ad30c 100644 --- a/crates/oxc_semantic/src/scope.rs +++ b/crates/oxc_semantic/src/scope.rs @@ -1,10 +1,9 @@ use std::{fmt, mem}; -use rustc_hash::{FxBuildHasher, FxHashMap}; +use rustc_hash::FxBuildHasher; -use oxc_allocator::Allocator; -use oxc_index::IndexVec; -use oxc_span::CompactStr; +use oxc_allocator::{Allocator, Vec as ArenaVec}; +use oxc_index::{Idx, IndexVec}; use oxc_syntax::{ node::NodeId, reference::ReferenceId, @@ -13,7 +12,8 @@ use oxc_syntax::{ }; pub(crate) type Bindings<'a> = hashbrown::HashMap<&'a str, SymbolId, FxBuildHasher, &'a Allocator>; -pub type UnresolvedReferences = FxHashMap>; +pub type UnresolvedReferences<'a> = + hashbrown::HashMap<&'a str, ArenaVec<'a, ReferenceId>, FxBuildHasher, &'a Allocator>; /// Scope Tree /// @@ -29,9 +29,6 @@ pub struct ScopeTree { /// Maps a scope to the parent scope it belongs in. parent_ids: IndexVec>, - /// Maps a scope to direct children scopes. - child_ids: IndexVec>, - /// Runtime flag for constructing child_ids. pub(crate) build_child_ids: bool, @@ -40,8 +37,6 @@ pub struct ScopeTree { flags: IndexVec, - pub(crate) root_unresolved_references: UnresolvedReferences, - pub(crate) cell: ScopeTreeCell, } @@ -55,13 +50,16 @@ impl Default for ScopeTree { fn default() -> Self { Self { parent_ids: IndexVec::new(), - child_ids: IndexVec::new(), build_child_ids: false, node_ids: IndexVec::new(), flags: IndexVec::new(), - root_unresolved_references: UnresolvedReferences::default(), - cell: ScopeTreeCell::new(Allocator::default(), |_bump| ScopeTreeInner { + cell: ScopeTreeCell::new(Allocator::default(), |allocator| ScopeTreeInner { bindings: IndexVec::new(), + child_ids: ArenaVec::new_in(allocator), + root_unresolved_references: UnresolvedReferences::with_hasher_in( + FxBuildHasher, + allocator, + ), }), } } @@ -80,6 +78,11 @@ pub(crate) struct ScopeTreeInner<'cell> { /// /// A binding is a mapping from an identifier name to its [`SymbolId`] pub(crate) bindings: IndexVec>, + + /// Maps a scope to direct children scopes. + child_ids: ArenaVec<'cell, ArenaVec<'cell, ScopeId>>, + + pub(crate) root_unresolved_references: UnresolvedReferences<'cell>, } impl ScopeTree { @@ -131,13 +134,28 @@ impl ScopeTree { #[inline] pub fn root_unresolved_references(&self) -> &UnresolvedReferences { - &self.root_unresolved_references + &self.cell.borrow_dependent().root_unresolved_references } pub fn root_unresolved_references_ids( &self, ) -> impl Iterator + '_> + '_ { - self.root_unresolved_references.values().map(|v| v.iter().copied()) + self.cell.borrow_dependent().root_unresolved_references.values().map(|v| v.iter().copied()) + } + + pub(crate) fn set_root_unresolved_references<'a>( + &mut self, + entries: impl Iterator)>, + ) { + self.cell.with_dependent_mut(|allocator, inner| { + for (k, v) in entries { + let k = allocator.alloc_str(k); + let v = ArenaVec::from_iter_in(v, allocator); + inner.root_unresolved_references.insert(k, v); + } + // = + // .extend_from(entries.map(|(k, v)| (allocator.alloc(k),))) + }); } /// Delete an unresolved reference. @@ -153,14 +171,16 @@ impl ScopeTree { // but `map.entry` requires an owned key to be provided. Currently we use `CompactStr`s as keys // which are not cheap to construct, so this is best we can do at present. // TODO: Switch to `Entry` API once we use `&str`s or `Atom`s as keys. - let reference_ids = self.root_unresolved_references.get_mut(name).unwrap(); - if reference_ids.len() == 1 { - assert!(reference_ids[0] == reference_id); - self.root_unresolved_references.remove(name); - } else { - let index = reference_ids.iter().position(|&id| id == reference_id).unwrap(); - reference_ids.swap_remove(index); - } + self.cell.with_dependent_mut(|_allocator, inner| { + let reference_ids = inner.root_unresolved_references.get_mut(name).unwrap(); + if reference_ids.len() == 1 { + assert!(reference_ids[0] == reference_id); + inner.root_unresolved_references.remove(name); + } else { + let index = reference_ids.iter().position(|&id| id == reference_id).unwrap(); + reference_ids.swap_remove(index); + } + }); } #[inline] @@ -189,7 +209,9 @@ impl ScopeTree { if self.build_child_ids { // Set this scope as child of parent scope if let Some(parent_id) = parent_id { - self.child_ids[parent_id].push(scope_id); + self.cell.with_dependent_mut(|_allocator, inner| { + inner.child_ids[parent_id.index()].push(scope_id); + }); } } } @@ -200,25 +222,29 @@ impl ScopeTree { pub fn change_parent_id(&mut self, scope_id: ScopeId, new_parent_id: Option) { let old_parent_id = mem::replace(&mut self.parent_ids[scope_id], new_parent_id); if self.build_child_ids { - // Remove this scope from old parent scope - if let Some(old_parent_id) = old_parent_id { - self.child_ids[old_parent_id].retain(|&child_id| child_id != scope_id); - } - // And add it to new parent scope - if let Some(parent_id) = new_parent_id { - self.child_ids[parent_id].push(scope_id); - } + self.cell.with_dependent_mut(|_allocator, inner| { + // Remove this scope from old parent scope + if let Some(old_parent_id) = old_parent_id { + inner.child_ids[old_parent_id.index()].retain(|&child_id| child_id != scope_id); + } + // And add it to new parent scope + if let Some(parent_id) = new_parent_id { + inner.child_ids[parent_id.index()].push(scope_id); + } + }); } } /// Delete a scope. pub fn delete_scope(&mut self, scope_id: ScopeId) { if self.build_child_ids { - self.child_ids[scope_id].clear(); - let parent_id = self.parent_ids[scope_id]; - if let Some(parent_id) = parent_id { - self.child_ids[parent_id].retain(|&child_id| child_id != scope_id); - } + self.cell.with_dependent_mut(|_allocator, inner| { + inner.child_ids[scope_id.index()].clear(); + let parent_id = self.parent_ids[scope_id]; + if let Some(parent_id) = parent_id { + inner.child_ids[parent_id.index()].retain(|&child_id| child_id != scope_id); + } + }); } } @@ -228,8 +254,15 @@ impl ScopeTree { self.get_binding(self.root_scope_id(), name) } - pub fn add_root_unresolved_reference(&mut self, name: CompactStr, reference_id: ReferenceId) { - self.root_unresolved_references.entry(name).or_default().push(reference_id); + pub fn add_root_unresolved_reference(&mut self, name: &str, reference_id: ReferenceId) { + self.cell.with_dependent_mut(|allocator, inner| { + let name = allocator.alloc_str(name); + inner + .root_unresolved_references + .entry(name) + .or_insert_with(|| ArenaVec::new_in(allocator)) + .push(reference_id); + }); } /// Check if a symbol is declared in a certain scope. @@ -310,15 +343,18 @@ impl ScopeTree { /// Get the child scopes of a scope #[inline] pub fn get_child_ids(&self, scope_id: ScopeId) -> &[ScopeId] { - &self.child_ids[scope_id] + &self.cell.borrow_dependent().child_ids[scope_id.index()] } pub fn iter_all_child_ids(&self, scope_id: ScopeId) -> impl Iterator + '_ { - let mut stack = self.child_ids[scope_id].clone(); - let child_ids: &IndexVec> = &self.child_ids; + let mut stack = self.cell.borrow_dependent().child_ids[scope_id.index()] + .iter() + .copied() + .collect::>(); + let child_ids = &self.cell.borrow_dependent().child_ids; std::iter::from_fn(move || { if let Some(scope_id) = stack.pop() { - if let Some(children) = child_ids.get(scope_id) { + if let Some(children) = child_ids.get(scope_id.index()) { stack.extend(children.iter().copied()); } Some(scope_id) @@ -328,10 +364,11 @@ impl ScopeTree { }) } - /// Get a mutable reference to a scope's children - #[inline] - pub fn get_child_ids_mut(&mut self, scope_id: ScopeId) -> &mut Vec { - &mut self.child_ids[scope_id] + pub fn remove_child_scopes(&mut self, scope_id: ScopeId, child_scope_ids: &[ScopeId]) { + self.cell.with_dependent_mut(|_allocator, inner| { + inner.child_ids[scope_id.index()] + .retain(|scope_id| !child_scope_ids.contains(scope_id)); + }); } /// Create a scope. @@ -349,10 +386,12 @@ impl ScopeTree { }); self.node_ids.push(node_id); if self.build_child_ids { - self.child_ids.push(vec![]); - if let Some(parent_id) = parent_id { - self.child_ids[parent_id].push(scope_id); - } + self.cell.with_dependent_mut(|allocator, inner| { + inner.child_ids.push(ArenaVec::new_in(allocator)); + if let Some(parent_id) = parent_id { + inner.child_ids[parent_id.index()].push(scope_id); + } + }); } scope_id } @@ -418,7 +457,9 @@ impl ScopeTree { }); self.node_ids.reserve(additional); if self.build_child_ids { - self.child_ids.reserve(additional); + self.cell.with_dependent_mut(|_allocator, inner| { + inner.child_ids.reserve(additional); + }); } } } diff --git a/crates/oxc_span/Cargo.toml b/crates/oxc_span/Cargo.toml index 9dab1ae6f17a53..cd5b6670aadcef 100644 --- a/crates/oxc_span/Cargo.toml +++ b/crates/oxc_span/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_span" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_syntax/CHANGELOG.md b/crates/oxc_syntax/CHANGELOG.md index e336da229ca257..1dbd6924035e8a 100644 --- a/crates/oxc_syntax/CHANGELOG.md +++ b/crates/oxc_syntax/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.43.0] - 2024-12-21 + +### Refactor + +- e7476a1 semantic: Remove `serialize` (#8015) (Boshen) + ## [0.42.0] - 2024-12-18 - 84b75a0 semantic: [**BREAKING**] Remove `ScopeFlags::Modifiers` (#7935) (overlookmotel) diff --git a/crates/oxc_syntax/Cargo.toml b/crates/oxc_syntax/Cargo.toml index 676ba5ed7bb872..58cf0e511e6fdd 100644 --- a/crates/oxc_syntax/Cargo.toml +++ b/crates/oxc_syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_syntax" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_transformer/CHANGELOG.md b/crates/oxc_transformer/CHANGELOG.md index 264e444af9e998..dcbc406cba4b1c 100644 --- a/crates/oxc_transformer/CHANGELOG.md +++ b/crates/oxc_transformer/CHANGELOG.md @@ -4,6 +4,39 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.43.0] - 2024-12-21 + +- de4c772 traverse: [**BREAKING**] Rename `Ancestor::is_via_*` methods to `is_parent_of_*` (#8031) (overlookmotel) + +- ed75e42 semantic: [**BREAKING**] Make SymbolTable fields `pub(crate)` instead of `pub` (#7999) (Boshen) + +### Features + +- 897a1a8 transformer/class-properties: Exit faster from super replacement visitor (#8028) (overlookmotel) +- 3ea4109 transformer/class-properties: Transform super update expressions within static prop initializer (#7997) (Dunqing) +- cc57db3 transformer/class-properties: Transform super assignment expressions within static prop initializer (#7991) (Dunqing) + +### Bug Fixes + +- 043252d transformer/class-properties: Replace `this` and class name in static blocks (#8035) (overlookmotel) +- 273795d transformer/class-properties: Run other transforms on static properties, static blocks, and computed keys (#7982) (overlookmotel) + +### Performance + +- 2736657 semantic: Allocate `UnresolvedReferences` in allocator (#8046) (Boshen) +- 0f9308f transformer/react-refresh: Reduce allocations (#8018) (overlookmotel) +- 0deb9e6 transformer/react-refresh: Reserve capacity in hook key string (#8016) (overlookmotel) +- 7b70347 transformer/react-refresh: Avoid allocating string in each hook call (#8013) (Dunqing) + +### Refactor + +- ac097e9 transformer/class-properties: Rename file (#8036) (overlookmotel) +- 059a5dd transformer/class-properties: Do not pass `ScopeId` into `insert_instance_inits` (#8001) (overlookmotel) +- 0a38eea transformer/class-properties: Use `temp_var_name_base` to generate temp var names for `super` transform (#8004) (overlookmotel) +- d1b7181 transformer/class-properties: Rename var (#8006) (overlookmotel) +- 5a23d72 transformer/class-properties: Remove outdated comment (#8000) (overlookmotel) +- b3a5f3e transformer/class-properties: Mark `transform_assignment_expression_if_super_member_assignment_target` as inline (#7993) (Dunqing) + ## [0.42.0] - 2024-12-18 - c071494 semantic: [**BREAKING**] Remove `SymbolTable::rename` method (#7868) (overlookmotel) diff --git a/crates/oxc_transformer/Cargo.toml b/crates/oxc_transformer/Cargo.toml index 59e1cabad7e096..cd71689681bd52 100644 --- a/crates/oxc_transformer/Cargo.toml +++ b/crates/oxc_transformer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_transformer" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs b/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs index 6f89dfe1acc54e..9316824c525e53 100644 --- a/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs +++ b/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs @@ -116,7 +116,7 @@ impl<'a, 'ctx> NullishCoalescingOperator<'a, 'ctx> { let current_scope_id = if is_parent_formal_parameter { ctx.create_child_scope_of_current(ScopeFlags::Arrow | ScopeFlags::Function) } else { - ctx.current_scope_id() + ctx.current_hoist_scope_id() }; // Add `var _name` to scope diff --git a/crates/oxc_transformer/src/jsx/jsx_impl.rs b/crates/oxc_transformer/src/jsx/jsx_impl.rs index 5338a9e79b7448..b90aff146e515d 100644 --- a/crates/oxc_transformer/src/jsx/jsx_impl.rs +++ b/crates/oxc_transformer/src/jsx/jsx_impl.rs @@ -1081,8 +1081,7 @@ fn get_read_identifier_reference<'a>( name: Atom<'a>, ctx: &mut TraverseCtx<'a>, ) -> Expression<'a> { - let reference_id = - ctx.create_reference_in_current_scope(name.to_compact_str(), ReferenceFlags::Read); + let reference_id = ctx.create_reference_in_current_scope(name.as_str(), ReferenceFlags::Read); let ident = ctx.ast.alloc_identifier_reference_with_reference_id(span, name, reference_id); Expression::Identifier(ident) } diff --git a/crates/oxc_transformer/src/jsx/refresh.rs b/crates/oxc_transformer/src/jsx/refresh.rs index bdee87debfe356..676213a9634ffd 100644 --- a/crates/oxc_transformer/src/jsx/refresh.rs +++ b/crates/oxc_transformer/src/jsx/refresh.rs @@ -68,8 +68,7 @@ impl<'a> RefreshIdentifierResolver<'a> { pub fn to_expression(&self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> { match self { Self::Identifier(ident) => { - let reference_id = - ctx.create_unbound_reference(ident.name.to_compact_str(), ReferenceFlags::Read); + let reference_id = ctx.create_unbound_reference(&ident.name, ReferenceFlags::Read); Expression::Identifier(ctx.ast.alloc_identifier_reference_with_reference_id( ident.span, ident.name.clone(), @@ -77,8 +76,7 @@ impl<'a> RefreshIdentifierResolver<'a> { )) } Self::Member((ident, property)) => { - let reference_id = - ctx.create_unbound_reference(ident.name.to_compact_str(), ReferenceFlags::Read); + let reference_id = ctx.create_unbound_reference(&ident.name, ReferenceFlags::Read); let ident = Expression::Identifier(ctx.ast.alloc_identifier_reference_with_reference_id( ident.span, diff --git a/crates/oxc_transformer/src/plugins/inject_global_variables.rs b/crates/oxc_transformer/src/plugins/inject_global_variables.rs index 7243d819d82c69..3a77614e479ed0 100644 --- a/crates/oxc_transformer/src/plugins/inject_global_variables.rs +++ b/crates/oxc_transformer/src/plugins/inject_global_variables.rs @@ -181,7 +181,7 @@ impl<'a> InjectGlobalVariables<'a> { } else if self.replaced_dot_defines.iter().any(|d| d.0 == i.specifier.local()) { false } else { - scopes.root_unresolved_references().contains_key(i.specifier.local()) + scopes.root_unresolved_references().contains_key(i.specifier.local().as_str()) } }) .cloned() diff --git a/crates/oxc_transformer/src/typescript/module.rs b/crates/oxc_transformer/src/typescript/module.rs index e9c4151d93927e..f0e05b58b7d0f4 100644 --- a/crates/oxc_transformer/src/typescript/module.rs +++ b/crates/oxc_transformer/src/typescript/module.rs @@ -1,5 +1,5 @@ use oxc_ast::{ast::*, NONE}; -use oxc_span::{CompactStr, SPAN}; +use oxc_span::SPAN; use oxc_syntax::reference::ReferenceFlags; use oxc_traverse::{Traverse, TraverseCtx}; @@ -58,8 +58,8 @@ impl<'a, 'ctx> TypeScriptModule<'a, 'ctx> { // module.exports let module_exports = { - let reference_id = ctx - .create_reference_in_current_scope(CompactStr::new("module"), ReferenceFlags::Read); + let reference_id = + ctx.create_reference_in_current_scope("module", ReferenceFlags::Read); let reference = ctx.ast.alloc_identifier_reference_with_reference_id(SPAN, "module", reference_id); let object = Expression::Identifier(reference); diff --git a/crates/oxc_traverse/CHANGELOG.md b/crates/oxc_traverse/CHANGELOG.md index 2c044dab6400cd..8a6c9af3598ae4 100644 --- a/crates/oxc_traverse/CHANGELOG.md +++ b/crates/oxc_traverse/CHANGELOG.md @@ -4,6 +4,27 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.43.0] - 2024-12-21 + +- de4c772 traverse: [**BREAKING**] Rename `Ancestor::is_via_*` methods to `is_parent_of_*` (#8031) (overlookmotel) + +- ed75e42 semantic: [**BREAKING**] Make SymbolTable fields `pub(crate)` instead of `pub` (#7999) (Boshen) + +### Features + +- 6b6444b traverse: Record current block scope (#8007) (overlookmotel) + +### Performance + +- 2736657 semantic: Allocate `UnresolvedReferences` in allocator (#8046) (Boshen) +- 2e8872c semantic: Allocate child scope in allocator (#8045) (Boshen) +- 414e828 semantic: Allocate symbol data in Allocator (#8012) (Boshen) +- 7aebed0 semantic: Allocate `Bindings` in allocator (#8021) (Boshen) + +### Refactor + +- f1adf9f semantic: `ScopeTree::rename_binding` remove old binding first (#8020) (overlookmotel) + ## [0.42.0] - 2024-12-18 ### Features diff --git a/crates/oxc_traverse/Cargo.toml b/crates/oxc_traverse/Cargo.toml index fd5da804c6afaf..4b7e0dcadea87e 100644 --- a/crates/oxc_traverse/Cargo.toml +++ b/crates/oxc_traverse/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_traverse" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_traverse/src/context/mod.rs b/crates/oxc_traverse/src/context/mod.rs index 98b62e8d5c0b22..4909deeeca163d 100644 --- a/crates/oxc_traverse/src/context/mod.rs +++ b/crates/oxc_traverse/src/context/mod.rs @@ -488,11 +488,7 @@ impl<'a> TraverseCtx<'a> { /// /// This is a shortcut for `ctx.scoping.create_unbound_reference`. #[inline] - pub fn create_unbound_reference( - &mut self, - name: CompactStr, - flags: ReferenceFlags, - ) -> ReferenceId { + pub fn create_unbound_reference(&mut self, name: &str, flags: ReferenceFlags) -> ReferenceId { self.scoping.create_unbound_reference(name, flags) } @@ -503,7 +499,7 @@ impl<'a> TraverseCtx<'a> { name: Atom<'a>, flags: ReferenceFlags, ) -> IdentifierReference<'a> { - let reference_id = self.create_unbound_reference(name.to_compact_str(), flags); + let reference_id = self.create_unbound_reference(name.as_str(), flags); self.ast.identifier_reference_with_reference_id(span, name, reference_id) } @@ -527,7 +523,7 @@ impl<'a> TraverseCtx<'a> { #[inline] pub fn create_reference( &mut self, - name: CompactStr, + name: &str, symbol_id: Option, flags: ReferenceFlags, ) -> ReferenceId { @@ -576,7 +572,7 @@ impl<'a> TraverseCtx<'a> { #[inline] pub fn create_reference_in_current_scope( &mut self, - name: CompactStr, + name: &str, flags: ReferenceFlags, ) -> ReferenceId { self.scoping.create_reference_in_current_scope(name, flags) diff --git a/crates/oxc_traverse/src/context/scoping.rs b/crates/oxc_traverse/src/context/scoping.rs index 50bc2cf1e68682..b932e5e8bc783b 100644 --- a/crates/oxc_traverse/src/context/scoping.rs +++ b/crates/oxc_traverse/src/context/scoping.rs @@ -133,8 +133,7 @@ impl TraverseScoping { fn insert_scope_below(&mut self, child_scope_ids: &[ScopeId], flags: ScopeFlags) -> ScopeId { // Remove these scopes from parent's children if self.scopes.has_child_ids() { - let current_child_scope_ids = self.scopes.get_child_ids_mut(self.current_scope_id); - current_child_scope_ids.retain(|scope_id| !child_scope_ids.contains(scope_id)); + self.scopes.remove_child_scopes(self.current_scope_id, child_scope_ids); } // Create new scope as child of parent @@ -314,11 +313,7 @@ impl TraverseScoping { } /// Create an unbound reference - pub fn create_unbound_reference( - &mut self, - name: CompactStr, - flags: ReferenceFlags, - ) -> ReferenceId { + pub fn create_unbound_reference(&mut self, name: &str, flags: ReferenceFlags) -> ReferenceId { let reference = Reference::new(NodeId::DUMMY, flags); let reference_id = self.symbols.create_reference(reference); self.scopes.add_root_unresolved_reference(name, reference_id); @@ -331,7 +326,7 @@ impl TraverseScoping { /// or `TraverseCtx::create_unbound_reference`. pub fn create_reference( &mut self, - name: CompactStr, + name: &str, symbol_id: Option, flags: ReferenceFlags, ) -> ReferenceId { @@ -345,10 +340,10 @@ impl TraverseScoping { /// Create reference in current scope, looking up binding for `name` pub fn create_reference_in_current_scope( &mut self, - name: CompactStr, + name: &str, flags: ReferenceFlags, ) -> ReferenceId { - let symbol_id = self.scopes.find_binding(self.current_scope_id, name.as_str()); + let symbol_id = self.scopes.find_binding(self.current_scope_id, name); self.create_reference(name, symbol_id, flags) } @@ -434,7 +429,7 @@ impl TraverseScoping { self.scopes .root_unresolved_references() .keys() - .map(CompactStr::as_str) + .copied() .chain(self.symbols.names()) .filter(|name| name.as_bytes().first() == Some(&b'_')) .map(CompactStr::from) diff --git a/justfile b/justfile index 97aa0defe68638..3eafb588c30749 100755 --- a/justfile +++ b/justfile @@ -36,7 +36,7 @@ ready: # Clone or update submodules # Make sure to update `.github/actions/clone-submodules/action.yml` too submodules: - just clone-submodule tasks/coverage/test262 https://github.com/tc39/test262.git dc0082c5ea347e5ecb585c1d7ebf4555aa429528 + just clone-submodule tasks/coverage/test262 https://github.com/tc39/test262.git c4317b0cb578d3fe7940f65b27162638efb9b34d just clone-submodule tasks/coverage/babel https://github.com/babel/babel.git 54a8389fa31ce4fd18b0335b05832dc1ad3cc21f just clone-submodule tasks/coverage/typescript https://github.com/microsoft/TypeScript.git d85767abfd83880cea17cea70f9913e9c4496dcc just clone-submodule tasks/prettier_conformance/prettier https://github.com/prettier/prettier.git 37fd1774d13ef68abcc03775ceef0a91f87a57d7 diff --git a/napi/transform/Cargo.toml b/napi/transform/Cargo.toml index c3c438d0ba8b51..af3e3995d8c0a4 100644 --- a/napi/transform/Cargo.toml +++ b/napi/transform/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_transform_napi" -version = "0.42.0" +version = "0.43.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/npm/oxc-parser/package.json b/npm/oxc-parser/package.json index 47cee86d610187..febdc43390ce0d 100644 --- a/npm/oxc-parser/package.json +++ b/npm/oxc-parser/package.json @@ -1,6 +1,6 @@ { "name": "oxc-parser", - "version": "0.42.0", + "version": "0.43.0", "description": "Oxc Parser Node API", "keywords": [ "Parser" diff --git a/npm/oxc-transform/package.json b/npm/oxc-transform/package.json index fefa2e9d173901..2e30f577fc978c 100644 --- a/npm/oxc-transform/package.json +++ b/npm/oxc-transform/package.json @@ -1,6 +1,6 @@ { "name": "oxc-transform", - "version": "0.42.0", + "version": "0.43.0", "description": "Oxc transform Node API", "keywords": [ "transform" diff --git a/npm/oxc-types/package.json b/npm/oxc-types/package.json index dd5c9e4b69f864..9905ae4ab936b9 100644 --- a/npm/oxc-types/package.json +++ b/npm/oxc-types/package.json @@ -1,6 +1,6 @@ { "name": "@oxc-project/types", - "version": "0.42.0", + "version": "0.43.0", "description": "Types for Oxc AST nodes", "keywords": [ "AST", diff --git a/tasks/coverage/snapshots/codegen_test262.snap b/tasks/coverage/snapshots/codegen_test262.snap index c59ae7590f6753..04c35958489def 100644 --- a/tasks/coverage/snapshots/codegen_test262.snap +++ b/tasks/coverage/snapshots/codegen_test262.snap @@ -1,5 +1,5 @@ -commit: dc0082c5 +commit: c4317b0c codegen_test262 Summary: -AST Parsed : 44096/44096 (100.00%) -Positive Passed: 44096/44096 (100.00%) +AST Parsed : 44101/44101 (100.00%) +Positive Passed: 44101/44101 (100.00%) diff --git a/tasks/coverage/snapshots/minifier_test262.snap b/tasks/coverage/snapshots/minifier_test262.snap index 982b24519460c6..ede289e7e4a924 100644 --- a/tasks/coverage/snapshots/minifier_test262.snap +++ b/tasks/coverage/snapshots/minifier_test262.snap @@ -1,5 +1,5 @@ -commit: dc0082c5 +commit: c4317b0c minifier_test262 Summary: -AST Parsed : 44096/44096 (100.00%) -Positive Passed: 44096/44096 (100.00%) +AST Parsed : 44101/44101 (100.00%) +Positive Passed: 44101/44101 (100.00%) diff --git a/tasks/coverage/snapshots/parser_test262.snap b/tasks/coverage/snapshots/parser_test262.snap index b003893212426b..d7080e7e330f76 100644 --- a/tasks/coverage/snapshots/parser_test262.snap +++ b/tasks/coverage/snapshots/parser_test262.snap @@ -1,19 +1,9 @@ -commit: dc0082c5 +commit: c4317b0c parser_test262 Summary: -AST Parsed : 44095/44096 (100.00%) -Positive Passed: 44095/44096 (100.00%) -Negative Passed: 4454/4456 (99.96%) -Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attributes/json-invalid.js -Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attributes/json-named-bindings.js -Expect to Parse: tasks/coverage/test262/test/language/module-code/source-phase-import/import-source-binding-name-2.js - - × Unexpected token - ╭─[test262/test/language/module-code/source-phase-import/import-source-binding-name-2.js:23:20] - 22 │ import source source from ''; - 23 │ import source from from ''; - · ──── - ╰──── +AST Parsed : 44101/44101 (100.00%) +Positive Passed: 44101/44101 (100.00%) +Negative Passed: 4454/4454 (100.00%) × '0'-prefixed octal literals and octal escape sequences are deprecated ╭─[test262/test/annexB/language/expressions/template-literal/legacy-octal-escape-sequence-strict.js:19:4] diff --git a/tasks/coverage/snapshots/runtime.snap b/tasks/coverage/snapshots/runtime.snap index 5666ded472ea4d..9d249fc308a0d9 100644 --- a/tasks/coverage/snapshots/runtime.snap +++ b/tasks/coverage/snapshots/runtime.snap @@ -1,4 +1,4 @@ -commit: dc0082c5 +commit: c4317b0c runtime Summary: AST Parsed : 18055/18055 (100.00%) diff --git a/tasks/coverage/snapshots/semantic_test262.snap b/tasks/coverage/snapshots/semantic_test262.snap index e2dd848fd929fd..d2107edbb972e3 100644 --- a/tasks/coverage/snapshots/semantic_test262.snap +++ b/tasks/coverage/snapshots/semantic_test262.snap @@ -1,8 +1,8 @@ -commit: dc0082c5 +commit: c4317b0c semantic_test262 Summary: -AST Parsed : 44096/44096 (100.00%) -Positive Passed: 43569/44096 (98.80%) +AST Parsed : 44101/44101 (100.00%) +Positive Passed: 43583/44101 (98.83%) tasks/coverage/test262/test/annexB/language/function-code/if-decl-else-decl-a-func-block-scoping.js semantic error: Symbol scope ID mismatch for "f": after transform: SymbolId(3): ScopeId(4294967294) @@ -1209,68 +1209,6 @@ Scope children mismatch: after transform: ScopeId(2): [ScopeId(3)] rebuilt : ScopeId(4): [] -tasks/coverage/test262/test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-assignment-expression-coalesce.js -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["C", "_x10", "_x11", "_x12", "_x5", "_x6", "_x7", "_x8", "_x9", "c", "x"] -rebuilt : ScopeId(0): ["C", "_x", "_x10", "_x11", "_x12", "_x2", "_x3", "_x4", "_x5", "_x6", "_x7", "_x8", "_x9", "c", "x"] -Bindings mismatch: -after transform: ScopeId(1): ["_x", "_x2", "_x3", "_x4"] -rebuilt : ScopeId(1): [] -Symbol scope ID mismatch for "_x": -after transform: SymbolId(5): ScopeId(1) -rebuilt : SymbolId(0): ScopeId(0) -Symbol scope ID mismatch for "_x2": -after transform: SymbolId(6): ScopeId(1) -rebuilt : SymbolId(1): ScopeId(0) -Symbol scope ID mismatch for "_x3": -after transform: SymbolId(7): ScopeId(1) -rebuilt : SymbolId(2): ScopeId(0) -Symbol scope ID mismatch for "_x4": -after transform: SymbolId(8): ScopeId(1) -rebuilt : SymbolId(3): ScopeId(0) - -tasks/coverage/test262/test/language/expressions/class/cpn-class-expr-computed-property-name-from-assignment-expression-coalesce.js -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["C", "_x3", "_x4", "_x5", "_x6", "c", "x"] -rebuilt : ScopeId(0): ["C", "_x", "_x2", "_x3", "_x4", "_x5", "_x6", "c", "x"] -Bindings mismatch: -after transform: ScopeId(1): ["_x", "_x2"] -rebuilt : ScopeId(1): [] -Symbol scope ID mismatch for "_x": -after transform: SymbolId(3): ScopeId(1) -rebuilt : SymbolId(0): ScopeId(0) -Symbol scope ID mismatch for "_x2": -after transform: SymbolId(4): ScopeId(1) -rebuilt : SymbolId(1): ScopeId(0) - -tasks/coverage/test262/test/language/expressions/class/cpn-class-expr-fields-computed-property-name-from-assignment-expression-coalesce.js -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["C", "_x3", "_x4", "_x5", "_x6", "c", "x"] -rebuilt : ScopeId(0): ["C", "_x", "_x2", "_x3", "_x4", "_x5", "_x6", "c", "x"] -Bindings mismatch: -after transform: ScopeId(1): ["_x", "_x2"] -rebuilt : ScopeId(1): [] -Symbol scope ID mismatch for "_x": -after transform: SymbolId(3): ScopeId(1) -rebuilt : SymbolId(0): ScopeId(0) -Symbol scope ID mismatch for "_x2": -after transform: SymbolId(4): ScopeId(1) -rebuilt : SymbolId(1): ScopeId(0) - -tasks/coverage/test262/test/language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-assignment-expression-coalesce.js -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["C", "_x3", "_x4", "_x5", "_x6", "c", "x"] -rebuilt : ScopeId(0): ["C", "_x", "_x2", "_x3", "_x4", "_x5", "_x6", "c", "x"] -Bindings mismatch: -after transform: ScopeId(1): ["_x", "_x2"] -rebuilt : ScopeId(1): [] -Symbol scope ID mismatch for "_x": -after transform: SymbolId(3): ScopeId(1) -rebuilt : SymbolId(0): ScopeId(0) -Symbol scope ID mismatch for "_x2": -after transform: SymbolId(4): ScopeId(1) -rebuilt : SymbolId(1): ScopeId(0) - tasks/coverage/test262/test/language/expressions/class/dstr/async-gen-meth-ary-ptrn-elem-ary-empty-init.js semantic error: Scope children mismatch: after transform: ScopeId(6): [ScopeId(3)] @@ -3695,9 +3633,6 @@ Unresolved references mismatch: after transform: ["$DONE", "Object", "assert", "require"] rebuilt : ["$DONE", "Object", "_superprop_getMethod", "assert", "require"] -tasks/coverage/test262/test/language/module-code/source-phase-import/import-source-binding-name-2.js -semantic error: Unexpected token - tasks/coverage/test262/test/language/module-code/top-level-await/syntax/for-await-await-expr-func-expression.js semantic error: Scope children mismatch: after transform: ScopeId(14): [ScopeId(1)] @@ -3849,68 +3784,6 @@ Scope children mismatch: after transform: ScopeId(2): [ScopeId(3)] rebuilt : ScopeId(4): [] -tasks/coverage/test262/test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-assignment-expression-coalesce.js -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["C", "_x10", "_x11", "_x12", "_x5", "_x6", "_x7", "_x8", "_x9", "c", "x"] -rebuilt : ScopeId(0): ["C", "_x", "_x10", "_x11", "_x12", "_x2", "_x3", "_x4", "_x5", "_x6", "_x7", "_x8", "_x9", "c", "x"] -Bindings mismatch: -after transform: ScopeId(1): ["_x", "_x2", "_x3", "_x4"] -rebuilt : ScopeId(1): [] -Symbol scope ID mismatch for "_x": -after transform: SymbolId(5): ScopeId(1) -rebuilt : SymbolId(0): ScopeId(0) -Symbol scope ID mismatch for "_x2": -after transform: SymbolId(6): ScopeId(1) -rebuilt : SymbolId(1): ScopeId(0) -Symbol scope ID mismatch for "_x3": -after transform: SymbolId(7): ScopeId(1) -rebuilt : SymbolId(2): ScopeId(0) -Symbol scope ID mismatch for "_x4": -after transform: SymbolId(8): ScopeId(1) -rebuilt : SymbolId(3): ScopeId(0) - -tasks/coverage/test262/test/language/statements/class/cpn-class-decl-computed-property-name-from-assignment-expression-coalesce.js -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["C", "_x3", "_x4", "_x5", "_x6", "c", "x"] -rebuilt : ScopeId(0): ["C", "_x", "_x2", "_x3", "_x4", "_x5", "_x6", "c", "x"] -Bindings mismatch: -after transform: ScopeId(1): ["_x", "_x2"] -rebuilt : ScopeId(1): [] -Symbol scope ID mismatch for "_x": -after transform: SymbolId(3): ScopeId(1) -rebuilt : SymbolId(0): ScopeId(0) -Symbol scope ID mismatch for "_x2": -after transform: SymbolId(4): ScopeId(1) -rebuilt : SymbolId(1): ScopeId(0) - -tasks/coverage/test262/test/language/statements/class/cpn-class-decl-fields-computed-property-name-from-assignment-expression-coalesce.js -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["C", "_x3", "_x4", "_x5", "_x6", "c", "x"] -rebuilt : ScopeId(0): ["C", "_x", "_x2", "_x3", "_x4", "_x5", "_x6", "c", "x"] -Bindings mismatch: -after transform: ScopeId(1): ["_x", "_x2"] -rebuilt : ScopeId(1): [] -Symbol scope ID mismatch for "_x": -after transform: SymbolId(3): ScopeId(1) -rebuilt : SymbolId(0): ScopeId(0) -Symbol scope ID mismatch for "_x2": -after transform: SymbolId(4): ScopeId(1) -rebuilt : SymbolId(1): ScopeId(0) - -tasks/coverage/test262/test/language/statements/class/cpn-class-decl-fields-methods-computed-property-name-from-assignment-expression-coalesce.js -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["C", "_x3", "_x4", "_x5", "_x6", "c", "x"] -rebuilt : ScopeId(0): ["C", "_x", "_x2", "_x3", "_x4", "_x5", "_x6", "c", "x"] -Bindings mismatch: -after transform: ScopeId(1): ["_x", "_x2"] -rebuilt : ScopeId(1): [] -Symbol scope ID mismatch for "_x": -after transform: SymbolId(3): ScopeId(1) -rebuilt : SymbolId(0): ScopeId(0) -Symbol scope ID mismatch for "_x2": -after transform: SymbolId(4): ScopeId(1) -rebuilt : SymbolId(1): ScopeId(0) - tasks/coverage/test262/test/language/statements/class/definition/methods-async-super-call-param.js semantic error: Symbol reference IDs mismatch for "_superprop_getMethod": after transform: SymbolId(5): [ReferenceId(8)] diff --git a/tasks/coverage/snapshots/semantic_typescript.snap b/tasks/coverage/snapshots/semantic_typescript.snap index 7798098d799c02..b60ce1b25ae139 100644 --- a/tasks/coverage/snapshots/semantic_typescript.snap +++ b/tasks/coverage/snapshots/semantic_typescript.snap @@ -2,7 +2,7 @@ commit: d85767ab semantic_typescript Summary: AST Parsed : 6503/6503 (100.00%) -Positive Passed: 2700/6503 (41.52%) +Positive Passed: 2701/6503 (41.53%) tasks/coverage/typescript/tests/cases/compiler/2dArrays.ts semantic error: Symbol reference IDs mismatch for "Cell": after transform: SymbolId(0): [ReferenceId(1)] @@ -46417,17 +46417,6 @@ semantic error: Scope children mismatch: after transform: ScopeId(0): [ScopeId(1)] rebuilt : ScopeId(0): [] -tasks/coverage/typescript/tests/cases/conformance/expressions/nullishCoalescingOperator/nullishCoalescingOperator12.ts -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["obj"] -rebuilt : ScopeId(0): ["_obj$arr", "obj"] -Bindings mismatch: -after transform: ScopeId(1): ["_obj$arr", "i"] -rebuilt : ScopeId(1): ["i"] -Symbol scope ID mismatch for "_obj$arr": -after transform: SymbolId(2): ScopeId(1) -rebuilt : SymbolId(0): ScopeId(0) - tasks/coverage/typescript/tests/cases/conformance/expressions/nullishCoalescingOperator/nullishCoalescingOperator2.ts semantic error: Bindings mismatch: after transform: ScopeId(0): ["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa1", "aa2", "aa3", "aa4", "aa5", "aa6", "aa7", "aa8", "aa9"] diff --git a/tasks/coverage/snapshots/transformer_test262.snap b/tasks/coverage/snapshots/transformer_test262.snap index b7dd47bbf66af7..d2deae3031a658 100644 --- a/tasks/coverage/snapshots/transformer_test262.snap +++ b/tasks/coverage/snapshots/transformer_test262.snap @@ -1,5 +1,5 @@ -commit: dc0082c5 +commit: c4317b0c transformer_test262 Summary: -AST Parsed : 44096/44096 (100.00%) -Positive Passed: 44096/44096 (100.00%) +AST Parsed : 44101/44101 (100.00%) +Positive Passed: 44101/44101 (100.00%) diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index cde7b69638608c..862dd783709820 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -1,13 +1,13 @@ | Oxc | ESBuild | Oxc | ESBuild | Original | minified | minified | gzip | gzip | Fixture ------------------------------------------------------------------------------------- -72.14 kB | 24.09 kB | 23.70 kB | 8.62 kB | 8.54 kB | react.development.js +72.14 kB | 24.05 kB | 23.70 kB | 8.61 kB | 8.54 kB | react.development.js -173.90 kB | 61.61 kB | 59.82 kB | 19.55 kB | 19.33 kB | moment.js +173.90 kB | 61.60 kB | 59.82 kB | 19.55 kB | 19.33 kB | moment.js 287.63 kB | 92.61 kB | 90.07 kB | 32.27 kB | 31.95 kB | jquery.js -342.15 kB | 121.79 kB | 118.14 kB | 44.59 kB | 44.37 kB | vue.js +342.15 kB | 121.77 kB | 118.14 kB | 44.58 kB | 44.37 kB | vue.js 544.10 kB | 73.37 kB | 72.48 kB | 26.13 kB | 26.20 kB | lodash.js @@ -15,13 +15,13 @@ Original | minified | minified | gzip | gzip | Fixture 1.01 MB | 467.14 kB | 458.89 kB | 126.74 kB | 126.71 kB | bundle.min.js -1.25 MB | 662.68 kB | 646.76 kB | 164.00 kB | 163.73 kB | three.js +1.25 MB | 662.66 kB | 646.76 kB | 164.00 kB | 163.73 kB | three.js -2.14 MB | 740.94 kB | 724.14 kB | 181.49 kB | 181.07 kB | victory.js +2.14 MB | 740.54 kB | 724.14 kB | 181.37 kB | 181.07 kB | victory.js 3.20 MB | 1.02 MB | 1.01 MB | 332.09 kB | 331.56 kB | echarts.js -6.69 MB | 2.39 MB | 2.31 MB | 496.17 kB | 488.28 kB | antd.js +6.69 MB | 2.39 MB | 2.31 MB | 495.63 kB | 488.28 kB | antd.js -10.95 MB | 3.55 MB | 3.49 MB | 910.48 kB | 915.50 kB | typescript.js +10.95 MB | 3.55 MB | 3.49 MB | 909.67 kB | 915.50 kB | typescript.js diff --git a/tasks/minsize/src/lib.rs b/tasks/minsize/src/lib.rs index 955b31712bc044..f6f7d8eed3def0 100644 --- a/tasks/minsize/src/lib.rs +++ b/tasks/minsize/src/lib.rs @@ -1,7 +1,8 @@ #![allow(clippy::print_stdout, clippy::print_stderr)] use std::{ - fs::File, + fs::{self, File}, io::{self, Write}, + path::Path, }; use flate2::{write::GzEncoder, Compression}; @@ -23,6 +24,7 @@ use rustc_hash::FxHashMap; /// # Panics /// # Errors pub fn run() -> Result<(), io::Error> { + let marker = std::env::args().nth(1).unwrap_or_else(|| String::from("default")); let files = TestFiles::minifier(); let path = project_root().join("tasks/minsize/minsize.snap"); @@ -90,8 +92,14 @@ pub fn run() -> Result<(), io::Error> { out.push_str(&str::repeat("-", width * 5 + fixture_width + 15)); out.push('\n'); + let save_path = Path::new("./target/minifier").join(marker); + for file in files.files() { let minified = minify_twice(file); + + fs::create_dir_all(&save_path).unwrap(); + fs::write(save_path.join(&file.file_name), &minified).unwrap(); + let s = format!( "{:width$} | {:width$} | {:width$} | {:width$} | {:width$} | {:width$}\n\n", format_size(file.source_text.len(), DECIMAL), diff --git a/tasks/transform_checker/src/lib.rs b/tasks/transform_checker/src/lib.rs index f6885942c6844d..1c7248f5ddd979 100644 --- a/tasks/transform_checker/src/lib.rs +++ b/tasks/transform_checker/src/lib.rs @@ -506,8 +506,12 @@ impl PostTransformChecker<'_, '_> { fn check_unresolved_references(&mut self) { let unresolved_names = self.get_static_pair(|scoping| { - let mut names = - scoping.scopes.root_unresolved_references().keys().cloned().collect::>(); + let mut names = scoping + .scopes + .root_unresolved_references() + .keys() + .map(ToString::to_string) + .collect::>(); names.sort_unstable(); names }); @@ -521,6 +525,10 @@ impl PostTransformChecker<'_, '_> { if let Some(reference_ids_rebuilt) = self.scoping_rebuilt.scopes.root_unresolved_references().get(name) { + let reference_ids_after_transform = + reference_ids_after_transform.iter().copied().collect::>(); + let reference_ids_rebuilt = + reference_ids_rebuilt.iter().copied().collect::>(); let reference_ids = Pair::new(reference_ids_after_transform, reference_ids_rebuilt); if self.remap_reference_ids_sets(&reference_ids).is_mismatch() { self.errors.push_mismatch_single( diff --git a/tasks/transform_conformance/snapshots/babel.snap.md b/tasks/transform_conformance/snapshots/babel.snap.md index ad00408a59ded5..921cd86d5b7c3b 100644 --- a/tasks/transform_conformance/snapshots/babel.snap.md +++ b/tasks/transform_conformance/snapshots/babel.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 599/927 +Passed: 602/927 # All Passed: * babel-plugin-transform-class-static-block @@ -464,7 +464,7 @@ x Output mismatch x Output mismatch -# babel-plugin-transform-optional-chaining (33/45) +# babel-plugin-transform-optional-chaining (36/45) * assumption-noDocumentAll/assignment/input.js Symbol reference IDs mismatch for "_obj$a": after transform: SymbolId(5): [ReferenceId(7), ReferenceId(8), ReferenceId(9)] @@ -476,17 +476,6 @@ Symbol reference IDs mismatch for "_obj$a2": after transform: SymbolId(7): [ReferenceId(15), ReferenceId(16), ReferenceId(17)] rebuilt : SymbolId(2): [ReferenceId(12), ReferenceId(14)] -* assumption-noDocumentAll/cast-to-boolean/input.js -Bindings mismatch: -after transform: ScopeId(25): ["_o$a$b$c$non_existent", "_o$a$b$c$non_existent3", "_o$a$b10", "_o$a$b6", "_o$a$b7", "_o$a$b8", "_o$a$b9", "o"] -rebuilt : ScopeId(25): ["_o$a$b$c$non_existent", "_o$a$b$c$non_existent2", "_o$a$b$c$non_existent3", "_o$a$b10", "_o$a$b6", "_o$a$b7", "_o$a$b8", "_o$a$b9", "o"] -Bindings mismatch: -after transform: ScopeId(26): ["_o$a$b$c$non_existent2"] -rebuilt : ScopeId(26): [] -Symbol scope ID mismatch for "_o$a$b$c$non_existent2": -after transform: SymbolId(31): ScopeId(26) -rebuilt : SymbolId(33): ScopeId(25) - * assumption-noDocumentAll/in-function-params/input.js Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(3), ScopeId(4), ScopeId(5)] @@ -593,17 +582,6 @@ Symbol reference IDs mismatch for "_foo$bar9$baz": after transform: SymbolId(15): [ReferenceId(66), ReferenceId(67), ReferenceId(68)] rebuilt : SymbolId(15): [ReferenceId(59), ReferenceId(61)] -* general/cast-to-boolean/input.js -Bindings mismatch: -after transform: ScopeId(25): ["_o$a$b$c$non_existent", "_o$a$b$c$non_existent3", "_o$a$b10", "_o$a$b6", "_o$a$b7", "_o$a$b8", "_o$a$b9", "o"] -rebuilt : ScopeId(25): ["_o$a$b$c$non_existent", "_o$a$b$c$non_existent2", "_o$a$b$c$non_existent3", "_o$a$b10", "_o$a$b6", "_o$a$b7", "_o$a$b8", "_o$a$b9", "o"] -Bindings mismatch: -after transform: ScopeId(26): ["_o$a$b$c$non_existent2"] -rebuilt : ScopeId(26): [] -Symbol scope ID mismatch for "_o$a$b$c$non_existent2": -after transform: SymbolId(31): ScopeId(26) -rebuilt : SymbolId(33): ScopeId(25) - * general/delete-in-function-params/input.js Scope children mismatch: after transform: ScopeId(0): [ScopeId(1)] @@ -775,17 +753,6 @@ Symbol scope ID mismatch for "b": after transform: SymbolId(13): ScopeId(5) rebuilt : SymbolId(16): ScopeId(9) -* loose/cast-to-boolean/input.js -Bindings mismatch: -after transform: ScopeId(25): ["_o$a$b$c$non_existent", "_o$a$b$c$non_existent3", "_o$a$b10", "_o$a$b6", "_o$a$b7", "_o$a$b8", "_o$a$b9", "o"] -rebuilt : ScopeId(25): ["_o$a$b$c$non_existent", "_o$a$b$c$non_existent2", "_o$a$b$c$non_existent3", "_o$a$b10", "_o$a$b6", "_o$a$b7", "_o$a$b8", "_o$a$b9", "o"] -Bindings mismatch: -after transform: ScopeId(26): ["_o$a$b$c$non_existent2"] -rebuilt : ScopeId(26): [] -Symbol scope ID mismatch for "_o$a$b$c$non_existent2": -after transform: SymbolId(31): ScopeId(26) -rebuilt : SymbolId(33): ScopeId(25) - * transparent-expr-wrappers/ts-as-function-call-loose/input.ts Unresolved references mismatch: after transform: ["A", "B", "foo"] diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index fc280d7e5ce441..97b97de79a4d4f 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 118/137 +Passed: 120/138 # All Passed: * babel-plugin-transform-class-static-block @@ -16,24 +16,7 @@ Passed: 118/137 * regexp -# babel-plugin-transform-class-properties (19/26) -* interaction-with-other-transforms/input.js -Bindings mismatch: -after transform: ScopeId(0): ["C", "C2", "_ref", "_ref2"] -rebuilt : ScopeId(0): ["C", "C2", "_a", "_e", "_g", "_ref", "_ref2"] -Bindings mismatch: -after transform: ScopeId(1): ["_a", "_e", "_g"] -rebuilt : ScopeId(1): [] -Symbol scope ID mismatch for "_a": -after transform: SymbolId(4): ScopeId(1) -rebuilt : SymbolId(0): ScopeId(0) -Symbol scope ID mismatch for "_e": -after transform: SymbolId(5): ScopeId(1) -rebuilt : SymbolId(1): ScopeId(0) -Symbol scope ID mismatch for "_g": -after transform: SymbolId(6): ScopeId(1) -rebuilt : SymbolId(2): ScopeId(0) - +# babel-plugin-transform-class-properties (20/26) * private-field-resolve-to-method-in-computed-key/input.js x Output mismatch diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/in-nested-block/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/in-nested-block/input.js new file mode 100644 index 00000000000000..3e054e84e0e671 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/in-nested-block/input.js @@ -0,0 +1,3 @@ +{ + x ?? y +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/in-nested-block/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/in-nested-block/output.js new file mode 100644 index 00000000000000..c81778680c3759 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/in-nested-block/output.js @@ -0,0 +1,4 @@ +{ + var _x; + (_x = x) !== null && _x !== void 0 ? _x : y; +} diff --git a/wasm/parser/package.json b/wasm/parser/package.json index 45be7260e5fdb6..6e04776bd7dbf5 100644 --- a/wasm/parser/package.json +++ b/wasm/parser/package.json @@ -1,6 +1,6 @@ { "name": "@oxc-parser/wasm", - "version": "0.42.0", + "version": "0.43.0", "description": "Wasm target for the oxc parser.", "keywords": [ "JavaScript",