1use std::fmt;
8use std::fmt::{Display, Formatter};
9use std::path::PathBuf;
10use std::string::FromUtf8Error;
11
12use swc_core::common::comments::{Comments, SingleThreadedComments};
13use swc_core::common::errors::{ColorConfig, Handler};
14use swc_core::common::input::StringInput;
15use swc_core::common::source_map::DefaultSourceMapGenConfig;
16use swc_core::common::sync::Lrc;
17use swc_core::common::{BytePos, FileName, GLOBALS, Globals, LineCol, Mark, SourceMap as SwcSourceMap};
18use swc_core::ecma::ast::{EsVersion, Pass as _, Program};
19use swc_core::ecma::codegen::text_writer::JsWriter;
20use swc_core::ecma::codegen::{Config as CodegenConfig, Emitter};
21use swc_core::ecma::parser::lexer::Lexer;
22use swc_core::ecma::parser::{Parser, Syntax, TsSyntax};
23use swc_core::ecma::transforms::base::fixer::fixer;
24use swc_core::ecma::transforms::base::hygiene::hygiene;
25use swc_core::ecma::transforms::base::resolver;
26use swc_core::ecma::transforms::typescript::strip;
27use swc_core::ecma::visit::VisitMut as _;
28use swc_sourcemap::SourceMap;
29
30use crate::config::Config;
31
32pub fn compile_typescript(filename: &str, source: &str) -> Result<(String, SourceMap), Error> {
33 let name = Lrc::new(FileName::Real(PathBuf::from(filename)));
34
35 let source_map: Lrc<SwcSourceMap> = Lrc::default();
36 let file = source_map.new_source_file(name, String::from(source));
37 let input = StringInput::from(&*file);
38
39 let comments = SingleThreadedComments::default();
40 let (handler, mut parser) = initialise_parser(Lrc::clone(&source_map), &comments, input);
41
42 let mut buffer = Vec::new();
43 let mut mappings = Vec::new();
44 let mut emitter = initialise_emitter(Lrc::clone(&source_map), &comments, &mut buffer, &mut mappings);
45
46 let mut program = if Config::global().script {
47 Program::Script(parser.parse_script().map_err(|e| {
48 e.into_diagnostic(&handler).emit();
49 Error::Parse
50 })?)
51 } else {
52 Program::Module(parser.parse_module().map_err(|e| {
53 e.into_diagnostic(&handler).emit();
54 Error::Parse
55 })?)
56 };
57 handle_program(&mut program, &mut emitter)?;
58
59 let source_map = source_map.build_source_map(&mappings, None, DefaultSourceMapGenConfig);
60 Ok((String::from_utf8(buffer)?, source_map))
61}
62
63pub fn handle_program(
64 program: &mut Program, emitter: &mut Emitter<JsWriter<&mut Vec<u8>>, SwcSourceMap>,
65) -> Result<(), Error> {
66 let globals = Globals::default();
67 GLOBALS.set(&globals, || {
68 let unresolved_mark = Mark::new();
69 let top_level_mark = Mark::new();
70
71 resolver(unresolved_mark, top_level_mark, true).visit_mut_program(program);
72 strip(unresolved_mark, top_level_mark).process(program);
73 hygiene().visit_mut_program(program);
74 fixer(emitter.comments).visit_mut_program(program);
75 });
76
77 emitter.emit_program(program).map_err(|_| Error::Emission)
78}
79
80fn initialise_parser<'a>(
81 source_map: Lrc<SwcSourceMap>, comments: &'a dyn Comments, input: StringInput<'a>,
82) -> (Handler, Parser<Lexer<'a>>) {
83 let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(source_map));
84 let lexer = Lexer::new(
85 Syntax::Typescript(TsSyntax::default()),
86 EsVersion::Es2022,
87 input,
88 Some(comments),
89 );
90 let mut parser = Parser::new_from(lexer);
91
92 for error in parser.take_errors() {
93 error.into_diagnostic(&handler).emit();
94 }
95
96 (handler, parser)
97}
98
99fn initialise_emitter<'a>(
100 source_map: Lrc<SwcSourceMap>, comments: &'a dyn Comments, buffer: &'a mut Vec<u8>,
101 mappings: &'a mut Vec<(BytePos, LineCol)>,
102) -> Emitter<'a, JsWriter<'a, &'a mut Vec<u8>>, SwcSourceMap> {
103 Emitter {
104 cfg: CodegenConfig::default().with_target(EsVersion::Es2024),
105 cm: Lrc::clone(&source_map),
106 comments: Some(comments),
107 wr: JsWriter::new(source_map, "\n", buffer, Some(mappings)),
108 }
109}
110
111#[derive(Debug)]
112pub enum Error {
113 Parse,
114 Emission,
115 FromUtf8(FromUtf8Error),
116}
117
118impl From<FromUtf8Error> for Error {
119 fn from(err: FromUtf8Error) -> Error {
120 Error::FromUtf8(err)
121 }
122}
123
124impl Display for Error {
125 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
126 match self {
127 Error::FromUtf8(err) => f.write_str(&err.to_string()),
128 _ => Ok(()),
129 }
130 }
131}