diff --git a/Cargo.lock b/Cargo.lock index a5f88b60..33ea9180 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,15 @@ version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +[[package]] +name = "ar_archive_writer" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348" +dependencies = [ + "object", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -144,6 +153,17 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "ast_node" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb025ef00a6da925cf40870b9c8d008526b6004ece399cb0974209720f0b194" +dependencies = [ + "quote", + "swc_macros_common", + "syn 2.0.117", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -152,7 +172,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -178,7 +198,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -215,6 +235,15 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "better_scoped_tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd228125315b132eed175bf47619ac79b945b26e56b848ba203ae4ea8603609" +dependencies = [ + "scoped-tls", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -278,6 +307,9 @@ name = "bumpalo" version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +dependencies = [ + "allocator-api2", +] [[package]] name = "bytecount" @@ -297,6 +329,36 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "bytes-str" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577d2bf5650f8554d5a372af5ac93535110a0fc75b3e702bb853369febf227c2" +dependencies = [ + "bytes", + "serde", +] + +[[package]] +name = "capacity_builder" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2d24a6dcf0cd402a21b65d35340f3a49ff3475dc5fdac91d22d2733e6641c6" +dependencies = [ + "capacity_builder_macros", + "itoa", +] + +[[package]] +name = "capacity_builder_macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "cast" version = "0.3.0" @@ -408,7 +470,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -522,7 +584,7 @@ dependencies = [ "ciborium", "clap", "criterion-plot", - "itertools", + "itertools 0.13.0", "num-traits", "oorandom", "page_size", @@ -542,7 +604,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" dependencies = [ "cast", - "itertools", + "itertools 0.13.0", ] [[package]] @@ -582,6 +644,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "css_dataset" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25670139e591f1c2869eb8d0d977028f8d05e859132b4c874ecd02a00d3c9174" + [[package]] name = "dashmap" version = "5.5.3" @@ -601,6 +669,79 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + +[[package]] +name = "deno_ast" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f7c1384d87fc0a6439a065312fbef8f6ac6128689dbc2831b28b3a1d4f3a4e6" +dependencies = [ + "capacity_builder", + "deno_error", + "deno_media_type", + "deno_terminal", + "dprint-swc-ext", + "percent-encoding", + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_lexer", + "swc_ecma_parser", + "swc_eq_ignore_macros", + "text_lines", + "thiserror 2.0.18", + "unicode-width", + "url", +] + +[[package]] +name = "deno_error" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3007d3f1ea92ea503324ae15883aac0c2de2b8cf6fead62203ff6a67161007ab" +dependencies = [ + "deno_error_macro", + "libc", +] + +[[package]] +name = "deno_error_macro" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b565e60a9685cdf312c888665b5f8647ac692a7da7e058a5e2268a466da8eaf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "deno_media_type" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "debab24ecd9f4fd64aa42fb18a02dff20a97d5830b2b85b98ce70b509f790763" +dependencies = [ + "data-url", + "serde", + "url", +] + +[[package]] +name = "deno_terminal" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ba8041ae7319b3ca6a64c399df4112badcbbe0868b4517637647614bede4be" +dependencies = [ + "once_cell", + "termcolor", +] + [[package]] name = "diff" version = "0.1.13" @@ -621,7 +762,66 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "dprint-core" +version = "0.67.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1d827947704a9495f705d6aeed270fa21a67f825f22902c28f38dc3af7a9ae" +dependencies = [ + "anyhow", + "bumpalo", + "hashbrown 0.15.5", + "indexmap", + "rustc-hash", + "serde", + "unicode-width", +] + +[[package]] +name = "dprint-core-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1675ad2b358481f3cc46202040d64ac7a36c4ade414a696df32e0e45421a6e9f" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dprint-plugin-typescript" +version = "0.96.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90733c56ab425fc299c25ad81a5924bfc3c0050f25d865da742a6987b1255927" +dependencies = [ + "anyhow", + "capacity_builder", + "deno_ast", + "dprint-core", + "dprint-core-macros", + "percent-encoding", + "rustc-hash", + "serde", +] + +[[package]] +name = "dprint-swc-ext" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33175ddb7a6d418589cab2966bd14a710b3b1139459d3d5ca9edf783c4833f4c" +dependencies = [ + "allocator-api2", + "bumpalo", + "num-bigint", + "rustc-hash", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_lexer", + "swc_ecma_parser", + "text_lines", ] [[package]] @@ -808,6 +1008,16 @@ dependencies = [ "num", ] +[[package]] +name = "from_variant" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ff35a391aef949120a0340d690269b3d9f63460a6106e99bd07b961f345ea9" +dependencies = [ + "swc_macros_common", + "syn 2.0.117", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -879,7 +1089,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1015,6 +1225,10 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "hashbrown" @@ -1022,6 +1236,8 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.1.5", ] @@ -1062,6 +1278,20 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hstr" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bb87e4b300d73412f6dcc7022ee7741452b51b155c2b06e5994d0770c2dbe2" +dependencies = [ + "hashbrown 0.14.5", + "new_debug_unreachable", + "once_cell", + "rustc-hash", + "serde", + "triomphe", +] + [[package]] name = "http" version = "1.4.0" @@ -1366,6 +1596,18 @@ dependencies = [ "serde", ] +[[package]] +name = "is-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -1381,6 +1623,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -1408,7 +1659,7 @@ checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1452,7 +1703,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1594,6 +1845,22 @@ dependencies = [ "url", ] +[[package]] +name = "markup_fmt" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b53ac34c9d6498632dcca4ad4542b1aef7c605d1d19037207910c4efe060a9" +dependencies = [ + "aho-corasick", + "anyhow", + "css_dataset", + "itertools 0.14.0", + "memchr", + "regex", + "serde", + "tiny_pretty", +] + [[package]] name = "memchr" version = "2.8.0" @@ -1647,6 +1914,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -1702,6 +1975,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", + "serde", ] [[package]] @@ -1759,6 +2033,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -1834,17 +2117,37 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + [[package]] name = "phf" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ - "phf_macros", - "phf_shared", + "phf_macros 0.13.1", + "phf_shared 0.13.1", "serde", ] +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.7", +] + [[package]] name = "phf_generator" version = "0.13.1" @@ -1852,7 +2155,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ "fastrand", - "phf_shared", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -1861,11 +2177,20 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.13.1", + "phf_shared 0.13.1", "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.2", ] [[package]] @@ -1874,7 +2199,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ - "siphasher", + "siphasher 1.0.2", ] [[package]] @@ -1894,7 +2219,7 @@ checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2024,7 +2349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.117", ] [[package]] @@ -2046,7 +2371,7 @@ dependencies = [ "bit-vec", "bitflags 2.11.1", "num-traits", - "rand", + "rand 0.9.4", "rand_chacha", "rand_xorshift", "regex-syntax", @@ -2055,6 +2380,16 @@ dependencies = [ "unarray", ] +[[package]] +name = "psm" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +dependencies = [ + "ar_archive_writer", + "cc", +] + [[package]] name = "pulldown-cmark" version = "0.13.3" @@ -2101,6 +2436,15 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "rand" version = "0.9.4" @@ -2108,7 +2452,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -2118,9 +2462,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", ] +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + [[package]] name = "rand_core" version = "0.9.5" @@ -2136,7 +2486,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -2185,7 +2535,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2300,6 +2650,7 @@ dependencies = [ "colored", "console_error_panic_hook", "criterion", + "dprint-plugin-typescript", "dyn-clone", "ec4rs", "env_logger", @@ -2313,11 +2664,12 @@ dependencies = [ "jsonschema", "libc", "log", + "markup_fmt", "memory-stats", "mimalloc", "notify", "paste", - "phf", + "phf 0.13.1", "postcard", "predicates", "pretty_assertions", @@ -2346,6 +2698,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -2508,9 +2866,15 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 2.0.117", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -2546,6 +2910,12 @@ version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +[[package]] +name = "seq-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" + [[package]] name = "serde" version = "1.0.228" @@ -2584,7 +2954,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2595,7 +2965,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2620,7 +2990,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2667,7 +3037,7 @@ checksum = "94e153fc76e1c6a068703d6d29c508a0b15c061c4b7e43da59cc097bc342673c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2692,6 +3062,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "siphasher" version = "1.0.2" @@ -2710,6 +3086,17 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + [[package]] name = "socket2" version = "0.6.3" @@ -2735,6 +3122,36 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "stacker" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.61.2", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_enum" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae36a4951ca7bd1cfd991c241584a9824a70f6aff1e7d4f693fb3f2465e4030e" +dependencies = [ + "quote", + "swc_macros_common", + "syn 2.0.117", +] + [[package]] name = "strsim" version = "0.11.1" @@ -2747,6 +3164,149 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "swc_atoms" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ccbe2ecad10ad7432100f878a107b1d972a8aee83ca53184d00c23a078bb8a" +dependencies = [ + "hstr", + "once_cell", + "serde", +] + +[[package]] +name = "swc_common" +version = "17.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "259b675d633a26d24efe3802a9d88858c918e6e8f062d3222d3aa02d56a2cf4c" +dependencies = [ + "anyhow", + "ast_node", + "better_scoped_tls", + "bytes-str", + "either", + "from_variant", + "new_debug_unreachable", + "num-bigint", + "once_cell", + "rustc-hash", + "serde", + "siphasher 0.3.11", + "swc_atoms", + "swc_eq_ignore_macros", + "swc_visit", + "tracing", + "unicode-width", + "url", +] + +[[package]] +name = "swc_ecma_ast" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a573a0c72850dec8d4d8085f152d5778af35a2520c3093b242d2d1d50776da7c" +dependencies = [ + "bitflags 2.11.1", + "is-macro", + "num-bigint", + "once_cell", + "phf 0.11.3", + "rustc-hash", + "serde", + "string_enum", + "swc_atoms", + "swc_common", + "swc_visit", + "unicode-id-start", +] + +[[package]] +name = "swc_ecma_lexer" +version = "26.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e82f7747e052c6ff6e111fa4adeb14e33b46ee6e94fe5ef717601f651db48fc" +dependencies = [ + "bitflags 2.11.1", + "either", + "num-bigint", + "rustc-hash", + "seq-macro", + "serde", + "smallvec", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "tracing", +] + +[[package]] +name = "swc_ecma_parser" +version = "27.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f1a51af1a92cd4904c073b293e491bbc0918400a45d58227b34c961dd6f52d7" +dependencies = [ + "bitflags 2.11.1", + "either", + "num-bigint", + "phf 0.11.3", + "rustc-hash", + "seq-macro", + "serde", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "tracing", +] + +[[package]] +name = "swc_eq_ignore_macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c16ce73424a6316e95e09065ba6a207eba7765496fed113702278b7711d4b632" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "swc_macros_common" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1efbaa74943dc5ad2a2fb16cbd78b77d7e4d63188f3c5b4df2b4dcd2faaae" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "swc_visit" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62fb71484b486c185e34d2172f0eabe7f4722742aad700f426a494bb2de232a2" +dependencies = [ + "either", + "new_debug_unreachable", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.117" @@ -2775,7 +3335,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2791,12 +3351,30 @@ dependencies = [ "windows-sys 0.61.2", ] +[[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" +[[package]] +name = "text_lines" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd5828de7deaa782e1dd713006ae96b3bee32d3279b79eb67ecf8072c059bcf" +dependencies = [ + "serde", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -2823,7 +3401,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2834,7 +3412,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2857,6 +3435,12 @@ dependencies = [ "tikv-jemalloc-sys", ] +[[package]] +name = "tiny_pretty" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6f118a569adc26e8a9a377130f181a461770225e588c73a00c8636b8680b6c" + [[package]] name = "tinystr" version = "0.8.3" @@ -2917,7 +3501,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3079,7 +3663,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3107,7 +3691,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3119,6 +3703,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "triomphe" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b40688ea6389c8171614b25491f71d4a27946e0c7ce2da1c6de27e25abf1a0ae" +dependencies = [ + "serde", + "stable_deref_trait", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -3143,6 +3737,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f" +[[package]] +name = "unicode-id-start" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -3364,7 +3964,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -3491,7 +4091,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3502,7 +4102,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3790,7 +4390,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -3806,7 +4406,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -3879,7 +4479,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -3900,7 +4500,7 @@ checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3920,7 +4520,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -3960,7 +4560,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8115fba8..9edb206a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,8 @@ schemars = "1.1" unicode-width = "0.2" memory-stats = { version = "1.2.0", optional = true } phf = { version = "0.13", features = ["macros"] } +markup_fmt = { version = "0.27.3", features = ["serde"], optional = true } +dprint-plugin-typescript = { version = "0.96.1", optional = true } # LSP dependencies (native only) tower-lsp = { version = "0.20", optional = true } @@ -94,7 +96,8 @@ clap_complete = "4.5.65" ec4rs = { version = "1.2.0", features = ["track-source"], optional = true } [features] -default = ["parallel", "native"] +default = ["parallel", "native", "html-fmt"] +html-fmt = ["dep:markup_fmt", "dep:dprint-plugin-typescript"] parallel = ["rayon"] profiling = [] native = [ diff --git a/README.md b/README.md index 0b4571e1..3e30c522 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Python linting, rumdl brings similar speed and developer experience improvements It offers: - ⚑️ **Built for speed** with Rust - significantly faster than alternatives -- πŸ” **82 lint rules** covering common Markdown issues +- πŸ” **83 lint rules** covering common Markdown issues - πŸ› οΈ **Automatic formatting** with `--fix` for files and stdin/stdout - πŸ“¦ **Zero dependencies** - single binary with no runtime requirements - πŸ”§ **Highly configurable** with TOML-based config files @@ -526,7 +526,7 @@ The action ref (`rvben/rumdl@v0`) selects the GitHub Action version, while the o ## Rules -rumdl implements 82 lint rules for Markdown files. Here are some key rule categories: +rumdl implements 83 lint rules for Markdown files. Here are some key rule categories: | Category | Description | Example Rules | | -------------- | ---------------------------------------- | ------------------- | diff --git a/docs/comparison.md b/docs/comparison.md index 745db640..ed4da76b 100644 --- a/docs/comparison.md +++ b/docs/comparison.md @@ -13,7 +13,7 @@ For detailed comparisons with specific tools, see: | Tool | Type | Language | Rules | Auto-fix | Flavors | Config format | Plugins | LSP | | --------------------- | ------------- | -------- | ----------------------------------------- | -------- | ------- | ----------------------- | ------------ | --- | -| **rumdl** | Lint + Format | Rust | 82 | Yes | 9 | TOML, JSON, YAML | No | Yes | +| **rumdl** | Lint + Format | Rust | 83 | Yes | 9 | TOML, JSON, YAML | No | Yes | | **markdownlint-cli** | Lint | Node.js | 53 | Yes | No | JSON, JSONC, YAML, TOML | Yes (JS) | No | | **markdownlint-cli2** | Lint | Node.js | 53 | Yes | No | JSONC, YAML, JS | Yes (JS) | No | | **remark-lint** | Lint | Node.js | ~80 (via presets) | No | No | JS, JSON, YAML | Yes (JS) | No | @@ -33,7 +33,7 @@ cover common cases. No built-in auto-fix for lint violations (remark itself can **mado** is a Rust-based linter with 38 rules (33 stable, 5 unstable). It has no auto-fix and no plugin system. -**rumdl** implements all 53 markdownlint rules plus 29 additional rules (82 total). It supports +**rumdl** implements all 53 markdownlint rules plus 30 additional rules (83 total). It supports auto-fix for most rules and includes rules not found in other tools, such as relative link validation (MD057), footnote checks (MD066-MD068), nested code fence detection (MD070), and TOC validation (MD073). @@ -136,7 +136,7 @@ Cold start benchmark on the Rust Book (478 Markdown files), measured with [hyper **Notes:** -- **mado** is faster in cold-start benchmarks because it does less work per file: fewer rules (38 vs 82), no fix generation, and no flavor detection. +- **mado** is faster in cold-start benchmarks because it does less work per file: fewer rules (38 vs 83), no fix generation, and no flavor detection. The gap reflects feature surface area, not implementation quality. - **rumdl** supports result caching (`rumdl check` without `--no-cache`), which skips unchanged files on subsequent runs β€” typically under 50 ms, faster than mado's cold start. - **pymarkdown** performs well for a Python tool due to its efficient scanner architecture. @@ -147,5 +147,5 @@ Cold start benchmark on the Rust Book (478 Markdown files), measured with [hyper - [Comparison with markdownlint](markdownlint-comparison.md) β€” detailed rule-by-rule comparison and migration guide - [Comparison with mdformat](mdformat-comparison.md) β€” formatting feature comparison and migration guide -- [Rules Reference](rules.md) β€” complete list of rumdl's 82 rules +- [Rules Reference](rules.md) β€” complete list of rumdl's 83 rules - [Markdown Flavors](flavors.md) β€” flavor configuration and per-rule adjustments diff --git a/docs/configuration/index.md b/docs/configuration/index.md index 36ad9801..65626416 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -10,6 +10,8 @@ Looking for something more specific? [Global Settings](../global-settings.md). - For per-document overrides via HTML comments, see [Inline Configuration](../inline-configuration.md). +- For formatting HTML and scripts embedded in Markdown, see + [Embedded HTML Formatting](../embedded-html-formatting.md). ## Supported config files diff --git a/docs/embedded-html-formatting.md b/docs/embedded-html-formatting.md new file mode 100644 index 00000000..bc6a5ddc --- /dev/null +++ b/docs/embedded-html-formatting.md @@ -0,0 +1,85 @@ +# Embedded HTML Formatting + +rumdl includes built-in support for checking and formatting HTML blocks embedded inside your Markdown files. It can also format JavaScript and TypeScript code blocks inside ` + +``` + +Running `rumdl check` will emit warnings pointing to the HTML block starting at `
` and the script block inside it. + +Running `rumdl check --fix` will format the block in-place: + +```markdown +Some text. + +
+

Hello World

+ +
+``` diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index e5698850..af328384 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -145,5 +145,5 @@ Each line shows: ## Next Steps - [CLI Commands](../usage/cli.md) - Full command reference -- [Rules Reference](../rules.md) - Explore all 82 rules +- [Rules Reference](../rules.md) - Explore all 83 rules - [Configuration](../global-settings.md) - Advanced configuration options diff --git a/docs/global-settings.md b/docs/global-settings.md index 7f492312..e348bfe8 100644 --- a/docs/global-settings.md +++ b/docs/global-settings.md @@ -1557,3 +1557,4 @@ find . -name "*.md" -o -name "*.markdown" | head -10 - [CLI Reference](../README.md#command-line-interface) - Command-line options - [Rule-specific Configuration](../README.md#configuration-file-example) - Configuring individual rules - [Code Block Tools](code-block-tools.md) - External linters/formatters for code blocks [preview] +- [Embedded HTML Formatting](embedded-html-formatting.md) - Built-in checking and formatting of HTML and script blocks diff --git a/docs/index.md b/docs/index.md index a96f1d62..c538fdf5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,7 +16,7 @@ icon: lucide/file-check [:octicons-arrow-right-24: Benchmarks](#performance) -- :mag:{ .lg .middle } **82 lint rules** +- :mag:{ .lg .middle } **83 lint rules** --- @@ -76,7 +76,7 @@ With intelligent caching, subsequent runs are even faster - rumdl only re-lints ## Features - :zap: **Built for speed** with Rust - significantly faster than alternatives -- :mag: **82 lint rules** covering common Markdown issues +- :mag: **83 lint rules** covering common Markdown issues - :wrench: **Automatic formatting** with `--fix` for files and stdin/stdout - :package: **Zero dependencies** - single binary with no runtime requirements - :gear: **Highly configurable** with TOML-based config files @@ -100,7 +100,7 @@ With intelligent caching, subsequent runs are even faster - rumdl only re-lints - [:octicons-book-24: **Rules Reference**](rules.md) - Explore all 82 linting rules with examples. + Explore all 83 linting rules with examples. - [:octicons-gear-24: **Configuration**](global-settings.md) diff --git a/docs/markdownlint-comparison.md b/docs/markdownlint-comparison.md index 7cbc3e99..bb563349 100644 --- a/docs/markdownlint-comparison.md +++ b/docs/markdownlint-comparison.md @@ -10,14 +10,14 @@ rumdl offers **high markdownlint compatibility with intentional differences** wh - **Performance**: rumdl is significantly faster (30-100x in many cases) thanks to Rust and intelligent caching - **Rule Coverage**: All 53 markdownlint rules are implemented, with a small number of intentional behavioral differences documented below -- **Unique Features**: 29 additional rules (MD057, MD061-MD088), built-in LSP server, VS Code extension, 6 Markdown flavors +- **Unique Features**: 30 additional rules (MD057, MD061-MD089), built-in LSP server, VS Code extension, 6 Markdown flavors - **Configuration**: Automatic markdownlint config discovery and conversion ## Rule Coverage ### Implemented Rules -rumdl implements **82 rules total**: all 53 markdownlint rules plus 29 unique rules. +rumdl implements **83 rules total**: all 53 markdownlint rules plus 30 unique rules. **Markdownlint-compatible rules (53):** All markdownlint rules are implemented with full compatibility. See the [Rules Reference](rules.md) for the complete list. @@ -25,7 +25,7 @@ rumdl implements **82 rules total**: all ### Rules Unique to rumdl -rumdl implements 29 additional rules not found in markdownlint: +rumdl implements 30 additional rules not found in markdownlint: | Rule | Name | Description | | ------ | ------------------------------ | ---------------------------------------------------------- | diff --git a/docs/md089.md b/docs/md089.md new file mode 100644 index 00000000..357c93c7 --- /dev/null +++ b/docs/md089.md @@ -0,0 +1,52 @@ +# MD089 - Embedded HTML blocks should be formatted + +Aliases: `embedded-html-fmt` + +This rule is enabled by default. + +## What this rule does + +Formats HTML blocks using `markup_fmt`. It can also format JavaScript/TypeScript inside ` +"#; + let mut config = make_test_config(true, true); + config.html.script.semi_colons = "always".to_string(); + config.html.script.quote_style = "single".to_string(); + + let rule = MD089EmbeddedHtmlFmt::from_config(&config); + let ctx = LintContext::new(content, MarkdownFlavor::Standard, None); + let warnings = rule.check(&ctx).unwrap(); + assert_eq!(warnings.len(), 1); + + let formatted = rule.fix(&ctx).unwrap(); + assert!(formatted.contains("const a = 1;")); + assert!(formatted.contains("const b = 'double';")); + } + + #[test] + fn test_html_comment_nested_formatting() { + let content = r#" + +A snippet of C++ code. Follow the link to read it. + +"#; + let config = make_test_config(true, false); + let rule = MD089EmbeddedHtmlFmt::from_config(&config); + let ctx = LintContext::new(content, MarkdownFlavor::Standard, None); + let formatted = rule.fix(&ctx).unwrap(); + let lines: Vec<&str> = formatted.lines().collect(); + assert_eq!(lines[0], r#""#); + assert_eq!(lines[1], " "); + } + + #[test] + fn test_html_comment_format_as_markdown() { + let content = r#"
+ +
+"#; + let mut config = make_test_config(true, false); + config.html.format_comments_as_markdown = true; + + let rule = MD089EmbeddedHtmlFmt::from_config(&config); + let ctx = LintContext::new(content, MarkdownFlavor::Standard, None); + let formatted = rule.fix(&ctx).unwrap(); + + assert!(formatted.contains("# Heading with spaces")); + assert!(formatted.contains(" # Heading with spaces")); + } + + #[test] + fn test_html_comment_already_indented_no_change() { + let content = r#"
+ +
+"#; + let config = make_test_config(true, false); + let rule = MD089EmbeddedHtmlFmt::from_config(&config); + let ctx = LintContext::new(content, MarkdownFlavor::Standard, None); + let formatted = rule.fix(&ctx).unwrap(); + assert_eq!(content, formatted); + } + + #[test] + fn test_invalid_script_formatting_is_skipped() { + let content = r#" +"#; + let config = make_test_config(true, true); + let rule = MD089EmbeddedHtmlFmt::from_config(&config); + let ctx = LintContext::new(content, MarkdownFlavor::Standard, None); + let formatted = rule.fix(&ctx).unwrap(); + assert_eq!( + formatted, content, + "Expected invalid script to skip formatting (return original content)" + ); + } + + #[test] + fn test_malformed_html_is_skipped() { + let content = r#"
Hello

"#; + let config = make_test_config(true, false); + let rule = MD089EmbeddedHtmlFmt::from_config(&config); + let ctx = LintContext::new(content, MarkdownFlavor::Standard, None); + let formatted = rule.fix(&ctx).unwrap(); + assert_eq!( + formatted, content, + "Expected malformed HTML to skip formatting (return original content)" + ); + } + + #[test] + fn test_format_embedded_html_blocks_print_width() { + let mut config = make_test_config(true, false); + config.html.print_width = 40; // small print width to trigger wrapping + + let content = r#"# Document + +
+

This is a very long line of text inside an HTML block that should be wrapped to 40 columns by the HTML formatter because we configured it to do so.

+
+"#; + let rule = MD089EmbeddedHtmlFmt::from_config(&config); + let ctx = LintContext::new(content, MarkdownFlavor::Standard, None); + let formatted = rule.fix(&ctx).unwrap(); + + assert!( + formatted.contains("

\n This is a very long line"), + "HTML paragraph content should be wrapped, got:\n{formatted}" + ); + } +} diff --git a/src/rules/mod.rs b/src/rules/mod.rs index 00b5003e..daa093a5 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -78,6 +78,8 @@ mod md085_paragraph_continuation_indent; mod md086_no_unclosed_comments; mod md087_unused_disable_comment; mod md088_quotes_dashes; +#[cfg(feature = "html-fmt")] +mod md089_embedded_html_fmt; pub use code_fence_utils::CodeFenceStyle; pub use md001_heading_increment::MD001HeadingIncrement; @@ -158,6 +160,8 @@ pub use md085_paragraph_continuation_indent::MD085ParagraphContinuationIndent; pub use md086_no_unclosed_comments::MD086NoUnclosedComments; pub use md087_unused_disable_comment::MD087UnusedDisableComment; pub use md088_quotes_dashes::MD088QuotesDashes; +#[cfg(feature = "html-fmt")] +pub use md089_embedded_html_fmt::MD089EmbeddedHtmlFmt; mod md012_no_multiple_blanks; pub use md012_no_multiple_blanks::MD012NoMultipleBlanks; @@ -613,6 +617,12 @@ const RULES: &[RuleEntry] = &[ ctor: crate::rules::md088_quotes_dashes::MD088QuotesDashes::from_config, opt_in: true, }, + #[cfg(feature = "html-fmt")] + RuleEntry { + name: "MD089", + ctor: MD089EmbeddedHtmlFmt::from_config, + opt_in: false, + }, ]; /// Returns all rule instances (including opt-in) for config validation and CLI diff --git a/src/utils/code_block_utils.rs b/src/utils/code_block_utils.rs index e0515221..4355c2d2 100644 --- a/src/utils/code_block_utils.rs +++ b/src/utils/code_block_utils.rs @@ -213,6 +213,20 @@ impl CodeBlockUtils { Self::is_in_code_block(blocks, pos) } + /// Check if an HTML block is a JSX block (starts with uppercase tag or fragment). + pub fn is_jsx_block(html: &str) -> bool { + let trimmed = html.trim_start(); + if trimmed.starts_with("<>") { + return true; + } + if let Some(stripped) = trimmed.strip_prefix('<') + && let Some(first_char) = stripped.chars().next() + { + return first_char.is_ascii_uppercase(); + } + false + } + /// Check if a byte position falls within any of the given sorted, non-overlapping ranges. /// /// Uses binary search on the sorted block ranges for O(log n) lookup. diff --git a/src/utils/range_utils.rs b/src/utils/range_utils.rs index 8338fbea..2e60427c 100644 --- a/src/utils/range_utils.rs +++ b/src/utils/range_utils.rs @@ -88,6 +88,24 @@ impl<'a> LineIndex<'a> { index } + /// Convert a global byte offset into 1-indexed (line, column). + /// Column is character-based, not byte-based. + pub fn byte_to_line_col(&self, byte_offset: usize) -> (usize, usize) { + let snapped_offset = find_char_boundary(self.content, byte_offset); + let line_idx = match self.line_starts.binary_search(&snapped_offset) { + Ok(idx) => idx, + Err(idx) => idx.saturating_sub(1), + }; + let line_start = self.line_starts[line_idx]; + let line_content = &self.content[line_start..snapped_offset]; + // Strip trailing newlines just in case + let line_content = line_content.strip_suffix('\n').unwrap_or(line_content); + let line_content = line_content.strip_suffix('\r').unwrap_or(line_content); + + let col = line_content.chars().count() + 1; + (line_idx + 1, col) + } + /// Create a `LineIndex` from pre-computed line starts and code block byte ranges. /// /// Instead of re-scanning content to find code blocks, this converts @@ -933,4 +951,38 @@ mod tests { assert_eq!(end_line, 1); assert!(end_col > start_col); } + + #[test] + fn test_byte_to_line_col() { + let content = "Hello\nWorld\nδ½ ε₯½\n"; + let line_index = LineIndex::new(content); + + // "Hello\n" is 6 bytes. + // "World\n" is 6 bytes. + // "δ½ ε₯½\n" is 7 bytes (each Chinese char is 3 bytes, plus \n). + + // Offset 0 -> Line 1, Col 1 (H) + assert_eq!(line_index.byte_to_line_col(0), (1, 1)); + // Offset 4 -> Line 1, Col 5 (o) + assert_eq!(line_index.byte_to_line_col(4), (1, 5)); + // Offset 5 -> Line 1, Col 6 (\n - stripped, so it behaves as Col 6) + assert_eq!(line_index.byte_to_line_col(5), (1, 6)); + + // Offset 6 -> Line 2, Col 1 (W) + assert_eq!(line_index.byte_to_line_col(6), (2, 1)); + + // Offset 12 -> Line 3, Col 1 (δ½ ) + assert_eq!(line_index.byte_to_line_col(12), (3, 1)); + // Offset 15 -> Line 3, Col 2 (ε₯½ - 3 bytes after start of line 3) + assert_eq!(line_index.byte_to_line_col(15), (3, 2)); + } + + #[test] + fn test_byte_to_line_col_mid_char() { + let content = "δ½ ε₯½\n"; + let line_index = LineIndex::new(content); + // This offset is in the middle of 'δ½ ' (bytes 0..3) + // It shouldn't panic, it should snap to nearest boundary or handle it. + let _ = line_index.byte_to_line_col(1); + } } diff --git a/tests/cli/cli_lsp_fix_consistency.rs b/tests/cli/cli_lsp_fix_consistency.rs index 87e60765..e65cc7c0 100644 --- a/tests/cli/cli_lsp_fix_consistency.rs +++ b/tests/cli/cli_lsp_fix_consistency.rs @@ -355,6 +355,8 @@ fn get_test_content_for_rule(rule_name: &str) -> Option<&'static str> { "MD086" => Some(""), "MD088" => Some("Text with \u{201C}quotes\u{201D} and dashes\u{2010}"), + #[cfg(feature = "html-fmt")] + "MD089" => Some("

\n

Unformatted HTML

\n
"), _ => None, } } diff --git a/tests/integration/rules_mod_test.rs b/tests/integration/rules_mod_test.rs index 3ee60a6d..e99c6ee9 100644 --- a/tests/integration/rules_mod_test.rs +++ b/tests/integration/rules_mod_test.rs @@ -7,7 +7,10 @@ fn test_all_rules_returns_all_rules() { let config = Config::default(); let rules = all_rules(&config); - // Should return all 82 rules as defined in the RULES array (MD001-MD088) + // Should return all rules as defined in the RULES array + #[cfg(feature = "html-fmt")] + assert_eq!(rules.len(), 83); + #[cfg(not(feature = "html-fmt"))] assert_eq!(rules.len(), 82); // Verify some specific rules are present diff --git a/tests/rules/md027_test.rs b/tests/rules/md027_test.rs index e17585e8..ac0ef91c 100644 --- a/tests/rules/md027_test.rs +++ b/tests/rules/md027_test.rs @@ -158,12 +158,11 @@ fn test_md027_html5_media_elements() { #[test] fn test_md027_self_closing_tags() { let rule = MD027MultipleSpacesBlockquote::default(); - // Self-closing tags like
don't create block context - // So > after them should flag - let content = "
\n> After self-closing\n"; + // Self-closing tags like
don't create block context across blank lines + let content = "
\n\n> After self-closing\n"; let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None); let result = rule.check(&ctx).unwrap(); - // Self-closing tag doesn't create block context, so this should flag + // Self-closing tag doesn't create block context across blank lines, so this should flag assert_eq!(result.len(), 1, "Content after self-closing tag should flag"); } @@ -174,9 +173,8 @@ fn test_md027_style_tag_allows_blanks() { let content = "\n> After style\n"; let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None); let result = rule.check(&ctx).unwrap(); - // Style tag context should continue through blank lines - // And content after (no blank) should not flag - assert!(result.is_empty(), "Content after style tag should not flag"); + // Content after is outside the style block, so the blockquote with multiple spaces is flagged + assert_eq!(result.len(), 1, "Content after style tag should flag"); } // ============================================================================= @@ -264,6 +262,6 @@ fn test_md027_script_tag_allows_blanks() { let content = "\n> After script\n"; let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None); let result = rule.check(&ctx).unwrap(); - // Script tag context should continue through blank lines - assert!(result.is_empty(), "Content after script tag should not flag"); + // Content after is outside the script block, so the blockquote with multiple spaces is flagged + assert_eq!(result.len(), 1, "Content after script tag should flag"); } diff --git a/tests/rules/md034_comprehensive_test.rs b/tests/rules/md034_comprehensive_test.rs index 13a463ea..55754ad3 100644 --- a/tests/rules/md034_comprehensive_test.rs +++ b/tests/rules/md034_comprehensive_test.rs @@ -328,7 +328,7 @@ fn test_urls_in_multiline_html_comments() { fn test_url_after_html_comment_is_flagged() { let rule = MD034NoBareUrls; - let content = " https://example.com"; + let content = "\nhttps://example.com"; let ctx = LintContext::new(content, MarkdownFlavor::Standard, None); let result = rule.check(&ctx).unwrap(); assert_eq!(result.len(), 1, "URL after HTML comment should be flagged");