Skip to content

Commit 087e924

Browse files
authored
improve vue-scoped-css/no-parsing-error (#13)
1 parent 0cedee7 commit 087e924

File tree

6 files changed

+159
-23
lines changed

6 files changed

+159
-23
lines changed

docs/.vuepress/components/rules/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,11 @@ categories.sort((a, b) =>
104104
)
105105

106106
export const DEFAULT_RULES_CONFIG = allRules.reduce((c, r) => {
107-
c[r.ruleId] = r.initChecked ? "error" : "off"
107+
if (r.ruleId === "vue/no-parsing-error") {
108+
c[r.ruleId] = "error"
109+
} else {
110+
c[r.ruleId] = r.initChecked ? "error" : "off"
111+
}
108112
return c
109113
}, {})
110114

lib/rules/no-parsing-error.ts

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
import { getStyleContexts, getCommentDirectivesReporter } from "../styles"
2-
import { RuleContext } from "../types"
1+
import {
2+
getStyleContexts,
3+
getCommentDirectivesReporter,
4+
StyleContext,
5+
} from "../styles"
6+
import { RuleContext, LineAndColumnData } from "../types"
37
import { VCSSParsingError } from "../styles/ast"
48

59
module.exports = {
@@ -17,7 +21,7 @@ module.exports = {
1721
type: "problem",
1822
},
1923
create(context: RuleContext) {
20-
const styles = getStyleContexts(context).filter(style => !style.invalid)
24+
const styles = getStyleContexts(context)
2125
if (!styles.length) {
2226
return {}
2327
}
@@ -40,11 +44,48 @@ module.exports = {
4044
})
4145
}
4246

47+
/**
48+
* Reports the given style
49+
* @param {ASTNode} node node to report
50+
*/
51+
function reportInvalidStyle(
52+
style: StyleContext & {
53+
invalid: {
54+
message: string
55+
needReport: boolean
56+
loc: LineAndColumnData
57+
}
58+
},
59+
) {
60+
reporter.report({
61+
node: style.styleElement,
62+
loc: style.invalid.loc,
63+
message: "Parsing error: {{message}}.",
64+
data: {
65+
message: style.invalid.message,
66+
},
67+
})
68+
}
69+
4370
return {
4471
"Program:exit"() {
4572
for (const style of styles) {
46-
for (const node of style.cssNode?.errors || []) {
47-
report(node)
73+
if (style.invalid != null) {
74+
if (style.invalid.needReport) {
75+
reportInvalidStyle(
76+
style as StyleContext & {
77+
invalid: {
78+
message: string
79+
needReport: boolean
80+
loc: LineAndColumnData
81+
}
82+
},
83+
)
84+
}
85+
} else {
86+
for (const node of style.cssNode?.errors || []) {
87+
report(node)
88+
}
4889
}
4990
}
5091
},

lib/styles/context/style/index.ts

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,51 @@
11
import { parse } from "../../parser"
2-
import { AST, SourceCode, RuleContext } from "../../../types"
2+
import { AST, SourceCode, RuleContext, LineAndColumnData } from "../../../types"
33
import { VCSSStyleSheet, VCSSNode } from "../../ast"
44
import { isVCSSContainerNode } from "../../utils/css-nodes"
55

66
/**
7-
* Check whether the templateBody of the program has invalid EOF or not.
8-
* @param {Program} node the program node to check.
9-
* @returns {boolean} `true` if it has invalid EOF.
7+
* Check whether the program has invalid EOF or not.
108
*/
11-
function hasInvalidEOF(node: AST.ESLintProgram) {
9+
function getInvalidEOFError(
10+
context: RuleContext,
11+
style: AST.VElement,
12+
): {
13+
inDocumentFragment: boolean
14+
error: AST.ParseError
15+
} | null {
16+
const node = context.getSourceCode().ast
1217
const body = node.templateBody
13-
if (body?.errors == null) {
14-
return false
18+
let errors = body?.errors
19+
let inDocumentFragment = false
20+
if (errors == null) {
21+
if (!context.parserServices.getDocumentFragment) {
22+
return null
23+
}
24+
const df = context.parserServices.getDocumentFragment()
25+
inDocumentFragment = true
26+
errors = df?.errors
27+
if (errors == null) {
28+
return null
29+
}
30+
}
31+
const error =
32+
errors.find(
33+
err =>
34+
typeof err.code === "string" &&
35+
err.code.startsWith("eof-") &&
36+
style.range[0] <= err.index &&
37+
err.index < style.range[1],
38+
) ||
39+
errors.find(
40+
err => typeof err.code === "string" && err.code.startsWith("eof-"),
41+
)
42+
if (!error) {
43+
return null
44+
}
45+
return {
46+
error,
47+
inDocumentFragment,
1548
}
16-
return body.errors.some(
17-
error =>
18-
typeof error.code === "string" && error.code.startsWith("eof-"),
19-
)
2049
}
2150

2251
/**
@@ -84,17 +113,36 @@ interface Visitor {
84113
export class StyleContext {
85114
public readonly styleElement: AST.VElement
86115
public readonly sourceCode: SourceCode
87-
public readonly invalid: boolean
116+
public readonly invalid: {
117+
message: string
118+
needReport: boolean
119+
loc: LineAndColumnData
120+
} | null
88121
public readonly scoped: boolean
89122
public readonly lang: string
90123
private readonly cssText: string | null
91124
public readonly cssNode: VCSSStyleSheet | null
92-
public constructor(style: AST.VElement, sourceCode: SourceCode) {
125+
public constructor(style: AST.VElement, context: RuleContext) {
126+
const sourceCode = context.getSourceCode()
93127
this.styleElement = style
94128
this.sourceCode = sourceCode
95129

96130
const { startTag, endTag } = style
97-
this.invalid = endTag == null || hasInvalidEOF(sourceCode.ast)
131+
this.invalid = null
132+
const eof = getInvalidEOFError(context, style)
133+
if (eof) {
134+
this.invalid = {
135+
message: eof.error.message,
136+
needReport: eof.inDocumentFragment,
137+
loc: { line: eof.error.lineNumber, column: eof.error.column },
138+
}
139+
} else if (endTag == null) {
140+
this.invalid = {
141+
message: "Missing end tag",
142+
needReport: true,
143+
loc: startTag.loc.end,
144+
}
145+
}
98146

99147
this.scoped = Boolean(style && isScoped(style))
100148

@@ -157,10 +205,9 @@ function traverseNodes(node: VCSSNode, visitor: Visitor): void {
157205
* @returns {StyleContext[]} the style contexts
158206
*/
159207
export function createStyleContexts(context: RuleContext): StyleContext[] {
160-
const sourceCode = context.getSourceCode()
161208
const styles = getStyleElements(context)
162209

163-
return styles.map(style => new StyleContext(style, sourceCode))
210+
return styles.map(style => new StyleContext(style, context))
164211
}
165212

166213
/**

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "eslint-plugin-vue-scoped-css",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "ESLint plugin for Scoped CSS in Vue.js",
55
"main": "dist/index.js",
66
"scripts": {
@@ -53,6 +53,7 @@
5353
"@types/estree": "0.0.39",
5454
"@types/lodash": "^4.14.147",
5555
"@types/mocha": "^5.2.7",
56+
"@types/semver": "^6.2.0",
5657
"babel-eslint": "^10.0.3",
5758
"cpx": "^1.5.0",
5859
"cross-env": "^6.0.3",
@@ -65,6 +66,7 @@
6566
"pack": "^2.2.0",
6667
"raw-loader": "^3.1.0",
6768
"rimraf": "^3.0.0",
69+
"semver": "^6.3.0",
6870
"ts-node": "^8.5.2",
6971
"typescript": "^3.7.2",
7072
"vue-eslint-editor": "^0.1.4",

tests/lib/rules/no-parsing-error.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { RuleTester } from "eslint"
2+
import semver from "semver"
23
const rule = require("../../../lib/rules/no-parsing-error")
4+
const parserVersion = require("vue-eslint-parser/package.json").version
35

46
const tester = new RuleTester({
57
parser: require.resolve("vue-eslint-parser"),
@@ -17,6 +19,9 @@ tester.run("no-parsing-error", rule, {
1719
.item {}
1820
</style>
1921
`,
22+
`
23+
<template></template>
24+
`,
2025
],
2126
invalid: [
2227
{
@@ -34,5 +39,36 @@ tester.run("no-parsing-error", rule, {
3439
},
3540
],
3641
},
42+
...(semver.satisfies(parserVersion, ">=7.0.0")
43+
? [
44+
{
45+
code: `
46+
<style scoped>
47+
.item {
48+
`,
49+
errors: [
50+
{
51+
message: "Parsing error: Missing end tag.",
52+
line: 2,
53+
column: 27,
54+
},
55+
],
56+
},
57+
{
58+
code: `
59+
<style scoped>
60+
.item {
61+
</style>
62+
<doc></doc`,
63+
errors: [
64+
{
65+
message: "Parsing error: eof-in-tag.",
66+
line: 5,
67+
column: 23,
68+
},
69+
],
70+
},
71+
]
72+
: []),
3773
],
3874
})

0 commit comments

Comments
 (0)