Skip to content

Commit

Permalink
feat(codegen): prefer backquotes over double / single quotes (#8839)
Browse files Browse the repository at this point in the history
When thinking about why #8443 made the gzip size increased, I thought it maybe because the backquotes appear more than before the PR. So I changed the codegen to prefer backquotes than double/single quotes. It seems this separate change have a positive effect with some projects.
My guess is that in most projects, back quotes does not appear in string literals than double quotes and single quotes.

The gzipped size slightly increases for `bundle.min.js`, `victory.js`, `antd.js`, but those will be decreased by #8443 except for `bundle.min.js`.
  • Loading branch information
sapphi-red committed Feb 3, 2025
1 parent 84b62c7 commit b4ee617
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 65 deletions.
32 changes: 21 additions & 11 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,7 @@ impl Gen for ImportDeclaration<'_> {
p.print_str("from");
}
p.print_soft_space();
self.source.print(p, ctx);
p.print_quoted_utf16(&self.source.value, false);
if let Some(with_clause) = &self.with_clause {
p.print_soft_space();
with_clause.print(p, ctx);
Expand Down Expand Up @@ -980,16 +980,18 @@ impl Gen for WithClause<'_> {
}

impl Gen for ImportAttribute<'_> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
fn gen(&self, p: &mut Codegen, _ctx: Context) {
match &self.key {
ImportAttributeKey::Identifier(identifier) => {
p.print_str(identifier.name.as_str());
}
ImportAttributeKey::StringLiteral(literal) => literal.print(p, ctx),
ImportAttributeKey::StringLiteral(literal) => {
p.print_quoted_utf16(&literal.value, false);
}
};
p.print_colon();
p.print_soft_space();
self.value.print(p, ctx);
p.print_quoted_utf16(&self.value.value, false);
}
}

Expand Down Expand Up @@ -1055,7 +1057,7 @@ impl Gen for ExportNamedDeclaration<'_> {
p.print_soft_space();
p.print_str("from");
p.print_soft_space();
source.print(p, ctx);
p.print_quoted_utf16(&source.value, false);
}
p.print_semicolon_after_statement();
}
Expand Down Expand Up @@ -1111,7 +1113,7 @@ impl Gen for ModuleExportName<'_> {
match self {
Self::IdentifierName(ident) => ident.print(p, ctx),
Self::IdentifierReference(ident) => ident.print(p, ctx),
Self::StringLiteral(literal) => literal.print(p, ctx),
Self::StringLiteral(literal) => p.print_quoted_utf16(&literal.value, false),
};
}
}
Expand Down Expand Up @@ -1139,7 +1141,7 @@ impl Gen for ExportAllDeclaration<'_> {

