From 37e6774018619e18247ba43312d0a8d3aaa2789c Mon Sep 17 00:00:00 2001
From: AbstractionFactory
<179820029+abstractionfactory@users.noreply.github.com>
Date: Fri, 6 Sep 2024 10:21:55 +0200
Subject: [PATCH 01/27] Registry docs stub
Signed-off-by: AbstractionFactory <179820029+abstractionfactory@users.noreply.github.com>
---
frontend/docs/index.md | 7 +++++++
frontend/docs/module-authors/adding.md | 6 ++++++
frontend/docs/module-authors/creating.md | 6 ++++++
frontend/docs/module-authors/index.md | 5 +++++
frontend/docs/module-authors/publishing.md | 6 ++++++
frontend/docs/provider-authors/adding.md | 6 ++++++
frontend/docs/provider-authors/artifacts.md | 6 ++++++
frontend/docs/provider-authors/creating.md | 6 ++++++
frontend/docs/provider-authors/gpg.md | 6 ++++++
frontend/docs/provider-authors/index.md | 5 +++++
frontend/docs/users/index.md | 5 +++++
frontend/docs/users/modules.md | 5 +++++
frontend/docs/users/providers.md | 5 +++++
frontend/docs/users/security.md | 5 +++++
14 files changed, 79 insertions(+)
create mode 100644 frontend/docs/index.md
create mode 100644 frontend/docs/module-authors/adding.md
create mode 100644 frontend/docs/module-authors/creating.md
create mode 100644 frontend/docs/module-authors/index.md
create mode 100644 frontend/docs/module-authors/publishing.md
create mode 100644 frontend/docs/provider-authors/adding.md
create mode 100644 frontend/docs/provider-authors/artifacts.md
create mode 100644 frontend/docs/provider-authors/creating.md
create mode 100644 frontend/docs/provider-authors/gpg.md
create mode 100644 frontend/docs/provider-authors/index.md
create mode 100644 frontend/docs/users/index.md
create mode 100644 frontend/docs/users/modules.md
create mode 100644 frontend/docs/users/providers.md
create mode 100644 frontend/docs/users/security.md
diff --git a/frontend/docs/index.md b/frontend/docs/index.md
new file mode 100644
index 0000000..b907ee9
--- /dev/null
+++ b/frontend/docs/index.md
@@ -0,0 +1,7 @@
+---
+title: The OpenTofu Registry
+sidebar_title: Overview
+sidebar_position: 1
+---
+
+The OpenTofu Registry provides an API for OpenTofu to locate providers and modules. This documentation will guide you through the most important steps in using the registry or publishing your modules and providers. Please select the topic you are interested in from the left sidebar.
\ No newline at end of file
diff --git a/frontend/docs/module-authors/adding.md b/frontend/docs/module-authors/adding.md
new file mode 100644
index 0000000..5411033
--- /dev/null
+++ b/frontend/docs/module-authors/adding.md
@@ -0,0 +1,6 @@
+---
+title: Adding your module to the OpenTofu registry
+sidebar_title: Adding your module to the registry
+sidebar_position: 303
+---
+
diff --git a/frontend/docs/module-authors/creating.md b/frontend/docs/module-authors/creating.md
new file mode 100644
index 0000000..d12f7fa
--- /dev/null
+++ b/frontend/docs/module-authors/creating.md
@@ -0,0 +1,6 @@
+---
+title: Creating an OpenTofu module
+sidebar_title: Creating an OpenTofu module
+sidebar_position: 301
+---
+
diff --git a/frontend/docs/module-authors/index.md b/frontend/docs/module-authors/index.md
new file mode 100644
index 0000000..431ddcc
--- /dev/null
+++ b/frontend/docs/module-authors/index.md
@@ -0,0 +1,5 @@
+---
+title: The OpenTofu Registry for Module Authors
+sidebar_title: For module authors
+sidebar_position: 200
+---
diff --git a/frontend/docs/module-authors/publishing.md b/frontend/docs/module-authors/publishing.md
new file mode 100644
index 0000000..a28adc5
--- /dev/null
+++ b/frontend/docs/module-authors/publishing.md
@@ -0,0 +1,6 @@
+---
+title: Publishing an OpenTofu module
+sidebar_title: Publishing an OpenTofu module
+sidebar_position: 302
+---
+
diff --git a/frontend/docs/provider-authors/adding.md b/frontend/docs/provider-authors/adding.md
new file mode 100644
index 0000000..af167e2
--- /dev/null
+++ b/frontend/docs/provider-authors/adding.md
@@ -0,0 +1,6 @@
+---
+title: Adding your GPG signing key to the OpenTofu registry
+sidebar_title: Adding your GPG signing key to the registry
+sidebar_position: 304
+---
+
diff --git a/frontend/docs/provider-authors/artifacts.md b/frontend/docs/provider-authors/artifacts.md
new file mode 100644
index 0000000..36214bd
--- /dev/null
+++ b/frontend/docs/provider-authors/artifacts.md
@@ -0,0 +1,6 @@
+---
+title: Publishing a provider version
+sidebar_title: Publishing a provider version
+sidebar_position: 302
+---
+
diff --git a/frontend/docs/provider-authors/creating.md b/frontend/docs/provider-authors/creating.md
new file mode 100644
index 0000000..9adbed5
--- /dev/null
+++ b/frontend/docs/provider-authors/creating.md
@@ -0,0 +1,6 @@
+---
+title: Creating an OpenTofu provider
+sidebar_title: Creating an OpenTofu provider
+sidebar_position: 301
+---
+
diff --git a/frontend/docs/provider-authors/gpg.md b/frontend/docs/provider-authors/gpg.md
new file mode 100644
index 0000000..a658dd9
--- /dev/null
+++ b/frontend/docs/provider-authors/gpg.md
@@ -0,0 +1,6 @@
+---
+title: Adding your provider to the OpenTofu registry
+sidebar_title: Adding your provider to the registry
+sidebar_position: 303
+---
+
diff --git a/frontend/docs/provider-authors/index.md b/frontend/docs/provider-authors/index.md
new file mode 100644
index 0000000..fefc9b1
--- /dev/null
+++ b/frontend/docs/provider-authors/index.md
@@ -0,0 +1,5 @@
+---
+title: The OpenTofu Registry for Provider Authors
+sidebar_title: For provider authors
+sidebar_position: 300
+---
diff --git a/frontend/docs/users/index.md b/frontend/docs/users/index.md
new file mode 100644
index 0000000..8b8e480
--- /dev/null
+++ b/frontend/docs/users/index.md
@@ -0,0 +1,5 @@
+---
+title: The OpenTofu Registry for Users
+sidebar_title: For user
+sidebar_position: 100
+---
diff --git a/frontend/docs/users/modules.md b/frontend/docs/users/modules.md
new file mode 100644
index 0000000..6a70485
--- /dev/null
+++ b/frontend/docs/users/modules.md
@@ -0,0 +1,5 @@
+---
+title: Using a module
+sidebar_title: Using a module
+sidebar_position: 102
+---
\ No newline at end of file
diff --git a/frontend/docs/users/providers.md b/frontend/docs/users/providers.md
new file mode 100644
index 0000000..305d3e8
--- /dev/null
+++ b/frontend/docs/users/providers.md
@@ -0,0 +1,5 @@
+---
+title: Using a provider
+sidebar_title: Using a provider
+sidebar_position: 101
+---
\ No newline at end of file
diff --git a/frontend/docs/users/security.md b/frontend/docs/users/security.md
new file mode 100644
index 0000000..45ccc88
--- /dev/null
+++ b/frontend/docs/users/security.md
@@ -0,0 +1,5 @@
+---
+title: Security in the OpenTofu Registry
+sidebar_title: Security
+sidebar_position: 103
+---
\ No newline at end of file
From 2b3d2821c3efe9dce40a638b787635eb866a410c Mon Sep 17 00:00:00 2001
From: Damian Stasik <920747+damianstasik@users.noreply.github.com>
Date: Fri, 6 Sep 2024 16:58:59 +0200
Subject: [PATCH 02/27] Registry docs UI
Signed-off-by: Damian Stasik <920747+damianstasik@users.noreply.github.com>
---
frontend/docs/index.md | 4 +-
frontend/docs/sidebar.json | 22 ++++
frontend/package.json | 1 +
frontend/src/main.tsx | 2 +-
frontend/src/router.tsx | 8 ++
.../routes/Docs/components/SidebarMenu.tsx | 123 ++++++++++++++++++
frontend/src/routes/Docs/index.tsx | 23 ++++
frontend/src/routes/Docs/loader.ts | 67 ++++++++++
frontend/src/vite-env.d.ts | 4 +-
frontend/vite.config.ts | 17 ++-
10 files changed, 265 insertions(+), 6 deletions(-)
create mode 100644 frontend/docs/sidebar.json
create mode 100644 frontend/src/routes/Docs/components/SidebarMenu.tsx
create mode 100644 frontend/src/routes/Docs/index.tsx
create mode 100644 frontend/src/routes/Docs/loader.ts
diff --git a/frontend/docs/index.md b/frontend/docs/index.md
index b907ee9..dffd47a 100644
--- a/frontend/docs/index.md
+++ b/frontend/docs/index.md
@@ -4,4 +4,6 @@ sidebar_title: Overview
sidebar_position: 1
---
-The OpenTofu Registry provides an API for OpenTofu to locate providers and modules. This documentation will guide you through the most important steps in using the registry or publishing your modules and providers. Please select the topic you are interested in from the left sidebar.
\ No newline at end of file
+# The OpenTofu Registry
+
+The OpenTofu Registry provides an API for OpenTofu to locate providers and modules. This documentation will guide you through the most important steps in using the registry or publishing your modules and providers. Please select the topic you are interested in from the left sidebar.
diff --git a/frontend/docs/sidebar.json b/frontend/docs/sidebar.json
new file mode 100644
index 0000000..508f497
--- /dev/null
+++ b/frontend/docs/sidebar.json
@@ -0,0 +1,22 @@
+[
+ {
+ "title": "Overview",
+ "path": "index.md",
+ "slug": ""
+ },
+ {
+ "title": "Users",
+ "items": [
+ {
+ "title": "For user",
+ "path": "users/index.md",
+ "slug": "users"
+ },
+ {
+ "title": "Using a module",
+ "path": "users/modules.md",
+ "slug": "users/modules"
+ }
+ ]
+ }
+]
diff --git a/frontend/package.json b/frontend/package.json
index 03c0f8f..1720fc7 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -55,6 +55,7 @@
"typescript": "^5.6.2",
"typescript-eslint": "^8.5.0",
"unified": "^11.0.5",
+ "vfile-matter": "^5.0.0",
"vite": "^5.4.2",
"vite-tsconfig-paths": "^5.0.1"
},
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index 2a46674..e5ceecf 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -7,7 +7,7 @@ import { HelmetProvider } from "react-helmet-async";
import { queryClient } from "./query";
import { router } from "./router";
import { AnnouncementBar } from "./components/AnnouncementBar";
-import announcement from "../announcement.md";
+import { content as announcement } from "../announcement.md";
import "./index.css";
createRoot(document.getElementById("root")!).render(
diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx
index c9ba62d..ab1c391 100644
--- a/frontend/src/router.tsx
+++ b/frontend/src/router.tsx
@@ -47,6 +47,8 @@ import { ModuleSubmoduleRouteContext } from "./routes/ModuleSubmodule/types";
import { moduleSubmoduleReadmeLoader } from "./routes/ModuleSubmodule/Readme/loader";
import { moduleSubmoduleMiddleware } from "./routes/ModuleSubmodule/middleware";
import { ProviderError } from "./routes/Provider/components/Error";
+import { Docs } from "./routes/Docs";
+import { docsLoader } from "./routes/Docs/loader";
export const router = createBrowserRouter(
[
@@ -58,6 +60,12 @@ export const router = createBrowserRouter(
index: true,
element: ,
},
+ {
+ id: "docs",
+ path: "/docs/*",
+ element: ,
+ loader: docsLoader,
+ },
{
id: "providers",
path: "/providers",
diff --git a/frontend/src/routes/Docs/components/SidebarMenu.tsx b/frontend/src/routes/Docs/components/SidebarMenu.tsx
new file mode 100644
index 0000000..7694122
--- /dev/null
+++ b/frontend/src/routes/Docs/components/SidebarMenu.tsx
@@ -0,0 +1,123 @@
+import { Icon } from "@/components/Icon";
+import { TreeView, TreeViewItem } from "@/components/TreeView";
+import { chevron } from "@/icons/chevron";
+
+import clsx from "clsx";
+import { useState } from "react";
+import { NavLink, To } from "react-router-dom";
+import sidebar from "../../../../docs/sidebar.json";
+
+type TabLinkProps = {
+ to: To;
+ label: string;
+};
+
+function TabLink({ to, label }: TabLinkProps) {
+ return (
+
+ clsx(
+ "flex break-all px-4 py-2 text-left",
+ isActive &&
+ "bg-brand-500 text-brand-600 text-inherit dark:bg-brand-800",
+ !isActive && "text-inherit hover:bg-gray-100 dark:hover:bg-blue-900",
+ )
+ }
+ >
+ {label}
+
+ );
+}
+
+type SidebarItem =
+ | {
+ title: string;
+ items: SidebarItem[];
+ }
+ | {
+ title: string;
+ slug: string;
+ path: string;
+ items?: never;
+ };
+
+type DocsTreeViewItemProps = {
+ item: SidebarItem;
+ isOpenByDefault?: boolean;
+ nested?: boolean;
+};
+
+function DocsTreeViewItem({
+ item,
+ isOpenByDefault = false,
+ nested = false,
+}: DocsTreeViewItemProps) {
+ const [open, setOpen] = useState(isOpenByDefault);
+ let button;
+
+ if (item.items) {
+ button = (
+
+ );
+ } else {
+ button = (
+
+ );
+ }
+
+ return (
+
+ {button}
+ {open && item.items && (
+
+ {item.items.map((subitem) => (
+
+ ))}
+
+ )}
+
+ );
+}
+
+export function DocsSidebarMenu() {
+ return (
+
+ {sidebar.map((item) => (
+
+ ))}
+
+ );
+}
+
+export function ProviderDocsMenuSkeleton() {
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/routes/Docs/index.tsx b/frontend/src/routes/Docs/index.tsx
new file mode 100644
index 0000000..67cafaa
--- /dev/null
+++ b/frontend/src/routes/Docs/index.tsx
@@ -0,0 +1,23 @@
+import { SidebarLayout } from "@/components/SidebarLayout";
+import { useLoaderData } from "react-router-dom";
+import { Markdown } from "@/components/Markdown";
+import { SidebarPanel } from "@/components/SidebarPanel";
+import { DocsSidebarMenu } from "./components/SidebarMenu";
+
+export function Docs() {
+ const data = useLoaderData();
+
+ return (
+
+
+
+ }
+ >
+
+
+
+
+ );
+}
diff --git a/frontend/src/routes/Docs/loader.ts b/frontend/src/routes/Docs/loader.ts
new file mode 100644
index 0000000..4bfaf23
--- /dev/null
+++ b/frontend/src/routes/Docs/loader.ts
@@ -0,0 +1,67 @@
+import { defer, LoaderFunction } from "react-router-dom";
+import { NotFoundPageError } from "@/utils/errors";
+import sidebar from "../../../docs/sidebar.json";
+
+interface Document {
+ content: string;
+ frontmatter: {
+ title: string;
+ };
+}
+
+const docs = import.meta.glob("../../../docs/**/*.md", {
+ eager: true,
+});
+
+type SidebarItem =
+ | {
+ title: string;
+ items: SidebarItem[];
+ }
+ | {
+ title: string;
+ slug: string;
+ path: string;
+ items?: never;
+ };
+
+function buildSlugPathMap(sidebarItems: SidebarItem[]) {
+ const slugPathMap: Record = {};
+
+ const traverseItems = (items: SidebarItem[]) => {
+ for (const item of items) {
+ if (item.items) {
+ traverseItems(item.items);
+ } else {
+ slugPathMap[item.slug] = item.path;
+ }
+ }
+ };
+
+ traverseItems(sidebarItems);
+
+ return slugPathMap;
+}
+
+export const docsLoader: LoaderFunction = async ({ params }) => {
+ const { "*": slug = "" } = params;
+ const normalizedSlug = slug.replace(/[^a-zA-Z0-9/-]/g, "");
+
+ const slugPathMap = buildSlugPathMap(sidebar);
+ const sidebarItem = slugPathMap[normalizedSlug];
+
+ if (!sidebarItem) {
+ throw new NotFoundPageError();
+ }
+
+ const document = docs[`../../../docs/${sidebarItem}`];
+
+ if (!document) {
+ throw new NotFoundPageError();
+ }
+
+ return defer({
+ content: document.content,
+ frontmatter: document.frontmatter,
+ });
+};
diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts
index d27cf9e..aceb727 100644
--- a/frontend/src/vite-env.d.ts
+++ b/frontend/src/vite-env.d.ts
@@ -1,6 +1,6 @@
///
declare module "*.md" {
- const content: string;
- export default content;
+ export const content: string;
+ export const frontmatter: Record;
}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index a94cb82..2f8670b 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -1,10 +1,18 @@
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
-import { unified } from "unified";
+import { Plugin, unified } from "unified";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
+import remarkFrontmatter from "remark-frontmatter";
+import { matter } from "vfile-matter";
+
+const extractFrontmatter: Plugin = () => {
+ return (_, file) => {
+ matter(file);
+ };
+};
export default defineConfig({
plugins: [
@@ -16,12 +24,17 @@ export default defineConfig({
if (id.endsWith(".md")) {
const content = unified()
.use(remarkParse)
+ .use(remarkFrontmatter)
+ .use(extractFrontmatter)
.use(remarkRehype)
.use(rehypeStringify)
.processSync(src);
return {
- code: `export default ${JSON.stringify(content.value)}`,
+ code: `
+ export const content = ${JSON.stringify(content.value)};
+ export const frontmatter = ${JSON.stringify(content.data.matter)};
+ `,
map: null,
};
}
From 57c09c107b1333b2b6c329bcaf5dfbfbde0ce18e Mon Sep 17 00:00:00 2001
From: Damian Stasik <920747+damianstasik@users.noreply.github.com>
Date: Fri, 6 Sep 2024 18:40:52 +0200
Subject: [PATCH 03/27] Move more code to build time using macros
Signed-off-by: Damian Stasik <920747+damianstasik@users.noreply.github.com>
---
frontend/package.json | 1 +
.../src/components/AnnouncementBar/content.ts | 11 +++
.../src/components/AnnouncementBar/index.tsx | 6 +-
frontend/src/components/Header/index.tsx | 5 ++
frontend/src/main.tsx | 3 +-
frontend/src/routes/Docs/index.tsx | 7 +-
frontend/src/routes/Docs/loader.ts | 59 ++-------------
frontend/src/routes/Docs/types.ts | 7 ++
frontend/src/routes/Docs/utils.ts | 72 +++++++++++++++++++
frontend/src/vite-env.d.ts | 5 --
frontend/vite.config.ts | 40 +----------
11 files changed, 110 insertions(+), 106 deletions(-)
create mode 100644 frontend/src/components/AnnouncementBar/content.ts
create mode 100644 frontend/src/routes/Docs/types.ts
create mode 100644 frontend/src/routes/Docs/utils.ts
diff --git a/frontend/package.json b/frontend/package.json
index 1720fc7..8298e87 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -55,6 +55,7 @@
"typescript": "^5.6.2",
"typescript-eslint": "^8.5.0",
"unified": "^11.0.5",
+ "unplugin-macros": "^0.13.3",
"vfile-matter": "^5.0.0",
"vite": "^5.4.2",
"vite-tsconfig-paths": "^5.0.1"
diff --git a/frontend/src/components/AnnouncementBar/content.ts b/frontend/src/components/AnnouncementBar/content.ts
new file mode 100644
index 0000000..bb8139b
--- /dev/null
+++ b/frontend/src/components/AnnouncementBar/content.ts
@@ -0,0 +1,11 @@
+import { unified } from "unified";
+import remarkParse from "remark-parse";
+import remarkRehype from "remark-rehype";
+import rehypeStringify from "rehype-stringify";
+import announcement from "../../../announcement.md?raw";
+
+export const content = unified()
+ .use(remarkParse)
+ .use(remarkRehype)
+ .use(rehypeStringify)
+ .processSync(announcement).value;
diff --git a/frontend/src/components/AnnouncementBar/index.tsx b/frontend/src/components/AnnouncementBar/index.tsx
index e490b9d..01e38eb 100644
--- a/frontend/src/components/AnnouncementBar/index.tsx
+++ b/frontend/src/components/AnnouncementBar/index.tsx
@@ -1,8 +1,6 @@
-interface AnnouncementBarProps {
- content: string;
-}
+import { content } from "./content" with { type: "macro" };
-export function AnnouncementBar({ content }: AnnouncementBarProps) {
+export function AnnouncementBar() {
return (
id.startsWith("module")}
/>
+
id === "docs"}
+ />