Skip to content

Commit

Permalink
fix: better handling of callouts (BL-11548)
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnThomson committed Sep 28, 2022
1 parent 40346d4 commit 054f098
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 40 deletions.
126 changes: 126 additions & 0 deletions src/CalloutTransformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { NotionToMarkdown } from "notion-to-md";
import { ListBlockChildrenResponseResult } from "notion-to-md/build/types";
import { Client } from "@notionhq/client";
import { getBlockChildren } from "./CustomTranformers";

export async function notionCalloutToAdmonition(
notionToMarkdown: NotionToMarkdown,
notionClient: Client,
block: ListBlockChildrenResponseResult
): Promise<string> {
// In this case typescript is not able to index the types properly, hence ignoring the error
// @ts-ignore
const blockContent = block.callout.text || block.callout.rich_text || [];
// @ts-ignore
const icon = block.callout.icon;
let parsedData = "";
blockContent.map((content: Text) => {
const annotations = content.annotations;
let plain_text = content.plain_text;

plain_text = notionToMarkdown.annotatePlainText(plain_text, annotations);

// if (content["href"])
// plain_text = md.link(plain_text, content["href"]);

parsedData += plain_text;
});

let callout_string = "";
const { id, has_children } = block as any;
if (!has_children) {
const result1 = callout(parsedData, icon);
return result1;
}

const callout_children_object = await getBlockChildren(notionClient, id, 100);

// // parse children blocks to md object
const callout_children = await notionToMarkdown.blocksToMarkdown(
callout_children_object
);

callout_string += `${parsedData}\n`;
callout_children.map(child => {
callout_string += `${child.parent}\n\n`;
});

const result = callout(callout_string.trim(), icon);
return result;
}

// types copied from notion-to-md to allow compilation of copied code.
type TextRequest = string;

type Annotations = {
bold: boolean;
italic: boolean;
strikethrough: boolean;
underline: boolean;
code: boolean;
color:
| "default"
| "gray"
| "brown"
| "orange"
| "yellow"
| "green"
| "blue"
| "purple"
| "pink"
| "red"
| "gray_background"
| "brown_background"
| "orange_background"
| "yellow_background"
| "green_background"
| "blue_background"
| "purple_background"
| "pink_background"
| "red_background";
};
type Text = {
type: "text";
text: {
content: string;
link: {
url: TextRequest;
} | null;
};
annotations: Annotations;
plain_text: string;
href: string | null;
};

type CalloutIcon =
| { type: "emoji"; emoji?: string }
| { type: "external"; external?: { url: string } }
| { type: "file"; file: { url: string; expiry_time: string } }
| null;

const calloutsToAdmonitions = {
/* prettier-ignore */ "ℹ️": "note",
"💡": "tip",
"❗": "info",
"⚠️": "caution",
"🔥": "danger",
};

// This is the main change from the notion-to-md code.
function callout(text: string, icon?: CalloutIcon) {
let emoji: string | undefined;
if (icon?.type === "emoji") {
emoji = icon.emoji;
}
let docusaurusAdmonition = "note";
if (emoji) {
// the keyof typeof magic persuades typescript that it really is OK to use emoji as a key into calloutsToAdmonitions
docusaurusAdmonition =
calloutsToAdmonitions[emoji as keyof typeof calloutsToAdmonitions] ??
// For Notion callouts with other emojis, pass them through using hte emoji as the name.
// For this to work on a Docusaurus site, it will need to define that time on the remark-admonitions options in the docusaurus.config.js.
// See https://github.com/elviswolcott/remark-admonitions and https://docusaurus.io/docs/using-plugins#using-presets.
emoji;
}
return `:::${docusaurusAdmonition}\n\n${text}\n\n:::\n\n`;
}
18 changes: 16 additions & 2 deletions src/CustomTranformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ListBlockChildrenResponseResult,
ListBlockChildrenResponseResults,
} from "notion-to-md/build/types";
import { notionCalloutToAdmonition } from "./CalloutTransformer";

export function setupCustomTransformers(
notionToMarkdown: NotionToMarkdown,
Expand Down Expand Up @@ -76,6 +77,19 @@ export function setupCustomTransformers(
}
);

// In Notion, you can make a callout and change its emoji. We map 5 of these
// to the 5 Docusaurus admonition styles.
// This is mostly a copy of the callout code from notion-to-md. The change is to output docusaurus
// admonitions instead of emulating a callout with markdown > syntax.
// Note: I haven't yet tested this with any emoji except "💡"/"tip", nor the case where the
// callout has-children. Not even sure what that would mean, since the document I was testing
// with has quite complex markup inside the callout, but still takes the no-children branch.
notionToMarkdown.setCustomTransformer(
"callout",
(block: ListBlockChildrenResponseResult) =>
notionCalloutToAdmonition(notionToMarkdown, notionClient, block)
);

// 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 Expand Up @@ -127,11 +141,11 @@ async function notionColumnToMarkdown(
)}\n\n</div>`;
}

async function getBlockChildren(
export async function getBlockChildren(
notionClient: Client,
block_id: string,
totalPage: number | null
) {
): Promise<ListBlockChildrenResponseResults> {
try {
const result: ListBlockChildrenResponseResults = [];
let pageCount = 0;
Expand Down
39 changes: 1 addition & 38 deletions src/DocusaurusTweaks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ export function tweakForDocusaurus(input: string): {
body: string;
imports: string;
} {
const output = notionCalloutsToAdmonitions(input);
const { body, imports } = notionEmbedsToMDX(output);
const { body, imports } = notionEmbedsToMDX(input);
return { body, imports };
}
// In Notion, you can embed videos & such. To show these
Expand Down Expand Up @@ -101,39 +100,3 @@ function notionEmbedsToMDX(input: string): {

return { body, imports: [...imports].join("\n") };
}

// In Notion, you can make a callout and change its emoji. We map 5 of these
// to the 5 Docusaurus admonition styles.
function notionCalloutsToAdmonitions(input: string): string {
const notionCalloutPattern = />\s(ℹ️|⚠️|💡|❗|🔥|.)\s(.*)\n/gmu;
const calloutsToAdmonitions = {
/* prettier-ignore */ "ℹ️": "note",
"💡": "tip",
"❗": "info",
"⚠️": "caution",
"🔥": "danger",
};
let output = input;
let match;
while ((match = notionCalloutPattern.exec(input)) !== null) {
const string = match[0];
const emoji = match[1] as keyof typeof calloutsToAdmonitions;
const content = match[2];

const docusaurusAdmonition = calloutsToAdmonitions[emoji];
if (docusaurusAdmonition) {
output = output.replace(
string,
`:::${docusaurusAdmonition}\n\n${content}\n\n:::\n\n`
);
}
// For Notion callouts with other emojis, pass them through using hte emoji as the name.
// For this to work on a Docusaurus site, it will need to define that time on the remark-admonitions options in the docusaurus.config.js.
// See https://github.com/elviswolcott/remark-admonitions and https://docusaurus.io/docs/using-plugins#using-presets.
else {
output = output.replace(string, `:::${emoji}\n\n${content}\n\n:::\n\n`);
}
}

return output;
}

0 comments on commit 054f098

Please sign in to comment.