diff --git a/.gitignore b/.gitignore index c263c54e0..2edfd7403 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,5 @@ packages/wallet/next-env.d.ts # Turbo tmp stuff .turbo + +generated-docs/ \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 008966414..57f9152e7 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/docs/replace-external.js b/docs/replace-external.js new file mode 100644 index 000000000..f96459cae --- /dev/null +++ b/docs/replace-external.js @@ -0,0 +1,67 @@ +/** + * @param {import('typedoc-plugin-markdown').MarkdownApplication} app + */ +export function load(app) { + /** + * The external symbol map we want to replace + */ + const symbolMap = { + viem: { + home: "https://viem.sh/docs/getting-started.html", + Address: "https://viem.sh/docs/glossary/types#address", + Hex: "https://viem.sh/docs/glossary/types#hex", + SiweMessage: "https://eips.ethereum.org/EIPS/eip-4361", + }, + "@tanstack/react-query": { + home: "https://tanstack.com/query/latest", + useQuery: + "https://tanstack.com/query/v4/docs/framework/react/reference/useQuery", + UseQueryResult: + "https://tanstack.com/query/v4/docs/framework/react/reference/useQuery", + QueryClientProvider: + "https://tanstack.com/query/v4/docs/framework/react/reference/QueryClientProvider", + useMutation: + "https://tanstack.com/query/v4/docs/framework/react/reference/useMutation", + UseMutationResult: + "https://tanstack.com/query/v4/docs/framework/react/reference/useMutation", + }, + }; + + /** + * Add some symbol converter (for viem / tanstack specially) + */ + app.converter.addUnknownSymbolResolver((ref) => { + if ( + ref.moduleSource !== "viem" && + ref.moduleSource !== "@tanstack/react-query" + ) { + return; + } + const knownSymbols = symbolMap[ref.moduleSource]; + + // If someone did {@link viem!} or {@link @tanstack/react-query!}, link them directly to the home page. + if (!ref.symbolReference) { + console.log("Returning home", ref); + return knownSymbols.home; + } + + // Otherwise, we need to navigate through the symbol reference to + // determine where they meant to link to. Since the symbols we know + // about are all a single "level" deep, this is pretty simple. + if (!ref.symbolReference.path) { + console.log("No path", ref); + // Someone included a meaning, but not a path. + // https://typedoc.org/guides/declaration-references/#meaning + return; + } + + if (ref.symbolReference.path.length === 1) { + const name = ref.symbolReference.path[0].path; + const output = knownSymbols[name]; + if (!output) { + console.log("No output", ref, name, knownSymbols[name]); + } + return output; + } + }); +} diff --git a/docs/vocs-frontmatter.js b/docs/vocs-frontmatter.js new file mode 100644 index 000000000..8781fdfca --- /dev/null +++ b/docs/vocs-frontmatter.js @@ -0,0 +1,40 @@ +// @ts-check +import { ReflectionKind } from "typedoc"; +import { MarkdownPageEvent } from "typedoc-plugin-markdown"; + +/** + * @param {import('typedoc-plugin-markdown').MarkdownApplication} app + */ +export function load(app) { + // Get the generation data that will be pushed in the header (year-month-day) + const date = new Date(); + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const day = date.getDate(); + const dateStr = `${year}-${month}-${day}`; + + // The modal kind we want + const targetModelKink = [ + ReflectionKind.Variable, + ReflectionKind.TypeAlias, + ReflectionKind.Function, + ]; + + app.renderer.on( + MarkdownPageEvent.BEGIN, + /** @param {import('typedoc-plugin-markdown').MarkdownPageEvent} page */ + (page) => { + // Set the frontmatter options depending on the model kind + if (targetModelKink.includes(page.model?.kind)) { + // Set custom front matter options here + page.frontmatter = { + // Add a few options to the frontmatter section + title: `${page.group} - ${page.model?.name}`, + date: dateStr, + // spread the existing frontmatter + ...page.frontmatter, + }; + } + } + ); +} diff --git a/docs/vocs-sidebar.js b/docs/vocs-sidebar.js new file mode 100644 index 000000000..e523f0c0d --- /dev/null +++ b/docs/vocs-sidebar.js @@ -0,0 +1,114 @@ +import { writeFileSync } from "node:fs"; +import path from "node:path"; +import { MarkdownPageEvent } from "typedoc-plugin-markdown"; + +/** + * @param {import('typedoc-plugin-markdown').MarkdownApplication} app + */ +export function load(app) { + /** + * Slugify each anchor reference in the final markdown generated + */ + app.renderer.on(MarkdownPageEvent.END, (page) => { + page.contents = page.contents?.replace( + /\[([^\]]+)\]\((?!https?:|\/|\.)([^)]*#?[^)]*)\)/g, + (_match, text, url) => { + const urlWithAnchor = url.split("#"); + if (urlWithAnchor.length > 1) { + const anchorPart = slugifyAnchor(urlWithAnchor[1]); + return `[${text}](${encodeURI(`${urlWithAnchor[0]}#${anchorPart}`)})`; + } + return `[${text}](${encodeURI(url)})`; + } + ); + }); + + /** + * Generate the sidebar ts file + */ + app.renderer.postRenderAsyncJobs.push(async (output) => { + if (!output.navigation) return; + + const outDir = app.options.getValue("out"); + const basePath = app.options.getValue("publicPath"); + const sidebarPath = path.resolve(outDir, "references-sidebar.json"); + + const sidebar = output.navigation.map((navigationItem) => + getNavigationItem(navigationItem, basePath) + ); + writeFileSync(sidebarPath, JSON.stringify(sidebar, null, 4)); + }); +} + +/** + * Helper to slugify the anchor + * - From https://github.com/typedoc2md/typedoc-plugin-markdown/blob/5bd12c8670969726095417413bac5ab69efd12b5/packages/typedoc-vitepress-theme/src/utils/utils.ts#L6 + * @param str + */ +export const slugifyAnchor = (str) => + str + .normalize("NFKD") + // Remove accents + // biome-ignore lint/suspicious/noMisleadingCharacterClass: False positive + .replace(/[\u0300-\u036F]/g, "") + // Remove control characters + // biome-ignore lint/suspicious/noControlCharactersInRegex: Exactly what we want to do, remove control char from the text + .replace(/[\u0000-\u001f]/g, "") + // Replace special characters + .replace(/[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'“”‘’<>,.?/]+/g, "-") + // Remove continuos separators + .replace(/-{2,}/g, "-") + // Remove prefixing and trailing separators + .replace(/^-+|-+$/g, "") + // ensure it doesn't start with a number (#121) + .replace(/^(\d)/, "_$1") + // lowercase + .toLowerCase(); + +/** + * @param {import('typedoc-plugin-markdown').NavigationItem} navigationItem + * @param {string} basePath + */ +function getNavigationItem(navigationItem, basePath) { + const hasChildren = navigationItem?.children?.length; + + const linkParts = []; + + if (navigationItem?.path) { + if (basePath.length) { + linkParts.push(basePath); + } + linkParts.push( + getParsedUrl(navigationItem.path) + .replace(/\\/g, "/") + .replace(".mdx", "") + ); + } + + // Get all the childrens of the navigation item + let items = navigationItem.children?.map((group) => + getNavigationItem(group, basePath) + ); + + // If it only has one items, and this items has sub items, directly use the sub items + if (items?.length === 1 && items[0].items) { + items = items[0].items; + } + + return { + text: navigationItem.title, + ...(linkParts.length && { + link: `${linkParts.join("/")}`, + }), + items, + // All navigation are collapsed by default + ...(hasChildren && { collapsed: true }), + }; +} + +function getParsedUrl(url) { + if (path.basename(url) === "index.md") { + return `${path.dirname(url)}/`; + } + return url; +} diff --git a/package.json b/package.json index dcdb3713d..9126304f2 100644 --- a/package.json +++ b/package.json @@ -31,14 +31,18 @@ "@changesets/changelog-git": "^0.2.0", "@changesets/changelog-github": "^0.5.0", "@changesets/cli": "^2.27.7", + "@pulumi/aws": "^6.60.0", + "@pulumi/pulumi": "^3.141.0", "@types/node": "^22.0.0", "knip": "^5.27.0", "npm-check-updates": "^16.14.20", "rimraf": "^6.0.1", "sst": "3.3.28", - "typescript": "^5.6.2", - "@pulumi/aws": "^6.60.0", - "@pulumi/pulumi": "^3.141.0" + "typedoc": "^0.27.4", + "typedoc-plugin-frontmatter": "^1.1.2", + "typedoc-plugin-inline-sources": "^1.2.0", + "typedoc-plugin-markdown": "^4.3.2", + "typescript": "^5.6.2" }, "workspaces": [ "packages/*", diff --git a/sdk/components/package.json b/sdk/components/package.json index dc99b5340..e18ba2feb 100644 --- a/sdk/components/package.json +++ b/sdk/components/package.json @@ -21,7 +21,7 @@ "keywords": [ "frak-labs", "nexus-wallet", - "erc-4337", + "erc-frak", "eip-4337", "smart-wallet", "components", diff --git a/sdk/components/src/ButtonShare/ButtonShare.tsx b/sdk/components/src/ButtonShare/ButtonShare.tsx index 771f5b9d1..477ec24a3 100644 --- a/sdk/components/src/ButtonShare/ButtonShare.tsx +++ b/sdk/components/src/ButtonShare/ButtonShare.tsx @@ -1,11 +1,23 @@ import { useCallback, useEffect, useState } from "preact/hooks"; import { getModalBuilderSteps, onClientReady } from "../utils"; +/** + * The props type for {@link ButtonShare}. + * @inline + */ type ButtonShareProps = { - text: string; + /** Text to display on the button */ + text?: string; + /** Classname to apply to the button */ classname?: string; }; +/** + * Open the share modal + * + * @description + * This function will open the share modal with the configuration provided in the `window.FrakSetup.modalShareConfig` object. + */ function modalShare() { const modalBuilderSteps = getModalBuilderSteps(); @@ -19,6 +31,36 @@ function modalShare() { .display(); } +/** + * Button to share the current page + * + * @param args + * @param args.text - Text to display on the button + * @param args.classname - Classname to apply to the button + * @returns The share button with `