().is_some()
diff --git a/src/plugin/frontmatter.rs b/src/plugin/frontmatter.rs
index 72979a0766a565c4585225cceeff60c739796d1e..5e4788b5de0ee35ddf8dbd794e8440fe8b30f128 100644
--- a/src/plugin/frontmatter.rs
+++ b/src/plugin/frontmatter.rs
@@ -2,7 +2,7 @@ use markdown_it::parser::block::{BlockRule, BlockState};
use markdown_it::Node;
use oxc_diagnostics::{LabeledSpan, OxcDiagnostic};
-use crate::plugin::{get_line_raw, ErrorBlock, RawBlock};
+use crate::plugin::{get_line_raw, ErrorBlock, StatementBlock};
/// Parse frontmatter (---) at document start
pub(crate) struct Rule;
@@ -243,6 +243,6 @@ impl BlockRule for Rule {
lines_consumed += 1;
}
- Some((Node::new(RawBlock { content }), lines_consumed))
+ Some((Node::new(StatementBlock { content }), lines_consumed))
}
}
diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs
index f6ffa59ad16b789aa51c7c367375bb6d2b7037d9..ba68e6a0ba648eab4c9bd4481a213fdc4483f200 100644
--- a/src/plugin/mod.rs
+++ b/src/plugin/mod.rs
@@ -40,7 +40,7 @@ impl NodeValue for ErrorBlock {
}
}
-/// A raw node that passes Marko syntax verbaitim
+/// A raw node that passes Marko syntax verbatim
#[derive(Debug)]
pub(crate) struct RawBlock {
pub content: String,
@@ -52,6 +52,19 @@ impl NodeValue for RawBlock {
}
}
+/// A statement block (import, export, static, server, client) that needs hoisting.
+/// These are kept separate from RawBlock so they can be extracted before Layout wrapping.
+#[derive(Debug)]
+pub struct StatementBlock {
+ pub content: String,
+}
+
+impl NodeValue for StatementBlock {
+ fn render(&self, _node: &Node, fmt: &mut dyn Renderer) {
+ fmt.text_raw(&self.content);
+ }
+}
+
/// Helper to get a line with original whitespace preserved
pub(crate) fn get_line_raw<'a, 'b>(state: &'a BlockState<'a, 'b>, line: usize) -> &'b str {
if line < state.line_max {
diff --git a/src/plugin/statement.rs b/src/plugin/statement.rs
index 55c59821e1205be090becda07237ad741c33d4db..eb0dbf282687bd4a1840c885625ece60f519f602 100644
--- a/src/plugin/statement.rs
+++ b/src/plugin/statement.rs
@@ -2,7 +2,7 @@ use markdown_it::parser::block::{BlockRule, BlockState};
use markdown_it::Node;
use crate::adjust_err;
-use crate::plugin::{ErrorBlock, RawBlock};
+use crate::plugin::{ErrorBlock, StatementBlock};
use crate::typescript::scan_first_statement_forbid_trailing;
/// Parse JavaScript statements (import, export, static X, server X, client X)
@@ -57,7 +57,7 @@ impl BlockRule for Rule {
let content = &unbounded_src[0..unbounded_src.len().min(total_end + 1)];
let line_count = 1 + content[..total_end].chars().filter(|&c| c == '\n').count();
- let node = Node::new(RawBlock {
+ let node = Node::new(StatementBlock {
content: content.into(),
});
Some((node, line_count))
diff --git a/tests/fixtures.rs b/tests/fixtures.rs
index 174b1644e12a43c9fc08d03a7c3d3d8b5911aebc..50fcc6a432c55795f90ce0237786948f30f60849 100644
--- a/tests/fixtures.rs
+++ b/tests/fixtures.rs
@@ -20,7 +20,7 @@ fn run_fixture(name: &str) {
let expected = fs::read_to_string(&html_path)
.unwrap_or_else(|e| panic!("failed to read {}: {e}", html_path.display()));
- let output = transform(&source, Some(OutputFormat::Html), None).unwrap();
+ let output = transform(&source, Some(OutputFormat::Html), None, None).unwrap();
assert_eq!(
output.format,
@@ -40,7 +40,7 @@ fn run_fixture(name: &str) {
let expected = fs::read_to_string(&marko_path)
.unwrap_or_else(|e| panic!("failed to read {}: {e}", marko_path.display()));
- let output = transform(&source, Some(OutputFormat::Marko), None).unwrap();
+ let output = transform(&source, Some(OutputFormat::Marko), None, None).unwrap();
assert_eq!(
output.format,
diff --git a/tests/fixtures/13-kitchen-sink.marko b/tests/fixtures/13-kitchen-sink.marko
index 0be8f9a75eb5e952f1ea85171e869f579bf3c2c5..89f062a8f1e3949cc273d64bae2dbc078b38b78a 100644
--- a/tests/fixtures/13-kitchen-sink.marko
+++ b/tests/fixtures/13-kitchen-sink.marko
@@ -2,12 +2,12 @@ export const title = "Full Example";
import Chart from "./chart.marko";
static const year = 2026;
export const slug = "full-example";
-// Page starts here
-${title}
-This is a complete example of markodown featuring ${year}.
server {
const data = await fetchData();
}
+// Page starts here
+${title}
+This is a complete example of markodown featuring ${year}.
Chart
diff --git a/tests/fixtures/23-outline-extracting.marko b/tests/fixtures/23-outline-extracting.marko
new file mode 100644
index 0000000000000000000000000000000000000000..fbf38d068ae9b24a960651da95fcd01e644962e4
--- /dev/null
+++ b/tests/fixtures/23-outline-extracting.marko
@@ -0,0 +1,24 @@
+
+good morning
+>
+
+good night
+>
+
+snow time
+>
+
+
+content 1
+
+content 2
+
+content 3
+>
+content 4
+>
diff --git a/tests/fixtures/23-outline-extracting.mdo b/tests/fixtures/23-outline-extracting.mdo
new file mode 100644
index 0000000000000000000000000000000000000000..d97eaaab8bcc6954ae574001acb34379981d53e8
--- /dev/null
+++ b/tests/fixtures/23-outline-extracting.mdo
@@ -0,0 +1,7 @@
+good morning
+
+## good night
+
+### **snow** time
+
+rain **time**>