diff --git a/README.md b/README.md index 6c84d92..8419c1c 100644 --- a/README.md +++ b/README.md @@ -112,20 +112,47 @@ NOTE: if you just localize an image, it will not get picked up. You also must lo Here is a working Github Action script to copy and customize: https://github.com/BloomBooks/bloom-docs/blob/master/.github/workflows/release.yml +# Links within mermaid code blocks + +Mermaid code blocks can contain clickable links to other notion documents. This works great in notion, but to convert you might need to add the flag: + +``` + --slug-prefix +``` + +where `` might be e.g. `blog` if the root of the converted documents is are your blogs and they live in the directory "./blog". + +This converts the `An internal page` node below into a clickable link in your published site: + +```mermaid +graph LR + A[An internal page] --> B(Somewhere else) + click A "https://www.notion.so/Introduction-to-docu-notion-779f83504bd94642a9b87b2afc810aaa" +``` + +``` +graph LR + A[An internal page] --> B(Somewhere else) + click A "https://www.notion.so/Introduction-to-docu-notion-779f83504bd94642a9b87b2afc810aaa" +``` + + + # Command line Usage: docu-notion -n -r [options] Options: -| flag | required? | description | -| ------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| -n, --notion-token | required | notion api token, which looks like `secret_3bc1b50XFYb15123RHF243x43450XFY33250XFYa343` | -| -r, --root-page | required | The 31 character ID of the page which is the root of your docs page in notion. The code will look like `9120ec9960244ead80fa2ef4bc1bba25`. This page must have a child page named 'Outline' | -| -m, --markdown-output-path | | Root of the hierarchy for md files. WARNING: node-pull-mdx will delete files from this directory. Note also that if it finds localized images, it will create an i18n/ directory as a sibling. (default: "./docs") | -| -t, --status-tag | | Database pages without a Notion page property 'status' matching this will be ignored. Use '\*' to ignore status altogether. (default: `Publish`) | -| --locales | | Comma-separated list of iso 639-2 codes, the same list as in docusaurus.config.js, minus the primary (i.e. 'en'). This is needed for image localization. (default: []) | -| -l, --log-level | | Log level (choices: `info`, `verbose`, `debug`) | -| -i, --img-output-path | | Path to directory where images will be stored. If this is not included, images will be placed in the same directory as the document that uses them, which then allows for localization of screenshots. | -| -p, --img-prefix-in-markdown | | When referencing an image from markdown, prefix with this path instead of the full img-output-path. Should be used only in conjunction with --img-output-path. | -| -h, --help | | display help for command | +| flag | required? | description | +| ---------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| -n, --notion-token | required | notion api token, which looks like `secret_3bc1b50XFYb15123RHF243x43450XFY33250XFYa343` | +| -r, --root-page | required | The 31 character ID of the page which is the root of your docs page in notion. The code will look like `9120ec9960244ead80fa2ef4bc1bba25`. This page must have a child page named 'Outline' | +| -m, --markdown-output-path | | Root of the hierarchy for md files. WARNING: node-pull-mdx will delete files from this directory. Note also that if it finds localized images, it will create an i18n/ directory as a sibling. (default: "./docs") | +| -t, --status-tag | | Database pages without a Notion page property 'status' matching this will be ignored. Use '\*' to ignore status altogether. (default: `Publish`) | +| --locales | | Comma-separated list of iso 639-2 codes, the same list as in docusaurus.config.js, minus the primary (i.e. 'en'). This is needed for image localization. (default: []) | +| -l, --log-level | | Log level (choices: `info`, `verbose`, `debug`) | +| -i, --img-output-path | | Path to directory where images will be stored. If this is not included, images will be placed in the same directory as the document that uses them, which then allows for localization of screenshots. | +| -p, --img-prefix-in-markdown | | When referencing an image from markdown, prefix with this path instead of the full img-output-path. Should be used only in conjunction with --img-output-path. | +| -s, --mermaid-slug-prefix | | Code block mermaid diagrams can contain document links that are not processed by docusaurus. docu-notion prefixes the document slug with this value to correct this (default ""). | +| -h, --help | | display help for command | diff --git a/src/index.ts b/src/index.ts index 8ad512a..aa24223 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,11 @@ program "-r, --root-page ", "The 31 character ID of the page which is the root of your docs page in notion. The code will look like 9120ec9960244ead80fa2ef4bc1bba25. This page must have a child page named 'Outline'" ) + .option( + "-s, --mermaid-slug-prefix ", + "Prefix to add to document slugs for mermaid internal diagram links", + "" + ) .option( "-m, --markdown-output-path ", "Root of the hierarchy for md files. WARNING: node-pull-mdx will delete files from this directory. Note also that if it finds localized images, it will create an i18n/ directory as a sibling.", diff --git a/src/linksMermaid.ts b/src/linksMermaid.ts new file mode 100644 index 0000000..9b6814f --- /dev/null +++ b/src/linksMermaid.ts @@ -0,0 +1,80 @@ +import { join } from "path"; +import { LayoutStrategy } from "./LayoutStrategy"; +import { verbose, warning } from "./log"; +import { NotionPage } from "./NotionPage"; + +export function convertMermaidInternalLinks( + markdown: string, + pages: NotionPage[], + layoutStrategy: LayoutStrategy, + slugPrefix: string +): string { + // Eg url="https://www.notion.so/Introduction-to-docu-notion-779f83504bd94642a9b87b2afc810a97" + const convertHref = (url: string) => { + // Do not convert non-notion links + if (!url.startsWith("https://www.notion.so/")) { + return url; + } + const notionId = url.split("-").pop() || ""; + + const page = pages.find(p => { + return p.matchesLinkId(notionId); + }); + if (page) { + let convertedLink = layoutStrategy.getLinkPathForPage(page); + + if (slugPrefix) { + convertedLink = + (slugPrefix.startsWith("/") ? "" : "/") + + join(slugPrefix, convertedLink); + } + + verbose(`Converting Link ${url} --> ${convertedLink}`); + return convertedLink; + } + + // About this situation. See https://github.com/sillsdev/docu-notion/issues/9 + warning( + `Could not find the target of this link. Note that links to outline sections are not supported. ${url}. https://github.com/sillsdev/docu-notion/issues/9` + ); + + return url; + }; + + return transformMermaidLinks(markdown, convertHref); +} + +function transformMermaidLinks( + pageMarkdown: string, + convertHref: (url: string) => string +) { + // The mermaid interactive click syntax: + // https://mermaid.js.org/syntax/flowchart.html#interaction + // NB this processing is just for internal link navigation + const linkRegExp = + /\s*click\s+([A-za-z][A-za-z0-9_-]*)\s+"(https:\/\/www\.notion\.so\/\S*)"/g; + let output = pageMarkdown; + let match; + + // The key to understanding this while is that linkRegExp actually has state, and + // it gives you a new one each time. https://stackoverflow.com/a/1520853/723299 + + while ((match = linkRegExp.exec(pageMarkdown)) !== null) { + const originalLink = match[0]; + + const hrefFromNotion = match[2]; + const hrefForDocusaurus = convertHref(hrefFromNotion); + + if (hrefForDocusaurus) { + output = output.replace( + match[0], + `\n click ${match[1]} "${hrefForDocusaurus}"` + ); + verbose(`transformed link: ${originalLink}-->${hrefForDocusaurus}`); + } else { + verbose(`Maybe problem with link ${JSON.stringify(match)}`); + } + } + + return output; +} diff --git a/src/pull.ts b/src/pull.ts index 9b37b35..63aefae 100644 --- a/src/pull.ts +++ b/src/pull.ts @@ -17,10 +17,12 @@ import { error, heading, info, logDebug, verbose, warning } from "./log"; import { convertInternalLinks } from "./links"; import { ListBlockChildrenResponseResult } from "notion-to-md/build/types"; import chalk from "chalk"; +import { convertMermaidInternalLinks } from "./linksMermaid"; export type Options = { notionToken: string; rootPage: string; + mermaidSlugPrefix: string; locales: string[]; markdownOutputPath: string; imgOutputPath: string; @@ -263,6 +265,13 @@ async function outputPage(page: NotionPage) { // Improve: maybe this could be another markdown-to-md "custom transformer" markdown = convertInternalLinks(markdown, pages, layoutStrategy); + // Improve: maybe this could be another markdown-to-md "custom transformer" + markdown = convertMermaidInternalLinks( + markdown, + pages, + layoutStrategy, + options.mermaidSlugPrefix + ); // Improve: maybe this could be another markdown-to-md "custom transformer" const { body, imports } = tweakForDocusaurus(markdown);