mirror of
https://github.com/facebook/docusaurus.git
synced 2025-12-25 17:22:50 +00:00
feat(mdx): resolve `@site/*` markdown links, fix resolution priority bugs (#11397)
Some checks failed
Argos CI / take-screenshots (push) Has been cancelled
Build Hash Router / Build Hash Router (push) Has been cancelled
Canary Release / Publish Canary (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Continuous Releases / Continuous Releases (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (18.0) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (20) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (22) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (24) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 Windows (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (node-modules, -s) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (node-modules, -st) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (pnp, -s) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (pnp, -st) (push) Has been cancelled
E2E Tests / E2E — npm (push) Has been cancelled
E2E Tests / E2E — pnpm (push) Has been cancelled
Some checks failed
Argos CI / take-screenshots (push) Has been cancelled
Build Hash Router / Build Hash Router (push) Has been cancelled
Canary Release / Publish Canary (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Continuous Releases / Continuous Releases (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (18.0) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (20) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (22) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (24) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 Windows (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (node-modules, -s) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (node-modules, -st) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (pnp, -s) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (pnp, -st) (push) Has been cancelled
E2E Tests / E2E — npm (push) Has been cancelled
E2E Tests / E2E — pnpm (push) Has been cancelled
This commit is contained in:
parent
72c48b5806
commit
3dacdf33c9
|
|
@ -75,4 +75,60 @@ describe('resolveMarkdownLinkPathname', () => {
|
|||
test('api/classes/divine_uri.URI.md', '/docs/api/classes/uri');
|
||||
test('another.md', '/docs/another');
|
||||
});
|
||||
|
||||
it('uses relative file paths in priority - Fix #11099', () => {
|
||||
// Unit test to fix bug https://github.com/facebook/docusaurus/issues/11099
|
||||
|
||||
const context: Context = {
|
||||
siteDir: '.',
|
||||
sourceFilePath: 'docs/test/file.mdx',
|
||||
contentPaths: {
|
||||
contentPath: 'docs',
|
||||
contentPathLocalized: 'i18n/docs-localized',
|
||||
},
|
||||
sourceToPermalink: new Map(
|
||||
Object.entries({
|
||||
'@site/docs/file.mdx': '/docs/file',
|
||||
'@site/docs/test/file.mdx': '/docs/test/file',
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
function test(linkPathname: string, expectedOutput: string) {
|
||||
const output = resolveMarkdownLinkPathname(linkPathname, context);
|
||||
expect(output).toEqual(expectedOutput);
|
||||
}
|
||||
|
||||
test('./file.mdx', '/docs/test/file');
|
||||
test('file.mdx', '/docs/test/file');
|
||||
});
|
||||
|
||||
it('can resolve @site/ links', () => {
|
||||
const context: Context = {
|
||||
siteDir: '.',
|
||||
sourceFilePath: 'docs/test/file.mdx',
|
||||
contentPaths: {
|
||||
contentPath: 'docs',
|
||||
contentPathLocalized: 'i18n/docs-localized',
|
||||
},
|
||||
sourceToPermalink: new Map(
|
||||
Object.entries({
|
||||
'@site/docs/file.mdx': '/docs/file',
|
||||
'@site/docs/dir with spaces/file.mdx': '/docs/dir-with-spaces/file',
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
function test(linkPathname: string, expectedOutput: string) {
|
||||
const output = resolveMarkdownLinkPathname(linkPathname, context);
|
||||
expect(output).toEqual(expectedOutput);
|
||||
}
|
||||
|
||||
test('@site/docs/file.mdx', '/docs/file');
|
||||
test('@site/docs/dir with spaces/file.mdx', '/docs/dir-with-spaces/file');
|
||||
test(
|
||||
'@site/docs/dir%20with%20spaces/file.mdx',
|
||||
'/docs/dir-with-spaces/file',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -47,9 +47,6 @@ export type SourceToPermalink = Map<
|
|||
string // Permalink: "/docs/content"
|
||||
>;
|
||||
|
||||
// Note this is historical logic extracted during a 2024 refactor
|
||||
// The algo has been kept exactly as before for retro compatibility
|
||||
// See also https://github.com/facebook/docusaurus/pull/10168
|
||||
export function resolveMarkdownLinkPathname(
|
||||
linkPathname: string,
|
||||
context: {
|
||||
|
|
@ -60,20 +57,45 @@ export function resolveMarkdownLinkPathname(
|
|||
},
|
||||
): string | null {
|
||||
const {sourceFilePath, sourceToPermalink, contentPaths, siteDir} = context;
|
||||
const sourceDirsToTry: string[] = [];
|
||||
// ./file.md and ../file.md are always relative to the current file
|
||||
if (!linkPathname.startsWith('./') && !linkPathname.startsWith('../')) {
|
||||
sourceDirsToTry.push(...getContentPathList(contentPaths), siteDir);
|
||||
}
|
||||
// /file.md is never relative to the source file path
|
||||
if (!linkPathname.startsWith('/')) {
|
||||
sourceDirsToTry.push(path.dirname(sourceFilePath));
|
||||
|
||||
// If the link is already @site aliased, there's no need to resolve it
|
||||
if (linkPathname.startsWith('@site/')) {
|
||||
return sourceToPermalink.get(decodeURIComponent(linkPathname)) ?? null;
|
||||
}
|
||||
|
||||
const aliasedSourceMatch = sourceDirsToTry
|
||||
// Get the dirs to "look into", ordered by priority, when resolving the link
|
||||
function getSourceDirsToTry() {
|
||||
// /file.md is always resolved from
|
||||
// - the plugin content paths,
|
||||
// - then siteDir
|
||||
if (linkPathname.startsWith('/')) {
|
||||
return [...getContentPathList(contentPaths), siteDir];
|
||||
}
|
||||
// ./file.md and ../file.md are always resolved from
|
||||
// - the current file dir
|
||||
else if (linkPathname.startsWith('./') || linkPathname.startsWith('../')) {
|
||||
return [path.dirname(sourceFilePath)];
|
||||
}
|
||||
// file.md is resolved from
|
||||
// - the current file dir,
|
||||
// - then from the plugin content paths,
|
||||
// - then siteDir
|
||||
else {
|
||||
return [
|
||||
path.dirname(sourceFilePath),
|
||||
...getContentPathList(contentPaths),
|
||||
siteDir,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const sourcesToTry = getSourceDirsToTry()
|
||||
.map((sourceDir) => path.join(sourceDir, decodeURIComponent(linkPathname)))
|
||||
.map((source) => aliasedSitePath(source, siteDir))
|
||||
.find((source) => sourceToPermalink.has(source));
|
||||
.map((source) => aliasedSitePath(source, siteDir));
|
||||
|
||||
const aliasedSourceMatch = sourcesToTry.find((source) =>
|
||||
sourceToPermalink.has(source),
|
||||
);
|
||||
|
||||
return aliasedSourceMatch
|
||||
? sourceToPermalink.get(aliasedSourceMatch) ?? null
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# Link resolution tests
|
||||
|
||||
## Test for issue [#11099](https://github.com/facebook/docusaurus/issues/11099)
|
||||
|
||||
These links should target the root `index.mdx` file:
|
||||
|
||||
[`/index.mdx`](/index.mdx)
|
||||
|
||||
[`@site/_dogfooding/_docs tests/index.mdx`](@site/_dogfooding/_docs%20tests/index.mdx)
|
||||
|
||||
These links should target the current `index.mdx` file:
|
||||
|
||||
[`/tests/links/index.mdx`](/tests/links/resolution/index.mdx)
|
||||
|
||||
[`@site/_dogfooding/_docs tests/tests/links/resolution/index.mdx`](@site/_dogfooding/_docs%20tests/tests/links/resolution/index.mdx)
|
||||
|
||||
[`index.mdx`](index.mdx)
|
||||
|
||||
[`./index.mdx`](./index.mdx)
|
||||
|
|
@ -29,13 +29,17 @@ Reference to another [document in a subfolder](subfolder/doc3.mdx).
|
|||
|
||||
Relative file paths are resolved against the current file's directory. Absolute file paths, on the other hand, are resolved relative to the **content root**, usually `docs/`, `blog/`, or [localized ones](../../i18n/i18n-tutorial.mdx) like `i18n/zh-Hans/plugin-content-docs/current`.
|
||||
|
||||
Absolute file paths can also be relative to the site directory. However, beware that links that begin with `/docs/` or `/blog/` are **not portable** as you would need to manually update them if you create new doc versions or localize them.
|
||||
Here are some examples of file path links and how they get resolved, assuming the current file is `website/docs/category/source.mdx`:
|
||||
|
||||
```md
|
||||
You can write [links](/otherFolder/doc4.mdx) relative to the content root (`/docs/`).
|
||||
- `[link](./target.mdx)` is resolved from the current file's directory `website/docs/category`.
|
||||
- `[link](../target.mdx)` is resolved from the parent file's directory `website/docs`.
|
||||
- `[link](/target.mdx)` is resolved from the docs content root `website/docs`, using in priority the localized docs.
|
||||
- `[link](target.mdx)` is resolved from the current directory `website/docs/category`, then from the docs content roots, then from the site root.
|
||||
|
||||
You can also write [links](/docs/otherFolder/doc4.mdx) relative to the site directory, but it's not recommended.
|
||||
```
|
||||
Absolute file paths can also be relative to the site directory. However, beware that links that begin with `/docs/`, `/blog/` or `@site/` are **not portable** as you would need to manually update them if you create new doc versions or localize them:
|
||||
|
||||
- `[link](/docs/target.mdx)` is resolved from the site root `website` (⚠️ less portable).
|
||||
- `[link](@site/docs/target.mdx)` is relative to the site root `website` (⚠️ less portable).
|
||||
|
||||
Using relative _file_ paths (with `.md` extensions) instead of relative _URL_ links provides the following benefits:
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue