Skip to content

Commit

Permalink
Merge pull request #22 from sillsdev/HeadingLinks
Browse files Browse the repository at this point in the history
feat: Make heading links work (#20)
  • Loading branch information
andrew-polk authored Dec 1, 2022
2 parents 5c7f420 + 088e6a6 commit cc93d95
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 10 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"scripts": {
"test": "jest",
"build": "yarn test && tsc && cp ./src/css/*.css dist/",
"build-only": "tsc && cp ./src/css/*.css dist/",
"clean": "rm -rf ./dist/",
"semantic-release": "semantic-release",
"typecheck": "tsc --noEmit",
Expand Down
33 changes: 33 additions & 0 deletions src/CustomTranformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,39 @@ export function setupCustomTransformers(
numberedListTransformer(notionToMarkdown, notionClient, block)
);

const headingCustomTransformer = async (
block: ListBlockChildrenResponseResult
) => {
// This is the other half of the horrible hack in pull.ts which sets the type
// of every heading_n to my_heading_n. We have to do this because if
// we simply set a custom transformer to heading_n, it will keep
// recursively calling this code, with blockToMarkdown using the custom transformer
// over and over. Instead, we want blockToMarkdown to give us the normal
// result, to which we will append the block ID to enable heading links.
(block as any).type = (block as any).type.replace("my_", "");

const unmodifiedMarkdown = await notionToMarkdown.blockToMarkdown(block);
// For some reason, inline links come in without the dashes, so we have to strip
// dashes here to match them.
const blockIdSansDashes = block.id.replaceAll("-", "");
// To make heading links work in docusaurus, you make them look like:
// ### Hello World {#my-explicit-id}
// See https://docusaurus.io/docs/markdown-features/toc#heading-ids.
return `${unmodifiedMarkdown} {#${blockIdSansDashes}}`;
};
notionToMarkdown.setCustomTransformer(
"my_heading_1",
headingCustomTransformer
);
notionToMarkdown.setCustomTransformer(
"my_heading_2",
headingCustomTransformer
);
notionToMarkdown.setCustomTransformer(
"my_heading_3",
headingCustomTransformer
);

// Note: Pull.ts also adds an image transformer, but has to do that for each
// page so we don't do it here.
}
Expand Down
9 changes: 6 additions & 3 deletions src/NotionPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { RateLimiter } from "limiter";
import { Client } from "@notionhq/client";
import { logDebug } from "./log";
import { parseLinkId } from "./links";
import { info } from "console";

const notionLimiter = new RateLimiter({
Expand Down Expand Up @@ -75,9 +76,11 @@ export class NotionPage {
}

public matchesLinkId(id: string): boolean {
const { baseLinkId } = parseLinkId(id);

const match =
id === this.pageId || // from a link_to_page.pageId, which still has the dashes
id === this.pageId.replaceAll("-", ""); // from inline links, which are lacking the dashes
baseLinkId === this.pageId || // from a link_to_page.pageId, which still has the dashes
baseLinkId === this.pageId.replaceAll("-", ""); // from inline links, which are lacking the dashes

logDebug(
`matchedLinkId`,
Expand All @@ -95,7 +98,7 @@ export class NotionPage {
or
"type": "database_id",
...
},
},
*/
return (this.metadata as any).parent.type === "database_id"
? PageType.DatabasePage
Expand Down
31 changes: 25 additions & 6 deletions src/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ export function convertInternalLinks(
layoutStrategy: LayoutStrategy
): string {
const convertHref = (url: string) => {
const p = pages.find(p => {
const page = pages.find(p => {
return p.matchesLinkId(url);
});
if (p) {
verbose(
`Converting Link ${url} --> ${layoutStrategy.getLinkPathForPage(p)}`
);
return layoutStrategy.getLinkPathForPage(p);
if (page) {
let convertedLink = layoutStrategy.getLinkPathForPage(page);

// Include the fragment (# and after) if it exists
const { fragmentId } = parseLinkId(url);
convertedLink += fragmentId;

verbose(`Converting Link ${url} --> ${convertedLink}`);
return convertedLink;
}

// About this situation. See https://github.com/sillsdev/docu-notion/issues/9
Expand Down Expand Up @@ -101,3 +105,18 @@ function transformLinks(

return output;
}

// Parse the link ID to get the base (before the #) and the fragment (# and after).
export function parseLinkId(fullLinkId: string): {
baseLinkId: string; // before the #
fragmentId: string; // # and after
} {
const iHash: number = fullLinkId.indexOf("#");
if (iHash >= 0) {
return {
baseLinkId: fullLinkId.substring(0, iHash),
fragmentId: fullLinkId.substring(iHash),
};
}
return { baseLinkId: fullLinkId, fragmentId: "" };
}
11 changes: 10 additions & 1 deletion src/pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ async function getPagesRecursively(

// The best practice is to keep content pages in the "database" (e.g. kanban board), but we do allow people to make pages in the outline directly.
// So how can we tell the difference between a page that is supposed to be content and one that is meant to form the sidebar? If it
// have just links, then it's a page for forming the sidebar. If it has contents and no links, then it's a content page. But what if
// has only links, then it's a page for forming the sidebar. If it has contents and no links, then it's a content page. But what if
// it has both? Well then we assume it's a content page.
if (pageInfo.linksPageIdsAndOrder?.length) {
warning(
Expand Down Expand Up @@ -233,6 +233,15 @@ async function outputPage(page: NotionPage) {
relativePathToFolderContainingPage
)
);

// One half of a horrible hack to make heading links work.
// See the other half and explanation in CustomTransformers.ts => headingCustomTransformer.
for (const block_t of blocks) {
const block = block_t as any;
if (block.type.startsWith("heading"))
block.type = block.type.replace("heading", "my_heading");
}

const mdBlocks = await notionToMarkdown.blocksToMarkdown(blocks);

// if (page.nameOrTitle.startsWith("Embed")) {
Expand Down

0 comments on commit cc93d95

Please sign in to comment.