-
-
Notifications
You must be signed in to change notification settings - Fork 80
Expand file tree
/
Copy patherb-no-instance-variables-in-partials.ts
More file actions
101 lines (78 loc) · 2.95 KB
/
erb-no-instance-variables-in-partials.ts
File metadata and controls
101 lines (78 loc) · 2.95 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
import { PrismVisitor, PrismNodes } from "@herb-tools/core"
import { ParserRule } from "../types.js"
import { isPartialFile } from "./file-utils.js"
import { locationFromOffset } from "./rule-utils.js"
import type { ParseResult, ParserOptions, PrismLocation } from "@herb-tools/core"
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
interface InstanceVariableNode {
name: string
location: PrismLocation
}
type InstanceVariableUsage = "read" | "write"
interface InstanceVariableReference {
name: string
usage: InstanceVariableUsage
startOffset: number
length: number
}
class InstanceVariableCollector extends PrismVisitor {
public readonly instanceVariables: InstanceVariableReference[] = []
visitInstanceVariableReadNode(node: PrismNodes.InstanceVariableReadNode): void {
this.collect(node, "read")
}
visitInstanceVariableWriteNode(node: PrismNodes.InstanceVariableWriteNode): void {
this.collect(node, "write")
}
visitInstanceVariableAndWriteNode(node: PrismNodes.InstanceVariableAndWriteNode): void {
this.collect(node, "write")
}
visitInstanceVariableOrWriteNode(node: PrismNodes.InstanceVariableOrWriteNode): void {
this.collect(node, "write")
}
visitInstanceVariableOperatorWriteNode(node: PrismNodes.InstanceVariableOperatorWriteNode): void {
this.collect(node, "write")
}
visitInstanceVariableTargetNode(node: PrismNodes.InstanceVariableTargetNode): void {
this.collect(node, "write")
}
private collect(node: InstanceVariableNode, usage: InstanceVariableUsage): void {
this.instanceVariables.push({
name: node.name,
usage,
startOffset: node.location.startOffset,
length: node.location.length
})
}
}
export class ERBNoInstanceVariablesInPartialsRule extends ParserRule {
static ruleName = "erb-no-instance-variables-in-partials"
get defaultConfig(): FullRuleConfig {
return {
enabled: true,
severity: "error",
}
}
get parserOptions(): Partial<ParserOptions> {
return {
track_whitespace: true,
prism_program: true,
}
}
isEnabled(_result: ParseResult, context?: Partial<LintContext>): boolean {
return isPartialFile(context?.fileName) === true
}
check(result: ParseResult, _context?: Partial<LintContext>): UnboundLintOffense[] {
const source = result.value.source
const prismNode = result.value.prismNode
if (!prismNode || !source) return []
const collector = new InstanceVariableCollector()
collector.visit(prismNode)
return collector.instanceVariables.map(ivar => {
const location = locationFromOffset(source, ivar.startOffset, ivar.length)
const message = ivar.usage === "read"
? `Avoid using instance variables in partials. Pass \`${ivar.name}\` as a local variable instead.`
: `Avoid setting instance variables in partials. Use a local variable instead of \`${ivar.name}\`.`
return this.createOffense(message, location)
})
}
}