Enable sub-directories in docs/ (#705)

This commit is contained in:
Endilie Yacop Sucipto 2018-06-07 22:41:28 +08:00 committed by Joel Marcey
parent 49c27b733b
commit d04b3ca87b
10 changed files with 135 additions and 36 deletions

View file

@ -5,8 +5,8 @@ preserve_hierarchy: true
files: files:
- -
source: '/docs/*.md' source: '/docs/**/*.md'
translation: '/website/translated_docs/%locale%/%original_file_name%' translation: '/website/translated_docs/%locale%/**/%original_file_name%'
languages_mapping: &anchor languages_mapping: &anchor
locale: locale:
'af': 'af' 'af': 'af'

View file

@ -140,8 +140,8 @@ preserve_hierarchy: true
files: files:
- -
source: '/docs/*.md' source: '/docs/**/*.md'
translation: '/website/translated_docs/%locale%/%original_file_name%' translation: '/website/translated_docs/%locale%/**/%original_file_name%'
languages_mapping: &anchor languages_mapping: &anchor
locale: locale:
'de': 'de' 'de': 'de'

View file

@ -42,7 +42,7 @@ beforeAll(() => {
generateSite(); generateSite();
return Promise.all([ return Promise.all([
glob(docsDir + '/**/*.md'), glob(docsDir + '/**/*.md'),
glob(buildDir + '/' + siteConfig.projectName + '/docs/*.html'), glob(buildDir + '/' + siteConfig.projectName + '/docs/**/*.html'),
glob(docsDir + '/assets/*'), glob(docsDir + '/assets/*'),
glob(buildDir + '/' + siteConfig.projectName + '/img/*'), glob(buildDir + '/' + siteConfig.projectName + '/img/*'),
]).then(function(results) { ]).then(function(results) {

View file

@ -12,9 +12,20 @@ const DocsSidebar = require('./DocsSidebar.js');
const OnPageNav = require('./nav/OnPageNav.js'); 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 path = require('path');
// 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 {
getRelativeURL = (from, to) => {
const extension = this.props.config.cleanUrl ? '' : '.html';
return (
path
.relative(from, to)
.replace('\\', '/')
.replace(/^\.\.\//, '') + extension
);
};
render() { render() {
const metadata = this.props.metadata; const metadata = this.props.metadata;
const content = this.props.children; const content = this.props.children;
@ -28,7 +39,6 @@ class DocsLayout extends React.Component {
this.props.metadata.localized_id this.props.metadata.localized_id
] || this.props.metadata.title ] || this.props.metadata.title
: this.props.metadata.title; : this.props.metadata.title;
const extension = this.props.config.cleanUrl ? '' : '.html';
return ( return (
<Site <Site
config={this.props.config} config={this.props.config}
@ -55,7 +65,10 @@ class DocsLayout extends React.Component {
{metadata.previous_id && ( {metadata.previous_id && (
<a <a
className="docs-prev button" className="docs-prev button"
href={metadata.previous_id + extension}> href={this.getRelativeURL(
metadata.localized_id,
metadata.previous_id
)}>
{' '} {' '}
{i18n {i18n
? translation[this.props.metadata.language][ ? translation[this.props.metadata.language][
@ -71,7 +84,10 @@ class DocsLayout extends React.Component {
{metadata.next_id && ( {metadata.next_id && (
<a <a
className="docs-next button" className="docs-next button"
href={metadata.next_id + extension}> href={this.getRelativeURL(
metadata.localized_id,
metadata.next_id
)}>
{i18n {i18n
? translation[this.props.metadata.language][ ? translation[this.props.metadata.language][
'localized-strings' 'localized-strings'

View file

@ -15,7 +15,7 @@ const chalk = require('chalk');
const env = require('./env.js'); const env = require('./env.js');
const siteConfig = require(CWD + '/siteConfig.js'); const siteConfig = require(CWD + '/siteConfig.js');
const versionFallback = require('./versionFallback.js'); const versionFallback = require('./versionFallback.js');
const escapeStringRegexp = require('escape-string-regexp'); const utils = require('./utils.js');
const SupportedHeaderFields = new Set([ const SupportedHeaderFields = new Set([
'id', 'id',
@ -121,17 +121,10 @@ function extractMetadata(content) {
return {metadata, rawContent: both.content}; return {metadata, rawContent: both.content};
} }
// process the metadata for a document found in the docs folder // process the metadata for a document found in either 'docs' or 'translated_docs'
function processMetadata(file) { function processMetadata(file, refDir) {
const result = extractMetadata(fs.readFileSync(file, 'utf8')); const result = extractMetadata(fs.readFileSync(file, 'utf8'));
const language = utils.getLanguage(file, refDir) || 'en';
let regexSubFolder = new RegExp(
'/' + escapeStringRegexp(getDocsPath()) + '/(.*)/.*/'
);
const match = regexSubFolder.exec(file);
let language = match ? match[1] : 'en';
const metadata = {}; const metadata = {};
for (const fieldName of Object.keys(result.metadata)) { for (const fieldName of Object.keys(result.metadata)) {
if (SupportedHeaderFields.has(fieldName)) { if (SupportedHeaderFields.has(fieldName)) {
@ -142,7 +135,6 @@ function processMetadata(file) {
} }
const rawContent = result.rawContent; const rawContent = result.rawContent;
metadata.source = path.basename(file);
if (!metadata.id) { if (!metadata.id) {
metadata.id = path.basename(file, path.extname(file)); metadata.id = path.basename(file, path.extname(file));
@ -150,6 +142,21 @@ function processMetadata(file) {
if (metadata.id.includes('/')) { if (metadata.id.includes('/')) {
throw new Error('Document id cannot include "/".'); throw new Error('Document id cannot include "/".');
} }
// If a file is located in a subdirectory, prepend the subdir to it's ID
// Example:
// (file: 'docusaurus/docs/projectA/test.md', ID 'test', refDir: 'docs')
// returns 'projectA/test'
const subDir = utils.getSubDir(file, refDir);
if (subDir) {
metadata.id = `${subDir}/${metadata.id}`;
}
// Example: `docs/projectA/test.md` source is `projectA/test.md`
metadata.source = subDir
? `${subDir}/${path.basename(file)}`
: path.basename(file);
if (!metadata.title) { if (!metadata.title) {
metadata.title = metadata.id; metadata.title = metadata.id;
} }
@ -209,6 +216,7 @@ function generateMetadataDocs() {
const defaultMetadatas = {}; const defaultMetadatas = {};
// metadata for english files // metadata for english files
const docsDir = path.join(CWD, '../', getDocsPath());
let files = glob.sync(CWD + '/../' + getDocsPath() + '/**'); let files = glob.sync(CWD + '/../' + getDocsPath() + '/**');
files.forEach(file => { files.forEach(file => {
let language = 'en'; let language = 'en';
@ -216,7 +224,7 @@ function generateMetadataDocs() {
const extension = path.extname(file); const extension = path.extname(file);
if (extension === '.md' || extension === '.markdown') { if (extension === '.md' || extension === '.markdown') {
const res = processMetadata(file); const res = processMetadata(file, docsDir);
if (!res) { if (!res) {
return; return;
@ -255,23 +263,17 @@ function generateMetadataDocs() {
}); });
// metadata for non-english docs // metadata for non-english docs
const regexSubFolder = /translated_docs\/(.*?)\/.*/; const translatedDir = path.join(CWD, 'translated_docs');
files = glob.sync(CWD + '/translated_docs/**'); files = glob.sync(CWD + '/translated_docs/**');
files.forEach(file => { files.forEach(file => {
let language = 'en'; if (!utils.getLanguage(file, translatedDir)) {
const match = regexSubFolder.exec(file);
if (match) {
language = match[1];
}
if (enabledLanguages.indexOf(language) === -1) {
return; return;
} }
const extension = path.extname(file); const extension = path.extname(file);
if (extension === '.md' || extension === '.markdown') { if (extension === '.md' || extension === '.markdown') {
const res = processMetadata(file); const res = processMetadata(file, translatedDir);
if (!res) { if (!res) {
return; return;
} }

View file

@ -130,7 +130,7 @@ function execute(port) {
// handle all requests for document pages // handle all requests for document pages
const app = express(); const app = express();
app.get(/docs\/.*html$/, (req, res, next) => { app.get(/^\/docs\/.*html$/, (req, res, next) => {
let url = req.path.toString().replace(siteConfig.baseUrl, ''); let url = req.path.toString().replace(siteConfig.baseUrl, '');
// links is a map from a permalink to an id for each document // links is a map from a permalink to an id for each document

46
lib/server/utils.js Normal file
View file

@ -0,0 +1,46 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const path = require('path');
const escapeStringRegexp = require('escape-string-regexp');
const env = require('./env.js');
// Return the subdirectory path from a reference directory
// Example:
// (file: 'docs/projectA/test.md', refDir: 'subDir')
// returns 'projectA'
function getSubDir(file, refDir) {
let subDir = path.dirname(path.relative(refDir, file));
subDir = subDir.replace('\\', '/');
return subDir !== '.' ? subDir : null;
}
// Get the corresponding enabled language locale of a file.
// Example:
// (file: '/website/translated_docs/ko/projectA/test.md', refDir: 'website/translated_docs')
// returns 'ko'
function getLanguage(file, refDir) {
let regexSubFolder = new RegExp(
'/' + escapeStringRegexp(path.basename(refDir)) + '/(.*)/.*/'
);
const match = regexSubFolder.exec(file);
// Avoid misinterpreting subdirectory as language
if (match && env.translation.enabled) {
const enabledLanguages = env.translation
.enabledLanguages()
.map(language => language.tag);
if (enabledLanguages.indexOf(match[1]) !== -1) {
return match[1];
}
}
return null;
}
module.exports = {
getSubDir,
getLanguage,
};

View file

@ -12,6 +12,7 @@ const path = require('path');
const assert = require('assert'); const assert = require('assert');
const env = require('./env.js'); const env = require('./env.js');
const utils = require('./utils.js');
const siteConfig = require(CWD + '/siteConfig.js'); const siteConfig = require(CWD + '/siteConfig.js');
const ENABLE_TRANSLATION = fs.existsSync(CWD + '/languages.js'); const ENABLE_TRANSLATION = fs.existsSync(CWD + '/languages.js');
@ -194,7 +195,25 @@ function diffLatestDoc(file, id) {
// the version of the file to be used, and its language // the version of the file to be used, and its language
function processVersionMetadata(file, version, useVersion, language) { function processVersionMetadata(file, version, useVersion, language) {
const metadata = extractMetadata(fs.readFileSync(file, 'utf8')).metadata; const metadata = extractMetadata(fs.readFileSync(file, 'utf8')).metadata;
metadata.source = 'version-' + useVersion + '/' + path.basename(file);
// Add subdirectory information to versioned_doc metadata
// Example: `versioned_docs/version-1.1.6/projectA/readme.md` file with id `version-1.1.6-readme`
// and original_id `readme` will have metadata id of `version-1.1.6-projectA/readme` and original_id `projectA/readme`
const subDir = utils.getSubDir(
file,
path.join(CWD, 'versioned_docs', `version-${useVersion}`)
);
if (subDir) {
metadata.original_id = `${subDir}/${metadata.original_id}`;
metadata.id = metadata.id.replace(
`version-${useVersion}-`,
`version-${useVersion}-${subDir}/`
);
}
metadata.source = subDir
? `version-${useVersion}/${subDir}/${path.basename(file)}`
: `version-${useVersion}/${path.basename(file)}`;
const latestVersion = versions[0]; const latestVersion = versions[0];

View file

@ -13,6 +13,7 @@ const path = require('path');
const mkdirp = require('mkdirp'); const mkdirp = require('mkdirp');
const chalk = require('chalk'); const chalk = require('chalk');
const readMetadata = require('./server/readMetadata.js'); const readMetadata = require('./server/readMetadata.js');
const utils = require('./server/utils.js');
const versionFallback = require('./server/versionFallback.js'); const versionFallback = require('./server/versionFallback.js');
const env = require('./server/env.js'); const env = require('./server/env.js');
@ -66,12 +67,18 @@ function makeHeader(metadata) {
return header; return header;
} }
function writeFileAndCreateFolder(file, content, encoding) {
mkdirp.sync(path.dirname(file));
fs.writeFileSync(file, content, encoding);
}
const versionFolder = CWD + '/versioned_docs/version-' + version; const versionFolder = CWD + '/versioned_docs/version-' + version;
mkdirp.sync(versionFolder); mkdirp.sync(versionFolder);
// copy necessary files to new version, changing some of its metadata to reflect the versioning // copy necessary files to new version, changing some of its metadata to reflect the versioning
let files = glob.sync(CWD + '/../' + readMetadata.getDocsPath() + '/*'); let files = glob.sync(CWD + '/../' + readMetadata.getDocsPath() + '/**');
files.forEach(file => { files.forEach(file => {
const ext = path.extname(file); const ext = path.extname(file);
if (ext !== '.md' && ext !== '.markdown') { if (ext !== '.md' && ext !== '.markdown') {
@ -102,9 +109,17 @@ files.forEach(file => {
metadata.original_id = metadata.id; metadata.original_id = metadata.id;
metadata.id = 'version-' + version + '-' + metadata.id; metadata.id = 'version-' + version + '-' + metadata.id;
const targetFile = versionFolder + '/' + path.basename(file); const docsDir = path.join(CWD, '../', readMetadata.getDocsPath());
const subDir = utils.getSubDir(file, docsDir);
const targetFile = subDir
? `${versionFolder}/${subDir}/${path.basename(file)}`
: `${versionFolder}/${path.basename(file)}`;
fs.writeFileSync(targetFile, makeHeader(metadata) + rawContent, 'utf8'); writeFileAndCreateFolder(
targetFile,
makeHeader(metadata) + rawContent,
'utf8'
);
}); });
// copy sidebar if necessary // copy sidebar if necessary

View file

@ -46,13 +46,14 @@ function execute() {
}; };
// look through markdown headers of docs for titles and categories to translate // look through markdown headers of docs for titles and categories to translate
const docsDir = path.join(CWD, '../', readMetadata.getDocsPath());
let files = glob.sync(CWD + '/../' + readMetadata.getDocsPath() + '/**'); let files = glob.sync(CWD + '/../' + readMetadata.getDocsPath() + '/**');
files.forEach(file => { files.forEach(file => {
const extension = path.extname(file); const extension = path.extname(file);
if (extension === '.md' || extension === '.markdown') { if (extension === '.md' || extension === '.markdown') {
let res; let res;
try { try {
res = readMetadata.processMetadata(file); res = readMetadata.processMetadata(file, docsDir);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
process.exit(1); process.exit(1);