-
-
Notifications
You must be signed in to change notification settings - Fork 82
Expand file tree
/
Copy pathactionview-strict-locals-first-line.ts
More file actions
119 lines (92 loc) · 3.28 KB
/
actionview-strict-locals-first-line.ts
File metadata and controls
119 lines (92 loc) · 3.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { BaseRuleVisitor } from "./rule-utils.js"
import { ParserRule } from "../types.js"
import { createLiteral } from "@herb-tools/core"
import { isERBStrictLocalsNode, isHTMLTextNode } from "@herb-tools/core"
import { isPartialFile } from "./file-utils.js"
import type { ParseResult, DocumentNode, ERBStrictLocalsNode } from "@herb-tools/core"
import type { UnboundLintOffense, LintOffense, LintContext, FullRuleConfig } from "../types.js"
class ActionViewStrictLocalsFirstLineVisitor extends BaseRuleVisitor {
visitDocumentNode(node: DocumentNode) {
const { children } = node
for (let i = 0; i < children.length; i++) {
const child = children[i]
if (!isERBStrictLocalsNode(child)) continue
const next = children[i + 1]
if (!next) break
if (isHTMLTextNode(next)) {
if (!next.content.startsWith("\n\n") && children[i + 2]) {
this.addOffense(
"Add a blank line after the strict locals declaration.",
child.location
)
}
} else {
this.addOffense(
"Add a blank line after the strict locals declaration.",
child.location
)
}
break
}
this.visitChildNodes(node)
}
visitERBStrictLocalsNode(node: ERBStrictLocalsNode): void {
if (isPartialFile(this.context.fileName) !== true) return
if (node.location.start.line !== 1) {
this.addOffense(
"Strict locals declaration must be on the first line of the partial.",
node.location
)
}
}
}
export class ActionViewStrictLocalsFirstLineRule extends ParserRule {
static autocorrectable = true
static ruleName = "actionview-strict-locals-first-line"
static introducedIn = this.version("unreleased")
get parserOptions() {
return { strict_locals: true }
}
get defaultConfig(): FullRuleConfig {
return {
enabled: false,
severity: "error",
}
}
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
if (isPartialFile(context?.fileName) !== true) return []
const visitor = new ActionViewStrictLocalsFirstLineVisitor(this.ruleName, context)
visitor.visit(result.value)
return visitor.offenses
}
autofix(offense: LintOffense, result: ParseResult): ParseResult | null {
const children = result.value.children
const index = children.findIndex(child =>
child.location.start.line === offense.location.start.line &&
child.location.start.column === offense.location.start.column
)
if (index === -1) return null
if (offense.location.start.line === 1) {
const next = children[index + 1]
if (isHTMLTextNode(next)) {
children.splice(index + 1, 1, createLiteral("\n\n"))
} else {
children.splice(index + 1, 0, createLiteral("\n\n"))
}
} else {
const [node] = children.splice(index, 1)
if (index > 0) {
const previous = children[index - 1]
if (isHTMLTextNode(previous) && /^\s*$/.test(previous.content)) {
children.splice(index - 1, 1)
}
}
const firstChild = children[0]
if (!firstChild || !isHTMLTextNode(firstChild) || !firstChild.content.startsWith("\n\n")) {
children.unshift(createLiteral("\n\n"))
}
children.unshift(node)
}
return result
}
}