diff --git a/server/utils/parseDocumentIds.test.ts b/server/utils/parseDocumentIds.test.ts index 80e2fb85c..46a5e1c37 100644 --- a/server/utils/parseDocumentIds.test.ts +++ b/server/utils/parseDocumentIds.test.ts @@ -3,14 +3,21 @@ import parseDocumentIds from "./parseDocumentIds"; it("should not return non links", () => { expect(parseDocumentIds(`# Header`).length).toBe(0); }); + it("should return an array of document ids", () => { const result = parseDocumentIds(`# Header - [internal](/doc/test-456733) + [internal](http://app.getoutline.com/doc/test-456733) + + More text + + [internal](/doc/test-123456#heading-anchor) `); - expect(result.length).toBe(1); + expect(result.length).toBe(2); expect(result[0]).toBe("test-456733"); + expect(result[1]).toBe("test-123456"); }); + it("should not return duplicate document ids", () => { expect(parseDocumentIds(`# Header`).length).toBe(0); const result = parseDocumentIds(`# Header @@ -22,9 +29,11 @@ it("should not return duplicate document ids", () => { expect(result.length).toBe(1); expect(result[0]).toBe("test-456733"); }); + it("should not return non document links", () => { expect(parseDocumentIds(`[google](http://www.google.com)`).length).toBe(0); }); + it("should not return non document relative links", () => { expect(parseDocumentIds(`[relative](/developers)`).length).toBe(0); }); diff --git a/server/utils/parseDocumentIds.ts b/server/utils/parseDocumentIds.ts index fbdbba53b..4bd5f7c7d 100644 --- a/server/utils/parseDocumentIds.ts +++ b/server/utils/parseDocumentIds.ts @@ -1,28 +1,30 @@ import { Node } from "prosemirror-model"; +import parseDocumentSlug from "@shared/utils/parseDocumentSlug"; import { parser } from "@server/editor"; +/** + * Parse a list of unique document identifiers contained in links in markdown + * text. + * + * @param text The text to parse in Markdown format + * @returns An array of document identifiers + */ export default function parseDocumentIds(text: string): string[] { const value = parser.parse(text); - const links: string[] = []; + const identifiers: string[] = []; function findLinks(node: Node) { // get text nodes if (node.type.name === "text") { // get marks for text nodes node.marks.forEach((mark) => { - // any of the marks links? + // any of the marks identifiers? if (mark.type.name === "link") { - const { href } = mark.attrs; + const slug = parseDocumentSlug(mark.attrs.href); - // any of the links to other docs? - if (href.startsWith("/doc")) { - const tokens = href.replace(/\/$/, "").split("/"); - const lastToken = tokens[tokens.length - 1]; - - // don't return the same link more than once - if (!links.includes(lastToken)) { - links.push(lastToken); - } + // don't return the same link more than once + if (slug && !identifiers.includes(slug)) { + identifiers.push(slug); } } }); @@ -36,5 +38,5 @@ export default function parseDocumentIds(text: string): string[] { } findLinks(value); - return links; + return identifiers; } diff --git a/shared/utils/parseDocumentSlug.ts b/shared/utils/parseDocumentSlug.ts index 556306caf..189f69de7 100644 --- a/shared/utils/parseDocumentSlug.ts +++ b/shared/utils/parseDocumentSlug.ts @@ -1,3 +1,9 @@ +/** + * Parse the likely document identifier from a given url. + * + * @param url The url to parse. + * @returns A document identifier or undefined if not found. + */ export default function parseDocumentSlug(url: string) { let parsed; @@ -12,6 +18,6 @@ export default function parseDocumentSlug(url: string) { } return parsed.lastIndexOf("/doc/") === 0 - ? parsed.replace(/^\/doc\//, "") - : null; + ? parsed.replace(/^\/doc\//, "").split("#")[0] + : undefined; }