Compare commits

...

4 Commits

Author SHA1 Message Date
pax-k
7f3990de3f feat(docs): process mdx files in docs layout - wip 2024-11-11 17:39:21 +02:00
pax
7118fa0413 feat(docs): extract table of contents - wip 2024-11-07 00:13:35 +00:00
Trisha Lim
60928b3a44 Remove custom layouts for docs with TOC 2024-11-05 11:19:10 +00:00
Trisha Lim
c3335f8def Docs: autogenerate heading links and table of contents 2024-11-05 11:14:46 +00:00
32 changed files with 1277 additions and 519 deletions

View File

@@ -1,13 +0,0 @@
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
export default function DocsLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<Prose className="container w-full max-w-full col-span-12 md:col-span-8 lg:col-span-9">
{children}
</Prose>
);
}

View File

@@ -0,0 +1,52 @@
// app/docs/[...slug]/layout.tsx
import React from "react";
import { DocNav } from "@/components/docs/nav";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import path from "path";
import { extractMDXSourceAndTOC, TOCItem } from "@/lib/extractMDXSourceAndTOC";
import { MDXRemote } from "next-mdx-remote/rsc";
import { CodeGroup } from "@/components/forMdx"; // Import your custom components
import { TOC } from "@/components/TOC";
import { compile, evaluate, run } from "@mdx-js/mdx";
// import TOC from "@/components/TOC";
// Define the LayoutProps interface
interface LayoutProps {
children: React.ReactNode;
params: { slug: string[] };
}
import * as runtime from "react/jsx-runtime";
export default async function DocsLayout({ children, params }: LayoutProps) {
const { slug } = params;
const mdxPath = path.join(
process.cwd(),
"content",
"docs",
...slug,
"page.mdx",
);
try {
const { toc, source } = await extractMDXSourceAndTOC(mdxPath);
return (
<div className="container relative flex justify-between gap-5 py-8">
<DocNav className="w-1/4" />
<Prose className="w-1/2">
{children}
<MDXRemote source={source} components={{ CodeGroup }} />
</Prose>
<aside className="toc-sidebar w-1/4">
<TOC toc={toc} />
</aside>
</div>
);
} catch (error) {
console.error(`Error processing MDX file at ${mdxPath}:`, error);
return <div>Error loading documentation.</div>;
}
}

View File

@@ -0,0 +1,50 @@
// app/docs/[...slug]/page.tsx
import React from "react";
// app/docs/[...slug]/page.tsx
import fs from "fs";
import path from "path";
export async function generateStaticParams() {
const docsDirectory = path.join(process.cwd(), "app", "docs");
const slugs = getAllMDXSlugs(docsDirectory);
return slugs.map((slug) => ({
slug: slug.split("/").filter(Boolean), // Convert slug string to array
}));
}
// Helper function to recursively get all slugs
function getAllMDXSlugs(dir: string, relativePath = ""): string[] {
const entries = fs.readdirSync(dir, { withFileTypes: true });
let slugs: string[] = [];
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
const entryRelativePath = path.join(relativePath, entry.name);
if (entry.isDirectory()) {
slugs = slugs.concat(getAllMDXSlugs(fullPath, entryRelativePath));
} else if (entry.isFile() && entry.name === "page.mdx") {
const slug = path.dirname(entryRelativePath); // Remove 'page.mdx' from the path
slugs.push(slug);
}
}
return slugs;
}
// Rest of your page code...
interface PageProps {
params: { slug: string[] };
}
export default function DocsPage(props: PageProps) {
console.log("DocsPage props", props);
// The content is already handled by the layout
// return <MDXRemote source={markdown} />;
return <></>;
}

View File

@@ -1,89 +0,0 @@
import Guide from "./guide.mdx";
import { TableOfContents } from "@/components/docs/TableOfContents";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
const navItems = [
{
name: "Project Setup",
href: "/docs/guide#project-setup",
},
{
name: "Intro to CoValues",
href: "/docs/guide#intro-to-covalues",
items: [
{
name: "Declaration",
href: "/docs/guide#declaring-covalues",
},
{
name: "Reading",
href: "/docs/guide#reading-covalues",
},
{
name: "Creation",
href: "/docs/guide#creating-covalues",
},
{
name: "Editing & Subscription",
href: "/docs/guide#editing-and-subscription",
},
{
name: "Persistence",
href: "/docs/guide#persistence",
},
{
name: "Remote Sync",
href: "/docs/guide#remote-sync",
},
{
name: "Simple Public Sharing",
href: "/docs/guide#simple-public-sharing",
},
],
},
{
name: "Refs & Auto-Subscribe",
href: "/docs/guide#refs-and-on-demand-subscribe",
items: [
{
name: "Precise Loading Depths",
href: "/docs/guide#loading-depth",
},
],
},
{
name: "Groups & Permissions",
href: "/docs/guide#groups-and-permissions",
items: [
{
name: "Groups/Accounts as Scopes",
href: "/docs/guide#groups-accounts-as-scopes",
},
{
name: "Creating Invites",
href: "/docs/guide#creating-invites",
},
{
name: "Consuming Invites",
href: "/docs/guide#consuming-invites",
},
],
},
];
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">
<Guide />
</Prose>
<TableOfContents className="w-48 shrink-0" items={navItems} />
</div>
);
}

