2017-05-30 03:28:43 +08:00
|
|
|
import * as tpl from '../../src/core/render/tpl'
|
|
|
|
import fetch from 'node-fetch'
|
2017-05-29 22:24:38 +08:00
|
|
|
import { AbstractHistory } from '../../src/core/router/history/abstract'
|
2017-05-30 03:28:43 +08:00
|
|
|
import { Compiler } from '../../src/core/render/compiler'
|
|
|
|
import { isAbsolutePath } from '../../src/core/router/util'
|
2017-05-30 00:23:43 +08:00
|
|
|
import { readFileSync } from 'fs'
|
2017-05-30 03:28:43 +08:00
|
|
|
import { resolve, basename } from 'path'
|
2017-05-30 15:27:04 +08:00
|
|
|
import resolvePathname from 'resolve-pathname'
|
2017-05-30 16:16:36 +08:00
|
|
|
import debug from 'debug'
|
2017-05-30 00:23:43 +08:00
|
|
|
|
|
|
|
function cwd (...args) {
|
|
|
|
return resolve(process.cwd(), ...args)
|
|
|
|
}
|
|
|
|
|
|
|
|
function mainTpl (config) {
|
2017-05-30 06:32:41 +08:00
|
|
|
let html = `<nav class="app-nav${config.repo ? '' : ' no-badge'}"><!--navbar--></nav>`
|
2017-05-30 00:23:43 +08:00
|
|
|
|
|
|
|
if (config.repo) {
|
|
|
|
html += tpl.corner(config.repo)
|
|
|
|
}
|
|
|
|
if (config.coverpage) {
|
|
|
|
html += tpl.cover()
|
|
|
|
}
|
|
|
|
|
|
|
|
html += tpl.main(config)
|
|
|
|
|
|
|
|
return html
|
|
|
|
}
|
2017-05-29 22:24:38 +08:00
|
|
|
|
|
|
|
export default class Renderer {
|
|
|
|
constructor ({
|
|
|
|
template,
|
|
|
|
config,
|
|
|
|
cache
|
|
|
|
}) {
|
2017-05-30 01:25:52 +08:00
|
|
|
this.html = template
|
2017-05-30 03:28:43 +08:00
|
|
|
this.config = config = Object.assign({}, config, {
|
2017-05-30 00:23:43 +08:00
|
|
|
routerMode: 'history'
|
|
|
|
})
|
2017-05-29 22:24:38 +08:00
|
|
|
this.cache = cache
|
|
|
|
|
2017-05-30 00:23:43 +08:00
|
|
|
this.router = new AbstractHistory(config)
|
2017-05-29 22:24:38 +08:00
|
|
|
this.compiler = new Compiler(config, this.router)
|
2017-05-30 00:23:43 +08:00
|
|
|
|
|
|
|
this.router.getCurrentPath = () => this.url
|
|
|
|
this._renderHtml('inject-config', `<script>window.$docsify = ${JSON.stringify(config)}</script>`)
|
|
|
|
this._renderHtml('inject-app', mainTpl(config))
|
2017-05-30 01:25:52 +08:00
|
|
|
|
|
|
|
this.template = this.html
|
2017-05-30 00:23:43 +08:00
|
|
|
}
|
|
|
|
|
2017-05-30 03:48:29 +08:00
|
|
|
_getPath (url) {
|
2017-05-30 03:28:43 +08:00
|
|
|
const file = this.router.getFile(url)
|
|
|
|
|
|
|
|
return isAbsolutePath(file)
|
|
|
|
? file
|
2017-05-30 06:32:41 +08:00
|
|
|
: cwd(`./${file}`)
|
2017-05-30 03:28:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
async renderToString (url) {
|
2017-05-30 01:25:52 +08:00
|
|
|
this.url = url = this.router.parse(url).path
|
2017-05-30 00:23:43 +08:00
|
|
|
const { loadSidebar, loadNavbar } = this.config
|
|
|
|
|
2017-05-30 03:28:43 +08:00
|
|
|
const mainFile = this._getPath(url)
|
|
|
|
this._renderHtml('main', await this._render(mainFile))
|
2017-05-30 00:23:43 +08:00
|
|
|
|
|
|
|
if (loadSidebar) {
|
|
|
|
const name = loadSidebar === true ? '_sidebar.md' : loadSidebar
|
2017-05-30 11:54:37 +08:00
|
|
|
const sidebarFile = this._getPath(resolve(url, `./${name}`))
|
2017-05-30 03:28:43 +08:00
|
|
|
this._renderHtml('sidebar', await this._render(sidebarFile, 'sidebar'))
|
2017-05-30 00:23:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (loadNavbar) {
|
|
|
|
const name = loadNavbar === true ? '_navbar.md' : loadNavbar
|
2017-05-30 11:54:37 +08:00
|
|
|
const navbarFile = this._getPath(resolve(url, `./${name}`))
|
2017-05-30 03:28:43 +08:00
|
|
|
this._renderHtml('navbar', await this._render(navbarFile, 'navbar'))
|
2017-05-30 00:23:43 +08:00
|
|
|
}
|
|
|
|
|
2017-05-30 01:25:52 +08:00
|
|
|
const html = this.html
|
|
|
|
this.html = this.template
|
|
|
|
|
|
|
|
return html
|
2017-05-30 00:23:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
_renderHtml (match, content) {
|
2017-05-30 01:32:04 +08:00
|
|
|
this.html = this.html.replace(new RegExp(`<!--${match}-->`, 'g'), content)
|
|
|
|
|
|
|
|
return this.html
|
2017-05-29 22:24:38 +08:00
|
|
|
}
|
|
|
|
|
2017-05-30 03:28:43 +08:00
|
|
|
async _render (path, type) {
|
|
|
|
let html = await this._loadFile(path)
|
2017-05-30 01:25:52 +08:00
|
|
|
const { subMaxLevel, maxLevel } = this.config
|
2017-05-30 00:23:43 +08:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case 'sidebar':
|
2017-05-30 01:32:04 +08:00
|
|
|
html = this.compiler.sidebar(html, maxLevel) +
|
|
|
|
`<script>window.__SUB_SIDEBAR__ = ${JSON.stringify(
|
2017-05-30 01:25:52 +08:00
|
|
|
this.compiler.subSidebar(html, subMaxLevel)
|
|
|
|
)}</script>`
|
2017-05-30 00:23:43 +08:00
|
|
|
break
|
|
|
|
case 'cover':
|
|
|
|
html = this.compiler.cover(html)
|
|
|
|
break
|
|
|
|
case 'navbar':
|
|
|
|
case 'article':
|
|
|
|
default:
|
|
|
|
html = this.compiler.compile(html)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
return html
|
|
|
|
}
|
|
|
|
|
2017-05-30 03:28:43 +08:00
|
|
|
async _loadFile (filePath) {
|
2017-05-30 16:16:36 +08:00
|
|
|
debug('docsify')(`load > ${filePath}`)
|
2017-05-30 06:32:41 +08:00
|
|
|
let content
|
2017-05-30 00:23:43 +08:00
|
|
|
try {
|
2017-05-30 06:32:41 +08:00
|
|
|
if (isAbsolutePath(filePath)) {
|
|
|
|
const res = await fetch(filePath)
|
2017-05-30 16:16:36 +08:00
|
|
|
if (!res.ok) throw Error()
|
2017-05-30 06:32:41 +08:00
|
|
|
content = await res.text()
|
|
|
|
this.lock = 0
|
|
|
|
} else {
|
|
|
|
content = await readFileSync(filePath, 'utf8')
|
|
|
|
this.lock = 0
|
|
|
|
}
|
|
|
|
return content
|
|
|
|
} catch (e) {
|
2017-05-30 04:27:49 +08:00
|
|
|
this.lock = this.lock || 0
|
|
|
|
if (++this.lock > 10) {
|
|
|
|
this.lock = 0
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-05-30 00:23:43 +08:00
|
|
|
const fileName = basename(filePath)
|
|
|
|
|
2017-05-30 15:27:04 +08:00
|
|
|
return await this._loadFile(resolvePathname(`../${fileName}`, filePath))
|
2017-05-30 00:23:43 +08:00
|
|
|
}
|
2017-05-29 22:24:38 +08:00
|
|
|
}
|
|
|
|
}
|