Skip to content

Commit d82a1a1

Browse files
committed
feat: support custom blocks
closes #2
1 parent b1d8868 commit d82a1a1

File tree

7 files changed

+99
-5
lines changed

7 files changed

+99
-5
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,26 @@ module.exports = {
123123

124124
That's it, [all the goodies](https://vue-loader.vuejs.org/) of `.vue` SFC are available in your `.vue.js` and `.vue.ts` files now!
125125

126+
### Custom blocks
127+
128+
You can also use [custom blocks](https://vue-loader.vuejs.org/guide/custom-blocks.html) in the `html` tag:
129+
130+
```js
131+
html`
132+
<custom-block name="i18n"> { "en": {} } </custom-block>
133+
`
134+
```
135+
136+
It will be converted to:
137+
138+
```vue
139+
<i18n>
140+
{
141+
"en": {}
142+
}
143+
</i18n>
144+
```
145+
126146
### Syntax higlighting
127147

128148
To highlight the code inside `html` template tag, you can use following editor plugins:

lib/attrs.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const reAttributes = /([\w-]+)\s*=\s*(?:(?:(["'])((?:(?!\2).)*)\2)|([\w-]+))|([\w-]+)/g
2+
3+
function camelCase(str) {
4+
return str.replace(/-([a-z])/g, (_, letter) => {
5+
return letter.toUpperCase()
6+
})
7+
}
8+
9+
exports.parseAttrs = attrString => {
10+
const attributes = {}
11+
12+
let match = null
13+
14+
let attrName = ''
15+
16+
let attrValue = null
17+
18+
if (!attrString) {
19+
return attributes
20+
}
21+
22+
while ((match = reAttributes.exec(attrString))) {
23+
attrName = match[1] || match[5]
24+
if (!attrName) {
25+
continue
26+
}
27+
if (attrName.indexOf('-') !== -1) {
28+
attrName = camelCase(attrName)
29+
}
30+
attrValue = match[3] || match[4] || true
31+
attributes[attrName] = attrValue
32+
}
33+
34+
return attributes
35+
}
36+
37+
exports.stringifyAttrs = require('stringify-attributes')

lib/compile.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const path = require('path')
22
const { parse } = require('@babel/parser')
33
const traverse = require('@babel/traverse')
44
const generator = require('@babel/generator')
5+
const { parseAttrs, stringifyAttrs } = require('./attrs')
56

67
module.exports = async (content, filename = 'foo.js') => {
78
const ext = path.extname(filename).slice(1)
@@ -20,6 +21,7 @@ module.exports = async (content, filename = 'foo.js') => {
2021

2122
let html = ''
2223
const styles = []
24+
const customBlocks = []
2325

2426
// Extract template the code
2527
traverse.default(ast, {
@@ -55,11 +57,23 @@ module.exports = async (content, filename = 'foo.js') => {
5557

5658
// Extract styles from the template
5759
const STYLE_RE = /<style[\s\S]*>[\s\S]*<\/style>/g
60+
const CUSTOM_BLOCK_RE = /<custom-block([\s\S]*)>([\s\S]*)<\/custom-block>/g
5861

59-
const template = html.replace(STYLE_RE, match => {
60-
styles.push(match)
61-
return ''
62-
})
62+
const template = html
63+
.replace(STYLE_RE, match => {
64+
styles.push(match)
65+
return ''
66+
})
67+
.replace(CUSTOM_BLOCK_RE, (_, m1, content) => {
68+
const attrs = parseAttrs(m1)
69+
const { name } = attrs
70+
if (!name) {
71+
throw new Error('[lit-vue] <custom-block> must have `name` attribute!')
72+
}
73+
delete attrs.name
74+
customBlocks.push(`<${name}${stringifyAttrs(attrs)}>${content}</${name}>`)
75+
return ''
76+
})
6377

6478
return `
6579
<template>
@@ -71,5 +85,6 @@ module.exports = async (content, filename = 'foo.js') => {
7185
</script>
7286
7387
${styles.join('\n')}
88+
${customBlocks.join('\n')}
7489
`
7590
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"dependencies": {
2424
"@babel/generator": "^7.2.2",
2525
"@babel/parser": "^7.2.3",
26-
"@babel/traverse": "^7.2.3"
26+
"@babel/traverse": "^7.2.3",
27+
"stringify-attributes": "^1.0.0"
2728
},
2829
"devDependencies": {
2930
"@babel/plugin-proposal-decorators": "^7.2.3",

test/__snapshots__/compile.test.js.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ exports[`simple 1`] = `
66
77
<div></div>
88
9+
10+
911
1012
</template>
1113
@@ -21,5 +23,8 @@ export default {
2123
color: red;
2224
}
2325
</style>
26+
<i18n>
27+
hello
28+
</i18n>
2429
"
2530
`;

test/compile.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ test('simple', async () => {
1212
color: red;
1313
}
1414
</style>
15+
16+
<custom-block name="i18n">
17+
hello
18+
</custom-block>
1519
\`
1620
1721
export default {

yarn.lock

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3683,6 +3683,11 @@ es6-promisify@^5.0.0:
36833683
dependencies:
36843684
es6-promise "^4.0.3"
36853685

3686+
escape-goat@^1.1.0:
3687+
version "1.3.0"
3688+
resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-1.3.0.tgz#bf3ee8ad1e488fbba404b084b2e4a55e09231c64"
3689+
integrity sha512-E2nU1Y39N5UgfLU8qwMlK0vZrZprIwWLeVmDYN8wd/e37hMtGzu2w1DBiREts0XHfgyZEQlj/hYr0H0izF0HDQ==
3690+
36863691
escape-html@~1.0.3:
36873692
version "1.0.3"
36883693
resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@@ -10640,6 +10645,13 @@ string_decoder@~1.1.1:
1064010645
dependencies:
1064110646
safe-buffer "~5.1.0"
1064210647

10648+
stringify-attributes@^1.0.0:
10649+
version "1.0.0"
10650+
resolved "https://registry.npmjs.org/stringify-attributes/-/stringify-attributes-1.0.0.tgz#9e8b2f9a9467e7b48093cb2124ebc1c17e6382c5"
10651+
integrity sha1-nosvmpRn57SAk8shJOvBwX5jgsU=
10652+
dependencies:
10653+
escape-goat "^1.1.0"
10654+
1064310655
stringify-object@^3.2.2:
1064410656
version "3.3.0"
1064510657
resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629"

0 commit comments

Comments
 (0)