Files
actual/packages/docs/docusaurus.config.js
Stephen Brown II 123cf1e25a [AI] docs: fix broken blog links and add enforce-doc-links remark plugin (#8180)
* [AI] docs: fix broken blog links and add enforce-doc-links remark plugin

- Fix three docs/ pages that linked to blog posts using URL slugs instead of
  file paths, with an extra ../ level in the relative path
- Add src/remark/enforce-doc-links.js: remark plugin that enforces link hygiene
  in docs/ and blog/ at build time (no absolute internal links, .md extension
  required, slug-style links caught via frontmatter cache)
- Upgrade onBrokenMarkdownLinks from 'warn' to 'throw' so broken .md file-path
  links fail the build rather than being silently ignored

* [autofix.ci] apply automated fixes

* [AI] docs: add vfile to spell-check allowlist

* [AI] docs: fix build failures from cross-plugin and absolute links

- Revert docs→blog links to URL-slug style (Docusaurus cannot resolve .md
  file paths across content plugins — blog is a separate plugin from docs)
- Fix absolute /docs/... links in two blog posts to relative URL form
- Fix generate-upcoming-release-notes.mjs hardcoded /docs/releases absolute
  URLs to relative ./releases.md paths
- Update enforce-doc-links plugin: exempt docs→blog URL-slug links from Rule 3
  (cross-plugin constraint means slug URLs are the required format there);
  improve Rule 1 error hint to distinguish docs vs blog context

* [AI] docs: fix blog→docs links to use absolute URLs

Blog posts are embedded in multiple Docusaurus URL contexts (individual post,
paginated list pages, tag pages), so relative URLs resolve differently per
context. Blog→docs links must use absolute URLs (/docs/...) — relative URLs
like ../docs/... incorrectly resolve to /blog/docs/... on list pages.

Update enforce-doc-links plugin to exempt blog files from Rule 1 (absolute
link check) since absolute URLs are the required format for blog→docs links.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-10 19:58:39 +00:00

228 lines
6.1 KiB
JavaScript

// @ts-check
// Note: type annotations allow type checking and IDEs autocompletion
const { themes } = require('prism-react-renderer');
const defaultOptions = {
editUrl: 'https://github.com/actualbudget/actual/tree/master/packages/docs',
beforeDefaultRemarkPlugins: [
require('./src/remark/mentions'),
require('./src/remark/enforce-doc-links'),
],
};
/** @type {import('@docusaurus/types').Config} */
module.exports = {
title: 'Actual Budget',
tagline: 'Your finances - made simple',
url: 'https://actualbudget.org/',
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenAnchors: 'throw',
favicon: 'img/favicon.ico',
projectName: 'actualbudget.github.io',
organizationName: 'actualbudget',
deploymentBranch: 'main',
// Even if you don't use internalization, you can use this field to set useful
// metadata like html lang. For example, if your site is Chinese, you may want
// to replace "en" with "zh-Hans".
i18n: {
defaultLocale: 'en',
locales: ['en'],
},
markdown: {
mermaid: true,
hooks: {
onBrokenMarkdownLinks: 'throw',
},
},
themes: ['@docusaurus/theme-mermaid'],
presets: [
[
'classic',
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
routeBasePath: 'docs',
sidebarPath: require.resolve('./docs-sidebar.js'),
...defaultOptions,
},
blog: {
...defaultOptions,
blogSidebarTitle: 'All posts',
blogSidebarCount: 'ALL',
feedOptions: {
type: 'rss',
title: 'Actual Budget Blog',
description:
'Stay updated with the latest blog posts from Actual Budget',
copyright: `Copyright © ${new Date().getFullYear()} Actual Budget. All rights reserved.`,
},
onUntruncatedBlogPosts: 'ignore',
},
theme: {
customCss: require.resolve('./src/css/custom.css'),
},
}),
],
],
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
image: 'img/og.webp',
navbar: {
title: 'Actual Budget',
logo: {
alt: 'Actual Open Source',
src: 'img/logo.webp',
},
items: [
{
to: '/#features',
// never render as active
activeBaseRegex: '^$',
label: 'Features',
position: 'left',
},
{
type: 'docSidebar',
sidebarId: 'tourSidebar',
label: 'Tour',
position: 'left',
},
{
type: 'doc',
docId: 'index',
label: 'Docs',
position: 'left',
},
{
to: 'blog',
label: 'Blog',
position: 'left',
},
{
to: '/contact',
label: 'Contact',
position: 'left',
},
{
to: '/download',
label: 'Download',
position: 'left',
},
{
href: 'https://opencollective.com/actual',
label: 'Donate',
position: 'left',
},
{
href: 'https://discord.gg/8JfAXSgfRf',
label: 'Discord',
position: 'right',
},
{
href: 'https://github.com/actualbudget/actual',
label: 'GitHub',
position: 'right',
},
],
},
footer: {
style: 'dark',
links: [
{
label: 'Discord',
href: 'https://discord.gg/8JfAXSgfRf',
},
{
label: 'GitHub',
href: 'https://github.com/actualbudget/actual',
},
{
href: 'https://opencollective.com/actual',
label: 'Donate',
},
{
label: 'Website Source',
href: 'https://github.com/actualbudget/actual/tree/master/packages/docs',
},
{
label: 'Privacy Policy',
to: '/docs/privacy-policy',
},
{
label: 'RSS Feed',
href: 'https://actualbudget.org/blog/rss.xml',
},
],
copyright: `Copyright © ${new Date().getFullYear()} Actual Budget. Built with Docusaurus.`,
},
prism: {
theme: themes.github,
darkTheme: themes.dracula,
additionalLanguages: ['nginx'],
},
colorMode: {
defaultMode: 'light',
disableSwitch: false,
respectPrefersColorScheme: true,
},
zoom: {
// See: https://github.com/timmywil/panzoom for available options
disableZoom: true,
// A list of selectors to look for elements to enable pan and zoom
selectors: [
'div.mermaid[data-processed="true"]:not(.panzoom-exclude *)',
'div.docusaurus-mermaid-container:not(.panzoom-exclude *)',
'.drawio',
'.panzoom-example',
],
// Whether to wrap the panzoom items in a div with overflow:hidden
// This constrains the pan zoom detail into the original container
wrap: true,
// The amount of time to wait in MS before the plugin client module tries to look for
// and alter pan zoom elements. Some renders take a little bit before they appear in the
// dom to find.
timeout: 2000,
excludeClass: 'panzoom-exclude',
toolbar: {
enabled: true,
},
},
}),
plugins: [
[
'@docusaurus/plugin-ideal-image',
{
quality: 70,
max: 1030, // max resized image's size.
min: 640, // min resized image's size. if original is lower, use that size.
steps: 2, // the max number of images generated between min and max (inclusive)
disableInDev: false,
},
],
[
require.resolve('@easyops-cn/docusaurus-search-local'),
/** @type {import("@easyops-cn/docusaurus-search-local").PluginOptions} */
({
hashed: true,
indexDocs: true,
indexPages: false,
language: 'en',
}),
],
'@r74tech/docusaurus-plugin-panzoom',
],
};