chore(v2): normalize url properly (#1105)

* refactor(v2): normalize url properly

* nits
This commit is contained in:
Endilie Yacop Sucipto 2018-11-12 00:25:13 +08:00 committed by GitHub
parent 34dcc0c22e
commit b84754dde8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 128 additions and 11 deletions

View file

@ -8,7 +8,7 @@
const globby = require('globby'); const globby = require('globby');
const path = require('path'); const path = require('path');
const fs = require('fs-extra'); const fs = require('fs-extra');
const {parse, idx} = require('./utils'); const {parse, idx, normalizeUrl} = require('./utils');
function fileToUrl(fileName) { function fileToUrl(fileName) {
return fileName return fileName
@ -47,7 +47,7 @@ async function loadBlog({blogDir, env, siteConfig}) {
const fileString = await fs.readFile(source, 'utf-8'); const fileString = await fs.readFile(source, 'utf-8');
const {metadata: rawMetadata} = parse(fileString); const {metadata: rawMetadata} = parse(fileString);
const metadata = { const metadata = {
permalink: path.join(baseUrl, `blog`, fileToUrl(blogFileName)), permalink: normalizeUrl([baseUrl, `blog`, fileToUrl(blogFileName)]),
source, source,
...rawMetadata, ...rawMetadata,
date, date,
@ -67,7 +67,10 @@ async function loadBlog({blogDir, env, siteConfig}) {
/* eslint-disable */ /* eslint-disable */
for (let page = 0; page < numberOfPage; page++) { for (let page = 0; page < numberOfPage; page++) {
blogMetadatas.push({ blogMetadatas.push({
permalink: path.join(basePageUrl, `${page > 0 ? `page${page + 1}` : ''}`), permalink: normalizeUrl([
basePageUrl,
`${page > 0 ? `page${page + 1}` : ''}`,
]),
language: defaultLangTag, language: defaultLangTag,
isBlogPage: true, isBlogPage: true,
posts: blogMetadatas.slice(page * perPage, (page + 1) * perPage), posts: blogMetadatas.slice(page * perPage, (page + 1) * perPage),

View file

@ -7,7 +7,7 @@
const fs = require('fs-extra'); const fs = require('fs-extra');
const path = require('path'); const path = require('path');
const {getSubFolder, idx, parse} = require('../utils'); const {getSubFolder, idx, parse, normalizeUrl} = require('../utils');
function getLanguage(filepath, refDir, env) { function getLanguage(filepath, refDir, env) {
const translationEnabled = idx(env, ['translation', 'enabled']); const translationEnabled = idx(env, ['translation', 'enabled']);
@ -135,9 +135,13 @@ module.exports = async function processMetadata(
.replace(/:id/, metadata.id), .replace(/:id/, metadata.id),
); );
} else { } else {
metadata.permalink = `${baseUrl}${docsUrl}/${langPart}${versionPart}${ metadata.permalink = normalizeUrl([
metadata.id baseUrl,
}`; docsUrl,
langPart,
versionPart,
metadata.id,
]);
} }
/* if version */ /* if version */

View file

@ -5,6 +5,8 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
const {normalizeUrl} = require('./utils');
async function genRoutesConfig({ async function genRoutesConfig({
siteConfig = {}, siteConfig = {},
docsMetadatas = {}, docsMetadatas = {},
@ -33,7 +35,7 @@ async function genRoutesConfig({
}`; }`;
} }
const rootDocsUrl = baseUrl + docsUrl; const rootDocsUrl = normalizeUrl([baseUrl, docsUrl]);
const docsRoutes = ` const docsRoutes = `
{ {
path: '${rootDocsUrl}', path: '${rootDocsUrl}',
@ -133,8 +135,9 @@ async function genRoutesConfig({
`import BlogPage from '@theme/BlogPage';\n` + `import BlogPage from '@theme/BlogPage';\n` +
`import Pages from '@theme/Pages';\n` + `import Pages from '@theme/Pages';\n` +
`import NotFound from '@theme/NotFound';\n` + `import NotFound from '@theme/NotFound';\n` +
`const routes = [${docsRoutes}, `const routes = [
${pagesMetadatas.map(genPagesRoute).join(',')}, ${pagesMetadatas.map(genPagesRoute).join(',')},
${docsRoutes},
${blogMetadatas.map(genBlogRoute).join(',')}, ${blogMetadatas.map(genBlogRoute).join(',')},
${notFoundRoute}\n];\n` + ${notFoundRoute}\n];\n` +
`export default routes;\n` `export default routes;\n`

View file

@ -77,6 +77,67 @@ function parse(fileString) {
return {metadata, content}; return {metadata, content};
} }
function normalizeUrl(rawUrls) {
const urls = rawUrls;
const resultArray = [];
// If the first part is a plain protocol, we combine it with the next part.
if (urls[0].match(/^[^/:]+:\/*$/) && urls.length > 1) {
const first = urls.shift();
urls[0] = first + urls[0];
}
// There must be two or three slashes in the file protocol, two slashes in anything else.
if (urls[0].match(/^file:\/\/\//)) {
urls[0] = urls[0].replace(/^([^/:]+):\/*/, '$1:///');
} else {
urls[0] = urls[0].replace(/^([^/:]+):\/*/, '$1://');
}
// eslint-disable-next-line
for (let i = 0; i < urls.length; i++) {
let component = urls[i];
if (typeof component !== 'string') {
throw new TypeError(`Url must be a string. Received ${component}`);
}
if (component === '') {
// eslint-disable-next-line
continue;
}
if (i > 0) {
// Removing the starting slashes for each component but the first.
component = component.replace(/^[/]+/, '');
}
if (i < urls.length - 1) {
// Removing the ending slashes for each component but the last.
component = component.replace(/[/]+$/, '');
} else {
// For the last component we will combine multiple slashes to a single one.
component = component.replace(/[/]+$/, '/');
}
resultArray.push(component);
}
let str = resultArray.join('/');
// Each input component is now separated by a single slash except the possible first plain protocol part.
// remove trailing slash before parameters or hash
str = str.replace(/\/(\?|&|#[^!])/g, '$1');
// replace ? in parameters with &
const parts = str.split('?');
str = parts.shift() + (parts.length > 0 ? '?' : '') + parts.join('&');
// dedupe forward slashes
str = str.replace(/^\/+/, '/');
return str;
}
module.exports = { module.exports = {
encodePath, encodePath,
generate, generate,
@ -84,5 +145,6 @@ module.exports = {
fileToComponentName, fileToComponentName,
getSubFolder, getSubFolder,
idx, idx,
normalizeUrl,
parse, parse,
}; };

View file

@ -11,6 +11,7 @@ import {
fileToComponentName, fileToComponentName,
idx, idx,
getSubFolder, getSubFolder,
normalizeUrl,
} from '@lib/load/utils'; } from '@lib/load/utils';
describe('load utils', () => { describe('load utils', () => {
@ -104,4 +105,48 @@ describe('load utils', () => {
expect(getSubFolder(testD, 'docs')).toBe('ro'); expect(getSubFolder(testD, 'docs')).toBe('ro');
expect(getSubFolder(testE, 'docs')).toBeNull(); expect(getSubFolder(testE, 'docs')).toBeNull();
}); });
test('normalizeUrl', () => {
const asserts = [
{
input: ['/', '/'],
output: '/',
},
{
input: ['/', 'docs'],
output: '/docs',
},
{
input: ['/', 'docs', 'en', 'next', 'blog'],
output: '/docs/en/next/blog',
},
{
input: ['/test/', '/docs', 'ro', 'doc1'],
output: '/test/docs/ro/doc1',
},
{
input: ['', '/', 'ko', 'hello'],
output: '/ko/hello',
},
{
input: ['hello', 'world'],
output: 'hello/world',
},
{
input: ['http://www.google.com/', 'foo/bar', '?test=123'],
output: 'http://www.google.com/foo/bar?test=123',
},
{
input: ['http:', 'www.google.com///', 'foo/bar', '?test=123'],
output: 'http://www.google.com/foo/bar?test=123',
},
{
input: ['http://foobar.com', '', 'test'],
output: 'http://foobar.com/test',
},
];
asserts.forEach(testCase => {
expect(normalizeUrl(testCase.input)).toBe(testCase.output);
});
});
}); });

View file

@ -6,8 +6,8 @@
*/ */
module.exports = { module.exports = {
title: 'docusaurus', title: 'Docusaurus',
tagline: '📝⚡️ Transform your document (문서) to a website', tagline: '⚡️ Painless static site generator',
organizationName: 'facebook', organizationName: 'facebook',
projectName: 'docusaurus', projectName: 'docusaurus',
baseUrl: '/', baseUrl: '/',