astronotion
Version:
Library to make Astro sites with content from Notion
175 lines (146 loc) • 4.66 kB
text/typescript
import type { Collection, Decoration, ExtendedRecordMap, PageBlock } from "notion-types";
import { checkIfParentPage } from "./check-page-type";
import { filterContentBlocks, filterChildBlocks } from "./filter-blocks";
import type { AnContentBlock } from "./filter-blocks";
import { flattenBlocks, flattenText } from "./flatten";
import { getCover } from "./get-cover";
import { transformImageUrl } from "./image-file";
type AnPageBase = {
title: string;
icon?: string;
cover?: ReturnType<typeof getCover>;
description?: Decoration[];
};
export type AnExcerpt = AnPageBase & {
id: string;
taxonomies: string[]; // !! update when implementing the feature
created: number;
updated: number;
};
export type AnParentPage = {
pageType: "NOTION_PARENT";
pageData: AnPageBase & {
childPages: AnExcerpt[];
};
};
export type AnEntryPage = {
pageType: "NOTION_ENTRY";
pageData: AnExcerpt & {
contents: AnContentBlock[];
};
};
export type AnTopLevelPage = AnParentPage | AnEntryPage;
type CollectionWithCoverAndDesc = Collection & {
cover?: string;
description?: Decoration[];
};
/**
* Get and transform the "signed_urls" field from page data returned
* by notion-client. These will be compared against image URLs to
* determine whether they should be downloaded before they expire.
*
* @param {ExtendedRecordMap} data
* @returns {Image}
*/
const getDownloadableUrlsFromPage = (data: ExtendedRecordMap) => {
if (!data.signed_urls) return [];
return Object.entries(data.signed_urls).map((item) => item[1]);
};
const transformChildPageBlock = (data: PageBlock, downloadableUrls?: string[]) => {
const { id, properties, format, created_time, last_edited_time } = data;
const title = flattenText(properties?.title || []);
const icon = format?.page_icon;
const cover = getCover(data);
if (cover && cover.url) {
cover.url = transformImageUrl(cover.url, downloadableUrls);
}
// TODO (later) taxonomies & custom created/updated
const taxonomies: string[] = [];
const created = created_time;
const updated = last_edited_time;
return {
id,
title,
icon,
cover,
description: undefined as Decoration[] | undefined, // !! temporary workaround
taxonomies,
created,
updated,
};
};
const transformContentBlock = (
data: ReturnType<typeof filterContentBlocks>[number],
downloadableUrls?: string[]
) => {
if (data.type !== "image") return data; // Do nothing for non-image blocks.
const imageUrl = data.properties.source[0][0];
if (imageUrl) {
data.properties.source[0][0] = transformImageUrl(imageUrl, downloadableUrls);
}
return data;
};
export const transformParentPage = (data: ExtendedRecordMap): AnParentPage | undefined => {
const coll = Object.entries(data.collection)[0][1].value as CollectionWithCoverAndDesc;
const downloadableUrls = getDownloadableUrlsFromPage(data);
const { name, description, icon } = coll;
const title = flattenText(name);
const cover = getCover(coll);
if (cover && cover.url) {
cover.url = transformImageUrl(cover.url, downloadableUrls);
}
const id = flattenBlocks(data.block)[0].id;
const childPages = filterChildBlocks(data.block, id).map((item) =>
transformChildPageBlock(item as PageBlock, downloadableUrls)
);
return {
pageType: "NOTION_PARENT",
pageData: {
title,
icon,
cover,
description,
childPages,
},
};
};
export const transformEntryPage = (data: ExtendedRecordMap): AnEntryPage | undefined => {
const blocks = flattenBlocks(data.block);
const pageBlock = blocks[0];
if (pageBlock.type !== "page" || !pageBlock.properties || blocks.length < 3) return;
const downloadableUrls = getDownloadableUrlsFromPage(data);
const { id, properties, format, created_time, last_edited_time } = pageBlock;
const title = flattenText(properties.title);
const icon = format?.page_icon;
const cover = getCover(pageBlock);
if (cover && cover.url) {
cover.url = transformImageUrl(cover.url, downloadableUrls);
}
const contents = filterContentBlocks(blocks).map((item) =>
transformContentBlock(item, downloadableUrls)
);
// TODO (later) taxonomies, custom created/updated, description
const taxonomies: string[] = [];
const created = created_time;
const updated = last_edited_time;
let description;
return {
pageType: "NOTION_ENTRY",
pageData: {
id,
title,
icon,
cover,
description,
contents,
taxonomies,
created,
updated,
},
};
};
export const transformTopLevelPage = (data: ExtendedRecordMap) => {
const isParent = checkIfParentPage(data);
return isParent ? transformParentPage(data) : transformEntryPage(data);
};
// ?? differentiate _top-level vs child_ entry page if needed