Skip to content

Commit ee44848

Browse files
committed
build: fix cross document links in docusaurus
1 parent ec936ae commit ee44848

5 files changed

Lines changed: 101 additions & 8 deletions

File tree

_changelog/2.4.0.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
### Quality of Life
1919

2020
- Allow `DB_BACKUP_INTERVAL` to be set to `never`, to disable automatic database backups. This is useful for users who want to manage their own backup strategy or do not want to use the built-in backup functionality.
21-
- Adds a new `DB_BACKUP_INTERVAL_ARGS` environment variable, that works in tandem with `DB_BACKUP_INTERVAL`, to allow more fine-grained control over the database backup process. Read more in the [Dot Env documentation](/architecture/10-dot%20Env)
21+
- Adds a new `DB_BACKUP_INTERVAL_ARGS` environment variable, that works in tandem with `DB_BACKUP_INTERVAL`, to allow more fine-grained control over the database backup process. Read more in the [Dot Env documentation](../_documentation/3-architecture/10-dot%20Env.md)
2222
- Adds new `APP_TRUSTED_PROXIES` environment variable, to the app to run behind a reverse proxy with SSL termination. Read more in the [Dot Env documentation](../_documentation/3-architecture/10-dot%20Env.md#APP_TRUSTED_PROXIES)
2323
- `php artisan check:model-status` is now `php artisan ai:models:check-status`, to be more consistent with the naming of other AI related commands. The old command name is still available as an alias, but it is recommended to use the new command name.
2424

_documentation.build/docusaurus.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @ts-check
22
import {themes as prismThemes} from 'prism-react-renderer';
33
import {changelogSorter} from './changelogSorter.js';
4+
import remarkRewriteCrossDocLinks from './remarkRewriteCrossDocLinks.js';
45

56
// The "x" is there, because the docs are stored in the parent directory, which confuses docusaurus
67
// It will be automatically be stripped out by docusaurus
@@ -37,6 +38,7 @@ const config = {
3738
id: 'changelog',
3839
sidebarPath: require.resolve('./sidebars-changelog.js'),
3940
editUrl: editUrl,
41+
beforeDefaultRemarkPlugins: [remarkRewriteCrossDocLinks],
4042
async sidebarItemsGenerator({docs}) {
4143
return changelogSorter(docs, githubOrganization, githubProject);
4244
}
@@ -53,7 +55,8 @@ const config = {
5355
path: '../_documentation',
5456
routeBasePath: '/',
5557
sidebarPath: require.resolve('./sidebars-docs.js'),
56-
editUrl: editUrl
58+
editUrl: editUrl,
59+
beforeDefaultRemarkPlugins: [remarkRewriteCrossDocLinks]
5760
},
5861
theme: {
5962
customCss: require.resolve('./custom.css')

_documentation.build/package-lock.json

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

_documentation.build/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
"clsx": "^2.0.0",
2121
"prism-react-renderer": "^2.3.0",
2222
"react": "^18.0.0",
23-
"react-dom": "^18.0.0",
24-
"semver": "^7.7.3"
23+
"react-dom": "^18.0.0",
24+
"semver": "^7.7.3",
25+
"unist-util-visit": "^5.1.0"
2526
},
2627
"devDependencies": {
2728
"@docusaurus/module-type-aliases": "3.6.3",
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import {visit} from 'unist-util-visit';
2+
3+
/**
4+
* A remark plugin that rewrites cross-documentation-root links in markdown files.
5+
*
6+
* In the source markdown, authors write relative links across doc roots like:
7+
* ../_documentation/3-architecture/10-dot Env.md#ANCHOR (from _changelog)
8+
* ../_changelog/2.4.0.md (from _documentation)
9+
*
10+
* These links work on GitHub but break in Docusaurus because `_changelog` and `_documentation`
11+
* are separate docs plugin instances with different base paths.
12+
*
13+
* This plugin rewrites those links to absolute Docusaurus URLs by:
14+
* 1. Detecting `../_documentation/` or `../_changelog/` prefixes
15+
* 2. Removing the `.md` extension
16+
* 3. Stripping Docusaurus-style number prefixes (e.g. `3-` from dirs, `10-` from files)
17+
* 4. Prepending the target routeBasePath (`/` for docs, `/changelog` for changelog)
18+
*
19+
* The result is a valid absolute URL like `/architecture/dot%20Env#anchor`
20+
* or `/changelog/2.4.0`
21+
*/
22+
function remarkRewriteCrossDocLinks() {
23+
/**
24+
* Strips a Docusaurus-style number prefix from a single path segment.
25+
* E.g. "3-architecture" -> "architecture", "10-dot Env" -> "dot Env"
26+
* But preserves prefixes like "10.1-Model Config" (version-like, ignored by Docusaurus).
27+
*/
28+
function stripNumberPrefix(segment) {
29+
// Docusaurus ignores prefixes that look like versions: \d+[-_.]\d+
30+
if (/^\d+[-_.]\d+/.test(segment)) {
31+
return segment;
32+
}
33+
// Strip pattern: leading digits followed by separator(s)
34+
return segment.replace(/^\d+\s*[-_.]+\s*/, '');
35+
}
36+
37+
/**
38+
* Maps a `../<sourceDir>/` prefix to the corresponding Docusaurus routeBasePath.
39+
*/
40+
const prefixToRouteBase = {
41+
'_documentation': '', // routeBasePath: '/'
42+
'_changelog': 'changelog' // routeBasePath: 'changelog'
43+
};
44+
45+
const prefixPattern = new RegExp(
46+
'^\\.\\.\/(' + Object.keys(prefixToRouteBase).join('|') + ')\/(.*)'
47+
);
48+
49+
return (tree) => {
50+
visit(tree, 'link', (node) => {
51+
if (!node.url) return;
52+
53+
const match = node.url.match(prefixPattern);
54+
if (!match) return;
55+
56+
const sourceDir = match[1];
57+
const routeBase = prefixToRouteBase[sourceDir];
58+
let targetPath = match[2];
59+
60+
// Separate anchor from path
61+
let anchor = '';
62+
const hashIndex = targetPath.indexOf('#');
63+
if (hashIndex !== -1) {
64+
anchor = targetPath.substring(hashIndex).toLowerCase();
65+
targetPath = targetPath.substring(0, hashIndex);
66+
}
67+
68+
// Remove .md extension
69+
targetPath = targetPath.replace(/\.md$/, '');
70+
71+
// Decode URL encoding (e.g. %20 -> space) so we can process segments
72+
targetPath = decodeURIComponent(targetPath);
73+
74+
// Strip number prefixes from each path segment
75+
const segments = targetPath.split('/').map(stripNumberPrefix);
76+
77+
// Re-encode spaces and rebuild path
78+
const rewrittenPath = '/' + [routeBase, ...segments]
79+
.filter(Boolean)
80+
.map(s => encodeURIComponent(s))
81+
.join('/');
82+
83+
node.url = rewrittenPath + anchor;
84+
});
85+
};
86+
}
87+
88+
export default remarkRewriteCrossDocLinks;

0 commit comments

Comments
 (0)