Skip to content

Commit

Permalink
feat: improve global style support
Browse files Browse the repository at this point in the history
  • Loading branch information
egoist committed Jan 18, 2020
1 parent af2e8f7 commit 63d5497
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 73 deletions.
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@
yarn add styled-vue --dev
```

Then register the Vue plugin (**optional**):

```js
import Vue from 'vue'
import { StyledVue } from 'styled-vue'

Vue.use(StyledVue)
```

So far the plugin is only required for [globalStyle](#globalstyle), if you only need scoped style, you can safely skip this.

## Example

```vue
Expand Down Expand Up @@ -204,14 +215,23 @@ import { css } from 'styled-vue'

export default {
globalStyle: css`
#app {
body {
color: ${vm => vm.bodyColor};
}
`
}
```

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.
`globalStyle` relies on the Vue plugin, make sure the register it first:

```js
import Vue from 'vue'
import { StyledVue } from 'styled-vue'

Vue.use(StyledVue)
```

This only adds ~100 bytes to your application.

### TypeScript

Expand Down
2 changes: 1 addition & 1 deletion example/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const border = `10px solid pink`
export default {
globalStyle: css`
#app {
body {
border: ${border};
}
`,
Expand Down
3 changes: 3 additions & 0 deletions example/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import Vue from 'vue'
import { StyledVue } from 'styled-vue'
import App from './App.vue'

Vue.use(StyledVue)

new Vue({
el: '#app',
render: h => h(App)
Expand Down
2 changes: 2 additions & 0 deletions example/poi.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ module.exports = {
options.compiler = require('../compiler')
return options
})

config.resolve.alias.set('styled-vue', path.join(__dirname, '../lib'))
}
}
27 changes: 25 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,25 @@
// noop
export const css = () => ''
Object.defineProperty(exports, '__esModule', { value: true })

function css(_) {
throw new Error(
`You need to replace vue-template-compiler with styled-vue/compiler`
)
}

function StyledVue(Vue) {
Vue.component('styled-vue-global-css', {
render(h) {
const globalStyle = this.$parent.$options.globalStyle(this.$parent)
let css = ''
// eslint-disable-next-line guard-for-in
for (const key in globalStyle) {
css += key + ':' + globalStyle[key] + ';'
}
return h('style', {}, [':root {' + css + '}'])
}
})
}

exports.css = css

exports.StyledVue = StyledVue
21 changes: 14 additions & 7 deletions lib/parseComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,30 @@ module.exports = (content, opts) => {
styleLang,
hasVars,
scriptContent,
hasGlobalVars,
globalStyle,
globalStyleLang
} = parseScript(sfc.script)

sfc.script.content = scriptContent

if (sfc.template && hasVars) {
if (sfc.template && (hasVars || hasGlobalVars)) {
sfc.template.content = posthtml([
tree => {
for (const node of tree) {
if (node.tag) {
node.attrs = node.attrs || {}
const existing =
node.attrs[':style'] || node.attrs['v-bind:style']
node.attrs[
':style'
] = `$options._getCssVariables(this, $options, ${existing})`
if (hasVars) {
node.attrs = node.attrs || {}
const existing =
node.attrs[':style'] || node.attrs['v-bind:style']
node.attrs[':style'] = `$options.style(this, ${existing})`
}
if (node.content && hasGlobalVars) {
node.content.unshift({
tag: 'styled-vue-global-css'
})
}
break
}
}
return tree
Expand Down
74 changes: 27 additions & 47 deletions lib/parseScript.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ const LANGS = ['css', 'stylus', 'less', 'sass', 'scss']
module.exports = script => {
let style
let styleLang
let hasVars = false
let globalStyle
let globalStyleLang
let hasVars = false
let hasGlobalVars = false

const ast = parser.parse(script.content, {
sourceType: 'module',
Expand Down Expand Up @@ -40,7 +41,6 @@ module.exports = script => {
type: 'TemplateElement',
value: { raw: value, cooked: value }
})
hasVars = true

// Check unit
let unit
Expand Down Expand Up @@ -140,14 +140,14 @@ module.exports = script => {
continue
}

styleLang = specifier.imported.name
const lang = specifier.imported.name

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

const binding = path.scope.getBinding(specifier.local.name)
let objectExpressionPath

for (let i = 0; i < binding.referencePaths.length; i++) {
// The tagged template path
const ref = binding.referencePaths[i].parentPath
Expand All @@ -164,28 +164,43 @@ module.exports = script => {
)
}

// The object expression path
objectExpressionPath = propertyPath.parentPath

const isGlobal = propertyPath.node.key.name === 'globalStyle'
const { vars, varDeclarations, extractedStyle } = parseTaggedTemplate(
ref,
isGlobal
)
if (isGlobal) {
globalStyle = extractedStyle
globalStyleLang = lang
if (vars.length > 0) {
hasGlobalVars = true
}
} else {
style = extractedStyle
styleLang = lang
if (vars.length > 0) {
hasVars = true
}
}

if (vars.length > 0) {
ref.replaceWith(
t.functionExpression(
null,
[t.identifier('vm'), t.identifier('existing')],
[
t.identifier('vm'),
!isGlobal && t.identifier('existing') // Global vars are handled differently
].filter(Boolean),
t.blockStatement([
...varDeclarations,
t.returnStatement(t.objectExpression(vars))
t.returnStatement(
isGlobal
? t.objectExpression(vars)
: t.arrayExpression([
t.identifier('existing'),
t.objectExpression(vars)
])
)
])
)
)
Expand All @@ -197,42 +212,6 @@ module.exports = script => {
ref.replaceWith(NoVarsFound)
}
}

if (hasVars && objectExpressionPath) {
const createObjectCall = name => {
return t.logicalExpression(
'&&',
t.memberExpression(t.identifier('options'), t.identifier(name)),
t.callExpression(
t.memberExpression(t.identifier('options'), t.identifier(name)),
[t.identifier('vm')]
)
)
}

objectExpressionPath.node.properties.push(
t.objectProperty(
t.identifier('_getCssVariables'),
t.functionExpression(
null,
[
t.identifier('vm'),
t.identifier('options'),
t.identifier('existing')
],
t.blockStatement([
t.returnStatement(
t.arrayExpression([
t.identifier('existing'),
createObjectCall('globalStyle'),
createObjectCall('style')
])
)
])
)
)
)
}
}

// Remove the import
Expand All @@ -245,6 +224,7 @@ module.exports = script => {
styleLang,
globalStyle,
globalStyleLang,
hasGlobalVars,
hasVars,
scriptContent: generator.default(ast).code
}
Expand Down
22 changes: 8 additions & 14 deletions test/__snapshots__/parseComponent.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default {
</script>
<style scoped=\\"undefined\\" lang=\\"undefined\\">
<style scoped=\\"undefined\\" lang=\\"css\\">
h1 {color: red}
</style>
Expand Down Expand Up @@ -73,7 +73,7 @@ export default {
</script>
<style scoped=\\"undefined\\" lang=\\"undefined\\">
<style scoped=\\"undefined\\" lang=\\"css\\">
h1 {color: red}
</style>
Expand Down Expand Up @@ -106,29 +106,26 @@ export default {
<template>
<h1 :style=\\"$options._getCssVariables(this, $options, undefined)\\">hello</h1>
<h1><styled-vue-global-css></styled-vue-global-css>hello</h1>
</template>
<script>
export default {
style: undefined // No CSS variables
,
globalStyle: function (vm, existing) {
globalStyle: function (vm) {
var gv0 = vm => vm.fontSize;
return {
\\"--gv0\\": gv0(vm)
};
},
_getCssVariables: function (vm, options, existing) {
return [existing, options.globalStyle && options.globalStyle(vm), options.style && options.style(vm)];
}
};
</script>
<style scoped=\\"undefined\\" lang=\\"undefined\\">
<style scoped=\\"undefined\\" lang=\\"css\\">
h1 {color: red; font-size: var(--gv0);}
</style>
Expand Down Expand Up @@ -233,7 +230,7 @@ exports[`simple 1`] = `
<template>
<h1 style=\\"foo\\" :style=\\"$options._getCssVariables(this, $options, {})\\">hello</h1>
<h1 style=\\"foo\\" :style=\\"$options.style(this, {})\\">hello</h1>
</template>
Expand All @@ -243,13 +240,10 @@ exports[`simple 1`] = `
var v0 = vm => vm.color;
var v1 = 200 + 1;
return {
return [existing, {
\\"--v0\\": v0(vm),
\\"--v1\\": v1 + \\"px\\"
};
},
_getCssVariables: function (vm, options, existing) {
return [existing, options.globalStyle && options.globalStyle(vm), options.style && options.style(vm)];
}];
}
};
</script>
Expand Down

0 comments on commit 63d5497

Please sign in to comment.