Compare commits

...
6 Commits
Author SHA1 Message Date
qingwei.li 8dd4378e89 chore: add changelog 4.6.1 2018-02-12 16:28:38 +08:00
qingwei.li 149d9929f8 [build] 4.6.1 2018-02-12 16:28:37 +08:00
qingwei.li 50fe22563b chore: fix lint 2018-02-12 16:28:10 +08:00
qingwei.li 4aafde85fb chore: remove console.log 2018-02-12 16:25:28 +08:00
qingwei.li dc0c3ced4e fix(embed): compatible ssr 2018-02-12 16:25:28 +08:00
qingwei.li 62ce447fc3 refactor(embed): async fetch embed files, fixed #387 2018-02-12 16:25:28 +08:00
14 changed files with 412 additions and 164 deletions
+10
View File
@@ -1,3 +1,13 @@
<a name="4.6.1"></a>
## [4.6.1](https://github.com/QingWei-Li/docsify/compare/v4.6.0...v4.6.1) (2018-02-12)
### Bug Fixes
* **embed:** compatible ssr ([dc0c3ce](https://github.com/QingWei-Li/docsify/commit/dc0c3ce))
<a name="4.6.0"></a>
# [4.6.0](https://github.com/QingWei-Li/docsify/compare/v4.5.9...v4.6.0) (2018-02-11)
+1 -1
View File
@@ -1,6 +1,6 @@
![logo](_media/icon.svg)
# docsify <small>4.6.0</small>
# docsify <small>4.6.1</small>
> A magical documentation site generator.
+196 -71
View File
@@ -5,8 +5,9 @@
function cached (fn) {
var cache = Object.create(null);
return function cachedFn (str) {
var hit = cache[str];
return hit || (cache[str] = fn(str))
var key = isPrimitive(str) ? str : JSON.stringify(str);
var hit = cache[key];
return hit || (cache[key] = fn(str))
}
}
@@ -2876,7 +2877,6 @@ var replaceSlug = cached(function (path) {
});
var cachedLinks = {};
var uid = 0;
function getAndRemoveConfig (str) {
if ( str === void 0 ) str = '';
@@ -2894,46 +2894,37 @@ function getAndRemoveConfig (str) {
return { str: str, config: config }
}
var compileMedia = {
markdown: function markdown (url) {
var this$1 = this;
var id = "docsify-get-" + (uid++);
{
get(url, false).then(function (text) {
document.getElementById(id).innerHTML = this$1.compile(text);
});
return ("<div data-origin=\"" + url + "\" id=" + id + "></div>")
return {
url: url
}
},
iframe: function iframe (url, title) {
return ("<iframe src=\"" + url + "\" " + (title || 'width=100% height=400') + "></iframe>")
return {
code: ("<iframe src=\"" + url + "\" " + (title || 'width=100% height=400') + "></iframe>")
}
},
video: function video (url, title) {
return ("<video src=\"" + url + "\" " + (title || 'controls') + ">Not Support</video>")
return {
code: ("<video src=\"" + url + "\" " + (title || 'controls') + ">Not Support</video>")
}
},
audio: function audio (url, title) {
return ("<audio src=\"" + url + "\" " + (title || 'controls') + ">Not Support</audio>")
return {
code: ("<audio src=\"" + url + "\" " + (title || 'controls') + ">Not Support</audio>")
}
},
code: function code (url, title) {
var this$1 = this;
var lang = url.match(/\.(\w+)$/);
var id = "docsify-get-" + (uid++);
var ext = url.match(/\.(\w+)$/);
lang = title || (lang && lang[1]);
if (lang === 'md') { lang = 'markdown'; }
ext = title || (ext && ext[1]);
if (ext === 'md') { ext = 'markdown'; }
{
get(url, false).then(function (text) {
document.getElementById(id).innerHTML = this$1.compile(
'```' + ext + '\n' + text.replace(/`/g, '@qm@') + '\n```\n'
).replace(/@qm@/g, '`');
});
return ("<div data-origin=\"" + url + "\" id=" + id + "></div>")
return {
url: url,
lang: lang
}
}
};
@@ -2961,12 +2952,18 @@ var Compiler = function Compiler (config, router) {
compile = marked;
}
this._marked = compile;
this.compile = cached(function (text) {
var html = '';
if (!text) { return text }
html = compile(text);
if (isPrimitive(text)) {
html = compile(text);
} else {
html = compile.parser(text);
}
html = config.noEmoji ? html : emojify(html);
slugify.clear();
@@ -2974,7 +2971,42 @@ var Compiler = function Compiler (config, router) {
});
};
Compiler.prototype.matchNotCompileLink = function matchNotCompileLink (link) {
Compiler.prototype.compileEmbed = function compileEmbed (href, title) {
var ref = getAndRemoveConfig(title);
var str = ref.str;
var config = ref.config;
var embed;
title = str;
if (config.include) {
if (!isAbsolutePath(href)) {
href = getPath(this.contentBase, href);
}
var media;
if (config.type && (media = compileMedia[config.type])) {
embed = media.call(this, href, title);
embed.type = config.type;
} else {
var type = 'code';
if (/\.(md|markdown)/.test(href)) {
type = 'markdown';
} else if (/\.html?/.test(href)) {
type = 'iframe';
} else if (/\.(mp4|ogg)/.test(href)) {
type = 'video';
} else if (/\.mp3/.test(href)) {
type = 'audio';
}
embed = compileMedia[type].call(this, href, title);
embed.type = type;
}
return embed
}
};
Compiler.prototype._matchNotCompileLink = function _matchNotCompileLink (link) {
var links = this.config.noCompileLinks || [];
for (var i = 0; i < links.length; i++) {
@@ -3026,6 +3058,7 @@ Compiler.prototype._initRenderer = function _initRenderer () {
origin.code = renderer.code = function (code, lang) {
if ( lang === void 0 ) lang = '';
code = code.replace(/@DOCSIFY_QM@/g, '`');
var hl = prism.highlight(
code,
prism.languages[lang] || prism.languages.markup
@@ -3043,33 +3076,9 @@ Compiler.prototype._initRenderer = function _initRenderer () {
var config = ref.config;
title = str;
if (config.include) {
if (!isAbsolutePath(href)) {
href = getPath(contentBase, href);
}
var media;
if (config.type && (media = compileMedia[config.type])) {
return media.call(_self, href, title)
}
var type = 'code';
if (/\.(md|markdown)/.test(href)) {
type = 'markdown';
} else if (/\.html?/.test(href)) {
type = 'iframe';
} else if (/\.(mp4|ogg)/.test(href)) {
type = 'video';
} else if (/\.mp3/.test(href)) {
type = 'audio';
}
return compileMedia[type].call(_self, href, title)
}
if (
!/:|(\/{2})/.test(href) &&
!_self.matchNotCompileLink(href) &&
!_self._matchNotCompileLink(href) &&
!config.ignore
) {
if (href === _self.config.homepage) { href = 'README'; }
@@ -3508,6 +3517,105 @@ function scroll2Top (offset) {
scrollEl.scrollTop = offset === true ? 0 : Number(offset);
}
var cached$1 = {};
function walkFetchEmbed (ref, cb) {
var step = ref.step; if ( step === void 0 ) step = 0;
var embedTokens = ref.embedTokens;
var compile = ref.compile;
var fetch = ref.fetch;
var token = embedTokens[step];
if (!token) {
return cb({})
}
var next = function (text) {
var embedToken;
if (text) {
if (token.embed.type === 'markdown') {
embedToken = compile.lexer(text);
} else if (token.embed.type === 'code') {
embedToken = compile.lexer(
'```' +
token.embed.lang +
'\n' +
text.replace(/`/g, '@DOCSIFY_QM@') +
'\n```\n'
);
}
}
cb({ token: token, embedToken: embedToken });
walkFetchEmbed({ step: ++step, compile: compile, embedTokens: embedTokens, fetch: fetch }, cb);
};
{
get(token.embed.url).then(next);
}
}
function prerenderEmbed (ref, done) {
var compiler = ref.compiler;
var raw = ref.raw;
var fetch = ref.fetch;
var hit;
if ((hit = cached$1[raw])) {
return done(hit)
}
var compile = compiler._marked;
var tokens = compile.lexer(raw);
var embedTokens = [];
var linkRE = compile.InlineLexer.rules.link;
var links = tokens.links;
tokens.forEach(function (token, index) {
if (token.type === 'paragraph') {
token.text = token.text.replace(
new RegExp(linkRE, 'g'),
function (src, filename, href, title) {
var embed = compiler.compileEmbed(href, title);
if (embed) {
if (embed.type === 'markdown' || embed.type === 'code') {
embedTokens.push({
index: index,
embed: embed
});
}
return embed.code
}
return src
}
);
}
});
var moveIndex = 0;
walkFetchEmbed({ compile: compile, embedTokens: embedTokens, fetch: fetch }, function (ref) {
var embedToken = ref.embedToken;
var token = ref.token;
if (token) {
var index = token.index + moveIndex;
merge(links, embedToken.links);
tokens = tokens
.slice(0, index)
.concat(embedToken, tokens.slice(index + 1));
moveIndex += embedToken.length - 1;
} else {
cached$1[raw] = tokens.concat();
tokens.links = cached$1[raw].links = links;
done(tokens);
}
});
}
function executeScript () {
var script = findAll('.markdown-section>script')
.filter(function (s) { return !/template/.test(s.type); })[0];
@@ -3621,7 +3729,7 @@ function renderMixin (proto) {
getAndActive(this.router, 'nav');
};
proto._renderMain = function (text, opt) {
proto._renderMain = function (text, opt, next) {
var this$1 = this;
if ( opt === void 0 ) opt = {};
@@ -3630,12 +3738,31 @@ function renderMixin (proto) {
}
callHook(this, 'beforeEach', text, function (result) {
var html = this$1.isHTML ? result : this$1.compiler.compile(result);
if (opt.updatedAt) {
html = formatUpdated(html, opt.updatedAt, this$1.config.formatUpdated);
}
var html;
var callback = function () {
if (opt.updatedAt) {
html = formatUpdated(html, opt.updatedAt, this$1.config.formatUpdated);
}
callHook(this$1, 'afterEach', html, function (text) { return renderMain.call(this$1, text); });
callHook(this$1, 'afterEach', html, function (text) { return renderMain.call(this$1, text); });
};
if (this$1.isHTML) {
html = this$1.result;
callback();
next();
} else {
prerenderEmbed(
{
compiler: this$1.compiler,
raw: text
},
function (tokens) {
html = this$1.compiler.compile(tokens);
callback();
next();
}
);
}
});
};
@@ -3736,16 +3863,16 @@ function initRender (vm) {
toggleClass(body, 'ready');
}
var cached$1 = {};
var cached$2 = {};
function getAlias (path, alias, last) {
var match = Object.keys(alias).filter(function (key) {
var re = cached$1[key] || (cached$1[key] = new RegExp(("^" + key + "$")));
var re = cached$2[key] || (cached$2[key] = new RegExp(("^" + key + "$")));
return re.test(path) && path !== last
})[0];
return match
? getAlias(path.replace(cached$1[match], alias[match]), alias, path)
? getAlias(path.replace(cached$2[match], alias[match]), alias, path)
: path
}
@@ -4074,12 +4201,10 @@ function fetchMixin (proto) {
// Load main content
last.then(
function (text, opt) {
this$1._renderMain(text, opt);
loadSideAndNav();
this$1._renderMain(text, opt, loadSideAndNav);
},
function (_) {
this$1._renderMain(null);
loadSideAndNav();
this$1._renderMain(null, {}, loadSideAndNav);
}
);
@@ -4254,7 +4379,7 @@ initGlobalAPI();
/**
* Version
*/
Docsify.version = '4.6.0';
Docsify.version = '4.6.1';
/**
* Run Docsify
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "docsify",
"version": "4.6.0",
"version": "4.6.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "docsify",
"version": "4.6.0",
"version": "4.6.1",
"description": "A magical documentation generator.",
"author": {
"name": "qingwei-li",
+15 -1
View File
@@ -7,6 +7,7 @@ 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)
@@ -61,7 +62,7 @@ export default class Renderer {
const { loadSidebar, loadNavbar, coverpage } = this.config
const mainFile = this._getPath(url)
this._renderHtml('main', await this._render(mainFile))
this._renderHtml('main', await this._render(mainFile, 'main'))
if (loadSidebar) {
const name = loadSidebar === true ? '_sidebar.md' : loadSidebar
@@ -120,6 +121,19 @@ export default class Renderer {
case 'cover':
html = this.compiler.cover(html)
break
case 'main':
const 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:
+1 -1
View File
@@ -37,5 +37,5 @@
"integrity": "sha1-6DWIAbhrg7F1YNTjw4LXrvIQCUQ="
}
},
"version": "4.6.0"
"version": "4.6.1"
}
@@ -1,6 +1,6 @@
{
"name": "docsify-server-renderer",
"version": "4.6.0",
"version": "4.6.1",
"description": "docsify server renderer",
"author": {
"name": "qingwei-li",
+2 -4
View File
@@ -47,12 +47,10 @@ export function fetchMixin (proto) {
// Load main content
last.then(
(text, opt) => {
this._renderMain(text, opt)
loadSideAndNav()
this._renderMain(text, opt, loadSideAndNav)
},
_ => {
this._renderMain(null)
loadSideAndNav()
this._renderMain(null, {}, loadSideAndNav)
}
)
+63 -74
View File
@@ -5,13 +5,11 @@ import { genTree } from './gen-tree'
import { slugify } from './slugify'
import { emojify } from './emojify'
import { isAbsolutePath, getPath } from '../router/util'
import { isFn, merge, cached } from '../util/core'
import { get } from '../fetch/ajax'
import { isFn, merge, cached, isPrimitive } from '../util/core'
const cachedLinks = {}
let uid = 0
function getAndRemoveConfig (str = '') {
export function getAndRemoveConfig (str = '') {
const config = {}
if (str) {
@@ -25,62 +23,37 @@ function getAndRemoveConfig (str = '') {
return { str, config }
}
const compileMedia = {
markdown (url) {
const id = `docsify-get-${uid++}`
if (!process.env.SSR) {
get(url, false).then(text => {
document.getElementById(id).innerHTML = this.compile(text)
})
return `<div data-origin="${url}" id=${id}></div>`
} else {
return `<div data-origin="${url}" id=${uid}></div>
<script>
var compile = window.__current_docsify_compiler__
Docsify.get('${url}', false).then(function(text) {
document.getElementById('${uid}').innerHTML = compile(text)
})
</script>`
return {
url
}
},
iframe (url, title) {
return `<iframe src="${url}" ${title || 'width=100% height=400'}></iframe>`
return {
code: `<iframe src="${url}" ${title || 'width=100% height=400'}></iframe>`
}
},
video (url, title) {
return `<video src="${url}" ${title || 'controls'}>Not Support</video>`
return {
code: `<video src="${url}" ${title || 'controls'}>Not Support</video>`
}
},
audio (url, title) {
return `<audio src="${url}" ${title || 'controls'}>Not Support</audio>`
return {
code: `<audio src="${url}" ${title || 'controls'}>Not Support</audio>`
}
},
code (url, title) {
const id = `docsify-get-${uid++}`
let ext = url.match(/\.(\w+)$/)
let lang = url.match(/\.(\w+)$/)
ext = title || (ext && ext[1])
if (ext === 'md') ext = 'markdown'
lang = title || (lang && lang[1])
if (lang === 'md') lang = 'markdown'
if (!process.env.SSR) {
get(url, false).then(text => {
document.getElementById(id).innerHTML = this.compile(
'```' + ext + '\n' + text.replace(/`/g, '@qm@') + '\n```\n'
).replace(/@qm@/g, '`')
})
return `<div data-origin="${url}" id=${id}></div>`
} else {
return `<div data-origin="${url}" id=${id}></div>
<script>
setTimeout(() => {
var compiler = window.__current_docsify_compiler__
Docsify.get('${url}', false).then(function(text) {
document.getElementById('${id}').innerHTML = compiler
.compile('\`\`\`${ext}\\n' + text.replace(/\`/g, '@qm@') + '\\n\`\`\`\\n')
.replace(/@qm@/g, '\`')
})
})
</script>`
return {
url,
lang
}
}
}
@@ -109,12 +82,18 @@ export class Compiler {
compile = marked
}
this._marked = compile
this.compile = cached(text => {
let html = ''
if (!text) return text
html = compile(text)
if (isPrimitive(text)) {
html = compile(text)
} else {
html = compile.parser(text)
}
html = config.noEmoji ? html : emojify(html)
slugify.clear()
@@ -122,7 +101,40 @@ export class Compiler {
})
}
matchNotCompileLink (link) {
compileEmbed (href, title) {
const { str, config } = getAndRemoveConfig(title)
let embed
title = str
if (config.include) {
if (!isAbsolutePath(href)) {
href = getPath(process.env.SSR ? '' : this.contentBase, href)
}
let media
if (config.type && (media = compileMedia[config.type])) {
embed = media.call(this, href, title)
embed.type = config.type
} else {
let type = 'code'
if (/\.(md|markdown)/.test(href)) {
type = 'markdown'
} else if (/\.html?/.test(href)) {
type = 'iframe'
} else if (/\.(mp4|ogg)/.test(href)) {
type = 'video'
} else if (/\.mp3/.test(href)) {
type = 'audio'
}
embed = compileMedia[type].call(this, href, title)
embed.type = type
}
return embed
}
}
_matchNotCompileLink (link) {
const links = this.config.noCompileLinks || []
for (var i = 0; i < links.length; i++) {
@@ -169,6 +181,7 @@ export class Compiler {
}
// highlight code
origin.code = renderer.code = function (code, lang = '') {
code = code.replace(/@DOCSIFY_QM@/g, '`')
const hl = Prism.highlight(
code,
Prism.languages[lang] || Prism.languages.markup
@@ -182,33 +195,9 @@ export class Compiler {
const { str, config } = getAndRemoveConfig(title)
title = str
if (config.include) {
if (!isAbsolutePath(href)) {
href = getPath(contentBase, href)
}
let media
if (config.type && (media = compileMedia[config.type])) {
return media.call(_self, href, title)
}
let type = 'code'
if (/\.(md|markdown)/.test(href)) {
type = 'markdown'
} else if (/\.html?/.test(href)) {
type = 'iframe'
} else if (/\.(mp4|ogg)/.test(href)) {
type = 'video'
} else if (/\.mp3/.test(href)) {
type = 'audio'
}
return compileMedia[type].call(_self, href, title)
}
if (
!/:|(\/{2})/.test(href) &&
!_self.matchNotCompileLink(href) &&
!_self._matchNotCompileLink(href) &&
!config.ignore
) {
if (href === _self.config.homepage) href = 'README'
+91
View File
@@ -0,0 +1,91 @@
import { get } from '../fetch/ajax'
import { merge } from '../util/core'
const cached = {}
function walkFetchEmbed ({ step = 0, embedTokens, compile, fetch }, cb) {
const token = embedTokens[step]
if (!token) {
return cb({})
}
const next = text => {
let embedToken
if (text) {
if (token.embed.type === 'markdown') {
embedToken = compile.lexer(text)
} else if (token.embed.type === 'code') {
embedToken = compile.lexer(
'```' +
token.embed.lang +
'\n' +
text.replace(/`/g, '@DOCSIFY_QM@') +
'\n```\n'
)
}
}
cb({ token, embedToken })
walkFetchEmbed({ step: ++step, compile, embedTokens, fetch }, cb)
}
if (process.env.SSR) {
fetch(token.embed.url).then(next)
} else {
get(token.embed.url).then(next)
}
}
export function prerenderEmbed ({ compiler, raw, fetch }, done) {
let hit
if ((hit = cached[raw])) {
return done(hit)
}
const compile = compiler._marked
let tokens = compile.lexer(raw)
const embedTokens = []
const linkRE = compile.InlineLexer.rules.link
const links = tokens.links
tokens.forEach((token, index) => {
if (token.type === 'paragraph') {
token.text = token.text.replace(
new RegExp(linkRE, 'g'),
(src, filename, href, title) => {
const embed = compiler.compileEmbed(href, title)
if (embed) {
if (embed.type === 'markdown' || embed.type === 'code') {
embedTokens.push({
index,
embed
})
}
return embed.code
}
return src
}
)
}
})
let moveIndex = 0
walkFetchEmbed({ compile, embedTokens, fetch }, ({ embedToken, token }) => {
if (token) {
const index = token.index + moveIndex
merge(links, embedToken.links)
tokens = tokens
.slice(0, index)
.concat(embedToken, tokens.slice(index + 1))
moveIndex += embedToken.length - 1
} else {
cached[raw] = tokens.concat()
tokens.links = cached[raw].links = links
done(tokens)
}
})
}
+26 -6
View File
@@ -9,6 +9,7 @@ import { getPath, isAbsolutePath } from '../router/util'
import { isMobile, inBrowser } from '../util/env'
import { isPrimitive } from '../util/core'
import { scrollActiveSidebar, scroll2Top } from '../event/scroll'
import { prerenderEmbed } from './embed'
function executeScript () {
const script = dom
@@ -119,18 +120,37 @@ export function renderMixin (proto) {
getAndActive(this.router, 'nav')
}
proto._renderMain = function (text, opt = {}) {
proto._renderMain = function (text, opt = {}, next) {
if (!text) {
return renderMain.call(this, text)
}
callHook(this, 'beforeEach', text, result => {
let html = this.isHTML ? result : this.compiler.compile(result)
if (opt.updatedAt) {
html = formatUpdated(html, opt.updatedAt, this.config.formatUpdated)
}
let html
const callback = () => {
if (opt.updatedAt) {
html = formatUpdated(html, opt.updatedAt, this.config.formatUpdated)
}
callHook(this, 'afterEach', html, text => renderMain.call(this, text))
callHook(this, 'afterEach', html, text => renderMain.call(this, text))
}
if (this.isHTML) {
html = this.result
callback()
next()
} else {
prerenderEmbed(
{
compiler: this.compiler,
raw: text
},
tokens => {
html = this.compiler.compile(tokens)
callback()
next()
}
)
}
})
}
+3 -2
View File
@@ -4,8 +4,9 @@
export function cached (fn) {
const cache = Object.create(null)
return function cachedFn (str) {
const hit = cache[str]
return hit || (cache[str] = fn(str))
const key = isPrimitive(str) ? str : JSON.stringify(str)
const hit = cache[key]
return hit || (cache[key] = fn(str))
}
}