eslint-plugin-lube 0.1.3

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/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Eslint plugin lube
2
+
3
+ | Rule | Code | Test |
4
+ |---|---|---|
5
+ | svelte-naming-convention | [code](https://github.com/Artxe2/lube-series/blob/master/packages/eslint-plugin-lube/rules/svelte-naming-convention.js) | [test](https://github.com/Artxe2/lube-series/blob/master/packages/eslint-plugin-lube/test/svelte-naming-convention.js) |
package/index.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = { rules: { "svelte-naming-convention": require("./rules/svelte-naming-convention") } }
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "author": "artxe2",
3
+ "bugs": {
4
+ "url": "https://github.com/Artxe2/lube-series/issues"
5
+ },
6
+ "dependencies": {
7
+ "@types/estree": "^1.0.1"
8
+ },
9
+ "description": "Eslint rules expand options",
10
+ "devDependencies": {
11
+ "@types/eslint": "^8.44.2",
12
+ "@typescript-eslint/parser": "^6.3.0",
13
+ "eslint": "^8.47.0",
14
+ "mocha": "^10.2.0"
15
+ },
16
+ "license": "MIT",
17
+ "name": "eslint-plugin-lube",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/Artxe2/lube-series"
21
+ },
22
+ "scripts": {
23
+ "test": "mocha"
24
+ },
25
+ "type": "commonjs",
26
+ "version": "0.1.3"
27
+ }
package/private.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ import {
2
+ Node,
3
+ Pattern
4
+ } from "estree"
5
+
6
+ interface ASTNode extends Node, Pattern {
7
+ computed: boolean
8
+ id: ASTNode
9
+ imported: ASTNode
10
+ key: ASTNode
11
+ left: ASTNode
12
+ local: ASTNode
13
+ name: string
14
+ object: ASTNode
15
+ parent: ASTNode
16
+ property: ASTNode
17
+ range: [number, number]
18
+ right: ASTNode
19
+ shorthand: boolean
20
+ type: string
21
+ value: ASTNode
22
+ }
@@ -0,0 +1,398 @@
1
+ "use strict"
2
+ /** @type {import("eslint").Rule.RuleModule} */
3
+ module.exports = {
4
+ meta: {
5
+ docs: {
6
+ description: "Enforces snake_case with some symbols for variable declaration.",
7
+ recommended: true,
8
+ url: "https://github.com/sveltejs/svelte/blob/master/CONTRIBUTING.md#code-conventions"
9
+ },
10
+ fixable: "code",
11
+ messages: {
12
+ not_match: "Identifier '{{name}}' does not match for svelte's naming conventions.",
13
+ not_match_private: "PrivateIdentifier '#{{name}}' does not match for svelte's naming conventions."
14
+ },
15
+ schema: [{
16
+ properties: {
17
+ fixSameNames: {
18
+ type: "boolean",
19
+ default: false
20
+ },
21
+ },
22
+ type: "object"
23
+ }],
24
+ type: "suggestion"
25
+ },
26
+ create(context) {
27
+ const fix_same_names = /** @type {boolean} */ (context.options[0]?.fixSameNames)
28
+
29
+ // let name | const name | class name
30
+ const allow_regex = /^[_$]?[_$]?(?:[0-9a-z]+(?:_[0-9a-z]+)*\$?\$?)?$|^[A-Z][0-9A-Z_]*$|^(?:[0-9A-Z][0-9a-z]*)+$/
31
+ const fixable_name_regex = /^[_$]?[_$]?(?:[0-9A-Za-z]+(?:_[0-9A-Za-z]+)*\$?\$?)?$/
32
+ const fix_regex = /([0-9a-z]?)([A-Z][0-9A-Z]*)/g
33
+
34
+ /**
35
+ * @param {string} _
36
+ * @param {string} a
37
+ * @param {string} b
38
+ */
39
+ function fix_callback(_, a, b) {
40
+ return a + (a ? "_" : "") + b.toLowerCase()
41
+ }
42
+
43
+ /** @type {Set<string>} */
44
+ const reported_declarations = new Set()
45
+ /** @type {Map<string, import("../private").ASTNode[]>} */
46
+ const pending_usages = new Map()
47
+
48
+ /** @param {import("../private").ASTNode} node */
49
+ function defer(node) {
50
+ if (fix_same_names) {
51
+ const name = node.name
52
+ if (reported_declarations.has(name)) report(node)
53
+ else {
54
+ let usages = pending_usages.get(name)
55
+ if (!usages) {
56
+ usages = []
57
+ pending_usages.set(name, usages)
58
+ }
59
+ usages.push(node)
60
+ }
61
+ }
62
+ }
63
+
64
+ /** @param {import("../private").ASTNode} node */
65
+ function report(node) {
66
+ const name = node.name
67
+ context.report({
68
+ data: { name },
69
+ fix(fixer) {
70
+ if (fixable_name_regex.test(name)) {
71
+ return fixer.replaceTextRange(
72
+ node.range,
73
+ node.name.replace(fix_regex, fix_callback)
74
+ )
75
+ }
76
+ return null
77
+ },
78
+ messageId: node.type == "PrivateIdentifier"
79
+ ? "not_match_private"
80
+ : "not_match",
81
+ node: /** @type {import("estree").Node} */(node)
82
+ })
83
+ if (fix_same_names) {
84
+ const usages = pending_usages.get(name)
85
+ while (usages?.length) {
86
+ report(/** @type {import("../private").ASTNode} */(
87
+ usages.pop()
88
+ ))
89
+ }
90
+ reported_declarations.add(name)
91
+ }
92
+ }
93
+
94
+ return {
95
+ /** @param {import("../private").ASTNode} node */
96
+ Identifier(node) {
97
+ const name = node.name
98
+ if (allow_regex.test(name)) return
99
+ const parent = node.parent
100
+ const type = parent.type
101
+ // var value = [ camelCase ]
102
+ if ("ArrayExpression" == type) {
103
+ defer(node)
104
+ return
105
+ }
106
+ // var [ camelCase ] = value
107
+ if ("ArrayPattern" == type) {
108
+ report(node)
109
+ return
110
+ }
111
+ // (camelCase) => {}
112
+ if ("ArrowFunctionExpression" == type) {
113
+ report(node)
114
+ return
115
+ }
116
+ // left = right
117
+ if ("AssignmentExpression" == type) {
118
+ defer(node)
119
+ return
120
+ }
121
+ // (left = right) => {}
122
+ if ("AssignmentPattern" == type) {
123
+ if (parent.left == node) {
124
+ report(node)
125
+ } else {
126
+ defer(node)
127
+ }
128
+ return
129
+ }
130
+ // await camelCase
131
+ if ("AwaitExpression" == type) {
132
+ defer(node)
133
+ return
134
+ }
135
+ // left +-*/&| right
136
+ if ("BinaryExpression" == type) {
137
+ defer(node)
138
+ return
139
+ }
140
+ /* BlockStatement */
141
+ /* BreakStatement */
142
+ // camelCase()
143
+ if ("CallExpression" == type) {
144
+ defer(node)
145
+ return
146
+ }
147
+ /* CatchClause */
148
+ /* ChainExpression */
149
+ /* ClassBody */
150
+ // class camelCase {}
151
+ if ("ClassDeclaration" == type) {
152
+ report(node)
153
+ return
154
+ }
155
+ /* ClassExpression */
156
+ // test ? alternate : consequent
157
+ if ("ConditionalExpression" == type) {
158
+ defer(node)
159
+ return
160
+ }
161
+ /* ContinueStatement */
162
+ /* DebuggerStatement */
163
+ /* DoWhileStatement */
164
+ /* EmptyStatement */
165
+ // export * as camelCase from "module"
166
+ if ("ExportAllDeclaration" == type) {
167
+ return
168
+ }
169
+ // export default camelCase
170
+ if ("ExportDefaultDeclaration" == type) {
171
+ defer(node)
172
+ return
173
+ }
174
+ /* ExportNamedDeclaration */
175
+ // export { default as camelCase, camelCase2 } from "module"
176
+ if ("ExportSpecifier" == type) {
177
+ return
178
+ }
179
+ // camelCase
180
+ if ("ExpressionStatement" == type) {
181
+ defer(node)
182
+ return
183
+ }
184
+ // for (left in right) {}
185
+ if ("ForInStatement" == type) {
186
+ if (parent.left == node) {
187
+ report(node)
188
+ } else {
189
+ defer(node)
190
+ }
191
+ return
192
+ }
193
+ // for (left of right) {}
194
+ if ("ForOfStatement" == type) {
195
+ if (parent.left == node) {
196
+ report(node)
197
+ } else {
198
+ defer(node)
199
+ }
200
+ return
201
+ }
202
+ // for (init; test; update) {}
203
+ if ("ForStatement" == type) {
204
+ defer(node)
205
+ return
206
+ }
207
+ // function camelCase() {}
208
+ if ("FunctionDeclaration" == type) {
209
+ report(node)
210
+ return
211
+ }
212
+ /* FunctionExpression */
213
+ /* Identifier */
214
+ // if (camelCase) {}
215
+ if ("IfStatement" == type) {
216
+ defer(node)
217
+ return
218
+ }
219
+ /* ImportDeclaration */
220
+ // import camelCase from "module"
221
+ if ("ImportDefaultSpecifier" == type) {
222
+ report(node)
223
+ return
224
+ }
225
+ /* ImportExpression */
226
+ // import * as camelCase from "module"
227
+ if ("ImportNamespaceSpecifier" == type) {
228
+ report(node)
229
+ return
230
+ }
231
+ // import { imported as local, local2 } from "module"
232
+ if ("ImportSpecifier" == type) {
233
+ // import { imported as camelCase } from "module"
234
+ if (parent.local == node && parent.imported.name != parent.local.name) {
235
+ report(node)
236
+ }
237
+ return
238
+ }
239
+ /* LabeledStatement */
240
+ /* Literal */
241
+ // left &&||?? right
242
+ if ("LogicalExpression" == type) {
243
+ defer(node)
244
+ return
245
+ }
246
+ // object.property
247
+ if ("MemberExpression" == type) {
248
+ if (parent.object == node) {
249
+ defer(node)
250
+ }
251
+ // object[camelCase]
252
+ // camelCase[key]
253
+ else if (parent.computed) {
254
+ defer(node)
255
+ }
256
+ return
257
+ }
258
+ /* MetaProperty */
259
+ // class Object { camelCase() {} }
260
+ if ("MethodDefinition" == type) {
261
+ report(node)
262
+ return
263
+ }
264
+ // new camelCase
265
+ if ("NewExpression" == type) {
266
+ defer(node)
267
+ return
268
+ }
269
+ /* ObjectExpression */
270
+ /* ObjectPattern */
271
+ /* PrivateIdentifier */
272
+ /* Program */
273
+ // var value = { key: value }
274
+ // var { key: value } = value
275
+ if ("Property" == type) {
276
+ if (parent.value == node && parent.key.name != parent.value.name) {
277
+ defer(node)
278
+ }
279
+ return
280
+ }
281
+ // class Object { key = value }
282
+ if ("PropertyDefinition" == type) {
283
+ if (parent.key == node) {
284
+ report(node)
285
+ } else {
286
+ defer(node)
287
+ }
288
+ return
289
+ }
290
+ // var [ ...camelCase ] = value
291
+ // var { ...camelCase } = value
292
+ if ("RestElement" == type) {
293
+ report(node)
294
+ return
295
+ }
296
+ // function func() { return camelCase }
297
+ if ("ReturnStatement" == type) {
298
+ defer(node)
299
+ return
300
+ }
301
+ // a, b, camelCase
302
+ if ("SequenceExpression" == type) {
303
+ defer(node)
304
+ return
305
+ }
306
+ // var value = [ ...camelCase ]
307
+ // var value = { ...camelCase }
308
+ if ("SpreadElement" == type) {
309
+ defer(node)
310
+ return
311
+ }
312
+ /* StaticBlock */
313
+ /* Super */
314
+ // switch (camelCase) {}
315
+ if ("SwitchCase" == type) {
316
+ defer(node)
317
+ return
318
+ }
319
+ // switch (cond) { case camelCase: break }
320
+ if ("SwitchStatement" == type) {
321
+ defer(node)
322
+ return
323
+ }
324
+ // camelCase`quasis${expression}quasis`
325
+ if ("TaggedTemplateExpression" == type) {
326
+ defer(node)
327
+ return
328
+ }
329
+ /* TemplateElement */
330
+ // `quasis${camelCase}quasis`
331
+ if ("TemplateLiteral" == type) {
332
+ defer(node)
333
+ return
334
+ }
335
+ /* ThisExpression */
336
+ // throw camelCase
337
+ if ("ThrowStatement" == type) {
338
+ defer(node)
339
+ return
340
+ }
341
+ /* TryStatement */
342
+ // +-!~ camelCase
343
+ if ("UnaryExpression" == type) {
344
+ defer(node)
345
+ return
346
+ }
347
+ // ++--camelCase++--
348
+ if ("UpdateExpression" == type) {
349
+ defer(node)
350
+ return
351
+ }
352
+ /* VariableDeclaration */
353
+ // var id = init
354
+ if ("VariableDeclarator" == type) {
355
+ if (parent.id == node) {
356
+ report(node)
357
+ } else {
358
+ defer(node)
359
+ }
360
+ return
361
+ }
362
+ // while (camelCase) {}
363
+ if ("WhileStatement" == type) {
364
+ defer(node)
365
+ return
366
+ }
367
+ // with (camelCase) {}
368
+ if ("WithStatement" == type) {
369
+ defer(node)
370
+ return
371
+ }
372
+ // function* func() { yield camelCase }
373
+ if ("YieldExpression" == type) {
374
+ defer(node)
375
+ return
376
+ }
377
+ },
378
+ /** @param {import("../private").ASTNode} node */
379
+ PrivateIdentifier(node) {
380
+ const name = node.name
381
+ if (allow_regex.test(name)) return
382
+ const parent = node.parent
383
+ const type = parent.type
384
+ // class Object { #camelCase() {} }
385
+ if ("MethodDefinition" == type) {
386
+ report(node)
387
+ return
388
+ }
389
+ // class Object { #camelCase = value }
390
+ if ("PropertyDefinition" == type) {
391
+ report(node)
392
+ return
393
+ }
394
+ // TODO ?
395
+ }
396
+ }
397
+ }
398
+ }
@@ -0,0 +1,34 @@
1
+ const error = {
2
+ messageId: "not_match",
3
+ type: "Identifier"
4
+ }
5
+ /**
6
+ * @param {import("eslint").RuleTester.ValidTestCase[]} valid
7
+ * @param {import("eslint").RuleTester.InvalidTestCase[]} invalid
8
+ */
9
+ exports.module = (valid, invalid) => {
10
+ valid.push(
11
+ { code: "var s = 'Hello'" },
12
+ { code: "var snake_case = 'Hello'" },
13
+ { code: "var snake_case_123 = 'Hello'" },
14
+ { code: "var snake_case_123 = 'Hello'" },
15
+ { code: "var snake1_2_3case_4 = 'Hello'" },
16
+ { code: "var SNAKE_CASE = 'Hello'" },
17
+ { code: "var SNAKE1_2_3CASE_4 = 'Hello'" },
18
+ { code: "camelCase = 'Hello'" },
19
+ { code: "var snake_case = camelCase" },
20
+ { code: "var PascalCase = 'Hello'" },
21
+ { code: "var P1ascal2Case345 = 'Hello'" },
22
+ )
23
+ invalid.push(
24
+ {
25
+ code: "var camelCase = 'Hello'",
26
+ errors: [{
27
+ ...error,
28
+ line: 1,
29
+ column: 5
30
+ }],
31
+ output: "var camel_case = 'Hello'"
32
+ },
33
+ )
34
+ }