Skip to content

Commit bc7defb

Browse files
authored
Add vue-scoped-css/no-parent-of-v-global rule (#45)
* Add `vue-scoped-css/no-parent-of-v-global` rule * update
1 parent aeb7aed commit bc7defb

File tree

6 files changed

+165
-0
lines changed

6 files changed

+165
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ Enforce all the rules in this category with:
9999
| Rule ID | Description | |
100100
|:--------|:------------|:---|
101101
| [vue-scoped-css/no-deprecated-deep-combinator](https://future-architect.github.io/eslint-plugin-vue-scoped-css/rules/no-deprecated-deep-combinator.html) | disallow using deprecated deep combinators | :wrench: |
102+
| [vue-scoped-css/no-parent-of-v-global](https://future-architect.github.io/eslint-plugin-vue-scoped-css/rules/no-parent-of-v-global.html) | disallow parent selector for `::v-global` pseudo-element | |
102103
| [vue-scoped-css/no-parsing-error](https://future-architect.github.io/eslint-plugin-vue-scoped-css/rules/no-parsing-error.html) | disallow parsing errors in `<style>` | |
103104
| [vue-scoped-css/no-unused-keyframes](https://future-architect.github.io/eslint-plugin-vue-scoped-css/rules/no-unused-keyframes.html) | disallow `@keyframes` which don't use in Scoped CSS | |
104105
| [vue-scoped-css/no-unused-selector](https://future-architect.github.io/eslint-plugin-vue-scoped-css/rules/no-unused-selector.html) | disallow selectors defined in Scoped CSS that don't use in `<template>` | |

docs/rules/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Enforce all the rules in this category with:
1919
| Rule ID | Description | |
2020
|:--------|:------------|:---|
2121
| [vue-scoped-css/no-deprecated-deep-combinator](./no-deprecated-deep-combinator.md) | disallow using deprecated deep combinators | :wrench: |
22+
| [vue-scoped-css/no-parent-of-v-global](./no-parent-of-v-global.md) | disallow parent selector for `::v-global` pseudo-element | |
2223
| [vue-scoped-css/no-parsing-error](./no-parsing-error.md) | disallow parsing errors in `<style>` | |
2324
| [vue-scoped-css/no-unused-keyframes](./no-unused-keyframes.md) | disallow `@keyframes` which don't use in Scoped CSS | |
2425
| [vue-scoped-css/no-unused-selector](./no-unused-selector.md) | disallow selectors defined in Scoped CSS that don't use in `<template>` | |
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
pageClass: "rule-details"
3+
sidebarDepth: 0
4+
title: "vue-scoped-css/no-parent-of-v-global"
5+
description: "disallow parent selector for `::v-global` pseudo-element"
6+
---
7+
# vue-scoped-css/no-parent-of-v-global
8+
9+
> disallow parent selector for `::v-global` pseudo-element
10+
11+
- :gear: This rule is included in `"plugin:vue-scoped-css/vue3-recommended"` and `"plugin:vue-scoped-css/all"`.
12+
13+
## :book: Rule Details
14+
15+
This rule reports parent selector for `::v-global` pseudo-element.
16+
17+
<eslint-code-block :rules="{'vue-scoped-css/no-parent-of-v-global': ['error']}">
18+
19+
```vue
20+
<style scoped>
21+
/* ✗ BAD */
22+
.bar ::v-global(.foo) {}
23+
24+
/* ✓ GOOD */
25+
::v-global(.foo) {}
26+
</style>
27+
```
28+
29+
</eslint-code-block>
30+
31+
## :wrench: Options
32+
33+
Nothing.
34+
35+
## Implementation
36+
37+
- [Rule source](https://github.com/future-architect/eslint-plugin-vue-scoped-css/blob/master/lib/rules/no-parent-of-v-global.ts)
38+
- [Test source](https://github.com/future-architect/eslint-plugin-vue-scoped-css/blob/master/tests/lib/rules/no-parent-of-v-global.js)

lib/rules/no-parent-of-v-global.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import {
2+
getStyleContexts,
3+
getCommentDirectivesReporter,
4+
StyleContext,
5+
ValidStyleContext,
6+
} from "../styles/context"
7+
import type { VCSSSelectorNode } from "../styles/ast"
8+
import type { RuleContext, Rule } from "../types"
9+
import { isVGlobalPseudo } from "../styles/utils/selectors"
10+
11+
declare const module: {
12+
exports: Rule
13+
}
14+
15+
module.exports = {
16+
meta: {
17+
docs: {
18+
description:
19+
"disallow parent selector for `::v-global` pseudo-element",
20+
categories: ["vue3-recommended"],
21+
default: "warn",
22+
url:
23+
"https://future-architect.github.io/eslint-plugin-vue-scoped-css/rules/no-parent-of-v-global.html",
24+
},
25+
fixable: null,
26+
messages: {
27+
unexpected:
28+
"The parent selector of the `::v-global()` pseudo-element is useless.",
29+
},
30+
schema: [],
31+
type: "suggestion", // "problem",
32+
},
33+
create(context: RuleContext) {
34+
const styles = getStyleContexts(context)
35+
.filter(StyleContext.isValid)
36+
.filter((style) => style.scoped)
37+
if (!styles.length) {
38+
return {}
39+
}
40+
const reporter = getCommentDirectivesReporter(context)
41+
42+
/**
43+
* Reports the given node
44+
* @param {ASTNode} node node to report
45+
*/
46+
function report(node: VCSSSelectorNode) {
47+
reporter.report({
48+
node,
49+
loc: node.loc,
50+
messageId: "unexpected",
51+
})
52+
}
53+
54+
/**
55+
* Verify the style
56+
*/
57+
function verify(style: ValidStyleContext) {
58+
style.traverseSelectorNodes({
59+
enterNode(node) {
60+
if (!isVGlobalPseudo(node)) {
61+
return
62+
}
63+
const nodes = node.parent.nodes
64+
const selectorIndex = nodes.indexOf(node)
65+
if (selectorIndex > 0) {
66+
report(node)
67+
}
68+
},
69+
})
70+
}
71+
72+
return {
73+
"Program:exit"() {
74+
for (const style of styles) {
75+
verify(style)
76+
}
77+
},
78+
}
79+
},
80+
}

lib/utils/rules.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ const baseRules = [
66
ruleName: "no-deprecated-deep-combinator",
77
ruleId: "vue-scoped-css/no-deprecated-deep-combinator",
88
},
9+
{
10+
rule: require("../rules/no-parent-of-v-global"),
11+
ruleName: "no-parent-of-v-global",
12+
ruleId: "vue-scoped-css/no-parent-of-v-global",
13+
},
914
{
1015
rule: require("../rules/no-parsing-error"),
1116
ruleName: "no-parsing-error",
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { RuleTester } from "eslint"
2+
import rule = require("../../../lib/rules/no-parent-of-v-global")
3+
4+
const tester = new RuleTester({
5+
parser: require.resolve("vue-eslint-parser"),
6+
parserOptions: {
7+
ecmaVersion: 2019,
8+
sourceType: "module",
9+
},
10+
})
11+
12+
tester.run("no-parent-of-v-global", rule as any, {
13+
valid: [
14+
`
15+
<template><div class="item">sample</div></template>
16+
<style scoped>
17+
::v-global(.foo) {}
18+
</style>
19+
`,
20+
],
21+
invalid: [
22+
{
23+
code: `
24+
<template><div class="item">sample</div></template>
25+
<style scoped>
26+
.bar ::v-global(.foo) {}
27+
</style>
28+
`,
29+
errors: [
30+
{
31+
message:
32+
"The parent selector of the `::v-global()` pseudo-element is useless.",
33+
line: 4,
34+
column: 18,
35+
endColumn: 34,
36+
},
37+
],
38+
},
39+
],
40+
})

0 commit comments

Comments
 (0)