Skip to content

Instantly share code, notes, and snippets.

@barelyhuman
Last active October 26, 2022 11:52
Show Gist options
  • Save barelyhuman/0fd16dc0c8247eef992adb49d1dc6308 to your computer and use it in GitHub Desktop.
Save barelyhuman/0fd16dc0c8247eef992adb49d1dc6308 to your computer and use it in GitHub Desktop.

Notion Vuepress Sync

The script simply syncs from a notion database and renders the content of the pages created inside the table based on the following structure.

title type published
page name FAQ [x]
another page Category [x]

You can add in more customizations like sorting orders etc.

Currently does

  • Creates folders for a slugified version based on the type values
  • Adds the pages to these folders
  • If the page name and the folder name are the same, it'll create a README.md file instead

Recommended Plugins Plugins that'd make it easier if you're using the script above

require("dotenv").config();
const fs = require("fs");
const path = require("path");
const { Client } = require("@notionhq/client");
const { NotionToMarkdown } = require("notion-to-md");
const slugify = require("slugify").default;
const asyncSerial = require("@barelyhuman/useless/asyncSerial").default;
const config = {
// Token for a Notion Integration
apiToken: process.env.NOTION_API_TOKEN,
// a database table of the
baseDB: process.env.NOTION_BASE_DB,
};
const notion = new Client({
auth: config.apiToken,
});
// passing notion client to the option
const n2m = new NotionToMarkdown({ notionClient: notion });
const indexGen = indexContentGenerator();
(async function main() {
const { results } = await notion.databases.query({
database_id: config.baseDB,
});
const props = getPropsFromBlocks(results);
await asyncSerial(props, dirMapper, { limit: 1 });
indexGen.commit();
fs.writeFileSync(path.join(".", "src", "README.md"), indexGen.value);
updateConfigWithProps(props);
})();
function normalizePropItem(propItem, target, key) {
if (!propItem) return;
const value = propItem[propItem.type];
if (typeof value === "undefined" || value === null) return;
if (
typeof value === "string" ||
typeof value === "number" ||
typeof value === "boolean"
) {
target[key] = value;
} else if (Array.isArray(value)) {
value.map((inner) => normalizePropItem(inner, target, key));
} else if (typeof value === "object" && value !== null) {
if ("type" in value) {
normalizePropItem(value, target, key);
} else if ("content" in value) {
target[key] = value;
}
}
}
function updateConfigWithProps(dirs) {
const uniqueArray = [];
dirs.reduce((acc, item) => {
const key = item.type.content;
if (!acc.has(key)) {
uniqueArray.push(item);
acc.add(key);
}
return acc;
}, new Set());
// define path to config
const configPath = "./src/.vuepress/config.js";
let config = fs.readFileSync(configPath, "utf-8");
config = normalizeNav(config, uniqueArray);
fs.writeFileSync(configPath, config);
}
function normalizeNav(config, directories) {
const out = config.replace(
/nav:[ ]*\[[^\]]+\](,)*/,
`nav:[
${directories.map(
(x) =>
`{
text: "${x.type.content}",
link:"/${slugify(x.type.content.toLowerCase())}/"
}`
)}
],`
);
return out;
}
function getPropsFromBlocks(blocks) {
const directories = [];
blocks.forEach((item) => {
var props = {};
props.id = item.id;
Object.keys(item.properties).forEach((prop) => {
normalizePropItem(item.properties[prop], props, prop);
});
if (props.published) {
directories.push(props);
}
});
return directories;
}
async function dirMapper(propItem) {
const _category = propItem.type.content;
const categorySlug = slugify(propItem.type.content.toLowerCase());
const _dirPath = path.join(".", "src", categorySlug);
fs.mkdirSync(_dirPath, {
recursive: true,
});
indexGen.add(`- [${_category}](/${categorySlug}/)`);
const mdblocks = await n2m.pageToMarkdown(propItem.id);
const mdString = n2m.toMarkdownString(mdblocks);
let fileName = slugify(propItem.title.content.toLowerCase());
if (propItem.title.content.toLowerCase() === categorySlug.toLowerCase()) {
fileName = "README";
}
//writing to file
let _fpath = path.join(".", "src", categorySlug, fileName);
const finalMDString = "# " + propItem.title.content + "\n\n" + mdString;
fs.writeFileSync(_fpath + ".md", finalMDString);
const _dirBaseFile = path.join(_dirPath, "README.md");
const hasBaseFile = fs.existsSync(_dirBaseFile);
if (!hasBaseFile) {
fs.writeFileSync(_dirBaseFile, `# ${_category}`);
}
}
function indexContentGenerator() {
let indexContent = `
# Contents
{{template}}`;
return {
add(value) {
indexContent = indexContent.replace(
"{{template}}",
`${value}
{{template}}`
);
},
commit() {
indexContent = indexContent.replace("{{template}}", "");
},
value: indexContent,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment