diff --git a/scripts/smart-search.mjs b/scripts/smart-search.mjs
index 73715af6..e8344f8a 100644
--- a/scripts/smart-search.mjs
+++ b/scripts/smart-search.mjs
@@ -48,14 +48,16 @@ async function main() {
*/
async function collectPages() {
const pages = [];
- const entries = await getAllDocMeta();
+ const docsEntries = await getAllDocMeta("docs");
+ const toolkitEntries = await getAllDocMeta("toolkit");
+ const entries = [...docsEntries, ...toolkitEntries];
for (const entry of entries) {
const entryContent = await getRawDocContent(entry.download_url);
const parsedContent = await getTextContentFromMd(entryContent);
- const cleanedPath = getDocUriFromPath(entry.path);
+ const cleanedPath = getDocUriFromPath(entry.path, "docs");
const id = generateDocIdFromUri(cleanedPath);
diff --git a/src/components/on-this-page-nav.jsx b/src/components/on-this-page-nav.jsx
index 56d9a006..bdfd91e9 100644
--- a/src/components/on-this-page-nav.jsx
+++ b/src/components/on-this-page-nav.jsx
@@ -30,7 +30,7 @@ export default function OnThisPageNav({ headings }) {
return (
<>
On This Page
- {headings.length > 0 && (
+ {headings && headings.length > 0 && (
{headings.map((heading) => (
-
+ -
+
+ sendMainNavItemSelectEvent({
+ item_id: "/toolkit/",
+ item_name: "Toolkit",
+ item_category: "mdx_doc",
+ })
+ }
+ >
+ Toolkit
+
+
-
}
*/
-export async function getSerializedContextFromMd(mdContent, pageUrl) {
+export async function getSerializedContextFromMd(
+ mdContent,
+ pageUrl,
+ type = "docs",
+) {
return serialize({
source: mdContent,
options: {
@@ -64,7 +68,7 @@ export async function getSerializedContextFromMd(mdContent, pageUrl) {
{
selectors: ["img[src]"],
inspectEach: ({ url, node }) => {
- node.properties.src = getRemoteImgUrl(url, pageUrl);
+ node.properties.src = getRemoteImgUrl(url, pageUrl, type);
},
},
],
diff --git a/src/lib/remote-mdx-files.mjs b/src/lib/remote-mdx-files.mjs
index 6e86a100..4d0e3626 100644
--- a/src/lib/remote-mdx-files.mjs
+++ b/src/lib/remote-mdx-files.mjs
@@ -1,4 +1,5 @@
import { hash } from "node:crypto";
+import fs from "node:fs/promises";
import path from "node:path";
import { env } from "node:process";
import { Octokit } from "@octokit/core";
@@ -7,6 +8,10 @@ import {
DOCS_REPO,
DOCS_BRANCH,
DOCS_FOLDER,
+ TOOLKIT_OWNER,
+ TOOLKIT_REPO,
+ TOOLKIT_BRANCH,
+ TOOLKIT_FOLDER,
} from "../constants/repo.mjs";
import { getSerializedContextFromMd } from "./remark-parsing.mjs";
@@ -21,12 +26,44 @@ const octokit = new Octokit({
const DOCS_EXT_REG = /(?.*)index.md(?:x?)$/i;
const IMG_PATH_REG = /^(?\.\/)?(?.+)$/i;
-const DOCS_PATH = `https://raw.githubusercontent.com/${DOCS_OWNER}/${DOCS_REPO}/refs/heads/${DOCS_BRANCH}/${DOCS_FOLDER}`;
+function getRepoConfig(type = "docs") {
+ switch (type) {
+ case "toolkit": {
+ return {
+ owner: TOOLKIT_OWNER,
+ repo: TOOLKIT_REPO,
+ branch: TOOLKIT_BRANCH,
+ folder: TOOLKIT_FOLDER,
+ };
+ }
+
+ default: {
+ return {
+ owner: DOCS_OWNER,
+ repo: DOCS_REPO,
+ branch: DOCS_BRANCH,
+ folder: DOCS_FOLDER,
+ };
+ }
+ }
+}
+
+function getDocsPath(type = "docs") {
+ if (env.LOCAL_FILE_PATH) {
+ const { folder: docsFolder } = getRepoConfig(type);
+ return path.join(env.LOCAL_FILE_PATH, docsFolder);
+ }
+
+ const { owner, repo, branch, folder } = getRepoConfig(type);
+ return `https://raw.githubusercontent.com/${owner}/${repo}/refs/heads/${branch}/${folder}`;
+}
-const DOCS_NAV_CONFIG_URL = `${DOCS_PATH}/nav.json`;
+function getDocsNavConfigUrl(type = "docs") {
+ return `${getDocsPath(type)}/nav.json`;
+}
-function docUrlFromSlug(slug = []) {
- return path.join(DOCS_PATH, ...slug, "index.md");
+function docUrlFromSlug(slug = [], type = "docs") {
+ return path.join(getDocsPath(type), ...slug, "index.md");
}
/**
@@ -34,14 +71,15 @@ function docUrlFromSlug(slug = []) {
*
* @param {string} imgPath
* @param {string[]} pageUrl
+ * @param {string} type
* @returns
*/
-function imgUrlFromPath(imgPath, pageUrl) {
+function imgUrlFromPath(imgPath, pageUrl, type = "docs") {
if (!Array.isArray(pageUrl)) {
throw new TypeError("pageUrl should be an array");
}
- return `${DOCS_PATH}/${pageUrl.join("/")}/${imgPath}`;
+ return `${getDocsPath(type)}/${pageUrl.join("/")}/${imgPath}`;
}
/**
@@ -49,24 +87,76 @@ function imgUrlFromPath(imgPath, pageUrl) {
*
* @param {string} localPath
* @param {string[]} pageUrl
+ * @param {string} type
* @returns {string}
*/
-export function getRemoteImgUrl(localPath, pageUrl) {
- return imgUrlFromPath(localPath.match(IMG_PATH_REG).groups.slug, pageUrl);
+export function getRemoteImgUrl(localPath, pageUrl, type = "docs") {
+ return imgUrlFromPath(
+ localPath.match(IMG_PATH_REG).groups.slug,
+ pageUrl,
+ type,
+ );
+}
+
+/**
+ * Helper function to get local file metadata
+ *
+ * @param {string} type
+ * @param {string} pathToFolder
+ */
+async function getLocalDocMeta(type, pathToFolder) {
+ const { folder: docsFolder } = getRepoConfig(type);
+ const basePath = pathToFolder
+ ? path.join(env.LOCAL_FILE_PATH, pathToFolder)
+ : path.join(env.LOCAL_FILE_PATH, docsFolder);
+
+ const localItems = [];
+ const localSubItems = [];
+
+ const entries = await fs.readdir(basePath, { withFileTypes: true });
+
+ for (const entry of entries) {
+ if (entry.isFile() && entry.name === "index.md") {
+ const relativePath = pathToFolder
+ ? path.join(pathToFolder, entry.name)
+ : path.join(docsFolder, entry.name);
+ localItems.push({
+ type: "file",
+ name: entry.name,
+ path: relativePath,
+ });
+ } else if (entry.isDirectory() && entry.name !== "images") {
+ const subPath = pathToFolder
+ ? path.join(pathToFolder, entry.name)
+ : path.join(docsFolder, entry.name);
+ localSubItems.push(getAllDocMeta(type, subPath));
+ }
+ }
+
+ const localSubFolderItems = await Promise.all(localSubItems);
+
+ return [...localItems, ...localSubFolderItems.flat()];
}
/**
* Retrieves the metadata for all documents in the docs folder.
*
+ * @param {string} type
+ * @param {string} pathToFolder
*/
-export async function getAllDocMeta(pathToFolder = DOCS_FOLDER) {
+export async function getAllDocMeta(type, pathToFolder) {
+ if (env.LOCAL_FILE_PATH) {
+ return getLocalDocMeta(type, pathToFolder);
+ }
+
+ const { owner, repo, branch, folder } = getRepoConfig(type);
const { status, data } = await octokit.request(
"GET /repos/{owner}/{repo}/contents/{path}",
{
- owner: DOCS_OWNER,
- repo: DOCS_REPO,
- path: pathToFolder,
- ref: DOCS_BRANCH, // This makes it so only released features show up in the docs.
+ owner,
+ repo,
+ path: pathToFolder ?? folder,
+ ref: branch, // This makes it so only released features show up in the docs.
},
);
@@ -82,7 +172,7 @@ export async function getAllDocMeta(pathToFolder = DOCS_FOLDER) {
if (item.type === "file" && item.name === "index.md") {
items.push(item);
} else if (item.type === "dir" && item.name !== "images") {
- subItems.push(getAllDocMeta(item.path));
+ subItems.push(getAllDocMeta(type, item.path));
}
}
@@ -94,9 +184,16 @@ export async function getAllDocMeta(pathToFolder = DOCS_FOLDER) {
/**
* Retrieves the nav.json file from the docs folder.
*
+ * @param {string} type
*/
-export async function getDocsNav() {
- const resp = await fetch(DOCS_NAV_CONFIG_URL);
+export async function getDocsNav(type = "docs") {
+ if (env.LOCAL_FILE_PATH) {
+ const navPath = getDocsNavConfigUrl(type);
+ const content = await fs.readFile(navPath, "utf8");
+ return JSON.parse(content);
+ }
+
+ const resp = await fetch(getDocsNavConfigUrl(type));
if (!resp.ok) {
throw new Error(resp.statusText);
@@ -105,8 +202,8 @@ export async function getDocsNav() {
return resp.json();
}
-export async function getAllDocUri() {
- const data = await getAllDocMeta();
+export async function getAllDocUri(type = "docs") {
+ const data = await getAllDocMeta(type);
if (!Array.isArray(data)) {
console.error(data);
@@ -116,15 +213,17 @@ export async function getAllDocUri() {
const accumulator = [];
for (const file of data) {
if (DOCS_EXT_REG.test(file.path)) {
- accumulator.push(getDocUriFromPath(file.path));
+ accumulator.push(getDocUriFromPath(file.path, type));
}
}
return accumulator;
}
-export function getDocUriFromPath(ghPath) {
- return path.join("/", ghPath.match(DOCS_EXT_REG).groups.slug);
+export function getDocUriFromPath(ghPath, type = "docs") {
+ const { folder } = getRepoConfig(type);
+ const relativePath = path.relative(folder, ghPath);
+ return path.join("/", relativePath.match(DOCS_EXT_REG).groups.slug);
}
/**
@@ -134,6 +233,19 @@ export function getDocUriFromPath(ghPath) {
* @returns {Promise}
*/
export async function getRawDocContent(url) {
+ if (env.LOCAL_FILE_PATH) {
+ try {
+ return await fs.readFile(url, "utf8");
+ } catch (error) {
+ if (error.code === "ENOENT") {
+ // eslint-disable-next-line no-throw-literal
+ throw { notFound: true };
+ }
+
+ throw error;
+ }
+ }
+
const resp = await fetch(url);
if (!resp.ok) {
@@ -152,12 +264,13 @@ export async function getRawDocContent(url) {
* Retrieves the parsed content of a document from its slug.
*
* @param {string} slug
+ * @param {string} type
* @returns {ReturnType}
*/
-export async function getParsedDoc(slug) {
- const content = await getRawDocContent(docUrlFromSlug(slug));
+export async function getParsedDoc(slug, type = "docs") {
+ const content = await getRawDocContent(docUrlFromSlug(slug, type));
- return getSerializedContextFromMd(content, slug);
+ return getSerializedContextFromMd(content, slug, type);
}
/**
diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx
index 30c53fd7..5c65bdf0 100644
--- a/src/pages/_app.jsx
+++ b/src/pages/_app.jsx
@@ -18,6 +18,7 @@ const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_TRACKING_ID;
export default function MyApp({ Component, pageProps }) {
const router = useRouter();
const isDocsRoute = router.pathname.startsWith("/docs");
+ const isToolkitRoute = router.pathname.startsWith("/toolkit");
return (
@@ -27,7 +28,7 @@ export default function MyApp({ Component, pageProps }) {
{/* eslint-disable-next-line unicorn/no-null */}
- {isDocsRoute ? (
+ {isDocsRoute || isToolkitRoute ? (
diff --git a/src/pages/docs/[[...slug]].jsx b/src/pages/docs/[[...slug]].jsx
index e84eb971..b3a8cd58 100644
--- a/src/pages/docs/[[...slug]].jsx
+++ b/src/pages/docs/[[...slug]].jsx
@@ -13,8 +13,8 @@ export default function Doc({ source }) {
export async function getStaticProps({ params }) {
try {
- const source = await getParsedDoc(params.slug);
- const docsNavData = await getDocsNav();
+ const source = await getParsedDoc(params.slug, "docs");
+ const docsNavData = await getDocsNav("docs");
return {
props: {
diff --git a/src/pages/toolkit/[[...slug]].jsx b/src/pages/toolkit/[[...slug]].jsx
new file mode 100644
index 00000000..0af3d530
--- /dev/null
+++ b/src/pages/toolkit/[[...slug]].jsx
@@ -0,0 +1,47 @@
+import path from "node:path";
+import { MDXClient } from "next-mdx-remote-client";
+import { getMDXComponents } from "@/components/mdx-components";
+import {
+ getParsedDoc,
+ getDocsNav,
+ generateDocIdFromUri,
+} from "@/lib/remote-mdx-files.mjs";
+
+export default function Doc({ source }) {
+ return ;
+}
+
+export async function getStaticProps({ params }) {
+ try {
+ const docsNavData = await getDocsNav("toolkit");
+ const source = await getParsedDoc(params.slug, "toolkit");
+
+ return {
+ props: {
+ id: generateDocIdFromUri(
+ params.slug?.length > 1
+ ? path.join("/toolkit", ...params.slug, "/")
+ : "/toolkit/",
+ ),
+ source,
+ docsNavData,
+ },
+ revalidate: 600,
+ };
+ } catch (error) {
+ if (error.notFound) {
+ console.error(params, error);
+ return error;
+ }
+
+ throw error;
+ }
+}
+
+export async function getStaticPaths() {
+ const toolkit_menu_paths = ["/toolkit/"];
+ return {
+ paths: toolkit_menu_paths,
+ fallback: "blocking",
+ };
+}