diff --git a/docs/site/src/shared/components/Cards/index.tsx b/docs/site/src/shared/components/Cards/index.tsx index 4223710f14..9078072f0a 100644 --- a/docs/site/src/shared/components/Cards/index.tsx +++ b/docs/site/src/shared/components/Cards/index.tsx @@ -1,9 +1,10 @@ // Copyright (c) Walrus Foundation // SPDX-License-Identifier: Apache-2.0 + import React, { useEffect, useState } from "react"; import { useHistory } from "@docusaurus/router"; import { usePluginData } from "@docusaurus/useGlobalData"; -import styles from "../../../css/cards.module.css"; +import styles from "@site/src/css/cards.module.css"; interface CardProps { title: string; @@ -14,27 +15,25 @@ interface CardProps { export function Card({ title, href, className, children }: CardProps) { const history = useHistory(); - const [url, setUrl] = useState(); + const [url, setUrl] = useState(); useEffect(() => { if (url) { window.open(url, "_blank"); } - return; }, [url]); - const { descriptions } = usePluginData("sui-description-plugin"); + const data = usePluginData("hashi-description-plugin") as + | { descriptions: { id: string; description: string }[] } + | undefined; let h = href; if (!h.match(/^\//)) { h = `/${h}`; } - const d = descriptions.find((desc) => desc["id"] === h); - let description = ""; - if (typeof d !== "undefined") { - description = d.description; - } + const d = data?.descriptions?.find((desc) => desc.id === h); + const description = d?.description ?? ""; - const handleClick = (loc) => { + const handleClick = (loc: string) => { if (loc.match(/^https?/)) { setUrl(loc); } else { @@ -44,13 +43,12 @@ export function Card({ title, href, className, children }: CardProps) { return (
handleClick(href)} >

{title}

