mirror of https://github.com/IoTcat/docsify.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
4.6 KiB
181 lines
4.6 KiB
import * as tpl from '../../src/core/render/tpl' |
|
import fetch from 'node-fetch' |
|
import {AbstractHistory} from '../../src/core/router/history/abstract' |
|
import {Compiler} from '../../src/core/render/compiler' |
|
import {isAbsolutePath} from '../../src/core/router/util' |
|
import {readFileSync} from 'fs' |
|
import {resolve, basename} from 'path' |
|
import resolvePathname from 'resolve-pathname' |
|
import debug from 'debug' |
|
import {prerenderEmbed} from '../../src/core/render/embed' |
|
|
|
function cwd(...args) { |
|
return resolve(process.cwd(), ...args) |
|
} |
|
|
|
function mainTpl(config) { |
|
let html = `<nav class="app-nav${ |
|
config.repo ? '' : ' no-badge' |
|
}"><!--navbar--></nav>` |
|
|
|
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({template, config, cache}) { |
|
this.html = template |
|
this.config = config = Object.assign({}, config, { |
|
routerMode: 'history' |
|
}) |
|
this.cache = cache |
|
|
|
this.router = new AbstractHistory(config) |
|
this.compiler = new Compiler(config, this.router) |
|
|
|
this.router.getCurrentPath = () => this.url |
|
this._renderHtml( |
|
'inject-config', |
|
`<script>window.$docsify = ${JSON.stringify(config)}</script>` |
|
) |
|
this._renderHtml('inject-app', mainTpl(config)) |
|
|
|
this.template = this.html |
|
} |
|
|
|
_getPath(url) { |
|
const file = this.router.getFile(url) |
|
|
|
return isAbsolutePath(file) ? file : cwd(`./${file}`) |
|
} |
|
|
|
async renderToString(url) { |
|
this.url = url = this.router.parse(url).path |
|
const {loadSidebar, loadNavbar, coverpage} = this.config |
|
|
|
const mainFile = this._getPath(url) |
|
this._renderHtml('main', await this._render(mainFile, 'main')) |
|
|
|
if (loadSidebar) { |
|
const name = loadSidebar === true ? '_sidebar.md' : loadSidebar |
|
const sidebarFile = this._getPath(resolve(url, `./${name}`)) |
|
this._renderHtml('sidebar', await this._render(sidebarFile, 'sidebar')) |
|
} |
|
|
|
if (loadNavbar) { |
|
const name = loadNavbar === true ? '_navbar.md' : loadNavbar |
|
const navbarFile = this._getPath(resolve(url, `./${name}`)) |
|
this._renderHtml('navbar', await this._render(navbarFile, 'navbar')) |
|
} |
|
|
|
if (coverpage) { |
|
let path = null |
|
if (typeof coverpage === 'string') { |
|
if (url === '/') { |
|
path = coverpage |
|
} |
|
} else if (Array.isArray(coverpage)) { |
|
path = coverpage.indexOf(url) > -1 && '_coverpage.md' |
|
} else { |
|
const cover = coverpage[url] |
|
path = cover === true ? '_coverpage.md' : cover |
|
} |
|
|
|
const coverFile = this._getPath(resolve(url, `./${path}`)) |
|
|
|
this._renderHtml('cover', await this._render(coverFile), 'cover') |
|
} |
|
|
|
const html = this.html |
|
this.html = this.template |
|
|
|
return html |
|
} |
|
|
|
_renderHtml(match, content) { |
|
this.html = this.html.replace(new RegExp(`<!--${match}-->`, 'g'), content) |
|
|
|
return this.html |
|
} |
|
|
|
async _render(path, type) { |
|
let html = await this._loadFile(path) |
|
const {subMaxLevel, maxLevel} = this.config |
|
let tokens |
|
|
|
switch (type) { |
|
case 'sidebar': |
|
html = |
|
this.compiler.sidebar(html, maxLevel) + |
|
`<script>window.__SUB_SIDEBAR__ = ${JSON.stringify( |
|
this.compiler.subSidebar(subMaxLevel) |
|
)}</script>` |
|
break |
|
case 'cover': |
|
html = this.compiler.cover(html) |
|
break |
|
case 'main': |
|
tokens = await new Promise(r => { |
|
prerenderEmbed( |
|
{ |
|
fetch: url => this._loadFile(this._getPath(url)), |
|
compiler: this.compiler, |
|
raw: html |
|
}, |
|
r |
|
) |
|
}) |
|
html = this.compiler.compile(tokens) |
|
break |
|
case 'navbar': |
|
case 'article': |
|
default: |
|
html = this.compiler.compile(html) |
|
break |
|
} |
|
|
|
return html |
|
} |
|
|
|
async _loadFile(filePath) { |
|
debug('docsify')(`load > ${filePath}`) |
|
let content |
|
try { |
|
if (isAbsolutePath(filePath)) { |
|
const res = await fetch(filePath) |
|
if (!res.ok) { |
|
throw Error() |
|
} |
|
content = await res.text() |
|
this.lock = 0 |
|
} else { |
|
content = await readFileSync(filePath, 'utf8') |
|
this.lock = 0 |
|
} |
|
return content |
|
} catch (e) { |
|
this.lock = this.lock || 0 |
|
if (++this.lock > 10) { |
|
this.lock = 0 |
|
return |
|
} |
|
|
|
const fileName = basename(filePath) |
|
const result = await this._loadFile( |
|
resolvePathname(`../${fileName}`, filePath) |
|
) |
|
|
|
return result |
|
} |
|
} |
|
} |
|
|
|
Renderer.version = '__VERSION__'
|
|
|