1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use std::path::Path;

use swc_common::comments::SingleThreadedComments;
use swc_common::errors::{ColorConfig, Handler};
use swc_common::sync::Lrc;
use swc_common::SourceMap;
use swc_ecma_ast::Module;
use swc_ecma_parser::lexer::Lexer;
use swc_ecma_parser::{EsConfig, StringInput, Syntax, TsConfig};

#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct Parser {}

impl Parser {
    pub fn parse_file(&self, file: &Path) -> Option<(Module, Syntax)> {
        let extension = file.extension()?.to_str()?;

        log::debug!("parse file {:#?}", file);

        let syntax = match extension {
            "ts" | "tsx" | "cts" | "mts" => Syntax::Typescript(TsConfig {
                dts: file.ends_with(".d.ts"),
                tsx: extension == "tsx",
                decorators: true,
                no_early_errors: true,
            }),
            "mjs" | "js" | "jsx" | "cjs" => Syntax::Es(EsConfig {
                jsx: true,
                fn_bind: true,
                decorators: true,
                decorators_before_export: true,
                export_default_from: true,
                import_assertions: true,
                private_in_object: true,
                allow_super_outside_method: true,
                allow_return_outside_function: true,
            }),
            _ => return None,
        };

        let cm: Lrc<SourceMap> = Default::default();
        let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(cm.clone()));

        let fm = cm
            .load_file(file)
            .map_err(|error| {
                log::error!("failed to load {:#?}", error);
            })
            .ok()?;

        let comments = SingleThreadedComments::default();
        let lexer = Lexer::new(
            syntax.to_owned(),
            Default::default(),
            StringInput::from(&*fm),
            Some(&comments),
        );

        let mut parser = swc_ecma_parser::Parser::new_from(lexer);

        for error in parser.take_errors() {
            error.into_diagnostic(&handler).emit();
        }

        let module: Module = parser
            .parse_module()
            .map_err(|e| e.into_diagnostic(&handler).emit())
            .map_err(|error| {
                log::error!("failed to parser module {:#?}", error);
            })
            .ok()?;

        Some((module, syntax.to_owned()))
    }
}