Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable sub-directories in docs/ #705

Merged
merged 12 commits into from
Jun 7, 2018
4 changes: 2 additions & 2 deletions crowdin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ preserve_hierarchy: true

files:
-
source: '/docs/*.md'
translation: '/website/translated_docs/%locale%/%original_file_name%'
source: '/docs/**/*.md'
translation: '/website/translated_docs/%locale%/**/%original_file_name%'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the Crowdin example shown at https://docusaurus.io/docs/en/translation#crowdin be updated with this change?

languages_mapping: &anchor
locale:
'af': 'af'
Expand Down
2 changes: 1 addition & 1 deletion lib/__tests__/build-files.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ beforeAll(() => {
generateSite();
return Promise.all([
glob(docsDir + '/**/*.md'),
glob(buildDir + '/' + siteConfig.projectName + '/docs/*.html'),
glob(buildDir + '/' + siteConfig.projectName + '/docs/**/*.html'),
glob(docsDir + '/assets/*'),
glob(buildDir + '/' + siteConfig.projectName + '/img/*'),
]).then(function(results) {
Expand Down
22 changes: 19 additions & 3 deletions lib/core/DocsLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,20 @@ const DocsSidebar = require('./DocsSidebar.js');
const OnPageNav = require('./nav/OnPageNav.js');
const Site = require('./Site.js');
const translation = require('../server/translation.js');
const path = require('path');

// component used to generate whole webpage for docs, including sidebar/header/footer
class DocsLayout extends React.Component {
getRelativeURL = (from, to) => {
const extension = this.props.config.cleanUrl ? '' : '.html';
return (
path
.relative(from, to)
.replace('\\', '/')
.replace(/^\.\.\//, '') + extension
);
};

render() {
const metadata = this.props.metadata;
const content = this.props.children;
Expand All @@ -28,7 +39,6 @@ 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 (
<Site
config={this.props.config}
Expand All @@ -55,7 +65,10 @@ class DocsLayout extends React.Component {
{metadata.previous_id && (
<a
className="docs-prev button"
href={metadata.previous_id + extension}>
href={this.getRelativeURL(
metadata.localized_id,
metadata.previous_id
)}>
←{' '}
{i18n
? translation[this.props.metadata.language][
Expand All @@ -71,7 +84,10 @@ class DocsLayout extends React.Component {
{metadata.next_id && (
<a
className="docs-next button"
href={metadata.next_id + extension}>
href={this.getRelativeURL(
metadata.localized_id,
metadata.next_id
)}>
{i18n
? translation[this.props.metadata.language][
'localized-strings'
Expand Down
46 changes: 24 additions & 22 deletions lib/server/readMetadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const chalk = require('chalk');
const env = require('./env.js');
const siteConfig = require(CWD + '/siteConfig.js');
const versionFallback = require('./versionFallback.js');
const escapeStringRegexp = require('escape-string-regexp');
const utils = require('./utils.js');

const SupportedHeaderFields = new Set([
'id',
Expand Down Expand Up @@ -121,17 +121,10 @@ function extractMetadata(content) {
return {metadata, rawContent: both.content};
}

// process the metadata for a document found in the docs folder
function processMetadata(file) {
// process the metadata for a document found in either 'docs' or 'translated_docs'
function processMetadata(file, refDir) {
const result = extractMetadata(fs.readFileSync(file, 'utf8'));

let regexSubFolder = new RegExp(
'/' + escapeStringRegexp(getDocsPath()) + '/(.*)/.*/'
);

const match = regexSubFolder.exec(file);
let language = match ? match[1] : 'en';

const language = utils.getLanguage(file, refDir) || 'en';
const metadata = {};
for (const fieldName of Object.keys(result.metadata)) {
if (SupportedHeaderFields.has(fieldName)) {
Expand All @@ -142,14 +135,28 @@ function processMetadata(file) {
}

const rawContent = result.rawContent;
metadata.source = path.basename(file);

if (!metadata.id) {
metadata.id = path.basename(file, path.extname(file));
}
if (metadata.id.includes('/')) {
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) {
metadata.title = metadata.id;
}
Expand Down Expand Up @@ -209,14 +216,15 @@ function generateMetadataDocs() {
const defaultMetadatas = {};

// metadata for english files
const docsDir = path.join(CWD, '../', getDocsPath());
let files = glob.sync(CWD + '/../' + getDocsPath() + '/**');
files.forEach(file => {
let language = 'en';

const extension = path.extname(file);

if (extension === '.md' || extension === '.markdown') {
const res = processMetadata(file);
const res = processMetadata(file, docsDir);

if (!res) {
return;
Expand Down Expand Up @@ -255,23 +263,17 @@ function generateMetadataDocs() {
});

// metadata for non-english docs
const regexSubFolder = /translated_docs\/(.*?)\/.*/;
const translatedDir = path.join(CWD, 'translated_docs');
files = glob.sync(CWD + '/translated_docs/**');
files.forEach(file => {
let language = 'en';
const match = regexSubFolder.exec(file);
if (match) {
language = match[1];
}

if (enabledLanguages.indexOf(language) === -1) {
if (!utils.getLanguage(file, translatedDir)) {
return;
}

const extension = path.extname(file);

if (extension === '.md' || extension === '.markdown') {
const res = processMetadata(file);
const res = processMetadata(file, translatedDir);
if (!res) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function execute(port) {
// handle all requests for document pages
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, '');

// links is a map from a permalink to an id for each document
Expand Down
46 changes: 46 additions & 0 deletions lib/server/utils.js
Original file line number Diff line number Diff line change
@@ -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,
};
21 changes: 20 additions & 1 deletion lib/server/versionFallback.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const path = require('path');
const assert = require('assert');

const env = require('./env.js');
const utils = require('./utils.js');
const siteConfig = require(CWD + '/siteConfig.js');

const ENABLE_TRANSLATION = fs.existsSync(CWD + '/languages.js');
Expand Down Expand Up @@ -194,7 +195,25 @@ function diffLatestDoc(file, id) {
// the version of the file to be used, and its language
function processVersionMetadata(file, version, useVersion, language) {
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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use template strings instead of concatenating the strings? It'll be easier to read.

`version-${useVersion}-`,
`version-${useVersion}-${subDir}/`
);
}

metadata.source = subDir
? `version-${useVersion}/${subDir}/${path.basename(file)}`
: `version-${useVersion}/${path.basename(file)}`;

const latestVersion = versions[0];

Expand Down
21 changes: 18 additions & 3 deletions lib/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const path = require('path');
const mkdirp = require('mkdirp');
const chalk = require('chalk');
const readMetadata = require('./server/readMetadata.js');
const utils = require('./server/utils.js');
const versionFallback = require('./server/versionFallback.js');
const env = require('./server/env.js');

Expand Down Expand Up @@ -66,12 +67,18 @@ function makeHeader(metadata) {
return header;
}

function writeFileAndCreateFolder(file, content, encoding) {
mkdirp.sync(path.dirname(file));

fs.writeFileSync(file, content, encoding);
}

const versionFolder = CWD + '/versioned_docs/version-' + version;

mkdirp.sync(versionFolder);

// 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 => {
const ext = path.extname(file);
if (ext !== '.md' && ext !== '.markdown') {
Expand Down Expand Up @@ -102,9 +109,17 @@ files.forEach(file => {
metadata.original_id = 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
Expand Down
3 changes: 2 additions & 1 deletion lib/write-translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ function execute() {
};

// 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() + '/**');
files.forEach(file => {
const extension = path.extname(file);
if (extension === '.md' || extension === '.markdown') {
let res;
try {
res = readMetadata.processMetadata(file);
res = readMetadata.processMetadata(file, docsDir);
} catch (e) {
console.error(e);
process.exit(1);
Expand Down