Skip to content

Commit

Permalink
extending error reporting API
Browse files Browse the repository at this point in the history
  • Loading branch information
ef4 committed Jul 12, 2023
1 parent 148b5eb commit bbd2e82
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 40 deletions.
25 changes: 13 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ swc_error_reporters = { git = "https://github.com/ef4/swc.git", branch = "conten
lazy_static = "1.4.0"

wasm-bindgen = "0.2.63"
js-sys = "0.3.64"

[dev-dependencies]
difference = "2"
Expand Down
28 changes: 16 additions & 12 deletions src/bindings.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::{Options, Preprocessor as CorePreprocessor};
use std::{fmt, path::PathBuf, str};
use std::{fmt, str};
use swc_common::{
errors::Handler,
sync::{Lock, Lrc},
SourceMap,
Spanned,
};
use swc_error_reporters::{GraphicalReportHandler, GraphicalTheme, PrettyEmitter};
use wasm_bindgen::prelude::*;
Expand All @@ -28,22 +29,26 @@ impl fmt::Write for Writer {
}
}

fn as_javascript_error(err: swc_ecma_parser::error::Error, source_map: Lrc<SourceMap>) -> JsValue {
fn capture_err_detail(err: swc_ecma_parser::error::Error, source_map: Lrc<SourceMap>, theme: GraphicalTheme) -> JsValue {
let wr = Writer::default();

let emitter = PrettyEmitter::new(
source_map,
Box::new(wr.clone()),
GraphicalReportHandler::new_themed(GraphicalTheme::unicode_nocolor()),
GraphicalReportHandler::new_themed(theme),
Default::default(),
);

let handler = Handler::with_emitter(true, false, Box::new(emitter));

err.into_diagnostic(&handler).emit();

let s = wr.0.lock().as_str().to_string();
return js_error(s.into());
s.into()
}

fn as_javascript_error(err: swc_ecma_parser::error::Error, source_map: Lrc<SourceMap>) -> JsValue {
let short_desc = format!("Parse Error at {}", source_map.span_to_string(err.span()));
let js_err = js_error(short_desc.into());
js_sys::Reflect::set(&js_err, &"source_code".into(), &capture_err_detail(err.clone(), source_map.clone(), GraphicalTheme::unicode_nocolor())).unwrap();
js_sys::Reflect::set(&js_err, &"source_code_color".into(), &capture_err_detail(err, source_map, GraphicalTheme::unicode())).unwrap();
return js_err;
}

#[wasm_bindgen]
Expand All @@ -55,13 +60,12 @@ impl Preprocessor {
}
}

pub fn process(&self, src: String) -> Result<String, JsValue> {
pub fn process(&self, src: String, filename: Option<String>) -> Result<String, JsValue> {
let result = self.core.process(
&src,
Options {
// when we start passing the filename to the preprocessor we will have a better file name here
filename: Some(PathBuf::new()),
},
filename: filename.map(|f| f.into())
}
);

match result {
Expand Down
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,14 @@ impl Preprocessor {
) -> Result<String, swc_ecma_parser::error::Error> {
let target_specifier = "template";
let target_module = "@ember/template-compiler";
let filename = options.filename.unwrap_or_else(|| "anonymous".into());
let filename = match options.filename {
Some(name) => FileName::Real(name),
None => FileName::Anon,
};

let source_file = self
.source_map
.new_source_file(FileName::Real(filename), src.to_string());
.new_source_file(filename, src.to_string());

let lexer = Lexer::new(
Syntax::Es(EsConfig {
Expand Down
42 changes: 28 additions & 14 deletions test/node-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,38 @@ describe("something", function() {
});`);
});

it("provides a useful error when there is a syntax error", function() {
it("Emits parse errors with anonymous file", function() {
expect(function() {
p.process(`const thing = "face";
<template>Hi`);
}).to.throw(`× Unexpected eof
╭─[:1:1]
1 │ const thing = "face";
2 │ <template>Hi
╰────`);
})

it("shows a graphical error info that points to the problem", function() {
}).to.throw(`Parse Error at <anon>:2:15: 2:15`);
});

it("Emits parse errors with real file", function() {
expect(function() {
p.process(`const thing = "face";
<template>Hi`, "path/to/my/component.gjs");
}).to.throw(`Parse Error at path/to/my/component.gjs:2:15: 2:15`);
});

it("Offers source_code snippet on parse errors", function() {
let parseError;
try {
p.process(`class {`)
} catch (err) {
parseError = err;
}
expect(parseError).to.have.property("source_code").matches(/Expected ident.*class \{/s);
});

it("Offers source_code_color snippet on parse errors", function() {
let parseError;
try {
p.process(`class {`)
}).to.throw(`× Expected ident
╭─[:1:1]
1 │ class {
· ─
╰────`);
} catch (err) {
parseError = err;
}
// eslint-disable-next-line no-control-regex
expect(parseError).to.have.property("source_code_color").matches(/Expected ident.*[\u001b].*class \{/s);
});
})

0 comments on commit bbd2e82

Please sign in to comment.