Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/code-snippets/js/utils/Linter.ts
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
/**
2
+
* Based on work licensed under the BSD 3-Clause license.
3
+
*
4
+
* Copyright (c) 2017, glayzzle
5
+
* All rights reserved.
6
+
*
7
+
* Redistribution and use in source and binary forms, with or without
8
+
* modification, are permitted provided that the following conditions are met:
9
+
*
10
+
* * Redistributions of source code must retain the above copyright notice, this
11
+
* list of conditions and the following disclaimer.
12
+
*
13
+
* * Redistributions in binary form must reproduce the above copyright notice,
14
+
* this list of conditions and the following disclaimer in the documentation
15
+
* and/or other materials provided with the distribution.
16
+
*
17
+
* * Neither the name of the copyright holder nor the names of its
18
+
* contributors may be used to endorse or promote products derived from
19
+
* this software without specific prior written permission.
20
+
*
21
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+
*/
32
+
33
+
import { Engine } from 'php-parser'
34
+
import CodeMirror from 'codemirror'
35
+
import type { Block, Location, Node } from 'php-parser'
36
+
37
+
export interface Annotation {
38
+
message: string
39
+
severity: string
40
+
from: CodeMirror.Position
41
+
to: CodeMirror.Position
42
+
}
43
+
44
+
export interface Identifier extends Node {
45
+
name: string
46
+
}
47
+
48
+
export interface Declaration extends Node {
49
+
name: Identifier | string
50
+
}
51
+
52
+
export class Linter {
53
+
private readonly code: string
54
+
55
+
private readonly function_names: Set<string>
56
+
57
+
private readonly class_names: Set<string>
58
+
59
+
public readonly annotations: Annotation[]
60
+
61
+
/**
62
+
* Constructor.
63
+
* @param code
64
+
*/
65
+
constructor(code: string) {
66
+
this.code = code
67
+
this.annotations = []
68
+
69
+
this.function_names = new Set()
70
+
this.class_names = new Set()
71
+
}
72
+
73
+
/**
74
+
* Lint the provided code.
75
+
*/
76
+
lint() {
77
+
const parser = new Engine({
78
+
parser: {
79
+
suppressErrors: true,
80
+
version: 800
81
+
},
82
+
ast: {
83
+
withPositions: true
84
+
}
85
+
})
86
+
87
+
try {
88
+
const program = parser.parseEval(this.code)
89
+
90
+
if (0 < program.errors.length) {
91
+
for (const error of program.errors) {
92
+
this.annotate(error.message, error.loc)
93
+
}
94
+
}
95
+
96
+
this.visit(program)
97
+
} catch (error) {
98
+
console.error(error)
99
+
}
100
+
}
101
+
102
+
/**
103
+
* Visit nodes recursively.
104
+
* @param node
105
+
*/
106
+
visit(node: Node) {
107
+
if (node.kind) {
108
+
this.validate(node)
109
+
}
110
+
111
+
if ('children' in node) {
112
+
const block = <Block> node
113
+
for (const child of block.children) {
114
+
this.visit(child)
115
+
}
116
+
}
117
+
}
118
+
119
+
/**
120
+
* Check whether a given identifier has already been defined, creating an annotation if so.
121
+
* @param identifier
122
+
* @param registry
123
+
* @param label
124
+
*/
125
+
checkDuplicateIdentifier(identifier: Identifier, registry: Set<string>, label: string) {
126
+
if (registry.has(identifier.name)) {
127
+
this.annotate(`Cannot redeclare ${label} ${identifier.name}()`, identifier.loc)
128
+
} else {
129
+
registry.add(identifier.name)
130
+
}
131
+
}
132
+
133
+
/**
134
+
* Perform additional validations on nodes.
135
+
* @param node
136
+
*/
137
+
validate(node: Node) {
138
+
const decl = <Declaration> node
139
+
const ident = <Identifier> decl.name
140
+
141
+
if (!('name' in decl && 'name' in ident) || 'identifier' !== ident.kind) {
142
+
return
143
+
}
144
+
145
+
if ('function' === node.kind) {
146
+
this.checkDuplicateIdentifier(ident, this.function_names, 'function')
147
+
} else if ('class' === node.kind) {
148
+
this.checkDuplicateIdentifier(ident, this.class_names, 'class')
149
+
}
150
+
}
151
+
152
+
/**
153
+
* Create a lint annotation.
154
+
* @param message
155
+
* @param location
156
+
* @param severity
157
+
*/
158
+
annotate(message: string, location: Location | null, severity = 'error') {
159
+
const [start, end] = location
160
+
? location.end.offset < location.start.offset ? [location.end, location.start] : [location.start, location.end]
161
+
: [{ line: 0, column: 0 }, { line: 0, column: 0 }]
162
+
163
+
this.annotations.push({
164
+
message,
165
+
severity,
166
+
from: CodeMirror.Pos(start.line - 1, start.column),
167
+
to: CodeMirror.Pos(end.line - 1, end.column)
168
+
})
169
+
}
170
+
}
171
+