View File

@@ -1,19 +1,3 @@
import { DocNav } from "@/components/docs/nav";
export const metadata = {
title: "Docs",
description: "Jazz Guide & Docs.",
};
export default function DocsLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="container relative grid grid-cols-12 gap-5 py-8">
<DocNav />
{children}
</div>
);
export default async function DocsLayout(props: any) {
return props.children;
}

View File

@@ -1,6 +1,8 @@
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
export default function Page() {
// console.log("plm", plm);
// console.log("toc", toc);
return (
<Prose>
<h1>Welcome to the Jazz documentation.</h1>

View File

@@ -1,35 +0,0 @@
import NextGuide from "./next.mdx";
import { TableOfContents } from "@/components/docs/TableOfContents";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
const navItems = [
{
name: "Client-side only",
href: "/docs/project-setup/react#next-csr",
},
{
name: "SSR use 🧪",
href: "/docs/project-setup/react#next-ssr",
},
{
name: "SSR + client-side 🧪",
href: "/docs/project-setup/react#next-ssr-plus-csr",
},
];
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">
<NextGuide />
</Prose>
<TableOfContents className="w-48 shrink-0" items={navItems} />
</div>
);
}

View File

@@ -1,30 +0,0 @@
import ReactNativeGuide from "./react-native.mdx";
import { TableOfContents } from "@/components/docs/TableOfContents";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
const navItems = [
{
name: "Setup",
href: "/docs/project-setup/react#react-native-setup",
},
{
name: "Using Jazz",
href: "/docs/project-setup/react#react-native-using-jazz",
},
];
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">
<ReactNativeGuide />
</Prose>
<TableOfContents className="w-48 shrink-0" items={navItems} />
</div>
);
}

View File

@@ -1,18 +0,0 @@
import ReactGuide from "./react.mdx";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">
<ReactGuide />
</Prose>
</div>
);
}

View File

@@ -1,39 +0,0 @@
import ServerGuide from "./server-side.mdx";
import { TableOfContents } from "@/components/docs/TableOfContents";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
const navItems = [
{
name: "Generating Credentials",
href: "/docs/project-setup/server-side#generating-credentials",
},
{
name: "Storing and Providing Credentials",
href: "/docs/project-setup/server-side#storing-credentials",
},
{
name: "Starting a Server Worker",
href: "/docs/project-setup/server-side#starting",
},
{
name: "Using CoValues instead of Requests",
href: "/docs/project-setup/server-side#covalues-instead-of-requests",
},
];
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">
<ServerGuide />
</Prose>
<TableOfContents className="w-48 shrink-0" items={navItems} />
</div>
);
}

View File

@@ -1,75 +0,0 @@
import CoValuesGuide from "./covalues.mdx";
import { TableOfContents } from "@/components/docs/TableOfContents";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
const navItems = [
{
name: "CoValues",
href: "/docs/schemas/covalues",
},
{
name: "Schemas as Your App's First Step",
href: "/docs/schemas/covalues#schemas-as-first-step",
},
{
name: "CoValue field types",
href: "/docs/schemas/covalues#field-types",
items: [
{
name: "Primitive Fields",
href: "/docs/schemas/covalues#primitive-fields",
},
{
name: "Refs",
href: "/docs/schemas/covalues#refs",
},
{
name: "Computed Fields, Methods & Constructors ",
href: "/docs/schemas/covalues#custom-fields",
},
],
},
{
name: "CoMaps",
href: "/docs/schemas/covalues#comaps",
items: [
{
name: "Struct-like CoMaps",
href: "/docs/schemas/covalues#comaps-struct-like",
},
{
name: "Dict/Record-like CoMaps",
href: "/docs/schemas/covalues#comaps-dict-like",
},
],
},
{
name: "CoLists",
href: "/docs/schemas/covalues#colists",
},
{
name: "CoStreams",
href: "/docs/schemas/covalues#costreams",
},
{
name: "BinaryCoStreams",
href: "/docs/schemas/covalues#binarycostreams",
},
];
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">
<CoValuesGuide />
</Prose>
<TableOfContents className="w-48 shrink-0" items={navItems} />
</div>
);
}

View File

@@ -1,47 +0,0 @@
import SyncAndStorage from "./sync-and-storage.mdx";
import { TableOfContents } from "@/components/docs/TableOfContents";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { clsx } from "clsx";
const navItems = [
{
name: "Using Jazz Cloud",
href: "/docs/sync-and-storage#using-jazz-cloud",
items: [
{
name: "Free Public Alpha",
href: "/docs/sync-and-storage#free-public-alpha",
},
],
},
{
name: "Running your own sync server",
href: "/docs/sync-and-storage#running-your-own",
items: [
{
name: "Command line options",
href: "/docs/sync-and-storage#command-line-options",
},
{
name: "Source code",
href: "/docs/sync-and-storage#source-code",
},
],
},
];
export default function Page() {
return (
<div
className={clsx(
"col-span-12 md:col-span-8 lg:col-span-9",
"flex justify-center lg:gap-5",
)}
>
<Prose className="overflow-x-hidden lg:flex-1">
<SyncAndStorage />
</Prose>
<TableOfContents className="w-48 shrink-0" items={navItems} />
</div>
);
}

View File

@@ -0,0 +1,16 @@
import { TOCItem } from "../lib/extractMDXSourceAndTOC";
export const TOC = ({ toc }: { toc: TOCItem[] }) => (
<nav>
<ul>
{toc.map((heading, index) => (
<li
key={index}
style={{ marginLeft: `${heading.depth - 1}em` }}
>
{heading.text}
</li>
))}
</ul>
</nav>
);

View File

@@ -1,45 +0,0 @@
import Link from "next/link";
import { clsx } from "clsx";
interface NavItem {
name: string;
href: string;
items?: NavItem[];
}
export function TableOfContents({
className,
items,
}: {
items: NavItem[];
className?: string;
}) {
if (!items.length) return null;
return (
<div
className={clsx(
"pl-3 sticky align-start top-[4.75rem] h-[calc(100vh-8rem)] overflow-y-auto overflow-x-hidden hidden md:block",
className,
)}
>
<p className="mb-3">On this page:</p>
<ul className="space-y-2 text-sm list-disc pl-4">
{items.map(({ name, href, items }) => (
<li key={name} className="space-y-2">
<Link href={href}>{name}</Link>
{items && items?.length > 0 && (
<ul className="list-disc pl-4 space-y-2">
{items.map(({ name, href }) => (
<li key={href}>
<Link href={href}>{name}</Link>
</li>
))}
</ul>
)}
</li>
))}
</ul>
</div>
);
}

View File

@@ -1,4 +1,3 @@
import { CodeGroup } from "@/components/forMdx";
# <span id="react-native">React Native</span>

View File

