runtime/
typescript.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use 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}