From 787748f274d2def8fa4a1e08795a398c5a771f4f Mon Sep 17 00:00:00 2001 From: tgreyuk Date: Sat, 9 Apr 2022 21:55:26 +0100 Subject: [PATCH] fix: Handle non-unique anchor ids (#303) --- packages/typedoc-plugin-markdown/package.json | 2 +- packages/typedoc-plugin-markdown/src/theme.ts | 23 +++++++++++++++---- .../__snapshots__/declarations.spec.ts.snap | 20 ++++++++-------- .../test/specs/__snapshots__/toc.spec.ts.snap | 16 ++++++++----- .../test/specs/toc.spec.ts | 11 ++++----- .../test/stubs/src/index.ts | 1 + .../test/stubs/src/toc.ts | 9 ++++++++ 7 files changed, 55 insertions(+), 27 deletions(-) create mode 100644 packages/typedoc-plugin-markdown/test/stubs/src/toc.ts diff --git a/packages/typedoc-plugin-markdown/package.json b/packages/typedoc-plugin-markdown/package.json index d2814bc40..6c024b17e 100644 --- a/packages/typedoc-plugin-markdown/package.json +++ b/packages/typedoc-plugin-markdown/package.json @@ -9,7 +9,7 @@ "scripts": { "lint": "eslint ./src --ext .ts", "prepublishOnly": "yarn run lint && yarn run build && yarn run test", - "build": "rm -rf dist && tsc && copyfiles --up 1 ./src/**/*.hbs ./dist/", + "build": "rimraf dist && tsc && copyfiles --up 1 ./src/**/*.hbs ./dist/", "pretest": "yarn run demo:md && markdownlint ./demo/md", "test": "jest --colors", "build-and-test": "yarn run build && yarn run test", diff --git a/packages/typedoc-plugin-markdown/src/theme.ts b/packages/typedoc-plugin-markdown/src/theme.ts index 9a92f81cb..2aaa93d3e 100644 --- a/packages/typedoc-plugin-markdown/src/theme.ts +++ b/packages/typedoc-plugin-markdown/src/theme.ts @@ -12,9 +12,7 @@ import { UrlMapping, } from 'typedoc'; import { getKindPlural } from './groups'; - import { NavigationItem } from './navigation-item'; - import { indexTemplate, reflectionMemberTemplate, @@ -44,6 +42,7 @@ export class MarkdownTheme extends Theme { project?: ProjectReflection; reflection?: DeclarationReflection; location!: string; + anchorMap: Record = {}; static URL_PREFIX = /^(http|ftp)s?:\/\//; @@ -127,6 +126,7 @@ export class MarkdownTheme extends Theme { reflection.url = url; reflection.hasOwnDocument = true; } + for (const child of reflection.children || []) { if (mapping.isLeaf) { this.applyAnchorUrl(child, reflection); @@ -160,9 +160,24 @@ export class MarkdownTheme extends Theme { } applyAnchorUrl(reflection: Reflection, container: Reflection) { - if (!reflection.url || !MarkdownTheme.URL_PREFIX.test(reflection.url)) { + if ( + container.url && + (!reflection.url || !MarkdownTheme.URL_PREFIX.test(reflection.url)) + ) { const reflectionId = reflection.name.toLowerCase(); - const anchor = this.toAnchorRef(reflectionId); + + this.anchorMap[container.url] + ? this.anchorMap[container.url].push(reflectionId) + : (this.anchorMap[container.url] = [reflectionId]); + + const count = this.anchorMap[container.url].filter( + (id) => id === reflectionId, + )?.length; + + const anchor = this.toAnchorRef( + reflectionId + (count > 1 ? '-' + (count - 1).toString() : ''), + ); + reflection.url = container.url + '#' + anchor; reflection.anchor = anchor; reflection.hasOwnDocument = false; diff --git a/packages/typedoc-plugin-markdown/test/specs/__snapshots__/declarations.spec.ts.snap b/packages/typedoc-plugin-markdown/test/specs/__snapshots__/declarations.spec.ts.snap index f939f1ea9..24ed39aff 100644 --- a/packages/typedoc-plugin-markdown/test/specs/__snapshots__/declarations.spec.ts.snap +++ b/packages/typedoc-plugin-markdown/test/specs/__snapshots__/declarations.spec.ts.snap @@ -148,16 +148,16 @@ exports[`Declarations: should compile object literal declaration 1`] = ` #### Type declaration -| Name | Type | -| :------ | :------ | -| \`valueA\` | \`number\` | -| \`valueB\` | \`boolean\` | -| \`valueC\` | {} | -| \`valueX\` | { \`valueA\`: \`number\`[] ; \`valueZ\`: \`string\` = 'foo' } | -| \`valueX.valueA\` | \`number\`[] | -| \`valueX.valueZ\` | \`string\` | -| \`valueY\` | () => \`string\` | -| \`valueZ\` | \`string\` | +| Name | Type | Description | +| :------ | :------ | :------ | +| \`valueA\` | \`number\` | Comment for valueA | +| \`valueB\` | \`boolean\` | - | +| \`valueC\` | {} | - | +| \`valueX\` | { \`valueA\`: \`number\`[] ; \`valueZ\`: \`string\` = 'foo' } | Comment for valueX | +| \`valueX.valueA\` | \`number\`[] | - | +| \`valueX.valueZ\` | \`string\` | - | +| \`valueY\` | () => \`string\` | Comment for value Y | +| \`valueZ\` | \`string\` | Comment for valueZ | [partial: member.sources] " diff --git a/packages/typedoc-plugin-markdown/test/specs/__snapshots__/toc.spec.ts.snap b/packages/typedoc-plugin-markdown/test/specs/__snapshots__/toc.spec.ts.snap index feb7a2364..a0ce6f0b5 100644 --- a/packages/typedoc-plugin-markdown/test/specs/__snapshots__/toc.spec.ts.snap +++ b/packages/typedoc-plugin-markdown/test/specs/__snapshots__/toc.spec.ts.snap @@ -5,23 +5,27 @@ exports[`TOC: (default) should display toc for class' 1`] = ` ### Constructors -- [constructor](Breadcrumbs.md#constructor) +- [constructor](SomeClass.md#constructor) ### Methods -- [someMethod](Breadcrumbs.md#somemethod) +- [someMethod](SomeClass.md#somemethod) " `; exports[`TOC: (default) should display toc for module' 1`] = ` "## Table of contents -### Constructors +### Classes -- [constructor](Breadcrumbs.md#constructor) +- [SomeClass](SomeClass.md) -### Methods +### Type aliases + +- [answer](../modules.md#answer) + +### Variables -- [someMethod](Breadcrumbs.md#somemethod) +- [answer](../modules.md#answer-1) " `; diff --git a/packages/typedoc-plugin-markdown/test/specs/toc.spec.ts b/packages/typedoc-plugin-markdown/test/specs/toc.spec.ts index a688ec05d..e897580cc 100644 --- a/packages/typedoc-plugin-markdown/test/specs/toc.spec.ts +++ b/packages/typedoc-plugin-markdown/test/specs/toc.spec.ts @@ -1,6 +1,5 @@ import * as Handlebars from 'handlebars'; import { Reflection } from 'typedoc'; - import { TestApp } from '../test-app'; describe(`TOC:`, () => { @@ -10,10 +9,10 @@ describe(`TOC:`, () => { describe(`(default)`, () => { let testApp: TestApp; beforeAll(async () => { - testApp = new TestApp(['breadcrumbs.ts']); + testApp = new TestApp(['toc.ts']); await testApp.bootstrap(); - moduleReflection = testApp.project.children[0]; - classReflection = testApp.project.findReflectionByName('Breadcrumbs'); + moduleReflection = testApp.project; + classReflection = testApp.project.findReflectionByName('SomeClass'); }); test(`should display toc for module'`, () => { @@ -31,10 +30,10 @@ describe(`TOC:`, () => { describe(`(hideInPageToc)`, () => { let testApp: TestApp; beforeAll(async () => { - testApp = new TestApp(['breadcrumbs.ts']); + testApp = new TestApp(['toc.ts']); await testApp.bootstrap({ hideInPageTOC: true }); moduleReflection = testApp.project.children[0]; - classReflection = testApp.project.findReflectionByName('Breadcrumbs'); + classReflection = testApp.project.findReflectionByName('SomeClass'); }); test(`should not display toc for class'`, () => { diff --git a/packages/typedoc-plugin-markdown/test/stubs/src/index.ts b/packages/typedoc-plugin-markdown/test/stubs/src/index.ts index 3ee5712d3..cfdc95a7e 100644 --- a/packages/typedoc-plugin-markdown/test/stubs/src/index.ts +++ b/packages/typedoc-plugin-markdown/test/stubs/src/index.ts @@ -10,4 +10,5 @@ export * as reflections from './reflections'; export * as signatures from './signatures'; export * as sources from './sources'; export * as theme from './theme'; +export * as toc from './toc'; export * as types from './types'; diff --git a/packages/typedoc-plugin-markdown/test/stubs/src/toc.ts b/packages/typedoc-plugin-markdown/test/stubs/src/toc.ts new file mode 100644 index 000000000..e1e0171a8 --- /dev/null +++ b/packages/typedoc-plugin-markdown/test/stubs/src/toc.ts @@ -0,0 +1,9 @@ +export type answer = 'yes' | 'no'; + +export const answer = 'yes'; + +export class SomeClass { + someMethod() { + return true; + } +}