mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 20:32:10 +00:00
139 lines
4.5 KiB
Rust
139 lines
4.5 KiB
Rust
use crate::{lexer::Kind, Parser};
|
|
|
|
impl<'a> Parser<'a> {
|
|
/// Check if the parser is at a start of a declaration
|
|
fn at_start_of_ts_declaration_worker(&mut self) -> bool {
|
|
loop {
|
|
match self.cur_kind() {
|
|
Kind::Var | Kind::Let | Kind::Const | Kind::Function | Kind::Class | Kind::Enum => {
|
|
return true;
|
|
}
|
|
Kind::Interface | Kind::Type => {
|
|
self.bump_any();
|
|
return self.cur_kind().is_binding_identifier()
|
|
&& !self.cur_token().is_on_new_line;
|
|
}
|
|
Kind::Module | Kind::Namespace => {
|
|
self.bump_any();
|
|
return !self.cur_token().is_on_new_line
|
|
&& (self.cur_kind().is_binding_identifier()
|
|
|| self.cur_kind() == Kind::Str);
|
|
}
|
|
Kind::Abstract
|
|
| Kind::Accessor
|
|
| Kind::Async
|
|
| Kind::Declare
|
|
| Kind::Private
|
|
| Kind::Protected
|
|
| Kind::Public
|
|
| Kind::Readonly => {
|
|
self.bump_any();
|
|
if self.cur_token().is_on_new_line {
|
|
return false;
|
|
}
|
|
}
|
|
Kind::Global => {
|
|
self.bump_any();
|
|
return matches!(self.cur_kind(), Kind::Ident | Kind::LCurly | Kind::Export);
|
|
}
|
|
Kind::Import => {
|
|
self.bump_any();
|
|
return matches!(self.cur_kind(), Kind::Str | Kind::Star | Kind::LCurly)
|
|
|| self.cur_kind().is_identifier();
|
|
}
|
|
Kind::Export => {
|
|
self.bump_any();
|
|
let kind = if self.cur_kind() == Kind::Type {
|
|
self.peek_kind()
|
|
} else {
|
|
self.cur_kind()
|
|
};
|
|
// This allows constructs like
|
|
// `export *`, `export default`, `export {}`, `export = {}` along with all
|
|
// export [declaration]
|
|
if matches!(
|
|
kind,
|
|
Kind::Eq | Kind::Star | Kind::Default | Kind::LCurly | Kind::At | Kind::As
|
|
) {
|
|
return true;
|
|
}
|
|
// falls through to check next token
|
|
}
|
|
Kind::Static => {
|
|
self.bump_any();
|
|
}
|
|
_ => {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn at_start_of_ts_declaration(&mut self) -> bool {
|
|
self.lookahead(Self::at_start_of_ts_declaration_worker)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test_is_declaration {
|
|
use oxc_allocator::Allocator;
|
|
use oxc_span::SourceType;
|
|
|
|
use super::*;
|
|
|
|
fn run_check(source: &str, expected: bool) {
|
|
let alloc = Allocator::default();
|
|
let source_type = SourceType::default().with_typescript(true);
|
|
let mut parser = Parser::new(&alloc, source, source_type);
|
|
// Get the parser to the first token.
|
|
parser.bump_any();
|
|
assert_eq!(expected, parser.at_start_of_ts_declaration());
|
|
}
|
|
|
|
#[test]
|
|
fn test_lexical_declaration() {
|
|
run_check("const a = 1", true);
|
|
run_check("let a = 1", true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_combined_modifier() {
|
|
// The order of modifiers shouldn't matter
|
|
let source = "abstract async function a() { return 123; }";
|
|
let source2 = "async abstract class C{}";
|
|
run_check(source, true);
|
|
run_check(source2, true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_contextual_keyword() {
|
|
// Here abstract should not be parsed as starting a declaration
|
|
run_check("abstract = 1", false);
|
|
run_check("private = 'abc'", false);
|
|
run_check("abstract\nclass A {}", false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_export() {
|
|
run_check("export = {}", true);
|
|
run_check("export *", true);
|
|
// modifiers can be combined with expory
|
|
run_check("abstract export type T", true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_declare_module() {
|
|
run_check("declare module 'external1' {}", true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_const_enum() {
|
|
run_check("const enum A {}", true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_type_alias() {
|
|
run_check("type string = I", true);
|
|
run_check("type void = I", false);
|
|
}
|
|
}
|