| author | |
| committer | |
| log | 3c0d72e45ae96aa23bb1c9a13364fd949e90098d |
| tree | ae1a7ce6613416ecafeeebc21745c81a20fd26b9 |
| parent | ce070f60cf2f9d6c1966b3cf36c1efc1ff81e796 |
| signature |
22 files changed, 259 insertions(+), 50 deletions(-)
src/component_transforms.rs+6-9| ... | @@ -180,7 +180,7 @@ pub fn generate_layout_boilerplate(used: &UsedElements) -> String { | ... | @@ -180,7 +180,7 @@ pub fn generate_layout_boilerplate(used: &UsedElements) -> String { |
| 180 | if used.code_block { | 180 | if used.code_block { |
| 181 | out.push_str(concat!( | 181 | out.push_str(concat!( |
| 182 | "<define/CodeBlockComponentFallback__markodown__|{ language, content }|>\n", | 182 | "<define/CodeBlockComponentFallback__markodown__|{ language, content }|>\n", |
| 183 | " <pre><code class=(language && 'language-' + language)><${content}/></code></pre>\n", | 183 | " <pre><code class=(language && 'language-' + language)>${content}</code></pre>\n", |
| 184 | "</>\n", | 184 | "</>\n", |
| 185 | "<const/CodeBlockComponent__markodown__ = LayoutModule__markodown__.components?.codeBlock ?? CodeBlockComponentFallback__markodown__ />\n", | 185 | "<const/CodeBlockComponent__markodown__ = LayoutModule__markodown__.components?.codeBlock ?? CodeBlockComponentFallback__markodown__ />\n", |
| 186 | )); | 186 | )); |
| ... | @@ -279,14 +279,11 @@ impl NodeValue for CodeBlockComponentNode { | ... | @@ -279,14 +279,11 @@ impl NodeValue for CodeBlockComponentNode { |
| 279 | if let Some(meta) = &self.meta { | 279 | if let Some(meta) = &self.meta { |
| 280 | fmt.text_raw(&format!(" {}", meta)); | 280 | fmt.text_raw(&format!(" {}", meta)); |
| 281 | } | 281 | } |
| 282 | 282 | fmt.text_raw(&format!( | |
| 283 | fmt.text_raw(">\n"); | 283 | " content=\"{}\"", |
| 284 | // Escape content for Marko template literal: | 284 | escape_template_literal(&self.content) |
| 285 | // - Use {:?} to escape quotes and backslashes | 285 | )); |
| 286 | // - Additionally escape ${ to prevent nested template expressions | 286 | fmt.text_raw("/>\n"); |
| 287 | let escaped = escape_template_literal(&self.content); | ||
| 288 | fmt.text_raw(&format!("${{\"{}\" }}", escaped)); | ||
| 289 | fmt.text_raw("\n</>\n"); | ||
| 290 | } | 287 | } |
| 291 | } | 288 | } |
| 292 | 289 |
src/lib.rs+21-2| ... | @@ -461,6 +461,25 @@ pub fn err<T: Into<Cow<'static, str>>>(str: T, offset: u32, length: usize) -> Ox | ... | @@ -461,6 +461,25 @@ pub fn err<T: Into<Cow<'static, str>>>(str: T, offset: u32, length: usize) -> Ox |
| 461 | OxcDiagnostic::error(str).and_label(LabeledSpan::new(None, offset as usize, length)) | 461 | OxcDiagnostic::error(str).and_label(LabeledSpan::new(None, offset as usize, length)) |
| 462 | } | 462 | } |
| 463 | 463 | ||
| 464 | /// Convert a byte offset into `src` to 1-indexed (line, column). | ||
| 465 | /// Used by both the WASM layer and error fixture tests. | ||
| 466 | pub fn offset_to_line_col(src: &str, offset: usize) -> (u32, u32) { | ||
| 467 | let mut line = 1u32; | ||
| 468 | let mut col = 1u32; | ||
| 469 | for (i, ch) in src.char_indices() { | ||
| 470 | if i >= offset { | ||
| 471 | break; | ||
| 472 | } | ||
| 473 | if ch == '\n' { | ||
| 474 | line += 1; | ||
| 475 | col = 1; | ||
| 476 | } else { | ||
| 477 | col += 1; | ||
| 478 | } | ||
| 479 | } | ||
| 480 | (line, col) | ||
| 481 | } | ||
| 482 | |||
| 464 | #[cfg(test)] | 483 | #[cfg(test)] |
| 465 | mod tests { | 484 | mod tests { |
| 466 | use super::*; | 485 | use super::*; |
| ... | @@ -1024,7 +1043,7 @@ mod tests { | ... | @@ -1024,7 +1043,7 @@ mod tests { |
| 1024 | fn layout_code_block_uses_component_in_body() { | 1043 | fn layout_code_block_uses_component_in_body() { |
| 1025 | let out = run_with_layout("```ts\nconst x = 1;\n```", "./l.marko"); | 1044 | let out = run_with_layout("```ts\nconst x = 1;\n```", "./l.marko"); |
| 1026 | assert!( | 1045 | assert!( |
| 1027 | out.contains("<CodeBlockComponent__markodown__ language=\"ts\">"), | 1046 | out.contains("<CodeBlockComponent__markodown__ language=\"ts\""), |
| 1028 | "fenced code block should be replaced with CodeBlockComponent" | 1047 | "fenced code block should be replaced with CodeBlockComponent" |
| 1029 | ); | 1048 | ); |
| 1030 | } | 1049 | } |
| ... | @@ -1033,7 +1052,7 @@ mod tests { | ... | @@ -1033,7 +1052,7 @@ mod tests { |
| 1033 | fn layout_code_block_without_language() { | 1052 | fn layout_code_block_without_language() { |
| 1034 | let out = run_with_layout("```\nsome code\n```", "./l.marko"); | 1053 | let out = run_with_layout("```\nsome code\n```", "./l.marko"); |
| 1035 | assert!( | 1054 | assert!( |
| 1036 | out.contains("<CodeBlockComponent__markodown__>"), | 1055 | out.contains("<CodeBlockComponent__markodown__"), |
| 1037 | "code block with no language should still use CodeBlockComponent (no language attr)" | 1056 | "code block with no language should still use CodeBlockComponent (no language attr)" |
| 1038 | ); | 1057 | ); |
| 1039 | } | 1058 | } |
src/typescript.rs+51-8| ... | @@ -8,10 +8,16 @@ use oxc_span::{GetSpan, SourceType}; | ... | @@ -8,10 +8,16 @@ use oxc_span::{GetSpan, SourceType}; |
| 8 | 8 | ||
| 9 | pub fn scan_first_statement_forbid_trailing(source: &str) -> Result<u32, OxcDiagnostic> { | 9 | pub fn scan_first_statement_forbid_trailing(source: &str) -> Result<u32, OxcDiagnostic> { |
| 10 | let mut allocator = Allocator::new(); | 10 | let mut allocator = Allocator::new(); |
| 11 | let expr = parse_stmt_extra(source, 0, &mut allocator)?; | 11 | let (stmt, parse_errors) = parse_stmt_extra(source, 0, &mut allocator)?; |
| 12 | let span = expr.span(); | 12 | let span = stmt.span(); |
| 13 | let len = span.end - span.start; | 13 | let len = span.end - span.start; |
| 14 | 14 | ||
| 15 | // If OXC reported parse errors while recovering the statement, surface the | ||
| 16 | // first one directly — it points at the actual invalid token. | ||
| 17 | if let Some(first) = parse_errors.into_iter().next() { | ||
| 18 | return Err(first); | ||
| 19 | } | ||
| 20 | |||
| 15 | if let Some(trailing) = source[span.end as usize..].lines().next() { | 21 | if let Some(trailing) = source[span.end as usize..].lines().next() { |
| 16 | if !trailing.trim().is_empty() { | 22 | if !trailing.trim().is_empty() { |
| 17 | return Err(err( | 23 | return Err(err( |
| ... | @@ -28,7 +34,7 @@ fn parse_stmt_extra<'alloc, 'src: 'alloc>( | ... | @@ -28,7 +34,7 @@ fn parse_stmt_extra<'alloc, 'src: 'alloc>( |
| 28 | source: &'src str, | 34 | source: &'src str, |
| 29 | offset: isize, | 35 | offset: isize, |
| 30 | allocator: &'alloc mut Allocator, | 36 | allocator: &'alloc mut Allocator, |
| 31 | ) -> Result<Statement<'alloc>, OxcDiagnostic> { | 37 | ) -> Result<(Statement<'alloc>, Vec<OxcDiagnostic>), OxcDiagnostic> { |
| 32 | if source.is_empty() { | 38 | if source.is_empty() { |
| 33 | return Err(err( | 39 | return Err(err( |
| 34 | "Expected expression, found end of file", | 40 | "Expected expression, found end of file", |
| ... | @@ -43,9 +49,13 @@ fn parse_stmt_extra<'alloc, 'src: 'alloc>( | ... | @@ -43,9 +49,13 @@ fn parse_stmt_extra<'alloc, 'src: 'alloc>( |
| 43 | .with_jsx(false); | 49 | .with_jsx(false); |
| 44 | 50 | ||
| 45 | let mut result = oxc_parser::Parser::new(allocator, source, source_type).parse(); | 51 | let mut result = oxc_parser::Parser::new(allocator, source, source_type).parse(); |
| 46 | if !result.errors.is_empty() && result.program.body.is_empty() { | 52 | // Capture errors from the initial full-source parse. These are the only errors |
| 47 | let first_err = result | 53 | // relevant to the caller — errors from truncated-candidate loop iterations below |
| 48 | .errors | 54 | // are artifacts of parsing incomplete source and must not be surfaced. |
| 55 | let initial_errors = std::mem::take(&mut result.errors); | ||
| 56 | |||
| 57 | if !initial_errors.is_empty() && result.program.body.is_empty() { | ||
| 58 | let first_err = initial_errors | ||
| 49 | .into_iter() | 59 | .into_iter() |
| 50 | .next() | 60 | .next() |
| 51 | .expect("no errors but no result!"); | 61 | .expect("no errors but no result!"); |
| ... | @@ -84,17 +94,50 @@ fn parse_stmt_extra<'alloc, 'src: 'alloc>( | ... | @@ -84,17 +94,50 @@ fn parse_stmt_extra<'alloc, 'src: 'alloc>( |
| 84 | break; | 94 | break; |
| 85 | } | 95 | } |
| 86 | } | 96 | } |
| 97 | |||
| 98 | let stmt = result.program.body.into_iter().next().ok_or_else(|| { | ||
| 99 | err( | ||
| 100 | "Expected statement", | ||
| 101 | offset.max(0).cast_unsigned() as u32, | ||
| 102 | 1, | ||
| 103 | ) | ||
| 104 | })?; | ||
| 105 | // Surface the OXC error only if it falls within the same line as the | ||
| 106 | // recovered statement (i.e. no newline between statement end and error). | ||
| 107 | // If there's a newline before the error, it's trailing content (markdown | ||
| 108 | // or another statement) that the caller will handle. | ||
| 109 | let stmt_end = stmt.span().end as usize; | ||
| 110 | let errors = if !source[stmt_end..first_err_offset].contains('\n') { | ||
| 111 | vec![adjust_err(first_err, offset)] | ||
| 112 | } else { | ||
| 113 | vec![] | ||
| 114 | }; | ||
| 115 | return Ok((stmt, errors)); | ||
| 87 | } else { | 116 | } else { |
| 88 | assert!(!result.panicked); | 117 | assert!(!result.panicked); |
| 89 | } | 118 | } |
| 90 | 119 | ||
| 91 | result.program.body.into_iter().next().ok_or_else(|| { | 120 | // Full-source parse succeeded; surface only errors that fall within the statement's |
| 121 | // span. Errors beyond the span are about trailing content (markdown, other statements) | ||
| 122 | // and should be handled by the trailing-content check in the caller, not here. | ||
| 123 | let stmt = result.program.body.into_iter().next().ok_or_else(|| { | ||
| 92 | err( | 124 | err( |
| 93 | "Expected statement", | 125 | "Expected statement", |
| 94 | offset.max(0).cast_unsigned() as u32, | 126 | offset.max(0).cast_unsigned() as u32, |
| 95 | 1, | 127 | 1, |
| 96 | ) | 128 | ) |
| 97 | }) | 129 | })?; |
| 130 | let stmt_end = stmt.span().end as usize; | ||
| 131 | let within_stmt_errors: Vec<OxcDiagnostic> = initial_errors | ||
| 132 | .into_iter() | ||
| 133 | .filter(|e| { | ||
| 134 | e.labels | ||
| 135 | .as_deref() | ||
| 136 | .and_then(|l| l.first()) | ||
| 137 | .map_or(false, |l| l.offset() < stmt_end) | ||
| 138 | }) | ||
| 139 | .collect(); | ||
| 140 | Ok((stmt, within_stmt_errors)) | ||
| 98 | } | 141 | } |
| 99 | 142 | ||
| 100 | pub fn parse_expr_extra<'alloc, 'src: 'alloc>( | 143 | pub fn parse_expr_extra<'alloc, 'src: 'alloc>( |
src/wasm.rs+2-20| ... | @@ -27,24 +27,6 @@ struct WasmLabel { | ... | @@ -27,24 +27,6 @@ struct WasmLabel { |
| 27 | width: u32, | 27 | width: u32, |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | /// Convert a byte offset to 1-indexed line and column numbers. | ||
| 31 | fn offset_to_line_col(src: &str, offset: usize) -> (u32, u32) { | ||
| 32 | let mut line = 1u32; | ||
| 33 | let mut col = 1u32; | ||
| 34 | for (i, ch) in src.char_indices() { | ||
| 35 | if i >= offset { | ||
| 36 | break; | ||
| 37 | } | ||
| 38 | if ch == '\n' { | ||
| 39 | line += 1; | ||
| 40 | col = 1; | ||
| 41 | } else { | ||
| 42 | col += 1; | ||
| 43 | } | ||
| 44 | } | ||
| 45 | (line, col) | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Convert an OxcDiagnostic to a WasmDiagnostic. | 30 | /// Convert an OxcDiagnostic to a WasmDiagnostic. |
| 49 | /// - Takes the first label and absorbs its position into the root error. | 31 | /// - Takes the first label and absorbs its position into the root error. |
| 50 | /// - If there are multiple labels or labels with text, forward them. | 32 | /// - If there are multiple labels or labels with text, forward them. |
| ... | @@ -53,7 +35,7 @@ fn convert_diagnostic(src: &str, diag: &OxcDiagnostic) -> WasmDiagnostic { | ... | @@ -53,7 +35,7 @@ fn convert_diagnostic(src: &str, diag: &OxcDiagnostic) -> WasmDiagnostic { |
| 53 | 35 | ||
| 54 | // Determine root line/column from first label, or default to 1:1 | 36 | // Determine root line/column from first label, or default to 1:1 |
| 55 | let (line, column) = if let Some(first) = labels.first() { | 37 | let (line, column) = if let Some(first) = labels.first() { |
| 56 | offset_to_line_col(src, first.offset()) | 38 | crate::offset_to_line_col(src, first.offset()) |
| 57 | } else { | 39 | } else { |
| 58 | (1, 1) | 40 | (1, 1) |
| 59 | }; | 41 | }; |
| ... | @@ -72,7 +54,7 @@ fn convert_diagnostic(src: &str, diag: &OxcDiagnostic) -> WasmDiagnostic { | ... | @@ -72,7 +54,7 @@ fn convert_diagnostic(src: &str, diag: &OxcDiagnostic) -> WasmDiagnostic { |
| 72 | labels | 54 | labels |
| 73 | .iter() | 55 | .iter() |
| 74 | .map(|label| { | 56 | .map(|label| { |
| 75 | let (l, c) = offset_to_line_col(src, label.offset()); | 57 | let (l, c) = crate::offset_to_line_col(src, label.offset()); |
| 76 | WasmLabel { | 58 | WasmLabel { |
| 77 | message: label.label().unwrap_or_default().to_string(), | 59 | message: label.label().unwrap_or_default().to_string(), |
| 78 | line: l, | 60 | line: l, |
tests/error-fixtures.rs created+142| ... | @@ -0,0 +1,142 @@ | ||
| 1 | use markodown::{offset_to_line_col, transform, ComponentImports}; | ||
| 2 | use std::fs; | ||
| 3 | use std::path::Path; | ||
| 4 | |||
| 5 | /// Run an error fixture: transform the `.mdo` input, expect it to fail, | ||
| 6 | /// and compare the formatted errors against the `.err` file. | ||
| 7 | /// | ||
| 8 | /// Each line of the `.err` file is `line:col message`, e.g.: | ||
| 9 | /// 3:5 Mismatched closing tag: expected </div>, found </span> | ||
| 10 | /// | ||
| 11 | /// The first label on each diagnostic determines the reported position. | ||
| 12 | /// Diagnostics with no labels are reported as `1:1`. | ||
| 13 | fn run_error_fixture(name: &str) { | ||
| 14 | run_error_fixture_with_options(name, None, None); | ||
| 15 | } | ||
| 16 | |||
| 17 | fn run_error_fixture_with_options( | ||
| 18 | name: &str, | ||
| 19 | layout: Option<&str>, | ||
| 20 | component_imports: Option<ComponentImports>, | ||
| 21 | ) { | ||
| 22 | let base = Path::new("tests/error-fixtures").join(name); | ||
| 23 | let input_path = base.with_extension("mdo"); | ||
| 24 | let err_path = base.with_extension("err"); | ||
| 25 | |||
| 26 | let source = fs::read_to_string(&input_path) | ||
| 27 | .unwrap_or_else(|e| panic!("failed to read {}: {e}", input_path.display())); | ||
| 28 | let expected = fs::read_to_string(&err_path) | ||
| 29 | .unwrap_or_else(|e| panic!("failed to read {}: {e}", err_path.display())); | ||
| 30 | |||
| 31 | let result = transform( | ||
| 32 | &source, | ||
| 33 | None, | ||
| 34 | layout.map(|s| s.to_string()), | ||
| 35 | component_imports, | ||
| 36 | None, | ||
| 37 | false, | ||
| 38 | None, | ||
| 39 | ); | ||
| 40 | |||
| 41 | let errors = match result { | ||
| 42 | Err(e) => e, | ||
| 43 | Ok(_) => panic!("[{name}] expected transform to fail but it succeeded"), | ||
| 44 | }; | ||
| 45 | |||
| 46 | // Format each diagnostic as: | ||
| 47 | // error: <message> | ||
| 48 | // line:col+len [label message] | ||
| 49 | // line:col+len [label message] | ||
| 50 | // Multiple errors are separated by a blank line. | ||
| 51 | let actual_lines: Vec<String> = errors | ||
| 52 | .iter() | ||
| 53 | .map(|diag| { | ||
| 54 | let mut out = format!("error: {}", diag.message); | ||
| 55 | if let Some(labels) = diag.labels.as_deref() { | ||
| 56 | for label in labels { | ||
| 57 | let (line, col) = offset_to_line_col(&source, label.offset()); | ||
| 58 | let len = label.len(); | ||
| 59 | if let Some(msg) = label.label() { | ||
| 60 | out.push_str(&format!("\n {line}:{col}+{len} {msg}")); | ||
| 61 | } else { | ||
| 62 | out.push_str(&format!("\n {line}:{col}+{len}")); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
| 66 | out | ||
| 67 | }) | ||
| 68 | .collect(); | ||
| 69 | |||
| 70 | let actual = actual_lines.join("\n\n"); | ||
| 71 | let expected = expected.trim(); | ||
| 72 | let actual = actual.trim(); | ||
| 73 | |||
| 74 | assert_eq!( | ||
| 75 | actual, | ||
| 76 | expected, | ||
| 77 | "\n\n[{name}] error output mismatch\n\n--- expected ---\n{expected}\n--- actual ---\n{actual}\n" | ||
| 78 | ); | ||
| 79 | } | ||
| 80 | |||
| 81 | // ------------------------------------------------------------------------- | ||
| 82 | // Mismatched close tag | ||
| 83 | // ------------------------------------------------------------------------- | ||
| 84 | |||
| 85 | #[test] | ||
| 86 | fn error_fixture_mismatched_close() { | ||
| 87 | run_error_fixture("mismatched-close"); | ||
| 88 | } | ||
| 89 | |||
| 90 | // ------------------------------------------------------------------------- | ||
| 91 | // Unclosed tag | ||
| 92 | // ------------------------------------------------------------------------- | ||
| 93 | |||
| 94 | #[test] | ||
| 95 | fn error_fixture_unclosed_tag() { | ||
| 96 | run_error_fixture("unclosed-tag"); | ||
| 97 | } | ||
| 98 | |||
| 99 | // ------------------------------------------------------------------------- | ||
| 100 | // Close without open | ||
| 101 | // ------------------------------------------------------------------------- | ||
| 102 | |||
| 103 | #[test] | ||
| 104 | fn error_fixture_close_without_open() { | ||
| 105 | run_error_fixture("close-without-open"); | ||
| 106 | } | ||
| 107 | |||
| 108 | // ------------------------------------------------------------------------- | ||
| 109 | // Unclosed inline tag | ||
| 110 | // ------------------------------------------------------------------------- | ||
| 111 | |||
| 112 | #[test] | ||
| 113 | fn error_fixture_inline_unclosed() { | ||
| 114 | run_error_fixture("inline-unclosed"); | ||
| 115 | } | ||
| 116 | |||
| 117 | // ------------------------------------------------------------------------- | ||
| 118 | // Invalid template expression | ||
| 119 | // ------------------------------------------------------------------------- | ||
| 120 | |||
| 121 | #[test] | ||
| 122 | fn error_fixture_bad_template() { | ||
| 123 | run_error_fixture("bad-template"); | ||
| 124 | } | ||
| 125 | |||
| 126 | // ------------------------------------------------------------------------- | ||
| 127 | // Malformed import/statement | ||
| 128 | // ------------------------------------------------------------------------- | ||
| 129 | |||
| 130 | #[test] | ||
| 131 | fn error_fixture_bad_statement() { | ||
| 132 | run_error_fixture("bad-statement"); | ||
| 133 | } | ||
| 134 | |||
| 135 | // ------------------------------------------------------------------------- | ||
| 136 | // Invalid YAML frontmatter | ||
| 137 | // ------------------------------------------------------------------------- | ||
| 138 | |||
| 139 | #[test] | ||
| 140 | fn error_fixture_bad_frontmatter() { | ||
| 141 | run_error_fixture("bad-frontmatter"); | ||
| 142 | } | ||
tests/error-fixtures/bad-frontmatter.err created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | error: YAML syntax error: did not find expected ',' or ']' at line 3 column 1, while parsing a flow sequence at line 2 column 9 | ||
| 2 | 3:16+1 | ||
tests/error-fixtures/bad-frontmatter.mdo created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | --- | ||
| 2 | title: valid | ||
| 3 | broken: [unclosed | ||
| 4 | --- | ||
| 5 | |||
| 6 | Some content. | ||
tests/error-fixtures/bad-statement.err created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | error: Expected `from` but found `@` | ||
| 2 | 1:10+1 `from` expected | ||
tests/error-fixtures/bad-statement.mdo created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | import @@@ from "nowhere"; | ||
tests/error-fixtures/bad-template.err created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | error: Unexpected token | ||
| 2 | 1:22+1 | ||
tests/error-fixtures/bad-template.mdo created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | Some text ${@@invalid} here. | ||
tests/error-fixtures/close-without-open.err created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | error: Closing tag </div> without matching open | ||
| 2 | 3:3+3 | ||
tests/error-fixtures/close-without-open.mdo created+3| ... | @@ -0,0 +1,3 @@ | ||
| 1 | hello world | ||
| 2 | |||
| 3 | </div> | ||
tests/error-fixtures/inline-unclosed.err created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | error: Unclosed inline tag <b> | ||
| 2 | 1:14+1 | ||
tests/error-fixtures/inline-unclosed.mdo created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | This has an <b>unclosed inline tag. | ||
tests/error-fixtures/mismatched-close.err created+3| ... | @@ -0,0 +1,3 @@ | ||
| 1 | error: Mismatched closing tag: expected </div>, found </span> | ||
| 2 | 1:1+4 opened here | ||
| 3 | 3:3+4 closed here | ||
tests/error-fixtures/mismatched-close.mdo created+3| ... | @@ -0,0 +1,3 @@ | ||
| 1 | <div> | ||
| 2 | hello | ||
| 3 | </span> | ||
tests/error-fixtures/unclosed-tag.err created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | error: Unclosed tag <div> | ||
| 2 | 1:1+4 | ||
tests/error-fixtures/unclosed-tag.mdo created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | <div> | ||
| 2 | hello world | ||
tests/fixtures/25-code-block-import.marko+2-6| ... | @@ -1,9 +1,5 @@ | ... | @@ -1,9 +1,5 @@ |
| 1 | import CodeBlockComponent__markodown__ from "./code-block.marko"; | 1 | import CodeBlockComponent__markodown__ from "./code-block.marko"; |
| 2 | <h1>Code Examples</h1> | 2 | <h1>Code Examples</h1> |
| 3 | <CodeBlockComponent__markodown__ language="ts"> | 3 | <CodeBlockComponent__markodown__ language="ts" content="const greeting = `Hello \${name}!`;\nconst value = \${1 + 2};\n"/> |
| 4 | ${"const greeting = `Hello \${name}!`;\nconst value = \${1 + 2};\n" } | 4 | <CodeBlockComponent__markodown__ language="marko" content="<for|item| of=items>\n <div>\${item.name}</div>\n</for>\n"/> |
| 5 | </> | ||
| 6 | <CodeBlockComponent__markodown__ language="marko"> | ||
| 7 | ${"<for|item| of=items>\n <div>\${item.name}</div>\n</for>\n" } | ||
| 8 | </> | ||
| 9 | <p>Inline: <code>hello \${world}</code></p> | 5 | <p>Inline: <code>hello \${world}</code></p> |
tests/fixtures/29-layout-all-components.marko+2-4| ... | @@ -6,7 +6,7 @@ import * as LayoutModule__markodown__ from "./layout.marko"; | ... | @@ -6,7 +6,7 @@ import * as LayoutModule__markodown__ from "./layout.marko"; |
| 6 | </> | 6 | </> |
| 7 | <const/HeadingComponent__markodown__ = LayoutModule__markodown__.components?.heading ?? HeadingComponentFallback__markodown__ /> | 7 | <const/HeadingComponent__markodown__ = LayoutModule__markodown__.components?.heading ?? HeadingComponentFallback__markodown__ /> |
| 8 | <define/CodeBlockComponentFallback__markodown__|{ language, content }|> | 8 | <define/CodeBlockComponentFallback__markodown__|{ language, content }|> |
| 9 | <pre><code class=(language && 'language-' + language)><${content}/></code></pre> | 9 | <pre><code class=(language && 'language-' + language)>${content}</code></pre> |
| 10 | </> | 10 | </> |
| 11 | <const/CodeBlockComponent__markodown__ = LayoutModule__markodown__.components?.codeBlock ?? CodeBlockComponentFallback__markodown__ /> | 11 | <const/CodeBlockComponent__markodown__ = LayoutModule__markodown__.components?.codeBlock ?? CodeBlockComponentFallback__markodown__ /> |
| 12 | <const/LinkComponent__markodown__ = LayoutModule__markodown__.components?.link ?? 'a' /> | 12 | <const/LinkComponent__markodown__ = LayoutModule__markodown__.components?.link ?? 'a' /> |
| ... | @@ -26,7 +26,5 @@ Hello | ... | @@ -26,7 +26,5 @@ Hello |
| 26 | <BlockquoteComponent__markodown__> | 26 | <BlockquoteComponent__markodown__> |
| 27 | <p>A blockquote.</p> | 27 | <p>A blockquote.</p> |
| 28 | </> | 28 | </> |
| 29 | <CodeBlockComponent__markodown__ language="ts"> | 29 | <CodeBlockComponent__markodown__ language="ts" content="const x = 1;\n"/> |
| 30 | ${"const x = 1;\n" } | ||
| 31 | </> | ||
| 32 | </> | 30 | </> |
wasm.sh+1-1| ... | @@ -19,7 +19,7 @@ echo "Generating JS bindings..." | ... | @@ -19,7 +19,7 @@ echo "Generating JS bindings..." |
| 19 | "$WASM" | 19 | "$WASM" |
| 20 | , wasm-opt -Os lib/bindgen/markodown_bg.wasm -o lib/bindgen/markodown_bg.wasm | 20 | , wasm-opt -Os lib/bindgen/markodown_bg.wasm -o lib/bindgen/markodown_bg.wasm |
| 21 | 21 | ||
| 22 | BASE64=$(base64 < "lib/bindgen/markodown_bg.wasm") | 22 | BASE64="$(base64 -w 0 < "lib/bindgen/markodown_bg.wasm")" |
| 23 | 23 | ||
| 24 | cat > lib/bindgen/wasm_bytes.js << EOF | 24 | cat > lib/bindgen/wasm_bytes.js << EOF |
| 25 | /* @ts-self-types="./wasm_bytes.d.ts" */ | 25 | /* @ts-self-types="./wasm_bytes.d.ts" */ |