Skip to content

Commit 62ce447

Browse files
committed
refactor(embed): async fetch embed files, fixed #387
1 parent 471e407 commit 62ce447

File tree

5 files changed

+177
-85
lines changed

5 files changed

+177
-85
lines changed

Diff for: src/core/fetch/index.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,10 @@ export function fetchMixin (proto) {
4747
// Load main content
4848
last.then(
4949
(text, opt) => {
50-
this._renderMain(text, opt)
51-
loadSideAndNav()
50+
this._renderMain(text, opt, loadSideAndNav)
5251
},
5352
_ => {
54-
this._renderMain(null)
55-
loadSideAndNav()
53+
this._renderMain(null, {}, loadSideAndNav)
5654
}
5755
)
5856

Diff for: src/core/render/compiler.js

+62-73
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@ import { genTree } from './gen-tree'
55
import { slugify } from './slugify'
66
import { emojify } from './emojify'
77
import { isAbsolutePath, getPath } from '../router/util'
8-
import { isFn, merge, cached } from '../util/core'
8+
import { isFn, merge, cached, isPrimitive } from '../util/core'
99
import { get } from '../fetch/ajax'
1010

1111
const cachedLinks = {}
12-
let uid = 0
1312

14-
function getAndRemoveConfig (str = '') {
13+
export function getAndRemoveConfig (str = '') {
1514
const config = {}
1615

1716
if (str) {
@@ -25,62 +24,37 @@ function getAndRemoveConfig (str = '') {
2524

2625
return { str, config }
2726
}
27+
2828
const compileMedia = {
2929
markdown (url) {
30-
const id = `docsify-get-${uid++}`
31-
32-
if (!process.env.SSR) {
33-
get(url, false).then(text => {
34-
document.getElementById(id).innerHTML = this.compile(text)
35-
})
36-
37-
return `<div data-origin="${url}" id=${id}></div>`
38-
} else {
39-
return `<div data-origin="${url}" id=${uid}></div>
40-
<script>
41-
var compile = window.__current_docsify_compiler__
42-
Docsify.get('${url}', false).then(function(text) {
43-
document.getElementById('${uid}').innerHTML = compile(text)
44-
})
45-
</script>`
30+
return {
31+
url
4632
}
4733
},
4834
iframe (url, title) {
49-
return `<iframe src="${url}" ${title || 'width=100% height=400'}></iframe>`
35+
return {
36+
code: `<iframe src="${url}" ${title || 'width=100% height=400'}></iframe>`
37+
}
5038
},
5139
video (url, title) {
52-
return `<video src="${url}" ${title || 'controls'}>Not Support</video>`
40+
return {
41+
code: `<video src="${url}" ${title || 'controls'}>Not Support</video>`
42+
}
5343
},
5444
audio (url, title) {
55-
return `<audio src="${url}" ${title || 'controls'}>Not Support</audio>`
45+
return {
46+
code: `<audio src="${url}" ${title || 'controls'}>Not Support</audio>`
47+
}
5648
},
5749
code (url, title) {
58-
const id = `docsify-get-${uid++}`
59-
let ext = url.match(/\.(\w+)$/)
50+
let lang = url.match(/\.(\w+)$/)
6051

61-
ext = title || (ext && ext[1])
62-
if (ext === 'md') ext = 'markdown'
63-
64-
if (!process.env.SSR) {
65-
get(url, false).then(text => {
66-
document.getElementById(id).innerHTML = this.compile(
67-
'```' + ext + '\n' + text.replace(/`/g, '@qm@') + '\n```\n'
68-
).replace(/@qm@/g, '`')
69-
})
52+
lang = title || (lang && lang[1])
53+
if (lang === 'md') lang = 'markdown'
7054

71-
return `<div data-origin="${url}" id=${id}></div>`
72-
} else {
73-
return `<div data-origin="${url}" id=${id}></div>
74-
<script>
75-
setTimeout(() => {
76-
var compiler = window.__current_docsify_compiler__
77-
Docsify.get('${url}', false).then(function(text) {
78-
document.getElementById('${id}').innerHTML = compiler
79-
.compile('\`\`\`${ext}\\n' + text.replace(/\`/g, '@qm@') + '\\n\`\`\`\\n')
80-
.replace(/@qm@/g, '\`')
81-
})
82-
})
83-
</script>`
55+
return {
56+
url,
57+
lang
8458
}
8559
}
8660
}
@@ -109,20 +83,59 @@ export class Compiler {
10983
compile = marked
11084
}
11185

86+
this._marked = compile
11287
this.compile = cached(text => {
11388
let html = ''
11489

11590
if (!text) return text
11691

117-
html = compile(text)
92+
if (isPrimitive(text)) {
93+
html = compile(text)
94+
} else {
95+
html = compile.parser(text)
96+
}
97+
11898
html = config.noEmoji ? html : emojify(html)
11999
slugify.clear()
120100

121101
return html
122102
})
123103
}
124104

125-
matchNotCompileLink (link) {
105+
compileEmbed (href, title) {
106+
const { str, config } = getAndRemoveConfig(title)
107+
let embed
108+
title = str
109+
110+
if (config.include) {
111+
if (!isAbsolutePath(href)) {
112+
href = getPath(this.contentBase, href)
113+
}
114+
115+
let media
116+
if (config.type && (media = compileMedia[config.type])) {
117+
embed = media.call(this, href, title)
118+
embed.type = config.type
119+
} else {
120+
let type = 'code'
121+
if (/\.(md|markdown)/.test(href)) {
122+
type = 'markdown'
123+
} else if (/\.html?/.test(href)) {
124+
type = 'iframe'
125+
} else if (/\.(mp4|ogg)/.test(href)) {
126+
type = 'video'
127+
} else if (/\.mp3/.test(href)) {
128+
type = 'audio'
129+
}
130+
embed = compileMedia[type].call(this, href, title)
131+
embed.type = type
132+
}
133+
134+
return embed
135+
}
136+
}
137+
138+
_matchNotCompileLink (link) {
126139
const links = this.config.noCompileLinks || []
127140

128141
for (var i = 0; i < links.length; i++) {
@@ -182,33 +195,9 @@ export class Compiler {
182195
const { str, config } = getAndRemoveConfig(title)
183196
title = str
184197

185-
if (config.include) {
186-
if (!isAbsolutePath(href)) {
187-
href = getPath(contentBase, href)
188-
}
189-
190-
let media
191-
if (config.type && (media = compileMedia[config.type])) {
192-
return media.call(_self, href, title)
193-
}
194-
195-
let type = 'code'
196-
if (/\.(md|markdown)/.test(href)) {
197-
type = 'markdown'
198-
} else if (/\.html?/.test(href)) {
199-
type = 'iframe'
200-
} else if (/\.(mp4|ogg)/.test(href)) {
201-
type = 'video'
202-
} else if (/\.mp3/.test(href)) {
203-
type = 'audio'
204-
}
205-
206-
return compileMedia[type].call(_self, href, title)
207-
}
208-
209198
if (
210199
!/:|(\/{2})/.test(href) &&
211-
!_self.matchNotCompileLink(href) &&
200+
!_self._matchNotCompileLink(href) &&
212201
!config.ignore
213202
) {
214203
if (href === _self.config.homepage) href = 'README'

Diff for: src/core/render/embed.js

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { get } from '../fetch/ajax'
2+
import { merge } from '../util/core'
3+
4+
const cached = {}
5+
6+
function walkFetchEmbed ({ step = 0, embedTokens, compile }, cb) {
7+
const token = embedTokens[step]
8+
9+
if (!token) {
10+
return cb({})
11+
}
12+
13+
get(token.embed.url).then(text => {
14+
let embedToken
15+
16+
if (token.embed.type === 'markdown') {
17+
embedToken = compile.lexer(text)
18+
} else if (token.embed.type === 'code') {
19+
embedToken = compile.lexer(
20+
'```' +
21+
token.embed.lang +
22+
'\n' +
23+
text.replace(/`/g, '@DOCSIFY_QM@') +
24+
'\n```\n'
25+
)
26+
}
27+
cb({ token, embedToken })
28+
walkFetchEmbed({ step: ++step, compile, embedTokens }, cb)
29+
})
30+
}
31+
32+
export function prerenderEmbed ({ compiler, raw }, done) {
33+
let hit
34+
if ((hit = cached[raw])) {
35+
return done(hit)
36+
}
37+
38+
const compile = compiler._marked
39+
let tokens = compile.lexer(raw)
40+
const embedTokens = []
41+
const linkRE = compile.InlineLexer.rules.link
42+
const links = tokens.links
43+
44+
tokens.forEach((token, index) => {
45+
if (token.type === 'paragraph') {
46+
token.text = token.text.replace(
47+
new RegExp(linkRE, 'g'),
48+
(src, filename, href, title) => {
49+
const embed = compiler.compileEmbed(href, title)
50+
51+
if (embed) {
52+
if (embed.type === 'markdown' || embed.type === 'code') {
53+
embedTokens.push({
54+
index,
55+
embed
56+
})
57+
}
58+
return embed.code
59+
}
60+
61+
return src
62+
}
63+
)
64+
}
65+
})
66+
67+
let moveIndex = 0
68+
walkFetchEmbed({ compile, embedTokens }, ({ embedToken, token }) => {
69+
if (token) {
70+
const index = token.index + moveIndex
71+
72+
merge(links, embedToken.links)
73+
74+
tokens = tokens
75+
.slice(0, index)
76+
.concat(embedToken, tokens.slice(index + 1))
77+
moveIndex += embedToken.length - 1
78+
} else {
79+
cached[raw] = tokens.concat()
80+
tokens.links = cached[raw].links = links
81+
done(tokens)
82+
}
83+
})
84+
}

Diff for: src/core/render/index.js

+26-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { getPath, isAbsolutePath } from '../router/util'
99
import { isMobile, inBrowser } from '../util/env'
1010
import { isPrimitive } from '../util/core'
1111
import { scrollActiveSidebar, scroll2Top } from '../event/scroll'
12+
import { prerenderEmbed } from './embed'
1213

1314
function executeScript () {
1415
const script = dom
@@ -119,18 +120,37 @@ export function renderMixin (proto) {
119120
getAndActive(this.router, 'nav')
120121
}
121122

122-
proto._renderMain = function (text, opt = {}) {
123+
proto._renderMain = function (text, opt = {}, next) {
123124
if (!text) {
124125
return renderMain.call(this, text)
125126
}
126127

127128
callHook(this, 'beforeEach', text, result => {
128-
let html = this.isHTML ? result : this.compiler.compile(result)
129-
if (opt.updatedAt) {
130-
html = formatUpdated(html, opt.updatedAt, this.config.formatUpdated)
131-
}
129+
let html
130+
const callback = () => {
131+
if (opt.updatedAt) {
132+
html = formatUpdated(html, opt.updatedAt, this.config.formatUpdated)
133+
}
132134

133-
callHook(this, 'afterEach', html, text => renderMain.call(this, text))
135+
callHook(this, 'afterEach', html, text => renderMain.call(this, text))
136+
}
137+
if (this.isHTML) {
138+
html = this.result
139+
callback()
140+
next()
141+
} else {
142+
prerenderEmbed(
143+
{
144+
compiler: this.compiler,
145+
raw: text
146+
},
147+
tokens => {
148+
html = this.compiler.compile(tokens)
149+
callback()
150+
next()
151+
}
152+
)
153+
}
134154
})
135155
}
136156

Diff for: src/core/util/core.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
export function cached (fn) {
55
const cache = Object.create(null)
66
return function cachedFn (str) {
7-
const hit = cache[str]
8-
return hit || (cache[str] = fn(str))
7+
const key = isPrimitive(str) ? str : JSON.stringify(str)
8+
const hit = cache[key]
9+
return hit || (cache[key] = fn(str))
910
}
1011
}
1112

0 commit comments

Comments
 (0)