From 5c2063aad68a6d860289830780f1ba52ee4c0e44 Mon Sep 17 00:00:00 2001 From: leynos Date: Mon, 25 May 2026 21:35:10 +0200 Subject: [PATCH 1/6] Align html5ever and rcdom dependencies (#278) Upgrade `html5ever` and `markup5ever_rcdom` onto the 0.39 parser stack so both crates use the same `markup5ever` trait definitions. Pin `markup5ever_rcdom` to the 0.39 line because the compatible crate is published as `0.39.0+unofficial`, while Cargo ignores build metadata in manifest requirements. --- Cargo.lock | 105 ++++++++++++++++++++--------------------------------- Cargo.toml | 4 +- 2 files changed, 41 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28882770..46b2e41b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,16 +249,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - [[package]] name = "futures-core" version = "0.3.31" @@ -334,16 +324,12 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "html5ever" -version = "0.27.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" +checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8" dependencies = [ "log", - "mac", "markup5ever", - "proc-macro2", - "quote", - "syn", ] [[package]] @@ -402,31 +388,22 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - [[package]] name = "markup5ever" -version = "0.12.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" +checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de" dependencies = [ "log", - "phf", - "phf_codegen", - "string_cache", - "string_cache_codegen", "tendril", + "web_atoms", ] [[package]] name = "markup5ever_rcdom" -version = "0.3.0" +version = "0.39.0+unofficial" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18" +checksum = "3ac010f19d6c4af81eeb4018a39d7a115de9d285af45c126a4ac02e6fc5716b7" dependencies = [ "html5ever", "markup5ever", @@ -519,18 +496,19 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ "phf_shared", + "serde", ] [[package]] name = "phf_codegen" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" dependencies = [ "phf_generator", "phf_shared", @@ -538,19 +516,19 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ + "fastrand", "phf_shared", - "rand", ] [[package]] name = "phf_shared" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ "siphasher", ] @@ -636,21 +614,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - [[package]] name = "rayon" version = "1.11.0" @@ -830,9 +793,9 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "string_cache" -version = "0.8.9" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" dependencies = [ "new_debug_unreachable", "parking_lot", @@ -843,9 +806,9 @@ dependencies = [ [[package]] name = "string_cache_codegen" -version = "0.5.4" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" dependencies = [ "phf_generator", "phf_shared", @@ -880,17 +843,16 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "tendril" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" dependencies = [ - "futf", - "mac", + "new_debug_unreachable", "utf-8", ] @@ -976,6 +938,18 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "web_atoms" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + [[package]] name = "windows-link" version = "0.2.1" @@ -1157,11 +1131,10 @@ dependencies = [ [[package]] name = "xml5ever" -version = "0.18.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bbb26405d8e919bc1547a5aa9abc95cbfa438f04844f5fdd9dc7596b748bf69" +checksum = "5ab627f34ff61b80d756180d556f9c68801d836d271b3b8c094504ceca69d221" dependencies = [ "log", - "mac", "markup5ever", ] diff --git a/Cargo.toml b/Cargo.toml index d0567311..864bd956 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,8 @@ clap = { version = "4", features = ["derive"] } regex = "1" once_cell = "1" rayon = "1.11" -html5ever = "0.27" -markup5ever_rcdom = "0.3" +html5ever = "0.39.0" +markup5ever_rcdom = "=0.39.0" textwrap = "0.16.2" unicode-width = "0.2" From d325be9685739cc8e9f26c07be3fb0df9a2864c8 Mon Sep 17 00:00:00 2001 From: leynos Date: Mon, 25 May 2026 21:50:57 +0200 Subject: [PATCH 2/6] Narrow test prelude lint suppression (#278) Remove the module-level `unfulfilled_lint_expectations` allowance from the integration test prelude. Use item-level `unused_imports` allowances for the shared re-exports because each integration test compiles the prelude independently and consumes a different subset of those utilities. --- tests/prelude/mod.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/prelude/mod.rs b/tests/prelude/mod.rs index 08d85145..fb24f715 100644 --- a/tests/prelude/mod.rs +++ b/tests/prelude/mod.rs @@ -1,15 +1,16 @@ //! Common imports for integration tests. -#![allow(unfulfilled_lint_expectations)] -#[expect(unused_imports, reason = "re-exporting common test utilities")] +// Each integration test compiles this prelude as its own module and consumes a +// different subset of the shared utilities. +#[allow(unused_imports)] pub use assert_cmd::{Command, prelude::*}; -#[expect(unused_imports, reason = "re-exporting common test utilities")] +#[allow(unused_imports)] pub use predicates::prelude::*; -#[expect(unused_imports, reason = "re-exporting common test utilities")] +#[allow(unused_imports)] pub use rstest::{fixture, rstest}; #[macro_use] #[path = "../common/mod.rs"] mod common; -#[expect(unused_imports, reason = "re-exporting common test utilities")] +#[allow(unused_imports)] pub use common::*; From 0b795b06917b0efdbdd7d5abe8724a721649bd81 Mon Sep 17 00:00:00 2001 From: leynos Date: Mon, 25 May 2026 21:52:48 +0200 Subject: [PATCH 3/6] Document html5ever rcdom version coupling (#278) Add a manifest comment explaining why `markup5ever_rcdom` is pinned to match the `html5ever` parser stack. This makes the dependency coupling visible before future updates change either crate independently. --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 864bd956..173d4325 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,8 @@ regex = "1" once_cell = "1" rayon = "1.11" html5ever = "0.39.0" +# `markup5ever_rcdom` must stay on the same parser stack as `html5ever` +# because `RcDom` implements `TreeSink` from that shared `markup5ever` line. markup5ever_rcdom = "=0.39.0" textwrap = "0.16.2" unicode-width = "0.2" From cdbd1814da7df26353b571f411773ad69028a045 Mon Sep 17 00:00:00 2001 From: leynos Date: Mon, 25 May 2026 21:54:46 +0200 Subject: [PATCH 4/6] Use caret requirement for rcdom (#278) Replace the exact `markup5ever_rcdom` pin with the repository-standard caret requirement so compatible 0.39 patch updates remain available. Keep the coupling comment because `RcDom` still needs to track the same parser stack as `html5ever`. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 173d4325..a33ccf8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ rayon = "1.11" html5ever = "0.39.0" # `markup5ever_rcdom` must stay on the same parser stack as `html5ever` # because `RcDom` implements `TreeSink` from that shared `markup5ever` line. -markup5ever_rcdom = "=0.39.0" +markup5ever_rcdom = "0.39.0" textwrap = "0.16.2" unicode-width = "0.2" From 42113ec670e922e547af5bb0ef6332a1d395f0df Mon Sep 17 00:00:00 2001 From: leynos Date: Mon, 25 May 2026 23:47:59 +0200 Subject: [PATCH 5/6] Replace test prelude with direct imports (#278) Remove the integration test prelude module and import only the helpers each consumer actually uses. Keep shared macros wired through direct `common` module paths so tests no longer need broad prelude re-exports or unused-import suppression. --- tests/breaks.rs | 5 +++-- tests/cli.rs | 6 ++++-- tests/cli_fences.rs | 5 +++-- tests/code_emphasis.rs | 5 +++-- tests/fences.rs | 3 ++- tests/footnotes.rs | 8 ++++---- tests/lists.rs | 5 +++-- tests/markdownlint.rs | 3 ++- tests/parallel.rs | 6 ++++-- tests/prelude/mod.rs | 16 ---------------- tests/table/mod.rs | 5 +++-- tests/wrap/mod.rs | 5 +++-- tests/wrap_renumber.rs | 3 ++- 13 files changed, 36 insertions(+), 39 deletions(-) delete mode 100644 tests/prelude/mod.rs diff --git a/tests/breaks.rs b/tests/breaks.rs index e5557092..aff60779 100644 --- a/tests/breaks.rs +++ b/tests/breaks.rs @@ -4,11 +4,12 @@ use std::borrow::Cow; +use assert_cmd::Command; use mdtablefix::{THEMATIC_BREAK_LEN, format_breaks}; #[macro_use] -mod prelude; -use prelude::*; +#[path = "common/mod.rs"] +mod common; #[test] fn test_format_breaks_basic() { diff --git a/tests/cli.rs b/tests/cli.rs index 840a5482..472a10d7 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -11,12 +11,14 @@ use std::{ io::Write, }; +use assert_cmd::Command; use rstest::rstest; use tempfile::tempdir; #[macro_use] -mod prelude; -use prelude::*; +#[path = "common/mod.rs"] +mod common; +use common::broken_table; /// Verifies that the CLI fails when the `--in-place` flag is used without specifying a file. /// diff --git a/tests/cli_fences.rs b/tests/cli_fences.rs index fb1b67f4..4f519e01 100644 --- a/tests/cli_fences.rs +++ b/tests/cli_fences.rs @@ -1,8 +1,9 @@ //! CLI regression tests for fence normalization edge cases. #[macro_use] -mod prelude; -use prelude::*; +#[path = "common/mod.rs"] +mod common; +use common::run_cli_with_stdin; #[test] fn test_cli_fences_preserves_nested_backtick_block() { diff --git a/tests/code_emphasis.rs b/tests/code_emphasis.rs index 8cc7cacd..0cbd749e 100644 --- a/tests/code_emphasis.rs +++ b/tests/code_emphasis.rs @@ -2,10 +2,11 @@ //! //! Verifies that emphasis markers adjacent to inline code are normalised. -mod prelude; use std::fs; -use prelude::{run_cli_with_args, run_cli_with_stdin}; +#[path = "common/mod.rs"] +mod common; +use common::{run_cli_with_args, run_cli_with_stdin}; use tempfile::tempdir; #[test] diff --git a/tests/fences.rs b/tests/fences.rs index 297f60c9..51da2599 100644 --- a/tests/fences.rs +++ b/tests/fences.rs @@ -1,7 +1,8 @@ //! Tests for fence normalisation functionality. #[macro_use] -mod prelude; +#[path = "common/mod.rs"] +mod common; use mdtablefix::{attach_orphan_specifiers, compress_fences}; use rstest::rstest; diff --git a/tests/footnotes.rs b/tests/footnotes.rs index 8456c8f7..66b0d97a 100644 --- a/tests/footnotes.rs +++ b/tests/footnotes.rs @@ -2,20 +2,20 @@ //! //! Each test processes a complete Markdown document using //! `convert_footnotes`. Inputs are loaded from fixture files through the -//! `include_lines!` and `lines_vec!` macros re-exported by `tests::prelude`. +//! `include_lines!` and `lines_vec!` macros from the shared test utilities. //! The cases mix headings, code blocks and ordinary text to confirm that //! inline references become footnote links; eligible trailing numeric lists are //! rewritten as definition-style footnotes when at least one footnote reference exists; //! footnotes are renumbered sequentially with definitions reordered to match. //! -//! A simple check ensures these macros are available so the prelude exports -//! are correctly wired for all integration tests. +//! A simple check ensures these macros are available for integration tests. use mdtablefix::{convert_footnotes, process_stream}; use rstest::rstest; #[macro_use] -mod prelude; +#[path = "common/mod.rs"] +mod common; #[test] fn macros_available() { diff --git a/tests/lists.rs b/tests/lists.rs index 0b7824d1..c0447a89 100644 --- a/tests/lists.rs +++ b/tests/lists.rs @@ -1,11 +1,12 @@ //! Integration tests for list renumbering. +use assert_cmd::Command; use mdtablefix::renumber_lists; use rstest::rstest; #[macro_use] -mod prelude; -use prelude::*; +#[path = "common/mod.rs"] +mod common; #[test] fn restart_after_equal_indent_paragraph() { diff --git a/tests/markdownlint.rs b/tests/markdownlint.rs index 4cd5ba72..494d0a13 100644 --- a/tests/markdownlint.rs +++ b/tests/markdownlint.rs @@ -5,7 +5,8 @@ //! after processing. Regular comments should still be wrapped normally. #[macro_use] -mod prelude; +#[path = "common/mod.rs"] +mod common; use mdtablefix::process_stream; use rstest::rstest; diff --git a/tests/parallel.rs b/tests/parallel.rs index e9622c6c..57716bd4 100644 --- a/tests/parallel.rs +++ b/tests/parallel.rs @@ -2,12 +2,14 @@ use std::{fs::File, io::Write}; +use assert_cmd::Command; use rstest::rstest; use tempfile::tempdir; #[macro_use] -mod prelude; -use prelude::*; +#[path = "common/mod.rs"] +mod common; +use common::{broken_table, run_cli_with_args}; #[rstest] fn test_cli_parallel_empty_file_list() { run_cli_with_args(&[]).success().stdout("\n"); } diff --git a/tests/prelude/mod.rs b/tests/prelude/mod.rs deleted file mode 100644 index fb24f715..00000000 --- a/tests/prelude/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! Common imports for integration tests. - -// Each integration test compiles this prelude as its own module and consumes a -// different subset of the shared utilities. -#[allow(unused_imports)] -pub use assert_cmd::{Command, prelude::*}; -#[allow(unused_imports)] -pub use predicates::prelude::*; -#[allow(unused_imports)] -pub use rstest::{fixture, rstest}; - -#[macro_use] -#[path = "../common/mod.rs"] -mod common; -#[allow(unused_imports)] -pub use common::*; diff --git a/tests/table/mod.rs b/tests/table/mod.rs index d93f61f0..f8a9531c 100644 --- a/tests/table/mod.rs +++ b/tests/table/mod.rs @@ -17,10 +17,11 @@ //! across different scenarios whilst avoiding duplication. use mdtablefix::{convert_html_tables, process_stream, reflow_table}; +use rstest::fixture; #[macro_use] -mod prelude; -use prelude::*; +#[path = "../common/mod.rs"] +mod common; #[fixture] fn malformed_table() -> Vec { diff --git a/tests/wrap/mod.rs b/tests/wrap/mod.rs index deb633e5..14d3efbb 100644 --- a/tests/wrap/mod.rs +++ b/tests/wrap/mod.rs @@ -6,8 +6,9 @@ use mdtablefix::process_stream; #[macro_use] -mod prelude; -use prelude::*; +#[path = "../common/mod.rs"] +mod common; +use common::{assert_wrapped_blockquote, assert_wrapped_list_item, run_cli_with_stdin}; mod paragraphs; mod lists; diff --git a/tests/wrap_renumber.rs b/tests/wrap_renumber.rs index 73d0d6f3..7497015b 100644 --- a/tests/wrap_renumber.rs +++ b/tests/wrap_renumber.rs @@ -3,7 +3,8 @@ use mdtablefix::{process_stream, renumber_lists}; #[macro_use] -mod prelude; +#[path = "common/mod.rs"] +mod common; #[test] fn wrap_then_renumber_preserves_order() { From ca083de1ec9e79ec8a1ae8e754d8b6b315e6e4b7 Mon Sep 17 00:00:00 2001 From: leynos Date: Tue, 26 May 2026 01:50:26 +0200 Subject: [PATCH 6/6] Document parser stack coupling (#278) Document the `html5ever` and `markup5ever_rcdom` version relationship in the architecture and developer guides. Add a trybuild pass test that compiles the `RcDom` parser sink with the active `html5ever` parser stack so future dependency bumps catch trait bound regressions at compile time. --- Cargo.lock | 140 +++++++++++++++++++++++++++++-- Cargo.toml | 1 + docs/architecture.md | 5 +- docs/developers-guide.md | 12 +++ tests/compile.rs | 7 ++ tests/ui/html5ever_rcdom_pass.rs | 12 +++ 6 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 tests/compile.rs create mode 100644 tests/ui/html5ever_rcdom_pass.rs diff --git a/Cargo.lock b/Cargo.lock index 46b2e41b..d84ca855 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,6 +360,12 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + [[package]] name = "libc" version = "0.2.174" @@ -429,6 +435,7 @@ dependencies = [ "rstest", "tempfile", "textwrap", + "trybuild", "unicode-width", ] @@ -743,24 +750,55 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + [[package]] name = "similar" version = "2.7.0" @@ -833,6 +871,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "target-triple" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" + [[package]] name = "tempfile" version = "3.23.0" @@ -856,6 +900,15 @@ dependencies = [ "utf-8", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "termtree" version = "0.5.1" @@ -873,12 +926,36 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "toml" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae2a4cf385da23d1d53bc15cdfa5c2109e93d8d362393c801e87da2f72f0e201" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.13", +] + [[package]] name = "toml_datetime" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.22.27" @@ -886,8 +963,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", - "toml_datetime", - "winnow", + "toml_datetime 0.6.11", + "winnow 0.7.13", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "trybuild" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f614c21bd3a61bad9501d75cbb7686f00386c806d7f456778432c25cf86948a" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml", ] [[package]] @@ -950,6 +1057,15 @@ dependencies = [ "string_cache_codegen", ] +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "windows-link" version = "0.2.1" @@ -1120,6 +1236,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" + [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -1138,3 +1260,9 @@ dependencies = [ "log", "markup5ever", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index a33ccf8c..d69d36c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ insta = "1.47" tempfile = "3" libc = "0.2.174" predicates = "3" +trybuild = "1" [lints.clippy] pedantic = "warn" diff --git a/docs/architecture.md b/docs/architecture.md index 3b9aefee..85204181 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -257,7 +257,10 @@ immediately follows an H2 heading when these conditions are met. `mdtablefix` can format simple HTML `` elements embedded in Markdown. These HTML tables are transformed into Markdown before the main table reflow -logic runs. That preprocessing is handled by the `convert_html_tables` function. +logic runs. That preprocessing is handled by the `convert_html_tables` +function. The parser path uses `html5ever` to build a temporary `RcDom` tree +through `markup5ever_rcdom`, then walks that tree to extract table rows and +cells. Only straightforward tables with ``, `
` and `` tags are detected. Attributes and tag casing are ignored, and complex nested or styled tables are diff --git a/docs/developers-guide.md b/docs/developers-guide.md index fb96560b..a286ba68 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -82,6 +82,18 @@ restores the separator row with widths derived from the final table body. - `format_separator_cells`: Expands separator cells to the target widths while preserving Markdown alignment markers. +## HTML parser dependency coupling + +HTML table conversion uses `html5ever` for parsing and `markup5ever_rcdom` for +the temporary DOM sink. These crates must stay on the same `markup5ever` parser +stack because `RcDom` implements the `TreeSink` trait from that shared +dependency line. If `html5ever` is upgraded, update `markup5ever_rcdom` in the +same change and run the compile-time parser integration test before merging. + +The manifest uses caret requirements rather than exact pins, so compatible +patch updates remain available. The lockfile records the concrete crate release +selected for the branch. + ## Fence normalization module `src/fences.rs` exposes the preprocessing helpers used by the `--fences` option. diff --git a/tests/compile.rs b/tests/compile.rs new file mode 100644 index 00000000..8379a15e --- /dev/null +++ b/tests/compile.rs @@ -0,0 +1,7 @@ +//! Compile-time regression tests for dependency integration. + +#[test] +fn html5ever_rcdom_parser_stack_compiles() { + let cases = trybuild::TestCases::new(); + cases.pass("tests/ui/html5ever_rcdom_pass.rs"); +} diff --git a/tests/ui/html5ever_rcdom_pass.rs b/tests/ui/html5ever_rcdom_pass.rs new file mode 100644 index 00000000..c0cb5611 --- /dev/null +++ b/tests/ui/html5ever_rcdom_pass.rs @@ -0,0 +1,12 @@ +//! Ensures `markup5ever_rcdom::RcDom` implements the `TreeSink` expected by +//! the active `html5ever` parser stack. + +use html5ever::{driver::ParseOpts, parse_document, tendril::TendrilSink}; +use markup5ever_rcdom::RcDom; + +fn main() { + let opts = ParseOpts::default(); + let dom: RcDom = parse_document(RcDom::default(), opts).one("
"); + + let _document = dom.document; +}