Skip to content

Commit

Permalink
feat(scully): support icon, cover, and more customization for Page Pr…
Browse files Browse the repository at this point in the history
…operties

- Post Icon is now supported
- Post Cover is now supported
- More customization via
NotionDomRouterPluginOptions

BREAKING CHANGE:
- `NotionPluginOptions` -> `NotionDomPluginOptions`
  • Loading branch information
Chau Tran authored and Chau Tran committed Oct 20, 2021
1 parent 37116e7 commit 2b9e1ac
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 37 deletions.
7 changes: 4 additions & 3 deletions libs/scully-plugin-notion/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ import { ScullyConfig, setPluginConfig } from '@scullyio/scully';
import {
NotionDom,
NotionDomRouter,
NotionPluginOptions,
NotionDomPluginOptions,
NotionDomRouterPluginOptions
} from '@notion-stuff/scully-plugin-notion';

setPluginConfig(NotionDom, {
Expand All @@ -86,7 +87,7 @@ setPluginConfig(NotionDom, {
... customer the parser ...
*/
},
} as NotionPluginOptions);
} as NotionDomPluginOptions);

export const config: ScullyConfig = {
projectRoot: './src',
Expand All @@ -101,7 +102,7 @@ export const config: ScullyConfig = {
basePath: '/blog', // optional, should match your route here
titleSuffix: '', // optional
slugKey: 'slug', // optional
},
} as NotionDomRouterPluginOptions,
},
};
```
52 changes: 51 additions & 1 deletion libs/scully-plugin-notion/src/lib/plugin-options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,55 @@
import type { NotionBlocksHtmlParserOptions } from '@notion-stuff/blocks-html-parser';
import { PostResult, PropertyValue } from '@notion-stuff/v4-types';
import type { RouteConfig } from '@scullyio/scully';

export interface NotionPluginOptions {

export interface NotionDomRouterPluginOptions extends RouteConfig {
/**
* @requires string Notion DatabaseID
*/
databaseId: string;

/**
* Notion API Key. This should be provided via NOTION_API_KEY environment variable instead
*/
notionApiKey?: string;

/**
* A custom function that will process the {PostResult} from Notion. You take over the Frontmatter with this function
*/
postResultProcessor?<TObject = Record<string, unknown>>(post: PostResult, options: NotionDomRouterPluginOptions, propertyValueParser: (propertyValue: PropertyValue) => any): TObject;

/**
* A custom function that will resolve the `published` flag that Scully needs.
*/
isPublished?<TObject = Record<string, unknown>>(frontmatter: TObject): boolean;

/**
* Default icon for your post
*/
defaultPostIcon?: string;

/**
* The key that you use as your "Slug" in your Notion table.
* @default "slug" (requires you to have a "Slug" property in your table)
*/
slugKey?: string;

/**
* The base path of the route when setup in Scully config
* @example
* {
* "/basePath/:slug": {
* basePath: "/basePath" // needs to match /basePath
* }
* }
*
* @default "/blog"
*/
basePath?: string;
titleSuffix?: string;
}

export interface NotionDomPluginOptions {
notionBlocksHtmlParserOptions?: NotionBlocksHtmlParserOptions;
}
65 changes: 40 additions & 25 deletions libs/scully-plugin-notion/src/lib/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { NotionBlocksHtmlParser } from '@notion-stuff/blocks-html-parser';
import { Client } from '@notionhq/client/build/src';
import type { HandledRoute, RouteConfig } from '@scullyio/scully';
import {
getPluginConfig,
log,
red,
registerPlugin,
yellow,
} from '@scullyio/scully';
import { getPluginConfig, log, red, registerPlugin, yellow } from '@scullyio/scully';
import { injectHtml } from '@scullyio/scully/src/lib/renderPlugins/content-render-utils/injectHtml';
import { pagePropertiesToFrontmatter } from './page-properties-to-frontmatter';
import type { NotionPluginOptions } from './plugin-options';
import type { NotionDomPluginOptions, NotionDomRouterPluginOptions } from './plugin-options';
import { processPageProperties } from './process-page-properties';

export const NotionDom = 'notionDom';
export const NotionDomRouter = 'notionDomRouter';

const pluginOptions: NotionDomPluginOptions =
getPluginConfig(NotionDom, 'postProcessByDom') || {};

const notionBlocksHtmlParser = NotionBlocksHtmlParser.getInstance(
pluginOptions.notionBlocksHtmlParserOptions
);

let notion: Client;

try {
Expand All @@ -29,12 +30,12 @@ try {

async function notionDomRouterPlugin(
route: string | undefined,
config: RouteConfig
config: NotionDomRouterPluginOptions
) {
const mergedConfig = {
...{ slugKey: 'slug', basePath: '/blog', titleSuffix: '' },
...(config || {}),
} as RouteConfig;
...{ slugKey: 'slug', basePath: '/blog', titleSuffix: '', defaultPostIcon: '' },
...config
};

try {
if (!notion) {
Expand All @@ -47,17 +48,36 @@ async function notionDomRouterPlugin(
}

notion = new Client({
auth: mergedConfig.notionApiKey,
auth: mergedConfig.notionApiKey
});
}

const posts = await notion.databases.query({
database_id: mergedConfig.databaseId,
database_id: mergedConfig.databaseId
});

return Promise.resolve(
posts.results.map((postResult) => {
const frontmatter = pagePropertiesToFrontmatter(postResult.properties);
const frontmatter = processPageProperties(postResult, mergedConfig);

const { url: cover } =
NotionBlocksHtmlParser.getMarkdownParser().parseFile(
postResult.cover
);

let icon = mergedConfig.defaultPostIcon;
switch (postResult.icon.type) {
case 'emoji':
icon = postResult.icon.emoji;
break;
case 'external':
case 'file':
icon = NotionBlocksHtmlParser.getMarkdownParser().parseFile(
postResult.icon
).url;
break;
}

return {
type: mergedConfig.type,
route: `${mergedConfig.basePath}/${
Expand All @@ -70,8 +90,9 @@ async function notionDomRouterPlugin(
...frontmatter,
id: postResult.id,
notionUrl: postResult.url,
cover: postResult.cover,
},
cover,
icon
}
} as HandledRoute;
})
);
Expand Down Expand Up @@ -107,19 +128,13 @@ async function notionDomPlugin(dom: any, route: HandledRoute | undefined) {

try {
const blocks = await notion.blocks.children.list({
block_id: postId,
block_id: postId
});
if (!blocks || !blocks.results.length) {
log(yellow(`Post does not have any blocks. Skipping ${route.route}`));
return Promise.resolve(dom);
}

const pluginOptions: NotionPluginOptions =
getPluginConfig(NotionDom, 'postProcessByDom') || {};
const notionBlocksHtmlParser = NotionBlocksHtmlParser.getInstance(
pluginOptions.notionBlocksHtmlParserOptions
);

return injectHtml(dom, notionBlocksHtmlParser.parse(blocks.results), route);
} catch (e) {
log(red(`Something went wrong. ${e}`));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
import type {
PostResult,
PropertyValue,
PropertyValueFormula,
PropertyValueMap,
PropertyValueRollup,
PropertyValueSelect,
PropertyValueUser,
PropertyValueUser
} from '@notion-stuff/v4-types';
import type { NotionDomRouterPluginOptions } from './plugin-options';

import { camelize } from './utils';

export function pagePropertiesToFrontmatter(properties: PropertyValueMap) {
export function processPageProperties(post: PostResult, options: NotionDomRouterPluginOptions) {
if (options.postResultProcessor) {
return options.postResultProcessor(post, options, parsePropertyValue);
}

const frontmatter: Record<string, unknown> = { published: false };

for (const [propertyKey, propertyValue] of Object.entries(properties)) {
for (const [propertyKey, propertyValue] of Object.entries(post.properties)) {
const camelizedKey = camelize(propertyKey);
frontmatter[camelizedKey] = parsePropertyValue(propertyValue);

if (propertyKey.toLowerCase() === 'status') {
if (!options.isPublished && propertyKey.toLowerCase() === 'status') {
frontmatter.published =
(propertyValue as PropertyValueSelect).select?.name.toLowerCase() === 'published';
}
}

if (options.isPublished) {
frontmatter.published = options.isPublished(frontmatter);
}

return frontmatter;
}

Expand All @@ -44,7 +53,7 @@ function parsePropertyValue(propertyValue: PropertyValue) {
if (propertyValue.date.end) {
return [
new Date(propertyValue.date.start),
new Date(propertyValue.date.end),
new Date(propertyValue.date.end)
];
}
return new Date(propertyValue.date.start);
Expand Down Expand Up @@ -92,7 +101,7 @@ function formularize(formulaValue: PropertyValueFormula) {
if (formulaValue.formula.date?.end) {
value = [
formulaValue.formula.date?.start,
formulaValue.formula.date?.end,
formulaValue.formula.date?.end
];
} else {
value = formulaValue.formula.date?.start;
Expand Down Expand Up @@ -129,6 +138,6 @@ function rollup(rollupValue: PropertyValueRollup) {
function personify(user: PropertyValueUser) {
return {
name: user.name,
avatar: user.avatar_url,
avatar: user.avatar_url
};
}

0 comments on commit 2b9e1ac

Please sign in to comment.