2020-08-17 11:50:22 -04:00
/ * *
* 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 path from 'path' ;
import fs from 'fs-extra' ;
import {
aliasedSitePath ,
getEditUrl ,
2020-11-26 06:16:46 -05:00
getFolderContainingFile ,
2020-11-30 10:42:58 -05:00
normalizeUrl ,
parseMarkdownString ,
2021-01-29 09:35:25 -05:00
posixPath ,
2021-03-05 09:30:09 -05:00
getDateTimeFormat ,
2020-08-17 11:50:22 -04:00
} from '@docusaurus/utils' ;
import { LoadContext } from '@docusaurus/types' ;
import { getFileLastUpdate } from './lastUpdate' ;
import {
2020-11-30 10:42:58 -05:00
DocFile ,
2020-08-17 11:50:22 -04:00
DocMetadataBase ,
LastUpdateData ,
MetadataOptions ,
PluginOptions ,
2020-11-30 10:42:58 -05:00
VersionMetadata ,
2020-08-17 11:50:22 -04:00
} from './types' ;
import getSlug from './slug' ;
import { CURRENT_VERSION_NAME } from './constants' ;
import globby from 'globby' ;
2020-11-26 06:16:46 -05:00
import { getDocsDirPaths } from './versions' ;
2020-08-17 11:50:22 -04:00
type LastUpdateOptions = Pick <
PluginOptions ,
'showLastUpdateAuthor' | 'showLastUpdateTime'
> ;
async function readLastUpdateData (
filePath : string ,
options : LastUpdateOptions ,
) : Promise < LastUpdateData > {
const { showLastUpdateAuthor , showLastUpdateTime } = options ;
if ( showLastUpdateAuthor || showLastUpdateTime ) {
// Use fake data in dev for faster development.
const fileLastUpdateData =
process . env . NODE_ENV === 'production'
? await getFileLastUpdate ( filePath )
: {
author : 'Author' ,
timestamp : 1539502055 ,
} ;
if ( fileLastUpdateData ) {
const { author , timestamp } = fileLastUpdateData ;
return {
lastUpdatedAt : showLastUpdateTime ? timestamp : undefined ,
lastUpdatedBy : showLastUpdateAuthor ? author : undefined ,
} ;
}
}
return { } ;
}
export async function readDocFile (
2020-11-26 06:16:46 -05:00
versionMetadata : Pick <
VersionMetadata ,
'docsDirPath' | 'docsDirPathLocalized'
> ,
2020-08-17 11:50:22 -04:00
source : string ,
options : LastUpdateOptions ,
) : Promise < DocFile > {
2020-12-28 04:25:47 -05:00
const docsDirPath = await getFolderContainingFile (
2020-11-26 06:16:46 -05:00
getDocsDirPaths ( versionMetadata ) ,
source ,
) ;
2020-12-28 04:25:47 -05:00
const filePath = path . join ( docsDirPath , source ) ;
2020-11-26 06:16:46 -05:00
2020-08-17 11:50:22 -04:00
const [ content , lastUpdate ] = await Promise . all ( [
fs . readFile ( filePath , 'utf-8' ) ,
readLastUpdateData ( filePath , options ) ,
] ) ;
2020-12-28 04:25:47 -05:00
return { source , content , lastUpdate , docsDirPath , filePath } ;
2020-08-17 11:50:22 -04:00
}
export async function readVersionDocs (
versionMetadata : VersionMetadata ,
options : Pick <
PluginOptions ,
'include' | 'showLastUpdateAuthor' | 'showLastUpdateTime'
> ,
) : Promise < DocFile [ ] > {
const sources = await globby ( options . include , {
cwd : versionMetadata.docsDirPath ,
} ) ;
return Promise . all (
2020-11-26 06:16:46 -05:00
sources . map ( ( source ) = > readDocFile ( versionMetadata , source , options ) ) ,
2020-08-17 11:50:22 -04:00
) ;
}
export function processDocMetadata ( {
docFile ,
versionMetadata ,
context ,
options ,
} : {
docFile : DocFile ;
versionMetadata : VersionMetadata ;
context : LoadContext ;
options : MetadataOptions ;
} ) : DocMetadataBase {
2020-12-28 04:25:47 -05:00
const { source , content , lastUpdate , docsDirPath , filePath } = docFile ;
const { homePageId } = options ;
2021-03-05 09:30:09 -05:00
const { siteDir , i18n } = context ;
2020-08-17 11:50:22 -04:00
// ex: api/myDoc -> api
// ex: myDoc -> .
const docsFileDirName = path . dirname ( source ) ;
const { frontMatter = { } , excerpt } = parseMarkdownString ( content ) ;
const { sidebar_label , custom_edit_url } = frontMatter ;
const baseID : string =
frontMatter . id || path . basename ( source , path . extname ( source ) ) ;
if ( baseID . includes ( '/' ) ) {
throw new Error ( ` Document id [ ${ baseID } ] cannot include "/". ` ) ;
}
// TODO legacy retrocompatibility
// The same doc in 2 distinct version could keep the same id,
// we just need to namespace the data by version
const versionIdPart =
versionMetadata . versionName === CURRENT_VERSION_NAME
? ''
: ` version- ${ versionMetadata . versionName } / ` ;
// TODO legacy retrocompatibility
// I think it's bad to affect the frontmatter id with the dirname
const dirNameIdPart = docsFileDirName === '.' ? '' : ` ${ docsFileDirName } / ` ;
// TODO legacy composite id, requires a breaking change to modify this
const id = ` ${ versionIdPart } ${ dirNameIdPart } ${ baseID } ` ;
const unversionedId = ` ${ dirNameIdPart } ${ baseID } ` ;
// TODO remove soon, deprecated homePageId
const isDocsHomePage = unversionedId === ( homePageId ? ? '_index' ) ;
if ( frontMatter . slug && isDocsHomePage ) {
throw new Error (
2020-09-11 14:33:08 -04:00
` The docs homepage (homePageId= ${ homePageId } ) is not allowed to have a frontmatter slug= ${ frontMatter . slug } => you have to choose either homePageId or slug, not both ` ,
2020-08-17 11:50:22 -04:00
) ;
}
const docSlug = isDocsHomePage
? '/'
: getSlug ( {
baseID ,
dirName : docsFileDirName ,
frontmatterSlug : frontMatter.slug ,
} ) ;
// Default title is the id.
const title : string = frontMatter . title || baseID ;
const description : string = frontMatter . description || excerpt ;
const permalink = normalizeUrl ( [ versionMetadata . versionPath , docSlug ] ) ;
2021-02-17 05:48:33 -05:00
function getDocEditUrl() {
const relativeFilePath = path . relative ( docsDirPath , filePath ) ;
if ( typeof options . editUrl === 'function' ) {
return options . editUrl ( {
version : versionMetadata.versionName ,
versionDocsDirPath : posixPath (
path . relative ( siteDir , versionMetadata . docsDirPath ) ,
) ,
docPath : posixPath ( relativeFilePath ) ,
permalink ,
locale : context.i18n.currentLocale ,
} ) ;
} else if ( typeof options . editUrl === 'string' ) {
const isLocalized = docsDirPath === versionMetadata . docsDirPathLocalized ;
const baseVersionEditUrl =
isLocalized && options . editLocalizedFiles
? versionMetadata . versionEditUrlLocalized
: versionMetadata . versionEditUrl ;
return getEditUrl ( relativeFilePath , baseVersionEditUrl ) ;
} else {
return undefined ;
}
}
2020-08-17 11:50:22 -04:00
// Assign all of object properties during instantiation (if possible) for
// NodeJS optimization.
// Adding properties to object after instantiation will cause hidden
// class transitions.
2020-11-30 10:42:58 -05:00
return {
2020-08-17 11:50:22 -04:00
unversionedId ,
id ,
isDocsHomePage ,
title ,
description ,
source : aliasedSitePath ( filePath , siteDir ) ,
slug : docSlug ,
permalink ,
2021-02-17 05:48:33 -05:00
editUrl : custom_edit_url !== undefined ? custom_edit_url : getDocEditUrl ( ) ,
2020-08-17 11:50:22 -04:00
version : versionMetadata.versionName ,
lastUpdatedBy : lastUpdate.lastUpdatedBy ,
lastUpdatedAt : lastUpdate.lastUpdatedAt ,
2021-03-05 09:30:09 -05:00
formattedLastUpdatedAt : lastUpdate.lastUpdatedAt
? getDateTimeFormat ( i18n . currentLocale ) ( i18n . currentLocale ) . format (
lastUpdate . lastUpdatedAt * 1000 ,
)
: undefined ,
2020-08-17 11:50:22 -04:00
sidebar_label ,
} ;
}