use std::sync::Arc;
use rspack_error::BatchErrors;
use swc_core::{
base::config::IsModule,
common::{
FileName, SourceFile,
comments::{Comments, SingleThreadedComments},
input::SourceFileInput,
},
ecma::{
ast::{EsVersion, Program as SwcProgram},
parser::{self, Parser, Syntax, lexer::Lexer, unstable::TokenAndSpan},
},
};
use super::JavaScriptCompiler;
use crate::{
ast::{self, Ast},
error::{DedupEcmaErrors, ecma_parse_error_deduped_to_rspack_error},
};
impl JavaScriptCompiler {
pub fn parse<S: Into<String>>(
self,
filename: FileName,
source: S,
target: EsVersion,
syntax: Syntax,
is_module: IsModule,
comments: Option<SingleThreadedComments>,
) -> Result<Ast, BatchErrors> {
let fm = self.cm.new_source_file(Arc::new(filename), source.into());
let lexer = Lexer::new(
syntax,
target,
SourceFileInput::from(&*fm),
comments.as_ref().map(|c| c as &dyn Comments),
);
parse_with_lexer(lexer, is_module, false)
.map(|(program, _)| {
Ast::new(program, self.cm.clone(), comments)
.with_context(ast::Context::new(self.cm, Some(self.globals)))
})
.map_err(|errs| {
BatchErrors(
errs
.dedup_ecma_errors()
.into_iter()
.map(|err| ecma_parse_error_deduped_to_rspack_error(err, fm.src.to_string()))
.collect::<Vec<_>>(),
)
})
}
pub fn parse_with_lexer(
self,
source: &str,
lexer: Lexer,
is_module: IsModule,
comments: Option<SingleThreadedComments>,
with_tokens: bool,
) -> Result<(Ast, Option<Vec<TokenAndSpan>>), BatchErrors> {
parse_with_lexer(lexer, is_module, with_tokens)
.map(|(program, tokens)| {
(
Ast::new(program, self.cm.clone(), comments)
.with_context(ast::Context::new(self.cm.clone(), Some(self.globals))),
tokens,
)
})
.map_err(|errs| {
BatchErrors(
errs
.dedup_ecma_errors()
.into_iter()
.map(|err| ecma_parse_error_deduped_to_rspack_error(err, source.to_string()))
.collect::<Vec<_>>(),
)
})
}
pub fn parse_js(
&self,
fm: Arc<SourceFile>,
target: EsVersion,
syntax: Syntax,
is_module: IsModule,
comments: Option<&dyn Comments>,
) -> Result<SwcProgram, BatchErrors> {
let lexer = Lexer::new(syntax, target, SourceFileInput::from(&*fm), comments);
parse_with_lexer(lexer, is_module, false)
.map(|(program, _)| program)
.map_err(|errs| {
BatchErrors(
errs
.dedup_ecma_errors()
.into_iter()
.map(|err| ecma_parse_error_deduped_to_rspack_error(err, fm.src.to_string()))
.collect::<Vec<_>>(),
)
})
}
}
use swc_core::ecma::parser::unstable::Capturing;
fn parse_with_lexer(
lexer: Lexer,
is_module: IsModule,
with_tokens: bool,
) -> Result<(SwcProgram, Option<Vec<TokenAndSpan>>), Vec<parser::error::Error>> {
let inner = || {
let (tokens, program_result, mut errors) = if with_tokens {
let lexer = Capturing::new(lexer);
let mut parser = Parser::new_from(lexer);
let program_result = match is_module {
IsModule::Bool(true) => parser.parse_module().map(SwcProgram::Module),
IsModule::Bool(false) => parser.parse_script().map(SwcProgram::Script),
IsModule::Unknown => parser.parse_program(),
IsModule::CommonJS => parser.parse_commonjs().map(SwcProgram::Script),
};
(
Some(parser.input_mut().iter_mut().take()),
program_result,
parser.take_errors(),
)
} else {
let mut parser = Parser::new_from(lexer);
let program_result = match is_module {
IsModule::Bool(true) => parser.parse_module().map(SwcProgram::Module),
IsModule::Bool(false) => parser.parse_script().map(SwcProgram::Script),
IsModule::Unknown => parser.parse_program(),
IsModule::CommonJS => parser.parse_commonjs().map(SwcProgram::Script),
};
(None, program_result, parser.take_errors())
};
match program_result {
Ok(program) => {
if !errors.is_empty() {
return Err(errors);
}
Ok((program, tokens))
}
Err(err) => {
errors.push(err);
Err(errors)
}
}
};
#[cfg(all(debug_assertions, not(target_family = "wasm")))]
{
stacker::maybe_grow(
2 * 1024 * 1024,
4 * 1024 * 1024,
inner,
)
}
#[cfg(any(not(debug_assertions), target_family = "wasm"))]
inner()
}