mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-10 07:37:19 +02:00
feat: add last contributor to each document (#980)
* add contributor list to each document * handle case where there is no github repo * Move to v1 * Revert "handle case where there is no github repo" This reverts commita76a887901
. * Revert "add contributor list to each document" This reverts commitc0cc79f995
. * Add last updated by field * -Include enableUpdateBy in the config - Rename css class to be more general * Fix lint * Refactor : - s/getGitlastupdated/getGitLastUpdatedTime - refactor part in getGitLastUpdated[Time|By] that overlaps - remove getAuthorInformation * -Modify the display logic -Refactor the utils, combine lastupdatedtime and lastupdatedby -Replace the test * Modify docs based on the display change * Update docs for url and baseurl For more clarity and to make relationship more clear * Update support for Web App Manifest (#1046) * misc: update nits * misc: update nits
This commit is contained in:
parent
4a7e1ea189
commit
8cf9afe4ac
7 changed files with 158 additions and 62 deletions
|
@ -18,25 +18,31 @@ The `siteConfig` object contains the bulk of the configuration settings for your
|
||||||
### Mandatory Fields
|
### Mandatory Fields
|
||||||
|
|
||||||
#### `baseUrl` [string]
|
#### `baseUrl` [string]
|
||||||
|
|
||||||
baseUrl for your site. This can also be considered the path after the host. For example, `/metro/` is the baseUrl of https://facebook.github.io/metro/. For urls that have no path, the baseUrl should be set to `/`. This field is related to the [`url` field](#url-string).
|
baseUrl for your site. This can also be considered the path after the host. For example, `/metro/` is the baseUrl of https://facebook.github.io/metro/. For urls that have no path, the baseUrl should be set to `/`. This field is related to the [`url` field](#url-string).
|
||||||
|
|
||||||
#### `colors` [object]
|
#### `colors` [object]
|
||||||
|
|
||||||
Color configurations for the site.
|
Color configurations for the site.
|
||||||
|
|
||||||
* `primaryColor` is the color used the header navigation bar and sidebars.
|
- `primaryColor` is the color used the header navigation bar and sidebars.
|
||||||
* `secondaryColor` is the color seen in the second row of the header navigation bar when the site window is narrow (including on mobile).
|
- `secondaryColor` is the color seen in the second row of the header navigation bar when the site window is narrow (including on mobile).
|
||||||
* Custom color configurations can also be added. For example, if user styles are added with colors specified as `$myColor`, then adding a `myColor` field to `colors` will allow you to easily configure this color.
|
- Custom color configurations can also be added. For example, if user styles are added with colors specified as `$myColor`, then adding a `myColor` field to `colors` will allow you to easily configure this color.
|
||||||
|
|
||||||
#### `copyright` [string]
|
#### `copyright` [string]
|
||||||
|
|
||||||
The copyright string at footer of site and within feed
|
The copyright string at footer of site and within feed
|
||||||
|
|
||||||
#### `favicon` [string]
|
#### `favicon` [string]
|
||||||
|
|
||||||
URL for site favicon.
|
URL for site favicon.
|
||||||
|
|
||||||
#### `headerIcon` [string]
|
#### `headerIcon` [string]
|
||||||
|
|
||||||
URL for icon used in header navigation bar.
|
URL for icon used in header navigation bar.
|
||||||
|
|
||||||
#### `headerLinks` [array]
|
#### `headerLinks` [array]
|
||||||
|
|
||||||
Links that will be used in the header navigation bar. The `label` field of each object will be the link text and will also be translated for each language.
|
Links that will be used in the header navigation bar. The `label` field of each object will be the link text and will also be translated for each language.
|
||||||
|
|
||||||
Example Usage:
|
Example Usage:
|
||||||
|
@ -59,45 +65,57 @@ headerLinks: [
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `noIndex` [boolean]
|
#### `noIndex` [boolean]
|
||||||
|
|
||||||
Boolean. If true, Docusaurus will politely ask crawlers and search engines to avoid indexing your site. This is done with a header tag and so only applies to docs and pages. Will not attempt to hide static resources. This is a best effort request. Malicious crawlers can and will still index your site.
|
Boolean. If true, Docusaurus will politely ask crawlers and search engines to avoid indexing your site. This is done with a header tag and so only applies to docs and pages. Will not attempt to hide static resources. This is a best effort request. Malicious crawlers can and will still index your site.
|
||||||
|
|
||||||
#### `organizationName` [string]
|
#### `organizationName` [string]
|
||||||
|
|
||||||
GitHub username of the organization or user hosting this project. This is used by the publishing script to determine where your GitHub pages website will be hosted.
|
GitHub username of the organization or user hosting this project. This is used by the publishing script to determine where your GitHub pages website will be hosted.
|
||||||
|
|
||||||
#### `projectName` [string]
|
#### `projectName` [string]
|
||||||
|
|
||||||
Project name. This must match your GitHub repository project name (case-sensitive).
|
Project name. This must match your GitHub repository project name (case-sensitive).
|
||||||
|
|
||||||
#### `tagline` [string]
|
#### `tagline` [string]
|
||||||
|
|
||||||
Tagline for your website.
|
Tagline for your website.
|
||||||
|
|
||||||
#### `title` [string]
|
#### `title` [string]
|
||||||
|
|
||||||
Title for your website.
|
Title for your website.
|
||||||
|
|
||||||
#### `url` [string]
|
#### `url` [string]
|
||||||
|
|
||||||
URL for your website. This can also be considered the top-level hostname. For example, `https://facebook.github.io` is the url of https://facebook.github.io/metro/, and `https://docusaurus.io` is the url for https://docusaurus.io. This field is related to the [`baseUrl` field](#baseurl-string).
|
URL for your website. This can also be considered the top-level hostname. For example, `https://facebook.github.io` is the url of https://facebook.github.io/metro/, and `https://docusaurus.io` is the url for https://docusaurus.io. This field is related to the [`baseUrl` field](#baseurl-string).
|
||||||
|
|
||||||
### Optional Fields
|
### Optional Fields
|
||||||
|
|
||||||
#### `algolia` [object]
|
#### `algolia` [object]
|
||||||
|
|
||||||
Information for Algolia search integration. If this field is excluded, the search bar will not appear in the header. You must specify two values for this field, and one (`appId`) is optional.
|
Information for Algolia search integration. If this field is excluded, the search bar will not appear in the header. You must specify two values for this field, and one (`appId`) is optional.
|
||||||
|
|
||||||
* `apiKey` - the Algolia provided API key for your search.
|
- `apiKey` - the Algolia provided API key for your search.
|
||||||
* `indexName` - the Algolia provided index name for your search (usually this is the project name)
|
- `indexName` - the Algolia provided index name for your search (usually this is the project name)
|
||||||
* `appId` - Algolia provides a default scraper for your docs. If you provide your own, you will probably get this id from them.
|
- `appId` - Algolia provides a default scraper for your docs. If you provide your own, you will probably get this id from them.
|
||||||
|
|
||||||
#### `blogSidebarCount` [number]
|
#### `blogSidebarCount` [number]
|
||||||
|
|
||||||
Control the number of blog posts that show up in the sidebar. See the [adding a blog docs](guides-blog.md#changing-how-many-blog-posts-show-on-sidebar) for more information.
|
Control the number of blog posts that show up in the sidebar. See the [adding a blog docs](guides-blog.md#changing-how-many-blog-posts-show-on-sidebar) for more information.
|
||||||
|
|
||||||
#### `blogSidebarTitle` [string]
|
#### `blogSidebarTitle` [string]
|
||||||
|
|
||||||
Control the title of the blog sidebar. See the [adding a blog docs](guides-blog.md#changing-the-sidebar-title) for more information.
|
Control the title of the blog sidebar. See the [adding a blog docs](guides-blog.md#changing-the-sidebar-title) for more information.
|
||||||
|
|
||||||
#### `cleanUrl` [string]
|
#### `cleanUrl` [string]
|
||||||
|
|
||||||
If `true`, allow URLs with no `html` extension. For example, a request to URL https://docusaurus.io/docs/installation will returns the same result as https://docusaurus.io/docs/installation.html.
|
If `true`, allow URLs with no `html` extension. For example, a request to URL https://docusaurus.io/docs/installation will returns the same result as https://docusaurus.io/docs/installation.html.
|
||||||
|
|
||||||
#### `cname` [string]
|
#### `cname` [string]
|
||||||
|
|
||||||
The CNAME for your website. It will go into a `CNAME` file when your site is built.
|
The CNAME for your website. It will go into a `CNAME` file when your site is built.
|
||||||
|
|
||||||
#### `customDocsPath` [string]
|
#### `customDocsPath` [string]
|
||||||
|
|
||||||
By default, Docusaurus expects your documentation to be in a directory called `docs`. This directory is at the same level as the `website` directory (i.e., not inside the `website` directory). You can specify a custom path to your documentation with this field.
|
By default, Docusaurus expects your documentation to be in a directory called `docs`. This directory is at the same level as the `website` directory (i.e., not inside the `website` directory). You can specify a custom path to your documentation with this field.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -109,30 +127,43 @@ customDocsPath: 'website-docs';
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `defaultVersionShown` [string]
|
#### `defaultVersionShown` [string]
|
||||||
|
|
||||||
The default version for the site to be shown. If this is not set, the latest version will be shown.
|
The default version for the site to be shown. If this is not set, the latest version will be shown.
|
||||||
|
|
||||||
#### `disableHeaderTitle` [boolean]
|
#### `disableHeaderTitle` [boolean]
|
||||||
|
|
||||||
An option to disable showing the title in the header next to the header icon. Exclude this field to keep the header as normal, otherwise set to `true`.
|
An option to disable showing the title in the header next to the header icon. Exclude this field to keep the header as normal, otherwise set to `true`.
|
||||||
|
|
||||||
#### `disableTitleTagline` [boolean]
|
#### `disableTitleTagline` [boolean]
|
||||||
|
|
||||||
An option to disable showing the tagline in the title of main pages. Exclude this field to keep page titles as `Title • Tagline`. Set to `true` to make page titles just `Title`.
|
An option to disable showing the tagline in the title of main pages. Exclude this field to keep page titles as `Title • Tagline`. Set to `true` to make page titles just `Title`.
|
||||||
|
|
||||||
#### `editUrl` [string]
|
#### `editUrl` [string]
|
||||||
|
|
||||||
URL for editing docs, usage example: `editUrl + 'en/doc1.md'`. If this field is omitted, there will be no "Edit this Doc" button for each document.
|
URL for editing docs, usage example: `editUrl + 'en/doc1.md'`. If this field is omitted, there will be no "Edit this Doc" button for each document.
|
||||||
|
|
||||||
#### `enableUpdateTime` [string]
|
#### `enableUpdateBy` [boolean]
|
||||||
An option to enable the docs showing last update time. Set to `true` to show a line at the bottom right corner of each doc page as `Last Updated: dd/mm/yyyy hh:MM:ss Z`.
|
|
||||||
|
An option to enable the docs showing the author who last updated the doc. Set to `true` to show a line at the bottom right corner of each doc page as `Last updated by <Author Name>`.
|
||||||
|
|
||||||
|
#### `enableUpdateTime` [boolean]
|
||||||
|
|
||||||
|
An option to enable the docs showing last update time. Set to `true` to show a line at the bottom right corner of each doc page as `Last updated on <date>`.
|
||||||
|
|
||||||
#### `facebookAppId` [string]
|
#### `facebookAppId` [string]
|
||||||
|
|
||||||
If you want Facebook Like/Share buttons in the footer and at the bottom of your blog posts, provide a [Facebook application id](https://www.facebook.com/help/audiencenetwork/804209223039296).
|
If you want Facebook Like/Share buttons in the footer and at the bottom of your blog posts, provide a [Facebook application id](https://www.facebook.com/help/audiencenetwork/804209223039296).
|
||||||
|
|
||||||
#### `facebookComments` [boolean]
|
#### `facebookComments` [boolean]
|
||||||
|
|
||||||
Set this to `true` if you want to enable Facebook comments at the bottom of your blog post. `facebookAppId` has to be also set.
|
Set this to `true` if you want to enable Facebook comments at the bottom of your blog post. `facebookAppId` has to be also set.
|
||||||
|
|
||||||
#### `facebookPixelId` [string]
|
#### `facebookPixelId` [string]
|
||||||
|
|
||||||
[Facebook Pixel](https://www.facebook.com/business/a/facebook-pixel) ID to track page views.
|
[Facebook Pixel](https://www.facebook.com/business/a/facebook-pixel) ID to track page views.
|
||||||
|
|
||||||
#### `fonts` [object]
|
#### `fonts` [object]
|
||||||
|
|
||||||
Font-family CSS configuration for the site. If a font family is specified in `siteConfig.js` as `$myFont`, then adding a `myFont` key to an array in `fonts` will allow you to configure the font. Items appearing earlier in the array will take priority of later elements, so ordering of the fonts matter.
|
Font-family CSS configuration for the site. If a font family is specified in `siteConfig.js` as `$myFont`, then adding a `myFont` key to an array in `fonts` will allow you to configure the font. Items appearing earlier in the array will take priority of later elements, so ordering of the fonts matter.
|
||||||
|
|
||||||
In the below example, we have two sets of font configurations, `myFont` and `myOtherFont`. `Times New Roman` is the preferred font in `myFont`. `-apple-system` is the preferred in `myOtherFont`.
|
In the below example, we have two sets of font configurations, `myFont` and `myOtherFont`. `Times New Roman` is the preferred font in `myFont`. `-apple-system` is the preferred in `myOtherFont`.
|
||||||
|
@ -159,18 +190,23 @@ h1 {
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `footerIcon` [string]
|
#### `footerIcon` [string]
|
||||||
|
|
||||||
URL for a footer icon. Currently used in the `core/Footer.js` file provided as an example, but it can be removed from that file.
|
URL for a footer icon. Currently used in the `core/Footer.js` file provided as an example, but it can be removed from that file.
|
||||||
|
|
||||||
#### `gaTrackingId` [string]
|
#### `gaTrackingId` [string]
|
||||||
|
|
||||||
Google Analytics tracking ID to track page views.
|
Google Analytics tracking ID to track page views.
|
||||||
|
|
||||||
#### `gaGtag` [boolean]
|
#### `gaGtag` [boolean]
|
||||||
|
|
||||||
Set this to `true` if you want to use [global site tags (gtag.js)](https://developers.google.com/gtagjs/) for Google analytics instead of `analytics.js`.
|
Set this to `true` if you want to use [global site tags (gtag.js)](https://developers.google.com/gtagjs/) for Google analytics instead of `analytics.js`.
|
||||||
|
|
||||||
#### `githubHost` [string]
|
#### `githubHost` [string]
|
||||||
|
|
||||||
Hostname of your server. Useful if you are using GitHub Enterprise.
|
Hostname of your server. Useful if you are using GitHub Enterprise.
|
||||||
|
|
||||||
#### `highlight`
|
#### `highlight`
|
||||||
|
|
||||||
[Syntax highlighting](api-doc-markdown.md) options:
|
[Syntax highlighting](api-doc-markdown.md) options:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -203,56 +239,73 @@ Hostname of your server. Useful if you are using GitHub Enterprise.
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `manifest` [string]
|
#### `manifest` [string]
|
||||||
|
|
||||||
Path to your web app manifest (e.g., `manifest.json`). This will add a `<link>` tag to `<head>` with `rel` as `"manifest"` and `href` as the provided path.
|
Path to your web app manifest (e.g., `manifest.json`). This will add a `<link>` tag to `<head>` with `rel` as `"manifest"` and `href` as the provided path.
|
||||||
|
|
||||||
#### `markdownPlugins` [array]
|
#### `markdownPlugins` [array]
|
||||||
|
|
||||||
An array of plugins to be loaded by Remarkable, the markdown parser and renderer used by Docusaurus. The plugin will receive a reference to the Remarkable instance, allowing custom parsing and rendering rules to be defined.
|
An array of plugins to be loaded by Remarkable, the markdown parser and renderer used by Docusaurus. The plugin will receive a reference to the Remarkable instance, allowing custom parsing and rendering rules to be defined.
|
||||||
|
|
||||||
#### `ogImage` [string]
|
#### `ogImage` [string]
|
||||||
|
|
||||||
Local path to an Open Graph image (e.g., `img/myImage.png`). This image will show up when your site is shared on Facebook and other websites/apps where the Open Graph protocol is supported.
|
Local path to an Open Graph image (e.g., `img/myImage.png`). This image will show up when your site is shared on Facebook and other websites/apps where the Open Graph protocol is supported.
|
||||||
|
|
||||||
#### `onPageNav` [string]
|
#### `onPageNav` [string]
|
||||||
|
|
||||||
If you want a visible navigation option for representing topics on the current page. Currently, there is one accepted value for this option:
|
If you want a visible navigation option for representing topics on the current page. Currently, there is one accepted value for this option:
|
||||||
|
|
||||||
* `separate` - The secondary navigation is a separate pane defaulting on the right side of a document. See http://docusaurus.io/docs/en/translation.html for an example.
|
- `separate` - The secondary navigation is a separate pane defaulting on the right side of a document. See http://docusaurus.io/docs/en/translation.html for an example.
|
||||||
|
|
||||||
#### `scripts` [array]
|
#### `scripts` [array]
|
||||||
|
|
||||||
Array of JavaScript sources to load. The values can be either strings or plain objects of attribute-value maps. Refer to the example below. The script tag will be inserted in the HTML head.
|
Array of JavaScript sources to load. The values can be either strings or plain objects of attribute-value maps. Refer to the example below. The script tag will be inserted in the HTML head.
|
||||||
|
|
||||||
#### `separateCss` [string]
|
#### `separateCss` [string]
|
||||||
|
|
||||||
Directories inside which any `css` files will not be processed and concatenated to Docusaurus' styles. This is to support static `html` pages that may be separate from Docusaurus with completely separate styles.
|
Directories inside which any `css` files will not be processed and concatenated to Docusaurus' styles. This is to support static `html` pages that may be separate from Docusaurus with completely separate styles.
|
||||||
|
|
||||||
#### `scrollToTop` [boolean]
|
#### `scrollToTop` [boolean]
|
||||||
|
|
||||||
Set this to `true` if you want to enable the scroll to top button at the bottom of your site.
|
Set this to `true` if you want to enable the scroll to top button at the bottom of your site.
|
||||||
|
|
||||||
#### `scrollToTopOptions` [object]
|
#### `scrollToTopOptions` [object]
|
||||||
|
|
||||||
Optional options configuration for the scroll to top button. You do not need to use this, even if you set `scrollToTop` to `true`; it just provides you more configuration control of the button. You can find more options [here](https://github.com/vfeskov/vanilla-back-to-top/blob/v7.1.14/OPTIONS.md). By default, we set the zIndex option to 100.
|
Optional options configuration for the scroll to top button. You do not need to use this, even if you set `scrollToTop` to `true`; it just provides you more configuration control of the button. You can find more options [here](https://github.com/vfeskov/vanilla-back-to-top/blob/v7.1.14/OPTIONS.md). By default, we set the zIndex option to 100.
|
||||||
|
|
||||||
#### `stylesheets` [array]
|
#### `stylesheets` [array]
|
||||||
|
|
||||||
Array of CSS sources to load. The values can be either strings or plain objects of attribute-value maps. The link tag will be inserted in the HTML head.
|
Array of CSS sources to load. The values can be either strings or plain objects of attribute-value maps. The link tag will be inserted in the HTML head.
|
||||||
|
|
||||||
#### `translationRecruitingLink` [string]
|
#### `translationRecruitingLink` [string]
|
||||||
|
|
||||||
URL for the `Help Translate` tab of language selection when languages besides English are enabled. This can be included you are using translations but does not have to be.
|
URL for the `Help Translate` tab of language selection when languages besides English are enabled. This can be included you are using translations but does not have to be.
|
||||||
|
|
||||||
#### `twitter` [boolean]
|
#### `twitter` [boolean]
|
||||||
|
|
||||||
Set this to `true` if you want a Twitter social button to appear at the bottom of your blog posts.
|
Set this to `true` if you want a Twitter social button to appear at the bottom of your blog posts.
|
||||||
|
|
||||||
#### `twitterUsername` [string]
|
#### `twitterUsername` [string]
|
||||||
|
|
||||||
If you want a Twitter follow button at the bottom of your page, provide a Twitter username to follow. For example: `docusaurus`.
|
If you want a Twitter follow button at the bottom of your page, provide a Twitter username to follow. For example: `docusaurus`.
|
||||||
|
|
||||||
#### `twitterImage` [string]
|
#### `twitterImage` [string]
|
||||||
|
|
||||||
Local path to your Twitter card image (e.g., `img/myImage.png`). This image will show up on the Twitter card when your site is shared on Twitter.
|
Local path to your Twitter card image (e.g., `img/myImage.png`). This image will show up on the Twitter card when your site is shared on Twitter.
|
||||||
|
|
||||||
#### `useEnglishUrl` [string]
|
#### `useEnglishUrl` [string]
|
||||||
|
|
||||||
If you do not have [translations](guides-translation.md) enabled (e.g., by having a `languages.js` file), but still want a link of the form `/docs/en/doc.html` (with the `en`), set this to `true`.
|
If you do not have [translations](guides-translation.md) enabled (e.g., by having a `languages.js` file), but still want a link of the form `/docs/en/doc.html` (with the `en`), set this to `true`.
|
||||||
|
|
||||||
#### `users` [array]
|
#### `users` [array]
|
||||||
|
|
||||||
The `users` array mentioned earlier.
|
The `users` array mentioned earlier.
|
||||||
|
|
||||||
#### `usePrism` [array]
|
#### `usePrism` [array]
|
||||||
|
|
||||||
Array of languages to use Prism syntax highlighter. Refer to [Using Prism as additional syntax highlighter](api-doc-markdown.md#using-prism-as-additional-syntax-highlighter). Set it to `true` to use Prism on all languages.
|
Array of languages to use Prism syntax highlighter. Refer to [Using Prism as additional syntax highlighter](api-doc-markdown.md#using-prism-as-additional-syntax-highlighter). Set it to `true` to use Prism on all languages.
|
||||||
|
|
||||||
#### `wrapPagesHTML` [boolean]
|
#### `wrapPagesHTML` [boolean]
|
||||||
|
|
||||||
Boolean flag to indicate whether `html` files in `/pages` should be wrapped with Docusaurus site styles, header and footer. This feature is experimental and relies on the files being `html` fragments instead of complete pages. It inserts the contents of your `html` file with no extra processing. Defaults to `false`.
|
Boolean flag to indicate whether `html` files in `/pages` should be wrapped with Docusaurus site styles, header and footer. This feature is experimental and relies on the files being `html` fragments instead of complete pages. It inserts the contents of your `html` file with no extra processing. Defaults to `false`.
|
||||||
|
|
||||||
Users can also add their own custom fields if they wish to provide some data across different files.
|
Users can also add their own custom fields if they wish to provide some data across different files.
|
||||||
|
@ -317,7 +370,7 @@ const siteConfig = {
|
||||||
idx,
|
idx,
|
||||||
options,
|
options,
|
||||||
env,
|
env,
|
||||||
instance
|
instance,
|
||||||
) {
|
) {
|
||||||
return '<div class="foo">bar</div>';
|
return '<div class="foo">bar</div>';
|
||||||
};
|
};
|
||||||
|
@ -326,16 +379,17 @@ const siteConfig = {
|
||||||
scripts: [
|
scripts: [
|
||||||
'https://docusaurus.io/slash.js',
|
'https://docusaurus.io/slash.js',
|
||||||
{
|
{
|
||||||
src: 'https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js',
|
src:
|
||||||
async: true
|
'https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js',
|
||||||
}
|
async: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
stylesheets: [
|
stylesheets: [
|
||||||
'https://docusaurus.io/style.css',
|
'https://docusaurus.io/style.css',
|
||||||
{
|
{
|
||||||
href: 'http://css.link',
|
href: 'http://css.link',
|
||||||
type: "text/css"
|
type: 'text/css',
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
facebookAppId: '1615782811974223',
|
facebookAppId: '1615782811974223',
|
||||||
facebookComments: true,
|
facebookComments: true,
|
||||||
|
|
|
@ -92,6 +92,12 @@ const siteConfig = {
|
||||||
ogImage: 'img/docusaurus.png',
|
ogImage: 'img/docusaurus.png',
|
||||||
twitterImage: 'img/docusaurus.png',
|
twitterImage: 'img/docusaurus.png',
|
||||||
|
|
||||||
|
// Show documentation's last contributor's name.
|
||||||
|
// enableUpdateBy: true,
|
||||||
|
|
||||||
|
// Show documentation's last update time.
|
||||||
|
// enableUpdateTime: true,
|
||||||
|
|
||||||
// You may provide arbitrary config keys to be used as needed by your
|
// You may provide arbitrary config keys to be used as needed by your
|
||||||
// template. For example, if you need your repo's URL...
|
// template. For example, if you need your repo's URL...
|
||||||
// repoUrl: 'https://github.com/facebook/test-site',
|
// repoUrl: 'https://github.com/facebook/test-site',
|
||||||
|
|
|
@ -17,7 +17,7 @@ const OnPageNav = require('./nav/OnPageNav.js');
|
||||||
const Site = require('./Site.js');
|
const Site = require('./Site.js');
|
||||||
const translation = require('../server/translation.js');
|
const translation = require('../server/translation.js');
|
||||||
const docs = require('../server/docs.js');
|
const docs = require('../server/docs.js');
|
||||||
const {idx, getGitLastUpdated} = require('./utils.js');
|
const {idx, getGitLastUpdatedTime, getGitLastUpdatedBy} = require('./utils.js');
|
||||||
|
|
||||||
// component used to generate whole webpage for docs, including sidebar/header/footer
|
// component used to generate whole webpage for docs, including sidebar/header/footer
|
||||||
class DocsLayout extends React.Component {
|
class DocsLayout extends React.Component {
|
||||||
|
@ -45,16 +45,19 @@ class DocsLayout extends React.Component {
|
||||||
if (this.props.Doc) {
|
if (this.props.Doc) {
|
||||||
DocComponent = this.props.Doc;
|
DocComponent = this.props.Doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
let updateTime;
|
|
||||||
if (this.props.config.enableUpdateTime) {
|
|
||||||
const filepath = docs.getFilePath(metadata);
|
const filepath = docs.getFilePath(metadata);
|
||||||
updateTime = getGitLastUpdated(filepath);
|
|
||||||
}
|
const updateTime = this.props.config.enableUpdateTime
|
||||||
|
? getGitLastUpdatedTime(filepath)
|
||||||
|
: null;
|
||||||
|
const updateAuthor = this.props.config.enableUpdateBy
|
||||||
|
? getGitLastUpdatedBy(filepath)
|
||||||
|
: null;
|
||||||
|
|
||||||
const title =
|
const title =
|
||||||
idx(i18n, ['localized-strings', 'docs', id, 'title']) || defaultTitle;
|
idx(i18n, ['localized-strings', 'docs', id, 'title']) || defaultTitle;
|
||||||
const hasOnPageNav = this.props.config.onPageNav === 'separate';
|
const hasOnPageNav = this.props.config.onPageNav === 'separate';
|
||||||
|
|
||||||
const previousTitle =
|
const previousTitle =
|
||||||
idx(i18n, ['localized-strings', metadata.previous_id]) ||
|
idx(i18n, ['localized-strings', metadata.previous_id]) ||
|
||||||
idx(i18n, ['localized-strings', 'previous']) ||
|
idx(i18n, ['localized-strings', 'previous']) ||
|
||||||
|
@ -90,15 +93,16 @@ class DocsLayout extends React.Component {
|
||||||
version={metadata.version}
|
version={metadata.version}
|
||||||
language={metadata.language}
|
language={metadata.language}
|
||||||
/>
|
/>
|
||||||
{this.props.config.enableUpdateTime &&
|
{(updateTime || updateAuthor) && (
|
||||||
updateTime && (
|
<div className="docLastUpdate">
|
||||||
<div className="docLastUpdateTimestamp">
|
|
||||||
<em>
|
<em>
|
||||||
<strong>Last updated: </strong>
|
Last updated
|
||||||
{updateTime}
|
{updateTime && ` on ${updateTime}`}
|
||||||
|
{updateAuthor && ` by ${updateAuthor}`}
|
||||||
</em>
|
</em>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="docs-prevnext">
|
<div className="docs-prevnext">
|
||||||
{metadata.previous_id && (
|
{metadata.previous_id && (
|
||||||
<a
|
<a
|
||||||
|
|
|
@ -74,13 +74,13 @@ describe('utils', () => {
|
||||||
expect(utils.removeExtension('pages.js')).toBe('pages');
|
expect(utils.removeExtension('pages.js')).toBe('pages');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getGitLastUpdated', () => {
|
test('getGitLastUpdatedTime', () => {
|
||||||
// existing test file in repository with git timestamp
|
// existing test file in repository with git timestamp
|
||||||
const existingFilePath = path.join(__dirname, '__fixtures__', 'test.md');
|
const existingFilePath = path.join(__dirname, '__fixtures__', 'test.md');
|
||||||
const gitLastUpdated = utils.getGitLastUpdated(existingFilePath);
|
const gitLastUpdatedTime = utils.getGitLastUpdatedTime(existingFilePath);
|
||||||
expect(typeof gitLastUpdated).toBe('string');
|
expect(typeof gitLastUpdatedTime).toBe('string');
|
||||||
expect(Date.parse(gitLastUpdated)).not.toBeNaN();
|
expect(Date.parse(gitLastUpdatedTime)).not.toBeNaN();
|
||||||
expect(gitLastUpdated).not.toBeNull();
|
expect(gitLastUpdatedTime).not.toBeNull();
|
||||||
|
|
||||||
// non existing file
|
// non existing file
|
||||||
const nonExistingFilePath = path.join(
|
const nonExistingFilePath = path.join(
|
||||||
|
@ -88,14 +88,14 @@ describe('utils', () => {
|
||||||
'__fixtures__',
|
'__fixtures__',
|
||||||
'.nonExisting',
|
'.nonExisting',
|
||||||
);
|
);
|
||||||
expect(utils.getGitLastUpdated(null)).toBeNull();
|
expect(utils.getGitLastUpdatedTime(null)).toBeNull();
|
||||||
expect(utils.getGitLastUpdated(undefined)).toBeNull();
|
expect(utils.getGitLastUpdatedTime(undefined)).toBeNull();
|
||||||
expect(utils.getGitLastUpdated(nonExistingFilePath)).toBeNull();
|
expect(utils.getGitLastUpdatedTime(nonExistingFilePath)).toBeNull();
|
||||||
|
|
||||||
// temporary created file that has no git timestamp
|
// temporary created file that has no git timestamp
|
||||||
const tempFilePath = path.join(__dirname, '__fixtures__', '.temp');
|
const tempFilePath = path.join(__dirname, '__fixtures__', '.temp');
|
||||||
fs.writeFileSync(tempFilePath, 'Lorem ipsum :)');
|
fs.writeFileSync(tempFilePath, 'Lorem ipsum :)');
|
||||||
expect(utils.getGitLastUpdated(tempFilePath)).toBeNull();
|
expect(utils.getGitLastUpdatedTime(tempFilePath)).toBeNull();
|
||||||
fs.unlinkSync(tempFilePath);
|
fs.unlinkSync(tempFilePath);
|
||||||
|
|
||||||
// test renaming and moving file
|
// test renaming and moving file
|
||||||
|
@ -111,24 +111,24 @@ describe('utils', () => {
|
||||||
// create new file
|
// create new file
|
||||||
shell.exec = jest.fn(() => ({
|
shell.exec = jest.fn(() => ({
|
||||||
stdout:
|
stdout:
|
||||||
'1539502055\n' +
|
'1539502055, Yangshun Tay\n' +
|
||||||
'\n' +
|
'\n' +
|
||||||
' create mode 100644 v1/lib/core/__tests__/__fixtures__/.temp2\n',
|
' create mode 100644 v1/lib/core/__tests__/__fixtures__/.temp2\n',
|
||||||
}));
|
}));
|
||||||
const createTime = utils.getGitLastUpdated(tempFilePath2);
|
const createTime = utils.getGitLastUpdatedTime(tempFilePath2);
|
||||||
expect(typeof createTime).toBe('string');
|
expect(typeof createTime).toBe('string');
|
||||||
|
|
||||||
// rename / move the file
|
// rename / move the file
|
||||||
shell.exec = jest.fn(() => ({
|
shell.exec = jest.fn(() => ({
|
||||||
stdout:
|
stdout:
|
||||||
'1539502056\n' +
|
'1539502056, Joel Marcey\n' +
|
||||||
'\n' +
|
'\n' +
|
||||||
' rename v1/lib/core/__tests__/__fixtures__/{.temp2 => test/.temp3} (100%)\n' +
|
' rename v1/lib/core/__tests__/__fixtures__/{.temp2 => test/.temp3} (100%)\n' +
|
||||||
'1539502055\n' +
|
'1539502055, Yangshun Tay\n' +
|
||||||
'\n' +
|
'\n' +
|
||||||
' create mode 100644 v1/lib/core/__tests__/__fixtures__/.temp2\n',
|
' create mode 100644 v1/lib/core/__tests__/__fixtures__/.temp2\n',
|
||||||
}));
|
}));
|
||||||
const lastUpdateTime = utils.getGitLastUpdated(tempFilePath3);
|
const lastUpdateTime = utils.getGitLastUpdatedTime(tempFilePath3);
|
||||||
// should only consider file content change
|
// should only consider file content change
|
||||||
expect(lastUpdateTime).toEqual(createTime);
|
expect(lastUpdateTime).toEqual(createTime);
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,8 +39,21 @@ function idx(target, keyPaths) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGitLastUpdated(filepath) {
|
function getGitLastUpdated(filepath) {
|
||||||
function isTimestamp(str) {
|
const timestampAndAuthorRegex = /^(\d+), (.+)$/;
|
||||||
return /^\d+$/.test(str);
|
|
||||||
|
function isTimestampAndAuthor(str) {
|
||||||
|
return timestampAndAuthorRegex.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimestampAndAuthor(str) {
|
||||||
|
if (!str) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const temp = str.match(timestampAndAuthorRegex);
|
||||||
|
return !temp || temp.length < 3
|
||||||
|
? null
|
||||||
|
: {timestamp: temp[1], author: temp[2]};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap in try/catch in case the shell commands fail (e.g. project doesn't use Git, etc).
|
// Wrap in try/catch in case the shell commands fail (e.g. project doesn't use Git, etc).
|
||||||
|
@ -51,40 +64,58 @@ function getGitLastUpdated(filepath) {
|
||||||
const silentState = shell.config.silent; // Save old silent state.
|
const silentState = shell.config.silent; // Save old silent state.
|
||||||
shell.config.silent = true;
|
shell.config.silent = true;
|
||||||
const result = shell
|
const result = shell
|
||||||
.exec(`git log --follow --summary --format=%ct ${filepath}`)
|
.exec(`git log --follow --summary --format="%ct, %an" ${filepath}`)
|
||||||
.stdout.trim();
|
.stdout.trim();
|
||||||
shell.config.silent = silentState;
|
shell.config.silent = silentState;
|
||||||
|
|
||||||
// Format the log results to be
|
// Format the log results to be
|
||||||
// ['1234567', 'rename ...', '1234566', 'move ...', '1234565', '1234564']
|
// ['1234567890, Yangshun Tay', 'rename ...', '1234567880,
|
||||||
|
// 'Joel Marcey', 'move ...', '1234567870', '1234567860']
|
||||||
const records = result
|
const records = result
|
||||||
.toString('utf-8')
|
.toString('utf-8')
|
||||||
.replace(/\n\s*\n/g, '\n')
|
.replace(/\n\s*\n/g, '\n')
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter(String);
|
.filter(String);
|
||||||
|
const lastContentModifierCommit = records.find((item, index, arr) => {
|
||||||
const timeSpan = records.find((item, index, arr) => {
|
const currentItemIsTimestampAndAuthor = isTimestampAndAuthor(item);
|
||||||
const currentItemIsTimestamp = isTimestamp(item);
|
|
||||||
const isLastTwoItem = index + 2 >= arr.length;
|
const isLastTwoItem = index + 2 >= arr.length;
|
||||||
const nextItemIsTimestamp = isTimestamp(arr[index + 1]);
|
const nextItemIsTimestampAndAuthor = isTimestampAndAuthor(arr[index + 1]);
|
||||||
return currentItemIsTimestamp && (isLastTwoItem || nextItemIsTimestamp);
|
return (
|
||||||
|
currentItemIsTimestampAndAuthor &&
|
||||||
|
(isLastTwoItem || nextItemIsTimestampAndAuthor)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (timeSpan) {
|
return lastContentModifierCommit
|
||||||
const date = new Date(parseInt(timeSpan, 10) * 1000);
|
? getTimestampAndAuthor(lastContentModifierCommit)
|
||||||
return date.toLocaleString();
|
: null;
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGitLastUpdatedTime(filepath) {
|
||||||
|
const commit = getGitLastUpdated(filepath);
|
||||||
|
|
||||||
|
if (commit && commit.timestamp) {
|
||||||
|
const date = new Date(parseInt(commit.timestamp, 10) * 1000);
|
||||||
|
return date.toLocaleDateString();
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getGitLastUpdatedBy(filepath) {
|
||||||
|
const commit = getGitLastUpdated(filepath);
|
||||||
|
return commit ? commit.author : null;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
blogPostHasTruncateMarker,
|
blogPostHasTruncateMarker,
|
||||||
extractBlogPostBeforeTruncate,
|
extractBlogPostBeforeTruncate,
|
||||||
getGitLastUpdated,
|
getGitLastUpdatedTime,
|
||||||
|
getGitLastUpdatedBy,
|
||||||
getPath,
|
getPath,
|
||||||
removeExtension,
|
removeExtension,
|
||||||
idx,
|
idx,
|
||||||
|
|
|
@ -1536,7 +1536,7 @@ input::placeholder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.docLastUpdateTimestamp {
|
.docLastUpdate {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
|
|
|
@ -70,6 +70,7 @@ const siteConfig = {
|
||||||
zIndex: 100,
|
zIndex: 100,
|
||||||
},
|
},
|
||||||
enableUpdateTime: true,
|
enableUpdateTime: true,
|
||||||
|
enableUpdateBy: true,
|
||||||
customDocsPath: '../docs',
|
customDocsPath: '../docs',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue