diff --git a/examples/sidebar.json b/examples/sidebar.json new file mode 100644 index 0000000000..7a1d054018 --- /dev/null +++ b/examples/sidebar.json @@ -0,0 +1,10 @@ +{ + "docs": { + "Docusaurus": ["doc1"], + "First Category": ["doc2"], + "Second Category": ["doc3"] + }, + "docs-other": { + "First Category": ["doc4", "doc5"] + } +} diff --git a/lib/core/DocsSidebar.js b/lib/core/DocsSidebar.js index 037490c26a..d49604028e 100644 --- a/lib/core/DocsSidebar.js +++ b/lib/core/DocsSidebar.js @@ -16,8 +16,8 @@ const readCategories = require("../server/readCategories.js"); class DocsSidebar extends React.Component { render() { - let layout = this.props.metadata.layout; - let docsCategories = readCategories(layout); + let sidebar = this.props.metadata.sidebar; + let docsCategories = readCategories(sidebar); return ( - + {this.props.config.ogImage && + } lang.enabled).map(lang => { enabledLanguages.push(lang.tag); @@ -82,27 +80,21 @@ function execute() { let mdToHtml = {}; for (let i = 0; i < Metadata.length; i++) { const metadata = Metadata[i]; - mdToHtml["/docs/" + metadata.language + "/" + metadata.source] = - "/" + siteConfig.projectName + "/" + metadata.permalink; + if (metadata.language !== "en") { + continue; + } + mdToHtml[metadata.source] = siteConfig.baseUrl + metadata.permalink; } const DocsLayout = require("../core/DocsLayout.js"); - fs.removeSync(__dirname + "/../../build"); + fs.removeSync(CWD + "/build"); - // create html files for all docs + // create html files for all English docs let files = glob.sync(CWD + "/../docs/**"); files.forEach(file => { // console.log(file); let language = "en"; - const match = regexSubFolder.exec(file); - if (match) { - language = match[1]; - } - - if (enabledLanguages.indexOf(language) === -1) { - return; - } const extension = path.extname(file); @@ -120,6 +112,62 @@ function execute() { rawContent = insertTableOfContents(rawContent); } + /* replace any links to markdown files to their website html links */ + Object.keys(mdToHtml).forEach(function(key, index) { + rawContent = rawContent.replace( + new RegExp(key, "g"), + mdToHtml[key].replace("/en/", "/" + language + "/") + ); + }); + + const docComp = ( + + {rawContent} + + ); + const str = renderToStaticMarkup(docComp); + + let targetFile = + CWD + "/build/" + siteConfig.projectName + "/" + metadata.permalink; + // console.log(targetFile); + writeFileAndCreateFolder(targetFile, str); + } + }); + + // create html files for all non-English docs + if (languages.length > 1) { + files = glob.sync(CWD + "/translated_docs/**"); + files.forEach(file => { + let language = "en"; + + const regexSubFolder = /translated_docs\/(.*)\/.*/; + const match = regexSubFolder.exec(file); + if (match) { + language = match[1]; + } + + if (enabledLanguages.indexOf(language) === -1) { + return; + } + + const extension = path.extname(file); + if (extension !== ".md" && extension !== ".markdown") { + return; + } + + const result = readMetadata.processMetadata(file); + if (!result) { + return; + } + + const metadata = result.metadata; + let rawContent = result.rawContent; + + /* generate table of contents if appropriate */ + if (rawContent && rawContent.indexOf(TABLE_OF_CONTENTS_TOKEN) != -1) { + rawContent = insertTableOfContents(rawContent); + } + /* replace any links to markdown files to their website html links */ Object.keys(mdToHtml).forEach(function(key, index) { rawContent = rawContent.replace(new RegExp(key, "g"), mdToHtml[key]); @@ -131,18 +179,20 @@ function execute() { ); const str = renderToStaticMarkup(docComp); - let targetFile = - __dirname + - "/../../build" + - "/" + - siteConfig.projectName + - "/" + - metadata.permalink; + CWD + "/build/" + siteConfig.projectName + "/" + metadata.permalink; // console.log(targetFile); writeFileAndCreateFolder(targetFile, str); - } - }); + }); + } + + /* copy docs assets if they exist */ + if (fs.existsSync(CWD + "/../docs/assets")) { + fs.copySync( + CWD + "/../docs/assets", + CWD + "/build/" + siteConfig.projectName + "/docs/assets" + ); + } // create html files for all blog posts if (fs.existsSync(__dirname + "../core/MetadataBlog.js")) { @@ -152,8 +202,13 @@ function execute() { const MetadataBlog = require("../core/MetadataBlog.js"); const BlogPostLayout = require("../core/BlogPostLayout.js"); - files = glob.sync(CWD + "/../blog/**/*.*"); + files = glob.sync(CWD + "/blog/**/*.*"); files.sort().reverse().forEach(file => { + const extension = path.extname(file); + if (extension !== ".md" && extension !== ".markdown") { + return; + } + /* convert filename ot use slashes */ const filePath = path .basename(file) @@ -185,13 +240,7 @@ function execute() { const str = renderToStaticMarkup(blogPostComp); let targetFile = - __dirname + - "/../../build" + - "/" + - siteConfig.projectName + - "/" + - "blog/" + - filePath; + CWD + "/build/" + siteConfig.projectName + "/blog/" + filePath; writeFileAndCreateFolder(targetFile, str); }); // create html files for all blog pages @@ -210,25 +259,32 @@ function execute() { const str = renderToStaticMarkup(blogPageComp); let targetFile = - __dirname + - "/../../build" + - "/" + + CWD + + "/build/" + siteConfig.projectName + - "/" + - "blog" + + "/blog" + (page > 0 ? "/page" + (page + 1) : "") + "/index.html"; writeFileAndCreateFolder(targetFile, str); } + /* copy blog assets if they exist */ + if (fs.existsSync(CWD + "/blog/assets")) { + fs.copySync( + CWD + "/blog/assets", + CWD + "/build/" + siteConfig.projectName + "/blog/assets" + ); + } + /* copy all static files from docusaurus */ files = glob.sync(__dirname + "/../static/**"); files.forEach(file => { - let targetFile = file.replace( - "/lib/static/", - "/build" + "/" + siteConfig.projectName + "/" - ); - + let targetFile = + CWD + + "/build/" + + siteConfig.projectName + + "/" + + file.split("/static/")[1]; if (file.match(/\.css$/)) { let cssContent = fs.readFileSync(file); cssContent = cssContent @@ -259,12 +315,7 @@ function execute() { files.forEach(file => { if (file.match(/\.css$/) && !isSeparateCss(file)) { const mainCss = - __dirname + - "/../../build" + - "/" + - siteConfig.projectName + - "/" + - "css/main.css"; + CWD + "/build/" + siteConfig.projectName + "/css/main.css"; let cssContent = fs.readFileSync(file); cssContent = fs.readFileSync(mainCss) + "\n" + cssContent; @@ -287,12 +338,7 @@ function execute() { } else if (!fs.lstatSync(file).isDirectory()) { let parts = file.split("static"); let targetFile = - __dirname + - "/../../build" + - "/" + - siteConfig.projectName + - "/" + - parts[1]; + CWD + "/build/" + siteConfig.projectName + "/" + parts[1]; mkdirp.sync(targetFile.replace(new RegExp("/[^/]*$"), "")); fs.copySync(file, targetFile); } @@ -315,7 +361,7 @@ function execute() { const ReactComp = require(tempFile); let targetFile = - __dirname + "/../../build/" + siteConfig.projectName + "/" + parts[1]; + CWD + "/build/" + siteConfig.projectName + "/" + parts[1]; targetFile = targetFile.replace(/\.js$/, ".html"); const regexLang = /\/pages\/(.*)\//; @@ -364,21 +410,14 @@ function execute() { } else if (!fs.lstatSync(file).isDirectory()) { let parts = file.split("pages"); let targetFile = - __dirname + - "/../../build" + - "/" + - siteConfig.projectName + - "/" + - parts[1]; + CWD + "/build/" + siteConfig.projectName + "/" + parts[1]; mkdirp.sync(targetFile.replace(new RegExp("/[^/]*$"), "")); fs.copySync(file, targetFile); } }); /* copy html files in 'en' to base level as well */ - files = glob.sync( - __dirname + "/../../build" + "/" + siteConfig.projectName + "/" + "en/**" - ); + files = glob.sync(CWD + "/build/" + siteConfig.projectName + "/en/**"); files.forEach(file => { let targetFile = file.replace("en/", ""); if (file.match(/\.html$/)) { diff --git a/lib/server/readCategories.js b/lib/server/readCategories.js index 49d5d0860c..2ae0d04875 100644 --- a/lib/server/readCategories.js +++ b/lib/server/readCategories.js @@ -23,7 +23,7 @@ if (fs.existsSync(CWD + "/languages.js")) { } ]; } -function readCategories(layout) { +function readCategories(sidebar) { const enabledLanguages = []; languages.filter(lang => lang.enabled).map(lang => { enabledLanguages.push(lang.tag); @@ -35,7 +35,7 @@ function readCategories(layout) { const language = enabledLanguages[k]; const metadatas = Metadata.filter(metadata => { - return metadata.layout === layout && metadata.language === language; + return metadata.sidebar === sidebar && metadata.language === language; }); // Build a hashmap of article_id -> metadata diff --git a/lib/server/readMetadata.js b/lib/server/readMetadata.js index 521f267624..efc31d8f65 100644 --- a/lib/server/readMetadata.js +++ b/lib/server/readMetadata.js @@ -12,6 +12,8 @@ const CWD = process.cwd(); const path = require("path"); const fs = require("fs"); const os = require("os"); +const glob = require("glob"); +const siteConfig = require(CWD + "/siteConfig.js"); let languages; if (fs.existsSync(CWD + "/languages.js")) { languages = require(CWD + "/languages.js"); @@ -24,7 +26,38 @@ if (fs.existsSync(CWD + "/languages.js")) { } ]; } -const glob = require("glob"); + +function readSidebar() { + const allSidebars = require(CWD + "/sidebar.json"); + const order = {}; + + Object.keys(allSidebars).forEach(sidebar => { + const categories = allSidebars[sidebar]; + + let ids = []; + let categoryOrder = []; + Object.keys(categories).forEach(category => { + ids = ids.concat(categories[category]); + for (let i = 0; i < categories[category].length; i++) { + categoryOrder.push(category); + } + }); + + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let previous, next; + if (i > 0) previous = ids[i - 1]; + if (i < ids.length - 1) next = ids[i + 1]; + order[id] = { + previous: previous, + next: next, + sidebar: sidebar, + category: categoryOrder[i] + }; + } + }); + return order; +} function splitHeader(content) { const lines = content.split(os.EOL); @@ -76,29 +109,39 @@ function processMetadata(file) { const rawContent = result.rawContent; metadata.source = path.basename(file); - // in permalink replace /en/ language with localized folder - metadata.permalink = metadata.permalink.replace( - /\/en\//g, - "/" + language + "/" - ); + if (languages.length === 1 && !siteConfig.useEnglishUrl) { + metadata.permalink = "docs/" + metadata.id + ".html"; + } else { + metadata.permalink = "docs/" + language + "/" + metadata.id + ".html"; + } + // change ids previous, next metadata.localized_id = metadata.id; metadata.id = language + "-" + metadata.id; - if (metadata.previous) { - metadata.previous_id = metadata.previous; - metadata.previous = language + "-" + metadata.previous; - } - if (metadata.next) { - metadata.next_id = metadata.next; - metadata.next = language + "-" + metadata.next; - } metadata.language = language; + const order = readSidebar(); + const id = metadata.localized_id; + + metadata.sidebar = order[id].sidebar; + metadata.category = order[id].category; + + if (order[id].next) { + metadata.next_id = order[id].next; + metadata.next = language + "-" + order[id].next; + } + if (order[id].previous) { + metadata.previous_id = order[id].previous; + metadata.previous = language + "-" + order[id].previous; + } + return { metadata, rawContent: rawContent }; } function generateDocsMetadata() { - const regexSubFolder = /docs\/(.*)\/.*/; + const order = readSidebar(); + + const regexSubFolder = /translated_docs\/(.*)\/.*/; const enabledLanguages = []; languages.filter(lang => lang.enabled).map(lang => { @@ -107,7 +150,25 @@ function generateDocsMetadata() { const metadatas = []; - const files = glob.sync(CWD + "/../docs/**"); + /* metadata for english files */ + let files = glob.sync(CWD + "/../docs/**"); + files.forEach(file => { + let language = "en"; + + const extension = path.extname(file); + + if (extension === ".md" || extension === ".markdown") { + const res = processMetadata(file); + if (!res) { + return; + } + let metadata = res.metadata; + metadatas.push(metadata); + } + }); + + /* metadata for non-english docs */ + files = glob.sync(CWD + "/translated_docs/**"); files.forEach(file => { let language = "en"; const match = regexSubFolder.exec(file); @@ -126,7 +187,7 @@ function generateDocsMetadata() { if (!res) { return; } - const metadata = res.metadata; + let metadata = res.metadata; metadatas.push(metadata); } }); @@ -145,8 +206,12 @@ function generateDocsMetadata() { function generateBlogMetadata() { const metadatas = []; - let files = glob.sync(CWD + "/../blog/**/*.*"); + let files = glob.sync(CWD + "/blog/**/*.*"); files.sort().reverse().forEach(file => { + const extension = path.extname(file); + if (extension !== ".md" && extension !== ".markdown") { + return; + } // Transform // 2015-08-13-blog-post-name-0.5.md // into @@ -156,8 +221,6 @@ function generateBlogMetadata() { .replace("-", "/") .replace("-", "/") .replace("-", "/") - // react-middleware is broken with files that contains multiple . - // like react-0.14.js .replace(/\./g, "-") .replace(/\-md$/, ".html"); const result = extractMetadata(fs.readFileSync(file, { encoding: "utf8" })); @@ -181,7 +244,9 @@ function generateBlogMetadata() { ); } -module.exports.extractMetadata = extractMetadata; -module.exports.processMetadata = processMetadata; -module.exports.generateDocsMetadata = generateDocsMetadata; -module.exports.generateBlogMetadata = generateBlogMetadata; +module.exports = { + extractMetadata, + processMetadata, + generateDocsMetadata, + generateBlogMetadata +}; diff --git a/lib/server/server.js b/lib/server/server.js index cdc1645b42..85a62e5048 100644 --- a/lib/server/server.js +++ b/lib/server/server.js @@ -9,7 +9,6 @@ function execute(port) { const translation = require("./translation.js"); - const CWD = process.cwd(); const express = require("express"); const React = require("react"); const request = require("request"); @@ -17,11 +16,14 @@ function execute(port) { const fs = require("fs-extra"); const os = require("os"); const path = require("path"); - const readMetadata = require("./readMetadata.js"); const toSlug = require("../core/toSlug.js"); const mkdirp = require("mkdirp"); const glob = require("glob"); const translate = require("./translate.js"); + + const CWD = process.cwd(); + const ENABLE_TRANSLATION = fs.existsSync(CWD + "/languages.js"); + let siteConfig = require(CWD + "/siteConfig.js"); /** @@ -70,9 +72,12 @@ function execute(port) { /****************************************************************************/ + let readMetadata; let Metadata; function reloadMetadata() { + purgeCache("./readMetadata.js"); + readMetadata = require("./readMetadata.js"); readMetadata.generateDocsMetadata(); purgeCache("../core/metadata.js"); Metadata = require("../core/metadata.js"); @@ -118,33 +123,36 @@ function execute(port) { reloadMetadata(); /* handle all requests for document pages */ - const app = express().get(/docs\/[\s\S]*html$/, (req, res, next) => { + const app = express().get(/docs\/.*html$/, (req, res, next) => { purgeCache(CWD + "/siteConfig.js"); siteConfig = require(CWD + "/siteConfig.js"); - console.log(req.path); - reloadMetadata(); let links = {}; for (let i = 0; i < Metadata.length; i++) { const metadata = Metadata[i]; - links[metadata.permalink] = - "docs/" + metadata.language + "/" + metadata.source; + if (metadata.language === "en") { + links[metadata.permalink] = CWD + "/../docs/" + metadata.source; + } else { + links[metadata.permalink] = + CWD + "/translated_docs/" + metadata.language + "/" + metadata.source; + } } let mdToHtml = {}; for (let i = 0; i < Metadata.length; i++) { const metadata = Metadata[i]; - mdToHtml["/docs/" + metadata.language + "/" + metadata.source] = - siteConfig.baseUrl + metadata.permalink; + if (metadata.language !== "en") { + continue; + } + mdToHtml[metadata.source] = siteConfig.baseUrl + metadata.permalink; } let file = links[req.path.toString().replace(siteConfig.baseUrl, "")]; - file = CWD + "/../" + file; + if (!fs.existsSync(file)) { next(); return; } - console.log(file); const result = readMetadata.processMetadata(file); const metadata = result.metadata; @@ -158,9 +166,17 @@ function execute(port) { /* replace any links to markdown files to their website html links */ Object.keys(mdToHtml).forEach(function(key, index) { - rawContent = rawContent.replace(new RegExp(key, "g"), mdToHtml[key]); + rawContent = rawContent.replace( + new RegExp(key, "g"), + mdToHtml[key].replace("/en/", "/" + language + "/") + ); }); + rawContent = rawContent.replace( + /\]\(assets\//g, + "](" + siteConfig.baseUrl + "docs/assets/" + ); + purgeCache("../core/DocsLayout.js"); const DocsLayout = require("../core/DocsLayout.js"); const docComp = ( @@ -172,7 +188,7 @@ function execute(port) { res.send(renderToStaticMarkup(docComp)); }); /* handle all requests for blog pages and posts */ - app.get(/blog\/[\s\S]*html$/, (req, res) => { + app.get(/blog\/.*html$/, (req, res) => { purgeCache(CWD + "/siteConfig.js"); siteConfig = require(CWD + "/siteConfig.js"); @@ -226,12 +242,16 @@ function execute(port) { let file = parts[1]; file = file.replace(/\.html$/, ".md"); file = file.replace(new RegExp("/", "g"), "-"); - file = CWD + "/../blog/" + file; + file = CWD + "/blog/" + file; const result = readMetadata.extractMetadata( fs.readFileSync(file, { encoding: "utf8" }) ); - const rawContent = result.rawContent; + let rawContent = result.rawContent; + rawContent = rawContent.replace( + /\]\(assets\//g, + "](" + siteConfig.baseUrl + "blog/assets/" + ); const metadata = Object.assign( { path: req.path.toString().split("blog/")[1], content: rawContent }, result.metadata @@ -260,8 +280,6 @@ function execute(port) { purgeCache(CWD + "/siteConfig.js"); siteConfig = require(CWD + "/siteConfig.js"); - console.log(req.path); - /* look for user provided html file first */ let htmlFile = req.path.toString().replace(siteConfig.baseUrl, ""); htmlFile = CWD + "/pages/" + htmlFile; @@ -339,7 +357,6 @@ function execute(port) { res.send(str); } else { - console.log(req.path); next(); return; } @@ -382,6 +399,14 @@ function execute(port) { }); /* serve static content first from user folder then from docusaurus */ + app.use( + siteConfig.baseUrl + "docs/assets/", + express.static(CWD + "/../docs/assets") + ); + app.use( + siteConfig.baseUrl + "blog/assets/", + express.static(CWD + "/blog/assets") + ); app.use(siteConfig.baseUrl, express.static(CWD + "/static")); app.use(siteConfig.baseUrl, express.static(__dirname + "/../static")); diff --git a/lib/static/css/main.css b/lib/static/css/main.css index 301fdf8607..06484bf7d2 100644 --- a/lib/static/css/main.css +++ b/lib/static/css/main.css @@ -492,6 +492,7 @@ header h2 { display: flex; flex-flow: row nowrap; height: 34px; + z-index: 10000; } .fixedHeaderContainer header { display: flex; diff --git a/lib/write-translations.js b/lib/write-translations.js index 4bd7e711a1..24c46f1514 100755 --- a/lib/write-translations.js +++ b/lib/write-translations.js @@ -20,6 +20,7 @@ const path = require("path"); const siteConfig = require(CWD + "/siteConfig.js"); const babylon = require("babylon"); const traverse = require("babel-traverse").default; +const sidebar = require(CWD + "/sidebar.json"); function writeFileAndCreateFolder(file, content) { mkdirp.sync(file.replace(new RegExp("/[^/]*$"), "")); @@ -37,16 +38,17 @@ function execute() { }; /* look through front matter of docs for titles and categories to translate */ - let files = glob.sync(CWD + "/../docs/en/**"); + let files = glob.sync(CWD + "/../docs/**"); files.forEach(file => { const extension = path.extname(file); if (extension === ".md" || extension === ".markdown") { - const metadata = readMetadata.extractMetadata( - fs.readFileSync(file, "utf8") - ).metadata; + const res = readMetadata.processMetadata(file); + if (!res) { + return; + } + const metadata = res.metadata; - translations["localized-strings"][metadata.id] = metadata.title; - translations["localized-strings"][metadata.category] = metadata.category; + translations["localized-strings"][metadata.localized_id] = metadata.title; if (metadata.sidebar_title) { translations["localized-strings"][metadata.sidebar_title] = @@ -64,6 +66,14 @@ function execute() { siteConfig.headerLinksExternal[i].text; } + /* find sidebar category titles to translate */ + Object.keys(sidebar).forEach(sb => { + const categories = sidebar[sb]; + Object.keys(categories).forEach(category => { + translations["localized-strings"][category] = category; + }); + }); + /* go through pages to look for text inside translate tags */ files = glob.sync(CWD + "/pages/en/**"); files.forEach(file => { @@ -94,7 +104,10 @@ function execute() { }); } }); - writeFileAndCreateFolder(CWD + "/i18n/en.json", JSON.stringify(translations)); + writeFileAndCreateFolder( + CWD + "/i18n/en.json", + JSON.stringify(translations, null, 2) + ); } execute(); diff --git a/package.json b/package.json index 59d7eea1e6..b987296c3b 100644 --- a/package.json +++ b/package.json @@ -17,13 +17,14 @@ "express": "^4.15.3", "fs-extra": "^3.0.1", "glob": "^7.1.2", + "prettier": "^1.5.3", "react": "^15.5.4", "react-dom": "^15.5.4", "request": "^2.81.0", "shelljs": "^0.7.8" }, "name": "docusaurus", - "version": "1.0.0-alpha.29", + "version": "1.0.0-alpha.34", "bin": { "docusaurus-start": "./lib/start-server.js", "docusaurus-build": "./lib/build-files.js",