diff --git a/src/components/UniLink.vue b/src/components/UniLink.vue index a4c4fb97..dc1455bb 100644 --- a/src/components/UniLink.vue +++ b/src/components/UniLink.vue @@ -6,14 +6,7 @@ export default { props: ['openInNewTab', 'externalLinkIcon'], - render( - h, - { - data, - children, - props: {openInNewTab, externalLinkIcon} - } - ) { + render(h, {data, children, props: {openInNewTab, externalLinkIcon}}) { const attrs = {...data.attrs} const {to} = attrs diff --git a/src/index.js b/src/index.js index 8d58e0d9..81f18ecb 100644 --- a/src/index.js +++ b/src/index.js @@ -22,6 +22,7 @@ import versionsPlugin from './plugins/versions' import bannerFooter from './plugins/banner-footer' import darkThemeToggler from './plugins/dark-theme-toggler' import searchPlugin from './plugins/search' +import embedPlugin from './plugins/embed' Vue.component(ImageZoom.name, ImageZoom) Vue.component(Badge.name, Badge) @@ -55,6 +56,7 @@ class Docute { }) const plugins = [ + embedPlugin, i18nPlugin, evaluateContentPlugin, versionsPlugin, diff --git a/src/plugins/embed/index.js b/src/plugins/embed/index.js new file mode 100644 index 00000000..a0e2bea5 --- /dev/null +++ b/src/plugins/embed/index.js @@ -0,0 +1,66 @@ +import {getFilenameByPath, getFileUrl, isExternalLink} from '../../utils' +import inlineRender from '../../utils/inlineRender' + +const SCAN = /#embed\(.*?\)(?!`)/g + +async function getFile(url, api, type) { + if (!isExternalLink(url)) { + let filename = getFilenameByPath(url) + if (type) { + filename = filename.replace('.md', '') + } + const file = getFileUrl(api.store.getters.config.sourcePath, filename) + const res = await fetch(file) + return res.text() + } + return url +} + +function compileMedia(type, url, api, codeType = '') { + switch (type) { + case 'fragment': + return getFile(url, api).then(inlineRender) + case 'code': + return getFile(url, api, type) + .then(x => `\`\`\`${codeType}\n${x}\n\`\`\``) + .then(inlineRender) + default: + return 'Missing embed kind' + } +} + +async function replaceAsync(str, regex, asyncFn) { + const promises = [] + str.replace(regex, (match, ...args) => { + const promise = asyncFn(match, ...args) + promises.push(promise) + }) + const data = await Promise.all(promises) + return str.replace(regex, () => data.shift()) +} + +function transformEmbeds(markdown, api) { + function process(match) { + const params = match + .replace('#embed(', '') + .replace(')', '') + .split(' ') + const href = params[0] + const type = params[1] + const codeType = params[2] + const embed = compileMedia(type, href, api, codeType, params.slice(3)) + return embed + } + return replaceAsync(markdown, SCAN, process) +} + +const embedPlugin = { + name: 'embed', + extend(api) { + api.processMarkdown(async markdown => { + return transformEmbeds(markdown, api) + }) + } +} + +export default embedPlugin diff --git a/src/utils/inlineRender.js b/src/utils/inlineRender.js new file mode 100644 index 00000000..e82920c1 --- /dev/null +++ b/src/utils/inlineRender.js @@ -0,0 +1,22 @@ +import hooks from '../hooks' +import markedRenderer from './markedRenderer' +import marked from './marked' +import highlight from './highlight' + +async function inlineRender(iContent) { + let content = await hooks.processPromise('processMarkdown', iContent) + const env = { + headings: [], + mixins: [], + config: {} + } + content = marked(content, { + renderer: markedRenderer(hooks), + highlight, + env + }) + content = await hooks.processPromise('processHTML', content) + return content +} + +export default inlineRender diff --git a/website/docs/exampleEmbeds/embedjs.js b/website/docs/exampleEmbeds/embedjs.js new file mode 100644 index 00000000..0e5ef3a2 --- /dev/null +++ b/website/docs/exampleEmbeds/embedjs.js @@ -0,0 +1,2 @@ +const a = 1 +const b = a? a: 0 \ No newline at end of file diff --git a/website/docs/exampleEmbeds/fragment.md b/website/docs/exampleEmbeds/fragment.md new file mode 100644 index 00000000..002bbcb4 --- /dev/null +++ b/website/docs/exampleEmbeds/fragment.md @@ -0,0 +1,3 @@ +### Title from fragment + +*fragment body* diff --git a/website/docs/guide/embed.md b/website/docs/guide/embed.md new file mode 100644 index 00000000..b7ea5387 --- /dev/null +++ b/website/docs/guide/embed.md @@ -0,0 +1,15 @@ +# Embed + +Embeds let you display items from different files. + +## Demonstrations +Some of these describe defaults, and caveats. + +### Code + +#embed(/exampleEmbeds/embedjs.js code js) + +### Fragment +Note that the titles from fragment aren't visible in sidebar. Titles of second and third level are suppose to be shown there with the first level used as browser tab title. + +#embed(/exampleEmbeds/fragment.md fragment) diff --git a/website/index.js b/website/index.js index 604cb11e..b0cd386a 100644 --- a/website/index.js +++ b/website/index.js @@ -106,6 +106,10 @@ new Docute({ title: 'Plugin', link: '/guide/plugin' }, + { + title: 'Embed', + link: '/guide/embed' + }, { title: 'Deployment', link: '/guide/deployment'