mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-23 05:57:05 +02:00
fix(mdx-loader): resolve Markdown/MDX links with Remark instead of RegExp (#10168)
This commit is contained in:
parent
aab332c2ae
commit
e34614963e
36 changed files with 902 additions and 1620 deletions
|
@ -17,6 +17,7 @@ import {
|
|||
addTrailingPathSeparator,
|
||||
createAbsoluteFilePathMatcher,
|
||||
createSlugger,
|
||||
resolveMarkdownLinkPathname,
|
||||
DEFAULT_PLUGIN_ID,
|
||||
} from '@docusaurus/utils';
|
||||
import {loadSidebars, resolveSidebarPathOption} from './sidebars';
|
||||
|
@ -28,7 +29,11 @@ import {
|
|||
type DocEnv,
|
||||
createDocsByIdIndex,
|
||||
} from './docs';
|
||||
import {readVersionsMetadata, toFullVersion} from './versions';
|
||||
import {
|
||||
getVersionFromSourceFilePath,
|
||||
readVersionsMetadata,
|
||||
toFullVersion,
|
||||
} from './versions';
|
||||
import {cliDocsVersionCommand} from './cli';
|
||||
import {VERSIONS_JSON_FILE} from './constants';
|
||||
import {toGlobalDataVersion} from './globalData';
|
||||
|
@ -38,6 +43,7 @@ import {
|
|||
} from './translations';
|
||||
import {createAllRoutes} from './routes';
|
||||
import {createSidebarsUtils} from './sidebars/utils';
|
||||
import type {Options as MDXLoaderOptions} from '@docusaurus/mdx-loader';
|
||||
|
||||
import type {
|
||||
PluginOptions,
|
||||
|
@ -48,13 +54,8 @@ import type {
|
|||
LoadedVersion,
|
||||
} from '@docusaurus/plugin-content-docs';
|
||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||
import type {
|
||||
SourceToPermalink,
|
||||
DocFile,
|
||||
DocsMarkdownOption,
|
||||
FullVersion,
|
||||
} from './types';
|
||||
import type {RuleSetRule} from 'webpack';
|
||||
import type {SourceToPermalink, DocFile, FullVersion} from './types';
|
||||
import type {RuleSetUseItem} from 'webpack';
|
||||
|
||||
export default async function pluginContentDocs(
|
||||
context: LoadContext,
|
||||
|
@ -251,72 +252,71 @@ export default async function pluginContentDocs(
|
|||
beforeDefaultRemarkPlugins,
|
||||
} = options;
|
||||
|
||||
const contentDirs = versionsMetadata
|
||||
.flatMap(getContentPathList)
|
||||
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
||||
.map(addTrailingPathSeparator);
|
||||
|
||||
// TODO this does not re-run when content gets updated in dev!
|
||||
// it's probably better to restore a mutable cache in the plugin
|
||||
function getSourceToPermalink(): SourceToPermalink {
|
||||
const allDocs = content.loadedVersions.flatMap((v) => v.docs);
|
||||
return Object.fromEntries(
|
||||
allDocs.map(({source, permalink}) => [source, permalink]),
|
||||
);
|
||||
}
|
||||
const sourceToPermalink = getSourceToPermalink();
|
||||
|
||||
const docsMarkdownOptions: DocsMarkdownOption = {
|
||||
siteDir,
|
||||
sourceToPermalink: getSourceToPermalink(),
|
||||
versionsMetadata,
|
||||
onBrokenMarkdownLink: (brokenMarkdownLink) => {
|
||||
logger.report(
|
||||
siteConfig.onBrokenMarkdownLinks,
|
||||
)`Docs markdown link couldn't be resolved: (url=${brokenMarkdownLink.link}) in path=${brokenMarkdownLink.filePath} for version number=${brokenMarkdownLink.contentPaths.versionName}`;
|
||||
},
|
||||
};
|
||||
function createMDXLoader(): RuleSetUseItem {
|
||||
const loaderOptions: MDXLoaderOptions = {
|
||||
admonitions: options.admonitions,
|
||||
remarkPlugins,
|
||||
rehypePlugins,
|
||||
beforeDefaultRehypePlugins,
|
||||
beforeDefaultRemarkPlugins,
|
||||
staticDirs: siteConfig.staticDirectories.map((dir) =>
|
||||
path.resolve(siteDir, dir),
|
||||
),
|
||||
siteDir,
|
||||
isMDXPartial: createAbsoluteFilePathMatcher(
|
||||
options.exclude,
|
||||
contentDirs,
|
||||
),
|
||||
metadataPath: (mdxPath: string) => {
|
||||
// Note that metadataPath must be the same/in-sync as
|
||||
// the path from createData for each MDX.
|
||||
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
||||
return path.join(dataDir, `${docuHash(aliasedPath)}.json`);
|
||||
},
|
||||
// Assets allow to convert some relative images paths to
|
||||
// require(...) calls
|
||||
createAssets: ({frontMatter}: {frontMatter: DocFrontMatter}) => ({
|
||||
image: frontMatter.image,
|
||||
}),
|
||||
markdownConfig: siteConfig.markdown,
|
||||
resolveMarkdownLink: ({linkPathname, sourceFilePath}) => {
|
||||
const version = getVersionFromSourceFilePath(
|
||||
sourceFilePath,
|
||||
content.loadedVersions,
|
||||
);
|
||||
const permalink = resolveMarkdownLinkPathname(linkPathname, {
|
||||
sourceFilePath,
|
||||
sourceToPermalink,
|
||||
siteDir,
|
||||
contentPaths: version,
|
||||
});
|
||||
if (permalink === null) {
|
||||
logger.report(
|
||||
siteConfig.onBrokenMarkdownLinks,
|
||||
)`Docs markdown link couldn't be resolved: (url=${linkPathname}) in source file path=${sourceFilePath} for version number=${version.versionName}`;
|
||||
}
|
||||
return permalink;
|
||||
},
|
||||
};
|
||||
|
||||
function createMDXLoaderRule(): RuleSetRule {
|
||||
const contentDirs = versionsMetadata
|
||||
.flatMap(getContentPathList)
|
||||
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
||||
.map(addTrailingPathSeparator);
|
||||
return {
|
||||
test: /\.mdx?$/i,
|
||||
include: contentDirs,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('@docusaurus/mdx-loader'),
|
||||
options: {
|
||||
admonitions: options.admonitions,
|
||||
remarkPlugins,
|
||||
rehypePlugins,
|
||||
beforeDefaultRehypePlugins,
|
||||
beforeDefaultRemarkPlugins,
|
||||
staticDirs: siteConfig.staticDirectories.map((dir) =>
|
||||
path.resolve(siteDir, dir),
|
||||
),
|
||||
siteDir,
|
||||
isMDXPartial: createAbsoluteFilePathMatcher(
|
||||
options.exclude,
|
||||
contentDirs,
|
||||
),
|
||||
metadataPath: (mdxPath: string) => {
|
||||
// Note that metadataPath must be the same/in-sync as
|
||||
// the path from createData for each MDX.
|
||||
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
||||
return path.join(dataDir, `${docuHash(aliasedPath)}.json`);
|
||||
},
|
||||
// Assets allow to convert some relative images paths to
|
||||
// require(...) calls
|
||||
createAssets: ({
|
||||
frontMatter,
|
||||
}: {
|
||||
frontMatter: DocFrontMatter;
|
||||
}) => ({
|
||||
image: frontMatter.image,
|
||||
}),
|
||||
markdownConfig: siteConfig.markdown,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: path.resolve(__dirname, './markdown/index.js'),
|
||||
options: docsMarkdownOptions,
|
||||
},
|
||||
].filter(Boolean),
|
||||
loader: require.resolve('@docusaurus/mdx-loader'),
|
||||
options: loaderOptions,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -333,7 +333,13 @@ export default async function pluginContentDocs(
|
|||
},
|
||||
},
|
||||
module: {
|
||||
rules: [createMDXLoaderRule()],
|
||||
rules: [
|
||||
{
|
||||
test: /\.mdx?$/i,
|
||||
include: contentDirs,
|
||||
use: [createMDXLoader()],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
### localized doc
|
|
@ -1,13 +0,0 @@
|
|||
# Don't transform any link here
|
||||
|
||||

|
||||
|
||||
# Don't replace inside fenced codeblock
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
### Non-existing Docs
|
||||
|
||||
- [hahaha](hahaha.md)
|
|
@ -1,12 +0,0 @@
|
|||
### Existing Docs
|
||||
|
||||
- [doc1](doc1.md)
|
||||
- [doc2](./doc2.md)
|
||||
- [doc3](subdir/doc3.md)
|
||||
|
||||
## Repeating Docs
|
||||
|
||||
- [doc1](doc1.md)
|
||||
- [doc2](./doc2.md)
|
||||
|
||||
- [doc-localized](/doc-localized.md)
|
|
@ -1,19 +0,0 @@
|
|||
### Existing Docs
|
||||
|
||||
- [doc1][doc1]
|
||||
- [doc2][doc2]
|
||||
|
||||
## Repeating Docs
|
||||
|
||||
- [doc1][doc1]
|
||||
- [doc2][doc2]
|
||||
|
||||
## Do not replace this
|
||||
|
||||
```md
|
||||
![image1][image1]
|
||||
```
|
||||
|
||||
[doc1]: doc1.md
|
||||
[doc2]: ./doc2.md
|
||||
[image1]: assets/image1.png
|
|
@ -1,6 +0,0 @@
|
|||
### Not Existing Docs
|
||||
|
||||
- [docNotExist1](docNotExist1.md)
|
||||
- [docNotExist2](./docNotExist2.mdx)
|
||||
- [docNotExist3](../docNotExist3.mdx)
|
||||
- [docNotExist4](./subdir/docNotExist4.md)
|
|
@ -1,3 +0,0 @@
|
|||
### Relative linking
|
||||
|
||||
- [doc1](../doc2.md)
|
|
@ -1 +0,0 @@
|
|||
[link](../docs/doc1.md)
|
|
@ -1,7 +0,0 @@
|
|||
### Existing Docs
|
||||
|
||||
- [doc1](subdir/doc1.md)
|
||||
|
||||
### With hash
|
||||
|
||||
- [doc2](doc2.md#existing-docs)
|
|
@ -1,3 +0,0 @@
|
|||
### Relative linking
|
||||
|
||||
- [doc1](../doc2.md)
|
|
@ -1,82 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`linkify transforms absolute links in versioned docs 1`] = `
|
||||
"### Existing Docs
|
||||
|
||||
- [doc1](/docs/1.0.0/subdir/doc1)
|
||||
|
||||
### With hash
|
||||
|
||||
- [doc2](/docs/1.0.0/doc2#existing-docs)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`linkify transforms nothing with no links 1`] = `
|
||||
"# Don't transform any link here
|
||||
|
||||

|
||||
|
||||
# Don't replace inside fenced codeblock
|
||||
|
||||
\`\`\`md
|
||||

|
||||
\`\`\`
|
||||
|
||||
### Non-existing Docs
|
||||
|
||||
- [hahaha](hahaha.md)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`linkify transforms reference links 1`] = `
|
||||
"### Existing Docs
|
||||
|
||||
- [doc1][doc1]
|
||||
- [doc2][doc2]
|
||||
|
||||
## Repeating Docs
|
||||
|
||||
- [doc1][doc1]
|
||||
- [doc2][doc2]
|
||||
|
||||
## Do not replace this
|
||||
|
||||
\`\`\`md
|
||||
![image1][image1]
|
||||
\`\`\`
|
||||
|
||||
[doc1]: /docs/doc1
|
||||
[doc2]: /docs/doc2
|
||||
[image1]: assets/image1.png
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`linkify transforms relative links 1`] = `
|
||||
"### Relative linking
|
||||
|
||||
- [doc1](/docs/doc2)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`linkify transforms relative links in versioned docs 1`] = `
|
||||
"### Relative linking
|
||||
|
||||
- [doc1](/docs/1.0.0/doc2)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`linkify transforms to correct links 1`] = `
|
||||
"### Existing Docs
|
||||
|
||||
- [doc1](/docs/doc1)
|
||||
- [doc2](/docs/doc2)
|
||||
- [doc3](/docs/subdir/doc3)
|
||||
|
||||
## Repeating Docs
|
||||
|
||||
- [doc1](/docs/doc1)
|
||||
- [doc2](/docs/doc2)
|
||||
|
||||
- [doc-localized](/fr/doc-localized)
|
||||
"
|
||||
`;
|
|
@ -1,210 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {jest} from '@jest/globals';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import {linkify} from '../linkify';
|
||||
import {VERSIONED_DOCS_DIR, CURRENT_VERSION_NAME} from '../../constants';
|
||||
import type {
|
||||
DocsMarkdownOption,
|
||||
SourceToPermalink,
|
||||
DocBrokenMarkdownLink,
|
||||
} from '../../types';
|
||||
import type {VersionMetadata} from '@docusaurus/plugin-content-docs';
|
||||
|
||||
function createFakeVersion({
|
||||
versionName,
|
||||
contentPath,
|
||||
contentPathLocalized,
|
||||
}: {
|
||||
versionName: string;
|
||||
contentPath: string;
|
||||
contentPathLocalized: string;
|
||||
}): VersionMetadata {
|
||||
return {
|
||||
versionName,
|
||||
label: 'Any',
|
||||
path: 'any',
|
||||
badge: true,
|
||||
banner: null,
|
||||
tagsPath: '/tags/',
|
||||
className: '',
|
||||
contentPath,
|
||||
contentPathLocalized,
|
||||
sidebarFilePath: 'any',
|
||||
routePriority: undefined,
|
||||
isLast: false,
|
||||
};
|
||||
}
|
||||
|
||||
const siteDir = path.join(__dirname, '__fixtures__');
|
||||
|
||||
const versionCurrent = createFakeVersion({
|
||||
versionName: CURRENT_VERSION_NAME,
|
||||
contentPath: path.join(siteDir, 'docs'),
|
||||
contentPathLocalized: path.join(
|
||||
siteDir,
|
||||
'i18n',
|
||||
'fr',
|
||||
'docusaurus-plugin-content-docs',
|
||||
CURRENT_VERSION_NAME,
|
||||
),
|
||||
});
|
||||
|
||||
const version100 = createFakeVersion({
|
||||
versionName: '1.0.0',
|
||||
contentPath: path.join(siteDir, VERSIONED_DOCS_DIR, 'version-1.0.0'),
|
||||
contentPathLocalized: path.join(
|
||||
siteDir,
|
||||
'i18n',
|
||||
'fr',
|
||||
'docusaurus-plugin-content-docs',
|
||||
'version-1.0.0',
|
||||
),
|
||||
});
|
||||
|
||||
const sourceToPermalink: SourceToPermalink = {
|
||||
'@site/docs/doc1.md': '/docs/doc1',
|
||||
'@site/docs/doc2.md': '/docs/doc2',
|
||||
'@site/docs/subdir/doc3.md': '/docs/subdir/doc3',
|
||||
'@site/docs/doc4.md': '/docs/doc4',
|
||||
'@site/versioned_docs/version-1.0.0/doc2.md': '/docs/1.0.0/doc2',
|
||||
'@site/versioned_docs/version-1.0.0/subdir/doc1.md':
|
||||
'/docs/1.0.0/subdir/doc1',
|
||||
|
||||
'@site/i18n/fr/docusaurus-plugin-content-docs/current/doc-localized.md':
|
||||
'/fr/doc-localized',
|
||||
'@site/docs/doc-localized.md': '/doc-localized',
|
||||
};
|
||||
|
||||
function createMarkdownOptions(
|
||||
options?: Partial<DocsMarkdownOption>,
|
||||
): DocsMarkdownOption {
|
||||
return {
|
||||
sourceToPermalink,
|
||||
onBrokenMarkdownLink: () => {},
|
||||
versionsMetadata: [versionCurrent, version100],
|
||||
siteDir,
|
||||
...options,
|
||||
};
|
||||
}
|
||||
|
||||
const transform = async (
|
||||
filepath: string,
|
||||
options?: Partial<DocsMarkdownOption>,
|
||||
) => {
|
||||
const markdownOptions = createMarkdownOptions(options);
|
||||
const content = await fs.readFile(filepath, 'utf-8');
|
||||
const transformedContent = linkify(content, filepath, markdownOptions);
|
||||
return [content, transformedContent];
|
||||
};
|
||||
|
||||
describe('linkify', () => {
|
||||
it('transforms nothing with no links', async () => {
|
||||
const doc1 = path.join(versionCurrent.contentPath, 'doc1.md');
|
||||
const [content, transformedContent] = await transform(doc1);
|
||||
expect(transformedContent).toMatchSnapshot();
|
||||
expect(content).toEqual(transformedContent);
|
||||
});
|
||||
|
||||
it('transforms to correct links', async () => {
|
||||
const doc2 = path.join(versionCurrent.contentPath, 'doc2.md');
|
||||
const [content, transformedContent] = await transform(doc2);
|
||||
expect(transformedContent).toMatchSnapshot();
|
||||
expect(transformedContent).toContain('](/docs/doc1');
|
||||
expect(transformedContent).toContain('](/docs/doc2');
|
||||
expect(transformedContent).toContain('](/docs/subdir/doc3');
|
||||
expect(transformedContent).toContain('](/fr/doc-localized');
|
||||
expect(transformedContent).not.toContain('](doc1.md)');
|
||||
expect(transformedContent).not.toContain('](./doc2.md)');
|
||||
expect(transformedContent).not.toContain('](subdir/doc3.md)');
|
||||
expect(transformedContent).not.toContain('](/doc-localized');
|
||||
expect(content).not.toEqual(transformedContent);
|
||||
});
|
||||
|
||||
it('transforms relative links', async () => {
|
||||
const doc3 = path.join(versionCurrent.contentPath, 'subdir', 'doc3.md');
|
||||
|
||||
const [content, transformedContent] = await transform(doc3);
|
||||
expect(transformedContent).toMatchSnapshot();
|
||||
expect(transformedContent).toContain('](/docs/doc2');
|
||||
expect(transformedContent).not.toContain('](../doc2.md)');
|
||||
expect(content).not.toEqual(transformedContent);
|
||||
});
|
||||
|
||||
it('transforms reference links', async () => {
|
||||
const doc4 = path.join(versionCurrent.contentPath, 'doc4.md');
|
||||
const [content, transformedContent] = await transform(doc4);
|
||||
expect(transformedContent).toMatchSnapshot();
|
||||
expect(transformedContent).toContain('[doc1]: /docs/doc1');
|
||||
expect(transformedContent).toContain('[doc2]: /docs/doc2');
|
||||
expect(transformedContent).not.toContain('[doc1]: doc1.md');
|
||||
expect(transformedContent).not.toContain('[doc2]: ./doc2.md');
|
||||
expect(content).not.toEqual(transformedContent);
|
||||
});
|
||||
|
||||
it('reports broken markdown links', async () => {
|
||||
const doc5 = path.join(versionCurrent.contentPath, 'doc5.md');
|
||||
const onBrokenMarkdownLink = jest.fn();
|
||||
const [content, transformedContent] = await transform(doc5, {
|
||||
onBrokenMarkdownLink,
|
||||
});
|
||||
expect(transformedContent).toEqual(content);
|
||||
expect(onBrokenMarkdownLink).toHaveBeenCalledTimes(4);
|
||||
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(1, {
|
||||
filePath: doc5,
|
||||
link: 'docNotExist1.md',
|
||||
contentPaths: versionCurrent,
|
||||
} as DocBrokenMarkdownLink);
|
||||
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(2, {
|
||||
filePath: doc5,
|
||||
link: './docNotExist2.mdx',
|
||||
contentPaths: versionCurrent,
|
||||
} as DocBrokenMarkdownLink);
|
||||
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(3, {
|
||||
filePath: doc5,
|
||||
link: '../docNotExist3.mdx',
|
||||
contentPaths: versionCurrent,
|
||||
} as DocBrokenMarkdownLink);
|
||||
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(4, {
|
||||
filePath: doc5,
|
||||
link: './subdir/docNotExist4.md',
|
||||
contentPaths: versionCurrent,
|
||||
} as DocBrokenMarkdownLink);
|
||||
});
|
||||
|
||||
it('transforms absolute links in versioned docs', async () => {
|
||||
const doc2 = path.join(version100.contentPath, 'doc2.md');
|
||||
const [content, transformedContent] = await transform(doc2);
|
||||
expect(transformedContent).toMatchSnapshot();
|
||||
expect(transformedContent).toContain('](/docs/1.0.0/subdir/doc1');
|
||||
expect(transformedContent).toContain('](/docs/1.0.0/doc2#existing-docs');
|
||||
expect(transformedContent).not.toContain('](subdir/doc1.md)');
|
||||
expect(transformedContent).not.toContain('](doc2.md#existing-docs)');
|
||||
expect(content).not.toEqual(transformedContent);
|
||||
});
|
||||
|
||||
it('transforms relative links in versioned docs', async () => {
|
||||
const doc1 = path.join(version100.contentPath, 'subdir', 'doc1.md');
|
||||
const [content, transformedContent] = await transform(doc1);
|
||||
expect(transformedContent).toMatchSnapshot();
|
||||
expect(transformedContent).toContain('](/docs/1.0.0/doc2');
|
||||
expect(transformedContent).not.toContain('](../doc2.md)');
|
||||
expect(content).not.toEqual(transformedContent);
|
||||
});
|
||||
|
||||
// See comment in linkify.ts
|
||||
it('throws for file outside version', async () => {
|
||||
const doc1 = path.join(__dirname, '__fixtures__/outside/doc1.md');
|
||||
await expect(() =>
|
||||
transform(doc1),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Unexpected error: Markdown file at "<PROJECT_ROOT>/packages/docusaurus-plugin-content-docs/src/markdown/__tests__/__fixtures__/outside/doc1.md" does not belong to any docs version!"`,
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {linkify} from './linkify';
|
||||
import type {DocsMarkdownOption} from '../types';
|
||||
import type {LoaderContext} from 'webpack';
|
||||
|
||||
export default function markdownLoader(
|
||||
this: LoaderContext<DocsMarkdownOption>,
|
||||
source: string,
|
||||
): void {
|
||||
const fileString = source;
|
||||
const callback = this.async();
|
||||
const options = this.getOptions();
|
||||
return callback(null, linkify(fileString, this.resourcePath, options));
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {replaceMarkdownLinks, getContentPathList} from '@docusaurus/utils';
|
||||
import type {DocsMarkdownOption} from '../types';
|
||||
|
||||
function getVersion(filePath: string, options: DocsMarkdownOption) {
|
||||
const versionFound = options.versionsMetadata.find((version) =>
|
||||
getContentPathList(version).some((docsDirPath) =>
|
||||
filePath.startsWith(docsDirPath),
|
||||
),
|
||||
);
|
||||
// At this point, this should never happen, because the MDX loaders' paths are
|
||||
// literally using the version content paths; but if we allow sourcing content
|
||||
// from outside the docs directory (through the `include` option, for example;
|
||||
// is there a compelling use-case?), this would actually be testable
|
||||
if (!versionFound) {
|
||||
throw new Error(
|
||||
`Unexpected error: Markdown file at "${filePath}" does not belong to any docs version!`,
|
||||
);
|
||||
}
|
||||
return versionFound;
|
||||
}
|
||||
|
||||
export function linkify(
|
||||
fileString: string,
|
||||
filePath: string,
|
||||
options: DocsMarkdownOption,
|
||||
): string {
|
||||
const {siteDir, sourceToPermalink, onBrokenMarkdownLink} = options;
|
||||
|
||||
const {newContent, brokenMarkdownLinks} = replaceMarkdownLinks({
|
||||
siteDir,
|
||||
fileString,
|
||||
filePath,
|
||||
contentPaths: getVersion(filePath, options),
|
||||
sourceToPermalink,
|
||||
});
|
||||
|
||||
brokenMarkdownLinks.forEach((l) => onBrokenMarkdownLink(l));
|
||||
|
||||
return newContent;
|
||||
}
|
|
@ -5,9 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type {BrokenMarkdownLink, Tag} from '@docusaurus/utils';
|
||||
import type {Tag} from '@docusaurus/utils';
|
||||
import type {
|
||||
VersionMetadata,
|
||||
LoadedVersion,
|
||||
CategoryGeneratedIndexMetadata,
|
||||
} from '@docusaurus/plugin-content-docs';
|
||||
|
@ -37,12 +36,3 @@ export type FullVersion = LoadedVersion & {
|
|||
sidebarsUtils: SidebarsUtils;
|
||||
categoryGeneratedIndices: CategoryGeneratedIndexMetadata[];
|
||||
};
|
||||
|
||||
export type DocBrokenMarkdownLink = BrokenMarkdownLink<VersionMetadata>;
|
||||
|
||||
export type DocsMarkdownOption = {
|
||||
versionsMetadata: VersionMetadata[];
|
||||
siteDir: string;
|
||||
sourceToPermalink: SourceToPermalink;
|
||||
onBrokenMarkdownLink: (brokenMarkdownLink: DocBrokenMarkdownLink) => void;
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import path from 'path';
|
||||
import {normalizeUrl, posixPath} from '@docusaurus/utils';
|
||||
import {getContentPathList, normalizeUrl, posixPath} from '@docusaurus/utils';
|
||||
import {CURRENT_VERSION_NAME} from '../constants';
|
||||
import {validateVersionsOptions} from './validation';
|
||||
import {
|
||||
|
@ -268,3 +268,20 @@ export function toFullVersion(version: LoadedVersion): FullVersion {
|
|||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function getVersionFromSourceFilePath(
|
||||
filePath: string,
|
||||
versionsMetadata: VersionMetadata[],
|
||||
): VersionMetadata {
|
||||
const versionFound = versionsMetadata.find((version) =>
|
||||
getContentPathList(version).some((docsDirPath) =>
|
||||
filePath.startsWith(docsDirPath),
|
||||
),
|
||||
);
|
||||
if (!versionFound) {
|
||||
throw new Error(
|
||||
`Unexpected error: file at "${filePath}" does not belong to any docs version!`,
|
||||
);
|
||||
}
|
||||
return versionFound;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue