Skip to content

Commit

Permalink
support using component at root level
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Apr 6, 2018
1 parent 4bde115 commit c05448d
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 12 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -6,6 +6,8 @@

- Vue-powered: the layout system is a Vue app. It's also pre-rendered into static HTML. And you can register and use Vue components inside markdown content.

- Deep markdown customization to make using Vue inside markdown a breeze.

- Docs first: most of your content will be in Markdown.

- Largely gitbook compatible: should be easy to migrate existing gitbook docs over, and maintain original URLs.
Expand Down
3 changes: 3 additions & 0 deletions docs/.vuepress/components/OtherComponent.vue
@@ -0,0 +1,3 @@
<template>
<p class="demo">This is another component</p>
</template>
6 changes: 3 additions & 3 deletions docs/.vuepress/components/demo-1.vue
@@ -1,14 +1,14 @@
<template>
<div>
<p class="demo">
{{ msg }}
</div>
</p>
</template>

<script>
export default {
data () {
return {
msg: 'hello this is a dynamic demo'
msg: 'Hello this is <demo-1>'
}
}
}
Expand Down
Empty file added docs/assets.md
Empty file.
5 changes: 5 additions & 0 deletions docs/test.md
@@ -0,0 +1,5 @@
<demo-1></demo-1>

<Demo>
<p>hello</p>
</Demo>
36 changes: 28 additions & 8 deletions docs/using-vue.md
Expand Up @@ -56,23 +56,43 @@ Any `*.vue` file found in `.vuepress/components` are automatically registered as
.
└── .vuepress
   └── components
      └── demo-1.vue
├── demo-1.vue
      └── OtherComponent.vue
```

Inside any markdown file you can then use the component like so:
Inside any markdown file you can then directly use the components (names are inferred from filenames):

``` markdown
<div>
<demo-1/>
</div>
<demo-1/>
<OtherComponent/>
```

<demo-1></demo-1>

<OtherComponent/>

::: warning
Note **components must be nested inside a `<div>`**, because otherwise they'd be automatically wrapped inside a `<p>`, which can only contain inline elements. If your component contains block level elements (it mostly likely does), it will cause hydration mismatch in static builds.
Make sure a custom component's names either contains a hyphen or is in PascalCase. Otherwise it will be treated as an inline element and wrapped inside a `<p>` tag, which will lead to hydration mismatch because `<p>` does not allow block elements to be placed inside it.
:::

## Script & Style Hoisting

(TODO)

Sometimes you may need to apply some JavaScript or CSS only to the current page. In those case you can directly write root-level `<script>` or `<style>` blocks in the markdown file, and they will be hoisted out of the compiled HTML and used as the `<script>` and `<style>` blocks for the resulting Vue single-file component.

<div id="inline-script-style-example"></div>

<!-- <style>
#inline-script-style-example {
color: #41b883;
}
</style>
<script>
export default {
mounted () {
document.getElementById('inline-script-style-example')
.textContent = 'Hello from inline script!'
}
}
</script>
-->
6 changes: 6 additions & 0 deletions lib/default-theme/styles.css
Expand Up @@ -40,6 +40,12 @@ div.danger {
background-color: red;
}

p.demo {
padding: 1rem;
border: 1px solid #ddd;
border-radius: 4px;
}

/*
Copied from nprogress since it doens't
allow programmatic configuration of color
Expand Down
81 changes: 81 additions & 0 deletions lib/markdown/component.js
@@ -0,0 +1,81 @@
// Replacing the default htmlBlock rule to allow using custom components at
// root level

const blockNames = require('markdown-it/lib/common/htmlBlocks')
const HTML_OPEN_CLOSE_TAG_RE = require('markdown-it/lib/common/html_re').HTML_OPEN_CLOSE_TAG_RE

// An array of opening and corresponding closing sequences for html tags,
// last argument defines whether it can terminate a paragraph or not
const HTML_SEQUENCES = [
[/^<(script|pre|style)(?=(\s|>|$))/i, /<\/(script|pre|style)>/i, true],
[/^<!--/, /-->/, true],
[/^<\?/, /\?>/, true],
[/^<![A-Z]/, />/, true],
[/^<!\[CDATA\[/, /\]\]>/, true],
// PascalCase Components
[/^<[A-Z]/, />/, true],
// custom elements with hyphens
[/^<\w+\-/, />/, true],
[new RegExp('^</?(' + blockNames.join('|') + ')(?=(\\s|/?>|$))', 'i'), /^$/, true],
[new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + '\\s*$'), /^$/, false]
]

module.exports = md => {
md.block.ruler.at('htmlBlock', htmlBlock)
}

function htmlBlock (state, startLine, endLine, silent) {
let i, nextLine, lineText
let pos = state.bMarks[startLine] + state.tShift[startLine]
let max = state.eMarks[startLine]

// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }

if (!state.md.options.html) { return false }

if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false }

lineText = state.src.slice(pos, max)

for (i = 0; i < HTML_SEQUENCES.length; i++) {
if (HTML_SEQUENCES[i][0].test(lineText)) { break }
}

if (i === HTML_SEQUENCES.length) {
console.log(lineText)
return false
}

if (silent) {
// true if this sequence can be a terminator, false otherwise
return HTML_SEQUENCES[i][2]
}

nextLine = startLine + 1

// If we are here - we detected HTML block.
// Let's roll down till block end.
if (!HTML_SEQUENCES[i][1].test(lineText)) {
for (; nextLine < endLine; nextLine++) {
if (state.sCount[nextLine] < state.blkIndent) { break }

pos = state.bMarks[nextLine] + state.tShift[nextLine]
max = state.eMarks[nextLine]
lineText = state.src.slice(pos, max)

if (HTML_SEQUENCES[i][1].test(lineText)) {
if (lineText.length !== 0) { nextLine++ }
break
}
}
}

state.line = nextLine

const token = state.push('htmlBlock', '', 0)
token.map = [startLine, nextLine]
token.content = state.getLines(startLine, nextLine, state.blkIndent, true)

return true
}
3 changes: 3 additions & 0 deletions lib/markdown/hoist.js
@@ -0,0 +1,3 @@
module.exports = md => {

}
6 changes: 5 additions & 1 deletion lib/markdown/index.js
@@ -1,5 +1,6 @@
const highlight = require('./highlight')
const highlightLines = require('./highlightLines')
const component = require('./component')
const convertRouterLink = require('./link')
const emoji = require('markdown-it-emoji')
const anchor = require('markdown-it-anchor')
Expand All @@ -16,8 +17,11 @@ module.exports = ({ markdown = {}}) => {
typographer: true,
highlight
})
.use(convertRouterLink)
// custom plugins
.use(component)
.use(highlightLines)
.use(convertRouterLink)
// 3rd party plugins
.use(emoji)
.use(anchor, Object.assign({ permalink: true, permalinkBefore: true }, markdown.anchor))
.use(toc, Object.assign({ includeLevel: [2, 3] }, markdown.toc))
Expand Down

0 comments on commit c05448d

Please sign in to comment.