From d6d291ddacee223b6f9765cfe5f612cdc8fc992d Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Mon, 31 Aug 2026 12:46:59 -0400 Subject: [PATCH 1/7] GRO-686 stub out reading nav.yml from _docs --- _includes/docs/left-sidebar.html | 21 --- _includes/learn/left-nav.html | 147 --------------------- astro.config.mjs | 2 +- src/components/chrome/Chevron.astro | 27 ++++ src/components/chrome/LeftSidebar.astro | 166 ++++++++++++++++++++++++ src/layouts/NewDocsLayout.astro | 5 +- 6 files changed, 198 insertions(+), 170 deletions(-) delete mode 100644 _includes/docs/left-sidebar.html delete mode 100644 _includes/learn/left-nav.html create mode 100644 src/components/chrome/Chevron.astro create mode 100644 src/components/chrome/LeftSidebar.astro diff --git a/_includes/docs/left-sidebar.html b/_includes/docs/left-sidebar.html deleted file mode 100644 index e84e27e1b9..0000000000 --- a/_includes/docs/left-sidebar.html +++ /dev/null @@ -1,21 +0,0 @@ - diff --git a/_includes/learn/left-nav.html b/_includes/learn/left-nav.html deleted file mode 100644 index c78e237ee9..0000000000 --- a/_includes/learn/left-nav.html +++ /dev/null @@ -1,147 +0,0 @@ -{% assign cleanPageURL = page.url | replace: '/index', '' %} - -{% for category in include.categories %} -
{{category.name}}
- - -{% endfor %} diff --git a/astro.config.mjs b/astro.config.mjs index 4111e4d6b6..2c4e0dd91f 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -35,7 +35,7 @@ export default defineConfig({ viteStaticCopy({ targets: [ { - src: `${DOCS_SRC_ROOT}/**/*.{jpg,png,gif,json}`, + src: `${DOCS_SRC_ROOT}/**/*.{jpg,png,gif,json,yml}`, dest: DOCS_DEST, rename: { stripBase: docsSrcStripBase }, }, diff --git a/src/components/chrome/Chevron.astro b/src/components/chrome/Chevron.astro new file mode 100644 index 0000000000..569e378b39 --- /dev/null +++ b/src/components/chrome/Chevron.astro @@ -0,0 +1,27 @@ +--- +type Props = { + width?: number; + height?: number; +}; + +const { width = 32, height = 32 } = Astro.props; +// TODO: Just make this a normal svg +--- + + + + diff --git a/src/components/chrome/LeftSidebar.astro b/src/components/chrome/LeftSidebar.astro new file mode 100644 index 0000000000..ef897838a0 --- /dev/null +++ b/src/components/chrome/LeftSidebar.astro @@ -0,0 +1,166 @@ +--- +import fs from "node:fs"; +import path from "node:path"; +import Chevron from "@/components/chrome/Chevron.astro"; +import { DOCS_SRC_ROOT, METABASE_REPO_PATH } from "@/constants"; +import YAML from "yamljs"; + +type NavNode = { + name: string; + navName?: string; + url?: string; + pages?: NavNode[]; +}; + +type Props = { + url: string; + version?: string; +}; + +const { url, version = "latest" } = Astro.props; + +const navPath = path.resolve( + process.cwd(), + DOCS_SRC_ROOT, + METABASE_REPO_PATH ? "" : version, + "_includes/data/nav.yml", +); + +if (!fs.existsSync(navPath)) { + throw new Error(`nav.yml not found for version "${version}" at ${navPath}`); +} + +const { categories } = YAML.parse(fs.readFileSync(navPath, "utf8")) as { + categories: NavNode[]; +}; + +// TODO: Is the `replace` necessary? +const cleanPageUrl = url.replace("/index", ""); + +const getName = (node: NavNode) => node.navName || node.name; + +const subtreeContainsUrl = (node: NavNode, targetUrl: string): boolean => { + if (node.url === targetUrl) { + return true; + } + return ( + node.pages?.some((child) => subtreeContainsUrl(child, targetUrl)) ?? false + ); +}; + +const descendantsContainUrl = (node: NavNode, targetUrl: string): boolean => + node.pages?.some((child) => subtreeContainsUrl(child, targetUrl)) ?? false; +--- + + diff --git a/src/layouts/NewDocsLayout.astro b/src/layouts/NewDocsLayout.astro index 5b159be21b..f4385c99da 100644 --- a/src/layouts/NewDocsLayout.astro +++ b/src/layouts/NewDocsLayout.astro @@ -1,4 +1,5 @@ --- +import LeftSidebar from "@/components/chrome/LeftSidebar.astro"; import LiquidInclude from "@/components/LiquidInclude.astro"; import { UNIFY_ENABLED_PAGES } from "@/constants"; import { getLiquidRenderer } from "@/lib/liquid/liquidRenderer"; @@ -9,6 +10,8 @@ type Props = { title: string; url: string; category?: string; + version?: string; + latest?: string; }; dirname: string; }; @@ -27,7 +30,7 @@ const unify = UNIFY_ENABLED_PAGES.includes(page.url);
- +
Date: Mon, 31 Aug 2026 17:27:20 -0400 Subject: [PATCH 2/7] GRO-686 left nav cleanup + tweak logic to use potentially relative urls --- _docs/latest/_includes/data/nav.yml | 582 ++++++++++++++++++++++++ src/components/chrome/LeftSidebar.astro | 140 +----- src/components/chrome/NavItem.astro | 51 +++ 3 files changed, 650 insertions(+), 123 deletions(-) create mode 100644 _docs/latest/_includes/data/nav.yml create mode 100644 src/components/chrome/NavItem.astro diff --git a/_docs/latest/_includes/data/nav.yml b/_docs/latest/_includes/data/nav.yml new file mode 100644 index 0000000000..b560b771db --- /dev/null +++ b/_docs/latest/_includes/data/nav.yml @@ -0,0 +1,582 @@ +categories: + - name: "Analytics" + pages: + - name: "Getting started" + url: "/learn/metabase-basics/getting-started" + - name: "Metabase concepts" + url: "/learn/metabase-basics/overview/concepts" + - name: Queries and charts + url: "questions/start" + pages: + - name: "Introduction" + url: "questions/introduction" + - name: "Query builder" + pages: + - name: "Editor" + url: "questions/query-builder/editor" + - name: "Filtering" + url: "questions/query-builder/filters" + - name: "Summarizing and grouping" + url: "questions/query-builder/summarizing-and-grouping" + - name: "Joining data" + url: "questions/query-builder/join" + - name: "Custom expressions" + url: "questions/query-builder/expressions" + - name: "List of expressions" + url: "questions/query-builder/expressions-list" + - name: "SQL and native queries" + pages: + - name: "SQL editor" + url: "questions/native-editor/writing-sql" + - name: "SQL parameters" + url: "questions/native-editor/sql-parameters" + - name: "Field filters" + url: "questions/native-editor/field-filters" + - name: "Basic SQL parameters" + url: "questions/native-editor/basic-sql-parameters" + - name: "Time grouping parameters" + url: "questions/native-editor/time-grouping-parameters" + - name: "Table variables" + url: "questions/native-editor/table-variables" + - name: "Optional variables" + url: "questions/native-editor/optional-variables" + - name: "Filter widgets" + url: "questions/native-editor/filter-widgets" + - name: "Referencing models and questions" + url: "questions/native-editor/referencing-saved-questions-in-queries" + - name: "Snippets" + url: "questions/native-editor/snippets" + - name: "Snippet folder permissions" + url: "permissions/snippets" + - name: "Visualizations" + pages: + - name: "Overview" + url: "questions/visualizations/visualizing-results" + - name: "Box plot" + url: "questions/visualizations/box-plot" + - name: "Combo chart" + url: "questions/visualizations/combo-chart" + - name: "Details chart" + url: "questions/visualizations/detail" + - name: "Funnel chart" + url: "questions/visualizations/funnel" + - name: "Gauge chart" + url: "questions/visualizations/gauge" + - name: "Line, bar, and area charts" + url: "questions/visualizations/line-bar-and-area-charts" + - name: "Maps" + url: "questions/visualizations/map" + - name: "Number chart" + url: "questions/visualizations/numbers" + - name: "Pie and sunburst charts" + url: "questions/visualizations/pie-or-donut-chart" + - name: "Pivot tables" + url: "questions/visualizations/pivot-table" + - name: "Progress bar" + url: "questions/visualizations/progress-bar" + - name: "Sankey chart" + url: "questions/visualizations/sankey" + - name: "Scatterplot" + url: "questions/visualizations/scatterplot-or-bubble-chart" + - name: "Table" + url: "questions/visualizations/table" + - name: "Treemap" + url: "questions/visualizations/treemap" + - name: "Trend chart" + url: "questions/visualizations/trend" + - name: "Waterfall chart" + url: "questions/visualizations/waterfall-chart" + - name: "Tooltips" + url: "questions/visualizations/tooltips" + - name: "Custom visualizations" + url: "questions/visualizations/custom" + - name: "Drill-through" + url: "questions/visualizations/drill-through" + + - name: "Metrics explorer" + url: "questions/metrics-explorer" + - name: "Alerts" + url: "questions/alerts" + - name: "Exporting data" + url: "questions/exporting-results" + - name: "Dashboards" + url: "dashboards/start" + pages: + - name: "Overview" + url: "dashboards/introduction" + - name: "Dashboard heading and text cards" + url: "dashboards/dashboard-markdown" + - name: "Format text with Markdown" + url: "dashboards/markdown" + - name: "Dashboard filters" + url: "dashboards/filters" + - name: "Linked filters" + url: "dashboards/linked-filters" + - name: "Dashboard interactivity" + url: "dashboards/interactive" + - name: "Charts with multiple series" + url: "dashboards/multiple-series" + - name: "Dashboard subscriptions" + url: "dashboards/subscriptions" + - name: "Actions on dashboards" + url: "dashboards/actions" + + - name: "Documents" + url: "documents/start" + pages: + - name: "Overview" + url: "documents/introduction" + + - name: "AI" + url: "ai/start" + pages: + - name: "Overview" + url: "ai/overview" + - name: "Metabot" + url: "ai/metabot" + - name: "Metabot in Slack" + url: "ai/metabot-slack" + - name: "Settings" + url: "ai/settings" + - name: "MCP server" + url: "ai/mcp" + - name: "Agent API" + url: "ai/agent-api" + - name: "Agent-driven development" + url: "ai/file-based-development" + - name: "Customization" + url: "ai/customization" + - name: "System prompts" + url: "ai/system-prompts" + - name: "Usage auditing" + url: "ai/usage-auditing" + - name: "Usage controls" + url: "ai/usage-controls" + - name: "AI privacy" + url: "ai/privacy" + + - name: "Data modeling" + url: "data-modeling/start" + pages: + - name: "Models" + url: "data-modeling/models" + - name: "Model persistence" + url: "data-modeling/model-persistence" + - name: "Metrics" + url: "data-modeling/metrics" + - name: "Table metadata settings" + url: "data-modeling/metadata-editing" + - name: "Data and semantic types" + url: "data-modeling/semantic-types" + - name: "Editable tables" + url: "data-modeling/editable-tables" + - name: "Formatting defaults" + url: "data-modeling/formatting" + - name: "Working with JSON" + url: "data-modeling/json-unfolding" + - name: "Segments" + url: "data-modeling/segments" + - name: "Actions" + pages: + - name: "Overview" + url: "actions/introduction" + - name: "Basic actions" + url: "actions/basic" + - name: "Custom actions" + url: "actions/custom" + + - name: "Data Studio" + url: "data-studio/overview" + pages: + - name: "Overview" + url: "data-studio/overview" + - name: "Library" + url: "data-studio/library" + - name: "Managing tables" + url: "data-studio/managing-tables" + - name: "Measures" + url: "data-studio/measures" + - name: "Segments" + url: "data-studio/segments" + - name: "Glossary" + url: "exploration-and-organization/data-model-reference#glossary" + - name: "Dependencies" + pages: + - name: "Dependency graph" + url: "data-studio/dependencies/graph" + - name: "Dependency diagnostics" + url: "data-studio/dependencies/diagnostics" + - name: "Replace data sources" + url: "data-studio/dependencies/replace-data-sources" + - name: "Transforms" + pages: + - name: "Overview" + url: "data-studio/transforms/transforms-overview" + - name: "Query transforms" + url: "data-studio/transforms/query-transforms" + - name: "Python transforms" + url: "data-studio/transforms/python-transforms" + - name: "Python runner" + url: "data-studio/transforms/python-runner" + - name: "Jobs and runs" + url: "data-studio/transforms/jobs-and-runs" + - name: "Transform inspector" + url: "data-studio/transforms/transform-inspector" + - name: "Add-ons" + url: "data-studio/transforms/addons" + + - name: "Organization" + url: "exploration-and-organization/start" + pages: + - name: "Basic exploration" + url: "exploration-and-organization/exploration" + - name: "Keyboard shortcuts" + url: "exploration-and-organization/keyboard-shortcuts" + - name: "Collections" + url: "exploration-and-organization/collections" + - name: "Data reference" + url: "exploration-and-organization/data-model-reference" + - name: "Events and timelines" + url: "exploration-and-organization/events-and-timelines" + - name: "X-rays" + url: "exploration-and-organization/x-rays" + - name: "Content verification" + url: "exploration-and-organization/content-verification" + - name: "History" + url: "exploration-and-organization/history" + - name: "Delete and restore" + url: "exploration-and-organization/delete-and-restore" + + - name: "Embedding" + url: "embedding/start" + pages: + - name: "Overview" + url: "embedding/introduction" + - name: "Modular embedding" + pages: + - name: "Overview" + url: "embedding/modular-embedding" + - name: "Components" + url: "embedding/components" + - name: "Filters and parameters" + url: "embedding/parameters" + - name: "Appearance" + url: "embedding/appearance" + - name: "Authentication" + url: "embedding/authentication" + - name: "Tenants" + url: "embedding/tenants" + - name: "Modular embedding SDK" + pages: + - name: "Overview" + url: "embedding/sdk/introduction" + - name: "Quickstarts" + url: "embedding/sdk/introduction#quickstarts" + - name: "Components" + pages: + - name: "Questions" + url: "embedding/sdk/questions" + - name: "Dashboards" + url: "embedding/sdk/dashboards" + - name: "AI chat" + url: "embedding/sdk/ai-chat" + - name: "Collections" + url: "embedding/sdk/collections" + - name: "Plugins" + url: "embedding/sdk/plugins" + - name: "Configuration" + pages: + - name: "Provider config" + url: "embedding/sdk/config" + - name: "Working with Next.js" + url: "embedding/sdk/next-js" + - name: "Versioning" + url: "embedding/sdk/version" + - name: "Upgrading" + url: "embedding/sdk/upgrade" + - name: "API" + url: "embedding/sdk/api/" + - name: "Guest embedding" + url: "embedding/guest-embedding" + - name: "Translate embeds" + url: "embedding/translations" + - name: "Full app embedding" + pages: + - name: "Overview" + url: "embedding/full-app-embedding" + - name: "Quickstart" + url: "embedding/full-app-embedding-quick-start-guide" + - name: "Full app UI components" + url: "embedding/full-app-ui-components" + - name: "Public links and embeds" + url: "embedding/public-links" + - name: "Securing embeds" + url: "embedding/securing-embeds" + - name: "AI agent resources" + url: "embedding/ai-agent-resources" + + - name: "Administration" + pages: + - name: "Installation" + url: "installation-and-operation/start" + pages: + - name: "Installing Metabase" + pages: + - name: "Installation overview" + url: "installation-and-operation/installing-metabase" + - name: "Metabase Cloud" + url: "cloud/start" + - name: "Running the JAR file" + url: "installation-and-operation/running-the-metabase-jar-file" + - name: "Running in Docker" + url: "installation-and-operation/running-metabase-on-docker" + - name: "Other installation options" + url: "installation-and-operation/installing-metabase#other-installation-options" + - name: "Upgrading Metabase" + url: "installation-and-operation/upgrading-metabase" + - name: "Configuring the Metabase application database" + url: "installation-and-operation/configuring-application-database" + - name: "Activating Enterprise features" + url: "installation-and-operation/activating-the-enterprise-edition" + - name: "Migrating to a production application database" + url: "installation-and-operation/migrating-from-h2" + - name: "Data sources" + url: "databases/start" + pages: + - name: "Adding and managing databases" + url: "databases/connecting" + - name: "Supported databases" + pages: + - name: "Athena" + url: "databases/connections/athena" + - name: "Amazon RDS" + url: "databases/connections/aws-rds" + - name: "BigQuery" + url: "databases/connections/bigquery" + - name: "ClickHouse" + url: "databases/connections/clickhouse" + - name: "Databricks" + url: "databases/connections/databricks" + - name: "Druid" + url: "databases/connections/druid" + - name: "MariaDB" + url: "databases/connections/mariadb" + - name: "MongoDB" + url: "databases/connections/mongodb" + - name: "MySQL" + url: "databases/connections/mysql" + - name: "Oracle" + url: "databases/connections/oracle" + - name: "PostgreSQL" + url: "databases/connections/postgresql" + - name: "Presto" + url: "databases/connections/presto" + - name: "Redshift" + url: "databases/connections/redshift" + - name: "Snowflake" + url: "databases/connections/snowflake" + - name: "SQL Server" + url: "databases/connections/sql-server" + - name: "SQLite" + url: "databases/connections/sqlite" + - name: "Spark SQL" + url: "databases/connections/sparksql" + - name: "Starburst" + url: "databases/connections/starburst" + - name: "Vertica" + url: "databases/connections/vertica" + - name: "Community drivers" + url: "developers-guide/community-drivers" + + - name: "Database users, roles, and privileges" + url: "databases/users-roles-privileges" + - name: "Writable connection" + url: "databases/writable-connection" + - name: "Syncing and scanning databases" + url: "databases/sync-scan" + - name: "Encrypting your database connection" + url: "databases/encrypting-details-at-rest" + - name: "SSH tunneling" + url: "databases/ssh-tunnel" + - name: "SSL certificate" + url: "databases/ssl-certificates" + - name: "Setting up data uploads" + url: "databases/uploads" + - name: "Uploading data" + url: "exploration-and-organization/uploads" + - name: "Metabase Cloud Storage" + url: "cloud/storage" + - name: "Sync Google Sheets" + url: "cloud/google-sheets" + - name: "Configuration" + url: "configuring-metabase/start" + pages: + - name: "Setting up Metabase" + url: "configuring-metabase/setting-up-metabase" + - name: "General settings" + url: "configuring-metabase/settings" + - name: "Set up email" + url: "configuring-metabase/email" + - name: "Set up Slack" + url: "configuring-metabase/slack" + - name: "Webhooks" + url: "configuring-metabase/webhooks" + - name: "Environment variables" + url: "configuring-metabase/environment-variables" + - name: "Configuration file" + url: "configuring-metabase/config-file" + - name: "Config file template" + url: "configuring-metabase/config-template" + - name: "Metabase log configuration" + url: "configuring-metabase/log-configuration" + - name: "Timezones" + url: "configuring-metabase/timezones" + - name: "Languages and localization" + url: "configuring-metabase/localization" + - name: "Appearance" + url: "configuring-metabase/appearance" + - name: "Fonts" + url: "configuring-metabase/fonts" + - name: "Caching query results" + url: "configuring-metabase/caching" + - name: "Custom maps" + url: "configuring-metabase/custom-maps" + - name: "Customizing the Metabase Jetty webserver" + url: "configuring-metabase/customizing-jetty-webserver" + - name: "Operations and monitoring" + url: "installation-and-operation/start" + pages: + - name: "Backing up Metabase" + url: "installation-and-operation/backing-up-metabase-application-data" + - name: "Development instances" + url: "installation-and-operation/development-instance" + - name: "Monitoring your Metabase" + url: "installation-and-operation/monitoring-metabase" + - name: "Observability with Prometheus" + url: "installation-and-operation/observability-with-prometheus" + - name: "Serialization" + url: "installation-and-operation/serialization" + - name: "Remote Sync" + url: "installation-and-operation/remote-sync" + - name: "Commands" + url: "installation-and-operation/commands" + - name: "Metabase CLI" + url: "installation-and-operation/metabase-cli" + - name: "Usage analytics" + url: "usage-and-performance-tools/usage-analytics" + - name: "Security center" + url: "installation-and-operation/security-center" + - name: "Admin tools" + url: "usage-and-performance-tools/tools" + - name: "Authentication" + url: "people-and-groups/start" + pages: + - name: "Account settings" + url: "people-and-groups/account-settings" + - name: "Password complexity" + url: "people-and-groups/changing-password-complexity" + - name: "Session expiration" + url: "people-and-groups/changing-session-expiration" + - name: "Google Sign-In" + url: "people-and-groups/google-sign-in" + - name: "LDAP" + url: "people-and-groups/ldap" + - name: "User provisioning" + url: "people-and-groups/user-provisioning" + - name: "API keys" + url: "people-and-groups/api-keys" + - name: "Paid SSO options" + url: "people-and-groups/start" + pages: + - name: "JWT-based authentication" + url: "people-and-groups/authenticating-with-jwt" + - name: "SAML-based authentication" + url: "people-and-groups/authenticating-with-saml" + - name: "SAML with Auth0" + url: "people-and-groups/saml-auth0" + - name: "SAML with Microsoft Entra ID" + url: "people-and-groups/saml-azure" + - name: "SAML with Google" + url: "people-and-groups/saml-google" + - name: "SAML with Keycloak" + url: "people-and-groups/saml-keycloak" + - name: "SAML with Okta" + url: "people-and-groups/saml-okta" + - name: "OIDC-based authentication" + url: "people-and-groups/authenticating-with-oidc" + - name: "OIDC with Keycloak" + url: "people-and-groups/oidc-keycloak" + - name: "Permissions" + url: "permissions/start" + pages: + - name: "Permissions introduction" + url: "permissions/introduction" + - name: "Managing people and groups" + url: "people-and-groups/managing" + - name: "Data permissions" + url: "permissions/data" + - name: "Data isolation methods" + url: "permissions/data-isolation-methods" + - name: "Collection permissions" + url: "permissions/collections" + - name: "Application permissions" + url: "permissions/application" + - name: "Row and column security" + url: "permissions/row-and-column-security" + - name: "Row and column security examples" + url: "permissions/row-and-column-security-examples" + - name: "Database routing" + url: "permissions/database-routing" + - name: "Impersonation" + url: "permissions/impersonation" + - name: "Snippets folder permissions" + url: "permissions/snippets" + - name: "Notification permissions" + url: "permissions/notifications" + - name: "Configuring permissions for embedding" + url: "permissions/embedding" + + - name: Other resources + pages: + - name: "API" + url: "api" + - name: "Metabase Cloud" + url: "cloud/start" + pages: + - name: "Cloud vs self-hosted" + url: "cloud/cloud-vs-self-hosting" + - name: "Custom domain" + url: "cloud/custom-domain" + - name: "Cloud limitations" + url: "cloud/limitations" + - name: "Change region" + url: "cloud/change-region" + - name: "IP addresses to whitelist" + url: "cloud/ip-addresses-to-whitelist" + - name: "Migrate from self-hosted to Cloud" + url: "cloud/migrate/guide" + - name: "Migrate from Cloud to self-hosted" + url: "cloud/migrate/cloud-to-self-hosted" + - name: "Migrate from Heroku" + url: "cloud/migrate/heroku" + - name: "Billing" + url: "https://www.metabase.com/how-billing-works" + pages: + - name: "How billing works" + url: "cloud/how-billing-works" + - name: "Accounts and billing" + url: "cloud/accounts-and-billing" + - name: "Troubleshooting" + url: "troubleshooting-guide/" + - name: "Developer guide" + url: "developers-guide/start" + - name: "Pro and Enterprise features" + url: "/paid-features/" + - name: "Accessibility" + url: "installation-and-operation/accessibility" + - name: "Supported browsers" + url: "installation-and-operation/supported-browsers" + - name: "Privacy" + url: "installation-and-operation/privacy" + - name: "About the anonymous usage data we collect" + url: "installation-and-operation/information-collection" diff --git a/src/components/chrome/LeftSidebar.astro b/src/components/chrome/LeftSidebar.astro index ef897838a0..1a5a9562bb 100644 --- a/src/components/chrome/LeftSidebar.astro +++ b/src/components/chrome/LeftSidebar.astro @@ -1,23 +1,16 @@ --- import fs from "node:fs"; import path from "node:path"; -import Chevron from "@/components/chrome/Chevron.astro"; +import NavItem, { type NavNode } from "@/components/chrome/NavItem.astro"; import { DOCS_SRC_ROOT, METABASE_REPO_PATH } from "@/constants"; import YAML from "yamljs"; -type NavNode = { - name: string; - navName?: string; - url?: string; - pages?: NavNode[]; -}; - type Props = { url: string; version?: string; }; -const { url, version = "latest" } = Astro.props; +const { url: pageUrl, version = "latest" } = Astro.props; const navPath = path.resolve( process.cwd(), @@ -34,131 +27,32 @@ const { categories } = YAML.parse(fs.readFileSync(navPath, "utf8")) as { categories: NavNode[]; }; -// TODO: Is the `replace` necessary? -const cleanPageUrl = url.replace("/index", ""); +const isRelativeUrl = (url: string) => !/^(\/|[a-z][a-z0-9+.-]*:)/i.test(url); -const getName = (node: NavNode) => node.navName || node.name; +// TODO: This seems expensive. Is there a cheaper way to do it? Or a way to cache/memoize? -const subtreeContainsUrl = (node: NavNode, targetUrl: string): boolean => { - if (node.url === targetUrl) { - return true; - } - return ( - node.pages?.some((child) => subtreeContainsUrl(child, targetUrl)) ?? false - ); -}; +const resolveUrls = (node: NavNode): NavNode => ({ + ...node, + url: + node.url && isRelativeUrl(node.url) + ? `/docs/${version}/${node.url}` + : node.url, + pages: node.pages?.map(resolveUrls), +}); -const descendantsContainUrl = (node: NavNode, targetUrl: string): boolean => - node.pages?.some((child) => subtreeContainsUrl(child, targetUrl)) ?? false; +const resolvedCategories = categories.map(resolveUrls); ---