From 0af93a2796f5d272058858801281e3ca52b6b4ff Mon Sep 17 00:00:00 2001 From: endiliey Date: Sat, 19 May 2018 19:00:27 +0800 Subject: [PATCH 1/9] implement clean url routing --- docs/api-site-config.md | 2 ++ lib/core/BlogPageLayout.js | 10 ++++++- lib/core/BlogPost.js | 22 ++++++++++++--- lib/core/BlogPostLayout.js | 23 ++++++++++++--- lib/core/nav/HeaderNav.js | 15 ++++++++-- lib/core/nav/SideNav.js | 25 ++++++++++++++--- lib/server/generate.js | 57 +++++++++++++++++++++++++++++++++++++- lib/server/server.js | 46 ++++++++++++++++++++---------- website/siteConfig.js | 1 + 9 files changed, 170 insertions(+), 31 deletions(-) diff --git a/docs/api-site-config.md b/docs/api-site-config.md index 78004db8be44..3c228b6788c6 100644 --- a/docs/api-site-config.md +++ b/docs/api-site-config.md @@ -167,6 +167,8 @@ h1 { `wrapPagesHTML` - 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`. +`cleanUrl` - If `true`, request to URL https://docusaurus.io/docs/installation will returns the same result as https://docusaurus.io/docs/installation.html. + Users can also add their own custom fields if they wish to provide some data across different files. ## Example siteConfig.js with many available fields diff --git a/lib/core/BlogPageLayout.js b/lib/core/BlogPageLayout.js index 77cf2116de26..c208cb8423c3 100644 --- a/lib/core/BlogPageLayout.js +++ b/lib/core/BlogPageLayout.js @@ -40,12 +40,20 @@ class BlogPageLayout extends React.Component {
{MetadataBlog.slice(page * perPage, (page + 1) * perPage).map( post => { + let postPath = post.path; + if (this.props.config.cleanUrl) { + if (postPath.endsWith('/index.html')) { + postPath = postPath.replace(/\/index.html$/, ''); + } else { + postPath = postPath.replace(/\.html$/, ''); + } + } return ( ); diff --git a/lib/core/BlogPost.js b/lib/core/BlogPost.js index 09c051b4f769..a2a605ee07c2 100644 --- a/lib/core/BlogPost.js +++ b/lib/core/BlogPost.js @@ -14,6 +14,14 @@ const utils = require('./utils'); class BlogPost extends React.Component { renderContent() { if (this.props.truncate) { + let postPath = this.props.post.path; + if (this.props.config.cleanUrl) { + if (postPath.endsWith('/index.html')) { + postPath = postPath.replace(/\/index.html$/, ''); + } else { + postPath = postPath.replace(/\.html$/, ''); + } + } return (
@@ -23,9 +31,7 @@ class BlogPost extends React.Component { @@ -71,9 +77,17 @@ class BlogPost extends React.Component { renderTitle() { const post = this.props.post; + let postPath = this.props.post.path; + if (this.props.config.cleanUrl) { + if (postPath.endsWith('/index.html')) { + postPath = postPath.replace(/\/index.html$/, ''); + } else { + postPath = postPath.replace(/\.html$/, ''); + } + } return (

- + {post.title}

diff --git a/lib/core/BlogPostLayout.js b/lib/core/BlogPostLayout.js index 9b1e0c6f983f..4ea246848618 100644 --- a/lib/core/BlogPostLayout.js +++ b/lib/core/BlogPostLayout.js @@ -14,7 +14,14 @@ const Site = require('./Site.js'); // used for entire blog posts, i.e., each written blog article with sidebar with site header/footer class BlogPostLayout extends React.Component { renderSocialButtons() { - const post = this.props.metadata; + let post = this.props.metadata; + if (this.props.config.cleanUrl) { + if (post.path.endsWith('/index.html')) { + post.path = post.path.replace(/\/index.html$/, ''); + } else { + post.path = post.path.replace(/\.html$/, ''); + } + } const fbComment = this.props.config.facebookAppId && this.props.config.facebookComments && ( @@ -92,10 +99,18 @@ class BlogPostLayout extends React.Component { } render() { + let post = this.props.metadata; + if (this.props.config.cleanUrl) { + if (post.path.endsWith('/index.html')) { + post.path = post.path.replace(/\/index.html$/, ''); + } else { + post.path = post.path.replace(/\.html$/, ''); + } + } return (
); const redirectStr = renderToStaticMarkupWithDoctype(redirectComp); @@ -212,6 +224,14 @@ async function execute() { metadata.permalink.replace('docs/en', 'docs') ); writeFileAndCreateFolder(redirectFile, redirectStr); + + // build extra files for extension-less url + if (siteConfig.cleanUrl) { + writeFileAndCreateFolder( + redirectFile.replace(/\.html$/, '/index.html'), + redirectStr + ); + } } }); @@ -274,6 +294,12 @@ async function execute() { let targetFile = join(buildDir, 'blog', filePath); writeFileAndCreateFolder(targetFile, str); + + // build extra files for extension-less url + if (siteConfig.cleanUrl) { + let cleanTargetFile = targetFile.replace(/\.html$/, '/index.html'); + writeFileAndCreateFolder(cleanTargetFile, str); + } }); // create html files for all blog pages (collections of article previews) const BlogPageLayout = require('../core/BlogPageLayout.js'); @@ -507,6 +533,15 @@ async function execute() { targetFile.replace(sep + 'en' + sep, sep + language + sep), str ); + // build extra files for extension-less url + if (siteConfig.cleanUrl && targetFile.indexOf('index.html') < 0) { + writeFileAndCreateFolder( + targetFile + .replace(sep + 'en' + sep, sep + language + sep) + .replace(/\.html$/, '/index.html'), + str + ); + } } // write to base level @@ -521,6 +556,16 @@ async function execute() { targetFile.replace(sep + 'en' + sep, sep), str ); + + // build extra files for extension-less url + if (siteConfig.cleanUrl && targetFile.indexOf('index.html') < 0) { + writeFileAndCreateFolder( + targetFile + .replace(sep + 'en' + sep, sep) + .replace(/\.html$/, '/index.html'), + str + ); + } } else { // allow for rendering of other files not in pages/en folder let language = env.translation.enabled ? 'en' : ''; @@ -534,6 +579,16 @@ async function execute() { targetFile.replace(sep + 'en' + sep, sep), str ); + + // build extra files for extension-less url + if (siteConfig.cleanUrl && targetFile.indexOf('index.html') < 0) { + writeFileAndCreateFolder( + targetFile + .replace(sep + 'en' + sep, sep) + .replace(/\.html$/, '/index.html'), + str + ); + } } fs.removeSync(tempFile); } else if (siteConfig.wrapPagesHTML && normalizedFile.match(/\.html$/)) { diff --git a/lib/server/server.js b/lib/server/server.js index 8aaf246a0eed..354e971ea7ae 100644 --- a/lib/server/server.js +++ b/lib/server/server.js @@ -199,6 +199,7 @@ function execute(port) { // replace any links to markdown files to their website html links Object.keys(mdToHtml).forEach(function(key, index) { let link = mdToHtml[key]; + link = siteConfig.cleanUrl ? link.replace(/\.html$/, '') : link; link = link.replace('/en/', '/' + language + '/'); link = link.replace( '/VERSION/', @@ -271,7 +272,7 @@ function execute(port) { }); // Handle all requests for blog pages and posts. - app.get(/blog\/.*html$/, (req, res) => { + app.get(/^\/blog\/.*html$/, (req, res) => { // Regenerate the blog metadata in case it has changed. Consider improving // this to regenerate on file save rather than on page request. reloadMetadataBlog(); @@ -305,7 +306,7 @@ function execute(port) { // send corresponding blog page if appropriate if (parts[1] === 'index.html') { res.send(blogPages['/index.html']); - } else if (parts[1].endsWith('/index.html')) { + } else if (parts[1].endsWith('/index.html') && blogPages[parts[1]]) { res.send(blogPages[parts[1]]); } else if (parts[1].match(/page([0-9]+)/)) { if (parts[1].endsWith('/')) { @@ -314,9 +315,14 @@ function execute(port) { res.send(blogPages[parts[1] + '/index.html']); } } else { - // else send corresponding blog post + // send corresponding blog post. Ex: request to "blog/test/index.html" or + // "blog/test.html" will return html rendered version of "blog/test.md" let file = parts[1]; - file = file.replace(/\.html$/, '.md'); + if (file.endsWith('/index.html')) { + file = file.replace(/\/index.html$/, '.md'); + } else { + file = file.replace(/\.html$/, '.md'); + } file = file.replace(new RegExp('/', 'g'), '-'); file = join(CWD, 'blog', file); @@ -526,23 +532,35 @@ function execute(port) { app.use(siteConfig.baseUrl, express.static(join(__dirname, '..', 'static'))); // "redirect" requests to pages ending with "/" or no extension so that, - // for example, request to "blog" returns same result as "blog/index.html" + // for example, request to "blog" returns "blog/index.html" or "blog.html" app.get(/\/[^\.]*\/?$/, (req, res) => { - let slash = req.path.toString().endsWith('/') ? '' : '/'; - request.get( - 'http://localhost:' + port + req.path + slash + 'index.html', - (err, response, body) => { - if (!err) { + const requestFile = (url, notFoundCallback) => { + request.get(url, (error, response, body) => { + if (!error) { if (response) { - res.status(response.statusCode).send(body); + if (response.statusCode === 404 && notFoundCallback) { + notFoundCallback(); + } else { + res.status(response.statusCode).send(body); + } } else { console.error('No response'); } } else { - console.error('Request failed:', err); + console.error('Request failed:', error); } - } - ); + }); + }; + let slash = req.path.toString().endsWith('/') ? '' : '/'; + let requestUrl = 'http://localhost:' + port + req.path; + requestFile(requestUrl + slash + 'index.html', () => { + requestFile( + slash === '/' + ? requestUrl + '.html' + : requestUrl.replace(/\/$/, '.html'), + null + ); + }); }); // Start LiveReload server. diff --git a/website/siteConfig.js b/website/siteConfig.js index 0cd93d4abb66..d118f63ff1b9 100644 --- a/website/siteConfig.js +++ b/website/siteConfig.js @@ -62,6 +62,7 @@ const siteConfig = { ogImage: 'img/docusaurus.png', twitterImage: 'img/docusaurus.png', onPageNav: 'separate', + cleanUrl: true }; module.exports = siteConfig; From d6d77ebed7a32049896e9ed8f7de9de34d6501d4 Mon Sep 17 00:00:00 2001 From: endiliey Date: Sun, 20 May 2018 22:57:43 +0800 Subject: [PATCH 2/9] refactor cleanurl code --- docs/api-site-config.md | 3 +- lib/core/BlogPost.js | 35 +++++++++++----------- lib/core/BlogPostLayout.js | 23 +++++++-------- lib/core/nav/SideNav.js | 33 ++++++++++----------- lib/server/generate.js | 60 +++++++++++++------------------------- 5 files changed, 65 insertions(+), 89 deletions(-) diff --git a/docs/api-site-config.md b/docs/api-site-config.md index 3c228b6788c6..e784d5d2bbf1 100644 --- a/docs/api-site-config.md +++ b/docs/api-site-config.md @@ -167,7 +167,7 @@ h1 { `wrapPagesHTML` - 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`. -`cleanUrl` - If `true`, request to URL https://docusaurus.io/docs/installation will returns the same result as https://docusaurus.io/docs/installation.html. +`cleanUrl` - If `true`, allow URLs with no `html` extension. Example: request to URL https://docusaurus.io/docs/installation will returns the same result as https://docusaurus.io/docs/installation.html. Users can also add their own custom fields if they wish to provide some data across different files. @@ -246,6 +246,7 @@ const siteConfig = { twitterUsername: 'docusaurus', twitterImage: 'img/docusaurus.png', ogImage: 'img/docusaurus.png', + cleanUrl: true }; module.exports = siteConfig; diff --git a/lib/core/BlogPost.js b/lib/core/BlogPost.js index a2a605ee07c2..dc1b66290dae 100644 --- a/lib/core/BlogPost.js +++ b/lib/core/BlogPost.js @@ -12,16 +12,19 @@ const utils = require('./utils'); // inner blog component for the article itself, without sidebar/header/footer class BlogPost extends React.Component { + getPath = path => { + if (this.props.config.cleanUrl) { + if (path.endsWith('/index.html')) { + return path.replace(/\/index.html$/, ''); + } else { + return path.replace(/\.html$/, ''); + } + } + return path; + }; + renderContent() { if (this.props.truncate) { - let postPath = this.props.post.path; - if (this.props.config.cleanUrl) { - if (postPath.endsWith('/index.html')) { - postPath = postPath.replace(/\/index.html$/, ''); - } else { - postPath = postPath.replace(/\.html$/, ''); - } - } return (
@@ -31,7 +34,11 @@ class BlogPost extends React.Component { @@ -77,17 +84,9 @@ class BlogPost extends React.Component { renderTitle() { const post = this.props.post; - let postPath = this.props.post.path; - if (this.props.config.cleanUrl) { - if (postPath.endsWith('/index.html')) { - postPath = postPath.replace(/\/index.html$/, ''); - } else { - postPath = postPath.replace(/\.html$/, ''); - } - } return (

- + {post.title}

diff --git a/lib/core/BlogPostLayout.js b/lib/core/BlogPostLayout.js index 4ea246848618..636bcb32e608 100644 --- a/lib/core/BlogPostLayout.js +++ b/lib/core/BlogPostLayout.js @@ -13,15 +13,20 @@ const Site = require('./Site.js'); // used for entire blog posts, i.e., each written blog article with sidebar with site header/footer class BlogPostLayout extends React.Component { - renderSocialButtons() { - let post = this.props.metadata; + getPath = path => { if (this.props.config.cleanUrl) { - if (post.path.endsWith('/index.html')) { - post.path = post.path.replace(/\/index.html$/, ''); + if (path.endsWith('/index.html')) { + return path.replace(/\/index.html$/, ''); } else { - post.path = post.path.replace(/\.html$/, ''); + return path.replace(/\.html$/, ''); } } + return path; + }; + + renderSocialButtons() { + let post = this.props.metadata; + post.path = this.getPath(post.path); const fbComment = this.props.config.facebookAppId && this.props.config.facebookComments && ( @@ -100,13 +105,7 @@ class BlogPostLayout extends React.Component { render() { let post = this.props.metadata; - if (this.props.config.cleanUrl) { - if (post.path.endsWith('/index.html')) { - post.path = post.path.replace(/\/index.html$/, ''); - } else { - post.path = post.path.replace(/\.html$/, ''); - } - } + post.path = this.getPath(post.path); return ( { + if (siteConfig.cleanUrl) { + if (path.endsWith('/index.html')) { + return path.replace(/\/index.html$/, ''); + } else { + return path.replace(/\.html$/, ''); + } + } + return path; + }; + // return link to doc in sidebar getLink(metadata) { if (metadata.permalink) { - let targetLink = metadata.permalink; - if (siteConfig.cleanUrl) { - if (targetLink.endsWith('/index.html')) { - targetLink = targetLink.replace(/\/index.html$/, ''); - } else { - targetLink = targetLink.replace(/\.html$/, ''); - } - } - + const targetLink = this.getPath(metadata.permalink); if (targetLink.match(/^https?:/)) { return targetLink; } return siteConfig.baseUrl + targetLink; } if (metadata.path) { - let targetPath = metadata.path; - if (siteConfig.cleanUrl) { - if (targetPath.endsWith('/index.html')) { - targetPath = targetPath.replace(/\/index.html$/, ''); - } else { - targetPath = targetPath.replace(/\.html$/, ''); - } - } - return siteConfig.baseUrl + 'blog/' + targetPath; + return siteConfig.baseUrl + 'blog/' + this.getPath(metadata.path); } return null; } diff --git a/lib/server/generate.js b/lib/server/generate.js index cd3b4faa78f7..55c743e906d2 100644 --- a/lib/server/generate.js +++ b/lib/server/generate.js @@ -45,6 +45,13 @@ async function execute() { fs.writeFileSync(file, content); } + // build extra file for extension-less url if "cleanUrl" siteConfig is true + function writeExtraFileIfCleanUrl(file, content) { + if (siteConfig.cleanUrl) { + writeFileAndCreateFolder(file.replace(/\.html$/, '/index.html'), content); + } + } + const TABLE_OF_CONTENTS_TOKEN = ''; // takes the content of a doc article and returns the content with a table of @@ -191,14 +198,7 @@ async function execute() { const targetFile = join(buildDir, metadata.permalink); writeFileAndCreateFolder(targetFile, str); - - // build extra files for extension-less url - if (siteConfig.cleanUrl) { - writeFileAndCreateFolder( - targetFile.replace(/\.html$/, '/index.html'), - str - ); - } + writeExtraFileIfCleanUrl(targetFile, str); // generate english page redirects when languages are enabled if ( @@ -224,14 +224,7 @@ async function execute() { metadata.permalink.replace('docs/en', 'docs') ); writeFileAndCreateFolder(redirectFile, redirectStr); - - // build extra files for extension-less url - if (siteConfig.cleanUrl) { - writeFileAndCreateFolder( - redirectFile.replace(/\.html$/, '/index.html'), - redirectStr - ); - } + writeExtraFileIfCleanUrl(redirectFile, redirectStr); } }); @@ -294,12 +287,7 @@ async function execute() { let targetFile = join(buildDir, 'blog', filePath); writeFileAndCreateFolder(targetFile, str); - - // build extra files for extension-less url - if (siteConfig.cleanUrl) { - let cleanTargetFile = targetFile.replace(/\.html$/, '/index.html'); - writeFileAndCreateFolder(cleanTargetFile, str); - } + writeExtraFileIfCleanUrl(targetFile, str); }); // create html files for all blog pages (collections of article previews) const BlogPageLayout = require('../core/BlogPageLayout.js'); @@ -533,12 +521,10 @@ async function execute() { targetFile.replace(sep + 'en' + sep, sep + language + sep), str ); - // build extra files for extension-less url - if (siteConfig.cleanUrl && targetFile.indexOf('index.html') < 0) { - writeFileAndCreateFolder( - targetFile - .replace(sep + 'en' + sep, sep + language + sep) - .replace(/\.html$/, '/index.html'), + + if (targetFile.indexOf('index.html') < 0) { + writeExtraFileIfCleanUrl( + targetFile.replace(sep + 'en' + sep, sep + language + sep), str ); } @@ -557,12 +543,9 @@ async function execute() { str ); - // build extra files for extension-less url - if (siteConfig.cleanUrl && targetFile.indexOf('index.html') < 0) { - writeFileAndCreateFolder( - targetFile - .replace(sep + 'en' + sep, sep) - .replace(/\.html$/, '/index.html'), + if (targetFile.indexOf('index.html') < 0) { + writeExtraFileIfCleanUrl( + targetFile.replace(sep + 'en' + sep, sep), str ); } @@ -580,12 +563,9 @@ async function execute() { str ); - // build extra files for extension-less url - if (siteConfig.cleanUrl && targetFile.indexOf('index.html') < 0) { - writeFileAndCreateFolder( - targetFile - .replace(sep + 'en' + sep, sep) - .replace(/\.html$/, '/index.html'), + if (targetFile.indexOf('index.html') < 0) { + writeExtraFileIfCleanUrl( + targetFile.replace(sep + 'en' + sep, sep), str ); } From 142eb1fabd250f15ddb1b85c90549cee8baa0756 Mon Sep 17 00:00:00 2001 From: endiliey Date: Sat, 26 May 2018 14:29:53 +0800 Subject: [PATCH 3/9] reorder cleanurl siteconfig in doc --- docs/api-site-config.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-site-config.md b/docs/api-site-config.md index e784d5d2bbf1..8ce4b8628d11 100644 --- a/docs/api-site-config.md +++ b/docs/api-site-config.md @@ -74,6 +74,8 @@ headerLinks: [ `blogSidebarCount` - 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. +`cleanUrl` - If `true`, allow URLs with no `html` extension. Example: request to URL https://docusaurus.io/docs/installation will returns the same result as https://docusaurus.io/docs/installation.html. + `cname` - The CNAME for your website. It will go into a `CNAME` file when your site it built. `customDocsPath` - 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. **Note that all of your documentation `*.md` files must still reside in a flat hierarchy. You cannot have your documents in nested directories**. @@ -167,8 +169,6 @@ h1 { `wrapPagesHTML` - 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`. -`cleanUrl` - If `true`, allow URLs with no `html` extension. Example: request to URL https://docusaurus.io/docs/installation will returns the same result as https://docusaurus.io/docs/installation.html. - Users can also add their own custom fields if they wish to provide some data across different files. ## Example siteConfig.js with many available fields From 0a4f03892ba05087bd10b33cc4134a7f69e2efcc Mon Sep 17 00:00:00 2001 From: endiliey Date: Mon, 28 May 2018 11:31:59 +0800 Subject: [PATCH 4/9] cleanurl on docslayout --- lib/core/DocsLayout.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/DocsLayout.js b/lib/core/DocsLayout.js index 5889511637b3..c1faf4a2db65 100644 --- a/lib/core/DocsLayout.js +++ b/lib/core/DocsLayout.js @@ -28,6 +28,7 @@ class DocsLayout extends React.Component { this.props.metadata.localized_id ] || this.props.metadata.title : this.props.metadata.title; + const extension = this.props.config.cleanUrl ? '' : '.html'; return ( + href={metadata.previous_id + extension}> ←{' '} {i18n ? translation[this.props.metadata.language][ @@ -70,7 +71,7 @@ class DocsLayout extends React.Component { {metadata.next_id && ( + href={metadata.next_id + extension}> {i18n ? translation[this.props.metadata.language][ 'localized-strings' From 031a49b3505a382f8c9287d6493cb9359c118227 Mon Sep 17 00:00:00 2001 From: endiliey Date: Sun, 3 Jun 2018 15:05:49 +0800 Subject: [PATCH 5/9] refactor getPath to utils --- lib/core/BlogPageLayout.js | 14 +++++--------- lib/core/BlogPost.js | 25 +++++++++++-------------- lib/core/BlogPostLayout.js | 16 +++------------- lib/core/nav/HeaderNav.js | 13 ++++--------- lib/core/nav/SideNav.js | 21 +++++++-------------- lib/core/utils.js | 12 ++++++++++++ 6 files changed, 42 insertions(+), 59 deletions(-) diff --git a/lib/core/BlogPageLayout.js b/lib/core/BlogPageLayout.js index c208cb8423c3..e3a815f4e713 100644 --- a/lib/core/BlogPageLayout.js +++ b/lib/core/BlogPageLayout.js @@ -11,6 +11,7 @@ const Container = require('./Container.js'); const MetadataBlog = require('./MetadataBlog.js'); const React = require('react'); const Site = require('./Site.js'); +const utils = require('./utils.js'); // used to generate entire blog pages, i.e. collection of truncated blog posts class BlogPageLayout extends React.Component { @@ -40,20 +41,15 @@ class BlogPageLayout extends React.Component {
{MetadataBlog.slice(page * perPage, (page + 1) * perPage).map( post => { - let postPath = post.path; - if (this.props.config.cleanUrl) { - if (postPath.endsWith('/index.html')) { - postPath = postPath.replace(/\/index.html$/, ''); - } else { - postPath = postPath.replace(/\.html$/, ''); - } - } return ( ); diff --git a/lib/core/BlogPost.js b/lib/core/BlogPost.js index dc1b66290dae..19126dcb94b0 100644 --- a/lib/core/BlogPost.js +++ b/lib/core/BlogPost.js @@ -8,21 +8,10 @@ const MarkdownBlock = require('./MarkdownBlock.js'); const React = require('react'); -const utils = require('./utils'); +const utils = require('./utils.js'); // inner blog component for the article itself, without sidebar/header/footer class BlogPost extends React.Component { - getPath = path => { - if (this.props.config.cleanUrl) { - if (path.endsWith('/index.html')) { - return path.replace(/\/index.html$/, ''); - } else { - return path.replace(/\.html$/, ''); - } - } - return path; - }; - renderContent() { if (this.props.truncate) { return ( @@ -37,7 +26,10 @@ class BlogPost extends React.Component { href={ this.props.config.baseUrl + 'blog/' + - this.getPath(this.props.post.path) + utils.getPath( + this.props.post.path, + this.props.config.cleanUrl + ) }> Read More @@ -86,7 +78,12 @@ class BlogPost extends React.Component { const post = this.props.post; return (

- + {post.title}

diff --git a/lib/core/BlogPostLayout.js b/lib/core/BlogPostLayout.js index 636bcb32e608..b0a12dd47217 100644 --- a/lib/core/BlogPostLayout.js +++ b/lib/core/BlogPostLayout.js @@ -10,23 +10,13 @@ const BlogPost = require('./BlogPost.js'); const BlogSidebar = require('./BlogSidebar.js'); const Container = require('./Container.js'); const Site = require('./Site.js'); +const utils = require('./utils.js'); // used for entire blog posts, i.e., each written blog article with sidebar with site header/footer class BlogPostLayout extends React.Component { - getPath = path => { - if (this.props.config.cleanUrl) { - if (path.endsWith('/index.html')) { - return path.replace(/\/index.html$/, ''); - } else { - return path.replace(/\.html$/, ''); - } - } - return path; - }; - renderSocialButtons() { let post = this.props.metadata; - post.path = this.getPath(post.path); + post.path = utils.getPath(post.path, this.props.config.cleanUrl); const fbComment = this.props.config.facebookAppId && this.props.config.facebookComments && ( @@ -105,7 +95,7 @@ class BlogPostLayout extends React.Component { render() { let post = this.props.metadata; - post.path = this.getPath(post.path); + post.path = utils.getPath(post.path, this.props.config.cleanUrl); return ( { - if (siteConfig.cleanUrl) { - if (path.endsWith('/index.html')) { - return path.replace(/\/index.html$/, ''); - } else { - return path.replace(/\.html$/, ''); - } - } - return path; - }; - // return link to doc in sidebar getLink(metadata) { if (metadata.permalink) { - const targetLink = this.getPath(metadata.permalink); + const targetLink = utils.getPath(metadata.permalink, siteConfig.cleanUrl); if (targetLink.match(/^https?:/)) { return targetLink; } return siteConfig.baseUrl + targetLink; } if (metadata.path) { - return siteConfig.baseUrl + 'blog/' + this.getPath(metadata.path); + return ( + siteConfig.baseUrl + + 'blog/' + + utils.getPath(metadata.path, siteConfig.cleanUrl) + ); } return null; } diff --git a/lib/core/utils.js b/lib/core/utils.js index 4d880763b975..0f99620eb3b9 100644 --- a/lib/core/utils.js +++ b/lib/core/utils.js @@ -20,8 +20,20 @@ function extractBlogPostSummary(content) { return content.substring(0, BLOG_POST_SUMMARY_LENGTH); } +function getPath(path, cleanUrl = false) { + if (cleanUrl) { + if (path.endsWith('/index.html')) { + return path.replace(/\/index.html$/, ''); + } else { + return path.replace(/\.html$/, ''); + } + } + return path; +} + module.exports = { blogPostHasTruncateMarker, extractBlogPostBeforeTruncate, extractBlogPostSummary, + getPath, }; From 52d4352af266ce1967ec5cdb67216fc0b6e9505a Mon Sep 17 00:00:00 2001 From: endiliey Date: Sun, 3 Jun 2018 15:34:14 +0800 Subject: [PATCH 6/9] refactor generate.js --- lib/server/generate.js | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/lib/server/generate.js b/lib/server/generate.js index f08c8ca4e1a6..93b510420933 100644 --- a/lib/server/generate.js +++ b/lib/server/generate.js @@ -41,14 +41,13 @@ async function execute() { // create the folder path for a file if it does not exist, then write the file function writeFileAndCreateFolder(file, content) { mkdirp.sync(path.dirname(file)); - fs.writeFileSync(file, content); - } - // build extra file for extension-less url if "cleanUrl" siteConfig is true - function writeExtraFileIfCleanUrl(file, content) { - if (siteConfig.cleanUrl) { - writeFileAndCreateFolder(file.replace(/\.html$/, '/index.html'), content); + // build extra file for extension-less url if "cleanUrl" siteConfig is true + if (siteConfig.cleanUrl && file.indexOf('index.html') === -1) { + const extraFile = file.replace(/\.html$/, '/index.html'); + mkdirp.sync(path.dirname(extraFile)); + fs.writeFileSync(extraFile, content); } } @@ -198,7 +197,6 @@ async function execute() { const targetFile = join(buildDir, metadata.permalink); writeFileAndCreateFolder(targetFile, str); - writeExtraFileIfCleanUrl(targetFile, str); // generate english page redirects when languages are enabled if ( @@ -224,7 +222,6 @@ async function execute() { metadata.permalink.replace('docs/en', 'docs') ); writeFileAndCreateFolder(redirectFile, redirectStr); - writeExtraFileIfCleanUrl(redirectFile, redirectStr); } }); @@ -287,7 +284,6 @@ async function execute() { let targetFile = join(buildDir, 'blog', filePath); writeFileAndCreateFolder(targetFile, str); - writeExtraFileIfCleanUrl(targetFile, str); }); // create html files for all blog pages (collections of article previews) const BlogPageLayout = require('../core/BlogPageLayout.js'); @@ -522,13 +518,6 @@ async function execute() { targetFile.replace(sep + 'en' + sep, sep + language + sep), str ); - - if (targetFile.indexOf('index.html') < 0) { - writeExtraFileIfCleanUrl( - targetFile.replace(sep + 'en' + sep, sep + language + sep), - str - ); - } } // write to base level @@ -547,13 +536,6 @@ async function execute() { targetFile.replace(sep + 'en' + sep, sep), str ); - - if (targetFile.indexOf('index.html') < 0) { - writeExtraFileIfCleanUrl( - targetFile.replace(sep + 'en' + sep, sep), - str - ); - } } else { // allow for rendering of other files not in pages/en folder let language = env.translation.enabled ? 'en' : ''; @@ -571,13 +553,6 @@ async function execute() { targetFile.replace(sep + 'en' + sep, sep), str ); - - if (targetFile.indexOf('index.html') < 0) { - writeExtraFileIfCleanUrl( - targetFile.replace(sep + 'en' + sep, sep), - str - ); - } } fs.removeSync(tempFile); } else if (siteConfig.wrapPagesHTML && normalizedFile.match(/\.html$/)) { From 2e88f27481d367e4af29c2eb7153067e9cf0c43d Mon Sep 17 00:00:00 2001 From: endiliey Date: Sun, 3 Jun 2018 15:45:27 +0800 Subject: [PATCH 7/9] remove hardcoded .html link on docusaurus web --- website/pages/en/help.js | 2 +- website/pages/en/index.js | 26 +++++++++++++------------- website/pages/en/versions.js | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/website/pages/en/help.js b/website/pages/en/help.js index c99f1af83cd8..7edd14a4f845 100755 --- a/website/pages/en/help.js +++ b/website/pages/en/help.js @@ -18,7 +18,7 @@ class Help extends React.Component { { title: Browse the docs, content: ( - `Learn more about Docusaurus using the [official documentation](${siteConfig.baseUrl}docs/${this.props.language}/installation.html).` + `Learn more about Docusaurus using the [official documentation](${siteConfig.baseUrl}docs/${this.props.language}/installation).` ), }, { diff --git a/website/pages/en/index.js b/website/pages/en/index.js index 73587e4d6f1d..0450d565e5e0 100755 --- a/website/pages/en/index.js +++ b/website/pages/en/index.js @@ -48,7 +48,7 @@ class HomeSplash extends React.Component {
@@ -92,7 +92,7 @@ class Index extends React.Component { { content: ( `Save time and focus on your project's documentation. Simply - write docs and blog posts with [Markdown](${siteConfig.baseUrl}docs/${this.props.language}/doc-markdown.html) + write docs and blog posts with [Markdown](${siteConfig.baseUrl}docs/${this.props.language}/doc-markdown) and Docusaurus will publish a set of static html files ready to serve.` ), @@ -103,7 +103,7 @@ class Index extends React.Component { }, { content: ( - `[Extend or customize](${siteConfig.baseUrl}docs/${this.props.language}/api-pages.html) + `[Extend or customize](${siteConfig.baseUrl}docs/${this.props.language}/api-pages) your project's layout by reusing React. Docusaurus can be extended while reusing the same header and footer.` ), @@ -114,7 +114,7 @@ class Index extends React.Component { }, { content: ( - `[Localization](${siteConfig.baseUrl}docs/${this.props.language}/translation.html) + `[Localization](${siteConfig.baseUrl}docs/${this.props.language}/translation) comes pre-configured. Use [Crowdin](https://crowdin.com/) to translate your docs into over 70 languages.` ), @@ -134,7 +134,7 @@ class Index extends React.Component { { content: ( `Support users on all versions of your project. Document - [versioning](${siteConfig.baseUrl}docs/${this.props.language}/versioning.html) + [versioning](${siteConfig.baseUrl}docs/${this.props.language}/versioning) helps you keep documentation in sync with project releases.` ), image: `${siteConfig.baseUrl}img/versioning.svg`, @@ -144,7 +144,7 @@ class Index extends React.Component { }, { content: ( - `Make it easy for your community to [find](${siteConfig.baseUrl}docs/${this.props.language}/search.html) what they need in your documentation. + `Make it easy for your community to [find](${siteConfig.baseUrl}docs/${this.props.language}/search) what they need in your documentation. We proudly support [Algolia documentation search](https://www.algolia.com/).` ), image: `${siteConfig.baseUrl}img/search.svg`, @@ -161,7 +161,7 @@ class Index extends React.Component { contents={[ { content: ( - `Get [up and running](${siteConfig.baseUrl}docs/${this.props.language}/site-creation.html) + `Get [up and running](${siteConfig.baseUrl}docs/${this.props.language}/site-creation) quickly without having to worry about site design.` ), imageAlign: "right", @@ -180,7 +180,7 @@ class Index extends React.Component { content: ( `Make design and documentation changes by using the included [live server](${siteConfig.baseUrl}docs/${this.props.language}/site-preparation#verifying-installation). - [Publish](${siteConfig.baseUrl}docs/${this.props.language}/publishing.html) + [Publish](${siteConfig.baseUrl}docs/${this.props.language}/publishing) your site to GitHub pages or other static file hosts manually, using a script, or with continuous integration like CircleCI.` @@ -200,10 +200,10 @@ class Index extends React.Component { { content: ( `Docusaurus currently provides support to help your website - use [translations](${siteConfig.baseUrl}docs/${this.props.language}/translation.html), - [search](${siteConfig.baseUrl}docs/${this.props.language}/search.html), - and [versioning](${siteConfig.baseUrl}docs/${this.props.language}/versioning.html), - along with some other special [documentation markdown features](${siteConfig.baseUrl}docs/${this.props.language}/doc-markdown.html). + use [translations](${siteConfig.baseUrl}docs/${this.props.language}/translation), + [search](${siteConfig.baseUrl}docs/${this.props.language}/search), + and [versioning](${siteConfig.baseUrl}docs/${this.props.language}/versioning), + along with some other special [documentation markdown features](${siteConfig.baseUrl}docs/${this.props.language}/doc-markdown). If you have ideas for useful features, feel free to contribute on [GitHub](https://github.com/facebook/docusaurus)!` ), @@ -223,7 +223,7 @@ class Index extends React.Component { diff --git a/website/pages/en/versions.js b/website/pages/en/versions.js index 5992e420fb1c..a26c82c96be1 100644 --- a/website/pages/en/versions.js +++ b/website/pages/en/versions.js @@ -33,7 +33,7 @@ class Versions extends React.Component { {latestVersion} - Documentation + Documentation Release Notes @@ -51,7 +51,7 @@ class Versions extends React.Component { Documentation @@ -75,7 +75,7 @@ class Versions extends React.Component { Documentation From 2cb0d513a76102f1c6d8e498a876310a7be1e25a Mon Sep 17 00:00:00 2001 From: Joel Marcey Date: Wed, 6 Jun 2018 13:16:49 -0700 Subject: [PATCH 8/9] Comma fix #1 --- docs/api-site-config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-site-config.md b/docs/api-site-config.md index ef8869a55858..95fbe89495f6 100644 --- a/docs/api-site-config.md +++ b/docs/api-site-config.md @@ -252,7 +252,7 @@ const siteConfig = { twitterUsername: 'docusaurus', twitterImage: 'img/docusaurus.png', ogImage: 'img/docusaurus.png', - cleanUrl: true + cleanUrl: true, scrollToTop: true, scrollToTopOptions: { zIndex: 100 From 52376f0de0bb5ec092a05bdaeeaa54dbcde25505 Mon Sep 17 00:00:00 2001 From: Joel Marcey Date: Wed, 6 Jun 2018 13:17:20 -0700 Subject: [PATCH 9/9] Comma Fix #2 --- website/siteConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/siteConfig.js b/website/siteConfig.js index 46789df4d602..d32ef5ff8674 100644 --- a/website/siteConfig.js +++ b/website/siteConfig.js @@ -62,7 +62,7 @@ const siteConfig = { ogImage: 'img/docusaurus.png', twitterImage: 'img/docusaurus.png', onPageNav: 'separate', - cleanUrl: true + cleanUrl: true, scrollToTop: true, scrollToTopOptions: { zIndex: 100