Skip to content

feat(docs): content variants — reuse one markdown file across navigation entries - #17640

Open
devin-ai-integration[bot] wants to merge 4 commits into
mainfrom
devin/1788448368-docs-content-variants
Open

feat(docs): content variants — reuse one markdown file across navigation entries#17640
devin-ai-integration[bot] wants to merge 4 commits into
mainfrom
devin/1788448368-docs-content-variants

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Description

Linear ticket: Refs (none)

Lets a single markdown file be referenced from several places in docs.yml, each rendered as its own page with variant-specific code blocks, links, and text. Motivation: docs teams maintaining 5–8 near-identical articles (e.g. Apache vs NGINX config, mobile SDK v1 vs v2 guides).

variants:                                   # centralized values, maintained once
  nginx:  { server: NGINX,  config-path: /etc/nginx/nginx.conf }
  apache: { server: Apache, config-path: /etc/apache2/httpd.conf }

navigation:
  - section: NGINX
    variant: nginx                          # inherited by descendant pages
    contents:
      - page: Configuration
        path: ./pages/shared/server-config.mdx
  - section: Apache
    contents:
      - page: Configuration
        path: ./pages/shared/server-config.mdx
        variant: apache                     # or set per page
Edit `{{variant.config-path}}` on your {{variant.server}} server.

<Variant name="nginx">  ...nginx example...  </Variant>
<Variant name="apache"> ...apache example... </Variant>
<Variant name="nginx, apache">shown for a subset</Variant>

<Markdown src="../snippets/restart.mdx" />   <!-- snippets may use <Variant>/{{variant.*}} too -->

Design notes:

  • Build-time only, no frontend changes. <Variant> blocks and {{variant.<key>}} are resolved in the CLI after <Markdown src> inclusion, so centralized snippets can carry variant syntax.
  • Each (file, variant) pair is registered under a virtual page id pages/shared/server-config~nginx.mdx (getVariantPageFilepath) so the two references don't collide in parsedDocsConfig.pages. The virtual path lives in the same directory as the source, so relative <Markdown src>/<Code src>/image resolution is unchanged; edit-this-page URLs use the real source path.
  • Referencing an undeclared variant is a ConfigError. Unresolved {{variant.x}} placeholders and <Variant> blocks on a page with no variant selected produce warnings (all variant blocks are stripped in that case).
  • Nested <Variant> blocks are not supported (regex-based, same approach as other markdown utils).

Changes Made

  • fern/apis/docs-yml/definition/docs.yml: top-level variants: map<VariantId, map<string,string>>; variant on PageConfiguration and SectionConfiguration (via WithVariant). SDK + JSON schema regenerated.
  • docs-markdown-utils/applyContentVariant.ts: pure function — keeps/strips <Variant name="a, b"> blocks, substitutes {{variant.key}}, reports missing keys.
  • configuration-loader: parsePageConfig resolves item.variant ?? inheritedVariant, sets absolutePath to the virtual path and variant.sourceAbsolutePath to the real file; getAllPageFiles/getVariantPages load content from source and register under the virtual path; getAllPages (used by validators) still returns deduplicated physical files.
  • docs-resolver: applyPageVariant runs on raw markdown and again after replaceReferencedMarkdown; edit-this-page URL uses the source path.
  • docs-validator: variants: noop in the docs.yml AST visitor.
  • Changelog: packages/cli/cli/changes/unreleased/add-docs-content-variants.yml (feat).
  • Updated README.md generator (not applicable)

Testing

  • Unit tests added/updated
    • docs-markdown-utils/src/__test__/applyContentVariant.test.ts (block selection, multi-name blocks, quoted/JSX name, inline substitution, missing values, no variant).
    • docs-resolver/src/__test__/content-variants.test.ts + fixtures/content-variants: same file → nginx/configuration and apache/configuration pages with distinct content, section-inherited variant, variant syntax inside a <Markdown src> snippet, frontmatter title substitution.
  • pnpm compile, pnpm lint:biome, pnpm format:fix, cspell on new files; turbo run test for docs-resolver, configuration-loader, docs-markdown-utils, docs-validator all green.
  • Manual testing against a live docs site (see follow-up proposal in session).

Link to Devin session: https://app.devin.ai/sessions/6c13066a819e47b2aad130b1665b6858
Open in Devin Desktop: https://app.devin.ai/desktop/session/6c13066a819e47b2aad130b1665b6858?variant=devin


Devin Review

…ages

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@nitpickybot nitpickybot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

Solid, well-tested feature. Main concerns: the applyPageVariant call inside the raw-markdown loop runs before <Markdown src> inclusion (so raw markdown for variant pages can contain unresolved snippet variant syntax — presumably intentional but worth confirming), a duplicate-variant-page collision risk with the ~ separator, and getVariantPages failing hard while other places warn. Also a couple of regex robustness issues in applyContentVariant.

  • 🟡 3 warning(s)
  • 🔵 2 suggestion(s)

To request another review, comment /ai-review on this pull request.

Comment thread packages/cli/configuration/src/docs-yml/ParsedDocsConfiguration.ts
Comment thread packages/cli/docs-markdown-utils/src/applyContentVariant.ts
Comment thread packages/cli/docs-markdown-utils/src/applyContentVariant.ts Outdated
Comment thread packages/cli/docs-resolver/src/DocsDefinitionResolver.ts Outdated
Comment thread packages/cli/configuration-loader/src/docs-yml/getAllPages.ts

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 potential issues.

2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment thread packages/cli/docs-resolver/src/DocsDefinitionResolver.ts
Comment thread packages/cli/docs-markdown-utils/src/applyContentVariant.ts
dvdaruri-art and others added 3 commits September 3, 2026 15:32
…keep literal <Variant> in code

- apply variant to page sources before frontmatter extraction so title/slug/sidebar-title resolve per variant
- section `variant` now applies to the section overview page (virtual path + source tracking)
- translation loading looks up translated files by source path and registers under the logical variant path; getTranslationPages applies the variant
- <Variant> tags inside fenced/inline code are left as literal text; {{variant.*}} still substitutes everywhere
- regenerate stale versions/products JSON schemas (lint CI)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ether, nested <Variant> warning, tighter name attr match

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Docs Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-09-03T05:10:38Z).

Fixture main PR Delta
docs 269.1s (n=5) 278.1s (35 versions) +9.0s (+3.3%)

Docs generation runs fern generate --docs --preview end-to-end against the benchmark fixture with 35 API versions (each version: markdown processing + OpenAPI-to-IR + FDR upload).
Delta is computed against the nightly baseline on main.
Baseline from nightly run(s) on main (latest: 2026-09-03T05:10:38Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-09-03 16:11 UTC

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-09-03T05:10:38Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 82s (n=5) 118s (n=5) 68s -14s (-17.1%)
go-sdk square 149s (n=5) 311s (n=5) 132s -17s (-11.4%)
java-sdk square 242s (n=5) 300s (n=5) 214s -28s (-11.6%)
php-sdk square 74s (n=5) N/A 84s +10s (+13.5%)
python-sdk square 152s (n=5) 254s (n=5) 128s -24s (-15.8%)
ruby-sdk-v2 square 92s (n=5) 142s (n=5) 85s -7s (-7.6%)
rust-sdk square 208s (n=5) 228s (n=5) 245s +37s (+17.8%)
swift-sdk square 80s (n=5) 464s (n=5) 81s +1s (+1.2%)
ts-sdk square 178s (n=5) 184s (n=5) 170s -8s (-4.5%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-09-03T05:10:38Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-09-03 16:12 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant