aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_utils/swc.rs
diff options
context:
space:
mode:
authorLibravatar Titus Wormer <tituswormer@gmail.com>2022-10-11 09:54:56 +0200
committerLibravatar Titus Wormer <tituswormer@gmail.com>2022-10-11 09:55:16 +0200
commita4b56e7b971fa81c56a59b465f90c8016f01320d (patch)
tree7002a44087e57c8158a51dd30b6eb89eb260af2b /tests/test_utils/swc.rs
parent1fd94f512834aa7bd70f22a60229ce01edfc754e (diff)
downloadmarkdown-rs-a4b56e7b971fa81c56a59b465f90c8016f01320d.tar.gz
markdown-rs-a4b56e7b971fa81c56a59b465f90c8016f01320d.tar.bz2
markdown-rs-a4b56e7b971fa81c56a59b465f90c8016f01320d.zip
Add support for proper positional info in swc tree
* Fix some positional info in SWC error messages * Add positional info in `to_document` on duplicate layouts * Add support for `path` on `Program` (`to_swc`, `to_document`, `jsx_rewrite`), for the path of a file on disk * Add support for `development` to `jsx-rewrite`, which when defined will embed info on where tags were written into the runtime code when they are not passed * Refactor to move some utilities to `micromark_swc_utils.rs`, `swc_utils.rs`
Diffstat (limited to 'tests/test_utils/swc.rs')
-rw-r--r--tests/test_utils/swc.rs145
1 files changed, 94 insertions, 51 deletions
diff --git a/tests/test_utils/swc.rs b/tests/test_utils/swc.rs
index fb91a3b..78859b6 100644
--- a/tests/test_utils/swc.rs
+++ b/tests/test_utils/swc.rs
@@ -2,7 +2,10 @@ extern crate micromark;
extern crate swc_common;
extern crate swc_ecma_ast;
extern crate swc_ecma_parser;
-use micromark::{MdxExpressionKind, MdxSignal};
+use crate::test_utils::micromark_swc_utils::{
+ bytepos_to_point, prefix_error_with_point, RewriteContext,
+};
+use micromark::{mdast::Stop, unist::Point, Location, MdxExpressionKind, MdxSignal};
use swc_common::{
source_map::Pos, sync::Lrc, BytePos, FileName, FilePathMapping, SourceFile, SourceMap, Spanned,
};
@@ -11,10 +14,7 @@ use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
use swc_ecma_parser::{
error::Error as SwcError, parse_file_as_expr, parse_file_as_module, EsConfig, Syntax,
};
-
-// To do:
-// Use lexer in the future:
-// <https://docs.rs/swc_ecma_parser/0.99.1/swc_ecma_parser/lexer/index.html>
+use swc_ecma_visit::VisitMutWith;
/// Lex ESM in MDX with SWC.
#[allow(dead_code)]
@@ -24,40 +24,42 @@ pub fn parse_esm(value: &str) -> MdxSignal {
let result = parse_file_as_module(&file, syntax, version, None, &mut errors);
match result {
- Err(error) => swc_error_to_signal(&error, value.len(), 0, "esm"),
+ Err(error) => swc_error_to_signal(&error, "esm", value.len(), 0),
Ok(tree) => {
if errors.is_empty() {
check_esm_ast(&tree)
} else {
- if errors.len() > 1 {
- println!("parse_esm: todo: multiple errors? {:?}", errors);
- }
- swc_error_to_signal(&errors[0], value.len(), 0, "esm")
+ swc_error_to_signal(&errors[0], "esm", value.len(), 0)
}
}
}
}
/// Parse ESM in MDX with SWC.
-/// To do: figure out how to fix positional info.
/// See `drop_span` in `swc_ecma_utils` for inspiration?
#[allow(dead_code)]
-pub fn parse_esm_to_tree(value: &str) -> Result<swc_ecma_ast::Module, String> {
+pub fn parse_esm_to_tree(
+ value: &str,
+ stops: &[Stop],
+ location: Option<&Location>,
+) -> Result<swc_ecma_ast::Module, String> {
let (file, syntax, version) = create_config(value.to_string());
let mut errors = vec![];
-
let result = parse_file_as_module(&file, syntax, version, None, &mut errors);
+ let mut rewrite_context = RewriteContext {
+ stops,
+ location,
+ prefix_len: 0,
+ };
match result {
- Err(error) => Err(swc_error_to_string(&error)),
- Ok(module) => {
+ Err(error) => Err(swc_error_to_error(&error, "esm", &rewrite_context)),
+ Ok(mut module) => {
if errors.is_empty() {
+ module.visit_mut_with(&mut rewrite_context);
Ok(module)
} else {
- if errors.len() > 1 {
- println!("parse_esm_to_tree: todo: multiple errors? {:?}", errors);
- }
- Err(swc_error_to_string(&errors[0]))
+ Err(swc_error_to_error(&errors[0], "esm", &rewrite_context))
}
}
}
@@ -87,33 +89,31 @@ pub fn parse_expression(value: &str, kind: &MdxExpressionKind) -> MdxSignal {
let result = parse_file_as_expr(&file, syntax, version, None, &mut errors);
match result {
- Err(error) => swc_error_to_signal(&error, value.len(), prefix.len(), "expression"),
+ Err(error) => swc_error_to_signal(&error, "expression", value.len(), prefix.len()),
Ok(tree) => {
if errors.is_empty() {
- let place = fix_swc_position(tree.span().hi.to_usize(), prefix.len());
+ let expression_end = fix_swc_position(tree.span().hi.to_usize(), prefix.len());
let result = check_expression_ast(&tree, kind);
if matches!(result, MdxSignal::Ok) {
- whitespace_and_comments(place, value)
+ whitespace_and_comments(expression_end, value)
} else {
result
}
} else {
- if errors.len() > 1 {
- unreachable!("parse_expression: todo: multiple errors? {:?}", errors);
- }
- swc_error_to_signal(&errors[0], value.len(), prefix.len(), "expression")
+ swc_error_to_signal(&errors[0], "expression", value.len(), prefix.len())
}
}
}
}
/// Parse ESM in MDX with SWC.
-/// To do: figure out how to fix positional info.
/// See `drop_span` in `swc_ecma_utils` for inspiration?
#[allow(dead_code)]
pub fn parse_expression_to_tree(
value: &str,
kind: &MdxExpressionKind,
+ stops: &[Stop],
+ location: Option<&Location>,
) -> Result<Box<swc_ecma_ast::Expr>, String> {
// For attribute expression, a spread is needed, for which we have to prefix
// and suffix the input.
@@ -127,11 +127,21 @@ pub fn parse_expression_to_tree(
let (file, syntax, version) = create_config(format!("{}{}{}", prefix, value, suffix));
let mut errors = vec![];
let result = parse_file_as_expr(&file, syntax, version, None, &mut errors);
+ let mut rewrite_context = RewriteContext {
+ stops,
+ location,
+ prefix_len: prefix.len(),
+ };
match result {
- Err(error) => Err(swc_error_to_string(&error)),
- Ok(expr) => {
+ Err(error) => Err(swc_error_to_error(&error, "expression", &rewrite_context)),
+ Ok(mut expr) => {
if errors.is_empty() {
+ // Fix positions.
+ expr.visit_mut_with(&mut rewrite_context);
+
+ let expr_bytepos = expr.span().lo;
+
if matches!(kind, MdxExpressionKind::AttributeExpression) {
let mut obj = None;
@@ -143,27 +153,37 @@ pub fn parse_expression_to_tree(
if let Some(mut obj) = obj {
if obj.props.len() > 1 {
- Err("Unexpected extra content in spread: only a single spread is supported".into())
+ Err(create_error_message(
+ "Unexpected extra content in spread: only a single spread is supported",
+ "expression",
+ bytepos_to_point(&obj.span.lo, location).as_ref()
+ ))
} else if let Some(swc_ecma_ast::PropOrSpread::Spread(d)) = obj.props.pop()
{
Ok(d.expr)
} else {
- Err("Unexpected prop in spread: only a spread is supported".into())
+ Err(create_error_message(
+ "Unexpected prop in spread: only a spread is supported",
+ "expression",
+ bytepos_to_point(&obj.span.lo, location).as_ref(),
+ ))
}
} else {
- Err("Expected an object spread (`{...spread}`)".into())
+ Err(create_error_message(
+ "Expected an object spread (`{...spread}`)",
+ "expression",
+ bytepos_to_point(&expr_bytepos, location).as_ref(),
+ ))
}
} else {
Ok(expr)
}
} else {
- if errors.len() > 1 {
- println!(
- "parse_expression_to_tree: todo: multiple errors? {:?}",
- errors
- );
- }
- Err(swc_error_to_string(&errors[0]))
+ Err(swc_error_to_error(
+ &errors[0],
+ "expression",
+ &rewrite_context,
+ ))
}
}
}
@@ -203,10 +223,10 @@ fn check_esm_ast(tree: &Module) -> MdxSignal {
let node = &tree.body[index];
if !node.is_module_decl() {
- let place = fix_swc_position(node.span().hi.to_usize(), 0);
+ let relative = fix_swc_position(node.span().lo.to_usize(), 0);
return MdxSignal::Error(
"Unexpected statement in code: only import/exports are supported".to_string(),
- place,
+ relative,
);
}
@@ -248,24 +268,47 @@ fn check_expression_ast(tree: &Expr, kind: &MdxExpressionKind) -> MdxSignal {
/// * Else, yields `MdxSignal::Error`.
fn swc_error_to_signal(
error: &SwcError,
+ name: &str,
value_len: usize,
prefix_len: usize,
- name: &str,
) -> MdxSignal {
- let place = fix_swc_position(error.span().hi.to_usize(), prefix_len);
- let message = format!(
- "Could not parse {} with swc: {}",
- name,
- swc_error_to_string(error)
- );
+ let reason = create_error_reason(&swc_error_to_string(error), name);
+ let error_end = fix_swc_position(error.span().hi.to_usize(), prefix_len);
- if place >= value_len {
- MdxSignal::Eof(message)
+ if error_end >= value_len {
+ MdxSignal::Eof(reason)
} else {
- MdxSignal::Error(message, place)
+ MdxSignal::Error(
+ reason,
+ fix_swc_position(error.span().lo.to_usize(), prefix_len),
+ )
}
}
+fn swc_error_to_error(error: &SwcError, name: &str, context: &RewriteContext) -> String {
+ create_error_message(
+ &swc_error_to_string(error),
+ name,
+ context
+ .location
+ .and_then(|location| {
+ location.relative_to_point(
+ context.stops,
+ fix_swc_position(error.span().lo.to_usize(), context.prefix_len),
+ )
+ })
+ .as_ref(),
+ )
+}
+
+fn create_error_message(reason: &str, name: &str, point: Option<&Point>) -> String {
+ prefix_error_with_point(create_error_reason(name, reason), point)
+}
+
+fn create_error_reason(reason: &str, name: &str) -> String {
+ format!("Could not parse {} with swc: {}", name, reason)
+}
+
/// Turn an SWC error into a string.
fn swc_error_to_string(error: &SwcError) -> String {
error.kind().msg().into()