p.print_str("from");
p.print_soft_space();
self.source.print(p, ctx);
p.print_quoted_utf16(&self.source.value, false);
if let Some(with_clause) = &self.with_clause {
p.print_hard_space();
with_clause.print(p, ctx);
Expand Down Expand Up @@ -3451,6 +3453,9 @@ impl Gen for TSSignature<'_> {
PropertyKey::PrivateIdentifier(key) => {
p.print_str(key.name.as_str());
}
PropertyKey::StringLiteral(key) => {
p.print_quoted_utf16(&key.value, false);
}
key => {
key.to_expression().print_expr(p, Precedence::Comma, ctx);
}
Expand Down Expand Up @@ -3499,6 +3504,9 @@ impl Gen for TSPropertySignature<'_> {
PropertyKey::PrivateIdentifier(key) => {
p.print_str(key.name.as_str());
}
PropertyKey::StringLiteral(key) => {
p.print_quoted_utf16(&key.value, false);
}
key => {
key.to_expression().print_expr(p, Precedence::Comma, ctx);
}
Expand Down Expand Up @@ -3585,7 +3593,9 @@ impl Gen for TSImportAttributeName<'_> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
match self {
TSImportAttributeName::Identifier(ident) => ident.print(p, ctx),
TSImportAttributeName::StringLiteral(literal) => literal.print(p, ctx),
TSImportAttributeName::StringLiteral(literal) => {
p.print_quoted_utf16(&literal.value, false);
}
}
}
}
Expand Down Expand Up @@ -3689,7 +3699,7 @@ impl Gen for TSModuleDeclarationName<'_> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
match self {
Self::Identifier(ident) => ident.print(p, ctx),
Self::StringLiteral(s) => s.print(p, ctx),
Self::StringLiteral(s) => p.print_quoted_utf16(&s.value, false),
}
}
}
Expand Down Expand Up @@ -3793,7 +3803,7 @@ impl Gen for TSEnumMember<'_> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
match &self.id {
TSEnumMemberName::Identifier(decl) => decl.print(p, ctx),
TSEnumMemberName::String(decl) => decl.print(p, ctx),
TSEnumMemberName::String(decl) => p.print_quoted_utf16(&decl.value, false),
}
if let Some(init) = &self.initializer {
p.print_soft_space();
Expand Down Expand Up @@ -3837,7 +3847,7 @@ impl Gen for TSModuleReference<'_> {
match self {
Self::ExternalModuleReference(decl) => {
p.print_str("require(");
decl.expression.print(p, ctx);
p.print_quoted_utf16(&decl.expression.value, false);
p.print_str(")");
}
match_ts_type_name!(Self) => self.to_ts_type_name().print(p, ctx),
Expand Down
12 changes: 6 additions & 6 deletions crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,13 +612,13 @@ impl<'a> Codegen<'a> {
}
}
let mut quote = b'"';
if double_cost > single_cost {
quote = b'\'';
if single_cost > backtick_cost && allow_backtick {
quote = b'`';
}
} else if double_cost > backtick_cost && allow_backtick {
if allow_backtick && double_cost >= backtick_cost {
quote = b'`';
if backtick_cost > single_cost {
quote = b'\'';
}
} else if double_cost > single_cost {
quote = b'\'';
}
quote
} else {
Expand Down
69 changes: 46 additions & 23 deletions crates/oxc_codegen/tests/integration/snapshots/minify.snap
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,51 @@ function foo<T extends string>(x: T, y: string, ...restOfParams: Omit<T, 'x'>):
return x;
}
----------
function foo<T extends string>(x:T,y:string,...restOfParams:Omit<T,"x">): T{return x}
function foo<T extends string>(x:T,y:string,...restOfParams:Omit<T,`x`>): T{return x}
########## 2
let x: string[] = ['abc', 'def', 'ghi'];
----------
let x:string[]=["abc","def","ghi"];
let x:string[]=[`abc`,`def`,`ghi`];
########## 3
let x: Array<string> = ['abc', 'def', 'ghi',];
----------
let x:Array<string>=["abc","def","ghi"];
let x:Array<string>=[`abc`,`def`,`ghi`];
########## 4
let x: [string, number] = ['abc', 123];
----------
let x:[string,number]=["abc",123];
let x:[string,number]=[`abc`,123];
########## 5
let x: string | number = 'abc';
----------
let x:string|number="abc";
let x:string|number=`abc`;
########## 6
let x: string & number = 'abc';
----------
let x:string&number="abc";
let x:string&number=`abc`;
########## 7
let x: typeof String = 'string';
----------
let x:typeof String="string";
let x:typeof String=`string`;
########## 8
let x: keyof string = 'length';
----------
let x:keyof string="length";
let x:keyof string=`length`;
########## 9
let x: keyof typeof String = 'length';
----------
let x:keyof typeof String="length";
let x:keyof typeof String=`length`;
########## 10
let x: string['length'] = 123;
----------
let x:string["length"]=123;
let x:string[`length`]=123;
########## 11
function isString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error('Not a string');
}
}
----------
function isString(value:unknown): asserts value is string{if(typeof value!=="string"){throw new Error("Not a string")}}
function isString(value:unknown): asserts value is string{if(typeof value!==`string`){throw new Error(`Not a string`)}}
########## 12
import type { Foo } from 'foo';
----------
Expand All @@ -74,7 +74,7 @@ type A<T>={[K in keyof T as K extends string ? B<K> : K]:T[K]};
########## 16
class A {readonly type = 'frame'}
----------
class A{readonly type="frame"}
class A{readonly type=`frame`}
########## 17
let foo: { <T>(t: T): void }
----------
Expand Down Expand Up @@ -104,34 +104,50 @@ abstract class A {private abstract static readonly prop: string}
----------
abstract class A{private abstract static readonly prop:string}
########## 24
interface A { a: string, 'b': number, 'c'(): void }
----------
interface A{a:string;"b":number;"c"():void;}
########## 25
enum A { a, 'b' }
----------
enum A {a,"b",}
########## 26
module 'a'
----------
module "a"
########## 27
declare module 'a'
----------
declare module "a"
########## 28
a = x!;
----------
a=x! ;
########## 25
########## 29
b = (x as y);
----------
b=x as y;
########## 26
########## 30
c = foo<string>;
----------
c=foo<string> ;
########## 27
########## 31
d = x satisfies y;
----------
d=((x) satisfies y);
########## 28
########## 32
export @x declare abstract class C {}
----------
export @x declare abstract class C{}
########## 29
########## 33
div<T>``
----------
div<T>``;
########## 30
########## 34
export type Component<Props = any> = Foo;
----------
export type Component<Props = any>=Foo;
########## 31
########## 35

export type Component<
Props = any,
Expand All @@ -147,19 +163,19 @@ export type Component<

----------
export type Component<Props = any,RawBindings = any,D = any,C extends ComputedOptions = ComputedOptions,M extends MethodOptions = MethodOptions,E extends EmitsOptions|Record<string,any[]> = {},S extends Record<string,any> = any>=ConcreteComponent<Props,RawBindings,D,C,M,E,S>|ComponentPublicInstanceConstructor<Props>;
########## 32
########## 36
(a || b) as any
----------
(a||b) as any;
########## 33
########## 37
(a ** b) as any
----------
(a**b) as any;
########## 34
########## 38
(function g() {}) as any
----------
(function g(){}) as any;
########## 35
########## 39

import defaultExport from "module-name";
import * as name from "module-name";
Expand Down Expand Up @@ -196,3 +212,10 @@ export { default as name16 } from "module-name";

----------
import defaultExport from"module-name";import*as name from"module-name";import{export1}from"module-name";import{export1 as alias1}from"module-name";import{default as alias}from"module-name";import{export1,export2}from"module-name";import{export1,export2 as alias2}from"module-name";import{"string name" as alias}from"module-name";import defaultExport,{export1}from"module-name";import defaultExport,*as name from"module-name";import"module-name";import{}from"mod";export let name1,name2;export const name3=1,name4=2;export function functionName(){}export class ClassName{}export function*generatorFunctionName(){}export const{name5,name2:bar}=o;export const[name6,name7]=array;export{name8,name81};export{variable1 as name9,variable2 as name10,name82};export{variable1 as "string name"};export{name1 as default1};export*from"module-name";export*as name11 from"module-name";export{name12,nameN}from"module-name";export{import1 as name13,import2 as name14,name15}from"module-name";export{default}from"module-name";export{default as name16}from"module-name";
########## 40

import a = require("a");
export import b = require("b");

----------
import a = require("a");export import b = require("b");
Loading

0 comments on commit b4ee617

Please sign in to comment.