feat(v2): new docs edit options: editCurrentVersion + editLocalizedDocs (#3949)

* editCurrentVersion initial poc

* ensure edit url allows to edit localized docs

* Add editLocalizedDocs option

* keep editing current version in dev (more convenient)
This commit is contained in:
Sébastien Lorber 2020-12-28 10:25:47 +01:00 committed by GitHub
parent 2791ccc4cf
commit b5c46bd1d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 394 additions and 59 deletions

View file

@ -11,4 +11,8 @@ module.exports = {
url: 'https://your-docusaurus-test-site.com',
baseUrl: '/',
favicon: 'img/favicon.ico',
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
};

View file

@ -1 +1 @@
Hello `1.0.0` ! (translated)
Hello `1.0.0` ! (translated en)

View file

@ -936,7 +936,7 @@ Object {
\\"id\\": \\"version-1.0.0/hello\\",
\\"isDocsHomePage\\": true,
\\"title\\": \\"hello\\",
\\"description\\": \\"Hello 1.0.0 ! (translated)\\",
\\"description\\": \\"Hello 1.0.0 ! (translated en)\\",
\\"source\\": \\"@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md\\",
\\"slug\\": \\"/\\",
\\"permalink\\": \\"/docs/1.0.0/\\",

View file

@ -14,6 +14,7 @@ import {
DocMetadataBase,
MetadataOptions,
VersionMetadata,
PluginOptions,
} from '../types';
import {LoadContext} from '@docusaurus/types';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
@ -42,6 +43,7 @@ ${markdown}
source,
content,
lastUpdate: {},
docsDirPath: 'docs',
filePath: source,
};
};
@ -111,19 +113,19 @@ function createTestUtils({
}
describe('simple site', () => {
async function loadSite() {
async function loadSite(
loadSiteOptions: {options: Partial<PluginOptions>} = {options: {}},
) {
const siteDir = path.join(fixtureDir, 'simple-site');
const context = await loadContext(siteDir);
const options = {
id: DEFAULT_PLUGIN_ID,
...DEFAULT_OPTIONS,
...loadSiteOptions.options,
};
const versionsMetadata = readVersionsMetadata({
context,
options: {
id: DEFAULT_PLUGIN_ID,
...DEFAULT_OPTIONS,
},
options,
});
expect(versionsMetadata.length).toEqual(1);
const [currentVersion] = versionsMetadata;
@ -191,12 +193,14 @@ describe('simple site', () => {
});
test('homePageId doc', async () => {
const {siteDir, context, options, currentVersion} = await loadSite();
const {siteDir, context, options, currentVersion} = await loadSite({
options: {homePageId: 'hello'},
});
const testUtilsLocal = createTestUtils({
siteDir,
context,
options: {...options, homePageId: 'hello'},
options,
versionMetadata: currentVersion,
});
@ -213,12 +217,14 @@ describe('simple site', () => {
});
test('homePageId doc nested', async () => {
const {siteDir, context, options, currentVersion} = await loadSite();
const {siteDir, context, options, currentVersion} = await loadSite({
options: {homePageId: 'foo/bar'},
});
const testUtilsLocal = createTestUtils({
siteDir,
context,
options: {...options, homePageId: 'foo/bar'},
options,
versionMetadata: currentVersion,
});
@ -235,15 +241,16 @@ describe('simple site', () => {
});
test('docs with editUrl', async () => {
const {siteDir, context, options, currentVersion} = await loadSite();
const {siteDir, context, options, currentVersion} = await loadSite({
options: {
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website',
},
});
const testUtilsLocal = createTestUtils({
siteDir,
context,
options: {
...options,
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website',
},
options,
versionMetadata: currentVersion,
});
@ -278,16 +285,17 @@ describe('simple site', () => {
});
test('docs with last update time and author', async () => {
const {siteDir, context, options, currentVersion} = await loadSite();
const {siteDir, context, options, currentVersion} = await loadSite({
options: {
showLastUpdateAuthor: true,
showLastUpdateTime: true,
},
});
const testUtilsLocal = createTestUtils({
siteDir,
context,
options: {
...options,
showLastUpdateAuthor: true,
showLastUpdateTime: true,
},
options,
versionMetadata: currentVersion,
});
@ -361,15 +369,16 @@ describe('simple site', () => {
});
test('docs with slug on doc home', async () => {
const {siteDir, context, options, currentVersion} = await loadSite();
const {siteDir, context, options, currentVersion} = await loadSite({
options: {
homePageId: 'homePageId',
},
});
const testUtilsLocal = createTestUtils({
siteDir,
context,
options: {
...options,
homePageId: 'homePageId',
},
options,
versionMetadata: currentVersion,
});
expect(() => {
@ -388,19 +397,23 @@ describe('simple site', () => {
});
describe('versioned site', () => {
async function loadSite() {
async function loadSite(
loadSiteOptions: {options: Partial<PluginOptions>; locale?: string} = {
options: {},
},
) {
const siteDir = path.join(fixtureDir, 'versioned-site');
const context = await loadContext(siteDir);
const context = await loadContext(siteDir, {
locale: loadSiteOptions.locale,
});
const options = {
id: DEFAULT_PLUGIN_ID,
...DEFAULT_OPTIONS,
...loadSiteOptions.options,
};
const versionsMetadata = readVersionsMetadata({
context,
options: {
id: DEFAULT_PLUGIN_ID,
...DEFAULT_OPTIONS,
},
options,
});
expect(versionsMetadata.length).toEqual(4);
const [
@ -495,7 +508,7 @@ describe('versioned site', () => {
permalink: '/docs/1.0.0/hello',
slug: '/hello',
title: 'hello',
description: 'Hello 1.0.0 ! (translated)',
description: 'Hello 1.0.0 ! (translated en)',
version: '1.0.0',
source:
'@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md',
@ -582,15 +595,17 @@ describe('versioned site', () => {
});
test('translated doc with editUrl', async () => {
const {siteDir, context, options, version100} = await loadSite();
const {siteDir, context, options, version100} = await loadSite({
options: {
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website',
// editCurrentVersion: true,
},
});
const testUtilsLocal = createTestUtils({
siteDir,
context,
options: {
...options,
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website',
},
options,
versionMetadata: version100,
});
@ -601,12 +616,108 @@ describe('versioned site', () => {
permalink: '/docs/1.0.0/hello',
slug: '/hello',
title: 'hello',
description: 'Hello 1.0.0 ! (translated)',
description: 'Hello 1.0.0 ! (translated en)',
version: '1.0.0',
source:
'@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md',
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md',
'https://github.com/facebook/docusaurus/edit/master/website/versioned_docs/version-1.0.0/hello.md',
});
});
test('translated en doc with editUrl and editCurrentVersion=true', async () => {
const {siteDir, context, options, version100} = await loadSite({
options: {
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website',
editCurrentVersion: true,
},
});
const testUtilsLocal = createTestUtils({
siteDir,
context,
options,
versionMetadata: version100,
});
await testUtilsLocal.testMeta(path.join('hello.md'), {
id: 'version-1.0.0/hello',
unversionedId: 'hello',
isDocsHomePage: false,
permalink: '/docs/1.0.0/hello',
slug: '/hello',
title: 'hello',
description: 'Hello 1.0.0 ! (translated en)',
version: '1.0.0',
source:
'@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md',
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website/docs/hello.md',
});
});
test('translated fr doc with editUrl and editLocalizedDocs=true', async () => {
const {siteDir, context, options, version100} = await loadSite({
options: {
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website',
editLocalizedDocs: true,
},
locale: 'fr',
});
const testUtilsLocal = createTestUtils({
siteDir,
context,
options,
versionMetadata: version100,
});
await testUtilsLocal.testMeta(path.join('hello.md'), {
id: 'version-1.0.0/hello',
unversionedId: 'hello',
isDocsHomePage: false,
permalink: '/fr/docs/1.0.0/hello',
slug: '/hello',
title: 'hello',
description: 'Hello 1.0.0 ! (translated fr)',
version: '1.0.0',
source:
'@site/i18n/fr/docusaurus-plugin-content-docs/version-1.0.0/hello.md',
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website/i18n/fr/docusaurus-plugin-content-docs/version-1.0.0/hello.md',
});
});
test('translated fr doc with editUrl and editLocalizedDocs=true + editCurrentVersion=true', async () => {
const {siteDir, context, options, version100} = await loadSite({
options: {
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website',
editCurrentVersion: true,
editLocalizedDocs: true,
},
locale: 'fr',
});
const testUtilsLocal = createTestUtils({
siteDir,
context,
options,
versionMetadata: version100,
});
await testUtilsLocal.testMeta(path.join('hello.md'), {
id: 'version-1.0.0/hello',
unversionedId: 'hello',
isDocsHomePage: false,
permalink: '/fr/docs/1.0.0/hello',
slug: '/hello',
title: 'hello',
description: 'Hello 1.0.0 ! (translated fr)',
version: '1.0.0',
source:
'@site/i18n/fr/docusaurus-plugin-content-docs/version-1.0.0/hello.md',
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website/i18n/fr/docusaurus-plugin-content-docs/current/hello.md',
});
});
});

View file

@ -38,6 +38,8 @@ describe('normalizeDocsPluginOptions', () => {
excludeNextVersionDocs: true,
includeCurrentVersion: false,
disableVersioning: true,
editCurrentVersion: true,
editLocalizedDocs: true,
versions: {
current: {
path: 'next',

View file

@ -381,6 +381,107 @@ describe('versioned site, pluginId=default', () => {
]);
});
test('readVersionsMetadata versioned site with editUrl', async () => {
const {
defaultOptions,
defaultContext,
vCurrent,
v101,
v100,
vwithSlugs,
} = await loadSite();
const versionsMetadata = readVersionsMetadata({
options: {
...defaultOptions,
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website/',
},
context: defaultContext,
});
expect(versionsMetadata).toEqual([
{
...vCurrent,
versionEditUrl:
'https://github.com/facebook/docusaurus/edit/master/website/docs',
versionEditUrlLocalized:
'https://github.com/facebook/docusaurus/edit/master/website/i18n/en/docusaurus-plugin-content-docs/current',
},
{
...v101,
versionEditUrl:
'https://github.com/facebook/docusaurus/edit/master/website/versioned_docs/version-1.0.1',
versionEditUrlLocalized:
'https://github.com/facebook/docusaurus/edit/master/website/i18n/en/docusaurus-plugin-content-docs/version-1.0.1',
},
{
...v100,
versionEditUrl:
'https://github.com/facebook/docusaurus/edit/master/website/versioned_docs/version-1.0.0',
versionEditUrlLocalized:
'https://github.com/facebook/docusaurus/edit/master/website/i18n/en/docusaurus-plugin-content-docs/version-1.0.0',
},
{
...vwithSlugs,
versionEditUrl:
'https://github.com/facebook/docusaurus/edit/master/website/versioned_docs/version-withSlugs',
versionEditUrlLocalized:
'https://github.com/facebook/docusaurus/edit/master/website/i18n/en/docusaurus-plugin-content-docs/version-withSlugs',
},
]);
});
test('readVersionsMetadata versioned site with editUrl and editCurrentVersion=true', async () => {
const {
defaultOptions,
defaultContext,
vCurrent,
v101,
v100,
vwithSlugs,
} = await loadSite();
const versionsMetadata = readVersionsMetadata({
options: {
...defaultOptions,
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website/',
editCurrentVersion: true,
},
context: defaultContext,
});
expect(versionsMetadata).toEqual([
{
...vCurrent,
versionEditUrl:
'https://github.com/facebook/docusaurus/edit/master/website/docs',
versionEditUrlLocalized:
'https://github.com/facebook/docusaurus/edit/master/website/i18n/en/docusaurus-plugin-content-docs/current',
},
{
...v101,
versionEditUrl:
'https://github.com/facebook/docusaurus/edit/master/website/docs',
versionEditUrlLocalized:
'https://github.com/facebook/docusaurus/edit/master/website/i18n/en/docusaurus-plugin-content-docs/current',
},
{
...v100,
versionEditUrl:
'https://github.com/facebook/docusaurus/edit/master/website/docs',
versionEditUrlLocalized:
'https://github.com/facebook/docusaurus/edit/master/website/i18n/en/docusaurus-plugin-content-docs/current',
},
{
...vwithSlugs,
versionEditUrl:
'https://github.com/facebook/docusaurus/edit/master/website/docs',
versionEditUrlLocalized:
'https://github.com/facebook/docusaurus/edit/master/website/i18n/en/docusaurus-plugin-content-docs/current',
},
]);
});
test('readVersionsMetadata versioned site with onlyIncludeVersions option', async () => {
const {defaultOptions, defaultContext, v101, vwithSlugs} = await loadSite();

View file

@ -70,18 +70,18 @@ export async function readDocFile(
source: string,
options: LastUpdateOptions,
): Promise<DocFile> {
const folderPath = await getFolderContainingFile(
const docsDirPath = await getFolderContainingFile(
getDocsDirPaths(versionMetadata),
source,
);
const filePath = path.join(folderPath, source);
const filePath = path.join(docsDirPath, source);
const [content, lastUpdate] = await Promise.all([
fs.readFile(filePath, 'utf-8'),
readLastUpdateData(filePath, options),
]);
return {source, content, lastUpdate, filePath};
return {source, content, lastUpdate, docsDirPath, filePath};
}
export async function readVersionDocs(
@ -110,15 +110,24 @@ export function processDocMetadata({
context: LoadContext;
options: MetadataOptions;
}): DocMetadataBase {
const {source, content, lastUpdate, filePath} = docFile;
const {editUrl, homePageId} = options;
const {source, content, lastUpdate, docsDirPath, filePath} = docFile;
const {homePageId} = options;
const {siteDir} = context;
// ex: api/myDoc -> api
// ex: myDoc -> .
const docsFileDirName = path.dirname(source);
const docsEditUrl = getEditUrl(path.relative(siteDir, filePath), editUrl);
const relativeFilePath = path.relative(docsDirPath, filePath);
const isLocalized = docsDirPath === versionMetadata.docsDirPathLocalized;
const versionEditUrl =
isLocalized && options.editLocalizedDocs
? versionMetadata.versionEditUrlLocalized
: versionMetadata.versionEditUrl;
const docsEditUrl = getEditUrl(relativeFilePath, versionEditUrl);
const {frontMatter = {}, excerpt} = parseMarkdownString(content);
const {sidebar_label, custom_edit_url} = frontMatter;

View file

@ -37,6 +37,8 @@ export const DEFAULT_OPTIONS: Omit<PluginOptions, 'id'> = {
disableVersioning: false,
lastVersion: undefined,
versions: {},
editCurrentVersion: false,
editLocalizedDocs: false,
};
const VersionOptionsSchema = Joi.object({
@ -51,6 +53,8 @@ const VersionsOptionsSchema = Joi.object()
export const OptionsSchema = Joi.object({
path: Joi.string().default(DEFAULT_OPTIONS.path),
editUrl: URISchema,
editCurrentVersion: Joi.boolean().default(DEFAULT_OPTIONS.editCurrentVersion),
editLocalizedDocs: Joi.boolean().default(DEFAULT_OPTIONS.editLocalizedDocs),
routeBasePath: Joi.string()
// '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
// .allow('') ""

View file

@ -9,7 +9,8 @@
/// <reference types="@docusaurus/module-type-aliases" />
export type DocFile = {
filePath: string;
docsDirPath: string; // /!\ may be localized
filePath: string; // /!\ may be localized
source: string;
content: string;
lastUpdate: LastUpdateData;
@ -21,6 +22,8 @@ export type VersionMetadata = {
versionName: VersionName; // 1.0.0
versionLabel: string; // Version 1.0.0
versionPath: string; // /baseUrl/docs/1.0.0
versionEditUrl?: string | undefined;
versionEditUrlLocalized?: string | undefined;
isLast: boolean;
docsDirPath: string; // "versioned_docs/version-1.0.0"
docsDirPathLocalized: string; // "i18n/fr/version-1.0.0/default"
@ -32,6 +35,8 @@ export type MetadataOptions = {
routeBasePath: string;
homePageId?: string;
editUrl?: string;
editCurrentVersion: boolean;
editLocalizedDocs: boolean;
showLastUpdateTime?: boolean;
showLastUpdateAuthor?: boolean;
};

View file

@ -22,7 +22,7 @@ import {
import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
import {LoadContext} from '@docusaurus/types';
import {getPluginI18nPath, normalizeUrl} from '@docusaurus/utils';
import {getPluginI18nPath, normalizeUrl, posixPath} from '@docusaurus/utils';
import {difference} from 'lodash';
import chalk from 'chalk';
@ -131,6 +131,30 @@ function readVersionNames(
return versions;
}
function getDocsDirPathLocalized({
siteDir,
locale,
pluginId,
versionName,
}: {
siteDir: string;
locale: string;
pluginId: string;
versionName: string;
}) {
return getPluginI18nPath({
siteDir,
locale,
pluginName: 'docusaurus-plugin-content-docs',
pluginId,
subPaths: [
versionName === CURRENT_VERSION_NAME
? CURRENT_VERSION_NAME
: `version-${versionName}`,
],
});
}
function getVersionMetadataPaths({
versionName,
context,
@ -152,16 +176,11 @@ function getVersionMetadataPaths({
`version-${versionName}`,
);
const docsDirPathLocalized = getPluginI18nPath({
const docsDirPathLocalized = getDocsDirPathLocalized({
siteDir: context.siteDir,
locale: context.i18n.currentLocale,
pluginName: 'docusaurus-plugin-content-docs',
pluginId: options.id,
subPaths: [
versionName === CURRENT_VERSION_NAME
? CURRENT_VERSION_NAME
: `version-${versionName}`,
],
versionName,
});
const sidebarFilePath = isCurrentVersion
@ -174,6 +193,54 @@ function getVersionMetadataPaths({
return {docsDirPath, docsDirPathLocalized, sidebarFilePath};
}
function getVersionEditUrls({
docsDirPath,
docsDirPathLocalized,
context: {siteDir, i18n},
options: {id, path: currentVersionPath, editUrl, editCurrentVersion},
}: {
docsDirPath: string;
docsDirPathLocalized: string;
context: Pick<LoadContext, 'siteDir' | 'i18n'>;
options: Pick<
PluginOptions,
'id' | 'path' | 'editUrl' | 'editCurrentVersion'
>;
}): {versionEditUrl: string; versionEditUrlLocalized: string} | undefined {
if (!editUrl) {
return undefined;
}
const editDirPath = editCurrentVersion ? currentVersionPath : docsDirPath;
const editDirPathLocalized = editCurrentVersion
? getDocsDirPathLocalized({
siteDir,
locale: i18n.currentLocale,
versionName: CURRENT_VERSION_NAME,
pluginId: id,
})
: docsDirPathLocalized;
const versionPathSegment = posixPath(
path.relative(siteDir, path.resolve(siteDir, editDirPath)),
);
const versionPathSegmentLocalized = posixPath(
path.relative(siteDir, path.resolve(siteDir, editDirPathLocalized)),
);
const versionEditUrl = normalizeUrl([editUrl, versionPathSegment]);
const versionEditUrlLocalized = normalizeUrl([
editUrl,
versionPathSegmentLocalized,
]);
return {
versionEditUrl,
versionEditUrlLocalized,
};
}
function createVersionMetadata({
versionName,
isLast,
@ -185,7 +252,13 @@ function createVersionMetadata({
context: Pick<LoadContext, 'siteDir' | 'baseUrl' | 'i18n'>;
options: Pick<
PluginOptions,
'id' | 'path' | 'sidebarPath' | 'routeBasePath' | 'versions'
| 'id'
| 'path'
| 'sidebarPath'
| 'routeBasePath'
| 'versions'
| 'editUrl'
| 'editCurrentVersion'
>;
}): VersionMetadata {
const {
@ -218,6 +291,13 @@ function createVersionMetadata({
versionPathPart,
]);
const versionEditUrls = getVersionEditUrls({
docsDirPath,
docsDirPathLocalized,
context,
options,
});
// Because /docs/:route` should always be after `/docs/versionName/:route`.
const routePriority = versionPathPart === '' ? -1 : undefined;
@ -225,6 +305,8 @@ function createVersionMetadata({
versionName,
versionLabel,
versionPath,
versionEditUrl: versionEditUrls?.versionEditUrl,
versionEditUrlLocalized: versionEditUrls?.versionEditUrlLocalized,
isLast,
routePriority,
sidebarFilePath,
@ -354,6 +436,8 @@ export function readVersionsMetadata({
| 'lastVersion'
| 'versions'
| 'onlyIncludeVersions'
| 'editUrl'
| 'editCurrentVersion'
>;
}): VersionMetadata[] {
const versionNamesUnfiltered = readVersionNames(context.siteDir, options);

View file

@ -35,6 +35,18 @@ module.exports = {
* Example: 'https://github.com/facebook/docusaurus/edit/master/website/'
*/
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website/',
/**
* When docs are versioned, the edit url will link to the doc
* in current version, instead of the versioned doc.
* Useful if you don't want users to submit doc pull-requests to older versions.
*/
editCurrentVersion: false,
/**
* When docs are localized, the edit url will target the localized doc,
* instead of the original unlocalized doc.
* Useful if you commit localized docs to git, instead of using a translation service.
*/
editLocalizedDocs: false,
/**
* URL route for the docs section of your site.
* *DO NOT* include a trailing slash.

View file

@ -77,6 +77,7 @@ module.exports = {
id: 'community',
path: 'community',
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website/',
editCurrentVersion: true,
routeBasePath: 'community',
sidebarPath: require.resolve('./sidebarsCommunity.js'),
showLastUpdateAuthor: true,
@ -205,20 +206,21 @@ module.exports = {
sidebarPath: require.resolve('./sidebars.js'),
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website/',
editCurrentVersion: true,
showLastUpdateAuthor: true,
showLastUpdateTime: true,
remarkPlugins: [
[require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}],
],
disableVersioning: isVersioningDisabled,
lastVersion: 'current',
lastVersion: isDev ? 'current' : undefined,
onlyIncludeVersions:
!isVersioningDisabled && (isDev || isDeployPreview)
? ['current', ...versions.slice(0, 2)]
: undefined,
versions: {
current: {
label: `${getNextAlphaVersionName()} (unreleased)`,
label: `${getNextAlphaVersionName()} 🚧`,
},
},
},