oxc/crates/oxc_span/src/atom.rs
Don Isaac 027a67d94c
feat(minifier): constant addition expression folding (#882)
Fold constant addition expressions. Handles string concatenation and
addition, both with implicit casting.

For example,
```ts
let x = 1 + 1
let y = "hello " + "world"
```
now becomes
```ts
let x = 2
let y = "hello world"
```

## Extra Goodies
- test(minifier): add `test_snapshot` helper to perform snapshot tests
with `insta`
- up(hir): implement `std::ops::Add` for `NumericValue`
- up(span): impl `TryFrom<Cow<'_, &str>>` for `Atom`
2023-09-11 10:38:35 +08:00

112 lines
2.6 KiB
Rust

use std::{
borrow::{Borrow, Cow},
fmt,
ops::Deref,
};
use compact_str::CompactString;
#[cfg(feature = "serde")]
use serde::Serialize;
/// Newtype for [`CompactString`]
#[derive(Clone, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Atom(CompactString);
const BASE54_CHARS: &[u8; 64] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
impl Atom {
pub const fn new_inline(s: &str) -> Self {
Self(CompactString::new_inline(s))
}
#[inline]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
/// Get the shortest mangled name for a given n.
/// Code adapted from [terser](https://github.com/terser/terser/blob/8b966d687395ab493d2c6286cc9dd38650324c11/lib/scope.js#L1041-L1051)
pub fn base54(n: usize) -> Self {
let mut num = n;
// Base 54 at first because these are the usable first characters in JavaScript identifiers
// <https://tc39.es/ecma262/#prod-IdentifierStart>
let base = 54usize;
let mut ret = CompactString::default();
ret.push(BASE54_CHARS[num % base] as char);
num /= base;
// Base 64 for the rest because after the first character we can also use 0-9 too
// <https://tc39.es/ecma262/#prod-IdentifierPart>
let base = 64usize;
while num > 0 {
num -= 1;
ret.push(BASE54_CHARS[num % base] as char);
num /= base;
}
Self(ret)
}
}
impl Deref for Atom {
type Target = str;
fn deref(&self) -> &Self::Target {
self.0.as_str()
}
}
impl<'a> From<&'a str> for Atom {
fn from(s: &'a str) -> Self {
Self(s.into())
}
}
impl From<String> for Atom {
fn from(s: String) -> Self {
Self(s.into())
}
}
impl From<Cow<'_, str>> for Atom {
fn from(s: Cow<'_, str>) -> Self {
Self(s.into())
}
}
impl AsRef<str> for Atom {
#[inline]
fn as_ref(&self) -> &str {
self.0.as_str()
}
}
impl Borrow<str> for Atom {
#[inline]
fn borrow(&self) -> &str {
self.0.as_str()
}
}
impl<T: AsRef<str>> PartialEq<T> for Atom {
fn eq(&self, other: &T) -> bool {
self.0.as_str() == other.as_ref()
}
}
impl PartialEq<Atom> for &str {
fn eq(&self, other: &Atom) -> bool {
*self == other.0.as_str()
}
}
impl fmt::Debug for Atom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.0.as_str(), f)
}
}
impl fmt::Display for Atom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.0.as_str(), f)
}
}