@@ -0,0 +1,146 @@
import fs from "fs/promises";
import matter from "gray-matter";
import rehypeShiki from "@shikijs/rehype";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
import rehypeRaw from "rehype-raw";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
import * as shiki from "shiki";
import {
remarkExtractTOC,
remarkHighlightPlugin,
remarkHtmlToJsx,
} from "./mdx-plugins.mjs";
export interface TOCItem {
depth: number;
text: string;
id: string;
}
export interface MDXContents {
source: string;
toc: TOCItem[];
}
import rehypeHighlight from "rehype-highlight";
/**
* Extracts metadata and TOC from an MDX file.
*
* @param mdxPath - The absolute path to the MDX file.
* @returns An object containing title, description, and toc.
*/
export async function extractMDXSourceAndTOC(
mdxPath: string,
): Promise<MDXContents> {
const source = await fs.readFile(mdxPath, "utf8");
// Extract frontmatter and content using gray-matter
// const { data, content } = matter(source);
// Initialize the processor with remark plugins and a compiler
const processor = unified()
.use(remarkParse)
.use(remarkExtractTOC)
// .use(remarkRehype, { allowDangerousHtml: true })
// .use(rehypeHighlight)
// .use(rehypeShiki, {
// theme: "light-plus",
// })
.use(remarkHighlightPlugin)
// .use(remarkHtmlToJsx)
// .use(rehypeRaw)
.use(remarkStringify); // Adds compiler to satisfy `process` requirements
// .use(rehypeStringify, { allowDangerousHtml: true });
// Process the content and extract TOC
const file = await processor.process(source);
// Extract TOC from the file's data and ensure type safety
const toc: TOCItem[] = (file.data.toc as TOCItem[]) || [];
// console.log("source", source);
return {
toc,
source,
};
}
import { compile } from "@mdx-js/mdx";
import { serialize } from "next-mdx-remote/serialize";
// import rehypeRaw from "rehype-raw"; // Import rehype-raw
// export async function extractMDXSourceAndTOC(mdxPath: string) {
// const source = await fs.readFile(mdxPath, "utf8");
// // Extract frontmatter and content using gray-matter
// const { data, content } = matter(source);
// // Compile MDX with @mdx-js/mdx
// const compiledMDX = await compile(content, {
// remarkPlugins: [
// remarkParse,
// remarkHighlightPlugin,
// remarkExtractTOC,
// remarkStringify,
// remarkHtmlToJsx,
// ],
// rehypePlugins: [], // Add any rehype plugins if necessary
// jsx: true, // Enables JSX syntax in the MDX
// });
// // Serialize the MDX to a format compatible with MDXRemote
// const mdxSource = await serialize(String(compiledMDX));
// // You might want to extract the TOC from frontmatter if added there
// const toc = data.toc || []; // Assume `remarkExtractTOC` adds `toc` to frontmatter
// return {
// toc,
// source: mdxSource,
// };
// }
////
// export async function extractMDXSourceAndTOC(mdxPath) {
// // Read the MDX file
// const source = await fs.readFile(mdxPath, "utf8");
// // Extract frontmatter and content using gray-matter
// const { data, content } = matter(source);
// // Serialize the MDX content to a format compatible with MDXRemote
// const mdxSource = await serialize(content, {
// mdxOptions: {
// // jsx: true,
// // format: "mdx",
// remarkPlugins: [
// // remarkParse,
// // remarkHighlightPlugin,
// // remarkExtractTOC,
// // remarkStringify,
// ], // Add your remark plugins here
// rehypePlugins: [], // Add your rehype plugins here
// // You can add more options as needed
// },
// // Optionally, pass the frontmatter data to MDX
// // scope: data,
// });
// console.log("mdxSource", mdxSource.compiledSource);
// // Extract TOC from frontmatter or from the plugin's result
// const toc = data.toc || []; // Adjust based on how `remarkExtractToc` provides TOC
// return {
// toc,
// source: mdxSource,
// };
// }

View File

@@ -0,0 +1,143 @@
import { getHighlighter } from "shiki";
import { visit, SKIP } from "unist-util-visit";
export function remarkHighlightPlugin() {
return async function transformer(tree) {
const highlighter = await getHighlighter({
langs: ["typescript", "bash", "tsx", "json"],
theme: "css-variables", // use the theme
});
visit(tree, "code", visitor);
// console.log("highlighter", highlighter);
function visitor(node) {
// console.log("nodex", node);
const lines = highlighter.codeToThemedTokens(
node.value,
node.lang, // "json",
"css-variables",
);
console.log("linesx", lines);
let lineNo = -1;
node.type = "html";
node.value = `<code class="not-prose py-2 flex flex-col leading-relaxed">${lines
.map((line) => {
let lineClassName = "";
const isSubduedLine = line.some((token) =>
token.content.includes("// old"),
);
const isBinnedLine = line.some((token) =>
token.content.includes("// *bin*"),
);
const isHighlighted = line.some((token) =>
token.content.includes("// *highlight*"),
);
if (!isBinnedLine) {
lineNo++;
}
if (isBinnedLine) {
lineClassName = "bg-red-100 dark:bg-red-800";
} else if (isHighlighted) {
lineClassName =
"my-0.5 bg-blue-50 text-blue dark:bg-stone-900 dark:text-blue-300";
}
return (
`<span class="block px-3 min-h-[1em] ${lineClassName}" style="${isBinnedLine ? "user-select: none" : ""}">` +
line
.map((token) => {
let color = isHighlighted
? "currentColor"
: token.color;
return `<span style="color: ${color};${isSubduedLine ? "opacity: 0.4;" : ""}">${escape(token.content.replace("// old", "").replace("// *bin*", "").replace("// *highlight*", ""))}</span>`;
})
.join("") +
"</span>"
);
})
.join("\n")}</code>`;
console.log("node.value", node.value);
node.children = [];
return SKIP;
}
};
}
export function remarkHtmlToJsx() {
async function transform(...args) {
// Async import since these packages are all in ESM
const { visit, SKIP } = await import("unist-util-visit");
const { mdxFromMarkdown } = await import("mdast-util-mdx");
const { fromMarkdown } = await import("mdast-util-from-markdown");
const { mdxjs } = await import("micromark-extension-mdxjs");
// This is a horror show, but it's the only way I could get the raw HTML into MDX.
const [ast] = args;
visit(ast, "html", (node) => {
const escapedHtml = JSON.stringify(node.value);
console.log("escapedHtml", escapedHtml);
const jsx = `<div dangerouslySetInnerHTML={{__html: ${escapedHtml} }}/>`;
const rawHtmlNode = fromMarkdown(jsx, {
extensions: [mdxjs()],
mdastExtensions: [mdxFromMarkdown()],
}).children[0];
Object.assign(node, rawHtmlNode);
return SKIP;
});
}
return transform;
}
export function remarkExtractTOC() {
return (tree, file) => {
const toc = [];
const idCounts = {};
visit(tree, "heading", (node) => {
const text = node.children
.filter(
(child) =>
child.type === "text" || child.type === "inlineCode",
)
.map((child) => child.value)
.join("");
let id = text
.toLowerCase()
.replace(/[^\w]+/g, "-")
.replace(/^-+|-+$/g, "");
// Ensure unique ID
if (idCounts[id]) {
idCounts[id] += 1;
id = `${id}-${idCounts[id]}`;
} else {
idCounts[id] = 1;
}
toc.push({
depth: node.depth,
text,
id,
});
});
file.data.toc = toc;
};
}
function escape(s) {
return s.replace(/[^0-9A-Za-z ]/g, (c) => "&#" + c.charCodeAt(0) + ";");
}

