diff --git a/CHANGELOG.md b/CHANGELOG.md index 7afe14b..dcf0f42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,38 @@ This file summarizes notable repository changes in a compact, release-oriented f ### Unreleased Git Log +Used git range: `v1.0.1..HEAD` + +```txt +``` + +## v1.0.1 Changes (2026-05-30) + +### v1.0.1 Overview + +* Generated ToC links now reuse explicit managed anchor IDs when `anchor=true`: + * `numbering=true` still affects ToC labels and heading display text + * link targets stay tied to the generated unnumbered `` values + * duplicate headings keep matching ToC and anchor suffixes such as `summary` and `summary-1` +* VS Code Marketplace publishing is more robust: + * VSIX publishing now retries transient `vsce publish` failures + * already-published package responses are treated as non-fatal after the VSIX assets have been validated +* Extension development dependency lockfile updates: + * `qs` was updated from `6.15.1` to `6.15.2` + * `tmp` was updated from `0.2.5` to `0.2.7` + +### v1.0.1 Git Log + Used git range: `v1.0.0..HEAD` ```txt +* 838007c Issue #109 fixed +* 7a37122 Merge pull request #108 from rokath/dependabot/npm_and_yarn/extension/tmp-0.2.7 +* afcaafb Merge pull request #107 from rokath/dependabot/npm_and_yarn/extension/qs-6.15.2 +* 57f2cc9 build(deps-dev): bump tmp from 0.2.5 to 0.2.7 in /extension +* c4aae24 build(deps-dev): bump qs from 6.15.1 to 6.15.2 in /extension +* e173126 Merge pull request #106 from rokath/dev +* b9c11fa retry logic added for poblishing ``` ## v1.0.0 Changes (2026-05-20) diff --git a/extension/package-lock.json b/extension/package-lock.json index 97f552d..37fc6d9 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "mdtoc-vscode", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mdtoc-vscode", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "devDependencies": { "@types/node": "^22.15.17", diff --git a/extension/package.json b/extension/package.json index 04b6987..34f4384 100644 --- a/extension/package.json +++ b/extension/package.json @@ -2,7 +2,7 @@ "name": "mdtoc", "displayName": "mdtoc - Markdown ToC with section numbering and stable links", "description": "Generate, strip and configure a Markdown table of contents. Toggle ToC, numbering and links; choose heading levels, bullet style and slug method.", - "version": "1.0.0", + "version": "1.0.1", "publisher": "rokath", "license": "MIT", "icon": "mdtoc_icon_128.png", diff --git a/internal/mdtoc/process.go b/internal/mdtoc/process.go index eb7c878..26a6397 100644 --- a/internal/mdtoc/process.go +++ b/internal/mdtoc/process.go @@ -226,6 +226,13 @@ func assignDerivedArtifacts(headings []Heading, cfg Config) { h.ManagedNumber = "" } + // Explicit managed anchors own the document target identity. Reusing + // anchorID keeps ToC hrefs tied to the exact ID rendered into headings + // instead of independently deriving a renderer-style slug. + if cfg.Anchor { + h.ManagedTOCTarget = anchorID + continue + } h.ManagedTOCTarget = tocSlugger.Next(tocSlugSource(*h, cfg)) } } @@ -243,9 +250,6 @@ func anchorSlugSource(h Heading, cfg Config) string { } func tocSlugSource(h Heading, cfg Config) string { - if cfg.Anchor { - return anchorSlugSource(h, cfg) - } source := h.TitleText switch cfg.Slug { case SlugCrossnote: diff --git a/internal/mdtoc/process_test.go b/internal/mdtoc/process_test.go index 6dcd14c..28f102d 100644 --- a/internal/mdtoc/process_test.go +++ b/internal/mdtoc/process_test.go @@ -304,6 +304,143 @@ func TestGenerateLinkFalseRendersPlainTOC(t *testing.T) { } } +// TestGenerateTOCTargetsFollowAnchorOwnershipMatrix verifies the issue #109 +// invariant across the persisted config flags that decide whether mdtoc owns +// heading IDs or delegates them to the selected renderer slug profile. +func TestGenerateTOCTargetsFollowAnchorOwnershipMatrix(t *testing.T) { + input := "# Title\n\n## Foo Bar\n## Foo Bar\n" + slugCases := []struct { + slug SlugMode + anchorTargets [2]string + plainTargets [2]string + numberedTargets [2]string + }{ + { + slug: SlugGitHub, + anchorTargets: [2]string{"foo--bar", "foo--bar-1"}, + plainTargets: [2]string{"foo--bar", "foo--bar-1"}, + numberedTargets: [2]string{"1-foo--bar", "2-foo--bar"}, + }, + { + slug: SlugGitLab, + anchorTargets: [2]string{"foo-bar", "foo-bar-1"}, + plainTargets: [2]string{"foo-bar", "foo-bar-1"}, + numberedTargets: [2]string{"1-foo-bar", "2-foo-bar"}, + }, + { + slug: SlugCrossnote, + anchorTargets: [2]string{"foo--bar", "foo--bar-1"}, + plainTargets: [2]string{"foo--bar", "foo--bar-1"}, + numberedTargets: [2]string{"1-foo--bar", "2-foo--bar"}, + }, + } + + for _, sc := range slugCases { + for _, numbering := range []bool{false, true} { + for _, anchor := range []bool{false, true} { + for _, link := range []bool{false, true} { + for _, toc := range []bool{false, true} { + name := fmt.Sprintf("slug=%s/numbering=%t/anchor=%t/link=%t/toc=%t", sc.slug, numbering, anchor, link, toc) + t.Run(name, func(t *testing.T) { + got, _, err := Generate(input, Options{ + Numbering: numbering, + MinLevel: 2, + MaxLevel: 2, + Slug: sc.slug, + Anchor: anchor, + AnchorSet: true, + Link: link, + LinkSet: true, + TOC: toc, + Bullets: BulletStar, + }) + if err != nil { + t.Fatalf("Generate error: %v", err) + } + + firstLabel := "Foo Bar" + secondLabel := "Foo Bar" + if numbering { + firstLabel = "1. " + firstLabel + secondLabel = "2. " + secondLabel + } + + firstHeading := "## " + secondHeading := "## " + if numbering { + firstHeading += "1. " + secondHeading += "2. " + } + if anchor { + firstHeading += fmt.Sprintf(``, sc.anchorTargets[0]) + secondHeading += fmt.Sprintf(``, sc.anchorTargets[1]) + } + firstHeading += "Foo Bar" + secondHeading += "Foo Bar" + + for _, want := range []string{firstHeading, secondHeading} { + if !strings.Contains(got, want) { + t.Fatalf("generated output missing heading %q:\n%s", want, got) + } + } + if !anchor && strings.Contains(got, "