From 66503ca6780ba2c0855915030372a0bd58bf52ce Mon Sep 17 00:00:00 2001 From: EGOIST <0x142857@gmail.com> Date: Sat, 18 Jan 2020 17:03:50 +0800 Subject: [PATCH 1/3] chore: add note for using global style --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 30a257e..08e8d23 100644 --- a/README.md +++ b/README.md @@ -204,13 +204,15 @@ import { css } from 'styled-vue' export default { globalStyle: css` - body { + #app { color: ${vm => vm.bodyColor}; } ` } ``` +Note CSS variables 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. + ### TypeScript We use Babel to parse your code, so TypeScript should work out-of-the-box, however there're some [caveats](https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats). From af2e8f765a13991a8a33b2aa78a0c00a9e34c662 Mon Sep 17 00:00:00 2001 From: EGOIST <0x142857@gmail.com> Date: Sat, 18 Jan 2020 17:05:01 +0800 Subject: [PATCH 2/3] chore: tweaks --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08e8d23..9435fc9 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,7 @@ export default { } ``` -Note CSS variables 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. +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. ### TypeScript From 63d54978d2600dc404ac3da8e797baf459bd5897 Mon Sep 17 00:00:00 2001 From: EGOIST <0x142857@gmail.com> Date: Sat, 18 Jan 2020 18:18:14 +0800 Subject: [PATCH 3/3] feat: improve global style support --- README.md | 24 +++++- example/App.vue | 2 +- example/index.js | 3 + example/poi.config.js | 2 + lib/index.js | 27 ++++++- lib/parseComponent.js | 21 ++++-- lib/parseScript.js | 74 +++++++------------ .../__snapshots__/parseComponent.test.js.snap | 22 ++---- 8 files changed, 102 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 9435fc9..dbd62ab 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 diff --git a/example/App.vue b/example/App.vue index 65c2c42..0b070fc 100644 --- a/example/App.vue +++ b/example/App.vue @@ -16,7 +16,7 @@ const border = `10px solid pink` export default { globalStyle: css` - #app { + body { border: ${border}; } `, diff --git a/example/index.js b/example/index.js index 385fcfe..aab98c7 100644 --- a/example/index.js +++ b/example/index.js @@ -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) diff --git a/example/poi.config.js b/example/poi.config.js index 75c3bd7..00e1245 100644 --- a/example/poi.config.js +++ b/example/poi.config.js @@ -10,5 +10,7 @@ module.exports = { options.compiler = require('../compiler') return options }) + + config.resolve.alias.set('styled-vue', path.join(__dirname, '../lib')) } } diff --git a/lib/index.js b/lib/index.js index ccae2e7..c93fd28 100644 --- a/lib/index.js +++ b/lib/index.js @@ -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 diff --git a/lib/parseComponent.js b/lib/parseComponent.js index 996f7d6..7bff065 100644 --- a/lib/parseComponent.js +++ b/lib/parseComponent.js @@ -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 diff --git a/lib/parseScript.js b/lib/parseScript.js index 1e8293a..79599f1 100644 --- a/lib/parseScript.js +++ b/lib/parseScript.js @@ -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', @@ -40,7 +41,6 @@ module.exports = script => { type: 'TemplateElement', value: { raw: value, cooked: value } }) - hasVars = true // Check unit let unit @@ -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 @@ -164,9 +164,6 @@ module.exports = script => { ) } - // The object expression path - objectExpressionPath = propertyPath.parentPath - const isGlobal = propertyPath.node.key.name === 'globalStyle' const { vars, varDeclarations, extractedStyle } = parseTaggedTemplate( ref, @@ -174,18 +171,36 @@ module.exports = script => { ) 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) + ]) + ) ]) ) ) @@ -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 @@ -245,6 +224,7 @@ module.exports = script => { styleLang, globalStyle, globalStyleLang, + hasGlobalVars, hasVars, scriptContent: generator.default(ast).code } diff --git a/test/__snapshots__/parseComponent.test.js.snap b/test/__snapshots__/parseComponent.test.js.snap index cb163c6..179d5f2 100644 --- a/test/__snapshots__/parseComponent.test.js.snap +++ b/test/__snapshots__/parseComponent.test.js.snap @@ -31,7 +31,7 @@ export default { - @@ -73,7 +73,7 @@ export default { - @@ -106,7 +106,7 @@ export default { @@ -114,21 +114,18 @@ export default { 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)]; } }; - @@ -233,7 +230,7 @@ exports[`simple 1`] = ` @@ -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)]; + }]; } };