From 9e7729ad748fe08c196cb4b74100064070646ab2 Mon Sep 17 00:00:00 2001 From: endiliey Date: Wed, 8 Aug 2018 00:39:17 +0800 Subject: [PATCH] feat: react router config generation for docs --- lib/load/blog.js | 47 ----------------------------------- lib/load/docs.js | 33 +++++++++---------------- lib/load/index.js | 61 ++++++++++++++++++++-------------------------- lib/load/routes.js | 38 +++++++++++++++++++++++++++++ lib/load/utils.js | 48 ++++++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 103 deletions(-) delete mode 100644 lib/load/blog.js create mode 100644 lib/load/routes.js create mode 100644 lib/load/utils.js diff --git a/lib/load/blog.js b/lib/load/blog.js deleted file mode 100644 index c2dccf848d..0000000000 --- a/lib/load/blog.js +++ /dev/null @@ -1,47 +0,0 @@ -const fs = require('fs-extra'); -const path = require('path'); -const fm = require('front-matter'); -const globby = require('globby'); - -const indexRE = /(^|.*\/)index\.md$/i; -const mdRE = /\.md$/; - -function fileToPath(file) { - if (indexRE.test(file)) { - return file.replace(indexRE, '/$1'); - } - return `/${file.replace(mdRE, '').replace(/\\/g, '/')}`; -} - -function parse(fileString) { - if (!fm.test(fileString)) { - return {metadata: null, content: fileString}; - } - const {attributes: metadata, body: content} = fm(fileString); - - return {metadata, content}; -} - -async function loadBlog(blogDir) { - const blogFiles = await globby(['**/*.md'], { - cwd: blogDir - }); - - const blogDatas = await Promise.all( - blogFiles.map(async file => { - const filepath = path.resolve(blogDir, file); - const fileString = await fs.readFile(filepath, 'utf-8'); - const {metadata, content} = parse(fileString); - - return { - path: fileToPath(file), - content, - ...metadata - }; - }) - ); - blogDatas.sort((a, b) => b.date - a.date); - return blogDatas; -} - -module.exports = loadBlog; diff --git a/lib/load/docs.js b/lib/load/docs.js index d5dfa0e7ce..7483b9d2e7 100644 --- a/lib/load/docs.js +++ b/lib/load/docs.js @@ -2,16 +2,7 @@ const fs = require('fs-extra'); const path = require('path'); const fm = require('front-matter'); const globby = require('globby'); - -const indexRE = /(^|.*\/)index\.md$/i; -const mdRE = /\.md$/; - -function fileToPath(file) { - if (indexRE.test(file)) { - return file.replace(indexRE, '/$1'); - } - return `/${file.replace(mdRE, '').replace(/\\/g, '/')}`; -} +const {encodePath, fileToPath} = require('./utils'); function parse(fileString) { if (!fm.test(fileString)) { @@ -22,27 +13,25 @@ function parse(fileString) { return {metadata, content}; } -// still TODO. still copy paste from blog logic -async function loadDocs(siteDir) { - const blogFiles = await globby(['**/*.md'], { - cwd: siteDir +async function loadDocs(docsDir) { + const docsFiles = await globby(['**/*.md'], { + cwd: docsDir }); - const blogDatas = await Promise.all( - blogFiles.map(async file => { - const filepath = path.resolve(siteDir, file); + const docsData = await Promise.all( + docsFiles.map(async source => { + const filepath = path.resolve(docsDir, source); const fileString = await fs.readFile(filepath, 'utf-8'); - const {metadata, content} = parse(fileString); + const {metadata} = parse(fileString); return { - path: fileToPath(file), - content, + path: encodePath(fileToPath(source)), + source, ...metadata }; }) ); - blogDatas.sort((a, b) => b.date - a.date); - return blogDatas; + return docsData; } module.exports = loadDocs; diff --git a/lib/load/index.js b/lib/load/index.js index cd56932b5a..c1729f2251 100644 --- a/lib/load/index.js +++ b/lib/load/index.js @@ -1,58 +1,51 @@ const fs = require('fs-extra'); const path = require('path'); const loadConfig = require('./config'); -const loadBlog = require('./blog'); const loadDocs = require('./docs'); -const {generate} = require('../helpers'); +const {generate} = require('./utils'); +const genRoutesConfig = require('./routes'); module.exports = async function load(siteDir) { - // load siteConfig + // siteConfig const siteConfig = loadConfig(siteDir); // docs - const docsRelativeDir = siteConfig.customDocsPath || 'docs'; - const docsMetadata = await loadDocs( - path.resolve(siteDir, '..', docsRelativeDir) + const docsDir = path.resolve( + siteDir, + '..', + siteConfig.customDocsPath || 'docs' ); + const docsData = await loadDocs(docsDir); await generate( - 'docsMetadata.js', - `${'/**\n * @generated\n */\n' + 'module.exports = '}${JSON.stringify( - docsMetadata, - null, - 2 - )};\n` - ); - - // blog - const blogMetadata = await loadBlog(path.resolve(siteDir, 'blog')); - await generate( - 'blogMetadata.js', - `${'/**\n * @generated\n */\n' + 'module.exports = '}${JSON.stringify( - blogMetadata, - null, - 2 - )};\n` + 'docsData.js', + `export const docsData = ${JSON.stringify(docsData, null, 2)}` ); // resolve outDir - const outDir = siteConfig.dest - ? path.resolve(siteConfig.dest) - : path.resolve(siteDir, '.munseo/dist'); + const outDir = path.resolve(siteDir, 'build'); - // resolve the path of our app user interface layout - const uiPath = - !siteConfig.uiPath || - !fs.existsSync(path.resolve(siteDir, siteConfig.uiPath)) - ? path.resolve(__dirname, '../ui') - : siteConfig.uiPath; + // resolve the theme + const themePath = + siteConfig.themePath && + fs.existsSync(path.resolve(siteDir, siteConfig.themePath)) + ? siteConfig.themePath + : path.resolve(__dirname, '../theme'); const baseUrl = siteConfig.baseUrl || '/'; - return { + const props = { siteConfig, siteDir, + docsDir, + docsData, outDir, - uiPath, + themePath, baseUrl }; + + // Generate React Router Config + const routesConfig = await genRoutesConfig(props); + await generate('routes.js', routesConfig); + + return props; }; diff --git a/lib/load/routes.js b/lib/load/routes.js new file mode 100644 index 0000000000..1482b66ba9 --- /dev/null +++ b/lib/load/routes.js @@ -0,0 +1,38 @@ +const path = require('path'); +const {fileToComponentName} = require('./utils'); + +async function genRoutesConfig({docsData, docsDir}) { + function genDocsRoute({path: docsPath, source}) { + const componentName = fileToComponentName(source); + return ` + { + path: ${JSON.stringify(docsPath)}, + component: () => <${componentName} /> + }`; + } + + function genDocsImport({source}) { + const filePath = path.resolve(docsDir, source); + const componentName = fileToComponentName(source); + return `import ${componentName} from ${JSON.stringify(filePath)}`; + } + + const notFoundRoute = `, + { + path: '*', + component: NotFound + }`; + + return ( + `import React from 'react';\n` + + `import Docs from '@theme/Docs';\n` + + `import NotFound from '@theme/NotFound';\n` + + `${docsData.map(genDocsImport).join('\n')}\n` + + `const routes = [${docsData + .map(genDocsRoute) + .join(',')}${notFoundRoute}\n];\n` + + `export default routes;\n` + ); +} + +module.exports = genRoutesConfig; diff --git a/lib/load/utils.js b/lib/load/utils.js new file mode 100644 index 0000000000..1163c886bf --- /dev/null +++ b/lib/load/utils.js @@ -0,0 +1,48 @@ +const path = require('path'); +const fs = require('fs-extra'); + +const genPath = path.resolve(__dirname, '../core/generated'); +fs.ensureDirSync(genPath); + +const genCache = new Map(); +async function generate(file, content) { + const cached = genCache.get(file); + if (cached !== content) { + await fs.writeFile(path.join(genPath, file), content); + genCache.set(file, content); + } +} + +const indexRE = /(^|.*\/)index\.md$/i; +const mdRE = /\.md$/; + +function fileToPath(file) { + if (indexRE.test(file)) { + return file.replace(indexRE, '/$1'); + } + return `/${file.replace(mdRE, '').replace(/\\/g, '/')}`; +} + +function encodePath(userpath) { + return userpath + .split('/') + .map(item => encodeURIComponent(item)) + .join('/'); +} + +function fileToComponentName(file) { + let str = file.replace(/([A-Z])/g, ' $1'); + if (str.length === 1) { + return str.toUpperCase(); + } + str = str.replace(/^[\W_]+|[\W_]+$/g, '').toLowerCase(); + str = str.charAt(0).toUpperCase() + str.slice(1); + return str.replace(/[\W_]+(\w|$)/g, (_, ch) => ch.toUpperCase()); +} + +module.exports = { + encodePath, + generate, + fileToPath, + fileToComponentName +};