Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions crates/oxc_codegen/examples/codegen.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![allow(clippy::print_stdout)]
use std::{env, path::Path};
use std::path::Path;

use oxc_allocator::Allocator;
use oxc_codegen::{CodeGenerator, CodegenOptions};
Expand All @@ -8,14 +8,16 @@ use oxc_span::SourceType;
use pico_args::Arguments;

// Instruction:
// 1. create a `test.js`
// 2. run `cargo run -p oxc_codegen --example codegen` or `just example codegen`
// create a `test.js`,
// run `cargo run -p oxc_codegen --example codegen`
// or `cargo watch -x "run -p oxc_codegen --example codegen"`

fn main() -> std::io::Result<()> {
let mut args = Arguments::from_env();
let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string());

let twice = args.contains("--twice");
let minify = args.contains("--minify");
let name = args.free_from_str().unwrap_or_else(|_| "test.js".to_string());

let path = Path::new(&name);
let source_text = std::fs::read_to_string(path)?;
Expand Down
63 changes: 46 additions & 17 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -726,12 +726,14 @@ impl Gen for ImportDeclaration<'_> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
p.add_source_mapping(self.span);
p.print_indent();
p.print_str("import ");
p.print_space_before_identifier();
p.print_str("import");
if self.import_kind.is_type() {
p.print_str("type ");
p.print_str(" type");
}
if let Some(specifiers) = &self.specifiers {
if specifiers.is_empty() {
p.print_soft_space();
p.print_str("{}");
p.print_soft_space();
p.print_str("from");
Expand All @@ -755,23 +757,33 @@ impl Gen for ImportDeclaration<'_> {
p.print_soft_space();
p.print_str("},");
in_block = false;
} else if index != 0 {
} else if index == 0 {
p.print_hard_space();
} else {
p.print_comma();
p.print_soft_space();
}
spec.local.print(p, ctx);
if index == specifiers.len() - 1 {
p.print_hard_space();
}
}
ImportDeclarationSpecifier::ImportNamespaceSpecifier(spec) => {
if in_block {
p.print_soft_space();
p.print_str("},");
in_block = false;
} else if index != 0 {
} else if index == 0 {
p.print_soft_space();
} else {
p.print_comma();
p.print_soft_space();
}
p.print_str("* as ");
p.print_ascii_byte(b'*');
p.print_soft_space();
p.print_str("as ");
spec.local.print(p, ctx);
p.print_hard_space();
}
ImportDeclarationSpecifier::ImportSpecifier(spec) => {
if in_block {
Expand All @@ -780,9 +792,9 @@ impl Gen for ImportDeclaration<'_> {
} else {
if index != 0 {
p.print_comma();
p.print_soft_space();
}
in_block = true;
p.print_soft_space();
p.print_ascii_byte(b'{');
p.print_soft_space();
}
Expand All @@ -804,12 +816,14 @@ impl Gen for ImportDeclaration<'_> {
if in_block {
p.print_soft_space();
p.print_ascii_byte(b'}');
p.print_soft_space();
}
p.print_str(" from ");
p.print_str("from");
}
p.print_soft_space();
self.source.print(p, ctx);
if let Some(with_clause) = &self.with_clause {
p.print_hard_space();
p.print_soft_space();
with_clause.print(p, ctx);
}
p.add_source_mapping_end(self.span);
Expand All @@ -822,9 +836,15 @@ impl Gen for WithClause<'_> {
p.add_source_mapping(self.span);
self.attributes_keyword.print(p, ctx);
p.print_soft_space();
p.print_block_start(self.span);
p.print_sequence(&self.with_entries, ctx);
p.print_block_end(self.span);
p.add_source_mapping(self.span);
p.print_ascii_byte(b'{');
if !self.with_entries.is_empty() {
p.print_soft_space();
p.print_list(&self.with_entries, ctx);
p.print_soft_space();
}
p.add_source_mapping_end(self.span);
p.print_ascii_byte(b'}');
}
}

Expand Down Expand Up @@ -864,11 +884,12 @@ impl Gen for ExportNamedDeclaration<'_> {
_ => {}
};
}
p.print_str("export ");
p.print_str("export");
if self.export_kind.is_type() {
p.print_str("type ");
p.print_str(" type ");
}
if let Some(decl) = &self.declaration {
p.print_hard_space();
match decl {
Declaration::VariableDeclaration(decl) => decl.print(p, ctx),
Declaration::FunctionDeclaration(decl) => decl.print(p, ctx),
Expand All @@ -891,6 +912,7 @@ impl Gen for ExportNamedDeclaration<'_> {
p.needs_semicolon = false;
}
} else {
p.print_soft_space();
p.print_ascii_byte(b'{');
if !self.specifiers.is_empty() {
p.print_soft_space();
Expand Down Expand Up @@ -969,18 +991,25 @@ impl Gen for ExportAllDeclaration<'_> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
p.add_source_mapping(self.span);
p.print_indent();
p.print_str("export ");
p.print_str("export");
if self.export_kind.is_type() {
p.print_str("type ");
p.print_str(" type ");
} else {
p.print_soft_space();
}
p.print_ascii_byte(b'*');

if let Some(exported) = &self.exported {
p.print_str(" as ");
p.print_soft_space();
p.print_str("as ");
exported.print(p, ctx);
p.print_hard_space();
} else {
p.print_soft_space();
}

p.print_str(" from ");
p.print_str("from");
p.print_soft_space();
self.source.print(p, ctx);
if let Some(with_clause) = &self.with_clause {
p.print_hard_space();
Expand Down
7 changes: 0 additions & 7 deletions crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,13 +382,6 @@ impl<'a> Codegen<'a> {
self.print_ascii_byte(b'=');
}

fn print_sequence<T: Gen>(&mut self, items: &[T], ctx: Context) {
for item in items {
item.print(self, ctx);
self.print_comma();
}
}

fn print_curly_braces<F: FnOnce(&mut Self)>(&mut self, span: Span, single_line: bool, op: F) {
self.add_source_mapping(span);
self.print_ascii_byte(b'{');
Expand Down
199 changes: 199 additions & 0 deletions crates/oxc_codegen/tests/integration/snapshots/minify.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
---
source: crates/oxc_codegen/tests/integration/main.rs
snapshot_kind: text
---
########## 0
let x: string = `\x01`;
----------
let x:string=`\x01`;
########## 1
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'];
########## 3
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];
########## 5
let x: string | number = 'abc';
----------
let x:string|number='abc';
########## 6
let x: string & number = 'abc';
----------
let x:string&number='abc';
########## 7
let x: typeof String = 'string';
----------
let x:typeof String='string';
########## 8
let x: keyof string = 'length';
----------
let x:keyof string='length';
########## 9
let x: keyof typeof String = 'length';
----------
let x:keyof typeof String='length';
########## 10
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')}}
########## 12
import type { Foo } from 'foo';
----------
import type{Foo}from'foo';
########## 13
import { Foo, type Bar } from 'foo';
----------
import{Foo,type Bar}from'foo';
########## 14
export { Foo, type Bar } from 'foo';
----------
export{Foo,type Bar}from'foo';
########## 15
type A<T> = { [K in keyof T as K extends string ? B<K> : K ]: T[K] }
----------
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'}
########## 17
let foo: { <T>(t: T): void }
----------
let foo:{<T>(t:T):void};
########## 18
let foo: { new <T>(t: T): void }
----------
let foo:{new <T>(t:T):void};
########## 19
function <const T>(){}
----------
function<const T>(){}
########## 20
class A {m?(): void}
----------
class A{m?():void;}
########## 21
class A {constructor(public readonly a: number) {}}
----------
class A{constructor(public readonly a:number){}}
########## 22
abstract class A {private abstract static m() {}}
----------
abstract class A{private abstract static m(){}}
########## 23
abstract class A {private abstract static readonly prop: string}
----------
abstract class A{private abstract static readonly prop:string}
########## 24
a = x!;
----------
a=x! ;
########## 25
b = (x as y);
----------
b=x as y;
########## 26
c = foo<string>;
----------
c=foo<string> ;
########## 27
d = x satisfies y;
----------
d=((x) satisfies y);
########## 28
export @x declare abstract class C {}
----------
export @x declare abstract class C{}
########## 29
div<T>``
----------
div<T>``;
########## 30
export type Component<Props = any> = Foo;
----------
export type Component<Props = any>=Foo;
########## 31

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>

----------
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
(a || b) as any
----------
(a||b) as any;
########## 33
(a ** b) as any
----------
(a**b) as any;
########## 34
(function g() {}) as any
----------
(function g(){}) as any;
########## 35

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/*, … */; // also var
export const name3 = 1, name4 = 2/*, … */; // also var, let
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";

----------
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';
Loading