diff --git a/Cargo.lock b/Cargo.lock index 4e71313fe71f..e542c2e075e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3225,9 +3225,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "ryu-js" @@ -4896,6 +4896,7 @@ dependencies = [ "once_cell", "rayon", "rustc-hash", + "ryu-js", "stacker", "swc_atoms", "swc_common", diff --git a/crates/swc_ecma_minifier/src/compress/pure/evaluate.rs b/crates/swc_ecma_minifier/src/compress/pure/evaluate.rs index d6f6e1db1477..bd2ee0210c85 100644 --- a/crates/swc_ecma_minifier/src/compress/pure/evaluate.rs +++ b/crates/swc_ecma_minifier/src/compress/pure/evaluate.rs @@ -1,7 +1,7 @@ use radix_fmt::Radix; use swc_common::{util::take::Take, Spanned, SyntaxContext}; use swc_ecma_ast::*; -use swc_ecma_utils::{undefined, ExprExt, IsEmpty, Value}; +use swc_ecma_utils::{number::ToJsString, undefined, ExprExt, IsEmpty, Value}; use super::Pure; use crate::compress::util::{eval_as_number, is_pure_undefined_or_null}; @@ -382,7 +382,7 @@ impl Pure<'_> { if args.first().is_none() { // https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.prototype.toprecision // 2. If precision is undefined, return ! ToString(x). - let value = ryu_js::Buffer::new().format(num.value).to_string().into(); + let value = num.value.to_js_string().into(); self.changed = true; report_change!( @@ -478,7 +478,7 @@ impl Pure<'_> { .map_or(Some(10f64), |arg| eval_as_number(&self.expr_ctx, &arg.expr)) { if base.trunc() == 10. { - let value = ryu_js::Buffer::new().format(num.value).to_string().into(); + let value = num.value.to_js_string().into(); *e = Expr::Lit(Lit::Str(Str { span: e.span(), raw: None, diff --git a/crates/swc_ecma_minifier/tests/exec.rs b/crates/swc_ecma_minifier/tests/exec.rs index 8ac36c3f02e1..58793b984acc 100644 --- a/crates/swc_ecma_minifier/tests/exec.rs +++ b/crates/swc_ecma_minifier/tests/exec.rs @@ -11311,3 +11311,12 @@ fn issue_8982_2() { ", ); } + +#[test] +fn issue_9010() { + run_default_exec_test( + r#" + console.log(-0 + []) + "#, + ); +} diff --git a/crates/swc_ecma_transforms_typescript/src/ts_enum.rs b/crates/swc_ecma_transforms_typescript/src/ts_enum.rs index d6365c4846e4..62825ed7b042 100644 --- a/crates/swc_ecma_transforms_typescript/src/ts_enum.rs +++ b/crates/swc_ecma_transforms_typescript/src/ts_enum.rs @@ -3,7 +3,7 @@ use std::mem; use swc_atoms::JsWord; use swc_common::{collections::AHashMap, Mark, DUMMY_SP}; use swc_ecma_ast::*; -use swc_ecma_utils::{undefined, ExprFactory}; +use swc_ecma_utils::{number::ToJsString, undefined, ExprFactory}; use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -207,14 +207,12 @@ impl<'a> EnumValueComputer<'a> { TsEnumRecordValue::String(format!("{}{}", left, right).into()) } (TsEnumRecordValue::Number(left), TsEnumRecordValue::String(right), op!(bin, "+")) => { - let mut buffer = ryu_js::Buffer::new(); - let left = buffer.format(left); + let left = left.to_js_string(); TsEnumRecordValue::String(format!("{}{}", left, right).into()) } (TsEnumRecordValue::String(left), TsEnumRecordValue::Number(right), op!(bin, "+")) => { - let mut buffer = ryu_js::Buffer::new(); - let right = buffer.format(right); + let right = right.to_js_string(); TsEnumRecordValue::String(format!("{}{}", left, right).into()) } @@ -279,7 +277,7 @@ impl<'a> EnumValueComputer<'a> { let expr = match expr { TsEnumRecordValue::String(s) => s.to_string(), - TsEnumRecordValue::Number(n) => ryu_js::Buffer::new().format(n).to_string(), + TsEnumRecordValue::Number(n) => n.to_js_string(), _ => return opaque_expr, }; diff --git a/crates/swc_ecma_utils/Cargo.toml b/crates/swc_ecma_utils/Cargo.toml index 7416e593d386..7a2c97e1d637 100644 --- a/crates/swc_ecma_utils/Cargo.toml +++ b/crates/swc_ecma_utils/Cargo.toml @@ -25,6 +25,7 @@ num_cpus = { workspace = true } once_cell = { workspace = true } rayon = { workspace = true, optional = true } rustc-hash = { workspace = true } +ryu-js = { workspace = true } tracing = { workspace = true } unicode-id = { workspace = true } diff --git a/crates/swc_ecma_utils/src/lib.rs b/crates/swc_ecma_utils/src/lib.rs index aa1292659b29..f69df5749e2c 100644 --- a/crates/swc_ecma_utils/src/lib.rs +++ b/crates/swc_ecma_utils/src/lib.rs @@ -13,6 +13,7 @@ pub extern crate swc_common; use std::{borrow::Cow, hash::Hash, num::FpCategory, ops::Add}; +use number::ToJsString; use rustc_hash::FxHashMap; use swc_atoms::JsWord; use swc_common::{ @@ -50,6 +51,7 @@ mod value; pub mod var; mod node_ignore_span; +pub mod number; pub mod stack_size; pub use node_ignore_span::NodeIgnoringSpan; @@ -979,7 +981,13 @@ pub trait ExprExt { match *expr { Expr::Lit(ref l) => match *l { Lit::Str(Str { ref value, .. }) => Known(Cow::Borrowed(value)), - Lit::Num(ref n) => Known(format!("{}", n).into()), + Lit::Num(ref n) => { + if n.value == -0.0 { + return Known(Cow::Borrowed("0")); + } + + Known(Cow::Owned(n.value.to_js_string())) + } Lit::Bool(Bool { value: true, .. }) => Known(Cow::Borrowed("true")), Lit::Bool(Bool { value: false, .. }) => Known(Cow::Borrowed("false")), Lit::Null(..) => Known(Cow::Borrowed("null")), diff --git a/crates/swc_ecma_utils/src/number.rs b/crates/swc_ecma_utils/src/number.rs new file mode 100644 index 000000000000..54d0ae892f38 --- /dev/null +++ b/crates/swc_ecma_utils/src/number.rs @@ -0,0 +1,11 @@ +/// +pub trait ToJsString { + fn to_js_string(&self) -> String; +} + +impl ToJsString for f64 { + fn to_js_string(&self) -> String { + let mut buffer = ryu_js::Buffer::new(); + buffer.format(*self).to_string() + } +}