Skip to content

Commit 86fa839

Browse files
committed
feat: initial release
0 parents  commit 86fa839

19 files changed

+12543
-0
lines changed

.editorconfig

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
end_of_line = lf
7+
charset = utf-8
8+
trim_trailing_whitespace = true
9+
insert_final_newline = true
10+
11+
[*.md]
12+
trim_trailing_whitespace = false

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.prettierrc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"semi": false,
3+
"singleQuote": true,
4+
"bracketSpacing": true
5+
}

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) EGOIST <0x142857@gmail.com> (https://egoist.sh)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# lit-vue
2+
3+
[![NPM version](https://badgen.net/npm/v/lit-vue)](https://npmjs.com/package/lit-vue) [![NPM downloads](https://badgen.net/npm/dm/lit-vue)](https://npmjs.com/package/lit-vue) [![CircleCI](https://badgen.net/circleci/github/egoist/lit-vue/master)](https://circleci.com/gh/egoist/lit-vue/tree/master) [![donate](https://badgen.net/badge/support%20me/donate/ff69b4)](https://patreon.com/egoist) [![chat](https://badgen.net/badge/chat%20on/discord/7289DA)](https://chat.egoist.moe)
4+
5+
**Please consider [donating](https://www.patreon.com/egoist) to this project's author, [EGOIST](#author), to show your ❤️ and support.**
6+
7+
## Introduction
8+
9+
All the features of `.vue` SFC are now available in your `.js` and `.ts` files.
10+
11+
Combine `vue-loader` and `lit-vue/loader` to make the dream come to reality.
12+
13+
## Install
14+
15+
```bash
16+
yarn add lit-vue --dev
17+
```
18+
19+
## Example
20+
21+
Previously you can use `.vue` single-file component like this:
22+
23+
```vue
24+
<template>
25+
<div>
26+
<h1>hello</h1>
27+
<hr />
28+
<button @click="inc">{{ count }}</button>
29+
</div>
30+
</template>
31+
32+
<script>
33+
export default {
34+
template,
35+
data() {
36+
return {
37+
count: 0
38+
}
39+
},
40+
methods: {
41+
inc() {
42+
this.count++
43+
}
44+
}
45+
}
46+
</script>
47+
48+
<style scoped>
49+
h1 {
50+
color: red;
51+
}
52+
</style>
53+
```
54+
55+
Now with `lit-html` you can use `.js` and `.ts` extensions:
56+
57+
```js
58+
import html from 'lit-vue/html'
59+
60+
const template = html`
61+
<div>
62+
<h1>hello</h1>
63+
<hr />
64+
<button @click="inc">{{ count }}</button>
65+
</div>
66+
67+
<style scoped>
68+
h1 {
69+
color: red;
70+
}
71+
</style>
72+
`
73+
74+
export default {
75+
template,
76+
data() {
77+
return {
78+
count: 0
79+
}
80+
},
81+
methods: {
82+
inc() {
83+
this.count++
84+
}
85+
}
86+
}
87+
```
88+
89+
## How to use
90+
91+
### Use with webpack
92+
93+
```js
94+
module.exports = {
95+
module: {
96+
rules: [
97+
{
98+
// Match .js .ts files
99+
test: [/\.[jt]s$/],
100+
// Excude .vue.js .vue.ts files
101+
// Since we want lit-vue to transform them into Vue SFC instead
102+
exclude: [/\.vue.[jt]s/]
103+
loader: 'babel-loader' // Use your desired loader
104+
},
105+
// Handle .vue.js .vue.ts with lit-vue/loader and vue-loader
106+
{
107+
test: [/\.vue.[jt]s$/],
108+
use: [
109+
'vue-loader',
110+
'lit-vue/loader'
111+
]
112+
},
113+
// This rule is also necessary even if you don't directly use .vue files
114+
{
115+
test: /\.vue$/,
116+
loader: 'vue-loader'
117+
}
118+
]
119+
}
120+
}
121+
```
122+
123+
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!
124+
125+
### Syntax higlighting
126+
127+
To highlight the code inside `html` template tag, you can use following editor plugins:
128+
129+
- VSCode: [lit-html](https://marketplace.visualstudio.com/items?itemName=bierner.lit-html)
130+
- Something is missing? Send a PR to add it here!
131+
132+
## Contributing
133+
134+
1. Fork it!
135+
2. Create your feature branch: `git checkout -b my-new-feature`
136+
3. Commit your changes: `git commit -am 'Add some feature'`
137+
4. Push to the branch: `git push origin my-new-feature`
138+
5. Submit a pull request :D
139+
140+
## Author
141+
142+
**lit-vue** © EGOIST, Released under the [MIT](./LICENSE) License.<br>
143+
Authored and maintained by EGOIST with help from contributors ([list](https://github.com/egoist/lit-vue/contributors)).
144+
145+
> [Website](https://egoist.sh) · GitHub [@EGOIST](https://github.com/egoist) · Twitter [@\_egoistlily](https://twitter.com/_egoistlily)

circle.yml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
version: 2
2+
jobs:
3+
build:
4+
docker:
5+
- image: circleci/node:latest
6+
branches:
7+
ignore:
8+
- gh-pages # list of branches to ignore
9+
- /release\/.*/ # or ignore regexes
10+
steps:
11+
- checkout
12+
- restore_cache:
13+
key: dependency-cache-{{ checksum "yarn.lock" }}
14+
- run:
15+
name: install dependences
16+
command: yarn
17+
- save_cache:
18+
key: dependency-cache-{{ checksum "yarn.lock" }}
19+
paths:
20+
- ./node_modules
21+
- run:
22+
name: test
23+
command: yarn test
24+
- run:
25+
name: Release
26+
command: yarn semantic-release

example/App.vue.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// eslint-disable-next-line import/no-unresolved
2+
import { html } from 'lit-vue'
3+
4+
const template = html`
5+
<div>
6+
<h1>hello</h1>
7+
<hr />
8+
<button @click="inc">{{ count }}</button>
9+
</div>
10+
11+
<style scoped>
12+
h1 {
13+
color: red;
14+
}
15+
</style>
16+
`
17+
18+
export default {
19+
template,
20+
data() {
21+
return {
22+
count: 0
23+
}
24+
},
25+
methods: {
26+
inc() {
27+
this.count++
28+
}
29+
}
30+
}

example/index.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Vue from 'vue'
2+
import App from './App.vue'
3+
4+
new Vue({
5+
el: '#app',
6+
render: h => h(App)
7+
})

example/poi.config.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const path = require('path')
2+
3+
module.exports = {
4+
entry: path.join(__dirname, 'index.js'),
5+
chainWebpack(config) {
6+
const SFC_EXT = /\.vue\.[jt]s$/
7+
8+
config.module.rule('js').exclude.add(SFC_EXT)
9+
10+
config.module
11+
.rule('lit-vue')
12+
.test(SFC_EXT)
13+
.use('vue-loader')
14+
.loader('vue-loader')
15+
.end()
16+
.use('lit-vue')
17+
.loader(require.resolve('../loader'))
18+
}
19+
}

index.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
console.error('[lit-vue] Missing lit-vue/loader in your webpack config!')
2+
3+
// noop
4+
export const html = () => undefined

lib/compile.js

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
const path = require('path')
2+
const { parse } = require('@babel/parser')
3+
const traverse = require('@babel/traverse')
4+
const generator = require('@babel/generator')
5+
const posthtml = require('posthtml')
6+
const renderHTML = require('./renderHTML')
7+
8+
module.exports = async (content, filename = 'foo.js') => {
9+
const ext = path.extname(filename).slice(1)
10+
const ast = parse(content, {
11+
sourceType: 'module',
12+
sourceFilename: filename,
13+
plugins: [
14+
ext === 'ts' && 'typescript',
15+
'classProperties',
16+
'objectRestSpread',
17+
'optionalChaining',
18+
'asyncGenerators'
19+
].filter(Boolean)
20+
})
21+
22+
let html = ''
23+
const styles = []
24+
25+
// Extract template the code
26+
traverse.default(ast, {
27+
ImportDeclaration(path) {
28+
if (path.node.source.value !== 'lit-vue') {
29+
return
30+
}
31+
32+
for (const specifier of path.node.specifiers) {
33+
if (specifier.type !== 'ImportSpecifier') {
34+
continue
35+
}
36+
37+
if (specifier.imported.name === 'html') {
38+
const binding = path.scope.getBinding(specifier.local.name)
39+
for (let i = 0; i < binding.referencePaths.length; i++) {
40+
if (i > 0) {
41+
throw new Error(
42+
`[lit-vue] You can only call \`html\` once in a single-file component.`
43+
)
44+
}
45+
const ref = binding.referencePaths[i].parentPath
46+
html = ref.get('quasi').evaluate().value
47+
ref.replaceWith({ type: 'Identifier', name: 'undefined' })
48+
}
49+
}
50+
}
51+
52+
// Remove the import
53+
path.remove()
54+
}
55+
})
56+
57+
// Extract styles from the template
58+
const template = await posthtml(
59+
[
60+
tree => {
61+
tree.walk(node => {
62+
if (node.tag === 'style') {
63+
styles.push(renderHTML(node))
64+
return
65+
}
66+
return node
67+
})
68+
return tree
69+
}
70+
],
71+
{ sync: false }
72+
)
73+
.process(html)
74+
.then(res => {
75+
return res.html.trim()
76+
})
77+
78+
return `
79+
<template>
80+
${template}
81+
</template>
82+
83+
<script lang="${ext}">
84+
${generator.default(ast).code}
85+
</script>
86+
87+
${styles.join('\n')}
88+
`
89+
}

lib/loader.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module.exports = async function(content) {
2+
this.cacheable()
3+
const done = this.async()
4+
try {
5+
const result = await require('./compile')(content, this.resourcePath)
6+
done(null, result)
7+
} catch (error) {
8+
done(error)
9+
}
10+
}

0 commit comments

Comments
 (0)