Skip to content

Commit

Permalink
feat: introduce basic global style support
Browse files Browse the repository at this point in the history
  • Loading branch information
egoist committed Jan 18, 2020
1 parent c9d8da0 commit 04eb4b5
Show file tree
Hide file tree
Showing 8 changed files with 1,585 additions and 643 deletions.
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ h1 {
</style>
```

What a trick... It uses [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables) for dynamic styles, that's why this feature is not supported in IE.
Under the hood, it uses [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables) for dynamic styles, that's why this feature is not supported in IE.

### CSS Preprocessors

Expand All @@ -197,7 +197,19 @@ The CSS will be passed to `vue-loader` and parsed by PostCSS if a `postcss.confi

### Global Styles

I think you should not do this, but I'm open for other thoughts, let's discuss it in issue tracker if you want this feature.
Use `globalStyle` instead of `style` on your component:

```js
import { css } from 'styled-vue'

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

### TypeScript

Expand Down
7 changes: 7 additions & 0 deletions example/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@
import { css } from 'styled-vue'
import { modularScale } from 'polished'
const border = `10px solid pink`
export default {
globalStyle: css`
#app {
border: ${border};
}
`,
style: css`
#app {
background-color: ${vm => (vm.theme === 'dark' ? '#000' : '#f0f0f0')};
Expand Down
49 changes: 38 additions & 11 deletions lib/parseComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ module.exports = (content, opts) => {
const sfc = parseComponent(content, opts)

if (sfc.script) {
const { style, styleLang, hasVars, scriptContent } = parseScript(sfc.script)
const {
style,
styleLang,
hasVars,
scriptContent,
globalStyle,
globalStyleLang
} = parseScript(sfc.script)

sfc.script.content = scriptContent

Expand All @@ -18,7 +25,9 @@ module.exports = (content, opts) => {
node.attrs = node.attrs || {}
const existing =
node.attrs[':style'] || node.attrs['v-bind:style']
node.attrs[':style'] = `$options.style(this, ${existing})`
node.attrs[
':style'
] = `$options._getCssVariables(this, $options, ${existing})`
}
}
return tree
Expand All @@ -29,17 +38,35 @@ module.exports = (content, opts) => {
}).html
}

if (style) {
sfc.styles.push({
let contentLength = content.length

const addStyleTag = (styleContent, styleLang, isGlobal) => {
const style = {
type: 'style',
content: style,
attrs: { scoped: true, lang: styleLang },
scoped: true,
lang: styleLang,
content: styleContent,
attrs: {},
// TODO: this might be wrong
start: content.length,
end: content.length + style.length
})
start: contentLength,
end: contentLength + styleContent.length
}
if (styleLang) {
style.lang = styleLang
style.attrs.lang = styleLang
}
if (!isGlobal) {
style.scoped = true
style.attrs.scoped = true
}
sfc.styles.push(style)
contentLength += styleContent.length
}

if (globalStyle) {
addStyleTag(globalStyle, globalStyleLang, true)
}

if (style) {
addStyleTag(style, styleLang, false)
}
}

Expand Down
108 changes: 84 additions & 24 deletions lib/parseScript.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ module.exports = script => {
let style
let styleLang
let hasVars = false
let globalStyle
let globalStyleLang

const ast = parser.parse(script.content, {
sourceType: 'module',
Expand All @@ -23,16 +25,17 @@ module.exports = script => {
]
})

const parseTaggedTemplate = ref => {
const parseTaggedTemplate = (ref, isGlobal) => {
const { quasi } = ref.node
const quasis = []
const vars = []
const varDeclarations = []
const variablePrefix = isGlobal ? 'gv' : `v`

for (const [i, q] of quasi.quasis.entries()) {
quasis.push(q)
if (quasi.expressions[i]) {
const value = `var(--v${i})`
const value = `var(--${variablePrefix}${i})`
quasis.push({
type: 'TemplateElement',
value: { raw: value, cooked: value }
Expand All @@ -55,13 +58,16 @@ module.exports = script => {
// var v0 = vm => vm.color
varDeclarations.push(
t.variableDeclaration('var', [
t.variableDeclarator(t.identifier(`v${i}`), quasi.expressions[i])
t.variableDeclarator(
t.identifier(`${variablePrefix}${i}`),
quasi.expressions[i]
)
])
)

// { '--v0': v0(vm, existing) }
// { '--v0': v0(vm, existing) + unit }
const id = t.identifier(`v${i}`)
// { '--v0': v0(vm) }
// { '--v0': v0(vm) + unit }
const id = t.identifier(`${variablePrefix}${i}`)
const expType = quasi.expressions[i].type
const mustBeFunction = [
'FunctionExpression',
Expand All @@ -73,10 +79,7 @@ module.exports = script => {
const getValue = isFunction => {
let leftExp
if (isFunction) {
leftExp = t.callExpression(id, [
t.identifier('vm'),
t.identifier('existing')
])
leftExp = t.callExpression(id, [t.identifier('vm')])
} else {
leftExp = id
}
Expand All @@ -86,7 +89,7 @@ module.exports = script => {
}
vars.push(
t.objectProperty(
t.stringLiteral(`--v${i}`),
t.stringLiteral(`--${variablePrefix}${i}`),
mustBeFunction
? getValue(true)
: mustNotBeFunction
Expand Down Expand Up @@ -144,37 +147,92 @@ module.exports = script => {
}

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
// The object property path
const propertyPath = ref.parentPath

if (
!propertyPath ||
propertyPath.node.type !== 'ObjectProperty' ||
!['style', 'globalStyle'].includes(propertyPath.node.key.name)
) {
throw new Error(
`css tag must be assigned to component property "style" or "globalStyle"`
)
}

// The object expression path
objectExpressionPath = propertyPath.parentPath

const isGlobal = propertyPath.node.key.name === 'globalStyle'
const { vars, varDeclarations, extractedStyle } = parseTaggedTemplate(
ref
ref,
isGlobal
)
style = extractedStyle
if (isGlobal) {
globalStyle = extractedStyle
} else {
style = extractedStyle
}

if (hasVars) {
if (vars.length > 0) {
ref.replaceWith(
t.functionExpression(
null,
[t.identifier('vm'), t.identifier('existing')],
t.blockStatement([
...varDeclarations,
t.returnStatement(
t.callExpression(
t.memberExpression(
t.identifier('Object'),
t.identifier('assign')
),
[t.objectExpression(vars), t.identifier('existing')]
)
)
t.returnStatement(t.objectExpression(vars))
])
)
)
} else {
ref.replaceWith(t.identifier('undefined'))
const NoVarsFound = t.identifier('undefined')
NoVarsFound.trailingComments = [
{ type: 'CommentLine', value: ' No CSS variables' }
]
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 @@ -185,6 +243,8 @@ module.exports = script => {
return {
style,
styleLang,
globalStyle,
globalStyleLang,
hasVars,
scriptContent: generator.default(ast).code
}
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"test": "npm run lint && jest",
"lint": "xo",
"toc": "markdown-toc README.md -i",
"example": "poi --config example/poi.config.js -so"
"example": "poi --config example/poi.config.js -s"
},
"repository": {
"url": "egoist/styled-vue",
Expand All @@ -27,7 +27,7 @@
"@babel/traverse": "^7.2.3",
"@babel/types": "^7.2.2",
"posthtml": "^0.11.3",
"vue-template-compiler": "^2.5.21"
"vue-template-compiler": "^2.6.11"
},
"devDependencies": {
"commitizen": "^3.0.5",
Expand All @@ -39,11 +39,11 @@
"jest": "^23.6.0",
"lint-staged": "^7.2.0",
"markdown-toc": "^1.2.0",
"poi": "^12.2.14",
"poi": "^12.7.5",
"polished": "^2.3.1",
"prettier": "^1.15.2",
"semantic-release": "^15.13.3",
"vue": "^2.5.21",
"vue": "^2.6.11",
"xo": "^0.23.0"
},
"xo": {
Expand Down
Loading

0 comments on commit 04eb4b5

Please sign in to comment.