[oden] Load typescript kinda
This commit is contained in:
parent
cc21e8c406
commit
3968aabdb1
10 changed files with 1284 additions and 31 deletions
155
src/script.rs
155
src/script.rs
|
|
@ -1,16 +1,165 @@
|
|||
use oden_js::{
|
||||
module::loader::{ModuleLoader, ModuleSource},
|
||||
Context, ContextRef, Result, Runtime, Value,
|
||||
Context, ContextRef, Error, Result, Runtime, Value,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc::{channel, Receiver};
|
||||
|
||||
use swc_common::{
|
||||
self, comments::SingleThreadedComments, errors::Diagnostic, sync::Lrc, FileName, Globals, Mark,
|
||||
SourceMap, GLOBALS,
|
||||
};
|
||||
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
|
||||
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax};
|
||||
use swc_ecma_transforms_base::{fixer::fixer, hygiene::hygiene, resolver};
|
||||
use swc_ecma_transforms_typescript::strip;
|
||||
use swc_ecma_visit::FoldWith;
|
||||
|
||||
pub mod graphics;
|
||||
use graphics::GraphicsCommand;
|
||||
|
||||
fn transpile_to_javascript(_path: &str, input: String) -> Result<String> {
|
||||
Ok(input)
|
||||
struct DiagnosticCollector {
|
||||
cell: Rc<RefCell<Vec<Diagnostic>>>,
|
||||
}
|
||||
|
||||
impl DiagnosticCollector {
|
||||
pub fn into_handler(self) -> swc_common::errors::Handler {
|
||||
swc_common::errors::Handler::with_emitter(true, false, Box::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl swc_common::errors::Emitter for DiagnosticCollector {
|
||||
fn emit(&mut self, db: &swc_common::errors::DiagnosticBuilder<'_>) {
|
||||
use std::ops::Deref;
|
||||
self.cell.borrow_mut().push(db.deref().clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn is_fatal_swc_diagnostic(diagnostic: &Diagnostic) -> bool {
|
||||
use swc_common::errors::Level;
|
||||
match diagnostic.level {
|
||||
Level::Bug
|
||||
| Level::Cancelled
|
||||
| Level::FailureNote
|
||||
| Level::Fatal
|
||||
| Level::PhaseFatal
|
||||
| Level::Error => true,
|
||||
Level::Help | Level::Note | Level::Warning => false,
|
||||
}
|
||||
}
|
||||
fn format_swc_diagnostic(source_map: &SourceMap, diagnostic: &Diagnostic) -> String {
|
||||
if let Some(span) = &diagnostic.span.primary_span() {
|
||||
let file_name = source_map.span_to_filename(*span);
|
||||
let loc = source_map.lookup_char_pos(span.lo);
|
||||
format!(
|
||||
"{} at {}:{}:{}",
|
||||
diagnostic.message(),
|
||||
file_name,
|
||||
loc.line,
|
||||
loc.col_display + 1,
|
||||
)
|
||||
} else {
|
||||
diagnostic.message()
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_no_fatal_swc_diagnostics<'a>(
|
||||
name: &str,
|
||||
source_map: &SourceMap,
|
||||
diagnostics: impl Iterator<Item = &'a Diagnostic>,
|
||||
) -> Result<()> {
|
||||
let fatal_diagnostics = diagnostics
|
||||
.filter(|d| is_fatal_swc_diagnostic(d))
|
||||
.collect::<Vec<_>>();
|
||||
if !fatal_diagnostics.is_empty() {
|
||||
Err(Error::ParseError(
|
||||
name.into(),
|
||||
fatal_diagnostics
|
||||
.iter()
|
||||
.map(|d| format_swc_diagnostic(source_map, d))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n\n"),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn transpile_to_javascript(path: &str, input: String) -> Result<String> {
|
||||
// NOTE: This was taken almost verbatim from
|
||||
// https://github.com/swc-project/swc/blob/main/crates/swc_ecma_transforms_typescript/examples/ts_to_js.rs
|
||||
// This appears to be similar to what deno_ast does, but this has
|
||||
// the advantage of actually compiling. :P
|
||||
let cm: Lrc<SourceMap> = Default::default();
|
||||
let diagnostics_cell: Rc<RefCell<Vec<Diagnostic>>> = Rc::new(RefCell::new(Vec::new()));
|
||||
let handler = DiagnosticCollector {
|
||||
cell: diagnostics_cell.clone(),
|
||||
}
|
||||
.into_handler();
|
||||
|
||||
let fm = cm.new_source_file(FileName::Custom(path.into()), input.into());
|
||||
let comments = SingleThreadedComments::default();
|
||||
|
||||
let lexer = Lexer::new(
|
||||
Syntax::Typescript(Default::default()),
|
||||
Default::default(),
|
||||
StringInput::from(&*fm),
|
||||
Some(&comments),
|
||||
);
|
||||
|
||||
let mut parser = Parser::new_from(lexer);
|
||||
|
||||
for e in parser.take_errors() {
|
||||
e.into_diagnostic(&handler).emit();
|
||||
}
|
||||
|
||||
let module = parser
|
||||
.parse_module()
|
||||
.map_err(|e| e.into_diagnostic(&handler).emit())
|
||||
.expect("failed to parse module.");
|
||||
|
||||
let globals = Globals::default();
|
||||
GLOBALS.set(&globals, || {
|
||||
let unresolved_mark = Mark::new();
|
||||
let top_level_mark = Mark::new();
|
||||
|
||||
// Optionally transforms decorators here before the resolver pass
|
||||
// as it might produce runtime declarations.
|
||||
|
||||
// Conduct identifier scope analysis
|
||||
let module = module.fold_with(&mut resolver(unresolved_mark, top_level_mark, true));
|
||||
|
||||
// Remove typescript types
|
||||
let module = module.fold_with(&mut strip(top_level_mark));
|
||||
|
||||
// Fix up any identifiers with the same name, but different contexts
|
||||
let module = module.fold_with(&mut hygiene());
|
||||
|
||||
// Ensure that we have enough parenthesis.
|
||||
let module = module.fold_with(&mut fixer(Some(&comments)));
|
||||
|
||||
let mut buf = vec![];
|
||||
{
|
||||
let mut emitter = Emitter {
|
||||
cfg: swc_ecma_codegen::Config {
|
||||
minify: false,
|
||||
..Default::default()
|
||||
},
|
||||
cm: cm.clone(),
|
||||
comments: Some(&comments),
|
||||
wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
|
||||
};
|
||||
|
||||
emitter.emit_module(&module).unwrap();
|
||||
}
|
||||
|
||||
let diagnostics = diagnostics_cell.borrow();
|
||||
ensure_no_fatal_swc_diagnostics(path, &cm, diagnostics.iter())?;
|
||||
Ok(String::from_utf8(buf).expect("non-utf8?"))
|
||||
})
|
||||
}
|
||||
|
||||
struct Loader {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue