diff --git a/Cargo.lock b/Cargo.lock
index 844c02464fb2f9bfe7f23242065dc9b2904ea0c7..3a775c3a288b2a2436fad8fdfec58895a0be1e05 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -403,8 +403,6 @@ dependencies = [
"serde",
"serde_json",
"serde_yml",
- "tree-sitter",
- "tree-sitter-typescript",
]
[[package]]
@@ -924,12 +922,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-[[package]]
-name = "streaming-iterator"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
-
[[package]]
name = "strsim"
version = "0.11.1"
@@ -1004,35 +996,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
-[[package]]
-name = "tree-sitter"
-version = "0.24.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5387dffa7ffc7d2dae12b50c6f7aab8ff79d6210147c6613561fc3d474c6f75"
-dependencies = [
- "cc",
- "regex",
- "regex-syntax",
- "streaming-iterator",
- "tree-sitter-language",
-]
-
-[[package]]
-name = "tree-sitter-language"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "009994f150cc0cd50ff54917d5bc8bffe8cad10ca10d81c34da2ec421ae61782"
-
-[[package]]
-name = "tree-sitter-typescript"
-version = "0.23.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c5f76ed8d947a75cc446d5fccd8b602ebf0cde64ccf2ffa434d873d7a575eff"
-dependencies = [
- "cc",
- "tree-sitter-language",
-]
-
[[package]]
name = "unicode-bidi"
version = "0.3.18"
diff --git a/Cargo.toml b/Cargo.toml
index 30b9afddd2c4ca96c66d0acc281fb2d9d5a2a062..47edb4de13565e2dd87ae02743e9fbd64fa0c5ba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,6 +17,3 @@ oxc_parser = "0.96.0"
oxc_span = "0.96.0"
serde_json = "1.0"
serde_yml = "0.0.12"
-tree-sitter = "0.24"
-tree-sitter-typescript = "0.23"
-
diff --git a/examples/next-js.mdo b/examples/next-js.mdo
index c22c29f10601459982d195e421731f594e451ca0..dbb63eae159cab3cf042f92fd2bf8efd20d35e02 100644
--- a/examples/next-js.mdo
+++ b/examples/next-js.mdo
@@ -656,7 +656,7 @@ object, but instead uses inline JSX to fill that gap.
[lib-jsr]: https://jsr.io/@clo/lib
[lib-npm]: https://npmjs.com/@paperclover/lib
-
`next/og` is Good Too>
+next/og is Good Too>
No strong opinions. I just want to remind everyone that the `@vercel/og` package exists.
@@ -742,8 +742,9 @@ subscribe to the email list:
-
-
+
diff --git a/src/lib.rs b/src/lib.rs
index dfae36e9693e8a68dd3af15f1d9031cb824818f4..4630e702fffdff2fc963ab208bc37eb94a1ad3b2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,6 +2,8 @@ pub mod marko;
pub mod plugin;
pub mod typescript;
+use std::borrow::Cow;
+
use oxc_diagnostics::{LabeledSpan, OxcDiagnostic};
use plugin::tags::{MarkoClose, MarkoOpen};
@@ -223,3 +225,7 @@ fn validate_marko_tags(node: &markdown_it::Node, errors: &mut Vec
);
}
}
+
+pub fn err>>(str: T, offset: usize, length: usize) -> OxcDiagnostic {
+ OxcDiagnostic::error(str).and_label(LabeledSpan::new(None, offset, length))
+}
diff --git a/src/marko.rs b/src/marko.rs
index 366530a89b38bf24daeaabab2c5288f9edec37c1..9240d56928b3c6cbeb049203f68658f7d469f166 100644
--- a/src/marko.rs
+++ b/src/marko.rs
@@ -1,9 +1,12 @@
-use oxc_diagnostics::{LabeledSpan, OxcDiagnostic};
+use oxc_diagnostics::OxcDiagnostic;
use oxc_span::Span;
-use crate::typescript::{
- parse_call_arguments, parse_expr, parse_expr_without_gt, parse_fn_params_and_body,
- parse_var_binding,
+use crate::{
+ err,
+ typescript::{
+ parse_call_arguments, parse_expr, parse_expr_without_gt, parse_fn_params_and_body,
+ parse_var_binding,
+ },
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -100,13 +103,7 @@ impl<'a> LexState<'a> {
self.offset += 1;
return Ok(b);
}
- Err(
- OxcDiagnostic::error("Unexpected end of file").and_label(LabeledSpan::new(
- None,
- self.offset,
- 1,
- )),
- )
+ Err(err("Unexpected end of file", self.offset, 1))
}
pub fn expect(&mut self, expected: &str) -> Result<(), OxcDiagnostic> {
@@ -114,13 +111,7 @@ impl<'a> LexState<'a> {
self.offset += expected.len();
Ok(())
} else {
- Err(
- OxcDiagnostic::error(format!("Expected {expected}")).and_label(LabeledSpan::new(
- None,
- self.offset,
- 1,
- )),
- )
+ Err(err(format!("Expected {expected}"), self.offset, 1))
}
}
@@ -153,10 +144,7 @@ pub fn parse_open(src: &str) -> Result {
if byte == b'#' {
if has_id {
- return Err(
- OxcDiagnostic::error("Cannot specify two ID shorthands".to_string())
- .and_label(LabeledSpan::new(None, offset, name.len())),
- );
+ return Err(err("Cannot specify two ID shorthands", offset, name.len()));
}
has_id = true;
}
@@ -174,6 +162,9 @@ pub fn parse_open(src: &str) -> Result {
match byte {
b'|' => {
// parameters
+ if has_js_params {
+ return Err(err("Params already specified", offset, 1));
+ }
l.offset += 1;
l.skip_whitespace();
while l.peek_byte() != Some(b'|') && l.peek_byte().is_some() {
@@ -192,6 +183,9 @@ pub fn parse_open(src: &str) -> Result {
}
b'(' => {
// arguments
+ if has_js_arguments {
+ return Err(err("Function call already specified", offset, 1));
+ }
// TODO: METHOD SHORTHAND
println!("{}", l.peek_rest());
parse_call_arguments(&mut l)?;
@@ -202,6 +196,9 @@ pub fn parse_open(src: &str) -> Result {
if l.peek_rest().starts_with("/>") {
break;
}
+ if has_js_variable {
+ return Err(err("Variable already specified", offset, 1));
+ }
l.offset += 1;
l.skip_whitespace();
parse_var_binding(&mut l)?;
@@ -212,45 +209,55 @@ pub fn parse_open(src: &str) -> Result {
}
if is_attribute_tag && byte == b'/' {
- return Err(
- OxcDiagnostic::error("Attribute tags do not support variables")
- .and_label(LabeledSpan::new(None, offset, l.offset - offset)),
- );
+ return Err(err(
+ "Attribute tags do not support variables",
+ offset,
+ l.offset - offset,
+ ));
}
if is_attribute_tag && byte == b'(' {
- return Err(
- OxcDiagnostic::error("Attribute tags do not support arguments")
- .and_label(LabeledSpan::new(None, offset, l.offset - offset)),
- );
+ return Err(err(
+ "Attribute tags do not support arguments",
+ offset,
+ l.offset - offset,
+ ));
}
}
- // keyless value
- if l.peek_byte() == Some(b'=') {
- l.offset += 1;
- l.skip_whitespace();
- parse_expr_without_gt(&mut l)?;
- l.skip_whitespace();
- }
-
- // attributes
let mut self_closing = false;
- loop {
- match l.peek_byte() {
- None => return Err(l.expect_byte().expect_err("err")),
- Some(b'>') => {
- l.advance(1);
- break;
- }
- Some(_) => {
- if l.expect("/>").is_ok() {
- self_closing = true;
+ if !has_js_arguments {
+ // keyless value
+ if l.peek_byte() == Some(b'=') {
+ l.offset += 1;
+ l.skip_whitespace();
+ parse_expr_without_gt(&mut l)?;
+ l.skip_whitespace();
+ }
+
+ // attributes
+ loop {
+ match l.peek_byte() {
+ None => return Err(l.expect_byte().expect_err("err")),
+ Some(b'>') => {
+ l.advance(1);
break;
}
- parse_attribute(&mut l)?;
- l.skip_whitespace();
+ Some(_) => {
+ if l.expect("/>").is_ok() {
+ self_closing = true;
+ break;
+ }
+ parse_attribute(&mut l)?;
+ l.skip_whitespace();
+ }
}
}
+ } else {
+ if l.expect("/>").is_ok() {
+ self_closing = true;
+ } else {
+ l.expect(">")?;
+ }
}
Ok(Open {
@@ -299,8 +306,7 @@ fn parse_tag_name<'a>(l: &mut LexState<'a>) -> Result<&'a str, OxcDiagnostic> {
if !tag_name.is_empty() {
Ok(tag_name)
} else {
- Err(OxcDiagnostic::error("Expected tag name".to_string())
- .and_label(LabeledSpan::new(None, l.offset, 1)))
+ Err(err("Expected tag name", l.offset, 1))
}
}
@@ -316,8 +322,7 @@ fn parse_attr_name<'a>(l: &mut LexState<'a>) -> Result<&'a str, OxcDiagnostic> {
if !tag_name.is_empty() {
Ok(tag_name)
} else {
- Err(OxcDiagnostic::error("Expected attribute name".to_string())
- .and_label(LabeledSpan::new(None, l.offset, 1)))
+ Err(err("Expected attribute name", l.offset, 1))
}
}
@@ -348,10 +353,7 @@ fn parse_attribute(l: &mut LexState) -> Result<(), OxcDiagnostic> {
parse_fn_params_and_body(l)?;
l.skip_whitespace();
} else if !has_ws_or_end {
- return Err(
- OxcDiagnostic::error("Expected whitespace or '>'".to_string())
- .and_label(LabeledSpan::new(None, l.offset, 1)),
- );
+ return Err(err("Expected whitespace or '>'", l.offset, 1));
}
Ok(())
@@ -375,12 +377,15 @@ fn parse_class_name_shorthand<'a>(
if !tag_name.is_empty() {
Ok(tag_name)
} else {
- Err(OxcDiagnostic::error(if id {
- "Expected id"
- } else {
- "Expected class name"
- })
- .and_label(LabeledSpan::new(None, l.offset, 1)))
+ Err(err(
+ if id {
+ "Expected id"
+ } else {
+ "Expected class name"
+ },
+ l.offset,
+ 1,
+ ))
}
}
@@ -1095,10 +1100,10 @@ mod tests {
#[test]
fn test_combined_complex() {
assert_eq!(
- parse_open(""),
+ parse_open(""),
Ok(Open {
tag_name: "my-tag",
- content: "",
+ content: "",
self_closing: false,
})
);
@@ -1270,13 +1275,11 @@ mod tests {
}
#[test]
- #[ignore = "need to fix parse_var_binding"]
fn test_tag_params_simple() {
parse_open("").unwrap();
}
#[test]
- #[ignore = "need to fix parse_var_binding"]
fn test_tag_params_destructure() {
parse_open("").unwrap();
}
diff --git a/src/plugin/statement.rs b/src/plugin/statement.rs
index 4ff40d7dc01c775acd8ef43ee1f4f67d7aba5930..55c59821e1205be090becda07237ad741c33d4db 100644
--- a/src/plugin/statement.rs
+++ b/src/plugin/statement.rs
@@ -3,7 +3,7 @@ use markdown_it::Node;
use crate::adjust_err;
use crate::plugin::{ErrorBlock, RawBlock};
-use crate::typescript::find_statement_extent;
+use crate::typescript::scan_first_statement_forbid_trailing;
/// Parse JavaScript statements (import, export, static X, server X, client X)
pub struct Rule;
@@ -11,20 +11,6 @@ pub struct Rule;
const KEYWORDS: &[&str] = &["import ", "export ", "static ", "server ", "client "];
const MARKO_PREFIXES: &[&str] = &["static ", "server ", "client "];
-/// Check if a line is a valid statement (starts with keyword and parses successfully).
-fn is_valid_statement(line: &str) -> bool {
- let trimmed = line.trim_start();
- let Some(keyword) = KEYWORDS.iter().find(|k| trimmed.starts_with(*k)) else {
- return false;
- };
- let keyword_trim = MARKO_PREFIXES
- .iter()
- .find(|k| *k == keyword)
- .map(|k| k.len())
- .unwrap_or(0);
- find_statement_extent(&trimmed[keyword_trim..]).is_ok()
-}
-
impl BlockRule for Rule {
fn run(state: &mut BlockState) -> Option<(Node, usize)> {
if state.line >= state.line_max || state.blk_indent > 0 {
@@ -37,9 +23,11 @@ impl BlockRule for Rule {
// Statements must start a new block - they can't be in the middle of a paragraph.
// Allow if: start of document, previous line is empty, or previous line is a valid statement.
if state.line > 0 {
- let prev_line = state.get_line(state.line - 1);
- let prev_is_empty = prev_line.trim().is_empty();
- if !prev_is_empty && !is_valid_statement(prev_line) {
+ let prev_line = state.get_line(state.line - 1).trim();
+ if !prev_line.is_empty()
+ && prev_line != "---"
+ && !KEYWORDS.iter().any(|k| prev_line.starts_with(*k))
+ {
return None;
}
}
@@ -52,17 +40,18 @@ impl BlockRule for Rule {
let unbounded_src = &state.src[state.line_offsets[state.line].first_nonspace..];
- let statement_end = match find_statement_extent(&unbounded_src[keyword_trim..]) {
- Ok(ok) => ok,
- Err(err) => {
- return Some((
- Node::new(ErrorBlock {
- errors: vec![adjust_err(err, keyword_trim.cast_signed())],
- }),
- 1,
- ));
- }
- };
+ let statement_end =
+ match scan_first_statement_forbid_trailing(&unbounded_src[keyword_trim..]) {
+ Ok(ok) => ok,
+ Err(err) => {
+ return Some((
+ Node::new(ErrorBlock {
+ errors: vec![adjust_err(err, keyword_trim.cast_signed())],
+ }),
+ 1,
+ ));
+ }
+ };
let total_end = keyword_trim + statement_end;
let content = &unbounded_src[0..unbounded_src.len().min(total_end + 1)];
diff --git a/src/typescript.rs b/src/typescript.rs
index f8185edfc720c5766bd1df2a6db077814c6939a2..ca3281db3ab6b68132fbac2cfa2e3dba97d53765 100644
--- a/src/typescript.rs
+++ b/src/typescript.rs
@@ -1,53 +1,93 @@
//! implement ts partial parsing helpers
-use crate::{adjust_err, marko::LexState};
+use crate::{adjust_err, err, marko::LexState};
use oxc_allocator::Allocator;
-use oxc_ast::ast::Expression;
+use oxc_ast::ast::{Expression, Statement};
use oxc_diagnostics::{LabeledSpan, OxcDiagnostic};
use oxc_span::{GetSpan, SourceType};
-use std::borrow::Cow;
-use tree_sitter::Parser;
-fn err>>(str: T, offset: usize, length: usize) -> OxcDiagnostic {
- OxcDiagnostic::error(str).and_label(LabeledSpan::new(None, offset, length))
+/// TODO: Replace with oxc parser, but oxc is tough here
+pub fn scan_first_statement_forbid_trailing(source: &str) -> Result {
+ let mut allocator = Allocator::new();
+ let expr = parse_stmt_extra(source, 0, &mut allocator)?;
+ let span = expr.span();
+ let len = (span.end - span.start) as usize;
+
+ if let Some(trailing) = source[span.end as usize..].lines().next() {
+ if trailing.trim().len() > 0 {
+ return Err(err(
+ "Trailing content not allowed here",
+ span.end as usize + (trailing.len() - trailing.trim_start().len()),
+ trailing.trim().len(),
+ ));
+ }
+ }
+ Ok(len)
}
-/// TODO: Blow up
-pub fn find_statement_extent(source: &str) -> Result {
- let mut parser = Parser::new();
-
- let language = tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into();
- parser
- .set_language(&language)
- .expect("Failed to load TypeScript grammar");
-
- let tree = parser.parse(source, None).ok_or_else(|| {
- OxcDiagnostic::error("Failed to parse").and_label(LabeledSpan::new(None, 0, 1))
- })?;
- let root = tree.root_node();
-
- let first_statement = root.named_child(0).ok_or_else(|| {
- OxcDiagnostic::error("Failed to parse").and_label(LabeledSpan::new(None, 0, 1))
- })?;
-
- if first_statement.kind() == "ERROR" {
- return Err(OxcDiagnostic::error("Invalid TypeScript/JavaScript syntax")
- .and_label(LabeledSpan::new(None, 0, first_statement.end_byte())));
+fn parse_stmt_extra<'alloc, 'src: 'alloc>(
+ source: &'src str,
+ offset: isize,
+ allocator: &'alloc mut Allocator,
+) -> Result, OxcDiagnostic> {
+ if source.is_empty() {
+ return Err(err(
+ "Expected expression, found end of file",
+ offset.max(0).cast_unsigned(),
+ 1,
+ ));
}
- let end_byte = first_statement.end_byte();
+ let source_type = SourceType::default()
+ .with_module(true)
+ .with_typescript(true);
+
+ let mut result = oxc_parser::Parser::new(allocator, source, source_type).parse();
+ if !result.errors.is_empty() && result.program.body.len() == 0 {
+ let first_err = result
+ .errors
+ .into_iter()
+ .next()
+ .expect("no errors but no result!");
+ let first_err_offset = first_err
+ .labels
+ .as_ref()
+ .ok_or_else(|| err("Expected statement", offset.max(0).cast_unsigned(), 1))?
+ .first()
+ .expect("labels, but no labels!")
+ .offset();
- let remaining = &source[end_byte..];
- let same_line = remaining
- .find('\n')
- .map(|x| &remaining[..x])
- .unwrap_or(remaining);
- if !same_line.trim().is_empty() {
- return Err(OxcDiagnostic::error("Unexpected content after statement.")
- .with_label(LabeledSpan::new(None, end_byte, same_line.len())));
+ let mut candidate = source[0..first_err_offset].trim_end();
+ let mut whitespace_groups = 0;
+ loop {
+ if candidate.is_empty() {
+ return Err(adjust_err(first_err, offset));
+ }
+ result = oxc_parser::Parser::new(allocator, candidate, source_type).parse();
+ if (!result.errors.is_empty() || result.panicked) && result.program.body.len() == 0 {
+ let before_trim = &candidate[..candidate.len() - 1];
+ let after_trim = before_trim.trim_end();
+ if after_trim.len() < before_trim.len() {
+ whitespace_groups += 1;
+ if whitespace_groups > 1 {
+ return Err(adjust_err(first_err, offset));
+ }
+ }
+ candidate = after_trim;
+ } else {
+ break;
+ }
+ }
+ } else {
+ assert!(!result.panicked);
}
- Ok(end_byte)
+ result
+ .program
+ .body
+ .into_iter()
+ .next()
+ .ok_or_else(|| err("Expected statement", offset.max(0).cast_unsigned(), 1))
}
fn parse_expr_extra<'alloc, 'src: 'alloc>(
@@ -71,15 +111,22 @@ fn parse_expr_extra<'alloc, 'src: 'alloc>(
Ok(expr) => expr,
Err(errors) => {
let first_err = errors.into_iter().next().expect("no errors but no result!");
- let first_err_offset = first_err
+ let first_label = first_err
.labels
.as_ref()
.ok_or_else(|| err("Expected expression", offset.max(0).cast_unsigned(), 1))?
.first()
- .expect("labels, but no labels!")
- .offset();
+ .expect("labels, but no labels!");
+ let first_err_offset = first_label.offset();
+ let first_err_len = first_label.len();
- let mut candidate = source[0..first_err_offset].trim_end();
+ // Use end of error span when error is at start (e.g. "Cannot assign to this expression")
+ let initial_end = if first_err_offset == 0 {
+ (first_err_offset + first_err_len).min(source.len())
+ } else {
+ first_err_offset
+ };
+ let mut candidate = source[0..initial_end].trim_end();
let mut whitespace_groups = 0;
loop {
if candidate.is_empty() {
@@ -184,6 +231,7 @@ pub fn parse_call_arguments(l: &mut LexState) -> Result {
/// parse variable binding. identifier or destructuring pattern
/// also parses optional `: Type`
pub fn parse_var_binding(l: &mut LexState) -> Result {
+ // TODO: this approach has bugs
let offset = l.offset();
let source = l.peek_rest();
if source.is_empty() {
@@ -724,52 +772,58 @@ mod tests {
assert_eq!(parse_var_binding("café"), Ok(5));
}
+ #[test]
+ fn test_var_binding_equals() {
+ assert_eq!(parse_var_binding("foo = 1"), Ok(3));
+ assert_eq!(parse_var_binding("foo| of=1>"), Ok(3));
+ }
+
#[test]
fn test_stmt_function_with_garbage() {
let source = "function hello() {\n console.log(1);\n}\n\nrandom markdown garbage";
- let end = find_statement_extent(source).unwrap();
+ let end = scan_first_statement_forbid_trailing(source).unwrap();
assert_eq!(&source[..end], "function hello() {\n console.log(1);\n}");
}
#[test]
fn test_stmt_import_with_garbage() {
let source = "import { foo } from 'bar';\n\n# markdown heading";
- let end = find_statement_extent(source).unwrap();
+ let end = scan_first_statement_forbid_trailing(source).unwrap();
assert_eq!(&source[..end], "import { foo } from 'bar';");
}
#[test]
fn test_stmt_interface_with_garbage() {
let source = "interface Foo {\n bar: string;\n}\n\nsome text";
- let end = find_statement_extent(source).unwrap();
+ let end = scan_first_statement_forbid_trailing(source).unwrap();
assert_eq!(&source[..end], "interface Foo {\n bar: string;\n}");
}
#[test]
fn test_stmt_const_declaration() {
let source = "const answer = 42;\n\n# Next section";
- let end = find_statement_extent(source).unwrap();
+ let end = scan_first_statement_forbid_trailing(source).unwrap();
assert_eq!(&source[..end], "const answer = 42;");
}
#[test]
fn test_stmt_expression_statement() {
let source = "console.log('hello');\n\nmore content";
- let end = find_statement_extent(source).unwrap();
+ let end = scan_first_statement_forbid_trailing(source).unwrap();
assert_eq!(&source[..end], "console.log('hello');");
}
#[test]
fn test_stmt_pure_garbage() {
let source = "random unexpected garbage";
- let result = find_statement_extent(source);
+ let result = scan_first_statement_forbid_trailing(source);
assert!(result.is_err());
}
#[test]
fn test_stmt_multiline_function() {
let source = "function sort(items) {\n while (!isSorted()) {\n shuffle(items);\n }\n return items;\n}\n\n# Heading";
- let end = find_statement_extent(source).unwrap();
+ let end = scan_first_statement_forbid_trailing(source).unwrap();
assert!(source[..end].contains("return items;"));
assert!(source[..end].contains("}"));
}
@@ -777,14 +831,14 @@ mod tests {
#[test]
fn test_stmt_trailing_content_on_same_line() {
let source = "const x = 1; const y = 2;";
- let result = find_statement_extent(source);
+ let result = scan_first_statement_forbid_trailing(source);
assert!(result.is_err());
}
#[test]
fn test_stmt_trailing_whitespace_ok() {
let source = "const x = 1; \n\nmore content";
- let result = find_statement_extent(source);
+ let result = scan_first_statement_forbid_trailing(source);
assert!(result.is_ok());
}
}
diff --git a/tests/fixtures.rs b/tests/fixtures.rs
index 5546dd3ba6648e6454c76211cb698ffb5192a5ad..b71571a07019e283c3ffb44dc548a0068061e674 100644
--- a/tests/fixtures.rs
+++ b/tests/fixtures.rs
@@ -147,3 +147,15 @@ fn fixture_16_same_line_text() {
fn fixture_17_consecutive_statements() {
run_fixture("17-consecutive-statements");
}
+
+#[ignore = "known bug"]
+#[test]
+fn fixture_18_multiline_tag_with_value() {
+ run_fixture("18-multiline-tag-with-value");
+}
+
+#[ignore = "known bug"]
+#[test]
+fn fixture_19_extra_cases() {
+ run_fixture("19-extra-cases");
+}
diff --git a/tests/fixtures/13-kitchen-sink.marko b/tests/fixtures/13-kitchen-sink.marko
index 0d3dd4baf05ef155ef24a692bdf796bf49d6fb19..0be8f9a75eb5e952f1ea85171e869f579bf3c2c5 100644
--- a/tests/fixtures/13-kitchen-sink.marko
+++ b/tests/fixtures/13-kitchen-sink.marko
@@ -1,5 +1,4 @@
export const title = "Full Example";
-
import Chart from "./chart.marko";
static const year = 2026;
export const slug = "full-example";
@@ -19,7 +18,7 @@ server {
-No data available. Visit example.com for help.
+No data available. Visit example.com for help.
diff --git a/tests/fixtures/13-kitchen-sink.mdo b/tests/fixtures/13-kitchen-sink.mdo
index 27c708c71c193f6b5ec85bf3a9f6bf1fe7266580..cf17139a9a8c1d080af8ad6c7e9f43b5ecbb7b20 100644
--- a/tests/fixtures/13-kitchen-sink.mdo
+++ b/tests/fixtures/13-kitchen-sink.mdo
@@ -1,11 +1,8 @@
---
title: Full Example
---
-
import Chart from "./chart.marko";
-
static const year = 2026;
-
export const slug = "full-example";
// Page starts here
@@ -19,16 +16,19 @@ server {
}
- ## Chart
-
+## Chart
-
- - **${point.label}**: ${point.value}
-
+
+
+
+
+- **${point.label}**: ${point.value}
+
+
- No data available. Visit for help.
+No data available. Visit for help.
diff --git a/tests/fixtures/18-multiline-tag-with-value.marko b/tests/fixtures/18-multiline-tag-with-value.marko
new file mode 100644
index 0000000000000000000000000000000000000000..5ae522df641ac04596d794ec208a30f21d8c4916
--- /dev/null
+++ b/tests/fixtures/18-multiline-tag-with-value.marko
@@ -0,0 +1,6 @@
+
+hi
+
diff --git a/tests/fixtures/18-multiline-tag-with-value.mdo b/tests/fixtures/18-multiline-tag-with-value.mdo
new file mode 100644
index 0000000000000000000000000000000000000000..3b351ccf6b7b466330afbd44b4dd3678fc9846f3
--- /dev/null
+++ b/tests/fixtures/18-multiline-tag-with-value.mdo
@@ -0,0 +1,9 @@
+
+
+
diff --git a/tests/fixtures/19-extra-cases.marko b/tests/fixtures/19-extra-cases.marko
new file mode 100644
index 0000000000000000000000000000000000000000..8df27aa35e461d8ad2d621211d1d2197db7c0c60
--- /dev/null
+++ b/tests/fixtures/19-extra-cases.marko
@@ -0,0 +1,3 @@
+
+5)>
diff --git a/tests/fixtures/19-extra-cases.mdo b/tests/fixtures/19-extra-cases.mdo
new file mode 100644
index 0000000000000000000000000000000000000000..c112954d2d96388f67ff84ff0a153804b887b3f3
--- /dev/null
+++ b/tests/fixtures/19-extra-cases.mdo
@@ -0,0 +1,4 @@
+
+
+5)>