Skip to content

Commit 63d5497

Browse files
committed
feat: improve global style support
1 parent af2e8f7 commit 63d5497

File tree

8 files changed

+102
-73
lines changed

8 files changed

+102
-73
lines changed

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@
4646
yarn add styled-vue --dev
4747
```
4848

49+
Then register the Vue plugin (**optional**):
50+
51+
```js
52+
import Vue from 'vue'
53+
import { StyledVue } from 'styled-vue'
54+
55+
Vue.use(StyledVue)
56+
```
57+
58+
So far the plugin is only required for [globalStyle](#globalstyle), if you only need scoped style, you can safely skip this.
59+
4960
## Example
5061

5162
```vue
@@ -204,14 +215,23 @@ import { css } from 'styled-vue'
204215

205216
export default {
206217
globalStyle: css`
207-
#app {
218+
body {
208219
color: ${vm => vm.bodyColor};
209220
}
210221
`
211222
}
212223
```
213224

214-
Note CSS variables (dynamic value) can only apply to current component and child components, so if you are trying to use them on parent selector like `body`, they **WON'T** work! Currently there's no easy way to fix this.
225+
`globalStyle` relies on the Vue plugin, make sure the register it first:
226+
227+
```js
228+
import Vue from 'vue'
229+
import { StyledVue } from 'styled-vue'
230+
231+
Vue.use(StyledVue)
232+
```
233+
234+
This only adds ~100 bytes to your application.
215235

216236
### TypeScript
217237

example/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const border = `10px solid pink`
1616
1717
export default {
1818
globalStyle: css`
19-
#app {
19+
body {
2020
border: ${border};
2121
}
2222
`,

example/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import Vue from 'vue'
2+
import { StyledVue } from 'styled-vue'
23
import App from './App.vue'
34

5+
Vue.use(StyledVue)
6+
47
new Vue({
58
el: '#app',
69
render: h => h(App)

example/poi.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@ module.exports = {
1010
options.compiler = require('../compiler')
1111
return options
1212
})
13+
14+
config.resolve.alias.set('styled-vue', path.join(__dirname, '../lib'))
1315
}
1416
}

lib/index.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,25 @@
1-
// noop
2-
export const css = () => ''
1+
Object.defineProperty(exports, '__esModule', { value: true })
2+
3+
function css(_) {
4+
throw new Error(
5+
`You need to replace vue-template-compiler with styled-vue/compiler`
6+
)
7+
}
8+
9+
function StyledVue(Vue) {
10+
Vue.component('styled-vue-global-css', {
11+
render(h) {
12+
const globalStyle = this.$parent.$options.globalStyle(this.$parent)
13+
let css = ''
14+
// eslint-disable-next-line guard-for-in
15+
for (const key in globalStyle) {
16+
css += key + ':' + globalStyle[key] + ';'
17+
}
18+
return h('style', {}, [':root {' + css + '}'])
19+
}
20+
})
21+
}
22+
23+
exports.css = css
24+
25+
exports.StyledVue = StyledVue

lib/parseComponent.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,30 @@ module.exports = (content, opts) => {
1111
styleLang,
1212
hasVars,
1313
scriptContent,
14+
hasGlobalVars,
1415
globalStyle,
1516
globalStyleLang
1617
} = parseScript(sfc.script)
1718

1819
sfc.script.content = scriptContent
1920

20-
if (sfc.template && hasVars) {
21+
if (sfc.template && (hasVars || hasGlobalVars)) {
2122
sfc.template.content = posthtml([
2223
tree => {
2324
for (const node of tree) {
2425
if (node.tag) {
25-
node.attrs = node.attrs || {}
26-
const existing =
27-
node.attrs[':style'] || node.attrs['v-bind:style']
28-
node.attrs[
29-
':style'
30-
] = `$options._getCssVariables(this, $options, ${existing})`
26+
if (hasVars) {
27+
node.attrs = node.attrs || {}
28+
const existing =
29+
node.attrs[':style'] || node.attrs['v-bind:style']
30+
node.attrs[':style'] = `$options.style(this, ${existing})`
31+
}
32+
if (node.content && hasGlobalVars) {
33+
node.content.unshift({
34+
tag: 'styled-vue-global-css'
35+
})
36+
}
37+
break
3138
}
3239
}
3340
return tree

lib/parseScript.js

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ const LANGS = ['css', 'stylus', 'less', 'sass', 'scss']
99
module.exports = script => {
1010
let style
1111
let styleLang
12-
let hasVars = false
1312
let globalStyle
1413
let globalStyleLang
14+
let hasVars = false
15+
let hasGlobalVars = false
1516

1617
const ast = parser.parse(script.content, {
1718
sourceType: 'module',
@@ -40,7 +41,6 @@ module.exports = script => {
4041
type: 'TemplateElement',
4142
value: { raw: value, cooked: value }
4243
})
43-
hasVars = true
4444

4545
// Check unit
4646
let unit
@@ -140,14 +140,14 @@ module.exports = script => {
140140
continue
141141
}
142142

143-
styleLang = specifier.imported.name
143+
const lang = specifier.imported.name
144144

145-
if (!LANGS.includes(styleLang)) {
146-
throw new Error(`[styled-vue] "${styleLang}" is not supported`)
145+
if (!LANGS.includes(lang)) {
146+
throw new Error(`[styled-vue] "${lang}" is not supported`)
147147
}
148148

149149
const binding = path.scope.getBinding(specifier.local.name)
150-
let objectExpressionPath
150+
151151
for (let i = 0; i < binding.referencePaths.length; i++) {
152152
// The tagged template path
153153
const ref = binding.referencePaths[i].parentPath
@@ -164,28 +164,43 @@ module.exports = script => {
164164
)
165165
}
166166

167-
// The object expression path
168-
objectExpressionPath = propertyPath.parentPath
169-
170167
const isGlobal = propertyPath.node.key.name === 'globalStyle'
171168
const { vars, varDeclarations, extractedStyle } = parseTaggedTemplate(
172169
ref,
173170
isGlobal
174171
)
175172
if (isGlobal) {
176173
globalStyle = extractedStyle
174+
globalStyleLang = lang
175+
if (vars.length > 0) {
176+
hasGlobalVars = true
177+
}
177178
} else {
178179
style = extractedStyle
180+
styleLang = lang
181+
if (vars.length > 0) {
182+
hasVars = true
183+
}
179184
}
180185

181186
if (vars.length > 0) {
182187
ref.replaceWith(
183188
t.functionExpression(
184189
null,
185-
[t.identifier('vm'), t.identifier('existing')],
190+
[
191+
t.identifier('vm'),
192+
!isGlobal && t.identifier('existing') // Global vars are handled differently
193+
].filter(Boolean),
186194
t.blockStatement([
187195
...varDeclarations,
188-
t.returnStatement(t.objectExpression(vars))
196+
t.returnStatement(
197+
isGlobal
198+
? t.objectExpression(vars)
199+
: t.arrayExpression([
200+
t.identifier('existing'),
201+
t.objectExpression(vars)
202+
])
203+
)
189204
])
190205
)
191206
)
@@ -197,42 +212,6 @@ module.exports = script => {
197212
ref.replaceWith(NoVarsFound)
198213
}
199214
}
200-
201-
if (hasVars && objectExpressionPath) {
202-
const createObjectCall = name => {
203-
return t.logicalExpression(
204-
'&&',
205-
t.memberExpression(t.identifier('options'), t.identifier(name)),
206-
t.callExpression(
207-
t.memberExpression(t.identifier('options'), t.identifier(name)),
208-
[t.identifier('vm')]
209-
)
210-
)
211-
}
212-
213-
objectExpressionPath.node.properties.push(
214-
t.objectProperty(
215-
t.identifier('_getCssVariables'),
216-
t.functionExpression(
217-
null,
218-
[
219-
t.identifier('vm'),
220-
t.identifier('options'),
221-
t.identifier('existing')
222-
],
223-
t.blockStatement([
224-
t.returnStatement(
225-
t.arrayExpression([
226-
t.identifier('existing'),
227-
createObjectCall('globalStyle'),
228-
createObjectCall('style')
229-
])
230-
)
231-
])
232-
)
233-
)
234-
)
235-
}
236215
}
237216

238217
// Remove the import
@@ -245,6 +224,7 @@ module.exports = script => {
245224
styleLang,
246225
globalStyle,
247226
globalStyleLang,
227+
hasGlobalVars,
248228
hasVars,
249229
scriptContent: generator.default(ast).code
250230
}

test/__snapshots__/parseComponent.test.js.snap

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default {
3131
</script>
3232
3333
34-
<style scoped=\\"undefined\\" lang=\\"undefined\\">
34+
<style scoped=\\"undefined\\" lang=\\"css\\">
3535
h1 {color: red}
3636
</style>
3737
@@ -73,7 +73,7 @@ export default {
7373
</script>
7474
7575
76-
<style scoped=\\"undefined\\" lang=\\"undefined\\">
76+
<style scoped=\\"undefined\\" lang=\\"css\\">
7777
h1 {color: red}
7878
</style>
7979
@@ -106,29 +106,26 @@ export default {
106106
107107
<template>
108108
109-
<h1 :style=\\"$options._getCssVariables(this, $options, undefined)\\">hello</h1>
109+
<h1><styled-vue-global-css></styled-vue-global-css>hello</h1>
110110
111111
</template>
112112
113113
<script>
114114
export default {
115115
style: undefined // No CSS variables
116116
,
117-
globalStyle: function (vm, existing) {
117+
globalStyle: function (vm) {
118118
var gv0 = vm => vm.fontSize;
119119
120120
return {
121121
\\"--gv0\\": gv0(vm)
122122
};
123-
},
124-
_getCssVariables: function (vm, options, existing) {
125-
return [existing, options.globalStyle && options.globalStyle(vm), options.style && options.style(vm)];
126123
}
127124
};
128125
</script>
129126
130127
131-
<style scoped=\\"undefined\\" lang=\\"undefined\\">
128+
<style scoped=\\"undefined\\" lang=\\"css\\">
132129
h1 {color: red; font-size: var(--gv0);}
133130
</style>
134131
@@ -233,7 +230,7 @@ exports[`simple 1`] = `
233230
234231
<template>
235232
236-
<h1 style=\\"foo\\" :style=\\"$options._getCssVariables(this, $options, {})\\">hello</h1>
233+
<h1 style=\\"foo\\" :style=\\"$options.style(this, {})\\">hello</h1>
237234
238235
</template>
239236
@@ -243,13 +240,10 @@ exports[`simple 1`] = `
243240
var v0 = vm => vm.color;
244241
245242
var v1 = 200 + 1;
246-
return {
243+
return [existing, {
247244
\\"--v0\\": v0(vm),
248245
\\"--v1\\": v1 + \\"px\\"
249-
};
250-
},
251-
_getCssVariables: function (vm, options, existing) {
252-
return [existing, options.globalStyle && options.globalStyle(vm), options.style && options.style(vm)];
246+
}];
253247
}
254248
};
255249
</script>

0 commit comments

Comments
 (0)