Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1 @@
{"files":{"Cargo.toml":"c792fa311eaaa5981edb2dddd53b88e6577f390d2a47250ec0c67f0c8c79892a","src/comments.rs":"4931d7fdc3bbcac52ac5b530c6974630e0acd6cd2dee7c0389f1d28f89afa562","src/config.rs":"12e9b1cfe927907da786c4124a2f380aab44ce206df0845a9af421653654cf19","src/decl.rs":"63bc819dacce9f8c20f051c4862550e0197260fc61bc96a712caa0810fc04453","src/expr.rs":"9b52500e35c35048ad0c29717e56891d2dd4c87abe1220b77f1b8b90b270c96a","src/jsx.rs":"c67587582f813b2d5b94d1067b50564014a619c608c7b39a0d5031d50a9103d1","src/lib.rs":"372473f65bbb62525a41c7335c70d7ee1543d245f8efb7bae60aff8db6237daa","src/macros.rs":"f4d438e50332966a217ecc6ca58b7f1bc518035baf9ecf741d7ec962fea1b5de","src/stmt.rs":"349ed30de02e3e081543231372d5a456455d0bb29b4c711f42b094e6e0d15766","src/tests.rs":"7f1b79942f73a2f03d1890126883aad542b869ee2e8312daba49fc809f688cd1","src/text_writer.rs":"3c8a4e28f98ba19c86d5de1b604ef9dc628bc9a107204326bae228bc6a9c4941","src/text_writer/basic_impl.rs":"94a4f31cae7d2a0b7eccdaeffa0dc650ad86c155113bb49d7a9db87f8bc1f5cf","src/text_writer/semicolon.rs":"6b419e0192599593e855c53fda6c31e6e88fc7cf49d561eda394e96aa4f8b401","src/typescript.rs":"ed72c8fab0b0b19a2258f87e9808225b21f4ffc70d16de60828fc01ebd8d02a6","src/util.rs":"5e97909bf31c2c7641868e01e8c14e44cdd6bbf5ca1d930c09e1895f750c4afd"},"package":"8e62ba2c0ed1f119fc1a76542d007f1b2c12854d54dea15f5491363227debe11"}

View file

@ -0,0 +1,97 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "swc_ecma_codegen"
version = "0.144.1"
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
include = [
"Cargo.toml",
"src/**/*.rs",
]
description = "Ecmascript code generator for the swc project."
documentation = "https://rustdoc.swc.rs/swc_ecma_codegen/"
license = "Apache-2.0"
repository = "https://github.com/swc-project/swc.git"
[lib]
bench = false
[[bench]]
name = "bench"
harness = false
[[bench]]
name = "with_parse"
harness = false
[dependencies.memchr]
version = "2.6.1"
[dependencies.num-bigint]
version = "0.4"
features = ["serde"]
[dependencies.once_cell]
version = "1.18.0"
[dependencies.rustc-hash]
version = "1.1.0"
[dependencies.serde]
version = "1.0.127"
[dependencies.sourcemap]
version = "6.2"
[dependencies.swc_atoms]
version = "0.5.9"
[dependencies.swc_common]
version = "0.32.0"
[dependencies.swc_ecma_ast]
version = "0.109.0"
[dependencies.swc_ecma_codegen_macros]
version = "0.7.3"
[dependencies.tracing]
version = "0.1.37"
[dev-dependencies.base64]
version = "0.13"
[dev-dependencies.criterion]
version = "0.5"
[dev-dependencies.serde_json]
version = "1"
[dev-dependencies.swc_common]
version = "0.32.0"
features = ["sourcemap"]
[dev-dependencies.swc_ecma_parser]
version = "0.139.0"
[dev-dependencies.swc_ecma_testing]
version = "0.21.0"
[dev-dependencies.swc_node_base]
version = "0.5.8"
[dev-dependencies.testing]
version = "0.34.0"
[features]
serde-impl = ["swc_ecma_ast/serde"]

View file

@ -0,0 +1,104 @@
use swc_common::comments::CommentKind;
use super::*;
macro_rules! write_comments {
($e:expr, $prefix_space:expr, $cmts:expr) => {{
let cmts = match $cmts {
Some(v) => v,
None => return Ok(()),
};
for cmt in cmts.iter() {
match cmt.kind {
CommentKind::Line => {
if $prefix_space && !$e.cfg.minify {
$e.wr.write_comment(" ")?;
}
srcmap!($e, cmt, true);
$e.wr.write_comment("//")?;
$e.wr.write_comment(&cmt.text)?;
srcmap!($e, cmt, false);
$e.wr.write_line()?;
}
CommentKind::Block => {
if $prefix_space && !$e.cfg.minify {
$e.wr.write_comment(" ")?;
}
srcmap!($e, cmt, true);
$e.wr.write_comment("/*")?;
$e.wr.write_comment(&cmt.text)?;
{
let hi = cmt.span_hi();
if !hi.is_dummy() && hi.0 > 2 {
$e.wr.add_srcmap(hi - swc_common::BytePos(2))?;
}
}
$e.wr.write_comment("*/")?;
if !$e.cfg.minify {
$e.wr.write_space()?;
}
}
}
}
return Ok(());
}};
}
impl<'a, W, S: SourceMapper> Emitter<'a, W, S>
where
W: WriteJs,
S: SourceMapperExt,
{
pub(super) fn emit_trailing_comments_of_pos(
&mut self,
pos: BytePos,
prefix_space: bool,
_is_hi: bool,
) -> Result {
if pos.is_dummy() {
return Ok(());
}
let comments = match self.comments {
Some(ref comments) => comments,
None => return Ok(()),
};
let cmts = comments.take_trailing(pos);
write_comments!(self, prefix_space, &cmts)
}
pub(super) fn emit_leading_comments(&mut self, mut pos: BytePos, is_hi: bool) -> Result {
if pos.is_dummy() {
return Ok(());
}
let comments = match self.comments {
Some(ref comments) => comments,
None => return Ok(()),
};
if is_hi {
pos = pos - BytePos(1)
}
write_comments!(self, false, comments.take_leading(pos))
}
#[inline(always)]
pub(super) fn emit_leading_comments_of_span(&mut self, span: Span, is_hi: bool) -> Result {
let pos = if is_hi { span.hi } else { span.lo };
self.emit_leading_comments(pos, is_hi)
}
}

View file

@ -0,0 +1,46 @@
#[cfg(feature = "serde-impl")]
use serde::{Deserialize, Serialize};
use swc_ecma_ast::EsVersion;
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde-impl", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde-impl", serde(rename_all = "camelCase"))]
pub struct Config {
/// The target runtime environment.
///
/// This defaults to [EsVersion::latest] because it preserves input as much
/// as possible.
///
/// Note: This does not verifies if output is valid for the target runtime.
/// e.g. `const foo = 1;` with [EsVersion::Es3] will emitted as `const foo =
/// 1` without verification.
/// This is because it's not a concern of the code generator.
#[cfg_attr(feature = "serde-impl", serde(default = "EsVersion::latest"))]
pub target: EsVersion,
/// Forces the code generator to use only ascii characters.
///
/// This is useful for environments that do not support unicode.
#[cfg_attr(feature = "serde-impl", serde(default))]
pub ascii_only: bool,
#[cfg_attr(feature = "serde-impl", serde(default))]
pub minify: bool,
/// If true, the code generator will emit the lastest semicolon.
///
/// Defaults to `false`.
#[cfg_attr(feature = "serde-impl", serde(default))]
pub omit_last_semi: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
target: EsVersion::latest(),
minify: false,
ascii_only: false,
omit_last_semi: false,
}
}
}

View file

