mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(minifier): change foo['bar'] -> foo.bar (#8169)
This commit is contained in:
parent
3c5718d68b
commit
29dc0dc070
3 changed files with 65 additions and 39 deletions
|
|
@ -4,6 +4,7 @@ use oxc_ecmascript::{ToInt32, ToJsString};
|
|||
use oxc_semantic::IsGlobalReference;
|
||||
use oxc_span::{GetSpan, SPAN};
|
||||
use oxc_syntax::{
|
||||
identifier::is_identifier_name,
|
||||
number::NumberBase,
|
||||
operator::{BinaryOperator, UnaryOperator},
|
||||
};
|
||||
|
|
@ -86,9 +87,7 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
|
|||
|
||||
// Change syntax
|
||||
match expr {
|
||||
Expression::ArrowFunctionExpression(arrow_expr) => {
|
||||
self.try_compress_arrow_expression(arrow_expr, ctx);
|
||||
}
|
||||
Expression::ArrowFunctionExpression(e) => self.try_compress_arrow_expression(e, ctx),
|
||||
Expression::ChainExpression(e) => self.try_compress_chain_call_expression(e, ctx),
|
||||
Expression::BinaryExpression(e) => self.try_compress_type_of_equal_string(e, ctx),
|
||||
_ => {}
|
||||
|
|
@ -101,12 +100,15 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
|
|||
Expression::AssignmentExpression(e) => Self::try_compress_assignment_expression(e, ctx),
|
||||
Expression::LogicalExpression(e) => Self::try_compress_is_null_or_undefined(e, ctx),
|
||||
Expression::NewExpression(e) => Self::try_fold_new_expression(e, ctx),
|
||||
Expression::TemplateLiteral(t) => Self::try_fold_template_literal(t, ctx),
|
||||
Expression::BinaryExpression(e) => Self::try_compress_typeof_undefined(e, ctx),
|
||||
Expression::ComputedMemberExpression(e) => {
|
||||
self.try_compress_computed_member_expression(e, ctx)
|
||||
}
|
||||
Expression::CallExpression(e) => {
|
||||
Self::try_fold_literal_constructor_call_expression(e, ctx)
|
||||
.or_else(|| Self::try_fold_simple_function_call(e, ctx))
|
||||
}
|
||||
Expression::TemplateLiteral(t) => Self::try_fold_template_literal(t, ctx),
|
||||
Expression::BinaryExpression(e) => Self::try_compress_typeof_undefined(e, ctx),
|
||||
_ => None,
|
||||
} {
|
||||
*expr = folded_expr;
|
||||
|
|
@ -714,7 +716,9 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax {
|
|||
// https://github.com/swc-project/swc/blob/4e2dae558f60a9f5c6d2eac860743e6c0b2ec562/crates/swc_ecma_minifier/src/compress/pure/properties.rs
|
||||
#[allow(clippy::cast_lossless)]
|
||||
fn try_compress_property_key(&mut self, key: &mut PropertyKey<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
use oxc_syntax::identifier::is_identifier_name;
|
||||
if self.in_fixed_loop {
|
||||
return;
|
||||
}
|
||||
let PropertyKey::StringLiteral(s) = key else { return };
|
||||
if match ctx.parent() {
|
||||
Ancestor::ObjectPropertyKey(key) => *key.computed(),
|
||||
|
|
@ -743,6 +747,26 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `foo['bar']` -> `foo.bar`
|
||||
fn try_compress_computed_member_expression(
|
||||
&self,
|
||||
e: &mut ComputedMemberExpression<'a>,
|
||||
ctx: Ctx<'a, 'b>,
|
||||
) -> Option<Expression<'a>> {
|
||||
if self.in_fixed_loop {
|
||||
return None;
|
||||
}
|
||||
let Expression::StringLiteral(s) = &e.expression else { return None };
|
||||
if !is_identifier_name(&s.value) {
|
||||
return None;
|
||||
}
|
||||
let property = ctx.ast.identifier_name(s.span, s.value.clone());
|
||||
let object = ctx.ast.move_expression(&mut e.object);
|
||||
Some(Expression::StaticMemberExpression(
|
||||
ctx.ast.alloc_static_member_expression(e.span, object, property, false),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Port from <https://github.com/google/closure-compiler/blob/v20240609/test/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntaxTest.java>
|
||||
|
|
@ -1240,4 +1264,10 @@ mod test {
|
|||
test("({ '0': _, 'a': _ })", "({ 0: _, a: _ })");
|
||||
test_same("({ '1.1': _, '😊': _, 'a.a': _ })");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_computed_to_member_expression() {
|
||||
test("x['true']", "x.true");
|
||||
test_same("x['😊']");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,25 +3,25 @@ Original | minified | minified | gzip | gzip | Fixture
|
|||
-------------------------------------------------------------------------------------
|
||||
72.14 kB | 23.74 kB | 23.70 kB | 8.61 kB | 8.54 kB | react.development.js
|
||||
|
||||
173.90 kB | 60.16 kB | 59.82 kB | 19.49 kB | 19.33 kB | moment.js
|
||||
173.90 kB | 60.16 kB | 59.82 kB | 19.48 kB | 19.33 kB | moment.js
|
||||
|
||||
287.63 kB | 90.59 kB | 90.07 kB | 32.18 kB | 31.95 kB | jquery.js
|
||||
287.63 kB | 90.70 kB | 90.07 kB | 32.21 kB | 31.95 kB | jquery.js
|
||||
|
||||
342.15 kB | 118.61 kB | 118.14 kB | 44.54 kB | 44.37 kB | vue.js
|
||||
342.15 kB | 118.59 kB | 118.14 kB | 44.53 kB | 44.37 kB | vue.js
|
||||
|
||||
544.10 kB | 72.04 kB | 72.48 kB | 26.18 kB | 26.20 kB | lodash.js
|
||||
544.10 kB | 72.49 kB | 72.48 kB | 26.22 kB | 26.20 kB | lodash.js
|
||||
|
||||
555.77 kB | 273.89 kB | 270.13 kB | 91.18 kB | 90.80 kB | d3.js
|
||||
555.77 kB | 274.10 kB | 270.13 kB | 91.25 kB | 90.80 kB | d3.js
|
||||
|
||||
1.01 MB | 461.09 kB | 458.89 kB | 126.91 kB | 126.71 kB | bundle.min.js
|
||||
1.01 MB | 461.06 kB | 458.89 kB | 126.91 kB | 126.71 kB | bundle.min.js
|
||||
|
||||
1.25 MB | 656.66 kB | 646.76 kB | 164.14 kB | 163.73 kB | three.js
|
||||
1.25 MB | 656.99 kB | 646.76 kB | 164.18 kB | 163.73 kB | three.js
|
||||
|
||||
2.14 MB | 735.26 kB | 724.14 kB | 180.97 kB | 181.07 kB | victory.js
|
||||
2.14 MB | 728.22 kB | 724.14 kB | 180.43 kB | 181.07 kB | victory.js
|
||||
|
||||
3.20 MB | 1.01 MB | 1.01 MB | 332.23 kB | 331.56 kB | echarts.js
|
||||
3.20 MB | 1.01 MB | 1.01 MB | 332.24 kB | 331.56 kB | echarts.js
|
||||
|
||||
6.69 MB | 2.36 MB | 2.31 MB | 494.89 kB | 488.28 kB | antd.js
|
||||
6.69 MB | 2.34 MB | 2.31 MB | 493.51 kB | 488.28 kB | antd.js
|
||||
|
||||
10.95 MB | 3.51 MB | 3.49 MB | 910.90 kB | 915.50 kB | typescript.js
|
||||
10.95 MB | 3.51 MB | 3.49 MB | 910.88 kB | 915.50 kB | typescript.js
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::{
|
|||
path::Path,
|
||||
};
|
||||
|
||||
use cow_utils::CowUtils;
|
||||
use flate2::{write::GzEncoder, Compression};
|
||||
use humansize::{format_size, DECIMAL};
|
||||
use oxc_allocator::Allocator;
|
||||
|
|
@ -16,7 +17,6 @@ use oxc_span::SourceType;
|
|||
use oxc_tasks_common::{project_root, TestFile, TestFiles};
|
||||
use oxc_transformer::{ReplaceGlobalDefines, ReplaceGlobalDefinesConfig};
|
||||
use rustc_hash::FxHashMap;
|
||||
use similar_asserts::assert_eq;
|
||||
|
||||
// #[test]
|
||||
// #[cfg(any(coverage, coverage_nightly))]
|
||||
|
|
@ -24,21 +24,6 @@ use similar_asserts::assert_eq;
|
|||
// run().unwrap();
|
||||
// }
|
||||
|
||||
macro_rules! assert_eq_minified_code {
|
||||
($left:expr, $right:expr, $($arg:tt)*) => {
|
||||
if $left != $right {
|
||||
let normalized_left = $crate::normalize_minified_code($left);
|
||||
let normalized_right = $crate::normalize_minified_code($right);
|
||||
assert_eq!(normalized_left, normalized_right, $($arg)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn normalize_minified_code(code: &str) -> String {
|
||||
use cow_utils::CowUtils;
|
||||
code.cow_replace(";", ";\n").cow_replace(",", ",\n").into_owned()
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
/// # Errors
|
||||
pub fn run() -> Result<(), io::Error> {
|
||||
|
|
@ -147,12 +132,7 @@ fn minify_twice(file: &TestFile) -> String {
|
|||
};
|
||||
let source_text1 = minify(&file.source_text, source_type, options);
|
||||
let source_text2 = minify(&source_text1, source_type, options);
|
||||
assert_eq_minified_code!(
|
||||
&source_text1,
|
||||
&source_text2,
|
||||
"Minification failed for {}",
|
||||
&file.file_name
|
||||
);
|
||||
assert_eq_minified_code(&source_text1, &source_text2, &file.file_name);
|
||||
source_text2
|
||||
}
|
||||
|
||||
|
|
@ -181,3 +161,19 @@ fn gzip_size(s: &str) -> usize {
|
|||
let s = e.finish().unwrap();
|
||||
s.len()
|
||||
}
|
||||
|
||||
fn assert_eq_minified_code(s1: &str, s2: &str, filename: &str) {
|
||||
if s1 != s2 {
|
||||
let normalized_left = normalize_minified_code(s1);
|
||||
let normalized_right = normalize_minified_code(s2);
|
||||
similar_asserts::assert_eq!(
|
||||
normalized_left,
|
||||
normalized_right,
|
||||
"Minification failed for {filename}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_minified_code(code: &str) -> String {
|
||||
code.cow_replace(";", ";\n").cow_replace(",", ",\n").into_owned()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue