mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-02 19:57:25 +02:00
refactor(v2): merge linkify function used in blog and docs and align properties (#4402)
* refactor(v2): merge linkify function used in blog and docs * refactor(v2): rename docsDirPath and docsDirPathLocalized ad update types * refactor(v2): rename blogPostsBySource and update types * improve replaceMarkdownLinks api Co-authored-by: slorber <lorber.sebastien@gmail.com>
This commit is contained in:
parent
bfe52cdae3
commit
2f53b1a895
17 changed files with 240 additions and 237 deletions
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {linkify, LinkifyParams, getPostsBySource} from '../blogUtils';
|
import {linkify, LinkifyParams, getSourceToPermalink} from '../blogUtils';
|
||||||
import {BlogBrokenMarkdownLink, BlogContentPaths, BlogPost} from '../types';
|
import {BlogBrokenMarkdownLink, BlogContentPaths, BlogPost} from '../types';
|
||||||
|
|
||||||
const siteDir = path.join(__dirname, '__fixtures__', 'website');
|
const siteDir = path.join(__dirname, '__fixtures__', 'website');
|
||||||
|
@ -43,10 +43,10 @@ const transform = (filePath: string, options?: Partial<LinkifyParams>) => {
|
||||||
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
||||||
const transformedContent = linkify({
|
const transformedContent = linkify({
|
||||||
filePath,
|
filePath,
|
||||||
fileContent,
|
fileString: fileContent,
|
||||||
siteDir,
|
siteDir,
|
||||||
contentPaths,
|
contentPaths,
|
||||||
blogPostsBySource: getPostsBySource(blogPosts),
|
sourceToPermalink: getSourceToPermalink(blogPosts),
|
||||||
onBrokenMarkdownLink: (brokenMarkdownLink) => {
|
onBrokenMarkdownLink: (brokenMarkdownLink) => {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Broken markdown link found: ${JSON.stringify(brokenMarkdownLink)}`,
|
`Broken markdown link found: ${JSON.stringify(brokenMarkdownLink)}`,
|
||||||
|
@ -82,12 +82,12 @@ test('report broken markdown links', () => {
|
||||||
expect(onBrokenMarkdownLink).toHaveBeenCalledTimes(2);
|
expect(onBrokenMarkdownLink).toHaveBeenCalledTimes(2);
|
||||||
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(1, {
|
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(1, {
|
||||||
filePath: path.resolve(folderPath, filePath),
|
filePath: path.resolve(folderPath, filePath),
|
||||||
folderPath,
|
contentPaths,
|
||||||
link: 'postNotExist1.md',
|
link: 'postNotExist1.md',
|
||||||
} as BlogBrokenMarkdownLink);
|
} as BlogBrokenMarkdownLink);
|
||||||
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(2, {
|
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(2, {
|
||||||
filePath: path.resolve(folderPath, filePath),
|
filePath: path.resolve(folderPath, filePath),
|
||||||
folderPath,
|
contentPaths,
|
||||||
link: './postNotExist2.mdx',
|
link: './postNotExist2.mdx',
|
||||||
} as BlogBrokenMarkdownLink);
|
} as BlogBrokenMarkdownLink);
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,16 +9,14 @@ import fs from 'fs-extra';
|
||||||
import globby from 'globby';
|
import globby from 'globby';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {resolve} from 'url';
|
|
||||||
import readingTime from 'reading-time';
|
import readingTime from 'reading-time';
|
||||||
import {Feed} from 'feed';
|
import {Feed} from 'feed';
|
||||||
import {keyBy} from 'lodash';
|
import {keyBy, mapValues} from 'lodash';
|
||||||
import {
|
import {
|
||||||
PluginOptions,
|
PluginOptions,
|
||||||
BlogPost,
|
BlogPost,
|
||||||
DateLink,
|
DateLink,
|
||||||
BlogContentPaths,
|
BlogContentPaths,
|
||||||
BlogBrokenMarkdownLink,
|
|
||||||
BlogMarkdownLoaderOptions,
|
BlogMarkdownLoaderOptions,
|
||||||
} from './types';
|
} from './types';
|
||||||
import {
|
import {
|
||||||
|
@ -31,15 +29,19 @@ import {
|
||||||
getDateTimeFormat,
|
getDateTimeFormat,
|
||||||
} from '@docusaurus/utils';
|
} from '@docusaurus/utils';
|
||||||
import {LoadContext} from '@docusaurus/types';
|
import {LoadContext} from '@docusaurus/types';
|
||||||
|
import {replaceMarkdownLinks} from '@docusaurus/utils/lib/markdownLinks';
|
||||||
|
|
||||||
export function truncate(fileString: string, truncateMarker: RegExp): string {
|
export function truncate(fileString: string, truncateMarker: RegExp): string {
|
||||||
return fileString.split(truncateMarker, 1).shift()!;
|
return fileString.split(truncateMarker, 1).shift()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPostsBySource(
|
export function getSourceToPermalink(
|
||||||
blogPosts: BlogPost[],
|
blogPosts: BlogPost[],
|
||||||
): Record<string, BlogPost> {
|
): Record<string, string> {
|
||||||
return keyBy(blogPosts, (item) => item.metadata.source);
|
return mapValues(
|
||||||
|
keyBy(blogPosts, (item) => item.metadata.source),
|
||||||
|
(v) => v.metadata.permalink,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// YYYY-MM-DD-{name}.mdx?
|
// YYYY-MM-DD-{name}.mdx?
|
||||||
|
@ -250,73 +252,31 @@ export async function generateBlogPosts(
|
||||||
|
|
||||||
export type LinkifyParams = {
|
export type LinkifyParams = {
|
||||||
filePath: string;
|
filePath: string;
|
||||||
fileContent: string;
|
fileString: string;
|
||||||
} & Pick<
|
} & Pick<
|
||||||
BlogMarkdownLoaderOptions,
|
BlogMarkdownLoaderOptions,
|
||||||
'blogPostsBySource' | 'siteDir' | 'contentPaths' | 'onBrokenMarkdownLink'
|
'sourceToPermalink' | 'siteDir' | 'contentPaths' | 'onBrokenMarkdownLink'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export function linkify({
|
export function linkify({
|
||||||
filePath,
|
filePath,
|
||||||
contentPaths,
|
contentPaths,
|
||||||
fileContent,
|
fileString,
|
||||||
siteDir,
|
siteDir,
|
||||||
blogPostsBySource,
|
sourceToPermalink,
|
||||||
onBrokenMarkdownLink,
|
onBrokenMarkdownLink,
|
||||||
}: LinkifyParams): string {
|
}: LinkifyParams): string {
|
||||||
// TODO temporary, should consider the file being in localized folder!
|
const {newContent, brokenMarkdownLinks} = replaceMarkdownLinks({
|
||||||
const folderPath = contentPaths.contentPath;
|
siteDir,
|
||||||
|
fileString,
|
||||||
let fencedBlock = false;
|
|
||||||
const lines = fileContent.split('\n').map((line) => {
|
|
||||||
if (line.trim().startsWith('```')) {
|
|
||||||
fencedBlock = !fencedBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fencedBlock) {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
let modifiedLine = line;
|
|
||||||
const mdRegex = /(?:(?:\]\()|(?:\]:\s?))(?!https)([^'")\]\s>]+\.mdx?)/g;
|
|
||||||
let mdMatch = mdRegex.exec(modifiedLine);
|
|
||||||
|
|
||||||
while (mdMatch !== null) {
|
|
||||||
const mdLink = mdMatch[1];
|
|
||||||
|
|
||||||
const aliasedSource = (source: string) =>
|
|
||||||
aliasedSitePath(source, siteDir);
|
|
||||||
|
|
||||||
const blogPost: BlogPost | undefined =
|
|
||||||
blogPostsBySource[aliasedSource(resolve(filePath, mdLink))] ||
|
|
||||||
blogPostsBySource[
|
|
||||||
aliasedSource(`${contentPaths.contentPathLocalized}/${mdLink}`)
|
|
||||||
] ||
|
|
||||||
blogPostsBySource[
|
|
||||||
aliasedSource(`${contentPaths.contentPath}/${mdLink}`)
|
|
||||||
];
|
|
||||||
|
|
||||||
if (blogPost) {
|
|
||||||
modifiedLine = modifiedLine.replace(
|
|
||||||
mdLink,
|
|
||||||
blogPost.metadata.permalink,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const brokenMarkdownLink: BlogBrokenMarkdownLink = {
|
|
||||||
folderPath,
|
|
||||||
filePath,
|
filePath,
|
||||||
link: mdLink,
|
contentPaths,
|
||||||
};
|
sourceToPermalink,
|
||||||
onBrokenMarkdownLink(brokenMarkdownLink);
|
|
||||||
}
|
|
||||||
|
|
||||||
mdMatch = mdRegex.exec(modifiedLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
return modifiedLine;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return lines.join('\n');
|
brokenMarkdownLinks.forEach((l) => onBrokenMarkdownLink(l));
|
||||||
|
|
||||||
|
return newContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Order matters: we look in priority in localized folder
|
// Order matters: we look in priority in localized folder
|
||||||
|
|
|
@ -50,7 +50,7 @@ import {
|
||||||
generateBlogFeed,
|
generateBlogFeed,
|
||||||
generateBlogPosts,
|
generateBlogPosts,
|
||||||
getContentPathList,
|
getContentPathList,
|
||||||
getPostsBySource,
|
getSourceToPermalink,
|
||||||
} from './blogUtils';
|
} from './blogUtils';
|
||||||
|
|
||||||
export default function pluginContentBlog(
|
export default function pluginContentBlog(
|
||||||
|
@ -416,7 +416,7 @@ export default function pluginContentBlog(
|
||||||
siteDir,
|
siteDir,
|
||||||
contentPaths,
|
contentPaths,
|
||||||
truncateMarker,
|
truncateMarker,
|
||||||
blogPostsBySource: getPostsBySource(blogPosts),
|
sourceToPermalink: getSourceToPermalink(blogPosts),
|
||||||
onBrokenMarkdownLink: (brokenMarkdownLink) => {
|
onBrokenMarkdownLink: (brokenMarkdownLink) => {
|
||||||
if (onBrokenMarkdownLinks === 'ignore') {
|
if (onBrokenMarkdownLinks === 'ignore') {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -12,13 +12,13 @@ import {BlogMarkdownLoaderOptions} from './types';
|
||||||
|
|
||||||
const markdownLoader: loader.Loader = function (source) {
|
const markdownLoader: loader.Loader = function (source) {
|
||||||
const filePath = this.resourcePath;
|
const filePath = this.resourcePath;
|
||||||
const fileContent = source as string;
|
const fileString = source as string;
|
||||||
const callback = this.async();
|
const callback = this.async();
|
||||||
const markdownLoaderOptions = getOptions(this) as BlogMarkdownLoaderOptions;
|
const markdownLoaderOptions = getOptions(this) as BlogMarkdownLoaderOptions;
|
||||||
|
|
||||||
// Linkify blog posts
|
// Linkify blog posts
|
||||||
let finalContent = linkify({
|
let finalContent = linkify({
|
||||||
fileContent,
|
fileString,
|
||||||
filePath,
|
filePath,
|
||||||
...markdownLoaderOptions,
|
...markdownLoaderOptions,
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,10 +5,12 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type BlogContentPaths = {
|
import {
|
||||||
contentPath: string;
|
BrokenMarkdownLink,
|
||||||
contentPathLocalized: string;
|
ContentPaths,
|
||||||
};
|
} from '@docusaurus/utils/lib/markdownLinks';
|
||||||
|
|
||||||
|
export type BlogContentPaths = ContentPaths;
|
||||||
|
|
||||||
export interface BlogContent {
|
export interface BlogContent {
|
||||||
blogPosts: BlogPost[];
|
blogPosts: BlogPost[];
|
||||||
|
@ -142,15 +144,11 @@ export interface TagModule {
|
||||||
permalink: string;
|
permalink: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BlogBrokenMarkdownLink = {
|
export type BlogBrokenMarkdownLink = BrokenMarkdownLink<BlogContentPaths>;
|
||||||
folderPath: string;
|
|
||||||
filePath: string;
|
|
||||||
link: string;
|
|
||||||
};
|
|
||||||
export type BlogMarkdownLoaderOptions = {
|
export type BlogMarkdownLoaderOptions = {
|
||||||
siteDir: string;
|
siteDir: string;
|
||||||
contentPaths: BlogContentPaths;
|
contentPaths: BlogContentPaths;
|
||||||
truncateMarker: RegExp;
|
truncateMarker: RegExp;
|
||||||
blogPostsBySource: Record<string, BlogPost>;
|
sourceToPermalink: Record<string, string>;
|
||||||
onBrokenMarkdownLink: (brokenMarkdownLink: BlogBrokenMarkdownLink) => void;
|
onBrokenMarkdownLink: (brokenMarkdownLink: BlogBrokenMarkdownLink) => void;
|
||||||
};
|
};
|
||||||
|
|
|
@ -60,6 +60,8 @@ exports[`translateLoadedContent should return translated loaded content matching
|
||||||
Object {
|
Object {
|
||||||
"loadedVersions": Array [
|
"loadedVersions": Array [
|
||||||
Object {
|
Object {
|
||||||
|
"contentPath": "any",
|
||||||
|
"contentPathLocalized": "any",
|
||||||
"docs": Array [
|
"docs": Array [
|
||||||
Object {
|
Object {
|
||||||
"description": "doc1 description",
|
"description": "doc1 description",
|
||||||
|
@ -147,8 +149,6 @@ Object {
|
||||||
"version": "any",
|
"version": "any",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"docsDirPath": "any",
|
|
||||||
"docsDirPathLocalized": "any",
|
|
||||||
"isLast": true,
|
"isLast": true,
|
||||||
"mainDocId": "",
|
"mainDocId": "",
|
||||||
"permalinkToSidebar": Object {},
|
"permalinkToSidebar": Object {},
|
||||||
|
@ -201,6 +201,8 @@ Object {
|
||||||
"versionPath": "/docs/",
|
"versionPath": "/docs/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
"contentPath": "any",
|
||||||
|
"contentPathLocalized": "any",
|
||||||
"docs": Array [
|
"docs": Array [
|
||||||
Object {
|
Object {
|
||||||
"description": "doc1 description",
|
"description": "doc1 description",
|
||||||
|
@ -288,8 +290,6 @@ Object {
|
||||||
"version": "any",
|
"version": "any",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"docsDirPath": "any",
|
|
||||||
"docsDirPathLocalized": "any",
|
|
||||||
"isLast": true,
|
"isLast": true,
|
||||||
"mainDocId": "",
|
"mainDocId": "",
|
||||||
"permalinkToSidebar": Object {},
|
"permalinkToSidebar": Object {},
|
||||||
|
@ -342,6 +342,8 @@ Object {
|
||||||
"versionPath": "/docs/",
|
"versionPath": "/docs/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
"contentPath": "any",
|
||||||
|
"contentPathLocalized": "any",
|
||||||
"docs": Array [
|
"docs": Array [
|
||||||
Object {
|
Object {
|
||||||
"description": "doc1 description",
|
"description": "doc1 description",
|
||||||
|
@ -429,8 +431,6 @@ Object {
|
||||||
"version": "any",
|
"version": "any",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"docsDirPath": "any",
|
|
||||||
"docsDirPathLocalized": "any",
|
|
||||||
"isLast": true,
|
"isLast": true,
|
||||||
"mainDocId": "",
|
"mainDocId": "",
|
||||||
"permalinkToSidebar": Object {},
|
"permalinkToSidebar": Object {},
|
||||||
|
|
|
@ -45,7 +45,7 @@ ${markdown}
|
||||||
source,
|
source,
|
||||||
content,
|
content,
|
||||||
lastUpdate: {},
|
lastUpdate: {},
|
||||||
docsDirPath: 'docs',
|
contentPath: 'docs',
|
||||||
filePath: source,
|
filePath: source,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -93,7 +93,7 @@ function createTestUtils({
|
||||||
editUrl: undefined,
|
editUrl: undefined,
|
||||||
source: path.posix.join(
|
source: path.posix.join(
|
||||||
'@site',
|
'@site',
|
||||||
posixPath(path.relative(siteDir, versionMetadata.docsDirPath)),
|
posixPath(path.relative(siteDir, versionMetadata.contentPath)),
|
||||||
posixPath(docFileSource),
|
posixPath(docFileSource),
|
||||||
),
|
),
|
||||||
...expectedMetadata,
|
...expectedMetadata,
|
||||||
|
|
|
@ -254,7 +254,7 @@ describe('simple website', () => {
|
||||||
sidebar: 'docs',
|
sidebar: 'docs',
|
||||||
source: path.posix.join(
|
source: path.posix.join(
|
||||||
'@site',
|
'@site',
|
||||||
posixPath(path.relative(siteDir, currentVersion.docsDirPath)),
|
posixPath(path.relative(siteDir, currentVersion.contentPath)),
|
||||||
'hello.md',
|
'hello.md',
|
||||||
),
|
),
|
||||||
title: 'Hello, World !',
|
title: 'Hello, World !',
|
||||||
|
@ -276,7 +276,7 @@ describe('simple website', () => {
|
||||||
sidebar: 'docs',
|
sidebar: 'docs',
|
||||||
source: path.posix.join(
|
source: path.posix.join(
|
||||||
'@site',
|
'@site',
|
||||||
posixPath(path.relative(siteDir, currentVersion.docsDirPath)),
|
posixPath(path.relative(siteDir, currentVersion.contentPath)),
|
||||||
'foo',
|
'foo',
|
||||||
'bar.md',
|
'bar.md',
|
||||||
),
|
),
|
||||||
|
@ -424,7 +424,7 @@ describe('versioned website', () => {
|
||||||
slug: '/foo/barSlug',
|
slug: '/foo/barSlug',
|
||||||
source: path.posix.join(
|
source: path.posix.join(
|
||||||
'@site',
|
'@site',
|
||||||
posixPath(path.relative(siteDir, currentVersion.docsDirPath)),
|
posixPath(path.relative(siteDir, currentVersion.contentPath)),
|
||||||
'foo',
|
'foo',
|
||||||
'bar.md',
|
'bar.md',
|
||||||
),
|
),
|
||||||
|
@ -446,7 +446,7 @@ describe('versioned website', () => {
|
||||||
slug: '/',
|
slug: '/',
|
||||||
source: path.posix.join(
|
source: path.posix.join(
|
||||||
'@site',
|
'@site',
|
||||||
posixPath(path.relative(siteDir, currentVersion.docsDirPath)),
|
posixPath(path.relative(siteDir, currentVersion.contentPath)),
|
||||||
'hello.md',
|
'hello.md',
|
||||||
),
|
),
|
||||||
title: 'hello',
|
title: 'hello',
|
||||||
|
@ -467,7 +467,7 @@ describe('versioned website', () => {
|
||||||
slug: '/',
|
slug: '/',
|
||||||
source: path.posix.join(
|
source: path.posix.join(
|
||||||
'@site',
|
'@site',
|
||||||
posixPath(path.relative(siteDir, version101.docsDirPath)),
|
posixPath(path.relative(siteDir, version101.contentPath)),
|
||||||
'hello.md',
|
'hello.md',
|
||||||
),
|
),
|
||||||
title: 'hello',
|
title: 'hello',
|
||||||
|
@ -488,7 +488,7 @@ describe('versioned website', () => {
|
||||||
slug: '/foo/baz',
|
slug: '/foo/baz',
|
||||||
source: path.posix.join(
|
source: path.posix.join(
|
||||||
'@site',
|
'@site',
|
||||||
posixPath(path.relative(siteDir, version100.docsDirPath)),
|
posixPath(path.relative(siteDir, version100.contentPath)),
|
||||||
'foo',
|
'foo',
|
||||||
'baz.md',
|
'baz.md',
|
||||||
),
|
),
|
||||||
|
@ -649,7 +649,7 @@ describe('versioned website (community)', () => {
|
||||||
slug: '/team',
|
slug: '/team',
|
||||||
source: path.posix.join(
|
source: path.posix.join(
|
||||||
'@site',
|
'@site',
|
||||||
posixPath(path.relative(siteDir, version100.docsDirPath)),
|
posixPath(path.relative(siteDir, version100.contentPath)),
|
||||||
'team.md',
|
'team.md',
|
||||||
),
|
),
|
||||||
title: 'team',
|
title: 'team',
|
||||||
|
|
|
@ -44,8 +44,8 @@ function createSampleVersion(
|
||||||
routePriority: undefined,
|
routePriority: undefined,
|
||||||
sidebarFilePath: 'any',
|
sidebarFilePath: 'any',
|
||||||
isLast: true,
|
isLast: true,
|
||||||
docsDirPath: 'any',
|
contentPath: 'any',
|
||||||
docsDirPathLocalized: 'any',
|
contentPathLocalized: 'any',
|
||||||
docs: [
|
docs: [
|
||||||
createSampleDoc({
|
createSampleDoc({
|
||||||
id: 'doc1',
|
id: 'doc1',
|
||||||
|
|
|
@ -68,8 +68,8 @@ describe('simple site', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const vCurrent: VersionMetadata = {
|
const vCurrent: VersionMetadata = {
|
||||||
docsDirPath: path.join(simpleSiteDir, 'docs'),
|
contentPath: path.join(simpleSiteDir, 'docs'),
|
||||||
docsDirPathLocalized: path.join(
|
contentPathLocalized: path.join(
|
||||||
simpleSiteDir,
|
simpleSiteDir,
|
||||||
'i18n/en/docusaurus-plugin-content-docs/current',
|
'i18n/en/docusaurus-plugin-content-docs/current',
|
||||||
),
|
),
|
||||||
|
@ -218,8 +218,8 @@ describe('versioned site, pluginId=default', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const vCurrent: VersionMetadata = {
|
const vCurrent: VersionMetadata = {
|
||||||
docsDirPath: path.join(versionedSiteDir, 'docs'),
|
contentPath: path.join(versionedSiteDir, 'docs'),
|
||||||
docsDirPathLocalized: path.join(
|
contentPathLocalized: path.join(
|
||||||
versionedSiteDir,
|
versionedSiteDir,
|
||||||
'i18n/en/docusaurus-plugin-content-docs/current',
|
'i18n/en/docusaurus-plugin-content-docs/current',
|
||||||
),
|
),
|
||||||
|
@ -232,8 +232,8 @@ describe('versioned site, pluginId=default', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const v101: VersionMetadata = {
|
const v101: VersionMetadata = {
|
||||||
docsDirPath: path.join(versionedSiteDir, 'versioned_docs/version-1.0.1'),
|
contentPath: path.join(versionedSiteDir, 'versioned_docs/version-1.0.1'),
|
||||||
docsDirPathLocalized: path.join(
|
contentPathLocalized: path.join(
|
||||||
versionedSiteDir,
|
versionedSiteDir,
|
||||||
'i18n/en/docusaurus-plugin-content-docs/version-1.0.1',
|
'i18n/en/docusaurus-plugin-content-docs/version-1.0.1',
|
||||||
),
|
),
|
||||||
|
@ -249,8 +249,8 @@ describe('versioned site, pluginId=default', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const v100: VersionMetadata = {
|
const v100: VersionMetadata = {
|
||||||
docsDirPath: path.join(versionedSiteDir, 'versioned_docs/version-1.0.0'),
|
contentPath: path.join(versionedSiteDir, 'versioned_docs/version-1.0.0'),
|
||||||
docsDirPathLocalized: path.join(
|
contentPathLocalized: path.join(
|
||||||
versionedSiteDir,
|
versionedSiteDir,
|
||||||
'i18n/en/docusaurus-plugin-content-docs/version-1.0.0',
|
'i18n/en/docusaurus-plugin-content-docs/version-1.0.0',
|
||||||
),
|
),
|
||||||
|
@ -266,11 +266,11 @@ describe('versioned site, pluginId=default', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const vwithSlugs: VersionMetadata = {
|
const vwithSlugs: VersionMetadata = {
|
||||||
docsDirPath: path.join(
|
contentPath: path.join(
|
||||||
versionedSiteDir,
|
versionedSiteDir,
|
||||||
'versioned_docs/version-withSlugs',
|
'versioned_docs/version-withSlugs',
|
||||||
),
|
),
|
||||||
docsDirPathLocalized: path.join(
|
contentPathLocalized: path.join(
|
||||||
versionedSiteDir,
|
versionedSiteDir,
|
||||||
'i18n/en/docusaurus-plugin-content-docs/version-withSlugs',
|
'i18n/en/docusaurus-plugin-content-docs/version-withSlugs',
|
||||||
),
|
),
|
||||||
|
@ -615,8 +615,8 @@ describe('versioned site, pluginId=community', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const vCurrent: VersionMetadata = {
|
const vCurrent: VersionMetadata = {
|
||||||
docsDirPath: path.join(versionedSiteDir, 'community'),
|
contentPath: path.join(versionedSiteDir, 'community'),
|
||||||
docsDirPathLocalized: path.join(
|
contentPathLocalized: path.join(
|
||||||
versionedSiteDir,
|
versionedSiteDir,
|
||||||
'i18n/en/docusaurus-plugin-content-docs-community/current',
|
'i18n/en/docusaurus-plugin-content-docs-community/current',
|
||||||
),
|
),
|
||||||
|
@ -629,11 +629,11 @@ describe('versioned site, pluginId=community', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const v100: VersionMetadata = {
|
const v100: VersionMetadata = {
|
||||||
docsDirPath: path.join(
|
contentPath: path.join(
|
||||||
versionedSiteDir,
|
versionedSiteDir,
|
||||||
'community_versioned_docs/version-1.0.0',
|
'community_versioned_docs/version-1.0.0',
|
||||||
),
|
),
|
||||||
docsDirPathLocalized: path.join(
|
contentPathLocalized: path.join(
|
||||||
versionedSiteDir,
|
versionedSiteDir,
|
||||||
'i18n/en/docusaurus-plugin-content-docs-community/version-1.0.0',
|
'i18n/en/docusaurus-plugin-content-docs-community/version-1.0.0',
|
||||||
),
|
),
|
||||||
|
|
|
@ -67,23 +67,23 @@ async function readLastUpdateData(
|
||||||
export async function readDocFile(
|
export async function readDocFile(
|
||||||
versionMetadata: Pick<
|
versionMetadata: Pick<
|
||||||
VersionMetadata,
|
VersionMetadata,
|
||||||
'docsDirPath' | 'docsDirPathLocalized'
|
'contentPath' | 'contentPathLocalized'
|
||||||
>,
|
>,
|
||||||
source: string,
|
source: string,
|
||||||
options: LastUpdateOptions,
|
options: LastUpdateOptions,
|
||||||
): Promise<DocFile> {
|
): Promise<DocFile> {
|
||||||
const docsDirPath = await getFolderContainingFile(
|
const contentPath = await getFolderContainingFile(
|
||||||
getDocsDirPaths(versionMetadata),
|
getDocsDirPaths(versionMetadata),
|
||||||
source,
|
source,
|
||||||
);
|
);
|
||||||
|
|
||||||
const filePath = path.join(docsDirPath, source);
|
const filePath = path.join(contentPath, source);
|
||||||
|
|
||||||
const [content, lastUpdate] = await Promise.all([
|
const [content, lastUpdate] = await Promise.all([
|
||||||
fs.readFile(filePath, 'utf-8'),
|
fs.readFile(filePath, 'utf-8'),
|
||||||
readLastUpdateData(filePath, options),
|
readLastUpdateData(filePath, options),
|
||||||
]);
|
]);
|
||||||
return {source, content, lastUpdate, docsDirPath, filePath};
|
return {source, content, lastUpdate, contentPath, filePath};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function readVersionDocs(
|
export async function readVersionDocs(
|
||||||
|
@ -94,7 +94,7 @@ export async function readVersionDocs(
|
||||||
>,
|
>,
|
||||||
): Promise<DocFile[]> {
|
): Promise<DocFile[]> {
|
||||||
const sources = await globby(options.include, {
|
const sources = await globby(options.include, {
|
||||||
cwd: versionMetadata.docsDirPath,
|
cwd: versionMetadata.contentPath,
|
||||||
});
|
});
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
sources.map((source) => readDocFile(versionMetadata, source, options)),
|
sources.map((source) => readDocFile(versionMetadata, source, options)),
|
||||||
|
@ -112,7 +112,7 @@ export function processDocMetadata({
|
||||||
context: LoadContext;
|
context: LoadContext;
|
||||||
options: MetadataOptions;
|
options: MetadataOptions;
|
||||||
}): DocMetadataBase {
|
}): DocMetadataBase {
|
||||||
const {source, content, lastUpdate, docsDirPath, filePath} = docFile;
|
const {source, content, lastUpdate, contentPath, filePath} = docFile;
|
||||||
const {homePageId} = options;
|
const {homePageId} = options;
|
||||||
const {siteDir, i18n} = context;
|
const {siteDir, i18n} = context;
|
||||||
|
|
||||||
|
@ -170,20 +170,20 @@ export function processDocMetadata({
|
||||||
const permalink = normalizeUrl([versionMetadata.versionPath, docSlug]);
|
const permalink = normalizeUrl([versionMetadata.versionPath, docSlug]);
|
||||||
|
|
||||||
function getDocEditUrl() {
|
function getDocEditUrl() {
|
||||||
const relativeFilePath = path.relative(docsDirPath, filePath);
|
const relativeFilePath = path.relative(contentPath, filePath);
|
||||||
|
|
||||||
if (typeof options.editUrl === 'function') {
|
if (typeof options.editUrl === 'function') {
|
||||||
return options.editUrl({
|
return options.editUrl({
|
||||||
version: versionMetadata.versionName,
|
version: versionMetadata.versionName,
|
||||||
versionDocsDirPath: posixPath(
|
versionDocsDirPath: posixPath(
|
||||||
path.relative(siteDir, versionMetadata.docsDirPath),
|
path.relative(siteDir, versionMetadata.contentPath),
|
||||||
),
|
),
|
||||||
docPath: posixPath(relativeFilePath),
|
docPath: posixPath(relativeFilePath),
|
||||||
permalink,
|
permalink,
|
||||||
locale: context.i18n.currentLocale,
|
locale: context.i18n.currentLocale,
|
||||||
});
|
});
|
||||||
} else if (typeof options.editUrl === 'string') {
|
} else if (typeof options.editUrl === 'string') {
|
||||||
const isLocalized = docsDirPath === versionMetadata.docsDirPathLocalized;
|
const isLocalized = contentPath === versionMetadata.contentPathLocalized;
|
||||||
const baseVersionEditUrl =
|
const baseVersionEditUrl =
|
||||||
isLocalized && options.editLocalizedFiles
|
isLocalized && options.editLocalizedFiles
|
||||||
? versionMetadata.versionEditUrlLocalized
|
? versionMetadata.versionEditUrlLocalized
|
||||||
|
|
|
@ -145,7 +145,7 @@ export default function pluginContentDocs(
|
||||||
versionMetadata.versionName
|
versionMetadata.versionName
|
||||||
} has no docs! At least one doc should exist at path=[${path.relative(
|
} has no docs! At least one doc should exist at path=[${path.relative(
|
||||||
siteDir,
|
siteDir,
|
||||||
versionMetadata.docsDirPath,
|
versionMetadata.contentPath,
|
||||||
)}]`,
|
)}]`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -337,7 +337,7 @@ export default function pluginContentDocs(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
reportMessage(
|
reportMessage(
|
||||||
`Docs markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath} for version ${brokenMarkdownLink.version.versionName}`,
|
`Docs markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath} for version ${brokenMarkdownLink.contentPaths.versionName}`,
|
||||||
siteConfig.onBrokenMarkdownLinks,
|
siteConfig.onBrokenMarkdownLinks,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,19 +18,19 @@ import {VERSIONED_DOCS_DIR, CURRENT_VERSION_NAME} from '../../constants';
|
||||||
|
|
||||||
function createFakeVersion({
|
function createFakeVersion({
|
||||||
versionName,
|
versionName,
|
||||||
docsDirPath,
|
contentPath,
|
||||||
docsDirPathLocalized,
|
contentPathLocalized,
|
||||||
}: {
|
}: {
|
||||||
versionName: string;
|
versionName: string;
|
||||||
docsDirPath: string;
|
contentPath: string;
|
||||||
docsDirPathLocalized: string;
|
contentPathLocalized: string;
|
||||||
}): VersionMetadata {
|
}): VersionMetadata {
|
||||||
return {
|
return {
|
||||||
versionName,
|
versionName,
|
||||||
versionLabel: 'Any',
|
versionLabel: 'Any',
|
||||||
versionPath: 'any',
|
versionPath: 'any',
|
||||||
docsDirPath,
|
contentPath,
|
||||||
docsDirPathLocalized,
|
contentPathLocalized,
|
||||||
sidebarFilePath: 'any',
|
sidebarFilePath: 'any',
|
||||||
routePriority: undefined,
|
routePriority: undefined,
|
||||||
isLast: false,
|
isLast: false,
|
||||||
|
@ -41,8 +41,8 @@ const siteDir = path.join(__dirname, '__fixtures__');
|
||||||
|
|
||||||
const versionCurrent = createFakeVersion({
|
const versionCurrent = createFakeVersion({
|
||||||
versionName: CURRENT_VERSION_NAME,
|
versionName: CURRENT_VERSION_NAME,
|
||||||
docsDirPath: path.join(siteDir, 'docs'),
|
contentPath: path.join(siteDir, 'docs'),
|
||||||
docsDirPathLocalized: path.join(
|
contentPathLocalized: path.join(
|
||||||
siteDir,
|
siteDir,
|
||||||
'i18n',
|
'i18n',
|
||||||
'fr',
|
'fr',
|
||||||
|
@ -53,8 +53,8 @@ const versionCurrent = createFakeVersion({
|
||||||
|
|
||||||
const version100 = createFakeVersion({
|
const version100 = createFakeVersion({
|
||||||
versionName: '1.0.0',
|
versionName: '1.0.0',
|
||||||
docsDirPath: path.join(siteDir, VERSIONED_DOCS_DIR, 'version-1.0.0'),
|
contentPath: path.join(siteDir, VERSIONED_DOCS_DIR, 'version-1.0.0'),
|
||||||
docsDirPathLocalized: path.join(
|
contentPathLocalized: path.join(
|
||||||
siteDir,
|
siteDir,
|
||||||
'i18n',
|
'i18n',
|
||||||
'fr',
|
'fr',
|
||||||
|
@ -97,14 +97,14 @@ const transform = (filepath: string, options?: Partial<DocsMarkdownOption>) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
test('transform nothing', () => {
|
test('transform nothing', () => {
|
||||||
const doc1 = path.join(versionCurrent.docsDirPath, 'doc1.md');
|
const doc1 = path.join(versionCurrent.contentPath, 'doc1.md');
|
||||||
const [content, transformedContent] = transform(doc1);
|
const [content, transformedContent] = transform(doc1);
|
||||||
expect(transformedContent).toMatchSnapshot();
|
expect(transformedContent).toMatchSnapshot();
|
||||||
expect(content).toEqual(transformedContent);
|
expect(content).toEqual(transformedContent);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('transform to correct links', () => {
|
test('transform to correct links', () => {
|
||||||
const doc2 = path.join(versionCurrent.docsDirPath, 'doc2.md');
|
const doc2 = path.join(versionCurrent.contentPath, 'doc2.md');
|
||||||
const [content, transformedContent] = transform(doc2);
|
const [content, transformedContent] = transform(doc2);
|
||||||
expect(transformedContent).toMatchSnapshot();
|
expect(transformedContent).toMatchSnapshot();
|
||||||
expect(transformedContent).toContain('](/docs/doc1');
|
expect(transformedContent).toContain('](/docs/doc1');
|
||||||
|
@ -119,7 +119,7 @@ test('transform to correct links', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('transform relative links', () => {
|
test('transform relative links', () => {
|
||||||
const doc3 = path.join(versionCurrent.docsDirPath, 'subdir', 'doc3.md');
|
const doc3 = path.join(versionCurrent.contentPath, 'subdir', 'doc3.md');
|
||||||
|
|
||||||
const [content, transformedContent] = transform(doc3);
|
const [content, transformedContent] = transform(doc3);
|
||||||
expect(transformedContent).toMatchSnapshot();
|
expect(transformedContent).toMatchSnapshot();
|
||||||
|
@ -129,7 +129,7 @@ test('transform relative links', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('transforms reference links', () => {
|
test('transforms reference links', () => {
|
||||||
const doc4 = path.join(versionCurrent.docsDirPath, 'doc4.md');
|
const doc4 = path.join(versionCurrent.contentPath, 'doc4.md');
|
||||||
const [content, transformedContent] = transform(doc4);
|
const [content, transformedContent] = transform(doc4);
|
||||||
expect(transformedContent).toMatchSnapshot();
|
expect(transformedContent).toMatchSnapshot();
|
||||||
expect(transformedContent).toContain('[doc1]: /docs/doc1');
|
expect(transformedContent).toContain('[doc1]: /docs/doc1');
|
||||||
|
@ -140,7 +140,7 @@ test('transforms reference links', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('report broken markdown links', () => {
|
test('report broken markdown links', () => {
|
||||||
const doc5 = path.join(versionCurrent.docsDirPath, 'doc5.md');
|
const doc5 = path.join(versionCurrent.contentPath, 'doc5.md');
|
||||||
const onBrokenMarkdownLink = jest.fn();
|
const onBrokenMarkdownLink = jest.fn();
|
||||||
const [content, transformedContent] = transform(doc5, {
|
const [content, transformedContent] = transform(doc5, {
|
||||||
onBrokenMarkdownLink,
|
onBrokenMarkdownLink,
|
||||||
|
@ -150,27 +150,27 @@ test('report broken markdown links', () => {
|
||||||
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(1, {
|
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(1, {
|
||||||
filePath: doc5,
|
filePath: doc5,
|
||||||
link: 'docNotExist1.md',
|
link: 'docNotExist1.md',
|
||||||
version: versionCurrent,
|
contentPaths: versionCurrent,
|
||||||
} as BrokenMarkdownLink);
|
} as BrokenMarkdownLink);
|
||||||
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(2, {
|
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(2, {
|
||||||
filePath: doc5,
|
filePath: doc5,
|
||||||
link: './docNotExist2.mdx',
|
link: './docNotExist2.mdx',
|
||||||
version: versionCurrent,
|
contentPaths: versionCurrent,
|
||||||
} as BrokenMarkdownLink);
|
} as BrokenMarkdownLink);
|
||||||
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(3, {
|
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(3, {
|
||||||
filePath: doc5,
|
filePath: doc5,
|
||||||
link: '../docNotExist3.mdx',
|
link: '../docNotExist3.mdx',
|
||||||
version: versionCurrent,
|
contentPaths: versionCurrent,
|
||||||
} as BrokenMarkdownLink);
|
} as BrokenMarkdownLink);
|
||||||
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(4, {
|
expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(4, {
|
||||||
filePath: doc5,
|
filePath: doc5,
|
||||||
link: './subdir/docNotExist4.md',
|
link: './subdir/docNotExist4.md',
|
||||||
version: versionCurrent,
|
contentPaths: versionCurrent,
|
||||||
} as BrokenMarkdownLink);
|
} as BrokenMarkdownLink);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('transforms absolute links in versioned docs', () => {
|
test('transforms absolute links in versioned docs', () => {
|
||||||
const doc2 = path.join(version100.docsDirPath, 'doc2.md');
|
const doc2 = path.join(version100.contentPath, 'doc2.md');
|
||||||
const [content, transformedContent] = transform(doc2);
|
const [content, transformedContent] = transform(doc2);
|
||||||
expect(transformedContent).toMatchSnapshot();
|
expect(transformedContent).toMatchSnapshot();
|
||||||
expect(transformedContent).toContain('](/docs/1.0.0/subdir/doc1');
|
expect(transformedContent).toContain('](/docs/1.0.0/subdir/doc1');
|
||||||
|
@ -181,7 +181,7 @@ test('transforms absolute links in versioned docs', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('transforms relative links in versioned docs', () => {
|
test('transforms relative links in versioned docs', () => {
|
||||||
const doc1 = path.join(version100.docsDirPath, 'subdir', 'doc1.md');
|
const doc1 = path.join(version100.contentPath, 'subdir', 'doc1.md');
|
||||||
const [content, transformedContent] = transform(doc1);
|
const [content, transformedContent] = transform(doc1);
|
||||||
expect(transformedContent).toMatchSnapshot();
|
expect(transformedContent).toMatchSnapshot();
|
||||||
expect(transformedContent).toContain('](/docs/1.0.0/doc2');
|
expect(transformedContent).toContain('](/docs/1.0.0/doc2');
|
||||||
|
|
|
@ -5,14 +5,9 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {resolve} from 'url';
|
import {DocsMarkdownOption} from '../types';
|
||||||
import {
|
|
||||||
DocsMarkdownOption,
|
|
||||||
VersionMetadata,
|
|
||||||
BrokenMarkdownLink,
|
|
||||||
} from '../types';
|
|
||||||
import {getDocsDirPaths} from '../versions';
|
import {getDocsDirPaths} from '../versions';
|
||||||
import {aliasedSitePath} from '@docusaurus/utils';
|
import {replaceMarkdownLinks} from '@docusaurus/utils/lib/markdownLinks';
|
||||||
|
|
||||||
function getVersion(filePath: string, options: DocsMarkdownOption) {
|
function getVersion(filePath: string, options: DocsMarkdownOption) {
|
||||||
const versionFound = options.versionsMetadata.find((version) =>
|
const versionFound = options.versionsMetadata.find((version) =>
|
||||||
|
@ -28,66 +23,22 @@ function getVersion(filePath: string, options: DocsMarkdownOption) {
|
||||||
return versionFound;
|
return versionFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceMarkdownLinks(
|
|
||||||
fileString: string,
|
|
||||||
filePath: string,
|
|
||||||
version: VersionMetadata,
|
|
||||||
options: DocsMarkdownOption,
|
|
||||||
) {
|
|
||||||
const {siteDir, sourceToPermalink, onBrokenMarkdownLink} = options;
|
|
||||||
const {docsDirPath, docsDirPathLocalized} = version;
|
|
||||||
|
|
||||||
// Replace internal markdown linking (except in fenced blocks).
|
|
||||||
let fencedBlock = false;
|
|
||||||
const lines = fileString.split('\n').map((line) => {
|
|
||||||
if (line.trim().startsWith('```')) {
|
|
||||||
fencedBlock = !fencedBlock;
|
|
||||||
}
|
|
||||||
if (fencedBlock) {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
let modifiedLine = line;
|
|
||||||
// Replace inline-style links or reference-style links e.g:
|
|
||||||
// This is [Document 1](doc1.md) -> we replace this doc1.md with correct link
|
|
||||||
// [doc1]: doc1.md -> we replace this doc1.md with correct link
|
|
||||||
const mdRegex = /(?:(?:\]\()|(?:\]:\s?))(?!https)([^'")\]\s>]+\.mdx?)/g;
|
|
||||||
let mdMatch = mdRegex.exec(modifiedLine);
|
|
||||||
while (mdMatch !== null) {
|
|
||||||
// Replace it to correct html link.
|
|
||||||
const mdLink = mdMatch[1];
|
|
||||||
|
|
||||||
const aliasedSource = (source: string) =>
|
|
||||||
aliasedSitePath(source, siteDir);
|
|
||||||
|
|
||||||
const permalink =
|
|
||||||
sourceToPermalink[aliasedSource(resolve(filePath, mdLink))] ||
|
|
||||||
sourceToPermalink[aliasedSource(`${docsDirPathLocalized}/${mdLink}`)] ||
|
|
||||||
sourceToPermalink[aliasedSource(`${docsDirPath}/${mdLink}`)];
|
|
||||||
|
|
||||||
if (permalink) {
|
|
||||||
modifiedLine = modifiedLine.replace(mdLink, permalink);
|
|
||||||
} else {
|
|
||||||
const brokenMarkdownLink: BrokenMarkdownLink = {
|
|
||||||
version,
|
|
||||||
filePath,
|
|
||||||
link: mdLink,
|
|
||||||
};
|
|
||||||
onBrokenMarkdownLink(brokenMarkdownLink);
|
|
||||||
}
|
|
||||||
mdMatch = mdRegex.exec(modifiedLine);
|
|
||||||
}
|
|
||||||
return modifiedLine;
|
|
||||||
});
|
|
||||||
|
|
||||||
return lines.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function linkify(
|
export function linkify(
|
||||||
fileString: string,
|
fileString: string,
|
||||||
filePath: string,
|
filePath: string,
|
||||||
options: DocsMarkdownOption,
|
options: DocsMarkdownOption,
|
||||||
): string {
|
): string {
|
||||||
const version = getVersion(filePath, options);
|
const {siteDir, sourceToPermalink, onBrokenMarkdownLink} = options;
|
||||||
return replaceMarkdownLinks(fileString, filePath, version, options);
|
|
||||||
|
const {newContent, brokenMarkdownLinks} = replaceMarkdownLinks({
|
||||||
|
siteDir,
|
||||||
|
fileString,
|
||||||
|
filePath,
|
||||||
|
contentPaths: getVersion(filePath, options),
|
||||||
|
sourceToPermalink,
|
||||||
|
});
|
||||||
|
|
||||||
|
brokenMarkdownLinks.forEach((l) => onBrokenMarkdownLink(l));
|
||||||
|
|
||||||
|
return newContent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,13 @@
|
||||||
// eslint-disable-next-line spaced-comment
|
// eslint-disable-next-line spaced-comment
|
||||||
/// <reference types="@docusaurus/module-type-aliases" />
|
/// <reference types="@docusaurus/module-type-aliases" />
|
||||||
|
|
||||||
|
import {
|
||||||
|
BrokenMarkdownLink as IBrokenMarkdownLink,
|
||||||
|
ContentPaths,
|
||||||
|
} from '@docusaurus/utils/lib/markdownLinks';
|
||||||
|
|
||||||
export type DocFile = {
|
export type DocFile = {
|
||||||
docsDirPath: string; // /!\ may be localized
|
contentPath: string; // /!\ may be localized
|
||||||
filePath: string; // /!\ may be localized
|
filePath: string; // /!\ may be localized
|
||||||
source: string;
|
source: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
@ -18,15 +23,15 @@ export type DocFile = {
|
||||||
|
|
||||||
export type VersionName = string;
|
export type VersionName = string;
|
||||||
|
|
||||||
export type VersionMetadata = {
|
export type VersionMetadata = ContentPaths & {
|
||||||
versionName: VersionName; // 1.0.0
|
versionName: VersionName; // 1.0.0
|
||||||
versionLabel: string; // Version 1.0.0
|
versionLabel: string; // Version 1.0.0
|
||||||
versionPath: string; // /baseUrl/docs/1.0.0
|
versionPath: string; // /baseUrl/docs/1.0.0
|
||||||
versionEditUrl?: string | undefined;
|
versionEditUrl?: string | undefined;
|
||||||
versionEditUrlLocalized?: string | undefined;
|
versionEditUrlLocalized?: string | undefined;
|
||||||
isLast: boolean;
|
isLast: boolean;
|
||||||
docsDirPath: string; // "versioned_docs/version-1.0.0"
|
// contentPath: string; // "versioned_docs/version-1.0.0"
|
||||||
docsDirPathLocalized: string; // "i18n/fr/version-1.0.0/default"
|
// contentPathLocalized: string; // "i18n/fr/version-1.0.0/default"
|
||||||
sidebarFilePath: string; // versioned_sidebars/1.0.0.json
|
sidebarFilePath: string; // versioned_sidebars/1.0.0.json
|
||||||
routePriority: number | undefined; // -1 for the latest docs
|
routePriority: number | undefined; // -1 for the latest docs
|
||||||
};
|
};
|
||||||
|
@ -192,11 +197,7 @@ export type GlobalPluginData = {
|
||||||
versions: GlobalVersion[];
|
versions: GlobalVersion[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BrokenMarkdownLink = {
|
export type BrokenMarkdownLink = IBrokenMarkdownLink<VersionMetadata>;
|
||||||
filePath: string;
|
|
||||||
version: VersionMetadata;
|
|
||||||
link: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DocsMarkdownOption = {
|
export type DocsMarkdownOption = {
|
||||||
versionsMetadata: VersionMetadata[];
|
versionsMetadata: VersionMetadata[];
|
||||||
|
|
|
@ -165,18 +165,18 @@ function getVersionMetadataPaths({
|
||||||
options: Pick<PluginOptions, 'id' | 'path' | 'sidebarPath'>;
|
options: Pick<PluginOptions, 'id' | 'path' | 'sidebarPath'>;
|
||||||
}): Pick<
|
}): Pick<
|
||||||
VersionMetadata,
|
VersionMetadata,
|
||||||
'docsDirPath' | 'docsDirPathLocalized' | 'sidebarFilePath'
|
'contentPath' | 'contentPathLocalized' | 'sidebarFilePath'
|
||||||
> {
|
> {
|
||||||
const isCurrentVersion = versionName === CURRENT_VERSION_NAME;
|
const isCurrentVersion = versionName === CURRENT_VERSION_NAME;
|
||||||
|
|
||||||
const docsDirPath = isCurrentVersion
|
const contentPath = isCurrentVersion
|
||||||
? path.resolve(context.siteDir, options.path)
|
? path.resolve(context.siteDir, options.path)
|
||||||
: path.join(
|
: path.join(
|
||||||
getVersionedDocsDirPath(context.siteDir, options.id),
|
getVersionedDocsDirPath(context.siteDir, options.id),
|
||||||
`version-${versionName}`,
|
`version-${versionName}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const docsDirPathLocalized = getDocsDirPathLocalized({
|
const contentPathLocalized = getDocsDirPathLocalized({
|
||||||
siteDir: context.siteDir,
|
siteDir: context.siteDir,
|
||||||
locale: context.i18n.currentLocale,
|
locale: context.i18n.currentLocale,
|
||||||
pluginId: options.id,
|
pluginId: options.id,
|
||||||
|
@ -190,17 +190,17 @@ function getVersionMetadataPaths({
|
||||||
`version-${versionName}-sidebars.json`,
|
`version-${versionName}-sidebars.json`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {docsDirPath, docsDirPathLocalized, sidebarFilePath};
|
return {contentPath, contentPathLocalized, sidebarFilePath};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVersionEditUrls({
|
function getVersionEditUrls({
|
||||||
docsDirPath,
|
contentPath,
|
||||||
docsDirPathLocalized,
|
contentPathLocalized,
|
||||||
context: {siteDir, i18n},
|
context: {siteDir, i18n},
|
||||||
options: {id, path: currentVersionPath, editUrl, editCurrentVersion},
|
options: {id, path: currentVersionPath, editUrl, editCurrentVersion},
|
||||||
}: {
|
}: {
|
||||||
docsDirPath: string;
|
contentPath: string;
|
||||||
docsDirPathLocalized: string;
|
contentPathLocalized: string;
|
||||||
context: Pick<LoadContext, 'siteDir' | 'i18n'>;
|
context: Pick<LoadContext, 'siteDir' | 'i18n'>;
|
||||||
options: Pick<
|
options: Pick<
|
||||||
PluginOptions,
|
PluginOptions,
|
||||||
|
@ -217,7 +217,7 @@ function getVersionEditUrls({
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const editDirPath = editCurrentVersion ? currentVersionPath : docsDirPath;
|
const editDirPath = editCurrentVersion ? currentVersionPath : contentPath;
|
||||||
const editDirPathLocalized = editCurrentVersion
|
const editDirPathLocalized = editCurrentVersion
|
||||||
? getDocsDirPathLocalized({
|
? getDocsDirPathLocalized({
|
||||||
siteDir,
|
siteDir,
|
||||||
|
@ -225,7 +225,7 @@ function getVersionEditUrls({
|
||||||
versionName: CURRENT_VERSION_NAME,
|
versionName: CURRENT_VERSION_NAME,
|
||||||
pluginId: id,
|
pluginId: id,
|
||||||
})
|
})
|
||||||
: docsDirPathLocalized;
|
: contentPathLocalized;
|
||||||
|
|
||||||
const versionPathSegment = posixPath(
|
const versionPathSegment = posixPath(
|
||||||
path.relative(siteDir, path.resolve(siteDir, editDirPath)),
|
path.relative(siteDir, path.resolve(siteDir, editDirPath)),
|
||||||
|
@ -269,8 +269,8 @@ function createVersionMetadata({
|
||||||
}): VersionMetadata {
|
}): VersionMetadata {
|
||||||
const {
|
const {
|
||||||
sidebarFilePath,
|
sidebarFilePath,
|
||||||
docsDirPath,
|
contentPath,
|
||||||
docsDirPathLocalized,
|
contentPathLocalized,
|
||||||
} = getVersionMetadataPaths({
|
} = getVersionMetadataPaths({
|
||||||
versionName,
|
versionName,
|
||||||
context,
|
context,
|
||||||
|
@ -298,8 +298,8 @@ function createVersionMetadata({
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const versionEditUrls = getVersionEditUrls({
|
const versionEditUrls = getVersionEditUrls({
|
||||||
docsDirPath,
|
contentPath,
|
||||||
docsDirPathLocalized,
|
contentPathLocalized,
|
||||||
context,
|
context,
|
||||||
options,
|
options,
|
||||||
});
|
});
|
||||||
|
@ -316,8 +316,8 @@ function createVersionMetadata({
|
||||||
isLast,
|
isLast,
|
||||||
routePriority,
|
routePriority,
|
||||||
sidebarFilePath,
|
sidebarFilePath,
|
||||||
docsDirPath,
|
contentPath,
|
||||||
docsDirPathLocalized,
|
contentPathLocalized,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,14 +328,14 @@ function checkVersionMetadataPaths({
|
||||||
versionMetadata: VersionMetadata;
|
versionMetadata: VersionMetadata;
|
||||||
context: Pick<LoadContext, 'siteDir'>;
|
context: Pick<LoadContext, 'siteDir'>;
|
||||||
}) {
|
}) {
|
||||||
const {versionName, docsDirPath, sidebarFilePath} = versionMetadata;
|
const {versionName, contentPath, sidebarFilePath} = versionMetadata;
|
||||||
const {siteDir} = context;
|
const {siteDir} = context;
|
||||||
|
|
||||||
if (!fs.existsSync(docsDirPath)) {
|
if (!fs.existsSync(contentPath)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The docs folder does not exist for version [${versionName}]. A docs folder is expected to be found at ${path.relative(
|
`The docs folder does not exist for version [${versionName}]. A docs folder is expected to be found at ${path.relative(
|
||||||
siteDir,
|
siteDir,
|
||||||
docsDirPath,
|
contentPath,
|
||||||
)}`,
|
)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -483,8 +483,8 @@ export function readVersionsMetadata({
|
||||||
export function getDocsDirPaths(
|
export function getDocsDirPaths(
|
||||||
versionMetadata: Pick<
|
versionMetadata: Pick<
|
||||||
VersionMetadata,
|
VersionMetadata,
|
||||||
'docsDirPath' | 'docsDirPathLocalized'
|
'contentPath' | 'contentPathLocalized'
|
||||||
>,
|
>,
|
||||||
): [string, string] {
|
): [string, string] {
|
||||||
return [versionMetadata.docsDirPathLocalized, versionMetadata.docsDirPath];
|
return [versionMetadata.contentPathLocalized, versionMetadata.contentPath];
|
||||||
}
|
}
|
||||||
|
|
93
packages/docusaurus-utils/src/markdownLinks.ts
Normal file
93
packages/docusaurus-utils/src/markdownLinks.ts
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/**
|
||||||
|
* 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 {resolve} from 'url';
|
||||||
|
import {aliasedSitePath} from './index';
|
||||||
|
|
||||||
|
export type ContentPaths = {
|
||||||
|
contentPath: string;
|
||||||
|
contentPathLocalized: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BrokenMarkdownLink<T extends ContentPaths> = {
|
||||||
|
filePath: string;
|
||||||
|
contentPaths: T;
|
||||||
|
link: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReplaceMarkdownLinksParams<T extends ContentPaths> = {
|
||||||
|
siteDir: string;
|
||||||
|
fileString: string;
|
||||||
|
filePath: string;
|
||||||
|
contentPaths: T;
|
||||||
|
sourceToPermalink: Record<string, string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReplaceMarkdownLinksReturn<T extends ContentPaths> = {
|
||||||
|
newContent: string;
|
||||||
|
brokenMarkdownLinks: BrokenMarkdownLink<T>[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function replaceMarkdownLinks<T extends ContentPaths>({
|
||||||
|
siteDir,
|
||||||
|
fileString,
|
||||||
|
filePath,
|
||||||
|
contentPaths,
|
||||||
|
sourceToPermalink,
|
||||||
|
}: ReplaceMarkdownLinksParams<T>): ReplaceMarkdownLinksReturn<T> {
|
||||||
|
const {contentPath, contentPathLocalized} = contentPaths;
|
||||||
|
|
||||||
|
const brokenMarkdownLinks: BrokenMarkdownLink<T>[] = [];
|
||||||
|
|
||||||
|
// Replace internal markdown linking (except in fenced blocks).
|
||||||
|
let fencedBlock = false;
|
||||||
|
const lines = fileString.split('\n').map((line) => {
|
||||||
|
if (line.trim().startsWith('```')) {
|
||||||
|
fencedBlock = !fencedBlock;
|
||||||
|
}
|
||||||
|
if (fencedBlock) {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
let modifiedLine = line;
|
||||||
|
// Replace inline-style links or reference-style links e.g:
|
||||||
|
// This is [Document 1](doc1.md) -> we replace this doc1.md with correct link
|
||||||
|
// [doc1]: doc1.md -> we replace this doc1.md with correct link
|
||||||
|
const mdRegex = /(?:(?:\]\()|(?:\]:\s?))(?!https)([^'")\]\s>]+\.mdx?)/g;
|
||||||
|
let mdMatch = mdRegex.exec(modifiedLine);
|
||||||
|
while (mdMatch !== null) {
|
||||||
|
// Replace it to correct html link.
|
||||||
|
const mdLink = mdMatch[1];
|
||||||
|
|
||||||
|
const aliasedSource = (source: string) =>
|
||||||
|
aliasedSitePath(source, siteDir);
|
||||||
|
|
||||||
|
const permalink: string | undefined =
|
||||||
|
sourceToPermalink[aliasedSource(resolve(filePath, mdLink))] ||
|
||||||
|
sourceToPermalink[aliasedSource(`${contentPathLocalized}/${mdLink}`)] ||
|
||||||
|
sourceToPermalink[aliasedSource(`${contentPath}/${mdLink}`)];
|
||||||
|
|
||||||
|
if (permalink) {
|
||||||
|
modifiedLine = modifiedLine.replace(mdLink, permalink);
|
||||||
|
} else {
|
||||||
|
const brokenMarkdownLink: BrokenMarkdownLink<T> = {
|
||||||
|
contentPaths,
|
||||||
|
filePath,
|
||||||
|
link: mdLink,
|
||||||
|
};
|
||||||
|
|
||||||
|
brokenMarkdownLinks.push(brokenMarkdownLink);
|
||||||
|
}
|
||||||
|
mdMatch = mdRegex.exec(modifiedLine);
|
||||||
|
}
|
||||||
|
return modifiedLine;
|
||||||
|
});
|
||||||
|
|
||||||
|
const newContent = lines.join('\n');
|
||||||
|
|
||||||
|
return {newContent, brokenMarkdownLinks};
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue