mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
The typescript transform pass is now required to strip typescript syntax for codegen to print things properly. Codegen will now print whatever is in the AST.
385 lines
15 KiB
Rust
385 lines
15 KiB
Rust
use oxc_allocator::Allocator;
|
|
use oxc_codegen::{Codegen, CodegenOptions};
|
|
use oxc_parser::Parser;
|
|
use oxc_span::SourceType;
|
|
|
|
fn test(source_text: &str, expected: &str, options: Option<CodegenOptions>) {
|
|
let allocator = Allocator::default();
|
|
let source_type = SourceType::default().with_module(true);
|
|
let ret = Parser::new(&allocator, source_text, source_type).parse();
|
|
let options = options.unwrap_or_default();
|
|
let result = Codegen::<false>::new("", source_text, ret.trivias, options)
|
|
.build(&ret.program)
|
|
.source_text;
|
|
assert_eq!(expected, result, "for source {source_text}, expect {expected}, got {result}");
|
|
}
|
|
|
|
fn test_ts(source_text: &str, expected: &str, is_typescript_definition: bool) {
|
|
let allocator = Allocator::default();
|
|
let source_type = SourceType::default()
|
|
.with_typescript(true)
|
|
.with_typescript_definition(is_typescript_definition)
|
|
.with_module(true);
|
|
let ret = Parser::new(&allocator, source_text, source_type).parse();
|
|
let codegen_options = CodegenOptions::default();
|
|
let result = Codegen::<false>::new("", source_text, ret.trivias, codegen_options)
|
|
.build(&ret.program)
|
|
.source_text;
|
|
assert_eq!(expected, result, "for source {source_text}, expect {expected}, got {result}");
|
|
}
|
|
|
|
#[test]
|
|
fn string() {
|
|
test("let x = ''", "let x = '';\n", None);
|
|
test(r"let x = '\b'", "let x = '\\b';\n", None);
|
|
test(r"let x = '\f'", "let x = '\\f';\n", None);
|
|
test("let x = '\t'", "let x = '\t';\n", None);
|
|
test(r"let x = '\v'", "let x = '\\v';\n", None);
|
|
test("let x = '\\n'", "let x = '\\n';\n", None);
|
|
test("let x = '\\''", "let x = \"'\";\n", None);
|
|
test("let x = '\\\"'", "let x = '\"';\n", None);
|
|
// test( "let x = '\\'''", "let x = `''`;\n", None);
|
|
test("let x = '\\\\'", "let x = '\\\\';\n", None);
|
|
test("let x = '\x00'", "let x = '\\0';\n", None);
|
|
test("let x = '\x00!'", "let x = '\\0!';\n", None);
|
|
test("let x = '\x001'", "let x = '\\x001';\n", None);
|
|
test("let x = '\\0'", "let x = '\\0';\n", None);
|
|
test("let x = '\\0!'", "let x = '\\0!';\n", None);
|
|
test("let x = '\x07'", "let x = '\\x07';\n", None);
|
|
test("let x = '\x07!'", "let x = '\\x07!';\n", None);
|
|
test("let x = '\x071'", "let x = '\\x071';\n", None);
|
|
test("let x = '\\7'", "let x = '\\x07';\n", None);
|
|
test("let x = '\\7!'", "let x = '\\x07!';\n", None);
|
|
test("let x = '\\01'", "let x = '\x01';\n", None);
|
|
test("let x = '\x10'", "let x = '\x10';\n", None);
|
|
test("let x = '\\x10'", "let x = '\x10';\n", None);
|
|
test("let x = '\x1B'", "let x = '\\x1B';\n", None);
|
|
test("let x = '\\x1B'", "let x = '\\x1B';\n", None);
|
|
test("let x = '\\uABCD'", "let x = 'ꯍ';\n", None);
|
|
// test( "let x = '\\uABCD'", r#"let x = '\uABCD';\n"#, None);
|
|
// test( r#"let x = '\U000123AB'"#, r#"let x = '\U000123AB';\n"#, None);
|
|
// test( "let x = '\\u{123AB}'", r#"let x = '\U000123AB';\n"#, None);
|
|
// test( "let x = '\\uD808\\uDFAB'", r#"let x = '\U000123AB';\n"#, None);
|
|
test("let x = '\\uD808'", "let x = '\\\\ud808';\n", None);
|
|
test("let x = '\\uD808X'", "let x = '\\\\ud808X';\n", None);
|
|
test("let x = '\\uDFAB'", "let x = '\\\\udfab';\n", None);
|
|
test("let x = '\\uDFABX'", "let x = '\\\\udfabX';\n", None);
|
|
|
|
// test( "let x = '\\x80'", r#"let x = '\U00000080';\n"#);
|
|
// test( "let x = '\\xFF'", r#"let x = '\U000000FF';\n"#);
|
|
// test( "let x = '\\xF0\\x9F\\x8D\\x95'", r#"let x = '\U000000F0\U0000009F\U0000008D\U00000095';\n"#);
|
|
// test("let x = '\\uD801\\uDC02\\uDC03\\uD804'", r#"let x = '\U00010402\\uDC03\\uD804';\n"#)
|
|
}
|
|
|
|
#[test]
|
|
fn template() {
|
|
test("let x = `\\0`", "let x = `\\0`;\n", None);
|
|
test("let x = `\\x01`", "let x = `\\x01`;\n", None);
|
|
test("let x = `\\0${0}`", "let x = `\\0${0}`;\n", None);
|
|
// test("let x = `\\x01${0}`", "let x = `\x01${0}`;\n", None);
|
|
test("let x = `${0}\\0`", "let x = `${0}\\0`;\n", None);
|
|
// test("let x = `${0}\\x01`", "let x = `${0}\x01`;\n", None);
|
|
test("let x = `${0}\\0${1}`", "let x = `${0}\\0${1}`;\n", None);
|
|
// test("let x = `${0}\\x01${1}`", "let x = `${0}\x01${1}`;\n", None);
|
|
|
|
test("let x = String.raw`\\1`", "let x = String.raw`\\1`;\n", None);
|
|
test("let x = String.raw`\\x01`", "let x = String.raw`\\x01`;\n", None);
|
|
test("let x = String.raw`\\1${0}`", "let x = String.raw`\\1${0}`;\n", None);
|
|
test("let x = String.raw`\\x01${0}`", "let x = String.raw`\\x01${0}`;\n", None);
|
|
test("let x = String.raw`${0}\\1`", "let x = String.raw`${0}\\1`;\n", None);
|
|
test("let x = String.raw`${0}\\x01`", "let x = String.raw`${0}\\x01`;\n", None);
|
|
test("let x = String.raw`${0}\\1${1}`", "let x = String.raw`${0}\\1${1}`;\n", None);
|
|
test("let x = String.raw`${0}\\x01${1}`", "let x = String.raw`${0}\\x01${1}`;\n", None);
|
|
|
|
test("let x = `${y}`", "let x = `${y}`;\n", None);
|
|
test("let x = `$(y)`", "let x = `$(y)`;\n", None);
|
|
test("let x = `{y}$`", "let x = `{y}$`;\n", None);
|
|
test("let x = `$}y{`", "let x = `$}y{`;\n", None);
|
|
test("let x = `\\${y}`", "let x = `\\${y}`;\n", None);
|
|
// test("let x = `$\\{y}`", "let x = `\\${y}`;\n", None);
|
|
|
|
test("await tag`x`", "await tag`x`;\n", None);
|
|
test("await (tag`x`)", "await tag`x`;\n", None);
|
|
test("(await tag)`x`", "(await tag)`x`;\n", None);
|
|
|
|
test("await tag`${x}`", "await tag`${x}`;\n", None);
|
|
test("await (tag`${x}`)", "await tag`${x}`;\n", None);
|
|
test("(await tag)`${x}`", "(await tag)`${x}`;\n", None);
|
|
|
|
test("new tag`x`", "new tag`x`();\n", None);
|
|
test("new (tag`x`)", "new tag`x`();\n", None);
|
|
test("new tag()`x`", "new tag()`x`;\n", None);
|
|
test("(new tag)`x`", "new tag()`x`;\n", None);
|
|
|
|
test("new tag`${x}`", "new tag`${x}`();\n", None);
|
|
test("new (tag`${x}`)", "new tag`${x}`();\n", None);
|
|
test("new tag()`${x}`", "new tag()`${x}`;\n", None);
|
|
test("(new tag)`${x}`", "new tag()`${x}`;\n", None);
|
|
}
|
|
|
|
#[test]
|
|
fn module_decl() {
|
|
test("export * as foo from 'foo'", "export * as foo from 'foo';\n", None);
|
|
test("import x from './foo.js' with {}", "import x from './foo.js' with {\n};\n", None);
|
|
test("import {} from './foo.js' with {}", "import './foo.js' with {\n};\n", None);
|
|
test("export * from './foo.js' with {}", "export * from './foo.js' with {\n};\n", None);
|
|
}
|
|
|
|
#[test]
|
|
fn new_expr() {
|
|
test("new (foo()).bar();", "new (foo()).bar();\n", None);
|
|
}
|
|
|
|
#[test]
|
|
fn for_stmt() {
|
|
test("for (let x = 0; x < 10; x++) {}", "for (let x = 0; x < 10; x++) {\n}\n", None);
|
|
test("for (;;) {}", "for (;;) {\n}\n", None);
|
|
test("for (let x = 1;;) {}", "for (let x = 1;;) {\n}\n", None);
|
|
test("for (;true;) {}", "for (; true;) {\n}\n", None);
|
|
test("for (;;i++) {}", "for (;; i++) {\n}\n", None);
|
|
|
|
test("for (using x = 1;;) {}", "for (using x = 1;;) {\n}\n", None);
|
|
}
|
|
|
|
#[test]
|
|
fn typescript() {
|
|
test_ts("let x: string = `\\x01`;", "let x: string = `\\x01`;\n", false);
|
|
|
|
test_ts("function foo<T extends string>(x: T, y: string, ...restOfParams: Omit<T, 'x'>): T {\n\treturn x;\n}", "function foo<T extends string>(x: T, y: string, ...restOfParams: Omit<T, 'x'>): T {\n\treturn x;\n}\n", false);
|
|
|
|
test_ts(
|
|
"let x: string[] = ['abc', 'def', 'ghi'];",
|
|
"let x: (string)[] = ['abc', 'def', 'ghi'];\n",
|
|
false,
|
|
);
|
|
test_ts(
|
|
"let x: Array<string> = ['abc', 'def', 'ghi',];",
|
|
"let x: Array<string> = ['abc', 'def', 'ghi',];\n",
|
|
false,
|
|
);
|
|
test_ts(
|
|
"let x: [string, number] = ['abc', 123];",
|
|
"let x: [string, number] = ['abc', 123];\n",
|
|
false,
|
|
);
|
|
test_ts("let x: string | number = 'abc';", "let x: ((string) | (number)) = 'abc';\n", false);
|
|
test_ts("let x: string & number = 'abc';", "let x: ((string) & (number)) = 'abc';\n", false);
|
|
test_ts("let x: typeof String = 'string';", "let x: typeof String = 'string';\n", false);
|
|
test_ts("let x: keyof string = 'length';", "let x: keyof string = 'length';\n", false);
|
|
test_ts(
|
|
"let x: keyof typeof String = 'length';",
|
|
"let x: keyof typeof String = 'length';\n",
|
|
false,
|
|
);
|
|
test_ts("let x: string['length'] = 123;", "let x: string['length'] = 123;\n", false);
|
|
|
|
test_ts("function isString(value: unknown): asserts value is string {\n\tif (typeof value !== 'string') {\n\t\tthrow new Error('Not a string');\n\t}\n}", "function isString(value: unknown): asserts value is string {\n\tif (typeof value !== 'string') {\n\t\tthrow new Error('Not a string');\n\t}\n}\n", false);
|
|
|
|
// type-only imports/exports
|
|
test_ts("import type { Foo } from 'foo';", "import type {Foo} from 'foo';\n", false);
|
|
test_ts("import { Foo, type Bar } from 'foo';", "import {Foo,type Bar} from 'foo';\n", false);
|
|
test_ts("export { Foo, type Bar } from 'foo';", "export { Foo, type Bar } from 'foo';", false);
|
|
}
|
|
|
|
fn test_comment_helper(source_text: &str, expected: &str) {
|
|
test(
|
|
source_text,
|
|
expected,
|
|
Some(CodegenOptions { enable_source_map: true, preserve_annotate_comments: true }),
|
|
);
|
|
}
|
|
#[test]
|
|
fn annotate_comment() {
|
|
test_comment_helper(
|
|
r"
|
|
x([
|
|
/* #__NO_SIDE_EFFECTS__ */ function() {},
|
|
/* #__NO_SIDE_EFFECTS__ */ function y() {},
|
|
/* #__NO_SIDE_EFFECTS__ */ function*() {},
|
|
/* #__NO_SIDE_EFFECTS__ */ function* y() {},
|
|
/* #__NO_SIDE_EFFECTS__ */ async function() {},
|
|
/* #__NO_SIDE_EFFECTS__ */ async function y() {},
|
|
/* #__NO_SIDE_EFFECTS__ */ async function*() {},
|
|
/* #__NO_SIDE_EFFECTS__ */ async function* y() {},
|
|
])
|
|
",
|
|
r"x([/* #__NO_SIDE_EFFECTS__ */ function() {
|
|
}, /* #__NO_SIDE_EFFECTS__ */ function y() {
|
|
}, /* #__NO_SIDE_EFFECTS__ */ function* () {
|
|
}, /* #__NO_SIDE_EFFECTS__ */ function* y() {
|
|
}, /* #__NO_SIDE_EFFECTS__ */ async function() {
|
|
}, /* #__NO_SIDE_EFFECTS__ */ async function y() {
|
|
}, /* #__NO_SIDE_EFFECTS__ */ async function* () {
|
|
}, /* #__NO_SIDE_EFFECTS__ */ async function* y() {
|
|
},]);
|
|
",
|
|
);
|
|
|
|
test_comment_helper(
|
|
r"
|
|
x([
|
|
/* #__NO_SIDE_EFFECTS__ */ y => y,
|
|
/* #__NO_SIDE_EFFECTS__ */ () => {},
|
|
/* #__NO_SIDE_EFFECTS__ */ (y) => (y),
|
|
/* #__NO_SIDE_EFFECTS__ */ async y => y,
|
|
/* #__NO_SIDE_EFFECTS__ */ async () => {},
|
|
/* #__NO_SIDE_EFFECTS__ */ async (y) => (y),
|
|
])",
|
|
r"x([/* #__NO_SIDE_EFFECTS__ */ (y) => y, /* #__NO_SIDE_EFFECTS__ */ () => {
|
|
}, /* #__NO_SIDE_EFFECTS__ */ (y) => y, /* #__NO_SIDE_EFFECTS__ */ async (y) => y, /* #__NO_SIDE_EFFECTS__ */ async () => {
|
|
}, /* #__NO_SIDE_EFFECTS__ */ async (y) => y,]);
|
|
",
|
|
);
|
|
test_comment_helper(
|
|
r"
|
|
x([
|
|
/* #__NO_SIDE_EFFECTS__ */ y => y,
|
|
/* #__NO_SIDE_EFFECTS__ */ () => {},
|
|
/* #__NO_SIDE_EFFECTS__ */ (y) => (y),
|
|
/* #__NO_SIDE_EFFECTS__ */ async y => y,
|
|
/* #__NO_SIDE_EFFECTS__ */ async () => {},
|
|
/* #__NO_SIDE_EFFECTS__ */ async (y) => (y),
|
|
])",
|
|
r"x([/* #__NO_SIDE_EFFECTS__ */ (y) => y, /* #__NO_SIDE_EFFECTS__ */ () => {
|
|
}, /* #__NO_SIDE_EFFECTS__ */ (y) => y, /* #__NO_SIDE_EFFECTS__ */ async (y) => y, /* #__NO_SIDE_EFFECTS__ */ async () => {
|
|
}, /* #__NO_SIDE_EFFECTS__ */ async (y) => y,]);
|
|
",
|
|
);
|
|
//
|
|
test_comment_helper(
|
|
r"
|
|
// #__NO_SIDE_EFFECTS__
|
|
function a() {}
|
|
// #__NO_SIDE_EFFECTS__
|
|
function* b() {}
|
|
// #__NO_SIDE_EFFECTS__
|
|
async function c() {}
|
|
// #__NO_SIDE_EFFECTS__
|
|
async function* d() {}
|
|
",
|
|
r"// #__NO_SIDE_EFFECTS__
|
|
function a() {
|
|
}
|
|
// #__NO_SIDE_EFFECTS__
|
|
function* b() {
|
|
}
|
|
// #__NO_SIDE_EFFECTS__
|
|
async function c() {
|
|
}
|
|
// #__NO_SIDE_EFFECTS__
|
|
async function* d() {
|
|
}
|
|
",
|
|
);
|
|
|
|
test_comment_helper(
|
|
r"
|
|
// #__NO_SIDE_EFFECTS__
|
|
function a() {}
|
|
// #__NO_SIDE_EFFECTS__
|
|
function* b() {}
|
|
// #__NO_SIDE_EFFECTS__
|
|
async function c() {}
|
|
// #__NO_SIDE_EFFECTS__
|
|
async function* d() {}
|
|
",
|
|
r"// #__NO_SIDE_EFFECTS__
|
|
function a() {
|
|
}
|
|
// #__NO_SIDE_EFFECTS__
|
|
function* b() {
|
|
}
|
|
// #__NO_SIDE_EFFECTS__
|
|
async function c() {
|
|
}
|
|
// #__NO_SIDE_EFFECTS__
|
|
async function* d() {
|
|
}
|
|
",
|
|
);
|
|
|
|
test_comment_helper(
|
|
r"
|
|
/* @__NO_SIDE_EFFECTS__ */ export function a() {}
|
|
/* @__NO_SIDE_EFFECTS__ */ export function* b() {}
|
|
/* @__NO_SIDE_EFFECTS__ */ export async function c() {}
|
|
/* @__NO_SIDE_EFFECTS__ */ export async function* d() {} ",
|
|
r"/* @__NO_SIDE_EFFECTS__ */ export function a() {
|
|
}
|
|
/* @__NO_SIDE_EFFECTS__ */ export function* b() {
|
|
}
|
|
/* @__NO_SIDE_EFFECTS__ */ export async function c() {
|
|
}
|
|
/* @__NO_SIDE_EFFECTS__ */ export async function* d() {
|
|
}
|
|
",
|
|
);
|
|
// Only "c0" and "c2" should have "no side effects" (Rollup only respects "const" and only for the first one)
|
|
test_comment_helper(
|
|
r"
|
|
/* #__NO_SIDE_EFFECTS__ */ export var v0 = function() {}, v1 = function() {}
|
|
/* #__NO_SIDE_EFFECTS__ */ export let l0 = function() {}, l1 = function() {}
|
|
/* #__NO_SIDE_EFFECTS__ */ export const c0 = function() {}, c1 = function() {}
|
|
/* #__NO_SIDE_EFFECTS__ */ export var v2 = () => {}, v3 = () => {}
|
|
/* #__NO_SIDE_EFFECTS__ */ export let l2 = () => {}, l3 = () => {}
|
|
/* #__NO_SIDE_EFFECTS__ */ export const c2 = () => {}, c3 = () => {}
|
|
",
|
|
r"export var v0 = function() {
|
|
}, v1 = function() {
|
|
};
|
|
export let l0 = function() {
|
|
}, l1 = function() {
|
|
};
|
|
export const c0 = /* #__NO_SIDE_EFFECTS__ */ function() {
|
|
}, c1 = function() {
|
|
};
|
|
export var v2 = () => {
|
|
}, v3 = () => {
|
|
};
|
|
export let l2 = () => {
|
|
}, l3 = () => {
|
|
};
|
|
export const c2 = /* #__NO_SIDE_EFFECTS__ */ () => {
|
|
}, c3 = () => {
|
|
};
|
|
",
|
|
);
|
|
// Only "c0" and "c2" should have "no side effects" (Rollup only respects "const" and only for the first one)
|
|
test_comment_helper(
|
|
r"
|
|
/* #__NO_SIDE_EFFECTS__ */ var v0 = function() {}, v1 = function() {}
|
|
/* #__NO_SIDE_EFFECTS__ */ let l0 = function() {}, l1 = function() {}
|
|
/* #__NO_SIDE_EFFECTS__ */ const c0 = function() {}, c1 = function() {}
|
|
/* #__NO_SIDE_EFFECTS__ */ var v2 = () => {}, v3 = () => {}
|
|
/* #__NO_SIDE_EFFECTS__ */ let l2 = () => {}, l3 = () => {}
|
|
/* #__NO_SIDE_EFFECTS__ */ const c2 = () => {}, c3 = () => {}
|
|
",
|
|
r"var v0 = function() {
|
|
}, v1 = function() {
|
|
};
|
|
let l0 = function() {
|
|
}, l1 = function() {
|
|
};
|
|
const c0 = /* #__NO_SIDE_EFFECTS__ */ function() {
|
|
}, c1 = function() {
|
|
};
|
|
var v2 = () => {
|
|
}, v3 = () => {
|
|
};
|
|
let l2 = () => {
|
|
}, l3 = () => {
|
|
};
|
|
const c2 = /* #__NO_SIDE_EFFECTS__ */ () => {
|
|
}, c3 = () => {
|
|
};
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn unicode_escape() {
|
|
test("console.log('你好');", "console.log('你好');\n", None);
|
|
test("console.log('こんにちは');", "console.log('こんにちは');\n", None);
|
|
test("console.log('안녕하세요');", "console.log('안녕하세요');\n", None);
|
|
test("console.log('🧑🤝🧑');", "console.log('🧑🤝🧑');\n", None);
|
|
}
|