From 7ecd4c9bef07e3ccf1fe2af461da91b8f58b055f Mon Sep 17 00:00:00 2001 From: endiliey Date: Mon, 30 Jul 2018 01:35:35 +0800 Subject: [PATCH] feat: prototype blog post generation in dev server --- blog/foo/bar.md | 6 ++++++ blog/foo/baz.md | 5 +++++ lib/core/blogPost.js | 13 +++++++++++++ lib/core/index.js | 32 ++++++++++++++++---------------- lib/dev.js | 34 +++++++++++++++++++++++++++++++--- lib/loader/index.js | 9 +++++++++ lib/webpack/base.js | 10 ---------- package.json | 4 ++++ yarn.lock | 30 ++++++++++++++++++++++++++++-- 9 files changed, 112 insertions(+), 31 deletions(-) create mode 100644 blog/foo/bar.md create mode 100644 blog/foo/baz.md create mode 100644 lib/core/blogPost.js diff --git a/blog/foo/bar.md b/blog/foo/bar.md new file mode 100644 index 0000000000..4b28a826e3 --- /dev/null +++ b/blog/foo/bar.md @@ -0,0 +1,6 @@ +--- +title: Lorem ipsum +date: 2018-06-20 +--- + +Lorem ipsumsdsdsad diff --git a/blog/foo/baz.md b/blog/foo/baz.md new file mode 100644 index 0000000000..c4e0cbf40d --- /dev/null +++ b/blog/foo/baz.md @@ -0,0 +1,5 @@ +--- +title: Baz +date: 2018-05-20 +--- +Life is so good diff --git a/lib/core/blogPost.js b/lib/core/blogPost.js new file mode 100644 index 0000000000..d3611ade2f --- /dev/null +++ b/lib/core/blogPost.js @@ -0,0 +1,13 @@ +const React = require('react'); +import blogDatas from '@generated/blogDatas'; + +// inner blog component for the article itself, without sidebar/header/footer +class BlogPost extends React.Component { + render() { + const {match} = this.props; + const post = blogDatas.find(blog => blog.path === match.path); + return
{post && post.content}
; + } +} + +module.exports = BlogPost; diff --git a/lib/core/index.js b/lib/core/index.js index dbe96b255c..2a4a5fc3ed 100644 --- a/lib/core/index.js +++ b/lib/core/index.js @@ -1,30 +1,30 @@ import React from 'react'; import {render} from 'react-dom'; -import {BrowserRouter, Route, Switch} from 'react-router-dom'; -import Hello from '@theme/hello'; -import Layout from '@theme/layout'; +import {BrowserRouter, Route, Switch, Link} from 'react-router-dom'; +import BlogPost from './blogPost'; +import blogDatas from '@generated/blogDatas'; class App extends React.Component { render() { - const routes = [ - { - path: '/', - component: Hello - }, - { - path: '/layout', - component: Layout - } - ]; - return (
- {routes.map(({path, component}) => ( - + {blogDatas.map(({path}) => ( + ))} +
+ {blogDatas.map(({path}) => { + return ( +
+ + {path} + +
+ ); + })} +
); diff --git a/lib/dev.js b/lib/dev.js index 7517b23d39..949048b135 100644 --- a/lib/dev.js +++ b/lib/dev.js @@ -3,11 +3,22 @@ const fs = require('fs-extra'); const chalk = require('chalk'); const webpack = require('webpack'); const chokidar = require('chokidar'); +const convert = require('koa-connect') +const range = require('koa-range') +const history = require('connect-history-api-fallback') +const portfinder = require('portfinder') const serve = require('webpack-serve'); const webpackNiceLog = require('webpack-nicelog'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); const load = require('./loader'); const createDevConfig = require('./webpack/dev'); +async function getPort (port) { + portfinder.basePort = parseInt(port) || 8080 + port = await portfinder.getPortPromise() + return port +} + module.exports = async function dev(sourceDir, cliOptions = {}) { // load site props from preprocessed files in source directory const props = await load(sourceDir); @@ -18,7 +29,7 @@ module.exports = async function dev(sourceDir, cliOptions = {}) { console.error(chalk.red(err.stack)); }); }; - const fsWatcher = chokidar.watch(['**/*.md'], { + const fsWatcher = chokidar.watch(['**/*.md', '.blogi/config.js'], { cwd: sourceDir, ignoreInitial: true }); @@ -31,7 +42,7 @@ module.exports = async function dev(sourceDir, cliOptions = {}) { // resolve webpack config let config = createDevConfig(props); - const port = cliOptions.port || 8080; + const port = await getPort(cliOptions.port); const {publicPath} = props; config.plugin('WebpackNiceLog').use(webpackNiceLog, [ @@ -46,6 +57,15 @@ module.exports = async function dev(sourceDir, cliOptions = {}) { } ]); + config.plugin('html-webpack-plugin').use(HtmlWebpackPlugin, [ + { + inject: false, + hash: true, + template: path.resolve(__dirname, 'core/index.html'), + filename: 'index.html' + } + ]); + // create compiler from generated webpack config config = config.toConfig(); const compiler = webpack(config); @@ -65,7 +85,15 @@ module.exports = async function dev(sourceDir, cliOptions = {}) { logLevel: 'error' }, logLevel: 'error', - port + port, + add: app => { + app.use(range) // enable range request https://tools.ietf.org/html/rfc7233 + app.use(convert(history({ + rewrites: [ + { from: /\.html$/, to: '/' } + ] + }))) + } } ); }; diff --git a/lib/loader/index.js b/lib/loader/index.js index a808bf23dc..274c06f206 100644 --- a/lib/loader/index.js +++ b/lib/loader/index.js @@ -10,6 +10,15 @@ module.exports = async function load(sourceDir) { // extract data from all blog files const blogDatas = await loadBlog(sourceDir); + fs.writeFile( + path.resolve(__dirname, '../generated/blogDatas.js'), + `${'/**\n' + + ' * @' + + 'generated\n' + + ' */\n' + + 'module.exports = '}${JSON.stringify(blogDatas, null, 2)};\n` + ); + // resolve outDir const outDir = siteConfig.dest ? path.resolve(siteConfig.dest) diff --git a/lib/webpack/base.js b/lib/webpack/base.js index 995f7f22de..9133a9ff7d 100644 --- a/lib/webpack/base.js +++ b/lib/webpack/base.js @@ -1,6 +1,5 @@ const Config = require('webpack-chain'); const path = require('path'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = function createBaseConfig(props) { const {outDir, themePath, sourceDir, publicPath} = props; @@ -43,14 +42,5 @@ module.exports = function createBaseConfig(props) { presets: ['env', 'react'] }); - config.plugin('html-webpack-plugin').use(HtmlWebpackPlugin, [ - { - inject: false, - hash: true, - template: path.resolve(__dirname, '../core/index.html'), - filename: 'index.html' - } - ]); - return config; }; diff --git a/package.json b/package.json index e7cf981cd8..933d0467ed 100644 --- a/package.json +++ b/package.json @@ -46,10 +46,14 @@ "chalk": "^2.4.1", "chokidar": "^2.0.4", "commander": "^2.16.0", + "connect-history-api-fallback": "^1.5.0", "front-matter": "^2.3.0", "fs-extra": "^7.0.0", "globby": "^8.0.1", "html-webpack-plugin": "^3.2.0", + "koa-connect": "^2.0.1", + "koa-range": "^0.3.0", + "portfinder": "^1.0.13", "react": "^16.4.1", "react-dom": "^16.4.1", "react-router-dom": "^4.3.1", diff --git a/yarn.lock b/yarn.lock index 3e081ced09..045cd34d26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -499,7 +499,7 @@ async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" -async@^1.4.0: +async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -1598,6 +1598,10 @@ configstore@^3.0.0: write-file-atomic "^2.0.0" xdg-basedir "^3.0.0" +connect-history-api-fallback@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#b06873934bc5e344fef611a196a6faae0aee015a" + console-browserify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" @@ -3989,6 +3993,10 @@ koa-compose@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" +koa-connect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/koa-connect/-/koa-connect-2.0.1.tgz#2acad159c33862de1d73aa4562a48de13f137c0f" + koa-convert@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" @@ -4000,6 +4008,12 @@ koa-is-json@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" +koa-range@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/koa-range/-/koa-range-0.3.0.tgz#3588e3496473a839a1bd264d2a42b1d85bd7feac" + dependencies: + stream-slice "^0.1.2" + koa-webpack@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/koa-webpack/-/koa-webpack-5.1.0.tgz#7b9f04ea85c43c4d7ad845d0de01f0ed495eb5c0" @@ -4435,7 +4449,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: +mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -5057,6 +5071,14 @@ pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" +portfinder@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9" + dependencies: + async "^1.5.2" + debug "^2.2.0" + mkdirp "0.5.x" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -5975,6 +5997,10 @@ stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" +stream-slice@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/stream-slice/-/stream-slice-0.1.2.tgz#2dc4f4e1b936fb13f3eb39a2def1932798d07a4b" + string-length@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"