#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(test, feature(bench_black_box))]
#![cfg_attr(test, feature(test))]
#![deny(clippy::all)]
#![deny(unused)]
#![allow(clippy::nonminimal_bool)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::unnecessary_unwrap)]
#![allow(clippy::vec_box)]
#![allow(clippy::wrong_self_convention)]
use error::Error;
use lexer::Lexer;
use serde::{Deserialize, Serialize};
use swc_common::{comments::Comments, input::SourceFileInput, SourceFile};
use swc_ecma_ast::*;
pub use self::{
lexer::input::{Input, StringInput},
parser::*,
};
#[deprecated(note = "Use `EsVersion` instead")]
pub type JscTarget = EsVersion;
#[macro_use]
mod macros;
pub mod error;
pub mod lexer;
mod parser;
pub mod token;
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
#[serde(deny_unknown_fields, tag = "syntax")]
pub enum Syntax {
#[serde(rename = "ecmascript")]
Es(EsConfig),
#[cfg(feature = "typescript")]
#[cfg_attr(docsrs, doc(cfg(feature = "typescript")))]
#[serde(rename = "typescript")]
Typescript(TsConfig),
}
impl Default for Syntax {
fn default() -> Self {
Syntax::Es(Default::default())
}
}
impl Syntax {
pub fn import_assertions(self) -> bool {
match self {
Syntax::Es(EsConfig {
import_assertions, ..
}) => import_assertions,
Syntax::Typescript(_) => true,
}
}
pub fn static_blocks(self) -> bool {
matches!(
self,
Syntax::Es(EsConfig {
static_blocks: true,
..
}) | Syntax::Typescript(..)
)
}
pub fn jsx(self) -> bool {
matches!(
self,
Syntax::Es(EsConfig { jsx: true, .. }) | Syntax::Typescript(TsConfig { tsx: true, .. })
)
}
pub const fn optional_chaining(self) -> bool {
true
}
pub const fn dynamic_import(self) -> bool {
true
}
pub fn fn_bind(self) -> bool {
matches!(self, Syntax::Es(EsConfig { fn_bind: true, .. }))
}
pub fn decorators(self) -> bool {
matches!(
self,
Syntax::Es(EsConfig {
decorators: true,
..
}) | Syntax::Typescript(TsConfig {
decorators: true,
..
})
)
}
pub fn decorators_before_export(self) -> bool {
matches!(
self,
Syntax::Es(EsConfig {
decorators_before_export: true,
..
}) | Syntax::Typescript(..)
)
}
#[cfg(not(feature = "typescript"))]
pub const fn typescript(self) -> bool {
false
}
#[cfg(feature = "typescript")]
pub const fn typescript(self) -> bool {
matches!(self, Syntax::Typescript(..))
}
pub fn export_default_from(self) -> bool {
matches!(
self,
Syntax::Es(EsConfig {
export_default_from: true,
..
})
)
}
pub fn dts(self) -> bool {
match self {
Syntax::Typescript(t) => t.dts,
_ => false,
}
}
pub fn private_in_object(self) -> bool {
match self {
Syntax::Es(EsConfig {
private_in_object, ..
}) => private_in_object,
Syntax::Typescript(_) => true,
}
}
pub fn allow_super_outside_method(self) -> bool {
match self {
Syntax::Es(EsConfig {
allow_super_outside_method,
..
}) => allow_super_outside_method,
Syntax::Typescript(_) => true,
}
}
pub(crate) fn early_errors(self) -> bool {
match self {
Syntax::Typescript(t) => !t.no_early_errors,
Syntax::Es(..) => true,
}
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TsConfig {
#[serde(default)]
pub tsx: bool,
#[serde(default)]
pub decorators: bool,
#[serde(skip, default)]
pub dts: bool,
#[serde(skip, default)]
pub no_early_errors: bool,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EsConfig {
#[serde(default)]
pub jsx: bool,
#[serde(rename = "functionBind")]
#[serde(default)]
pub fn_bind: bool,
#[serde(default)]
pub decorators: bool,
#[serde(rename = "decoratorsBeforeExport")]
#[serde(default)]
pub decorators_before_export: bool,
#[serde(default)]
pub export_default_from: bool,
#[serde(default)]
pub import_assertions: bool,
#[serde(default, rename = "staticBlocks")]
pub static_blocks: bool,
#[serde(default, rename = "privateInObject")]
pub private_in_object: bool,
#[serde(default, rename = "allowSuperOutsideMethod")]
pub allow_super_outside_method: bool,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Context {
ignore_error: bool,
module: bool,
can_be_module: bool,
strict: bool,
include_in_expr: bool,
in_async: bool,
in_generator: bool,
is_continue_allowed: bool,
is_break_allowed: bool,
in_type: bool,
in_declare: bool,
in_cond_expr: bool,
is_direct_child_of_cond: bool,
in_function: bool,
in_arrow_function: bool,
is_direct_child_of_braceless_arrow_function: bool,
in_parameters: bool,
has_super_class: bool,
in_property_name: bool,
in_forced_jsx_context: bool,
dont_parse_colon_as_type_ann: bool,
allow_direct_super: bool,
ignore_else_clause: bool,
}
#[cfg(test)]
fn with_test_sess<F, Ret>(src: &str, f: F) -> Result<Ret, ::testing::StdErr>
where
F: FnOnce(&swc_common::errors::Handler, StringInput<'_>) -> Result<Ret, ()>,
{
use swc_common::FileName;
::testing::run_test(false, |cm, handler| {
let fm = cm.new_source_file(FileName::Real("testing".into()), src.into());
f(handler, (&*fm).into())
})
}
pub fn with_file_parser<T>(
fm: &SourceFile,
syntax: Syntax,
target: EsVersion,
comments: Option<&dyn Comments>,
recovered_errors: &mut Vec<Error>,
op: impl for<'aa> FnOnce(&mut Parser<Lexer<SourceFileInput<'aa>>>) -> PResult<T>,
) -> PResult<T> {
let lexer = Lexer::new(syntax, target, SourceFileInput::from(fm), comments);
let mut p = Parser::new_from(lexer);
let ret = op(&mut p);
recovered_errors.append(&mut p.take_errors());
ret
}
macro_rules! expose {
(
$name:ident,
$T:ty,
$($t:tt)*
) => {
pub fn $name(
fm: &SourceFile,
syntax: Syntax,
target: EsVersion,
comments: Option<&dyn Comments>,
recovered_errors: &mut Vec<Error>,
) -> PResult<$T> {
with_file_parser(fm, syntax, target, comments, recovered_errors, $($t)*)
}
};
}
expose!(parse_file_as_expr, Box<Expr>, |p| { p.parse_expr() });
expose!(parse_file_as_module, Module, |p| { p.parse_module() });
expose!(parse_file_as_script, Script, |p| { p.parse_script() });
expose!(parse_file_as_program, Program, |p| { p.parse_program() });