const { themes } = require("prism-react-renderer"); const lightCodeTheme = themes.github; const darkCodeTheme = { ...themes.dracula, styles: [ ...themes.dracula.styles, { types: ["atrule"], style: { color: "rgb(139, 233, 253)" }, }, ], }; const semver = require("semver"); import fs from "fs/promises"; import { loadPages } from "./src/lib/ecosystem/loadPages.js"; import { loadEvents } from "./src/lib/events/loadEvents.js"; import { markdownExportPlugin } from "./src/lib/plugins/markdownExport.js"; import { loadRules } from "./src/lib/projects/regal/loadRules.js"; import { loadSurveyEventData, loadSurveyEventMetadata, loadSurveyQuestions } from "./src/lib/surveys/loadSurveyData.js"; const path = require("path"); const yaml = require("js-yaml"); const baseUrl = "/"; // With JSDoc @type annotations, IDEs can provide config autocompletion /** @type {import("@docusaurus/types").DocusaurusConfig} */ ( module.exports = { title: "Open Policy Agent", tagline: "Policy-based control for cloud native environments", url: "https://openpolicyagent.org", baseUrl: baseUrl, trailingSlash: false, // when BUILD_VERSION is set (release builds), warn on broken links/anchors so we don't break main // when not set (PR checks), throw to flag issues for developers onBrokenLinks: process.env.BUILD_VERSION ? "warn" : "throw", onBrokenAnchors: process.env.BUILD_VERSION ? "warn" : "throw", presets: [ [ "@docusaurus/preset-classic", /** @type {import("@docusaurus/preset-classic").Options} */ { docs: { path: "docs", routeBasePath: "/docs/", breadcrumbs: false, sidebarPath: require.resolve("./src/lib/sidebars.js"), }, blog: { path: "blog", routeBasePath: "blog", blogTitle: "OPA Blog", blogDescription: "News, releases, and community updates from the Open Policy Agent project.", postsPerPage: 10, blogSidebarTitle: "OPA Blog Posts", blogSidebarCount: "ALL", showReadingTime: false, feedOptions: { type: ["rss", "atom"], title: "Open Policy Agent Blog", }, onInlineTags: "ignore", onUntruncatedBlogPosts: "ignore", }, theme: { customCss: require.resolve("./src/css/custom.css"), }, }, ], ], customFields: { buildVersion: process.env.BUILD_VERSION, }, markdown: { hooks: { onBrokenMarkdownLinks: "throw" }, mermaid: true, }, themes: ["@docusaurus/theme-mermaid"], themeConfig: { colorMode: { defaultMode: "light", disableSwitch: false, respectPrefersColorScheme: true, }, metadata: [ { name: "msapplication-TileColor", content: "#2b5797" }, { name: "theme-color", content: "#ffffff" }, ], headTags: [ { tagName: "link", attributes: { rel: "icon", type: "image/png", href: "/favicon-96x96.png", sizes: "96x96", }, }, { tagName: "link", attributes: { rel: "icon", type: "image/svg+xml", href: "/favicon.svg", }, }, { tagName: "link", attributes: { rel: "shortcut icon", href: "/favicon.ico", }, }, { tagName: "link", attributes: { rel: "apple-touch-icon", sizes: "180x180", href: "/apple-touch-icon.png", }, }, { tagName: "meta", attributes: { name: "apple-mobile-web-app-title", content: "OPA", }, }, { tagName: "link", attributes: { rel: "manifest", href: "/site.webmanifest", }, }, ], navbar: { title: "Open Policy Agent", logo: { alt: "OPA Logo", src: "img/nav/logo.png", }, items: [ { // Based on implementation outlined here: // https://github.com/facebook/docusaurus/issues/7227#issue-1212117180 type: "custom-currentVersionNavbarItem", position: "left", }, { to: "/docs/", label: "Docs", position: "right" }, { type: "dropdown", label: "Resources", position: "right", items: [ { to: "/security", label: "Security" }, { to: "/support", label: "Support" }, { to: "/community", label: "Community" }, { to: "/survey", label: "Survey" }, { to: "/blog", label: "Blog" }, ], }, { type: "dropdown", label: "Projects", position: "right", items: [ { to: "/docs", label: "OPA" }, { to: "/projects/regal", label: "Regal" }, { type: "html", value: "
", }, { href: "https://open-policy-agent.github.io/gatekeeper/website/", label: "OPA Gatekeeper" }, { href: "https://www.conftest.dev", label: "Conftest" }, ], }, { to: "/ecosystem/", label: "Ecosystem", position: "right" }, { href: "https://play.openpolicyagent.org/", label: "Play", position: "right" }, { type: "html", position: "right", value: ` GitHub GitHub `, }, { type: "html", position: "right", value: ` Slack Slack `, }, ], }, footer: { style: "light", links: [], copyright: `Open Policy Agent is a Cloud Native Computing Foundation Graduated project. CNCF Logo CNCF Logo
© ${new Date().getFullYear()} Open Policy Agent contributors. Licensed under the Apache License, Version 2.0. See the contributing documentation for information about contributing. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page.`, }, prism: { theme: lightCodeTheme, darkTheme: darkCodeTheme, additionalLanguages: [ "rego", "hcl", "json", "java", "scala", "gradle", "javadoc", "sql", "http", "diff", "typescript", "ini", "cypher", "csharp", "shell-session", "go-module", "docker", "javastacktrace", "properties", "log", ], magicComments: [ { className: "code-block-terminal-command", line: "terminal-command", }, { className: "code-block-terminal-command", line: "cmd", }, { className: "code-block-diff-add-line", line: "diff-add", block: { start: "diff-add-start", end: "diff-add-end" }, }, { className: "code-block-diff-remove-line", line: "diff-remove", block: { start: "diff-remove-start", end: "diff-remove-end" }, }, { className: "theme-code-block-highlighted-line", line: "highlight-next-line", block: { start: "highlight-start", end: "highlight-end" }, }, { className: "code-block-error-line", line: "error-next-line", block: { start: "error-start", end: "error-end" }, }, ], }, mermaid: { theme: { light: "base", dark: "dark" }, options: { themeVariables: { // https://mermaid.js.org/config/theming.html#theme-variables fontFamily: "sans-serif", primaryColor: "#76d3ed", secondaryColor: "#fff", tertiaryColor: "#fff", }, }, }, }, plugins: [ [ "@docusaurus/plugin-google-gtag", { trackingID: "G-JNBNV64PDX", anonymizeIP: true, }, ], [ "@docusaurus/plugin-content-docs", { id: "regal", path: "projects/regal", routeBasePath: "projects/regal", sidebarPath: require.resolve("./src/lib/sidebar-auto.js"), }, ], () => ({ name: "raw-loader", configureWebpack() { return { module: { rules: [ { test: /\.rego$/, use: "raw-loader" }, { test: /\.mermaid$/, use: "raw-loader" }, { test: /\.txt$/, use: "raw-loader" }, ], }, }; }, }), async function ecosystemLanguagePageGen(context, _options) { return { name: "ecosystem-language-gen", async loadContent() { const languages = await loadPages(path.join(context.siteDir, "src/data/ecosystem/languages/*.md")); return { languages }; }, async contentLoaded({ content, actions }) { const { languages } = content; await Promise.all( Object.keys(languages).map(async (language) => { const routePath = path.join(baseUrl, `/ecosystem/by-language/${language}`); return actions.addRoute({ path: routePath, component: require.resolve("./src/EcosystemLanguage.jsx"), exact: true, modules: {}, customData: { language }, }); }), ); }, }; }, async function ecosystemFeaturePageGen(context, _options) { return { name: "ecosystem-feature-gen", async loadContent() { const features = await loadPages(path.join(context.siteDir, "src/data/ecosystem/features/*.md")); return { features }; }, async contentLoaded({ content, actions }) { const { features } = content; await Promise.all( Object.keys(features).map(async (feature) => { const routePath = path.join(baseUrl, `/ecosystem/by-feature/${feature}`); return actions.addRoute({ path: routePath, component: require.resolve("./src/EcosystemFeature.jsx"), exact: true, modules: {}, customData: { feature }, }); }), ); }, }; }, async function ecosystemData(context, _options) { return { name: "ecosystem-data", async loadContent() { const entries = await loadPages(path.join(context.siteDir, "src/data/ecosystem/entries/*.md")); const languages = await loadPages(path.join(context.siteDir, "src/data/ecosystem/languages/*.md")); const features = await loadPages(path.join(context.siteDir, "src/data/ecosystem/features/*.md")); const featureCategories = await loadPages( path.join(context.siteDir, "src/data/ecosystem/feature-categories/*.md"), ); return { entries, languages, features, featureCategories, }; }, async contentLoaded({ content, actions }) { const { createData } = actions; const { entries, languages, features, featureCategories } = content; await createData("entries.json", JSON.stringify(entries, null, 2)); await createData("languages.json", JSON.stringify(languages, null, 2)); await createData("features.json", JSON.stringify(features, null, 2)); await createData("feature-categories.json", JSON.stringify(featureCategories, null, 2)); }, }; }, async function builtinData(_context, _options) { return { name: "builtin-data", async loadContent() { const filePath = "../builtin_metadata.json"; const fileContent = await fs.readFile(filePath, "utf-8"); const builtins = JSON.parse(fileContent); return { builtins }; }, async contentLoaded({ content, actions }) { const { createData } = actions; const { builtins } = content; await createData("builtins.json", JSON.stringify(builtins, null, 2)); }, }; }, async function ecosystemPagesGen(context, _options) { return { name: "ecosystem-entries-pages-gen", async loadContent() { const entries = await loadPages(path.join(context.siteDir, "src/data/ecosystem/entries/*.md")); return { entries }; }, async contentLoaded({ content, actions }) { const { entries } = content; await Promise.all( Object.values(entries).map(async (entry) => { const routePath = path.join(baseUrl, `/ecosystem/entry/${entry.id}`); return actions.addRoute({ path: routePath, component: require.resolve("./src/EcosystemEntry.jsx"), exact: true, modules: {}, customData: { id: entry.id }, }); }), ); }, }; }, async function eventsData(context, _options) { return { name: "events-data", async loadContent() { const events = await loadEvents(path.join(context.siteDir, "src/data/events/*.json")); return { events }; }, async contentLoaded({ content, actions }) { const { createData } = actions; const { events } = content; await createData("events.json", JSON.stringify(events, null, 2)); }, }; }, async function eventsPagesGen(context, _options) { return { name: "events-pages-gen", async loadContent() { const events = await loadEvents(path.join(context.siteDir, "src/data/events/*.json")); return { events }; }, async contentLoaded({ content, actions }) { const { events } = content; await Promise.all( Object.values(events).map(async (event) => { const routePath = path.join(baseUrl, `/events/${event.id}`); return actions.addRoute({ path: routePath, component: require.resolve("./src/EventPage.jsx"), exact: true, modules: {}, customData: { id: event.id }, }); }), ); }, }; }, async function versionsData(context, _options) { return { name: "versions-data", async loadContent() { const capabilitiesDir = path.resolve(__dirname, "../capabilities"); const dirents = await fs.readdir(capabilitiesDir, { withFileTypes: true }); const versionStrings = dirents .filter(dirent => dirent.isFile() && dirent.name.endsWith(".json")) .map(dirent => dirent.name.replace(".json", "")); const validVersions = versionStrings.filter(v => semver.valid(v)); const sortedVersions = semver.sort(validVersions); return { versions: sortedVersions }; }, async contentLoaded({ content, actions }) { const { createData } = actions; const { versions } = content; await createData("versions.json", JSON.stringify(versions, null, 2)); const staticDir = path.join(context.siteDir, "static", "data"); await fs.mkdir(staticDir, { recursive: true }); await fs.writeFile( path.join(staticDir, "versions.json"), JSON.stringify(versions, null, 2), ); }, }; }, async function cliData(context, _options) { return { name: "cli-data", async loadContent() { const filePath = path.join(context.siteDir, "src/data/cli.json"); const cliJson = await fs.readFile(filePath, "utf-8"); const parsedData = JSON.parse(cliJson); return parsedData; }, async contentLoaded({ content, actions }) { const { createData } = actions; await createData("cli.json", JSON.stringify(content, null, 2)); }, }; }, async function versionsPageGen(_context, _options) { return { name: "version-page-gen", async contentLoaded({ content: _content, actions }) { return actions.addRoute({ path: path.join(baseUrl, `/docs/archive`), component: require.resolve("./src/Archive.jsx"), exact: true, modules: {}, }); }, }; }, async function regalRulesData() { return { name: "regal", async loadContent() { const rules = await loadRules(); return { rules }; }, async contentLoaded({ content, actions }) { await actions.createData("rules.json", JSON.stringify(content.rules, null, 2)); }, }; }, async function surveyData(context, _options) { return { name: "survey-data", async loadContent() { const eventData = await loadSurveyEventData( path.join(context.siteDir, "src/data/surveys/events/**/**/data.json"), ); const questions = await loadSurveyQuestions( path.join(context.siteDir, "src/data/surveys/questions/**/data.json"), ); const eventMetadata = await loadSurveyEventMetadata( path.join(context.siteDir, "src/data/surveys/events/**/metadata.json"), ); return { eventData, questions, eventMetadata }; }, async contentLoaded({ content, actions }) { const { createData, addRoute } = actions; const { eventData, questions, eventMetadata } = content; await createData("survey-event-data.json", JSON.stringify(eventData, null, 2)); await createData("survey-questions.json", JSON.stringify(questions, null, 2)); await createData("survey-event-metadata.json", JSON.stringify(eventMetadata, null, 2)); // Create a page for each event const eventSlugs = Object.keys(eventData); await Promise.all( eventSlugs.map(async (eventSlug) => { const routePath = path.join(baseUrl, `/survey/${eventSlug}`); return addRoute({ path: routePath, component: require.resolve("./src/SurveyEvent/index.jsx"), exact: true, modules: {}, customData: { eventSlug }, }); }), ); }, }; }, async function glossaryData(context) { return { name: "glossary-data", async loadContent() { const glossaryPath = path.join(context.siteDir, "glossary.yaml"); const fileContent = await fs.readFile(glossaryPath, "utf-8"); const glossary = yaml.load(fileContent); return { glossary }; }, async contentLoaded({ content, actions }) { await actions.createData("glossary.json", JSON.stringify(content.glossary, null, 2)); }, }; }, async function irPlanSchema(context) { return { name: "ir-plan-schema", async loadContent() { const sourcePath = path.resolve(__dirname, "../v1/ir/plan.schema.json"); const targetDir = path.join(context.siteDir, "static/schemas/ir/v1"); await fs.mkdir(targetDir, { recursive: true }); await fs.copyFile(sourcePath, path.join(targetDir, "plan.schema.json")); }, }; }, async function bundleManifestSchema(context) { return { name: "bundle-manifest-schema", async loadContent() { const sourcePath = path.resolve(__dirname, "../v1/bundle/manifest.schema.json"); const targetDir = path.join(context.siteDir, "static/schemas/bundle/v1"); await fs.mkdir(targetDir, { recursive: true }); await fs.copyFile(sourcePath, path.join(targetDir, "manifest.schema.json")); }, }; }, markdownExportPlugin, ], clientModules: [ require.resolve("./src/lib/gtagFallback.js"), require.resolve("./src/lib/playground.js"), require.resolve("./src/lib/kapa.js"), ], stylesheets: [ { href: "https://unpkg.com/@antonz/codapi@0.19.8/dist/snippet.css", }, ], scripts: [ { src: "https://unpkg.com/@antonz/codapi@0.19.8/dist/snippet.js", defer: true, }, { src: "https://widget.kapa.ai/kapa-widget.bundle.js", "data-website-id": "5c4af137-a024-4ba2-9488-826f1055852c", "data-project-name": "Open Policy Agent", "data-project-logo": "https://openpolicyagent.org/img/nav/logo.png", "data-search-mode-enabled": "true", // Source UUIDs come from https://app.kapa.ai/422cffb8-2750-48c9-9659-7e8d40b9d5d2/sources "data-search-source-ids-include": "f9c26922-8a4c-4a3c-913d-08c2a53a1011,e9daf266-5a9f-410f-8069-18a57dcfb6ee,723b2ac1-807a-43c1-a2aa-bcf8311d2354,fff41a96-7ebe-4fc5-bb60-359e5d830a87,799aac5c-a157-4734-9785-9c754c7c40a0", "data-modal-open-on-command-k": "true", "data-modal-command-k-search-mode-default": "true", "data-deep-thinking-button-hover-background-color": "#f1f8f1", "data-deep-thinking-button-hover-color": "#2e7d32", "data-deep-thinking-button-enabled-background-color": "#e8f5e9", "data-deep-thinking-button-enabled-color": "#2e7d32", "data-deep-thinking-button-enabled-hover-background-color": "#c8e6c9", "data-deep-thinking-button-enabled-hover-color": "#1b5e20", "data-deep-thinking-button-hover-background-color-dark": "#1e3320", "data-deep-thinking-button-hover-color-dark": "#81c784", "data-deep-thinking-button-enabled-background-color-dark": "#1a3d1c", "data-deep-thinking-button-enabled-color-dark": "#a5d6a7", "data-deep-thinking-button-enabled-hover-background-color-dark": "#245427", "data-deep-thinking-button-enabled-hover-color-dark": "#c8e6c9", "data-modal-title": "OPA Chat and Search", "data-color-scheme-selector": "[data-theme='dark']", async: true, }, ], } );