diff --git a/.eslintignore b/.eslintignore index 2d2ecd68d..e3f07b4bf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ .git/ +packages/docsify-server-renderer/build.js \ No newline at end of file diff --git a/package.json b/package.json index 775600067..f4d4d0046 100644 --- a/package.json +++ b/package.json @@ -20,11 +20,11 @@ "themes" ], "scripts": { - "build": "rm -rf lib themes && node build/build.js && mkdir lib/themes && mkdir themes && node build/build-css.js", - "dev:build": "rm -rf lib themes && mkdir themes && node build/build.js --dev && node build/build-css.js --dev", - "dev": "node app.js & nodemon -w src -e js,css --exec 'npm run dev:build'", + "build": "rm -rf lib themes && node build/build && mkdir lib/themes && mkdir themes && node build/build-css", + "dev:build": "rm -rf lib themes && mkdir themes && node build/build --dev && node build/build-css --dev", + "dev": "node app & nodemon -w src -e js,css --exec 'npm run dev:build'", "build:ssr": "node build/build-ssr", - "test": "eslint src --fix" + "test": "eslint {src,packages} --fix" }, "dependencies": { "marked": "^0.3.6", diff --git a/packages/docsify-server-renderer/README.md b/packages/docsify-server-renderer/README.md index 0217ac072..5f1708d9c 100644 --- a/packages/docsify-server-renderer/README.md +++ b/packages/docsify-server-renderer/README.md @@ -41,8 +41,8 @@ renderer.renderToString({ url }) -
- + + diff --git a/packages/docsify-server-renderer/index.js b/packages/docsify-server-renderer/index.js index 7ada4c11c..acb39f2f3 100644 --- a/packages/docsify-server-renderer/index.js +++ b/packages/docsify-server-renderer/index.js @@ -1,7 +1,27 @@ import { Compiler } from '../../src/core/render/compiler' import { AbstractHistory } from '../../src/core/router/history/abstract' -import path from 'path' -import fs from 'fs' +import { resolve, basename } from 'path' +import { readFileSync } from 'fs' +import * as tpl from '../../src/core/render/tpl' + +function cwd (...args) { + return resolve(process.cwd(), ...args) +} + +function mainTpl (config) { + let html = `` + + if (config.repo) { + html += tpl.corner(config.repo) + } + if (config.coverpage) { + html += tpl.cover() + } + + html += tpl.main(config) + + return html +} export default class Renderer { constructor ({ @@ -10,16 +30,80 @@ export default class Renderer { config, cache }) { - this.template = template - this.path = path - this.config = config + this.html = this.template = template + this.path = cwd(path) + this.config = Object.assign(config, { + routerMode: 'history' + }) this.cache = cache - this.router = new AbstractHistory() + this.router = new AbstractHistory(config) this.compiler = new Compiler(config, this.router) + + this.router.getCurrentPath = () => this.url + this._renderHtml('inject-config', ``) + this._renderHtml('inject-app', mainTpl(config)) + } + + renderToString (url) { + this.url = url + // TODO render cover page + const { loadSidebar, loadNavbar } = this.config + + const mainFile = cwd(this.path, `./${this.router.getFile(url)}`) + this._renderHtml('main', this._render(mainFile)) + + if (loadSidebar) { + const name = loadSidebar === true ? '_sidebar.md' : loadSidebar + const sidebarFile = cwd(mainFile, '..', name) + this._renderHtml('sidebar', this._render(sidebarFile, 'sidebar')) + } + + if (loadNavbar) { + const name = loadNavbar === true ? '_navbar.md' : loadNavbar + const navbarFile = cwd(mainFile, '..', name) + this._renderHtml('navbar', this._render(navbarFile, 'navbar')) + } + + return this.html + } + + _renderHtml (match, content) { + this.html = this.html.replace(new RegExp(``, 'g'), content) } - renderToString(url) { - console.log(url) + _render (path, type) { + let html = this._loadFile(path) + + switch (type) { + case 'sidebar': + html = this.compiler.sidebar(html) + break + case 'cover': + html = this.compiler.cover(html) + break + case 'navbar': + case 'article': + default: + html = this.compiler.compile(html) + break + } + + return html + } + + _loadFile (filePath) { + try { + return readFileSync(filePath, 'utf8') + } catch (e) { + const fileName = basename(filePath) + const parentPath = cwd(filePath, '../..') + + if (this.path.length < parentPath.length) { + throw Error(`Not found file ${fileName}`) + } + + this._loadFile(cwd(filePath, '../..', fileName)) + } } } diff --git a/src/core/fetch/index.js b/src/core/fetch/index.js index 79839719b..1d937f5d8 100644 --- a/src/core/fetch/index.js +++ b/src/core/fetch/index.js @@ -70,5 +70,12 @@ export function fetchMixin (proto) { } export function initFetch (vm) { - vm.$fetch(_ => callHook(vm, 'ready')) + if (vm.rendered) { + vm._fetchCover() + vm.$resetEvents() + callHook(vm, 'doneEach') + callHook(vm, 'ready') + } else { + vm.$fetch(_ => callHook(vm, 'ready')) + } } diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js index b6000bef5..128c7d5f8 100644 --- a/src/core/render/compiler.js +++ b/src/core/render/compiler.js @@ -35,7 +35,7 @@ export class Compiler { if (!text) return text html = compile(text) - html = emojify(html) + html = config.noEmoji ? html : emojify(html) slugify.clear() return html diff --git a/src/core/render/emojify.js b/src/core/render/emojify.js index b8d41aaff..0167d12c1 100644 --- a/src/core/render/emojify.js +++ b/src/core/render/emojify.js @@ -1,10 +1,12 @@ +import { inBrowser } from '../util/env' + function replace (m, $1) { return '' + $1 + '' } export function emojify (text) { - return $docsify.noEmoji ? text : text + return text .replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g, m => m.replace(/:/g, '__colon__')) - .replace(/:(\w+?):/ig, window.emojify || replace) + .replace(/:(\w+?):/ig, (inBrowser && window.emojify) || replace) .replace(/__colon__/g, ':') } diff --git a/src/core/render/index.js b/src/core/render/index.js index 58bf32527..6f26eaa9c 100644 --- a/src/core/render/index.js +++ b/src/core/render/index.js @@ -172,20 +172,20 @@ export function initRender (vm) { let html = '' let navAppendToTarget = dom.body - if (!el) { - el = dom.create(id) - dom.appendTo(dom.body, el) - } - if (config.repo) { - html += tpl.corner(config.repo) - } - if (config.coverpage) { - html += tpl.cover() - } + if (el) { + if (config.repo) { + html += tpl.corner(config.repo) + } + if (config.coverpage) { + html += tpl.cover() + } - html += tpl.main(config) - // Render main app - vm._renderTo(el, html, true) + html += tpl.main(config) + // Render main app + vm._renderTo(el, html, true) + } else { + vm.rendered = true + } if (config.mergeNavbar && isMobile) { navAppendToTarget = dom.find('.sidebar') diff --git a/src/core/render/tpl.js b/src/core/render/tpl.js index e2f1ec759..cc24c7bed 100644 --- a/src/core/render/tpl.js +++ b/src/core/render/tpl.js @@ -33,12 +33,12 @@ export function main (config) { (config.name ? `

${config.name}

` : '') + - '' + + '' + '') return (isMobile ? `${aside}
` : `
${aside}`) + '
' + - '
' + + '
' + '
' + '
' } diff --git a/src/core/router/history/abstract.js b/src/core/router/history/abstract.js index d0d005921..09a2df935 100644 --- a/src/core/router/history/abstract.js +++ b/src/core/router/history/abstract.js @@ -1,8 +1,35 @@ import { History } from './base' +import { parseQuery, stringifyQuery, cleanPath } from '../util' +import { merge } from '../../util/core' export class AbstractHistory extends History { constructor (config) { super(config) this.mode = 'abstract' } + + parse (path) { + let query = '' + + const queryIndex = path.indexOf('?') + if (queryIndex >= 0) { + query = path.slice(queryIndex + 1) + path = path.slice(0, queryIndex) + } + + return { path, query: parseQuery(query) } + } + + toURL (path, params, currentRoute) { + const local = currentRoute && path[0] === '#' + const route = this.parse(path) + + route.query = merge({}, route.query, params) + path = route.path + stringifyQuery(route.query) + path = path.replace(/\.md(\?)|\.md$/, '$1') + + if (local) path = currentRoute + path + + return cleanPath('/' + path) + } } diff --git a/src/core/router/util.js b/src/core/router/util.js index 38275f82b..ecdb54629 100644 --- a/src/core/router/util.js +++ b/src/core/router/util.js @@ -1,4 +1,5 @@ import { cached } from '../util/core' +import { inBrowser } from '../util/env' const decode = decodeURIComponent const encode = encodeURIComponent @@ -31,10 +32,13 @@ export function stringifyQuery (obj) { return qs.length ? `?${qs.join('&')}` : '' } -export const getBasePath = cached(base => { +export const getBasePath = cached((base = '') => { + // TODO + const path = inBrowser ? window.location.pathname : '' + return /^(\/|https?:)/g.test(base) ? base - : cleanPath(window.location.pathname + '/' + base) + : cleanPath(path + '/' + base) }) export function getPath (...args) { diff --git a/src/core/util/env.js b/src/core/util/env.js index dcbcce27c..dbb8b3b85 100644 --- a/src/core/util/env.js +++ b/src/core/util/env.js @@ -1,13 +1,11 @@ -export const UA = window.navigator.userAgent.toLowerCase() +export const inBrowser = typeof window !== 'undefined' -export const isIE = UA && /msie|trident/.test(UA) - -export const isMobile = document.body.clientWidth <= 600 +export const isMobile = inBrowser && document.body.clientWidth <= 600 /** * @see https://github.com/MoOx/pjax/blob/master/lib/is-supported.js */ -export const supportsPushState = (function () { +export const supportsPushState = inBrowser && (function () { // Borrowed wholesale from https://github.com/defunkt/jquery-pjax return window.history && window.history.pushState &&