diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 5edbe14f73fed..9e8cd4194e8a1 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -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); @@ -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); } } @@ -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(); } @@ -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), }; } } @@ -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); @@ -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); } @@ -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); } @@ -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); + } } } } @@ -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), } } } @@ -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(); @@ -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), diff --git a/crates/oxc_codegen/src/lib.rs b/crates/oxc_codegen/src/lib.rs index dc00fa3e82766..5101c8fe7d596 100644 --- a/crates/oxc_codegen/src/lib.rs +++ b/crates/oxc_codegen/src/lib.rs @@ -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 { diff --git a/crates/oxc_codegen/tests/integration/snapshots/minify.snap b/crates/oxc_codegen/tests/integration/snapshots/minify.snap index d6ef98ab1a54a..c15eb2163df80 100644 --- a/crates/oxc_codegen/tests/integration/snapshots/minify.snap +++ b/crates/oxc_codegen/tests/integration/snapshots/minify.snap @@ -10,43 +10,43 @@ function foo(x: T, y: string, ...restOfParams: Omit): return x; } ---------- -function foo(x:T,y:string,...restOfParams:Omit): T{return x} +function foo(x:T,y:string,...restOfParams:Omit): 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 = ['abc', 'def', 'ghi',]; ---------- -let x:Array=["abc","def","ghi"]; +let x:Array=[`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') { @@ -54,7 +54,7 @@ function isString(value: unknown): asserts value is 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'; ---------- @@ -74,7 +74,7 @@ type A={[K in keyof T as K extends string ? B : K]:T[K]}; ########## 16 class A {readonly type = 'frame'} ---------- -class A{readonly type="frame"} +class A{readonly type=`frame`} ########## 17 let foo: { (t: T): void } ---------- @@ -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; ---------- c=foo ; -########## 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`` ---------- div``; -########## 30 +########## 34 export type Component = Foo; ---------- export type Component=Foo; -########## 31 +########## 35 export type Component< Props = any, @@ -147,19 +163,19 @@ export type Component< ---------- export type Component = {},S extends Record = any>=ConcreteComponent|ComponentPublicInstanceConstructor; -########## 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"; @@ -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"); diff --git a/crates/oxc_codegen/tests/integration/snapshots/ts.snap b/crates/oxc_codegen/tests/integration/snapshots/ts.snap index e9b95683e23a7..3fe8b318658cb 100644 --- a/crates/oxc_codegen/tests/integration/snapshots/ts.snap +++ b/crates/oxc_codegen/tests/integration/snapshots/ts.snap @@ -156,41 +156,68 @@ abstract class A { } ########## 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; ---------- c = foo; -########## 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`` ---------- div``; -########## 30 +########## 34 export type Component = Foo; ---------- export type Component = Foo; -########## 31 +########## 35 export type Component< Props = any, @@ -215,22 +242,22 @@ export type Component< S extends Record = any > = ConcreteComponent | ComponentPublicInstanceConstructor; -########## 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"; @@ -295,3 +322,12 @@ 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'); diff --git a/crates/oxc_codegen/tests/integration/ts.rs b/crates/oxc_codegen/tests/integration/ts.rs index 1d9daa5857c72..ff7f06f344b7c 100644 --- a/crates/oxc_codegen/tests/integration/ts.rs +++ b/crates/oxc_codegen/tests/integration/ts.rs @@ -33,6 +33,10 @@ fn ts() { "class A {constructor(public readonly a: number) {}}", "abstract class A {private abstract static m() {}}", "abstract class A {private abstract static readonly prop: string}", + "interface A { a: string, 'b': number, 'c'(): void }", + "enum A { a, 'b' }", + "module 'a'", + "declare module 'a'", "a = x!;", "b = (x as y);", "c = foo;", @@ -89,6 +93,10 @@ 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"; +"#, + r#" +import a = require("a"); +export import b = require("b"); "#, ]; diff --git a/crates/oxc_codegen/tests/integration/unit.rs b/crates/oxc_codegen/tests/integration/unit.rs index 884ab3c38f226..c7673fe7b3e97 100644 --- a/crates/oxc_codegen/tests/integration/unit.rs +++ b/crates/oxc_codegen/tests/integration/unit.rs @@ -17,6 +17,10 @@ fn module_decl() { test("export * from './foo.js' with {}", "export * from \"./foo.js\" with {};\n"); test_minify("export { '☿' } from 'mod';", "export{\"☿\"}from\"mod\";"); test_minify("export { '☿' as '☿' } from 'mod';", "export{\"☿\"}from\"mod\";"); + test_minify( + "import x from './foo.custom' with { 'type': 'json' }", + "import x from\"./foo.custom\"with{\"type\":\"json\"};", + ); } #[test] @@ -27,20 +31,20 @@ fn expr() { test("1000000000000000128.0.toFixed(0)", "0xde0b6b3a7640080.toFixed(0);\n"); test_minify("1000000000000000128.0.toFixed(0)", "0xde0b6b3a7640080.toFixed(0);"); - test_minify("throw 'foo'", "throw\"foo\";"); - test_minify("return 'foo'", "return\"foo\";"); + test_minify("throw 'foo'", "throw`foo`;"); + test_minify("return 'foo'", "return`foo`;"); test_minify("return class {}", "return class{};"); test_minify("return async function foo() {}", "return async function foo(){};"); test_minify_same("return super();"); test_minify_same("return new.target;"); test_minify_same("throw await 1;"); - test_minify_same("await import(\"\");"); + test_minify_same("await import(``);"); test("delete 2e308", "delete (0, Infinity);\n"); test_minify("delete 2e308", "delete(1/0);"); test_minify_same(r#"({"http://a\r\" \n<'b:b@c\r\nd/e?f":{}});"#); - test_minify_same("new(import(\"\"),function(){});"); + test_minify_same("new(import(``),function(){});"); } #[test] diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 3c615bd4e2a0b..32f8eb3e910e3 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -1,27 +1,27 @@ | Oxc | ESBuild | Oxc | ESBuild | Original | minified | minified | gzip | gzip | Fixture ------------------------------------------------------------------------------------- -72.14 kB | 23.56 kB | 23.70 kB | 8.51 kB | 8.54 kB | react.development.js +72.14 kB | 23.56 kB | 23.70 kB | 8.50 kB | 8.54 kB | react.development.js -173.90 kB | 59.55 kB | 59.82 kB | 19.19 kB | 19.33 kB | moment.js +173.90 kB | 59.55 kB | 59.82 kB | 19.18 kB | 19.33 kB | moment.js 287.63 kB | 89.47 kB | 90.07 kB | 30.97 kB | 31.95 kB | jquery.js -342.15 kB | 117.67 kB | 118.14 kB | 43.57 kB | 44.37 kB | vue.js +342.15 kB | 117.67 kB | 118.14 kB | 43.48 kB | 44.37 kB | vue.js -544.10 kB | 71.42 kB | 72.48 kB | 25.87 kB | 26.20 kB | lodash.js +544.10 kB | 71.42 kB | 72.48 kB | 25.86 kB | 26.20 kB | lodash.js 555.77 kB | 271.24 kB | 270.13 kB | 88.34 kB | 90.80 kB | d3.js -1.01 MB | 440.93 kB | 458.89 kB | 122.50 kB | 126.71 kB | bundle.min.js +1.01 MB | 440.93 kB | 458.89 kB | 122.53 kB | 126.71 kB | bundle.min.js -1.25 MB | 650.33 kB | 646.76 kB | 161.01 kB | 163.73 kB | three.js +1.25 MB | 650.33 kB | 646.76 kB | 160.96 kB | 163.73 kB | three.js -2.14 MB | 718.69 kB | 724.14 kB | 162.14 kB | 181.07 kB | victory.js +2.14 MB | 718.69 kB | 724.14 kB | 162.18 kB | 181.07 kB | victory.js -3.20 MB | 1.01 MB | 1.01 MB | 324.45 kB | 331.56 kB | echarts.js +3.20 MB | 1.01 MB | 1.01 MB | 324.41 kB | 331.56 kB | echarts.js -6.69 MB | 2.30 MB | 2.31 MB | 468.52 kB | 488.28 kB | antd.js +6.69 MB | 2.30 MB | 2.31 MB | 468.57 kB | 488.28 kB | antd.js -10.95 MB | 3.36 MB | 3.49 MB | 862.12 kB | 915.50 kB | typescript.js +10.95 MB | 3.36 MB | 3.49 MB | 862.07 kB | 915.50 kB | typescript.js