Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 23be6e4

Browse files
Update some rules to support style CSS vars injection (#1574)
* Update `vue/no-unused-properties` and `vue/script-setup-uses-vars` rule to support style CSS vars * Add testcase * upgrade vue-eslint-parser * update test * Update no-unsupported-features
1 parent 9dc78d0 commit 23be6e4

File tree

11 files changed

+333
-2
lines changed

11 files changed

+333
-2
lines changed

‎docs/rules/no-unsupported-features.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ The `"ignores"` option accepts an array of the following strings.
3232
- Vue.js 3.1.0+
3333
- `"is-attribute-with-vue-prefix"` ... [`is` attribute with `vue:` prefix](https://v3.vuejs.org/api/special-attributes.html#is)
3434
- Vue.js 3.0.0+
35+
- `"style-css-vars-injection"` ... [SFC CSS variable injection][Vue RFCs - 0043-sfc-style-variables]
3536
- `"script-setup"` ... [`<script setup>`][Vue RFCs - 0040-script-setup]
3637
- `"v-model-argument"` ... [argument on `v-model`][Vue RFCs - 0005-replace-v-bind-sync-with-v-model-argument]
3738
- `"v-model-custom-modifiers"` ... [custom modifiers on `v-model`][Vue RFCs - 0011-v-model-api-change]
@@ -107,6 +108,7 @@ The `"ignores"` option accepts an array of the following strings.
107108
- [Vue RFCs - 0005-replace-v-bind-sync-with-v-model-argument]
108109
- [Vue RFCs - 0011-v-model-api-change]
109110
- [Vue RFCs - 0040-script-setup]
111+
- [Vue RFCs - 0043-sfc-style-variables]
110112
- [Vue RFCs - v-bind .prop shorthand proposal]
111113

112114
[Vue RFCs - 0001-new-slot-syntax]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0001-new-slot-syntax.md
@@ -115,6 +117,7 @@ The `"ignores"` option accepts an array of the following strings.
115117
[Vue RFCs - 0005-replace-v-bind-sync-with-v-model-argument]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0005-replace-v-bind-sync-with-v-model-argument.md
116118
[Vue RFCs - 0011-v-model-api-change]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0011-v-model-api-change.md
117119
[Vue RFCs - 0040-script-setup]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md
120+
[Vue RFCs - 0043-sfc-style-variables]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0043-sfc-style-variables.md
118121

119122
[Vue RFCs - v-bind .prop shorthand proposal]: https://github.com/vuejs/rfcs/pull/18
120123

‎lib/rules/no-unsupported-features.js‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const FEATURES = {
2727
'v-model-custom-modifiers': require('./syntaxes/v-model-custom-modifiers'),
2828
'v-is': require('./syntaxes/v-is'),
2929
'script-setup': require('./syntaxes/script-setup'),
30+
'style-css-vars-injection': require('./syntaxes/style-css-vars-injection'),
3031
// Vue.js 3.1.0+
3132
'is-attribute-with-vue-prefix': require('./syntaxes/is-attribute-with-vue-prefix')
3233
}
@@ -103,6 +104,8 @@ module.exports = {
103104
forbiddenVIs: '`v-is` are not supported until Vue.js "3.0.0".',
104105
forbiddenScriptSetup:
105106
'`<script setup>` are not supported until Vue.js "3.0.0".',
107+
forbiddenStyleCssVarsInjection:
108+
'SFC CSS variable injection is not supported until Vue.js "3.0.3".',
106109
// Vue.js 3.1.0+
107110
forbiddenIsAttributeWithVuePrefix:
108111
'`is="vue:"` are not supported until Vue.js "3.1.0".'

‎lib/rules/no-unused-properties.js‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
const utils = require('../utils')
1212
const eslintUtils = require('eslint-utils')
13+
const { getStyleVariablesContext } = require('../utils/style-variables')
1314

1415
/**
1516
* @typedef {import('../utils').GroupName} GroupName
@@ -1056,6 +1057,15 @@ module.exports = {
10561057
{
10571058
/** @param {Program} node */
10581059
'Program:exit'(node) {
1060+
const styleVars = getStyleVariablesContext(context)
1061+
if (styleVars) {
1062+
for (const { id } of styleVars.references) {
1063+
templatePropertiesContainer.usedProperties.addUsed(
1064+
id.name,
1065+
(context) => extractPatternOrThisProperties(id, context, true)
1066+
)
1067+
}
1068+
}
10591069
if (!node.templateBody) {
10601070
reportUnusedProperties()
10611071
}

‎lib/rules/script-setup-uses-vars.js‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
'use strict'
66

7+
const { getStyleVariablesContext } = require('../utils/style-variables')
78
// ------------------------------------------------------------------------------
89
// Requirements
910
// ------------------------------------------------------------------------------
@@ -112,7 +113,16 @@ module.exports = {
112113
}
113114
}
114115
},
115-
undefined,
116+
{
117+
Program() {
118+
const styleVars = getStyleVariablesContext(context)
119+
if (styleVars) {
120+
for (const ref of styleVars.references) {
121+
context.markVariableAsUsed(ref.id.name)
122+
}
123+
}
124+
}
125+
},
116126
{
117127
templateBodyTriggerSelector: 'Program'
118128
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const { getStyleVariablesContext } = require('../../utils/style-variables')
8+
9+
module.exports = {
10+
supported: '>=3.0.3',
11+
/** @param {RuleContext} context @returns {TemplateListener} */
12+
createScriptVisitor(context) {
13+
const styleVars = getStyleVariablesContext(context)
14+
if (!styleVars) {
15+
return {}
16+
}
17+
return {
18+
Program() {
19+
for (const vBind of styleVars.vBinds) {
20+
context.report({
21+
node: vBind,
22+
messageId: 'forbiddenStyleCssVarsInjection'
23+
})
24+
}
25+
}
26+
}
27+
}
28+
}

‎lib/utils/style-variables/index.js‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const { isVElement } = require('..')
2+
3+
module.exports = {
4+
getStyleVariablesContext
5+
}
6+
7+
class StyleVariablesContext {
8+
/**
9+
* @param {RuleContext} context
10+
* @param {VElement[]} styles
11+
*/
12+
constructor(context, styles) {
13+
this.context = context
14+
this.styles = styles
15+
/** @type {VReference[]} */
16+
this.references = []
17+
/** @type {VExpressionContainer[]} */
18+
this.vBinds = []
19+
for (const style of styles) {
20+
for (const node of style.children) {
21+
if (node.type === 'VExpressionContainer') {
22+
this.vBinds.push(node)
23+
for (const ref of node.references.filter(
24+
(ref) => ref.variable == null
25+
)) {
26+
this.references.push(ref)
27+
}
28+
}
29+
}
30+
}
31+
}
32+
}
33+
34+
/** @type {Map<VElement, StyleVariablesContext} */
35+
const cache = new Map()
36+
/**
37+
* Get the style vars context
38+
* @param {RuleContext} context
39+
* @returns {StyleVariablesContext | null}
40+
*/
41+
function getStyleVariablesContext(context) {
42+
const df =
43+
context.parserServices.getDocumentFragment &&
44+
context.parserServices.getDocumentFragment()
45+
if (!df) {
46+
return null
47+
}
48+
const styles = df.children
49+
.filter(isVElement)
50+
.filter((e) => e.name === 'style')
51+
if (!styles.length) {
52+
return null
53+
}
54+
let ctx = cache.get(styles[0])
55+
if (ctx) {
56+
return ctx
57+
}
58+
ctx = new StyleVariablesContext(context, styles)
59+
cache.set(styles[0], ctx)
60+
return ctx
61+
}

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"eslint-utils": "^2.1.0",
5757
"natural-compare": "^1.4.0",
5858
"semver": "^6.3.0",
59-
"vue-eslint-parser": "^7.8.0"
59+
"vue-eslint-parser": "^7.9.0"
6060
},
6161
"devDependencies": {
6262
"@types/eslint": "^7.2.0",

‎tests/lib/rules/no-parsing-error.js‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,32 @@ tester.run('no-parsing-error', rule, {
615615
{
616616
code: '<template><div xmlns=""></template>',
617617
errors: ['Parsing error: x-invalid-namespace.']
618+
},
619+
620+
//style vars
621+
{
622+
filename: 'test.vue',
623+
code: `
624+
<template></template>
625+
<style>
626+
.text {
627+
color: v-bind(color.);
628+
font-size: v-bind('font size');
629+
}
630+
</style>
631+
`,
632+
errors: [
633+
{
634+
message: 'Parsing error: Unexpected end of expression.',
635+
line: 5,
636+
column: 33
637+
},
638+
{
639+
message: 'Parsing error: Unexpected token size.',
640+
line: 6,
641+
column: 37
642+
}
643+
]
618644
}
619645
]
620646
})
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const RuleTester = require('eslint').RuleTester
8+
const rule = require('../../../../lib/rules/no-unsupported-features')
9+
const utils = require('./utils')
10+
11+
const buildOptions = utils.optionsBuilder('style-css-vars-injection', '^3.0.3')
12+
const tester = new RuleTester({
13+
parser: require.resolve('vue-eslint-parser'),
14+
parserOptions: {
15+
ecmaVersion: 2019,
16+
sourceType: 'module'
17+
}
18+
})
19+
20+
tester.run('no-unsupported-features/style-css-vars-injection', rule, {
21+
valid: [
22+
{
23+
code: `
24+
<template>
25+
<div class="text">hello</div>
26+
</template>
27+
28+
<script>
29+
export default {
30+
data() {
31+
return {
32+
color: 'red',
33+
font: {
34+
size: '2em',
35+
},
36+
}
37+
},
38+
}
39+
</script>
40+
41+
<style>
42+
.text {
43+
color: v-bind(color);
44+
45+
/* expressions (wrap in quotes) */
46+
font-size: v-bind('font.size');
47+
}
48+
</style>`,
49+
options: buildOptions()
50+
},
51+
{
52+
code: `
53+
<template>
54+
<div class="text">hello</div>
55+
</template>
56+
57+
<script>
58+
</script>
59+
60+
<style>
61+
.text {
62+
color: red;
63+
font-size: 2em;
64+
}
65+
</style>`,
66+
options: buildOptions({ version: '^2.6.0' })
67+
}
68+
],
69+
invalid: [
70+
{
71+
code: `
72+
<template>
73+
<div class="text">hello</div>
74+
</template>
75+
76+
<script>
77+
export default {
78+
data() {
79+
return {
80+
color: 'red',
81+
font: {
82+
size: '2em',
83+
},
84+
}
85+
},
86+
}
87+
</script>
88+
89+
<style>
90+
.text {
91+
color: v-bind(color);
92+
93+
/* expressions (wrap in quotes) */
94+
font-size: v-bind('font.size');
95+
}
96+
</style>`,
97+
options: buildOptions({ version: '^3.0.0' }),
98+
errors: [
99+
{
100+
message:
101+
'SFC CSS variable injection is not supported until Vue.js "3.0.3".',
102+
line: 21,
103+
column: 18,
104+
endLine: 21,
105+
endColumn: 31
106+
},
107+
{
108+
message:
109+
'SFC CSS variable injection is not supported until Vue.js "3.0.3".',
110+
line: 24,
111+
column: 22,
112+
endLine: 24,
113+
endColumn: 41
114+
}
115+
]
116+
}
117+
]
118+
})

‎tests/lib/rules/no-unused-properties.js‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,38 @@ tester.run('no-unused-properties', rule, {
15621562
}
15631563
</script>`,
15641564
options: allOptions
1565+
},
1566+
1567+
//style vars
1568+
{
1569+
filename: 'test.vue',
1570+
code: `
1571+
<template>
1572+
<div class="text">hello</div>
1573+
</template>
1574+
1575+
<script>
1576+
export default {
1577+
data() {
1578+
return {
1579+
color: 'red',
1580+
font: {
1581+
size: '2em',
1582+
},
1583+
}
1584+
},
1585+
}
1586+
</script>
1587+
1588+
<style>
1589+
.text {
1590+
color: v-bind(color);
1591+
1592+
/* expressions (wrap in quotes) */
1593+
font-size: v-bind('font.size');
1594+
}
1595+
</style>
1596+
`
15651597
}
15661598
],
15671599

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /