From bfc3eca302077246dad6f558b607628d0b58a7b2 Mon Sep 17 00:00:00 2001 From: Mohammed Alzakariya Date: Tue, 19 Aug 2025 15:30:51 +0300 Subject: [PATCH 1/3] Use github-slugger in place of slug As explained in #370, if there are periods used in the heading and it is referenced, then obsidian-export generates invalid links. Added gfm-heading-ids-compliance test to reproduce the issue in #370. Cargo.lock is also added to .gitignore as it is autogenerated. Closes #370. --- .gitignore | 3 + Cargo.lock | 102 +++--------------- Cargo.toml | 2 +- src/lib.rs | 4 +- tests/export_test.rs | 23 ++++ .../gfm-heading-ids-compliance/Note.md | 13 +++ .../input/gfm-heading-ids-compliance/Note.md | 10 ++ 7 files changed, 65 insertions(+), 92 deletions(-) create mode 100644 tests/testdata/expected/gfm-heading-ids-compliance/Note.md create mode 100644 tests/testdata/input/gfm-heading-ids-compliance/Note.md diff --git a/.gitignore b/.gitignore index 5c8a7a53..f2c9c832 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ # Tarpaulin (https://github.com/xd009642/tarpaulin) HTML coverage report tarpaulin-report.html + +# This is autogenerated and does not need to be tracked by version control +Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index ce1b50c3..6b4d6d23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,12 +27,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bumpalo" -version = "3.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" - [[package]] name = "cfg-if" version = "1.0.1" @@ -64,12 +58,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "deunicode" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" - [[package]] name = "diff" version = "0.1.13" @@ -190,6 +178,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "github-slugger" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721820f4eab1c427d482e144b63288754d9872fb7f0d72d73ab101008ef43147" +dependencies = [ + "once_cell", + "regex", +] + [[package]] name = "glob" version = "0.3.3" @@ -320,6 +318,7 @@ version = "25.3.0" dependencies = [ "eyre", "filetime", + "github-slugger", "gumdrop", "ignore", "pathdiff", @@ -331,7 +330,6 @@ dependencies = [ "regex", "rstest", "serde_yaml", - "slug", "snafu", "tempfile", "unicode-normalization", @@ -554,12 +552,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - [[package]] name = "ryu" version = "1.0.20" @@ -620,16 +612,6 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" -[[package]] -name = "slug" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" -dependencies = [ - "deunicode", - "wasm-bindgen", -] - [[package]] name = "snafu" version = "0.8.6" @@ -688,9 +670,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -770,64 +752,6 @@ dependencies = [ "wit-bindgen-rt", ] -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - [[package]] name = "winapi-util" version = "0.1.9" diff --git a/Cargo.toml b/Cargo.toml index c87ead56..bad64393 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ pulldown-cmark-to-cmark = "21.0.0" rayon = "1.10.0" regex = "1.10.5" serde_yaml = "0.9.34" -slug = "0.1.5" +github-slugger = "0.1.0" snafu = "0.8.3" unicode-normalization = "0.1.23" filetime = "0.2.23" diff --git a/src/lib.rs b/src/lib.rs index 39697a45..5aeccdd8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,13 +17,13 @@ pub use context::Context; use filetime::set_file_mtime; use frontmatter::{frontmatter_from_str, frontmatter_to_str}; pub use frontmatter::{Frontmatter, FrontmatterStrategy}; +use github_slugger::slug; use pathdiff::diff_paths; use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; use pulldown_cmark::{CodeBlockKind, CowStr, Event, HeadingLevel, Options, Parser, Tag, TagEnd}; use pulldown_cmark_to_cmark::cmark_with_options; use rayon::prelude::*; use references::{ObsidianNoteReference, RefParser, RefParserState, RefType}; -use slug::slugify; use snafu::{ResultExt, Snafu}; use unicode_normalization::UnicodeNormalization; pub use walker::{vault_contents, WalkOptions}; @@ -780,7 +780,7 @@ impl<'a> Exporter<'a> { if let Some(section) = reference.section { link.push('#'); - link.push_str(&slugify(section)); + link.push_str(&slug(section)); } let link_tag = Tag::Link { diff --git a/tests/export_test.rs b/tests/export_test.rs index 8b81721a..d8603b84 100644 --- a/tests/export_test.rs +++ b/tests/export_test.rs @@ -459,3 +459,26 @@ fn test_same_filename_different_directories() { let actual = read_to_string(tmp_dir.path().join(PathBuf::from("Note.md"))).unwrap(); assert_eq!(expected, actual); } + +#[test] +fn test_github_flavored_markdown_heading_id_compliance() { + let tmp_dir = TempDir::new().expect("failed to make tempdir"); + + Exporter::new( + PathBuf::from("tests/testdata/input/gfm-heading-ids-compliance"), + tmp_dir.path().to_path_buf(), + ) + .run() + .unwrap(); + + let expected = if cfg!(windows) { + read_to_string("tests/testdata/expected/gfm-heading-ids-compliance/Note.md") + .unwrap() + .replace('/', "\\") + } else { + read_to_string("tests/testdata/expected/gfm-heading-ids-compliance/Note.md").unwrap() + }; + + let actual = read_to_string(tmp_dir.path().join(PathBuf::from("Note.md"))).unwrap(); + assert_eq!(expected, actual); +} diff --git a/tests/testdata/expected/gfm-heading-ids-compliance/Note.md b/tests/testdata/expected/gfm-heading-ids-compliance/Note.md new file mode 100644 index 00000000..aa08f320 --- /dev/null +++ b/tests/testdata/expected/gfm-heading-ids-compliance/Note.md @@ -0,0 +1,13 @@ +[A.B.C.D](Note.md#abcd) +[with a name](Note.md#abcd) +[1.2.3.4](Note.md#1234) +[this-or-that](Note.md#this-or-that) +[this--or-that](Note.md#this--or-that) + +# A.B.C.D + +# 1.2.3.4 + +# this-or-that + +# this--or-that diff --git a/tests/testdata/input/gfm-heading-ids-compliance/Note.md b/tests/testdata/input/gfm-heading-ids-compliance/Note.md new file mode 100644 index 00000000..8958bee2 --- /dev/null +++ b/tests/testdata/input/gfm-heading-ids-compliance/Note.md @@ -0,0 +1,10 @@ +[[#A.B.C.D]] +[[#A.B.C.D|with a name]] +[[#1.2.3.4]] +[[#this-or-that]] +[[#this--or-that]] + +# A.B.C.D +# 1.2.3.4 +# this-or-that +# this--or-that From 9841ecff2283585518b0374e24a67217af778959 Mon Sep 17 00:00:00 2001 From: Mohammed Alzakariya Date: Tue, 19 Aug 2025 16:19:50 +0300 Subject: [PATCH 2/3] Update CONTRIBUTING.md with uv requirement When adding a new changelog fragment, uv [1] is required to be installed, but this is not mentioned in the document. [1]: https://github.com/astral-sh/uv --- CONTRIBUTING.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55ccb326..b00e7113 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -79,4 +79,9 @@ If you don't feel comfortable writing user documentation, I will be happy to gui ## Release notes [Towncrier](https://towncrier.readthedocs.io/en/stable/index.html) is used to generate release notes. -If you add a changelog fragment to the `changelog.d` directory with `just add-changelog` (requires [just](https://github.com/casey/just#installation)) it will automatically be picked up when a new release is made. + +If you add a changelog fragment to the `changelog.d` directory with `just add-changelog`, it will automatically be picked up when a new release is made. + +This requires +- [just](https://github.com/casey/just#installation) +- [uv](https://github.com/astral-sh/uv) From 27b7a54eb2b60e0f087c0e80a28272ce6b37c484 Mon Sep 17 00:00:00 2001 From: Mohammed Alzakariya Date: Tue, 19 Aug 2025 16:22:29 +0300 Subject: [PATCH 3/3] Add changelog fragment for github header link fix --- changelog.d/370.fix.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog.d/370.fix.md diff --git a/changelog.d/370.fix.md b/changelog.d/370.fix.md new file mode 100644 index 00000000..f558db74 --- /dev/null +++ b/changelog.d/370.fix.md @@ -0,0 +1,3 @@ +Fixed header id links being non-compliant with github and gitlab markdown viewers. + +This allows links to heading anchors to follow under github and gitlab.