@frozenproductions/niteo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +458 -0
- package/Cargo.toml +13 -0
- package/LICENSE +21 -0
- package/README.md +119 -0
- package/bin/niteo.js +51 -0
- package/package.json +27 -0
- package/scripts/build-binary.js +23 -0
- package/src/app.rs +132 -0
- package/src/cli.rs +43 -0
- package/src/config.rs +989 -0
- package/src/discovery.rs +42 -0
- package/src/git.rs +63 -0
- package/src/main.rs +13 -0
- package/src/report.rs +490 -0
- package/src/rules/max_directory_depth.rs +125 -0
- package/src/rules/max_file_exports.rs +478 -0
- package/src/rules/max_items_per_directory.rs +145 -0
- package/src/rules/min_items_per_directory.rs +146 -0
- package/src/rules/no_barrel_files.rs +284 -0
- package/src/rules/no_comments.rs +222 -0
- package/src/rules/no_console.rs +242 -0
- package/src/rules/no_debugger.rs +209 -0
- package/src/rules/no_default_export.rs +241 -0
- package/src/rules/no_duplicate_file_names.rs +196 -0
- package/src/rules/no_empty_directories.rs +467 -0
- package/src/rules/no_empty_interface.rs +280 -0
- package/src/rules/no_enums.rs +208 -0
- package/src/rules/no_eval.rs +268 -0
- package/src/rules/no_export_star.rs +252 -0
- package/src/rules/no_inline_types.rs +367 -0
- package/src/rules/no_interface.rs +570 -0
- package/src/rules/no_large_file.rs +98 -0
- package/src/rules/no_logic_in_barrel.rs +346 -0
- package/src/rules/no_logic_in_domain.rs +987 -0
- package/src/rules/no_mutable_exports.rs +253 -0
- package/src/rules/no_upward_import.rs +427 -0
- package/src/rules/prefer_satisfies.rs +319 -0
- package/src/rules.rs +247 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
use std::path::Path;
|
|
2
|
+
|
|
3
|
+
use crate::config::{RuleConfig, Severity};
|
|
4
|
+
use crate::rules::Violation;
|
|
5
|
+
|
|
6
|
+
const RULE_NAME: &str = "no-empty-interface";
|
|
7
|
+
const MESSAGE: &str = "Use a type alias instead of an empty interface.";
|
|
8
|
+
|
|
9
|
+
pub fn check_file(file: &Path, source: &str, config: &RuleConfig) -> Vec<Violation> {
|
|
10
|
+
let bytes = source.as_bytes();
|
|
11
|
+
let mut violations = Vec::new();
|
|
12
|
+
let mut cursor = Cursor::default();
|
|
13
|
+
let mut string_quote: Option<u8> = None;
|
|
14
|
+
|
|
15
|
+
while cursor.index < bytes.len() {
|
|
16
|
+
let current = bytes[cursor.index];
|
|
17
|
+
let next = bytes.get(cursor.index + 1).copied();
|
|
18
|
+
|
|
19
|
+
if let Some(quote) = string_quote {
|
|
20
|
+
if current == b'\\' {
|
|
21
|
+
cursor.advance(bytes);
|
|
22
|
+
if cursor.index < bytes.len() {
|
|
23
|
+
cursor.advance(bytes);
|
|
24
|
+
}
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if current == quote {
|
|
29
|
+
string_quote = None;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
cursor.advance(bytes);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
match (current, next) {
|
|
37
|
+
(b'\'', _) | (b'"', _) | (b'`', _) => {
|
|
38
|
+
string_quote = Some(current);
|
|
39
|
+
cursor.advance(bytes);
|
|
40
|
+
}
|
|
41
|
+
(b'/', Some(b'/')) => skip_line_comment(bytes, &mut cursor),
|
|
42
|
+
(b'/', Some(b'*')) => skip_block_comment(bytes, &mut cursor),
|
|
43
|
+
_ if starts_interface(bytes, cursor.index) => {
|
|
44
|
+
let saved_cursor = cursor;
|
|
45
|
+
cursor.index += b"interface".len();
|
|
46
|
+
cursor.column += b"interface".len();
|
|
47
|
+
|
|
48
|
+
if is_empty_interface(bytes, &mut cursor) {
|
|
49
|
+
violations.push(interface_violation(file, &saved_cursor, config.severity));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
_ => cursor.advance(bytes),
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
violations
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#[derive(Debug, Clone, Copy)]
|
|
60
|
+
struct Cursor {
|
|
61
|
+
index: usize,
|
|
62
|
+
line: usize,
|
|
63
|
+
column: usize,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
impl Default for Cursor {
|
|
67
|
+
fn default() -> Self {
|
|
68
|
+
Self {
|
|
69
|
+
index: 0,
|
|
70
|
+
line: 1,
|
|
71
|
+
column: 1,
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
impl Cursor {
|
|
77
|
+
fn advance(&mut self, bytes: &[u8]) {
|
|
78
|
+
if bytes[self.index] == b'\n' {
|
|
79
|
+
self.line += 1;
|
|
80
|
+
self.column = 1;
|
|
81
|
+
} else {
|
|
82
|
+
self.column += 1;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
self.index += 1;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
fn is_empty_interface(bytes: &[u8], cursor: &mut Cursor) -> bool {
|
|
90
|
+
skip_whitespace_and_comments(bytes, cursor);
|
|
91
|
+
|
|
92
|
+
if !is_identifier_byte(bytes.get(cursor.index).copied()) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
while cursor.index < bytes.len() && is_identifier_byte(bytes.get(cursor.index).copied()) {
|
|
97
|
+
cursor.advance(bytes);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
skip_whitespace_and_comments(bytes, cursor);
|
|
101
|
+
|
|
102
|
+
if bytes.get(cursor.index) != Some(&b'{') {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
cursor.advance(bytes);
|
|
106
|
+
|
|
107
|
+
skip_whitespace_and_comments(bytes, cursor);
|
|
108
|
+
|
|
109
|
+
if bytes.get(cursor.index) != Some(&b'}') {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fn skip_whitespace_and_comments(bytes: &[u8], cursor: &mut Cursor) {
|
|
117
|
+
loop {
|
|
118
|
+
while cursor.index < bytes.len() && bytes[cursor.index].is_ascii_whitespace() {
|
|
119
|
+
cursor.advance(bytes);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if cursor.index >= bytes.len() {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if bytes.get(cursor.index) == Some(&b'/') {
|
|
127
|
+
match bytes.get(cursor.index + 1) {
|
|
128
|
+
Some(b'/') => skip_line_comment(bytes, cursor),
|
|
129
|
+
Some(b'*') => skip_block_comment(bytes, cursor),
|
|
130
|
+
_ => break,
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fn interface_violation(file: &Path, cursor: &Cursor, severity: Severity) -> Violation {
|
|
139
|
+
Violation {
|
|
140
|
+
file: file.to_path_buf(),
|
|
141
|
+
line: Some(cursor.line),
|
|
142
|
+
column: Some(cursor.column),
|
|
143
|
+
rule: RULE_NAME,
|
|
144
|
+
message: MESSAGE,
|
|
145
|
+
severity,
|
|
146
|
+
detail: None,
|
|
147
|
+
subject: None,
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fn starts_interface(bytes: &[u8], index: usize) -> bool {
|
|
152
|
+
starts_keyword(bytes, index, b"interface")
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
fn starts_keyword(bytes: &[u8], index: usize, keyword: &[u8]) -> bool {
|
|
156
|
+
bytes.get(index..index + keyword.len()) == Some(keyword)
|
|
157
|
+
&& !is_identifier_byte(bytes.get(index.wrapping_sub(1)).copied())
|
|
158
|
+
&& !is_identifier_byte(bytes.get(index + keyword.len()).copied())
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fn is_identifier_byte(byte: Option<u8>) -> bool {
|
|
162
|
+
matches!(
|
|
163
|
+
byte,
|
|
164
|
+
Some(b'a'..=b'z') | Some(b'A'..=b'Z') | Some(b'0'..=b'9') | Some(b'_') | Some(b'$')
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
fn skip_line_comment(bytes: &[u8], cursor: &mut Cursor) {
|
|
169
|
+
while cursor.index < bytes.len() && bytes[cursor.index] != b'\n' {
|
|
170
|
+
cursor.advance(bytes);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
fn skip_block_comment(bytes: &[u8], cursor: &mut Cursor) {
|
|
175
|
+
cursor.advance(bytes);
|
|
176
|
+
cursor.advance(bytes);
|
|
177
|
+
|
|
178
|
+
while cursor.index < bytes.len() {
|
|
179
|
+
let current = bytes[cursor.index];
|
|
180
|
+
let next = bytes.get(cursor.index + 1).copied();
|
|
181
|
+
|
|
182
|
+
cursor.advance(bytes);
|
|
183
|
+
|
|
184
|
+
if current == b'*' && next == Some(b'/') {
|
|
185
|
+
cursor.advance(bytes);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#[cfg(test)]
|
|
192
|
+
mod tests {
|
|
193
|
+
use super::check_file;
|
|
194
|
+
use crate::config::{RuleConfig, Severity};
|
|
195
|
+
use std::path::Path;
|
|
196
|
+
|
|
197
|
+
#[test]
|
|
198
|
+
fn reports_empty_interface() {
|
|
199
|
+
let violations = check_file(
|
|
200
|
+
Path::new("types.ts"),
|
|
201
|
+
"interface Empty {}\n",
|
|
202
|
+
&test_config(),
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
assert_eq!(violations.len(), 1);
|
|
206
|
+
assert_eq!(violations[0].line, Some(1));
|
|
207
|
+
assert_eq!(violations[0].column, Some(1));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
#[test]
|
|
211
|
+
fn reports_empty_interface_with_newline() {
|
|
212
|
+
let source = "interface Empty {\n}\n";
|
|
213
|
+
let violations = check_file(Path::new("types.ts"), source, &test_config());
|
|
214
|
+
|
|
215
|
+
assert_eq!(violations.len(), 1);
|
|
216
|
+
assert_eq!(violations[0].line, Some(1));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
#[test]
|
|
220
|
+
fn reports_empty_interface_with_whitespace() {
|
|
221
|
+
let source = "interface Empty { }\n";
|
|
222
|
+
let violations = check_file(Path::new("types.ts"), source, &test_config());
|
|
223
|
+
|
|
224
|
+
assert_eq!(violations.len(), 1);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
#[test]
|
|
228
|
+
fn reports_multiple_empty_interfaces() {
|
|
229
|
+
let source = r#"interface A {}
|
|
230
|
+
interface B {}
|
|
231
|
+
"#;
|
|
232
|
+
let violations = check_file(Path::new("types.ts"), source, &test_config());
|
|
233
|
+
|
|
234
|
+
assert_eq!(violations.len(), 2);
|
|
235
|
+
assert_eq!(violations[0].line, Some(1));
|
|
236
|
+
assert_eq!(violations[1].line, Some(2));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
#[test]
|
|
240
|
+
fn allows_interface_with_members() {
|
|
241
|
+
let source = "interface User { name: string }\n";
|
|
242
|
+
let violations = check_file(Path::new("types.ts"), source, &test_config());
|
|
243
|
+
|
|
244
|
+
assert!(violations.is_empty());
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
#[test]
|
|
248
|
+
fn allows_interface_with_comment_in_body() {
|
|
249
|
+
let source = "interface User { /* todo */ name: string }\n";
|
|
250
|
+
let violations = check_file(Path::new("types.ts"), source, &test_config());
|
|
251
|
+
|
|
252
|
+
assert!(violations.is_empty());
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
#[test]
|
|
256
|
+
fn ignores_interface_in_comments_and_strings() {
|
|
257
|
+
let source = r#"// interface Empty {}
|
|
258
|
+
const text = "interface Empty {}";
|
|
259
|
+
/* interface Empty {} */
|
|
260
|
+
"#;
|
|
261
|
+
let violations = check_file(Path::new("types.ts"), source, &test_config());
|
|
262
|
+
|
|
263
|
+
assert!(violations.is_empty());
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
#[test]
|
|
267
|
+
fn does_not_match_identifier_fragments() {
|
|
268
|
+
let source = r#"const interfacex = true;
|
|
269
|
+
"#;
|
|
270
|
+
let violations = check_file(Path::new("types.ts"), source, &test_config());
|
|
271
|
+
|
|
272
|
+
assert!(violations.is_empty());
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
fn test_config() -> RuleConfig {
|
|
276
|
+
RuleConfig {
|
|
277
|
+
severity: Severity::Warn,
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
use std::path::Path;
|
|
2
|
+
|
|
3
|
+
use crate::config::{RuleConfig, Severity};
|
|
4
|
+
use crate::rules::Violation;
|
|
5
|
+
|
|
6
|
+
const RULE_NAME: &str = "no-enums";
|
|
7
|
+
const MESSAGE: &str = "Use union types or const objects instead of enums.";
|
|
8
|
+
|
|
9
|
+
pub fn check_file(file: &Path, source: &str, config: &RuleConfig) -> Vec<Violation> {
|
|
10
|
+
let bytes = source.as_bytes();
|
|
11
|
+
let mut violations = Vec::new();
|
|
12
|
+
let mut cursor = Cursor::default();
|
|
13
|
+
let mut string_quote: Option<u8> = None;
|
|
14
|
+
|
|
15
|
+
while cursor.index < bytes.len() {
|
|
16
|
+
let current = bytes[cursor.index];
|
|
17
|
+
let next = bytes.get(cursor.index + 1).copied();
|
|
18
|
+
|
|
19
|
+
if let Some(quote) = string_quote {
|
|
20
|
+
if current == b'\\' {
|
|
21
|
+
cursor.advance(bytes);
|
|
22
|
+
if cursor.index < bytes.len() {
|
|
23
|
+
cursor.advance(bytes);
|
|
24
|
+
}
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if current == quote {
|
|
29
|
+
string_quote = None;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
cursor.advance(bytes);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
match (current, next) {
|
|
37
|
+
(b'\'', _) | (b'"', _) | (b'`', _) => {
|
|
38
|
+
string_quote = Some(current);
|
|
39
|
+
cursor.advance(bytes);
|
|
40
|
+
}
|
|
41
|
+
(b'/', Some(b'/')) => skip_line_comment(bytes, &mut cursor),
|
|
42
|
+
(b'/', Some(b'*')) => skip_block_comment(bytes, &mut cursor),
|
|
43
|
+
_ if starts_enum(bytes, cursor.index) => {
|
|
44
|
+
violations.push(enum_violation(file, &cursor, config.severity));
|
|
45
|
+
cursor.index += b"enum".len();
|
|
46
|
+
cursor.column += b"enum".len();
|
|
47
|
+
}
|
|
48
|
+
_ => cursor.advance(bytes),
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
violations
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#[derive(Debug, Clone, Copy)]
|
|
56
|
+
struct Cursor {
|
|
57
|
+
index: usize,
|
|
58
|
+
line: usize,
|
|
59
|
+
column: usize,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
impl Default for Cursor {
|
|
63
|
+
fn default() -> Self {
|
|
64
|
+
Self {
|
|
65
|
+
index: 0,
|
|
66
|
+
line: 1,
|
|
67
|
+
column: 1,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
impl Cursor {
|
|
73
|
+
fn advance(&mut self, bytes: &[u8]) {
|
|
74
|
+
if bytes[self.index] == b'\n' {
|
|
75
|
+
self.line += 1;
|
|
76
|
+
self.column = 1;
|
|
77
|
+
} else {
|
|
78
|
+
self.column += 1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
self.index += 1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fn enum_violation(file: &Path, cursor: &Cursor, severity: Severity) -> Violation {
|
|
86
|
+
Violation {
|
|
87
|
+
file: file.to_path_buf(),
|
|
88
|
+
line: Some(cursor.line),
|
|
89
|
+
column: Some(cursor.column),
|
|
90
|
+
rule: RULE_NAME,
|
|
91
|
+
message: MESSAGE,
|
|
92
|
+
severity,
|
|
93
|
+
detail: None,
|
|
94
|
+
subject: None,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
fn starts_enum(bytes: &[u8], index: usize) -> bool {
|
|
99
|
+
starts_keyword(bytes, index, b"enum")
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fn starts_keyword(bytes: &[u8], index: usize, keyword: &[u8]) -> bool {
|
|
103
|
+
bytes.get(index..index + keyword.len()) == Some(keyword)
|
|
104
|
+
&& !is_identifier_byte(bytes.get(index.wrapping_sub(1)).copied())
|
|
105
|
+
&& !is_identifier_byte(bytes.get(index + keyword.len()).copied())
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
fn is_identifier_byte(byte: Option<u8>) -> bool {
|
|
109
|
+
matches!(
|
|
110
|
+
byte,
|
|
111
|
+
Some(b'a'..=b'z') | Some(b'A'..=b'Z') | Some(b'0'..=b'9') | Some(b'_') | Some(b'$')
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fn skip_line_comment(bytes: &[u8], cursor: &mut Cursor) {
|
|
116
|
+
while cursor.index < bytes.len() && bytes[cursor.index] != b'\n' {
|
|
117
|
+
cursor.advance(bytes);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fn skip_block_comment(bytes: &[u8], cursor: &mut Cursor) {
|
|
122
|
+
cursor.advance(bytes);
|
|
123
|
+
cursor.advance(bytes);
|
|
124
|
+
|
|
125
|
+
while cursor.index < bytes.len() {
|
|
126
|
+
let current = bytes[cursor.index];
|
|
127
|
+
let next = bytes.get(cursor.index + 1).copied();
|
|
128
|
+
|
|
129
|
+
cursor.advance(bytes);
|
|
130
|
+
|
|
131
|
+
if current == b'*' && next == Some(b'/') {
|
|
132
|
+
cursor.advance(bytes);
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#[cfg(test)]
|
|
139
|
+
mod tests {
|
|
140
|
+
use super::check_file;
|
|
141
|
+
use crate::config::{RuleConfig, Severity};
|
|
142
|
+
use std::path::Path;
|
|
143
|
+
|
|
144
|
+
#[test]
|
|
145
|
+
fn reports_enum_declaration() {
|
|
146
|
+
let violations = check_file(
|
|
147
|
+
Path::new("types.ts"),
|
|
148
|
+
"enum Status { Active, Inactive }\n",
|
|
149
|
+
&test_config(),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
assert_eq!(violations.len(), 1);
|
|
153
|
+
assert_eq!(violations[0].line, Some(1));
|
|
154
|
+
assert_eq!(violations[0].column, Some(1));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#[test]
|
|
158
|
+
fn reports_const_enum() {
|
|
159
|
+
let violations = check_file(
|
|
160
|
+
Path::new("types.ts"),
|
|
161
|
+
"const enum Color { Red, Green, Blue }\n",
|
|
162
|
+
&test_config(),
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
assert_eq!(violations.len(), 1);
|
|
166
|
+
assert_eq!(violations[0].line, Some(1));
|
|
167
|
+
assert_eq!(violations[0].column, Some(7));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
#[test]
|
|
171
|
+
fn reports_multiple_enums() {
|
|
172
|
+
let source = r#"enum A { X }
|
|
173
|
+
enum B { Y }
|
|
174
|
+
"#;
|
|
175
|
+
let violations = check_file(Path::new("types.ts"), source, &test_config());
|
|
176
|
+
|
|
177
|
+
assert_eq!(violations.len(), 2);
|
|
178
|
+
assert_eq!(violations[0].line, Some(1));
|
|
179
|
+
assert_eq!(violations[1].line, Some(2));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
#[test]
|
|
183
|
+
fn ignores_enum_in_comments_and_strings() {
|
|
184
|
+
let source = r#"// enum Status { Active }
|
|
185
|
+
const text = "enum Status";
|
|
186
|
+
/* enum Color { Red } */
|
|
187
|
+
"#;
|
|
188
|
+
let violations = check_file(Path::new("types.ts"), source, &test_config());
|
|
189
|
+
|
|
190
|
+
assert!(violations.is_empty());
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
#[test]
|
|
194
|
+
fn does_not_match_identifier_fragments() {
|
|
195
|
+
let source = r#"const enumeration = true;
|
|
196
|
+
const value = "before enum after";
|
|
197
|
+
"#;
|
|
198
|
+
let violations = check_file(Path::new("types.ts"), source, &test_config());
|
|
199
|
+
|
|
200
|
+
assert!(violations.is_empty());
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
fn test_config() -> RuleConfig {
|
|
204
|
+
RuleConfig {
|
|
205
|
+
severity: Severity::Warn,
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|