View File

@@ -1,23 +1,22 @@
import createMDX from "@next/mdx";
import { visit, SKIP } from "unist-util-visit";
import { getHighlighter } from "shiki";
import withSlugs from "rehype-slug";
import { remarkHighlightPlugin, remarkHtmlToJsx } from "./lib/mdx-plugins.mjs";
/** @type {import('next').NextConfig} */
const nextConfig = {
// Configure `pageExtensions`` to include MDX files
pageExtensions: ["js", "jsx", "mdx", "ts", "tsx"],
transpilePackages: ["gcmp-design-system"],
// Optionally, add any other Next.js config below
experimental: {
serverActions: true,
},
};
const withMDX = createMDX({
// Add markdown plugins here, as desired
options: {
remarkPlugins: [highlightPlugin, remarkHtmlToJsx],
rehypePlugins: [],
remarkPlugins: [remarkHighlightPlugin, remarkHtmlToJsx],
rehypePlugins: [withSlugs],
},
});
@@ -26,97 +25,4 @@ const config = {
output: "standalone",
};
function highlightPlugin() {
return async function transformer(tree) {
const highlighter = await getHighlighter({
langs: ["typescript", "bash", "tsx", "json"],
theme: "css-variables", // use the theme
});
visit(tree, "code", visitor);
function visitor(node) {
const lines = highlighter.codeToThemedTokens(
node.value,
node.lang,
"css-variables",
);
let lineNo = -1;
node.type = "html";
node.value = `<code class="not-prose py-2 flex flex-col leading-relaxed">${lines
.map((line) => {
let lineClassName = "";
const isSubduedLine = line.some((token) =>
token.content.includes("// old"),
);
const isBinnedLine = line.some((token) =>
token.content.includes("// *bin*"),
);
const isHighlighted = line.some((token) =>
token.content.includes("// *highlight*"),
);
if (!isBinnedLine) {
lineNo++;
}
if (isBinnedLine) {
lineClassName = 'bg-red-100 dark:bg-red-800'
} else if (isHighlighted) {
lineClassName = 'my-0.5 bg-blue-50 text-blue dark:bg-stone-900 dark:text-blue-300'
}
return (
`<span class="block px-3 min-h-[1em] ${lineClassName}" style="${isBinnedLine ? "user-select: none" : ""}">` +
line
.map(
(token) => {
let color = isHighlighted ? 'currentColor' : token.color
return `<span style="color: ${color};${isSubduedLine ? "opacity: 0.4;" : ""}">${escape(token.content.replace("// old", "").replace("// *bin*", "").replace("// *highlight*", ""))}</span>`
}
)
.join("") +
"</span>"
);
})
.join("\n")}</code>`;
node.children = [];
return SKIP;
}
};
}
function escape(s) {
return s.replace(/[^0-9A-Za-z ]/g, (c) => "&#" + c.charCodeAt(0) + ";");
}
function remarkHtmlToJsx() {
async function transform(...args) {
// Async import since these packages are all in ESM
const { visit, SKIP } = await import("unist-util-visit");
const { mdxFromMarkdown } = await import("mdast-util-mdx");
const { fromMarkdown } = await import("mdast-util-from-markdown");
const { mdxjs } = await import("micromark-extension-mdxjs");
// This is a horror show, but it's the only way I could get the raw HTML into MDX.
const [ast] = args;
visit(ast, "html", (node) => {
const escapedHtml = JSON.stringify(node.value);
const jsx = `<div dangerouslySetInnerHTML={{__html: ${escapedHtml} }}/>`;
const rawHtmlNode = fromMarkdown(jsx, {
extensions: [mdxjs()],
mdastExtensions: [mdxFromMarkdown()],
}).children[0];
Object.assign(node, rawHtmlNode);
return SKIP;
});
}
return transform;
}
export default config;

