Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/code-snippets/js/utils/Linter.ts

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
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 +