From c0e2fef0af80152d8525dd26312abdeb5d6e2a4e Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Sun, 6 Oct 2024 23:08:09 +0000 Subject: [PATCH] refactor(traverse): function to get var name from node (#6317) Pure refactor. Separate out the logic for creating a var name from an AST node into its own function, so it can be used standalone, outside of `generate_uid_based_on_node`. Apart from the new `get_var_name_from_node` function, and changing return type of `to_identifier` to `String`, the only other changes are moving files around. --- .../ast_operations/gather_node_parts.rs | 22 +++++++ .../src/ast_operations/identifier.rs | 62 +++++++++++++++++++ crates/oxc_traverse/src/ast_operations/mod.rs | 4 ++ .../src/context/ast_operations/mod.rs | 2 - crates/oxc_traverse/src/context/identifier.rs | 58 ----------------- crates/oxc_traverse/src/context/mod.rs | 20 ++---- crates/oxc_traverse/src/lib.rs | 1 + 7 files changed, 95 insertions(+), 74 deletions(-) rename crates/oxc_traverse/src/{context => }/ast_operations/gather_node_parts.rs (96%) create mode 100644 crates/oxc_traverse/src/ast_operations/identifier.rs create mode 100644 crates/oxc_traverse/src/ast_operations/mod.rs delete mode 100644 crates/oxc_traverse/src/context/ast_operations/mod.rs delete mode 100644 crates/oxc_traverse/src/context/identifier.rs diff --git a/crates/oxc_traverse/src/context/ast_operations/gather_node_parts.rs b/crates/oxc_traverse/src/ast_operations/gather_node_parts.rs similarity index 96% rename from crates/oxc_traverse/src/context/ast_operations/gather_node_parts.rs rename to crates/oxc_traverse/src/ast_operations/gather_node_parts.rs index bf528effb..6074b9754 100644 --- a/crates/oxc_traverse/src/context/ast_operations/gather_node_parts.rs +++ b/crates/oxc_traverse/src/ast_operations/gather_node_parts.rs @@ -8,6 +8,28 @@ use oxc_ast::ast::*; use oxc_ast::syntax_directed_operations::BoundNames; +use super::to_identifier; + +pub fn get_var_name_from_node<'a, N: GatherNodeParts<'a>>(node: &N) -> String { + let mut name = String::new(); + node.gather(&mut |mut part| { + if name.is_empty() { + part = part.trim_start_matches('_'); + } else { + name.push('$'); + } + name.push_str(part); + }); + + if name.is_empty() { + name = "ref".to_string(); + } else { + name.truncate(20); + } + + to_identifier(name) +} + pub trait GatherNodeParts<'a> { fn gather(&self, f: &mut F); } diff --git a/crates/oxc_traverse/src/ast_operations/identifier.rs b/crates/oxc_traverse/src/ast_operations/identifier.rs new file mode 100644 index 000000000..6a2f2b098 --- /dev/null +++ b/crates/oxc_traverse/src/ast_operations/identifier.rs @@ -0,0 +1,62 @@ +use oxc_syntax::identifier::{is_identifier_name, is_identifier_part, is_identifier_start}; + +/// Convert a String to a valid identifier name. +/// +/// Based on Babel's [`toIdentifier`] function. +/// +/// [`toIdentifier`]: https://github.com/babel/babel/blob/3bcfee232506a4cebe410f02042fb0f0adeeb0b1/packages/babel-types/src/converters/toIdentifier.ts#L4-L26 +pub fn to_identifier(input: String) -> String { + if is_identifier_name(&input) { + return input; + } + + let mut name = String::with_capacity(input.len()); + + let mut capitalize_next = false; + + let mut chars = input.chars(); + if let Some(first) = chars.next() { + if is_identifier_start(first) { + name.push(first); + } else { + capitalize_next = true; + } + } + + for c in chars { + if !is_identifier_part(c) { + capitalize_next = true; + } else if capitalize_next { + name.push(c.to_ascii_uppercase()); + capitalize_next = false; + } else { + name.push(c); + } + } + + if name.is_empty() { + return "_".to_string(); + } + + name +} + +#[test] +fn test() { + let cases = &[ + ("foo", "foo"), + ("fooBar", "fooBar"), + ("fooBar1", "fooBar1"), + ("foo-bar", "fooBar"), + ("foo bar", "fooBar"), + ("foo-bar-1", "fooBar1"), + ("1foo-bar", "FooBar"), + ("1-foo-bar", "FooBar"), + ("-- --", "_"), + ("_output$headers$x-amzn-requestid", "_output$headers$xAmznRequestid"), + ]; + + for &(input, expected) in cases { + assert_eq!(to_identifier(input.to_string()), expected); + } +} diff --git a/crates/oxc_traverse/src/ast_operations/mod.rs b/crates/oxc_traverse/src/ast_operations/mod.rs new file mode 100644 index 000000000..ff903a9a8 --- /dev/null +++ b/crates/oxc_traverse/src/ast_operations/mod.rs @@ -0,0 +1,4 @@ +mod gather_node_parts; +pub use self::gather_node_parts::get_var_name_from_node; +mod identifier; +pub use identifier::to_identifier; diff --git a/crates/oxc_traverse/src/context/ast_operations/mod.rs b/crates/oxc_traverse/src/context/ast_operations/mod.rs deleted file mode 100644 index 07efc6cff..000000000 --- a/crates/oxc_traverse/src/context/ast_operations/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod gather_node_parts; -pub use self::gather_node_parts::GatherNodeParts; diff --git a/crates/oxc_traverse/src/context/identifier.rs b/crates/oxc_traverse/src/context/identifier.rs deleted file mode 100644 index a325548b3..000000000 --- a/crates/oxc_traverse/src/context/identifier.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::borrow::Cow; - -use oxc_syntax::identifier::{is_identifier_name, is_identifier_part, is_identifier_start}; - -/// Convert a str to a valid identifier name. -/// -/// Based on Babel's [`toIdentifier`](https://github.com/babel/babel/blob/3bcfee232506a4cebe410f02042fb0f0adeeb0b1/packages/babel-types/src/converters/toIdentifier.ts#L4-L26) function. -pub fn to_identifier(input: &str) -> Cow { - if is_identifier_name(input) { - return Cow::Borrowed(input); - } - - let mut name = String::with_capacity(input.len()); - - let mut capitalize_next = false; - - let mut chars = input.chars(); - if let Some(first) = chars.next() { - if is_identifier_start(first) { - name.push(first); - } else { - capitalize_next = true; - } - } - - for c in chars { - if !is_identifier_part(c) { - capitalize_next = true; - } else if capitalize_next { - name.push(c.to_ascii_uppercase()); - capitalize_next = false; - } else { - name.push(c); - } - } - - if name.is_empty() { - return Cow::Borrowed("_"); - } - - Cow::Owned(name) -} - -#[test] -fn test() { - assert_eq!(to_identifier("foo"), "foo"); - assert_eq!(to_identifier("fooBar"), "fooBar"); - assert_eq!(to_identifier("fooBar1"), "fooBar1"); - - assert_eq!(to_identifier("foo-bar"), "fooBar"); - assert_eq!(to_identifier("foo bar"), "fooBar"); - assert_eq!(to_identifier("foo-bar-1"), "fooBar1"); - assert_eq!(to_identifier("1foo-bar"), "FooBar"); - assert_eq!(to_identifier("1-foo-bar"), "FooBar"); - assert_eq!(to_identifier("-- --"), "_"); - - assert_eq!(to_identifier("_output$headers$x-amzn-requestid"), "_output$headers$xAmznRequestid"); -} diff --git a/crates/oxc_traverse/src/context/mod.rs b/crates/oxc_traverse/src/context/mod.rs index 333a99e4b..31587038a 100644 --- a/crates/oxc_traverse/src/context/mod.rs +++ b/crates/oxc_traverse/src/context/mod.rs @@ -11,17 +11,16 @@ use oxc_syntax::{ symbol::{SymbolFlags, SymbolId}, }; -use crate::ancestor::{Ancestor, AncestorType}; +use crate::{ + ancestor::{Ancestor, AncestorType}, + ast_operations::get_var_name_from_node, +}; mod ancestry; -mod ast_operations; -use ast_operations::GatherNodeParts; mod bound_identifier; use ancestry::PopToken; pub use ancestry::TraverseAncestry; pub use bound_identifier::BoundIdentifier; -mod identifier; -use identifier::to_identifier; mod scoping; pub use scoping::TraverseScoping; @@ -359,15 +358,8 @@ impl<'a> TraverseCtx<'a> { scope_id: ScopeId, flags: SymbolFlags, ) -> BoundIdentifier<'a> { - let mut parts = String::new(); - node.gather(&mut |part| { - if !parts.is_empty() { - parts.push('$'); - } - parts.push_str(part); - }); - let name = if parts.is_empty() { "ref" } else { parts.trim_start_matches('_') }; - self.generate_uid(&to_identifier(name.get(..20).unwrap_or(name)), scope_id, flags) + let name = get_var_name_from_node(node); + self.generate_uid(&name, scope_id, flags) } /// Generate UID in current scope based on node. diff --git a/crates/oxc_traverse/src/lib.rs b/crates/oxc_traverse/src/lib.rs index 209fe2ac8..ade032ca9 100644 --- a/crates/oxc_traverse/src/lib.rs +++ b/crates/oxc_traverse/src/lib.rs @@ -64,6 +64,7 @@ use oxc_allocator::Allocator; use oxc_ast::ast::Program; use oxc_semantic::{ScopeTree, SymbolTable}; +pub mod ast_operations; mod context; pub use context::{BoundIdentifier, TraverseAncestry, TraverseCtx, TraverseScoping};