View File

@@ -17,28 +17,41 @@
"packageManager": "pnpm@9.1.4",
"dependencies": {
"@icons-pack/react-simple-icons": "^9.1.0",
"@jsdevtools/rehype-toc": "^3.0.2",
"@mdx-js/loader": "^2.3.0",
"@mdx-js/mdx": "^3.1.0",
"@mdx-js/react": "^2.3.0",
"@next/mdx": "^13.5.4",
"@stefanprobst/rehype-extract-toc": "^2.2.0",
"@types/mdx": "^2.0.8",
"@vercel/analytics": "^1.3.1",
"@vercel/speed-insights": "^1.0.12",
"clsx": "^2.1.1",
"gcmp-design-system": "workspace:*",
"gray-matter": "^4.0.3",
"lucide-react": "^0.436.0",
"mdast-util-from-markdown": "^2.0.0",
"mdast-util-mdx": "^3.0.0",
"micromark-extension-mdxjs": "^3.0.0",
"next": "13.5.4",
"next-mdx-remote": "^5.0.0",
"react": "^18",
"react-dom": "^18",
"rehype-highlight": "^7.0.1",
"rehype-raw": "^7.0.0",
"rehype-slug": "^6.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.1",
"remark-stringify": "^11.0.0",
"shiki": "^0.14.6",
"shiki-twoslash": "^3.1.2",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"unified": "^11.0.5",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@shikijs/rehype": "^1.22.2",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
@@ -47,6 +60,7 @@
"eslint-config-next": "13.5.4",
"postcss": "^8",
"prettier": "^3.2.5",
"rehype-stringify": "^10.0.1",
"tailwindcss": "^3",
"typedoc": "^0.25.13",
"typescript": "^5.3.3"

View File

@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -19,9 +23,19 @@
}
],
"paths": {
"@/*": ["./*"]
"@/*": [
"./*"
]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"lib/remark-extract-toc.mjs"
],
"exclude": [
"node_modules"
]
}

813
homepage/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,26 +5,36 @@ import { createWorkerAccount } from "../createWorkerAccount.js";
describe("createWorkerAccount - integration tests", () => {
it("should create a worker account using the local sync server", async () => {
// Pass port: undefined to let the server choose a random port
const server = await startSyncServer({ port: undefined, inMemory: true, db: '' });
const server = await startSyncServer({
port: undefined,
inMemory: true,
db: "",
});
onTestFinished(() => {
server.close()
server.close();
});
const address = server.address();
if (typeof address !== 'object' || address === null) {
throw new Error('Server address is not an object');
if (typeof address !== "object" || address === null) {
throw new Error("Server address is not an object");
}
const { accountId, agentSecret } = await createWorkerAccount({ name: "test", peer: `ws://localhost:${address.port}` });
const { accountId, agentSecret } = await createWorkerAccount({
name: "test",
peer: `ws://localhost:${address.port}`,
});
expect(accountId).toBeDefined();
expect(agentSecret).toBeDefined();
});
it("should create a worker account using the Jazz cloud", async () => {
const { accountId, agentSecret } = await createWorkerAccount({ name: "test", peer: `wss://cloud.jazz.tools` });
const { accountId, agentSecret } = await createWorkerAccount({
name: "test",
peer: `wss://cloud.jazz.tools`,
});
expect(accountId).toBeDefined();
expect(agentSecret).toBeDefined();