@ -0,0 +1,211 @@
use swc_common::{SourceMapper, Spanned};
use swc_ecma_ast::*;
use swc_ecma_codegen_macros::emitter;
use super::{Emitter, Result};
use crate::text_writer::WriteJs;
impl<'a, W, S: SourceMapper> Emitter<'a, W, S>
where
W: WriteJs,
S: SourceMapperExt,
{
#[emitter]
fn emit_decl(&mut self, node: &Decl) -> Result {
match node {
Decl::Class(ref n) => emit!(n),
Decl::Fn(ref n) => emit!(n),
Decl::Var(ref n) => {
self.emit_var_decl_inner(n)?;
formatting_semi!();
srcmap!(n, false);
}
Decl::Using(n) => emit!(n),
Decl::TsEnum(ref n) => emit!(n),
Decl::TsInterface(ref n) => emit!(n),
Decl::TsModule(ref n) => emit!(n),
Decl::TsTypeAlias(ref n) => emit!(n),
}
}
#[emitter]
fn emit_class_decl(&mut self, node: &ClassDecl) -> Result {
self.emit_class_decl_inner(node, false)?;
}
#[emitter]
fn emit_using_decl(&mut self, node: &UsingDecl) -> Result {
self.emit_leading_comments_of_span(node.span(), false)?;
if node.is_await {
keyword!("await");
space!();
}
keyword!("using");
space!();
self.emit_list(
node.span,
Some(&node.decls),
ListFormat::VariableDeclarationList,
)?;
}
pub(super) fn emit_class_decl_inner(
&mut self,
node: &ClassDecl,
skip_decorators: bool,
) -> Result {
self.emit_leading_comments_of_span(node.span(), false)?;
srcmap!(self, node, true);
if node.declare {
keyword!(self, "declare");
space!(self);
}
if !skip_decorators {
for dec in &node.class.decorators {
emit!(self, dec);
}
}
keyword!(self, "class");
space!(self);
emit!(self, node.ident);
emit!(self, node.class.type_params);
self.emit_class_trailing(&node.class)?;
Ok(())
}
#[emitter]
fn emit_fn_decl(&mut self, node: &FnDecl) -> Result {
self.emit_leading_comments_of_span(node.span(), false)?;
self.wr.commit_pending_semi()?;
srcmap!(node, true);
if node.declare {
keyword!("declare");
space!();
}
if node.function.is_async {
keyword!("async");
space!();
}
keyword!("function");
if node.function.is_generator {
punct!("*");
formatting_space!();
} else {
space!();
}
emit!(node.ident);
self.emit_fn_trailing(&node.function)?;
}
#[emitter]
fn emit_var_decl(&mut self, node: &VarDecl) -> Result {
self.emit_var_decl_inner(node)?;
}
fn emit_var_decl_inner(&mut self, node: &VarDecl) -> Result {
self.emit_leading_comments_of_span(node.span, false)?;
self.wr.commit_pending_semi()?;
srcmap!(self, node, true);
if node.declare {
keyword!(self, "declare");
space!(self);
}
keyword!(self, node.kind.as_str());
let starts_with_ident = match node.decls.first() {
Some(VarDeclarator {
name: Pat::Array(..) | Pat::Rest(..) | Pat::Object(..),
..
}) => false,
_ => true,
};
if starts_with_ident {
space!(self);
} else {
formatting_space!(self);
}
self.emit_list(
node.span(),
Some(&node.decls),
ListFormat::VariableDeclarationList,
)?;
Ok(())
}
#[emitter]
fn emit_var_declarator(&mut self, node: &VarDeclarator) -> Result {
self.emit_leading_comments_of_span(node.span(), false)?;
srcmap!(node, true);
emit!(node.name);
if let Some(ref init) = node.init {
formatting_space!();
punct!("=");
formatting_space!();
emit!(init);
}
}
}
#[cfg(test)]
mod tests {
use crate::tests::assert_min;
#[test]
fn issue_275() {
assert_min(
"function* foo(){
yield getServiceHosts()
}",
"function*foo(){yield getServiceHosts()}",
);
}
#[test]
fn issue_1764() {
assert_min(
"class Hoge {};
class HogeFuga extends Hoge {};",
"class Hoge{};class HogeFuga extends Hoge{};",
);
}
#[test]
fn single_argument_arrow_expression() {
assert_min("function* f(){ yield x => x}", "function*f(){yield x=>x}");
assert_min(
"function* f(){ yield ({x}) => x}",
"function*f(){yield({x})=>x}",
);
}
#[test]
fn class_static_block() {
assert_min("class Foo { static { 1 + 1; }}", "class Foo{static{1+1}}");
}
}

View file

