Skip to content

Commit c05448d

Browse files
committed
support using component at root level
1 parent 4bde115 commit c05448d

File tree

10 files changed

+136
-12
lines changed

10 files changed

+136
-12
lines changed

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
- 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.
88

9+
- Deep markdown customization to make using Vue inside markdown a breeze.
10+
911
- Docs first: most of your content will be in Markdown.
1012

1113
- Largely gitbook compatible: should be easy to migrate existing gitbook docs over, and maintain original URLs.

Diff for: docs/.vuepress/components/OtherComponent.vue

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<p class="demo">This is another component</p>
3+
</template>

Diff for: docs/.vuepress/components/demo-1.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
<template>
2-
<div>
2+
<p class="demo">
33
{{ msg }}
4-
</div>
4+
</p>
55
</template>
66

77
<script>
88
export default {
99
data () {
1010
return {
11-
msg: 'hello this is a dynamic demo'
11+
msg: 'Hello this is <demo-1>'
1212
}
1313
}
1414
}

Diff for: docs/assets.md

Whitespace-only changes.

Diff for: docs/test.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<demo-1></demo-1>
2+
3+
<Demo>
4+
<p>hello</p>
5+
</Demo>

Diff for: docs/using-vue.md

+28-8
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,43 @@ Any `*.vue` file found in `.vuepress/components` are automatically registered as
5656
.
5757
└── .vuepress
5858
   └── components
59-
      └── demo-1.vue
59+
├── demo-1.vue
60+
      └── OtherComponent.vue
6061
```
6162

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

6465
``` markdown
65-
<div>
66-
<demo-1/>
67-
</div>
66+
<demo-1/>
67+
<OtherComponent/>
6868
```
6969

70+
<demo-1></demo-1>
71+
72+
<OtherComponent/>
73+
7074
::: warning
71-
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.
75+
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.
7276
:::
7377

7478
## Script & Style Hoisting
7579

76-
(TODO)
77-
7880
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.
81+
82+
<div id="inline-script-style-example"></div>
83+
84+
<!-- <style>
85+
#inline-script-style-example {
86+
color: #41b883;
87+
}
88+
</style>
89+
90+
<script>
91+
export default {
92+
mounted () {
93+
document.getElementById('inline-script-style-example')
94+
.textContent = 'Hello from inline script!'
95+
}
96+
}
97+
</script>
98+
-->

Diff for: lib/default-theme/styles.css

+6
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ div.danger {
4040
background-color: red;
4141
}
4242

43+
p.demo {
44+
padding: 1rem;
45+
border: 1px solid #ddd;
46+
border-radius: 4px;
47+
}
48+
4349
/*
4450
Copied from nprogress since it doens't
4551
allow programmatic configuration of color

Diff for: lib/markdown/component.js

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Replacing the default htmlBlock rule to allow using custom components at
2+
// root level
3+
4+
const blockNames = require('markdown-it/lib/common/htmlBlocks')
5+
const HTML_OPEN_CLOSE_TAG_RE = require('markdown-it/lib/common/html_re').HTML_OPEN_CLOSE_TAG_RE
6+
7+
// An array of opening and corresponding closing sequences for html tags,
8+
// last argument defines whether it can terminate a paragraph or not
9+
const HTML_SEQUENCES = [
10+
[/^<(script|pre|style)(?=(\s|>|$))/i, /<\/(script|pre|style)>/i, true],
11+
[/^<!--/, /-->/, true],
12+
[/^<\?/, /\?>/, true],
13+
[/^<![A-Z]/, />/, true],
14+
[/^<!\[CDATA\[/, /\]\]>/, true],
15+
// PascalCase Components
16+
[/^<[A-Z]/, />/, true],
17+
// custom elements with hyphens
18+
[/^<\w+\-/, />/, true],
19+
[new RegExp('^</?(' + blockNames.join('|') + ')(?=(\\s|/?>|$))', 'i'), /^$/, true],
20+
[new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + '\\s*$'), /^$/, false]
21+
]
22+
23+
module.exports = md => {
24+
md.block.ruler.at('htmlBlock', htmlBlock)
25+
}
26+
27+
function htmlBlock (state, startLine, endLine, silent) {
28+
let i, nextLine, lineText
29+
let pos = state.bMarks[startLine] + state.tShift[startLine]
30+
let max = state.eMarks[startLine]
31+
32+
// if it's indented more than 3 spaces, it should be a code block
33+
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
34+
35+
if (!state.md.options.html) { return false }
36+
37+
if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false }
38+
39+
lineText = state.src.slice(pos, max)
40+
41+
for (i = 0; i < HTML_SEQUENCES.length; i++) {
42+
if (HTML_SEQUENCES[i][0].test(lineText)) { break }
43+
}
44+
45+
if (i === HTML_SEQUENCES.length) {
46+
console.log(lineText)
47+
return false
48+
}
49+
50+
if (silent) {
51+
// true if this sequence can be a terminator, false otherwise
52+
return HTML_SEQUENCES[i][2]
53+
}
54+
55+
nextLine = startLine + 1
56+
57+
// If we are here - we detected HTML block.
58+
// Let's roll down till block end.
59+
if (!HTML_SEQUENCES[i][1].test(lineText)) {
60+
for (; nextLine < endLine; nextLine++) {
61+
if (state.sCount[nextLine] < state.blkIndent) { break }
62+
63+
pos = state.bMarks[nextLine] + state.tShift[nextLine]
64+
max = state.eMarks[nextLine]
65+
lineText = state.src.slice(pos, max)
66+
67+
if (HTML_SEQUENCES[i][1].test(lineText)) {
68+
if (lineText.length !== 0) { nextLine++ }
69+
break
70+
}
71+
}
72+
}
73+
74+
state.line = nextLine
75+
76+
const token = state.push('htmlBlock', '', 0)
77+
token.map = [startLine, nextLine]
78+
token.content = state.getLines(startLine, nextLine, state.blkIndent, true)
79+
80+
return true
81+
}

Diff for: lib/markdown/hoist.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = md => {
2+
3+
}

Diff for: lib/markdown/index.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const highlight = require('./highlight')
22
const highlightLines = require('./highlightLines')
3+
const component = require('./component')
34
const convertRouterLink = require('./link')
45
const emoji = require('markdown-it-emoji')
56
const anchor = require('markdown-it-anchor')
@@ -16,8 +17,11 @@ module.exports = ({ markdown = {}}) => {
1617
typographer: true,
1718
highlight
1819
})
19-
.use(convertRouterLink)
20+
// custom plugins
21+
.use(component)
2022
.use(highlightLines)
23+
.use(convertRouterLink)
24+
// 3rd party plugins
2125
.use(emoji)
2226
.use(anchor, Object.assign({ permalink: true, permalinkBefore: true }, markdown.anchor))
2327
.use(toc, Object.assign({ includeLevel: [2, 3] }, markdown.toc))

0 commit comments

Comments
 (0)