-
{children ? children : description}
@@ -75,9 +73,8 @@ export function Cards({ children, type, ...props }: CardsProps) { "pb-8", ].join(" "); - const typeClass = type === "steps" - ? styles["step-card-container"] - : styles["card-container"]; + const typeClass = + type === "steps" ? styles["step-card-container"] : styles["card-container"]; return (
@@ -85,3 +82,5 @@ export function Cards({ children, type, ...props }: CardsProps) {
); } + +export default Cards; diff --git a/docs/site/src/shared/components/RelatedLink/index.tsx b/docs/site/src/shared/components/RelatedLink/index.tsx index 726fcd4e60..67a9ca6026 100644 --- a/docs/site/src/shared/components/RelatedLink/index.tsx +++ b/docs/site/src/shared/components/RelatedLink/index.tsx @@ -1,7 +1,6 @@ // Copyright (c) Walrus Foundation // SPDX-License-Identifier: Apache-2.0 - // src/components/RelatedLink.tsx import React from "react"; import Link from "@docusaurus/Link"; @@ -22,7 +21,7 @@ type Meta = { function useDescriptionsSafe(): Meta[] | null { try { - const raw = usePluginData("sui-description-plugin") as any; + const raw = usePluginData("hashi-description-plugin") as any; if (!raw) return null; if (Array.isArray(raw)) return raw as Meta[]; if (Array.isArray(raw?.items)) return raw.items as Meta[]; diff --git a/docs/site/src/shared/components/SidebarIframe/index.js b/docs/site/src/shared/components/SidebarIframe/index.js index 7750ef269a..e128a9a4ac 100644 --- a/docs/site/src/shared/components/SidebarIframe/index.js +++ b/docs/site/src/shared/components/SidebarIframe/index.js @@ -1,10 +1,9 @@ // Copyright (c) Walrus Foundation // SPDX-License-Identifier: Apache-2.0 - import React, { useState, useEffect } from 'react'; import { createPortal } from 'react-dom'; -import styles from "../../../css/sidebar.module.css"; +import styles from "@site/src/css/sidebar.module.css"; export default function SidebarIframe({ url, diff --git a/docs/site/src/shared/components/Snippet/index.tsx b/docs/site/src/shared/components/Snippet/index.tsx index c8b48d7ee3..1047b5fe45 100644 --- a/docs/site/src/shared/components/Snippet/index.tsx +++ b/docs/site/src/shared/components/Snippet/index.tsx @@ -1,36 +1,33 @@ // Copyright (c) Walrus Foundation // SPDX-License-Identifier: Apache-2.0 - import React from "react"; /** - * We glob-import every MDX file under @site/snippets/**. - * Docusaurus (Webpack 5) supports require.context at build time. + * Glob-imports every MDX file under docs/snippets at build time via + * Webpack's require.context. */ // eslint-disable-next-line @typescript-eslint/no-var-requires -const req = require.context("../../../../content/snippets", true, /\.mdx$/); +const req = (require as any).context( + "../../../../docs/snippets", + true, + /\.mdx$/, +); type SnippetModule = { default: React.ComponentType }; - const SNIPPETS: Record> = {}; req.keys().forEach((k: string) => { - const mod = req(k); - const keyWithExt = k.replace(/^\.\//, ""); // e.g. "sub/foo.mdx" - const keyNoExt = keyWithExt.replace(/\.mdx$/, ""); // e.g. "sub/foo" + const mod = req(k) as SnippetModule; + const keyWithExt = k.replace(/^\.\//, ""); + const keyNoExt = keyWithExt.replace(/\.mdx$/, ""); SNIPPETS[keyWithExt] = mod.default; SNIPPETS[keyNoExt] = mod.default; }); type Props = { - /** Path under snippets/, e.g. "subfolder-of-snippet/file" or "subfolder-of-snippet/file.mdx" */ source: string; } & Record; -/** - * Renders the MDX snippet inline, inheriting parent MDX providers - * (so code blocks, custom components, etc. all work). - */ export default function Snippet({ source, ...rest }: Props) { const Comp = SNIPPETS[source] || @@ -44,7 +41,5 @@ export default function Snippet({ source, ...rest }: Props) {
); } - - // Render the snippet's MDX component inline return ; } diff --git a/docs/site/src/shared/components/UnsafeLink/index.tsx b/docs/site/src/shared/components/UnsafeLink/index.tsx index 2a23c4f351..e25387820a 100644 --- a/docs/site/src/shared/components/UnsafeLink/index.tsx +++ b/docs/site/src/shared/components/UnsafeLink/index.tsx @@ -1,7 +1,6 @@ // Copyright (c) Walrus Foundation // SPDX-License-Identifier: Apache-2.0 - import React from "react"; type Props = { @@ -11,11 +10,8 @@ type Props = { }; /** - * Renders either a Docusaurus for internal paths - * or an for external URLs. - * This bypasses the broken-link checker since it’s a component, - * not a raw markdown link. Should never have an external link - * using this, but jic. + * Renders either an internal anchor or an external . + * Bypasses the broken-link checker since it's a component, not raw markdown. */ export default function UnsafeLink({ href, children, className }: Props) { const isExternal = /^https?:\/\//.test(href); diff --git a/docs/site/src/shared/js/convert-release-notes.js b/docs/site/src/shared/js/convert-release-notes.js index 6ffe059b54..220f9b7e62 100644 --- a/docs/site/src/shared/js/convert-release-notes.js +++ b/docs/site/src/shared/js/convert-release-notes.js @@ -1,7 +1,6 @@ // Copyright (c) Walrus Foundation // SPDX-License-Identifier: Apache-2.0 - const fs = require('fs'); const path = require('path'); const https = require('https'); @@ -164,6 +163,14 @@ function sanitizeForMDX(content) { return content; } +function stripReviewRequests(content) { + // Remove PR title/author lines like "PR #27067: [framework] future proofing type_name in coin by @Damir (Damir Shamanaev)" + content = content.replace(/^PR #\d+:.*by @.+$/gim, ''); + // Clean up excess blank lines left behind + content = content.replace(/\n{3,}/g, '\n\n'); + return content.trim(); +} + function convertGitHubHeadingsToH3(content) { return content.replace(/^(#{1,6})\s+(.*)$/gm, (match, hashes, text) => { const trimmedText = text.trim(); @@ -357,7 +364,8 @@ async function consolidateReleaseNotes() { } // Process content - let processedGitHubContent = sanitizeForMDX(releaseToUse.content); + let processedGitHubContent = stripReviewRequests(releaseToUse.content); + processedGitHubContent = sanitizeForMDX(processedGitHubContent); processedGitHubContent = convertGitHubHeadingsToH3(processedGitHubContent); processedGitHubContent = processedGitHubContent.replace(/\n{3,}/g, '\n\n'); diff --git a/docs/site/src/shared/js/serve-with-rewrites.js b/docs/site/src/shared/js/serve-with-rewrites.js index 3186db22cf..39ba0dedd9 100644 --- a/docs/site/src/shared/js/serve-with-rewrites.js +++ b/docs/site/src/shared/js/serve-with-rewrites.js @@ -31,6 +31,15 @@ const MIME_TYPES = { '.pdf': 'application/pdf', }; +// Trailing-slash duplicates split analytics and dilute SEO across two URLs for +// the same page. Redirect the trailing-slash variants to their canonical +// (non-trailing-slash) paths, mirroring the production edge middleware. +const TRAILING_SLASH_REDIRECTS = { + '/Pricing/': '/Pricing', + '/UsingSeal/': '/UsingSeal', + '/GettingStarted/': '/GettingStarted', +}; + function getContentType(filePath) { const ext = path.extname(filePath).toLowerCase(); return MIME_TYPES[ext] || 'application/octet-stream'; @@ -81,6 +90,15 @@ const server = http.createServer((req, res) => { const parsedUrl = url.parse(req.url); let pathname = parsedUrl.pathname; + // Consolidate trailing-slash duplicates onto their canonical paths. + const canonical = TRAILING_SLASH_REDIRECTS[pathname]; + if (canonical) { + const location = parsedUrl.search ? canonical + parsedUrl.search : canonical; + res.writeHead(301, { Location: location }); + res.end(); + return; + } + // Content negotiation: serve markdown when Accept: text/markdown if (acceptsMarkdown(req)) { const mdFile = resolveMarkdownFile(pathname); diff --git a/docs/site/src/shared/plugins/inject-code/index.js b/docs/site/src/shared/plugins/inject-code/index.js index a52d7dfd74..2583b2dc1c 100644 --- a/docs/site/src/shared/plugins/inject-code/index.js +++ b/docs/site/src/shared/plugins/inject-code/index.js @@ -1,13 +1,22 @@ // Copyright (c) Walrus Foundation // SPDX-License-Identifier: Apache-2.0 +// +// Registers the stepLoader webpack loader on .md/.mdx files so step heading +// shorthands (`##step Foo` / `###substep Bar`) get rewritten to numbered +// headings before docusaurus-plugin-content-docs's MDX loader sees them. +// +// In the Sui upstream this plugin used to also register an injectLoader to +// power . That loader is no longer in the upstream +// tree; now uses the importContentMap generated by +// scripts/generate-import-context.js at prebuild time. const path = require("path"); -const injectCode = (context, opts) => { +const injectCode = (_context, _opts) => { return { - name: "sui-inject-code-plugin", + name: "hashi-inject-code-plugin", - configureWebpack(config, _isServer, _utils) { + configureWebpack(config) { const pluginContentDocsPath = path.join( "plugin-content-docs", "lib", @@ -16,35 +25,27 @@ const injectCode = (context, opts) => { ); let docsPluginInclude = []; if (config.module && config.module.rules) { - var foundContentDocsPlugin = false; + let foundContentDocsPlugin = false; config.module.rules.forEach((rule) => { - if (rule === "...") { - return; - } - + if (rule === "...") return; if (!foundContentDocsPlugin && rule.use && rule.include) { - const includesArray = rule.include; const useArray = rule.use; useArray.forEach((useItem) => { - if (typeof useItem == "object" && useItem.loader) { - if (useItem.loader.endsWith(pluginContentDocsPath)) { - foundContentDocsPlugin = true; - } + if ( + typeof useItem === "object" && + useItem.loader && + useItem.loader.endsWith(pluginContentDocsPath) + ) { + foundContentDocsPlugin = true; } }); if (foundContentDocsPlugin) { - docsPluginInclude = [...includesArray]; // copy the include paths docusaurus-plugin-content-docs + docsPluginInclude = [...rule.include]; } } }); } - const loaderOptions = { - replacements: opts.replacements, - embeds: opts.embeds, - sharedFolders: opts.sharedFolders, - }; - return { module: { rules: [ @@ -55,10 +56,6 @@ const injectCode = (context, opts) => { { loader: path.resolve(__dirname, "./stepLoader.js"), }, - { - loader: path.resolve(__dirname, "./injectLoader.js"), - options: loaderOptions, - }, ], }, ],