@ -0,0 +1,205 @@
/// Copied from [ratel][]
///
/// [ratel]:https://github.com/ratel-rust/ratel-core
#[cfg(test)]
mod tests {
use crate::tests::assert_min;
#[test]
fn values() {
assert_min("null", "null");
assert_min("undefined", "undefined");
assert_min("true", "true");
assert_min("false", "false");
assert_min("42", "42");
assert_min("3.14", "3.14");
assert_min(r#" 'foobar' "#, r#""foobar""#);
}
#[test]
fn bin_expr() {
assert_min("1+2+3+4+5", "1+2+3+4+5");
}
#[test]
fn template_expression() {
assert_min("``", "``");
assert_min("foo``", "foo``");
assert_min("`foobar`", "`foobar`");
assert_min("foo`bar`", "foo`bar`");
assert_min("`foo${ 10 }bar${ 20 }baz`", "`foo${10}bar${20}baz`");
assert_min("foo`bar${ 10 }baz`", "foo`bar${10}baz`");
assert_min("foo`${ 10 }`", "foo`${10}`");
}
#[test]
fn sequence_expression() {
assert_min("foo, bar, baz;", "foo,bar,baz");
assert_min("1, 2, 3;", "1,2,3");
assert_min("1,2,3+4;", "1,2,3+4");
assert_min("1+2,3,4;", "1+2,3,4");
assert_min("1+(2,3,4);", "1+(2,3,4)");
assert_min("(1,2,3)+4;", "(1,2,3)+4");
}
#[test]
fn binary_expression() {
assert_min("a = 10", "a=10");
assert_min("a == 10", "a==10");
assert_min("a === 10", "a===10");
assert_min("a != 10", "a!=10");
assert_min("a !== 10", "a!==10");
assert_min("a += 10", "a+=10");
assert_min("a -= 10", "a-=10");
assert_min("a <<= 10", "a<<=10");
assert_min("a >>= 10", "a>>=10");
assert_min("a >>>= 10", "a>>>=10");
assert_min("2 + 2", "2+2");
assert_min("2 - 2", "2-2");
assert_min("2 * 2", "2*2");
assert_min("2 / 2", "2/2");
assert_min("2 % 2", "2%2");
assert_min("2 ** 2", "2**2");
assert_min("2 << 2", "2<<2");
assert_min("2 >> 2", "2>>2");
assert_min("2 >>> 2", "2>>>2");
assert_min("foo in bar", "foo in bar");
assert_min("foo instanceof Foo", "foo instanceof Foo");
}
#[test]
fn prefix_expression() {
assert_min("+foo", "+foo");
assert_min("-foo", "-foo");
assert_min("!foo", "!foo");
assert_min("~foo", "~foo");
assert_min("++foo", "++foo");
assert_min("--foo", "--foo");
assert_min("new foo", "new foo");
assert_min("new foo()", "new foo");
assert_min("new foo().bar()", "new foo().bar()");
assert_min("void foo", "void foo");
assert_min("typeof foo", "typeof foo");
}
#[test]
fn postfix_expression() {
assert_min("foo++", "foo++");
assert_min("foo--", "foo--");
}
#[test]
fn conditional_expression() {
assert_min("true ? foo : bar", "true?foo:bar")
}
#[test]
fn function_expression() {
assert_min("(function () {})", "(function(){})");
assert_min("(function foo() {})", "(function foo(){})");
}
#[test]
#[ignore]
fn class_expression() {
assert_min("(class {})", "(class{})");
assert_min("(class Foo {})", "(class Foo{})");
assert_min("(class extends Foo {})", "(class extends Foo{})");
assert_min("(class Foo extends Bar {})", "(class Foo extends Bar{})");
}
#[test]
fn call_expression() {
assert_min("foobar();", "foobar()");
assert_min("foobar(1, 2, 3);", "foobar(1,2,3)");
}
#[test]
fn member_expression() {
assert_min("foo.bar", "foo.bar");
assert_min("this.bar", "this.bar");
assert_min("10..fooz", "10..fooz");
assert_min("foo[10]", "foo[10]");
assert_min(r#"foo["bar"]"#, r#"foo["bar"]"#);
}
#[test]
fn array_expression() {
assert_min("[]", "[]");
assert_min("[foo]", "[foo]");
assert_min("[foo,bar]", "[foo,bar]");
assert_min("[foo,bar,baz,]", "[foo,bar,baz]");
}
#[test]
fn array_spread() {
assert_min("[...foo,...bar]", "[...foo,...bar]");
}
#[test]
fn sparse_array_expression() {
assert_min("[]", "[]");
assert_min("[,]", "[,]");
assert_min("[,1]", "[,1]");
assert_min("[,,];", "[,,]");
assert_min("[1,,];", "[1,,]");
assert_min("[,,1];", "[,,1]");
}
// #[test]
// fn sparse_array_expression_pretty() {
// assert_pretty("[]", "[];");
// assert_pretty("[,]", "[, ];");
// assert_pretty("[1,]", "[1, ];");
// assert_pretty("[,1]", "[, 1];");
// assert_pretty("[,,];", "[, , ];");
// assert_pretty("[1,,];", "[1, , ];");
// assert_pretty("[,,1];", "[, , 1];");
// }
#[test]
fn object_expression() {
assert_min("({});", "({})");
assert_min("({ foo });", "({foo})");
assert_min("({ foo: 10 });", "({foo:10})");
assert_min("({ foo, bar });", "({foo,bar})");
assert_min("({ foo: 10, bar: 20 });", "({foo:10,bar:20})");
assert_min("({ foo: 10, bar() {} });", "({foo:10,bar(){}})");
assert_min("({ foo(bar, baz) {} });", "({foo(bar,baz){}})");
// let expected = "({\n foo: true,\n bar: false\n});";
// assert_pretty("({ foo: true, bar: false })", expected);
}
#[test]
fn binding_power() {
assert_min("1 + 2 * 3;", "1+2*3");
assert_min("1 + 2 * 3;", "1+2*3");
assert_min("(1 + 2) * 3;", "(1+2)*3");
assert_min(
"(denominator / divider * 100).toFixed(2);",
"(denominator/divider*100).toFixed(2)",
);
assert_min("(1 + 1)[0];", "(1+1)[0]");
assert_min("2 * 2 / 2;", "2*2/2");
assert_min("2 * (2 / 2);", "2*(2/2)");
assert_min("2 * 2 / 2;", "2*2/2");
}
#[test]
fn regression_increments() {
assert_min("x++ + ++y", "x+++ ++y");
assert_min("x++ - ++y", "x++-++y");
}
#[test]
fn bigint_property_key() {
assert_min("({ 1n: 2 });", "({1n:2})");
assert_min("(class C { 1n = 1 });", "(class C{1n=1})");
assert_min("(class C { 1n () { } });", "(class C{1n(){}})");
}
#[test]
fn html_comment() {
assert_min("a < !--b && c-- > d;", "a< !--b&&c-- >d");
}
}

View file

@ -0,0 +1,194 @@
use swc_common::{SourceMapper, Spanned};
use swc_ecma_ast::*;
use swc_ecma_codegen_macros::emitter;
use super::{Emitter, Result};
use crate::text_writer::WriteJs;
impl<'a, W, S: SourceMapper> Emitter<'a, W, S>
where
W: WriteJs,
S: SourceMapperExt,
{
#[emitter]
fn emit_jsx_element(&mut self, node: &JSXElement) -> Result {
emit!(node.opening);
self.emit_list(
node.span(),
Some(&node.children),
ListFormat::JsxElementOrFragmentChildren,
)?;
if let Some(ref closing) = node.closing {
emit!(closing)
}
}
#[emitter]
fn emit_jsx_opening_element(&mut self, node: &JSXOpeningElement) -> Result {
punct!("<");
emit!(node.name);
if let Some(type_args) = &node.type_args {
emit!(type_args);
}
if !node.attrs.is_empty() {
space!();
self.emit_list(
node.span(),
Some(&node.attrs),
ListFormat::JsxElementAttributes,
)?;
}
if node.self_closing {
punct!("/");
}
punct!(">");
}
#[emitter]
fn emit_jsx_element_name(&mut self, node: &JSXElementName) -> Result {
match *node {
JSXElementName::Ident(ref n) => emit!(n),
JSXElementName::JSXMemberExpr(ref n) => emit!(n),
JSXElementName::JSXNamespacedName(ref n) => emit!(n),
}
}
#[emitter]
fn emit_jsx_attr(&mut self, node: &JSXAttr) -> Result {
emit!(node.name);
if let Some(ref value) = node.value {
punct!("=");
emit!(value);
}
}
#[emitter]
fn emit_jsx_attr_value(&mut self, node: &JSXAttrValue) -> Result {
match *node {
JSXAttrValue::Lit(ref n) => emit!(n),
JSXAttrValue::JSXExprContainer(ref n) => emit!(n),
JSXAttrValue::JSXElement(ref n) => emit!(n),
JSXAttrValue::JSXFragment(ref n) => emit!(n),
}
}
#[emitter]
fn emit_jsx_attr_name(&mut self, node: &JSXAttrName) -> Result {
match *node {
JSXAttrName::Ident(ref n) => emit!(n),
JSXAttrName::JSXNamespacedName(ref n) => emit!(n),
}
}
#[emitter]
fn emit_jsx_attr_or_spread(&mut self, node: &JSXAttrOrSpread) -> Result {
match *node {
JSXAttrOrSpread::JSXAttr(ref n) => emit!(n),
JSXAttrOrSpread::SpreadElement(ref n) => {
punct!("{");
emit!(n);
punct!("}");
}
}
}
#[emitter]
fn emit_jsx_element_child(&mut self, node: &JSXElementChild) -> Result {
match *node {
JSXElementChild::JSXElement(ref n) => emit!(n),
JSXElementChild::JSXExprContainer(ref n) => emit!(n),
JSXElementChild::JSXFragment(ref n) => emit!(n),
JSXElementChild::JSXSpreadChild(ref n) => emit!(n),
JSXElementChild::JSXText(ref n) => emit!(n),
}
}
#[emitter]
fn emit_jsx_spread_child(&mut self, node: &JSXSpreadChild) -> Result {
punct!("{");
punct!("...");
emit!(node.expr);
punct!("}");
}
#[emitter]
fn emit_jsx_expr_container(&mut self, node: &JSXExprContainer) -> Result {
punct!("{");
emit!(node.expr);
punct!("}");
}
#[emitter]
fn emit_jsx_expr(&mut self, node: &JSXExpr) -> Result {
match *node {
JSXExpr::Expr(ref n) => emit!(n),
JSXExpr::JSXEmptyExpr(ref n) => emit!(n),
}
}
#[emitter]
fn emit_jsx_closing_element(&mut self, node: &JSXClosingElement) -> Result {
punct!("</");
emit!(node.name);
punct!(">");
}
#[emitter]
fn emit_jsx_fragment(&mut self, node: &JSXFragment) -> Result {
emit!(node.opening);
self.emit_list(
node.span(),
Some(&node.children),
ListFormat::JsxElementOrFragmentChildren,
)?;
emit!(node.closing);
}
#[emitter]
fn emit_jsx_opening_fragment(&mut self, _: &JSXOpeningFragment) -> Result {
punct!("<>")
}
#[emitter]
fn emit_jsx_closing_fragment(&mut self, _: &JSXClosingFragment) -> Result {
punct!("</>")
}
#[emitter]
fn emit_jsx_namespaced_name(&mut self, node: &JSXNamespacedName) -> Result {
emit!(node.ns);
punct!(":");
emit!(node.name);
}
#[emitter]
fn emit_jsx_empty_expr(&mut self, _: &JSXEmptyExpr) -> Result {}
#[emitter]
fn emit_jsx_text(&mut self, node: &JSXText) -> Result {
self.emit_atom(node.span(), &node.value)?;
}
#[emitter]
fn emit_jsx_member_expr(&mut self, node: &JSXMemberExpr) -> Result {
emit!(node.obj);
punct!(".");
emit!(node.prop);
}
#[emitter]
fn emit_jsx_object(&mut self, node: &JSXObject) -> Result {
match *node {
JSXObject::Ident(ref n) => emit!(n),
JSXObject::JSXMemberExpr(ref n) => emit!(n),
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,148 @@
#![allow(unused)]
macro_rules! opt_leading_space {
($emitter:expr, $e:expr) => {
if let Some(ref e) = $e {
formatting_space!($emitter);
emit!($emitter, e);
}
};
}
macro_rules! opt {
($emitter:expr, $e:expr) => {{
if let Some(ref expr) = $e {
emit!($emitter, expr);
}
}};
($emitter:expr, $e:expr,) => {{
opt!($emitter, $e)
}};
}
macro_rules! emit {
($emitter:expr, $e:expr) => {{
crate::Node::emit_with(&$e, $emitter)?;
}};
}
macro_rules! keyword {
($emitter:expr, $span:expr, $s:expr) => {
$emitter.wr.write_keyword(Some($span), $s)?
};
($emitter:expr, $s:expr) => {
$emitter.wr.write_keyword(None, $s)?
};
}
macro_rules! punct {
($emitter:expr, $sp:expr, ";") => {
$emitter.wr.write_semi(Some($sp))?;
};
($emitter:expr, $sp:expr, $s:expr) => {
$emitter.wr.write_punct(Some($sp), $s)?;
};
($emitter:expr, ";") => {
$emitter.wr.write_semi(None)?
};
($emitter:expr, $s:expr) => {
$emitter.wr.write_punct(None, $s)?
};
}
macro_rules! operator {
($emitter:expr, $sp:expr, $s:expr) => {
$emitter.wr.write_operator(Some($sp), $s)?;
};
($emitter:expr, $s:expr) => {
$emitter.wr.write_operator(None, $s)?;
};
}
macro_rules! space {
($emitter:expr) => {
$emitter.wr.write_space()?
};
($emitter:expr,) => {
space!($emitter)
};
}
macro_rules! formatting_space {
($emitter:expr) => {
if !$emitter.cfg.minify {
$emitter.wr.write_space()?;
}
};
($emitter:expr,) => {
formatting_space!($emitter)
};
}
/// This macro *may* emit a semicolon, if it's required in this context.
macro_rules! formatting_semi {
($emitter:expr) => {
punct!($emitter, ";")
};
($emitter:expr, ) => {
punct!($emitter, ";")
};
}
/// This macro *always* emits a semicolon, as it's required by the structure we
/// emit.
macro_rules! semi {
($emitter:expr, $sp:expr) => {
$emitter.wr.write_semi(Some($sp))?;
};
($emitter:expr) => {
$emitter.wr.write_semi(None)?;
};
}
///
/// - `srcmap!(true)` for start (span.lo)
/// - `srcmap!(false)` for end (span.hi)
macro_rules! srcmap {
($emitter:expr, $n:expr, true) => {{
#[cfg(debug_assertions)]
let _span = tracing::span!(
tracing::Level::ERROR,
"srcmap",
file = file!(),
line = line!()
)
.entered();
let lo = $n.span_lo();
if !lo.is_dummy() {
$emitter.wr.add_srcmap(lo)?;
}
}};
($emitter:expr, $n:expr, false) => {
srcmap!($emitter, $n, false, false)
};
($emitter:expr, $n:expr, false, $subtract:expr) => {
#[cfg(debug_assertions)]
let _span = tracing::span!(
tracing::Level::ERROR,
"srcmap",
file = file!(),
line = line!()
)
.entered();
let hi = $n.span_hi();
if !hi.is_dummy() {
if $subtract {
// hi is exclusive
$emitter.wr.add_srcmap(hi - swc_common::BytePos(1))?;
} else {
// TODO(kdy1): Remove this branch.
$emitter.wr.add_srcmap(hi)?;
}
}
};
}

View file

@ -0,0 +1,154 @@
/// Copied from [ratel][]
///
/// [ratel]:https://github.com/ratel-rust/ratel-core
#[cfg(test)]
mod tests {
use crate::tests::{assert_min, assert_pretty};
#[test]
fn block_statement() {
assert_min("{}", "{}");
assert_min("{foo;}", "{foo}");
}
#[test]
fn empty_block_statement() {
assert_pretty("{\n}", "{}");
assert_pretty("{\n//todo\n}", "{\n//todo\n}");
assert_pretty(
"try {\n\n} catch {\n // Pass\n}\n",
"try {} catch {\n// Pass\n}",
);
}
#[test]
fn empty_object_lit() {
assert_pretty("Object.assign({\n}, a, b);", "Object.assign({}, a, b);");
}
#[test]
fn labeled_statement() {
assert_min("foo: {}", "foo:{}");
assert_min("foo: bar;", "foo:bar");
}
#[test]
fn function_statement() {
assert_min("function foo() {}", "function foo(){}");
}
#[test]
fn declaration_statement() {
assert_min("var foo;", "var foo");
assert_min("let foo;", "let foo");
assert_min("var foo = 10;", "var foo=10");
assert_min("let foo = 10;", "let foo=10");
assert_min("const foo = 10;", "const foo=10");
assert_min("var foo, bar;", "var foo,bar");
assert_min("let foo, bar;", "let foo,bar");
assert_min("var foo = 10, bar = 20;", "var foo=10,bar=20");
assert_min("let foo = 10, bar = 20;", "let foo=10,bar=20");
assert_min("const foo = 10, bar = 20;", "const foo=10,bar=20");
assert_min("const a = {...foo};", "const a={...foo}");
}
#[test]
fn if_statement() {
assert_min("if (true) foo;", "if(true)foo");
assert_min("if (true) { foo; }", "if(true){foo}");
assert_min("if (true) foo; else bar;", "if(true)foo;else bar");
assert_min("if (true) { foo; } else { bar; }", "if(true){foo}else{bar}");
assert_min("if (true) foo; else { bar; }", "if(true)foo;else{bar}");
assert_min("if (true) { foo; } else bar;", "if(true){foo}else bar");
assert_min("if (true) y(); else x++;", "if(true)y();else x++");
assert_min("if (true) y(); else x--;", "if(true)y();else x--");
}
#[test]
fn while_statement() {
assert_min("while (true) foo;", "while(true)foo");
assert_min("while (true) { foo; }", "while(true){foo}");
}
#[test]
fn do_statement() {
assert_min("do { foo; } while (true)", "do{foo}while(true)");
assert_min("do foo; while (true)", "do foo;while(true)");
}
#[test]
fn for_statement() {
assert_min("for (var i = 0; i < 10; i++) {}", "for(var i=0;i<10;i++){}");
assert_min("for (i = 0; i < 10; i++) {}", "for(i=0;i<10;i++){}");
assert_min("for (;;) {}", "for(;;){}");
assert_min("for (foo in bar){}", "for(foo in bar){}");
assert_min("for (let foo in bar){}", "for(let foo in bar){}");
assert_min("for (foo of bar){}", "for(foo of bar){}");
assert_min("for (let foo of bar){}", "for(let foo of bar){}");
}
#[test]
fn for_statement_pretty() {
assert_pretty(
"for (var i = 0; i < 10; i++) {}",
"for(var i = 0; i < 10; i++){}",
);
assert_pretty("for (i = 0; i < 10; i++) {}", "for(i = 0; i < 10; i++){}");
assert_pretty("for (;;) {}", "for(;;){}");
assert_pretty("for (foo in bar){}", "for(foo in bar){}");
assert_pretty("for (let foo in bar){}", "for(let foo in bar){}");
assert_pretty("for (foo of bar){}", "for (foo of bar){}");
assert_pretty("for (let foo of bar){}", "for (let foo of bar){}");
}
#[test]
fn import() {
assert_min(
"import colors, { color } from 'patterns/colors';",
"import colors,{color}from\"patterns/colors\"",
);
assert_pretty(
"import colors, { color } from 'patterns/colors';",
"import colors, { color } from 'patterns/colors';",
);
}
#[test]
fn issue_204_01() {
assert_min(r"'\r\n';", r#""\r\n""#);
}
#[test]
fn issue_204_02() {
assert_min(r"const a = fn() + '\r\n';", r#"const a=fn()+"\r\n""#);
}
#[test]
fn issue_177() {
assert_min(
"#!/usr/bin/env node
let x = 4;",
"#!/usr/bin/env node
let x=4",
);
}
#[test]
fn issue_197() {
assert_pretty(
"// type Foo = 'Oops';
const Link = 'Boo';",
"// type Foo = 'Oops';
const Link = 'Boo';",
);
}
#[test]
fn issue_266() {
assert_min(
"'Q' + +x1 + ',' + +y1 + ',' + (this._x1 = +x) + ',' + (this._y1 = +y);",
"\"Q\"+ +x1+\",\"+ +y1+\",\"+(this._x1=+x)+\",\"+(this._y1=+y)",
);
}
}

View file

@ -0,0 +1,891 @@
use std::{
fmt::Debug,
io::Write,
sync::{Arc, RwLock},
};
use swc_common::{comments::SingleThreadedComments, FileName, SourceMap};
use swc_ecma_parser;
use testing::DebugUsingDisplay;
use self::swc_ecma_parser::{EsConfig, Parser, StringInput, Syntax};
use super::*;
use crate::{config::Config, text_writer::omit_trailing_semi};
struct Builder {
cfg: Config,
cm: Lrc<SourceMap>,
comments: SingleThreadedComments,
}
impl Builder {
pub fn with<'a, F, Ret>(self, _: &str, s: &'a mut Vec<u8>, op: F) -> Ret
where
F: for<'aa> FnOnce(&mut Emitter<'aa, Box<(dyn WriteJs + 'aa)>, SourceMap>) -> Ret,
Ret: 'static,
{
let writer = text_writer::JsWriter::new(self.cm.clone(), "\n", s, None);
let writer: Box<dyn WriteJs> = if self.cfg.minify {
Box::new(omit_trailing_semi(writer))
} else {
Box::new(writer)
};
{
let mut e = Emitter {
cfg: self.cfg,
cm: self.cm.clone(),
wr: writer,
comments: Some(&self.comments),
};
op(&mut e)
}
}
pub fn text<F>(self, src: &str, op: F) -> String
where
F: for<'aa> FnOnce(&mut Emitter<'aa, Box<(dyn WriteJs + 'aa)>, SourceMap>),
{
let mut buf = vec![];
self.with(src, &mut buf, op);
String::from_utf8(buf).unwrap()
}
}
fn parse_then_emit(from: &str, cfg: Config, syntax: Syntax) -> String {
::testing::run_test(false, |cm, handler| {
let src = cm.new_source_file(FileName::Real("custom.js".into()), from.to_string());
println!(
"--------------------\nSource: \n{}\nPos: {:?} ~ {:?}\n",
from, src.start_pos, src.end_pos
);
let comments = Default::default();
let res = {
let mut parser = Parser::new(syntax, StringInput::from(&*src), Some(&comments));
let res = parser
.parse_module()
.map_err(|e| e.into_diagnostic(handler).emit());
for err in parser.take_errors() {
err.into_diagnostic(handler).emit()
}
res?
};
let out = Builder { cfg, cm, comments }.text(from, |e| e.emit_module(&res).unwrap());
Ok(out)
})
.unwrap()
}
#[track_caller]
pub(crate) fn assert_min(from: &str, to: &str) {
let out = parse_then_emit(
from,
Config {
minify: true,
target: EsVersion::latest(),
omit_last_semi: true,
..Default::default()
},
Syntax::Es(Default::default()),
);
assert_eq!(DebugUsingDisplay(out.trim()), DebugUsingDisplay(to),);
}
pub(crate) fn assert_min_target(from: &str, to: &str, target: EsVersion) {
let out = parse_then_emit(
from,
Config {
minify: true,
omit_last_semi: true,
target,
..Default::default()
},
Syntax::default(),
);
assert_eq!(DebugUsingDisplay(out.trim()), DebugUsingDisplay(to),);
}
/// Clone of the regular `assert_min` function but with TypeScript syntax.
pub(crate) fn assert_min_typescript(from: &str, to: &str) {
let out = parse_then_emit(
from,
Config {
minify: true,
omit_last_semi: true,
target: EsVersion::latest(),
..Default::default()
},
Syntax::Typescript(Default::default()),
);
assert_eq!(DebugUsingDisplay(out.trim()), DebugUsingDisplay(to),);
}
pub(crate) fn assert_pretty(from: &str, to: &str) {
let out = parse_then_emit(
from,
Config {
minify: false,
target: EsVersion::latest(),
..Default::default()
},
Syntax::default(),
);
println!("Expected: {:?}", to);
println!("Actual: {:?}", out);
assert_eq!(DebugUsingDisplay(out.trim()), DebugUsingDisplay(to),);
}
#[track_caller]
fn test_from_to(from: &str, expected: &str) {
let out = parse_then_emit(
from,
Config {
target: EsVersion::latest(),
..Default::default()
},
Syntax::default(),
);
dbg!(&out);
dbg!(&expected);
assert_eq!(
DebugUsingDisplay(out.trim()),
DebugUsingDisplay(expected.trim()),
);
}
fn test_identical(from: &str) {
test_from_to(from, from)
}
fn test_from_to_custom_config(from: &str, to: &str, cfg: Config, syntax: Syntax) {
let out = parse_then_emit(
from,
Config {
omit_last_semi: true,
..cfg
},
syntax,
);
assert_eq!(DebugUsingDisplay(out.trim()), DebugUsingDisplay(to.trim()),);
}
#[test]
fn empty_stmt() {
test_from_to(";", ";");
}
#[test]
fn comment_1() {
test_from_to(
"// foo
a",
"// foo
a;",
);
}
#[test]
fn comment_2() {
test_from_to("a // foo", "a // foo\n;\n");
}
#[test]
fn comment_3() {
test_from_to(
"// foo
// bar
a
// foo
b // bar",
"// foo\n// bar\na;\n// foo\nb // bar\n;\n",
);
}
#[test]
fn comment_4() {
test_from_to("/** foo */ a", "/** foo */ a;");
}
#[test]
fn comment_5() {
test_from_to(
"// foo
// bar
a",
"// foo
// bar
a;",
);
}
#[test]
fn no_octal_escape() {
test_from_to(
r"'\x00a';
'\x000';
'\x001';
'\x009'",
r"'\x00a';
'\x000';
'\x001';
'\x009';",
);
}
#[test]
fn empty_named_export() {
test_from_to("export { }", "export { };");
}
#[test]
fn empty_named_export_min() {
test_from_to_custom_config(
"export { }",
"export{}",
Config {
minify: true,
..Default::default()
},
Default::default(),
);
}
#[test]
fn empty_named_export_from() {
test_from_to("export { } from 'foo';", "export { } from 'foo';");
}
#[test]
fn empty_named_export_from_min() {
test_from_to_custom_config(
"export { } from 'foo';",
"export{}from\"foo\"",
Config {
minify: true,
..Default::default()
},
Default::default(),
);
}
#[test]
fn named_export_from() {
test_from_to("export { bar } from 'foo';", "export { bar } from 'foo';");
}
#[test]
fn named_export_from_min() {
test_from_to_custom_config(
"export { bar } from 'foo';",
"export{bar}from\"foo\"",
Config {
minify: true,
..Default::default()
},
Default::default(),
);
}
#[test]
fn export_namespace_from() {
test_from_to_custom_config(
"export * as Foo from 'foo';",
"export * as Foo from 'foo';",
Default::default(),
Syntax::Es(EsConfig::default()),
);
}
#[test]
fn export_namespace_from_min() {
test_from_to_custom_config(
"export * as Foo from 'foo';",
"export*as Foo from\"foo\"",
Config {
minify: true,
..Default::default()
},
Syntax::Es(EsConfig::default()),
);
}
#[test]
fn named_and_namespace_export_from() {
test_from_to_custom_config(
"export * as Foo, { bar } from 'foo';",
"export * as Foo, { bar } from 'foo';",
Default::default(),
Syntax::Es(EsConfig {
export_default_from: true,
..EsConfig::default()
}),
);
}
#[test]
fn named_and_namespace_export_from_min() {
test_from_to_custom_config(
"export * as Foo, { bar } from 'foo';",
"export*as Foo,{bar}from\"foo\"",
Config {
minify: true,
..Default::default()
},
Syntax::Es(EsConfig {
export_default_from: true,
..EsConfig::default()
}),
);
}
#[test]
fn issue_450() {
test_from_to(
r"console.log(`
\`\`\`html
<h1>It works!</h1>
\`\`\`
`);",
"console.log(`\n\\`\\`\\`html\n<h1>It works!</h1>\n\\`\\`\\`\n`);",
);
}
#[test]
fn issue_546() {
test_from_to(
"import availabilities, * as availabilityFunctions from 'reducers/availabilities';",
"import availabilities, * as availabilityFunctions from 'reducers/availabilities';",
);
}
#[test]
fn issue_637() {
test_from_to(
r"`\
`;", r"`\
`;",
);
}
#[test]
fn issue_639() {
test_from_to(r"`\x1b[33m Yellow \x1b[0m`;", r"`\x1b[33m Yellow \x1b[0m`;");
}
#[test]
fn issue_910() {
test_from_to(
"console.log(\"Hello World\");",
"console.log(\"Hello World\");",
);
test_from_to("console.log('Hello World');", "console.log('Hello World');");
test_from_to(
"console.log(\"Hello\\\" World\");",
"console.log(\"Hello\\\" World\");",
);
test_from_to(
"console.log('Hello\\' World');",
"console.log('Hello\\' World');",
);
}
#[test]
fn tpl_1() {
test_from_to(
"`id '${id}' must be a non-empty string`;",
"`id '${id}' must be a non-empty string`;",
)
}
#[test]
fn tpl_2() {
test_from_to(
"`${Module.wrapper[0]}${script}${Module.wrapper[1]}`",
"`${Module.wrapper[0]}${script}${Module.wrapper[1]}`;",
);
}
#[test]
fn tpl_escape_1() {
test_from_to(
"`${parent.path}\x00${request}`",
"`${parent.path}\x00${request}`;",
)
}
#[test]
fn tpl_escape_2() {
test_from_to("`${arg}\0`", "`${arg}\0`;");
}
#[test]
fn tpl_escape_3() {
test_from_to(
r"`${resolvedDevice.toLowerCase()}\\`",
r"`${resolvedDevice.toLowerCase()}\\`;",
);
}
#[test]
fn tpl_escape_4() {
test_from_to(
r"`\\\\${firstPart}\\${path.slice(last)}`",
r"`\\\\${firstPart}\\${path.slice(last)}`;",
);
}
#[test]
fn tpl_escape_5() {
test_from_to(
r"const data = text.encode(`${arg}\0`);",
r"const data = text.encode(`${arg}\0`);",
);
}
#[test]
fn tpl_escape_6() {
let from = r#"export class MultipartReader {
newLine = encoder.encode("\r\n");
newLineDashBoundary = encoder.encode(`\r\n--${this.boundary}`);
dashBoundaryDash = encoder.encode(`--${this.boundary}--`);
}"#;
let to = r#"export class MultipartReader {
newLine = encoder.encode("\r\n");
newLineDashBoundary = encoder.encode(`\r\n--${this.boundary}`);
dashBoundaryDash = encoder.encode(`--${this.boundary}--`);
}"#;
let out = parse_then_emit(
from,
Config {
target: EsVersion::latest(),
..Default::default()
},
Syntax::Typescript(Default::default()),
);
assert_eq!(DebugUsingDisplay(out.trim()), DebugUsingDisplay(to.trim()),);
}
#[test]
fn issue_915_1() {
test_identical(r"relResolveCacheIdentifier = `${parent.path}\x00${request}`;");
}
#[test]
fn issue_915_2() {
test_identical(r"relResolveCacheIdentifier = `${parent.path}\x00${request}`;");
}
#[test]
fn issue_915_3() {
test_identical(r#"encoder.encode("\\r\\n");"#);
}
#[test]
fn issue_915_4() {
test_identical(r"`\\r\\n--${this.boundary}`;");
}
#[test]
fn jsx_1() {
test_from_to_custom_config(
"<Foo title=\"name\" desc=\"<empty>\" bool it>foo</Foo>;",
"<Foo title=\"name\" desc=\"<empty>\" bool it>foo</Foo>;",
Default::default(),
Syntax::Es(EsConfig {
jsx: true,
..Default::default()
}),
);
}
#[test]
fn deno_8162() {
test_from_to(
r#""\x00\r\n\x85\u2028\u2029";"#,
r#""\x00\r\n\x85\u2028\u2029";"#,
);
}
#[test]
fn integration_01() {
test_from_to(
r#"
`Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
`"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
`Expected to find one of the known reducer keys instead: ` +
`"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
"#,
"
`Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` + `\"${unexpectedKeys.join('\", \
\"')}\" found in ${argumentName}. ` + `Expected to find one of the known reducer keys \
instead: ` + `\"${reducerKeys.join('\", \"')}\". Unexpected keys will be ignored.`;
",
);
}
#[test]
fn integration_01_reduced_01() {
test_from_to(
r#"
`Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
`"${unexpectedKeys.join('", "')}" found in ${argumentName}. `
"#,
"
`Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` + `\"${unexpectedKeys.join('\", \
\"')}\" found in ${argumentName}. `;",
);
}
#[test]
fn deno_8541_1() {
test_from_to(
"React.createElement('span', null, '\\u{b7}');",
"React.createElement('span', null, '\\u{b7}');",
);
}
#[test]
fn deno_8925() {
assert_pretty("const 𝒫 = 2;", "const 𝒫 = 2;");
}
#[test]
#[ignore = "Tested by a bundler test"]
fn deno_9620() {
assert_pretty(
"const content = `--------------------------366796e1c748a2fb\r
Content-Disposition: form-data; name=\"payload\"\r
Content-Type: text/plain\r
\r
CONTENT\r
--------------------------366796e1c748a2fb--`",
"`const content = `--------------------------366796e1c748a2fb\\r\\nContent-Disposition: \
form-data; name=\"payload\"\\r\\nContent-Type: \
text/plain\\r\\n\\r\\nCONTENT\\r\\n--------------------------366796e1c748a2fb--`;",
);
}
#[test]
fn test_get_quoted_utf16() {
#[track_caller]
fn es2020(src: &str, expected: &str) {
assert_eq!(
super::get_quoted_utf16(src, true, EsVersion::Es2020),
expected
)
}
#[track_caller]
fn es2020_nonascii(src: &str, expected: &str) {
assert_eq!(
super::get_quoted_utf16(src, true, EsVersion::Es2020),
expected
)
}
#[track_caller]
fn es5(src: &str, expected: &str) {
assert_eq!(super::get_quoted_utf16(src, true, EsVersion::Es5), expected)
}
es2020("abcde", "\"abcde\"");
es2020(
"\x00\r\n\u{85}\u{2028}\u{2029};",
"\"\\x00\\r\\n\\x85\\u2028\\u2029;\"",
);
es2020("\n", "\"\\n\"");
es2020("\t", "\"\t\"");
es2020("'string'", "\"'string'\"");
es2020("\u{0}", "\"\\x00\"");
es2020("\u{1}", "\"\\x01\"");
es2020("\u{1000}", "\"\\u1000\"");
es2020("\u{ff}", "\"\\xff\"");
es2020("\u{10ffff}", "\"\\u{10FFFF}\"");
es2020("😀", "\"\\u{1F600}\"");
es2020("", "\"\\uD7FB\"");
es2020_nonascii("\u{FEFF}abc", "\"\\uFEFFabc\"");
es2020_nonascii("\u{10ffff}", "\"\\u{10FFFF}\"");
es5("\u{FEFF}abc", "\"\\uFEFFabc\"");
es5("\u{10ffff}", "\"\\uDBFF\\uDFFF\"");
es5("\u{FFFF}", "\"\\uFFFF\"");
es5("😀", "\"\\uD83D\\uDE00\"");
es5("", "\"\\uD7FB\"");
}
#[test]
fn deno_8541_2() {
test_from_to(
"React.createElement('span', null, '\\u00b7');",
"React.createElement('span', null, '\\u00b7');",
);
}
#[test]
fn issue_1452_1() {
assert_min("async foo => 0", "async foo=>0");
}
#[test]
fn issue_1619_1() {
assert_min_target(
"\"\\x00\" + \"\\x31\"",
"\"\\x00\"+\"1\"",
EsVersion::latest(),
);
}
#[test]
fn issue_1619_2() {
assert_min_target(
"\"\\x00\" + \"\\x31\"",
"\"\\x00\"+\"1\"",
EsVersion::latest(),
);
}
#[test]
fn issue_1619_3() {
assert_eq!(
get_quoted_utf16("\x00\x31", true, EsVersion::Es3),
"\"\\x001\""
);
}
fn check_latest(src: &str, expected: &str) {
let actual = parse_then_emit(
src,
Config {
minify: false,
target: EsVersion::latest(),
..Default::default()
},
Default::default(),
);
assert_eq!(expected, actual.trim());
}
#[test]
fn invalid_unicode_in_ident() {
check_latest("\\ud83d;", "\\ud83d;");
}
#[test]
fn test_escape_with_source_str() {
check_latest("'\\ud83d'", "'\\ud83d';");
check_latest(
"'\\ud83d\\ud83d\\ud83d\\ud83d\\ud83d'",
"'\\ud83d\\ud83d\\ud83d\\ud83d\\ud83d';",
);
}
#[derive(Debug, Clone)]
struct Buf(Arc<RwLock<Vec<u8>>>);
impl Write for Buf {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
self.0.write().unwrap().write(data)
}
fn flush(&mut self) -> io::Result<()> {
self.0.write().unwrap().flush()
}
}
#[test]
fn issue_2213() {
assert_min("a - -b * c", "a- -b*c")
}
#[test]
fn issue_3617() {
// Convert characters to es5 compatibility code
let from = r"// a string of all valid unicode whitespaces
module.exports = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002' +
'\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF' + '\u{a0}';";
let expected = r#"// a string of all valid unicode whitespaces
module.exports = " \n\v\f\r \xa0\u1680\u2000\u2001\u2002" + "\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF" + "\xa0";"#;
let out = parse_then_emit(
from,
Config {
ascii_only: true,
target: EsVersion::Es5,
..Default::default()
},
Syntax::default(),
);
dbg!(&out);
dbg!(&expected);
assert_eq!(
DebugUsingDisplay(out.trim()),
DebugUsingDisplay(expected.trim()),
);
}
#[test]
fn issue_3617_1() {
// Print characters as is for ECMA target > 5
let from = r"// a string of all valid unicode whitespaces
module.exports = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002' +
'\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF' + '\u{a0}';";
let expected = r"// a string of all valid unicode whitespaces
module.exports = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002' + '\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF' + '\u{a0}';";
let out = parse_then_emit(
from,
Config {
target: EsVersion::Es2022,
..Default::default()
},
Syntax::default(),
);
dbg!(&out);
dbg!(&expected);
assert_eq!(
DebugUsingDisplay(out.trim()),
DebugUsingDisplay(expected.trim()),
);
}
fn test_all(src: &str, expected: &str, expected_minified: &str, config: Config) {
{
let out = parse_then_emit(
src,
Config {
minify: false,
omit_last_semi: true,
..config
},
Syntax::default(),
);
dbg!(out.trim());
dbg!(expected.trim());
assert_eq!(
DebugUsingDisplay(out.trim()),
DebugUsingDisplay(expected.trim()),
);
}
{
eprintln!("> minified");
let out = parse_then_emit(
src,
Config {
minify: true,
omit_last_semi: true,
..config
},
Syntax::default(),
);
dbg!(out.trim());
dbg!(expected_minified.trim());
assert_eq!(
DebugUsingDisplay(out.trim()),
DebugUsingDisplay(expected_minified.trim()),
);
}
}
#[test]
fn ascii_only_str_1() {
test_all(
"'😊❤️'",
"'😊❤️';\n",
r#""😊❤️""#,
Config {
ascii_only: false,
target: EsVersion::Es2015,
..Default::default()
},
);
}
#[test]
fn ascii_only_str_2() {
test_all(
"'😊❤️'",
r#""\u{1F60A}\u2764\uFE0F";"#,
r#""\u{1F60A}\u2764\uFE0F""#,
Config {
ascii_only: true,
..Default::default()
},
);
}
#[test]
fn ascii_only_str_3() {
test_all(
"'\\u{1F60A}'",
"'\\u{1F60A}';\n",
"\"\\u{1F60A}\"",
Config {
ascii_only: true,
..Default::default()
},
);
}
#[test]
fn ascii_only_tpl_lit() {
test_all(
"`😊❤️`",
r"`\u{1F60A}\u{2764}\u{FE0F}`;",
r"`\u{1F60A}\u{2764}\u{FE0F}`",
Config {
ascii_only: true,
..Default::default()
},
);
}
#[test]
fn ascii_only_issue_7240() {
test_all(
r#"
export default {
"\u3131": '\u11B0',
}
"#,
r#"
export default {
"\u3131": '\u11B0'
};
"#,
r#"export default{"\u3131":"\u11B0"}"#,
Config {
ascii_only: true,
..Default::default()
},
);
}

View file

@ -0,0 +1,233 @@
use swc_common::Span;
pub use self::{basic_impl::JsWriter, semicolon::omit_trailing_semi};
use super::*;
mod basic_impl;
mod semicolon;
/// TODO
pub type Symbol = Str;
/// Ecmascript writer.
///
/// Ported from `EmitWriteJs`.
pub trait WriteJs {
fn increase_indent(&mut self) -> Result;
fn decrease_indent(&mut self) -> Result;
/// This *may* write semicolon.
fn write_semi(&mut self, span: Option<Span>) -> Result;
fn write_space(&mut self) -> Result;
fn write_keyword(&mut self, span: Option<Span>, s: &'static str) -> Result;
fn write_operator(&mut self, span: Option<Span>, s: &str) -> Result;
fn write_param(&mut self, s: &str) -> Result;
fn write_property(&mut self, s: &str) -> Result;
fn write_line(&mut self) -> Result;
fn write_lit(&mut self, span: Span, s: &str) -> Result;
fn write_comment(&mut self, s: &str) -> Result;
fn write_str_lit(&mut self, span: Span, s: &str) -> Result;
fn write_str(&mut self, s: &str) -> Result;
fn write_symbol(&mut self, span: Span, s: &str) -> Result;
fn write_punct(&mut self, span: Option<Span>, s: &'static str) -> Result;
fn care_about_srcmap(&self) -> bool;
fn add_srcmap(&mut self, pos: BytePos) -> Result;
fn commit_pending_semi(&mut self) -> Result;
}
impl<W> WriteJs for Box<W>
where
W: ?Sized + WriteJs,
{
#[inline]
fn increase_indent(&mut self) -> Result {
(**self).increase_indent()
}
#[inline]
fn decrease_indent(&mut self) -> Result {
(**self).decrease_indent()
}
#[inline]
fn write_semi(&mut self, span: Option<Span>) -> Result {
(**self).write_semi(span)
}
#[inline]
fn write_space(&mut self) -> Result {
(**self).write_space()
}
#[inline]
fn write_keyword(&mut self, span: Option<Span>, s: &'static str) -> Result {
(**self).write_keyword(span, s)
}
#[inline]
fn write_operator(&mut self, span: Option<Span>, s: &str) -> Result {
(**self).write_operator(span, s)
}
#[inline]
fn write_param(&mut self, s: &str) -> Result {
(**self).write_param(s)
}
#[inline]
fn write_property(&mut self, s: &str) -> Result {
(**self).write_property(s)
}
#[inline]
fn write_line(&mut self) -> Result {
(**self).write_line()
}
#[inline]
fn write_lit(&mut self, span: Span, s: &str) -> Result {
(**self).write_lit(span, s)
}
#[inline]
fn write_comment(&mut self, s: &str) -> Result {
(**self).write_comment(s)
}
#[inline]
fn write_str_lit(&mut self, span: Span, s: &str) -> Result {
(**self).write_str_lit(span, s)
}
#[inline]
fn write_str(&mut self, s: &str) -> Result {
(**self).write_str(s)
}
#[inline]
fn write_symbol(&mut self, span: Span, s: &str) -> Result {
(**self).write_symbol(span, s)
}
#[inline]
fn write_punct(&mut self, span: Option<Span>, s: &'static str) -> Result {
(**self).write_punct(span, s)
}
#[inline]
fn care_about_srcmap(&self) -> bool {
(**self).care_about_srcmap()
}
#[inline]
fn add_srcmap(&mut self, pos: BytePos) -> Result {
(**self).add_srcmap(pos)
}
fn commit_pending_semi(&mut self) -> Result {
(**self).commit_pending_semi()
}
}
impl<W> WriteJs for &'_ mut W
where
W: ?Sized + WriteJs,
{
#[inline]
fn increase_indent(&mut self) -> Result {
(**self).increase_indent()
}
#[inline]
fn decrease_indent(&mut self) -> Result {
(**self).decrease_indent()
}
#[inline]
fn write_semi(&mut self, span: Option<Span>) -> Result {
(**self).write_semi(span)
}
#[inline]
fn write_space(&mut self) -> Result {
(**self).write_space()
}
#[inline]
fn write_keyword(&mut self, span: Option<Span>, s: &'static str) -> Result {
(**self).write_keyword(span, s)
}
#[inline]
fn write_operator(&mut self, span: Option<Span>, s: &str) -> Result {
(**self).write_operator(span, s)
}
#[inline]
fn write_param(&mut self, s: &str) -> Result {
(**self).write_param(s)
}
#[inline]
fn write_property(&mut self, s: &str) -> Result {
(**self).write_property(s)
}
#[inline]
fn write_line(&mut self) -> Result {
(**self).write_line()
}
#[inline]
fn write_lit(&mut self, span: Span, s: &str) -> Result {
(**self).write_lit(span, s)
}
#[inline]
fn write_comment(&mut self, s: &str) -> Result {
(**self).write_comment(s)
}
#[inline]
fn write_str_lit(&mut self, span: Span, s: &str) -> Result {
(**self).write_str_lit(span, s)
}
#[inline]
fn write_str(&mut self, s: &str) -> Result {
(**self).write_str(s)
}
#[inline]
fn write_symbol(&mut self, span: Span, s: &str) -> Result {
(**self).write_symbol(span, s)
}
#[inline]
fn write_punct(&mut self, span: Option<Span>, s: &'static str) -> Result {
(**self).write_punct(span, s)
}
#[inline]
fn care_about_srcmap(&self) -> bool {
(**self).care_about_srcmap()
}
#[inline]
fn add_srcmap(&mut self, pos: BytePos) -> Result {
(**self).add_srcmap(pos)
}
fn commit_pending_semi(&mut self) -> Result {
(**self).commit_pending_semi()
}
}

View file

@ -0,0 +1,361 @@
use std::io::Write;
use rustc_hash::FxHashSet;
use swc_common::{sync::Lrc, BytePos, LineCol, SourceMap, Span};
use super::{Result, WriteJs};
///
/// -----
///
/// Ported from `createTextWriter` of the typescript compiler.
///
/// https://github.com/Microsoft/TypeScript/blob/45eaf42006/src/compiler/utilities.ts#L2548
pub struct JsWriter<'a, W: Write> {
indent: usize,
indent_str: &'static str,
line_start: bool,
line_count: usize,
line_pos: usize,
new_line: &'a str,
srcmap: Option<&'a mut Vec<(BytePos, LineCol)>>,
srcmap_done: FxHashSet<(BytePos, u32, u32)>,
/// Used to avoid including whitespaces created by indention.
pending_srcmap: Option<BytePos>,
wr: W,
}
impl<'a, W: Write> JsWriter<'a, W> {
pub fn new(
_: Lrc<SourceMap>,
new_line: &'a str,
wr: W,
srcmap: Option<&'a mut Vec<(BytePos, LineCol)>>,
) -> Self {
JsWriter {
indent: Default::default(),
indent_str: " ",
line_start: true,
line_count: 0,
line_pos: Default::default(),
new_line,
srcmap,
wr,
pending_srcmap: Default::default(),
srcmap_done: Default::default(),
}
}
pub fn preamble(&mut self, s: &str) -> Result {
self.raw_write(s)?;
self.update_pos(s);
Ok(())
}
/// Sets the indentation string. Defaults to four spaces.
pub fn set_indent_str(&mut self, indent_str: &'static str) {
self.indent_str = indent_str;
}
#[inline]
fn write_indent_string(&mut self) -> Result {
for _ in 0..self.indent {
self.raw_write(self.indent_str)?;
}
if self.srcmap.is_some() {
self.line_pos += self.indent_str.len() * self.indent;
}
Ok(())
}
#[inline]
fn raw_write(&mut self, data: &str) -> Result {
// #[cfg(debug_assertions)]
// tracing::trace!("Write: `{}`", data);
self.wr.write_all(data.as_bytes())?;
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write(&mut self, span: Option<Span>, data: &str) -> Result {
if !data.is_empty() {
if self.line_start {
self.write_indent_string()?;
self.line_start = false;
if let Some(pending) = self.pending_srcmap.take() {
self.srcmap(pending);
}
}
if let Some(span) = span {
self.srcmap(span.lo());
}
self.raw_write(data)?;
self.update_pos(data);
if let Some(span) = span {
self.srcmap(span.hi());
}
}
Ok(())
}
#[inline]
fn update_pos(&mut self, s: &str) {
if self.srcmap.is_some() {
let line_start_of_s = compute_line_starts(s);
self.line_count += line_start_of_s.line_count;
let chars = s[line_start_of_s.byte_pos..].encode_utf16().count();
if line_start_of_s.line_count > 0 {
self.line_pos = chars;
} else {
self.line_pos += chars;
}
}
}
#[inline]
fn srcmap(&mut self, byte_pos: BytePos) {
if byte_pos.is_dummy() && byte_pos != BytePos(u32::MAX) {
return;
}
if let Some(ref mut srcmap) = self.srcmap {
if self
.srcmap_done
.insert((byte_pos, self.line_count as _, self.line_pos as _))
{
let loc = LineCol {
line: self.line_count as _,
col: self.line_pos as _,
};
// #[cfg(debug_assertions)]
// tracing::trace!("SourceMap: {:?} => {:?}", byte_pos, loc);
srcmap.push((byte_pos, loc));
}
}
}
}
impl<'a, W: Write> WriteJs for JsWriter<'a, W> {
#[inline]
fn increase_indent(&mut self) -> Result {
self.indent += 1;
Ok(())
}
#[inline]
fn decrease_indent(&mut self) -> Result {
self.indent -= 1;
Ok(())
}
#[inline]
fn write_semi(&mut self, span: Option<Span>) -> Result {
self.write(span, ";")?;
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_space(&mut self) -> Result {
self.write(None, " ")?;
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_keyword(&mut self, span: Option<Span>, s: &'static str) -> Result {
self.write(span, s)?;
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_operator(&mut self, span: Option<Span>, s: &str) -> Result {
self.write(span, s)?;
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_param(&mut self, s: &str) -> Result {
self.write(None, s)?;
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_property(&mut self, s: &str) -> Result {
self.write(None, s)?;
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_line(&mut self) -> Result {
let pending = self.pending_srcmap.take();
if !self.line_start {
self.raw_write(self.new_line)?;
if self.srcmap.is_some() {
self.line_count += 1;
self.line_pos = 0;
}
self.line_start = true;
if let Some(pending) = pending {
self.srcmap(pending)
}
}
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_lit(&mut self, span: Span, s: &str) -> Result {
if !s.is_empty() {
self.srcmap(span.lo());
self.write(None, s)?;
self.srcmap(span.hi());
}
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_comment(&mut self, s: &str) -> Result {
self.write(None, s)?;
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_str_lit(&mut self, span: Span, s: &str) -> Result {
if !s.is_empty() {
self.srcmap(span.lo());
self.write(None, s)?;
self.srcmap(span.hi());
}
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_str(&mut self, s: &str) -> Result {
self.write(None, s)?;
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_symbol(&mut self, span: Span, s: &str) -> Result {
self.write(Some(span), s)?;
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn write_punct(&mut self, span: Option<Span>, s: &'static str) -> Result {
self.write(span, s)?;
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn care_about_srcmap(&self) -> bool {
self.srcmap.is_some()
}
#[inline]
#[tracing::instrument(skip_all)]
fn add_srcmap(&mut self, pos: BytePos) -> Result {
if self.srcmap.is_some() {
if self.line_start {
self.pending_srcmap = Some(pos);
} else {
self.srcmap(pos);
}
}
Ok(())
}
#[inline]
#[tracing::instrument(skip_all)]
fn commit_pending_semi(&mut self) -> Result {
Ok(())
}
}
#[derive(Debug)]
struct LineStart {
line_count: usize,
byte_pos: usize,
}
fn compute_line_starts(s: &str) -> LineStart {
let mut count = 0;
let mut line_start = 0;
let mut chars = s.as_bytes().iter().enumerate().peekable();
while let Some((pos, c)) = chars.next() {
match c {
b'\r' => {
count += 1;
if let Some(&(_, b'\n')) = chars.peek() {
let _ = chars.next();
line_start = pos + 2
} else {
line_start = pos + 1
}
}
b'\n' => {
count += 1;
line_start = pos + 1;
}
_ => {}
}
}
LineStart {
line_count: count,
byte_pos: line_start,
}
}
#[cfg(test)]
mod test {
use std::sync::Arc;
use swc_common::SourceMap;
use super::JsWriter;
use crate::text_writer::WriteJs;
#[test]
fn changes_indent_str() {
let source_map = Arc::new(SourceMap::default());
let mut output = Vec::new();
let mut writer = JsWriter::new(source_map, "\n", &mut output, None);
writer.set_indent_str("\t");
writer.increase_indent().unwrap();
writer.write_indent_string().unwrap();
writer.increase_indent().unwrap();
writer.write_indent_string().unwrap();
assert_eq!(output, "\t\t\t".as_bytes());
}
}

View file

@ -0,0 +1,100 @@
use swc_common::{BytePos, Span, DUMMY_SP};
use super::{Result, WriteJs};
pub fn omit_trailing_semi<W: WriteJs>(w: W) -> impl WriteJs {
OmitTrailingSemi {
inner: w,
pending_semi: None,
}
}
#[derive(Debug, Clone)]
struct OmitTrailingSemi<W: WriteJs> {
inner: W,
pending_semi: Option<Span>,
}
macro_rules! with_semi {
(
$fn_name:ident
(
$(
$arg_name:ident
:
$arg_ty:ty
),*
)
) => {
fn $fn_name(&mut self, $($arg_name: $arg_ty),* ) -> Result {
self.commit_pending_semi()?;
self.inner.$fn_name( $($arg_name),* )
}
};
}
impl<W: WriteJs> WriteJs for OmitTrailingSemi<W> {
with_semi!(increase_indent());
with_semi!(decrease_indent());
with_semi!(write_space());
with_semi!(write_comment(s: &str));
with_semi!(write_keyword(span: Option<Span>, s: &'static str));
with_semi!(write_operator(span: Option<Span>, s: &str));
with_semi!(write_param(s: &str));
with_semi!(write_property(s: &str));
with_semi!(write_line());
with_semi!(write_lit(span: Span, s: &str));
with_semi!(write_str_lit(span: Span, s: &str));
with_semi!(write_str(s: &str));
with_semi!(write_symbol(span: Span, s: &str));
fn write_semi(&mut self, span: Option<Span>) -> Result {
self.pending_semi = Some(span.unwrap_or(DUMMY_SP));
Ok(())
}
fn write_punct(&mut self, span: Option<Span>, s: &'static str) -> Result {
match s {
"\"" | "'" | "[" | "!" | "/" | "{" | "(" | "~" | "-" | "+" | "#" | "`" | "*" => {
self.commit_pending_semi()?;
}
_ => {
self.pending_semi = None;
}
}
self.inner.write_punct(span, s)
}
#[inline]
fn care_about_srcmap(&self) -> bool {
self.inner.care_about_srcmap()
}
#[inline]
fn add_srcmap(&mut self, pos: BytePos) -> Result {
self.inner.add_srcmap(pos)
}
fn commit_pending_semi(&mut self) -> Result {
if let Some(span) = self.pending_semi {
self.inner.write_semi(Some(span))?;
self.pending_semi = None;
}
Ok(())
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,250 @@
use swc_ecma_ast::*;
pub trait EndsWithAlphaNum {
fn ends_with_alpha_num(&self) -> bool;
}
impl EndsWithAlphaNum for ForHead {
fn ends_with_alpha_num(&self) -> bool {
match self {
ForHead::VarDecl(n) => n.ends_with_alpha_num(),
ForHead::Pat(n) => n.ends_with_alpha_num(),
ForHead::UsingDecl(n) => n.ends_with_alpha_num(),
}
}
}
impl EndsWithAlphaNum for Pat {
fn ends_with_alpha_num(&self) -> bool {
match self {
Pat::Object(_) | Pat::Array(_) => false,
Pat::Rest(p) => p.arg.ends_with_alpha_num(),
Pat::Assign(p) => p.right.ends_with_alpha_num(),
Pat::Expr(p) => p.ends_with_alpha_num(),
_ => true,
}
}
}
impl EndsWithAlphaNum for VarDecl {
fn ends_with_alpha_num(&self) -> bool {
match self.decls.last() {
None => true,
Some(d) => match d.init.as_deref() {
Some(e) => e.ends_with_alpha_num(),
None => d.name.ends_with_alpha_num(),
},
}
}
}
impl EndsWithAlphaNum for UsingDecl {
fn ends_with_alpha_num(&self) -> bool {
match self.decls.last() {
None => true,
Some(d) => match d.init.as_deref() {
Some(e) => e.ends_with_alpha_num(),
None => d.name.ends_with_alpha_num(),
},
}
}
}
impl EndsWithAlphaNum for Expr {
fn ends_with_alpha_num(&self) -> bool {
!matches!(
self,
Expr::Array(..)
| Expr::Object(..)
| Expr::Lit(Lit::Str(..))
| Expr::Paren(..)
| Expr::Member(MemberExpr {
prop: MemberProp::Computed(..),
..
})
)
}
}
/// Leftmost recursion
pub trait StartsWithAlphaNum {
fn starts_with_alpha_num(&self) -> bool;
}
impl StartsWithAlphaNum for PropName {
fn starts_with_alpha_num(&self) -> bool {
match self {
PropName::Str(_) | PropName::Computed(_) => false,
PropName::Ident(_) | PropName::Num(_) | PropName::BigInt(_) => true,
}
}
}
impl StartsWithAlphaNum for Expr {
fn starts_with_alpha_num(&self) -> bool {
match self {
Expr::Ident(_)
| Expr::Lit(Lit::Bool(_))
| Expr::Lit(Lit::Num(_))
| Expr::Lit(Lit::Null(_))
| Expr::Lit(Lit::BigInt(_))
| Expr::Await(_)
| Expr::Fn(_)
| Expr::Class(_)
| Expr::This(_)
| Expr::Yield(_)
| Expr::New(_)
| Expr::MetaProp(_)
| Expr::SuperProp(_) => true,
Expr::PrivateName(_) => false,
// Handle other literals.
Expr::Lit(_) => false,
Expr::Seq(SeqExpr { ref exprs, .. }) => exprs
.first()
.map(|e| e.starts_with_alpha_num())
.unwrap_or(false),
//
Expr::Assign(AssignExpr { ref left, .. }) => left.starts_with_alpha_num(),
Expr::Bin(BinExpr { ref left, .. }) | Expr::Cond(CondExpr { test: ref left, .. }) => {
left.starts_with_alpha_num()
}
Expr::Call(CallExpr { callee: left, .. }) => left.starts_with_alpha_num(),
Expr::Member(MemberExpr { obj: ref left, .. }) => left.starts_with_alpha_num(),
Expr::Unary(UnaryExpr { op, .. }) => {
matches!(op, op!("void") | op!("delete") | op!("typeof"))
}
Expr::Arrow(ref expr) => {
if expr.is_async {
true
} else {
match expr.params.as_slice() {
[p] => p.starts_with_alpha_num(),
_ => false,
}
}
}
Expr::Update(ref expr) => {
if expr.prefix {
false
} else {
expr.arg.starts_with_alpha_num()
}
}
Expr::Tpl(_) | Expr::Array(_) | Expr::Object(_) | Expr::Paren(_) => false,
Expr::TaggedTpl(TaggedTpl { ref tag, .. }) => tag.starts_with_alpha_num(),
// it's empty
Expr::JSXEmpty(..) => false,
// start with `<`
Expr::JSXFragment(..) | Expr::JSXElement(..) => false,
Expr::JSXNamespacedName(..) => true,
Expr::JSXMember(..) => true,
Expr::TsTypeAssertion(..) => false,
Expr::TsNonNull(TsNonNullExpr { ref expr, .. })
| Expr::TsAs(TsAsExpr { ref expr, .. })
| Expr::TsConstAssertion(TsConstAssertion { ref expr, .. })
| Expr::TsInstantiation(TsInstantiation { ref expr, .. })
| Expr::TsSatisfies(TsSatisfiesExpr { ref expr, .. }) => expr.starts_with_alpha_num(),
Expr::OptChain(OptChainExpr { base, .. }) => match &**base {
OptChainBase::Member(base) => base.obj.starts_with_alpha_num(),
OptChainBase::Call(base) => base.callee.starts_with_alpha_num(),
},
Expr::Invalid(..) => true,
}
}
}
impl StartsWithAlphaNum for Pat {
fn starts_with_alpha_num(&self) -> bool {
match *self {
Pat::Ident(..) => true,
Pat::Assign(AssignPat { ref left, .. }) => left.starts_with_alpha_num(),
Pat::Object(..) | Pat::Array(..) | Pat::Rest(..) => false,
Pat::Expr(ref expr) => expr.starts_with_alpha_num(),
Pat::Invalid(..) => true,
}
}
}
impl StartsWithAlphaNum for PatOrExpr {
fn starts_with_alpha_num(&self) -> bool {
match *self {
PatOrExpr::Pat(ref p) => p.starts_with_alpha_num(),
PatOrExpr::Expr(ref e) => e.starts_with_alpha_num(),
}
}
}
impl StartsWithAlphaNum for ExprOrSpread {
fn starts_with_alpha_num(&self) -> bool {
match *self {
ExprOrSpread {
spread: Some(_), ..
} => false,
ExprOrSpread {
spread: None,
ref expr,
} => expr.starts_with_alpha_num(),
}
}
}
impl StartsWithAlphaNum for Callee {
fn starts_with_alpha_num(&self) -> bool {
match *self {
Callee::Super(_) | Callee::Import(_) => true,
Callee::Expr(ref e) => e.starts_with_alpha_num(),
}
}
}
impl StartsWithAlphaNum for Stmt {
fn starts_with_alpha_num(&self) -> bool {
match *self {
Stmt::Expr(ref expr) => expr.expr.starts_with_alpha_num(),
Stmt::Decl(ref decl) => decl.starts_with_alpha_num(),
Stmt::Debugger(..)
| Stmt::With(..)
| Stmt::While(..)
| Stmt::DoWhile(..)
| Stmt::Return(..)
| Stmt::Labeled(..)
| Stmt::Break(..)
| Stmt::Continue(..)
| Stmt::Switch(..)
| Stmt::Throw(..)
| Stmt::Try(..)
| Stmt::For(..)
| Stmt::ForIn(..)
| Stmt::ForOf(..)
| Stmt::If(..) => true,
Stmt::Block(..) | Stmt::Empty(..) => false,
}
}
}
impl StartsWithAlphaNum for Decl {
fn starts_with_alpha_num(&self) -> bool {
match *self {
Decl::Class(..)
| Decl::Fn(..)
| Decl::Var(..)
| Decl::TsEnum(..)
| Decl::TsInterface(..)
| Decl::TsModule(..)
| Decl::TsTypeAlias(..)
| Decl::Using(..) => true,
}
}
}