eslint-plugin-react-naming-convention 2.0.0-next.0 → 2.0.0-next.1
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/dist/index.js +40 -66
- package/package.json +10 -18
- package/dist/index.d.mts +0 -77
- package/dist/index.mjs +0 -497
package/dist/index.js
CHANGED
|
@@ -1,39 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
var path = require('path');
|
|
12
|
-
var stringTs = require('string-ts');
|
|
13
|
-
|
|
14
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
-
|
|
16
|
-
function _interopNamespace(e) {
|
|
17
|
-
if (e && e.__esModule) return e;
|
|
18
|
-
var n = Object.create(null);
|
|
19
|
-
if (e) {
|
|
20
|
-
Object.keys(e).forEach(function (k) {
|
|
21
|
-
if (k !== 'default') {
|
|
22
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
23
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
24
|
-
enumerable: true,
|
|
25
|
-
get: function () { return e[k]; }
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
n.default = e;
|
|
31
|
-
return Object.freeze(n);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
var AST__namespace = /*#__PURE__*/_interopNamespace(AST);
|
|
35
|
-
var ER2__namespace = /*#__PURE__*/_interopNamespace(ER2);
|
|
36
|
-
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
1
|
+
import * as AST from '@eslint-react/ast';
|
|
2
|
+
import * as ER2 from '@eslint-react/core';
|
|
3
|
+
import { RegExp } from '@eslint-react/kit';
|
|
4
|
+
import { getDocsUrl, getSettingsFromContext } from '@eslint-react/shared';
|
|
5
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
6
|
+
import { identity, isObject } from '@eslint-react/eff';
|
|
7
|
+
import { AST_NODE_TYPES } from '@typescript-eslint/types';
|
|
8
|
+
import { match, P } from 'ts-pattern';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { snakeCase, pascalCase, camelCase, kebabCase } from 'string-ts';
|
|
37
11
|
|
|
38
12
|
var __defProp = Object.defineProperty;
|
|
39
13
|
var __export = (target, all) => {
|
|
@@ -55,8 +29,8 @@ var rules = {
|
|
|
55
29
|
|
|
56
30
|
// package.json
|
|
57
31
|
var name2 = "eslint-plugin-react-naming-convention";
|
|
58
|
-
var version = "2.0.0-next.
|
|
59
|
-
var createRule =
|
|
32
|
+
var version = "2.0.0-next.1";
|
|
33
|
+
var createRule = ESLintUtils.RuleCreator(getDocsUrl("naming-convention"));
|
|
60
34
|
|
|
61
35
|
// src/rules/component-name.ts
|
|
62
36
|
var defaultOptions = [
|
|
@@ -121,8 +95,8 @@ var component_name_default = createRule({
|
|
|
121
95
|
function create(context) {
|
|
122
96
|
const options = normalizeOptions(context.options);
|
|
123
97
|
const { rule } = options;
|
|
124
|
-
const collector =
|
|
125
|
-
const collectorLegacy =
|
|
98
|
+
const collector = ER2.useComponentCollector(context);
|
|
99
|
+
const collectorLegacy = ER2.useComponentCollectorLegacy();
|
|
126
100
|
return {
|
|
127
101
|
...collector.listeners,
|
|
128
102
|
...collectorLegacy.listeners,
|
|
@@ -130,7 +104,7 @@ function create(context) {
|
|
|
130
104
|
const functionComponents = collector.ctx.getAllComponents(program);
|
|
131
105
|
const classComponents = collectorLegacy.ctx.getAllComponents(program);
|
|
132
106
|
for (const { node: component } of functionComponents.values()) {
|
|
133
|
-
const id =
|
|
107
|
+
const id = AST.getFunctionId(component);
|
|
134
108
|
if (id?.name == null) continue;
|
|
135
109
|
const name3 = id.name;
|
|
136
110
|
if (isValidName(name3, options)) return;
|
|
@@ -141,7 +115,7 @@ function create(context) {
|
|
|
141
115
|
});
|
|
142
116
|
}
|
|
143
117
|
for (const { node: component } of classComponents.values()) {
|
|
144
|
-
const id =
|
|
118
|
+
const id = AST.getClassId(component);
|
|
145
119
|
if (id?.name == null) continue;
|
|
146
120
|
const name3 = id.name;
|
|
147
121
|
if (isValidName(name3, options)) continue;
|
|
@@ -162,7 +136,7 @@ function normalizeOptions(options) {
|
|
|
162
136
|
...defaultOpts,
|
|
163
137
|
...typeof opts === "string" ? { rule: opts } : {
|
|
164
138
|
...opts,
|
|
165
|
-
excepts: opts.excepts?.map((s) =>
|
|
139
|
+
excepts: opts.excepts?.map((s) => RegExp.toRegExp(s)) ?? []
|
|
166
140
|
}
|
|
167
141
|
};
|
|
168
142
|
}
|
|
@@ -172,12 +146,12 @@ function isValidName(name3, options) {
|
|
|
172
146
|
const normalized = name3.split(".").at(-1) ?? name3;
|
|
173
147
|
switch (options.rule) {
|
|
174
148
|
case "CONSTANT_CASE":
|
|
175
|
-
return
|
|
149
|
+
return RegExp.CONSTANT_CASE.test(normalized);
|
|
176
150
|
case "PascalCase":
|
|
177
151
|
if (normalized.length > 3 && /^[A-Z]+$/u.test(normalized)) {
|
|
178
152
|
return options.allowAllCaps;
|
|
179
153
|
}
|
|
180
|
-
return
|
|
154
|
+
return RegExp.PASCAL_CASE.test(normalized);
|
|
181
155
|
}
|
|
182
156
|
}
|
|
183
157
|
var RULE_NAME2 = "context-name";
|
|
@@ -200,11 +174,11 @@ function create2(context) {
|
|
|
200
174
|
if (!context.sourceCode.text.includes("createContext")) return {};
|
|
201
175
|
return {
|
|
202
176
|
CallExpression(node) {
|
|
203
|
-
if (!
|
|
204
|
-
const id =
|
|
177
|
+
if (!ER2.isCreateContextCall(context, node)) return;
|
|
178
|
+
const id = ER2.getInstanceId(node);
|
|
205
179
|
if (id == null) return;
|
|
206
|
-
const name3 =
|
|
207
|
-
if (name3 != null &&
|
|
180
|
+
const name3 = match(id).with({ type: AST_NODE_TYPES.Identifier, name: P.select() }, identity).with({ type: AST_NODE_TYPES.MemberExpression, property: { name: P.select(P.string) } }, identity).otherwise(() => null);
|
|
181
|
+
if (name3 != null && ER2.isComponentName(name3) && name3.endsWith("Context")) return;
|
|
208
182
|
context.report({
|
|
209
183
|
messageId: "invalid",
|
|
210
184
|
node: id
|
|
@@ -271,16 +245,16 @@ function create3(context) {
|
|
|
271
245
|
const rule = typeof options === "string" ? options : options.rule ?? "PascalCase";
|
|
272
246
|
const excepts = typeof options === "string" ? [] : options.excepts ?? [];
|
|
273
247
|
function validate(name3, casing = rule, ignores = excepts) {
|
|
274
|
-
const shouldIgnore = ignores.map((s) =>
|
|
248
|
+
const shouldIgnore = ignores.map((s) => RegExp.toRegExp(s)).some((pattern) => pattern.test(name3));
|
|
275
249
|
if (shouldIgnore) return true;
|
|
276
|
-
return
|
|
250
|
+
return match(casing).with("PascalCase", () => RegExp.PASCAL_CASE.test(name3)).with("camelCase", () => RegExp.CAMEL_CASE.test(name3)).with("kebab-case", () => RegExp.KEBAB_CASE.test(name3)).with("snake_case", () => RegExp.SNAKE_CASE.test(name3)).exhaustive();
|
|
277
251
|
}
|
|
278
252
|
function getSuggestion(name3, casing = rule) {
|
|
279
|
-
return
|
|
253
|
+
return match(casing).with("PascalCase", () => pascalCase(name3)).with("camelCase", () => camelCase(name3)).with("kebab-case", () => kebabCase(name3)).with("snake_case", () => snakeCase(name3)).exhaustive();
|
|
280
254
|
}
|
|
281
255
|
return {
|
|
282
256
|
Program(node) {
|
|
283
|
-
const [basename = "", ...rest] =
|
|
257
|
+
const [basename = "", ...rest] = path.basename(context.filename).split(".");
|
|
284
258
|
if (basename.length === 0) {
|
|
285
259
|
context.report({ messageId: "filenameEmpty", node });
|
|
286
260
|
return;
|
|
@@ -355,8 +329,8 @@ var filename_extension_default = createRule({
|
|
|
355
329
|
});
|
|
356
330
|
function create4(context) {
|
|
357
331
|
const options = context.options[0] ?? defaultOptions3[0];
|
|
358
|
-
const allow =
|
|
359
|
-
const extensions =
|
|
332
|
+
const allow = isObject(options) ? options.allow : options;
|
|
333
|
+
const extensions = isObject(options) && "extensions" in options ? options.extensions : defaultOptions3[0].extensions;
|
|
360
334
|
const extensionsString = extensions.map((ext) => `'${ext}'`).join(", ");
|
|
361
335
|
const filename = context.filename;
|
|
362
336
|
let hasJSXNode = false;
|
|
@@ -381,7 +355,7 @@ function create4(context) {
|
|
|
381
355
|
return;
|
|
382
356
|
}
|
|
383
357
|
const hasCode = program.body.length > 0;
|
|
384
|
-
const ignoreFilesWithoutCode =
|
|
358
|
+
const ignoreFilesWithoutCode = isObject(options) && options.ignoreFilesWithoutCode === true;
|
|
385
359
|
if (!hasCode && ignoreFilesWithoutCode) {
|
|
386
360
|
return;
|
|
387
361
|
}
|
|
@@ -417,22 +391,22 @@ var use_state_default = createRule({
|
|
|
417
391
|
defaultOptions: []
|
|
418
392
|
});
|
|
419
393
|
function create5(context) {
|
|
420
|
-
const alias =
|
|
421
|
-
const isUseStateCall =
|
|
394
|
+
const alias = getSettingsFromContext(context).additionalHooks.useState ?? [];
|
|
395
|
+
const isUseStateCall = ER2.isReactHookCallWithNameAlias(context, "useState", alias);
|
|
422
396
|
return {
|
|
423
397
|
CallExpression(node) {
|
|
424
398
|
if (!isUseStateCall(node)) {
|
|
425
399
|
return;
|
|
426
400
|
}
|
|
427
|
-
if (node.parent.type !==
|
|
401
|
+
if (node.parent.type !== AST_NODE_TYPES.VariableDeclarator) {
|
|
428
402
|
context.report({
|
|
429
403
|
messageId: "missingDestructuring",
|
|
430
404
|
node
|
|
431
405
|
});
|
|
432
406
|
return;
|
|
433
407
|
}
|
|
434
|
-
const id =
|
|
435
|
-
if (id?.type !==
|
|
408
|
+
const id = ER2.getInstanceId(node);
|
|
409
|
+
if (id?.type !== AST_NODE_TYPES.ArrayPattern) {
|
|
436
410
|
context.report({
|
|
437
411
|
messageId: "missingDestructuring",
|
|
438
412
|
node: id ?? node
|
|
@@ -447,7 +421,7 @@ function create5(context) {
|
|
|
447
421
|
});
|
|
448
422
|
return;
|
|
449
423
|
}
|
|
450
|
-
const setterName =
|
|
424
|
+
const setterName = match(setter).with({ type: AST_NODE_TYPES.Identifier }, (id2) => id2.name).otherwise(() => null);
|
|
451
425
|
if (setterName == null || !setterName.startsWith("set")) {
|
|
452
426
|
context.report({
|
|
453
427
|
messageId: "invalidSetterNaming",
|
|
@@ -455,9 +429,9 @@ function create5(context) {
|
|
|
455
429
|
});
|
|
456
430
|
return;
|
|
457
431
|
}
|
|
458
|
-
const valueName =
|
|
432
|
+
const valueName = match(value).with({ type: AST_NODE_TYPES.Identifier }, ({ name: name3 }) => snakeCase(name3)).with({ type: AST_NODE_TYPES.ObjectPattern }, ({ properties }) => {
|
|
459
433
|
const values = properties.reduce((acc, prop) => {
|
|
460
|
-
if (prop.type ===
|
|
434
|
+
if (prop.type === AST_NODE_TYPES.Property && prop.key.type === AST_NODE_TYPES.Identifier) {
|
|
461
435
|
return [...acc, prop.key.name];
|
|
462
436
|
}
|
|
463
437
|
return acc;
|
|
@@ -471,7 +445,7 @@ function create5(context) {
|
|
|
471
445
|
});
|
|
472
446
|
return;
|
|
473
447
|
}
|
|
474
|
-
if (
|
|
448
|
+
if (snakeCase(setterName) !== `set_${valueName}`) {
|
|
475
449
|
context.report({
|
|
476
450
|
messageId: "invalidSetterNaming",
|
|
477
451
|
node: setter
|
|
@@ -520,4 +494,4 @@ var index_default = {
|
|
|
520
494
|
}
|
|
521
495
|
};
|
|
522
496
|
|
|
523
|
-
|
|
497
|
+
export { index_default as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-naming-convention",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.1",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for naming convention related rules.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -22,22 +22,14 @@
|
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"author": "Rel1cx<rel1cx@proton.me>",
|
|
24
24
|
"sideEffects": false,
|
|
25
|
+
"type": "module",
|
|
25
26
|
"exports": {
|
|
26
27
|
".": {
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
"default": "./dist/index.mjs"
|
|
30
|
-
},
|
|
31
|
-
"require": {
|
|
32
|
-
"types": "./dist/index.d.ts",
|
|
33
|
-
"default": "./dist/index.js"
|
|
34
|
-
}
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
35
30
|
},
|
|
36
31
|
"./package.json": "./package.json"
|
|
37
32
|
},
|
|
38
|
-
"main": "dist/index.js",
|
|
39
|
-
"module": "dist/index.mjs",
|
|
40
|
-
"types": "dist/index.d.ts",
|
|
41
33
|
"files": [
|
|
42
34
|
"dist",
|
|
43
35
|
"./package.json"
|
|
@@ -49,12 +41,12 @@
|
|
|
49
41
|
"@typescript-eslint/utils": "^8.30.1",
|
|
50
42
|
"string-ts": "^2.2.1",
|
|
51
43
|
"ts-pattern": "^5.7.0",
|
|
52
|
-
"@eslint-react/
|
|
53
|
-
"@eslint-react/
|
|
54
|
-
"@eslint-react/
|
|
55
|
-
"@eslint-react/
|
|
56
|
-
"@eslint-react/
|
|
57
|
-
"@eslint-react/var": "2.0.0-next.
|
|
44
|
+
"@eslint-react/ast": "2.0.0-next.1",
|
|
45
|
+
"@eslint-react/eff": "2.0.0-next.1",
|
|
46
|
+
"@eslint-react/core": "2.0.0-next.1",
|
|
47
|
+
"@eslint-react/shared": "2.0.0-next.1",
|
|
48
|
+
"@eslint-react/kit": "2.0.0-next.1",
|
|
49
|
+
"@eslint-react/var": "2.0.0-next.1"
|
|
58
50
|
},
|
|
59
51
|
"devDependencies": {
|
|
60
52
|
"@types/react": "^19.1.2",
|
package/dist/index.d.mts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import * as _typescript_eslint_utils_ts_eslint from '@typescript-eslint/utils/ts-eslint';
|
|
2
|
-
import { RulePreset } from '@eslint-react/kit';
|
|
3
|
-
|
|
4
|
-
type MessageID$2 = "missingDestructuring" | "invalidSetterNaming";
|
|
5
|
-
|
|
6
|
-
type MessageID$1 = "useJsxFileExtension" | "useNonJsxFileExtension";
|
|
7
|
-
|
|
8
|
-
type MessageID = "filenameEmpty" | "filenameInvalid";
|
|
9
|
-
|
|
10
|
-
declare const _default: {
|
|
11
|
-
configs: {
|
|
12
|
-
recommended: {
|
|
13
|
-
plugins: {
|
|
14
|
-
"react-naming-convention": {
|
|
15
|
-
readonly meta: {
|
|
16
|
-
readonly name: string;
|
|
17
|
-
readonly version: string;
|
|
18
|
-
};
|
|
19
|
-
readonly rules: {
|
|
20
|
-
readonly "component-name": _typescript_eslint_utils_ts_eslint.RuleModule<"invalid", readonly [("CONSTANT_CASE" | "PascalCase") | {
|
|
21
|
-
allowAllCaps?: boolean;
|
|
22
|
-
allowLeadingUnderscore?: boolean;
|
|
23
|
-
allowNamespace?: boolean;
|
|
24
|
-
excepts?: readonly string[];
|
|
25
|
-
rule?: "CONSTANT_CASE" | "PascalCase";
|
|
26
|
-
} | undefined], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
27
|
-
readonly "context-name": _typescript_eslint_utils_ts_eslint.RuleModule<"invalid", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
28
|
-
readonly filename: _typescript_eslint_utils_ts_eslint.RuleModule<MessageID, readonly [("PascalCase" | "camelCase" | "kebab-case" | "snake_case") | {
|
|
29
|
-
excepts?: readonly string[];
|
|
30
|
-
extensions?: readonly string[];
|
|
31
|
-
rule?: "PascalCase" | "camelCase" | "kebab-case" | "snake_case";
|
|
32
|
-
} | undefined], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
33
|
-
readonly "filename-extension": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID$1, readonly [("always" | "as-needed") | {
|
|
34
|
-
allow?: "always" | "as-needed";
|
|
35
|
-
extensions?: readonly string[];
|
|
36
|
-
ignoreFilesWithoutCode?: boolean;
|
|
37
|
-
} | undefined], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
38
|
-
readonly "use-state": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID$2, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
name: string;
|
|
43
|
-
rules: RulePreset;
|
|
44
|
-
};
|
|
45
|
-
"recommended-legacy": {
|
|
46
|
-
plugins: string[];
|
|
47
|
-
rules: RulePreset;
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
meta: {
|
|
51
|
-
readonly name: string;
|
|
52
|
-
readonly version: string;
|
|
53
|
-
};
|
|
54
|
-
rules: {
|
|
55
|
-
readonly "component-name": _typescript_eslint_utils_ts_eslint.RuleModule<"invalid", readonly [("CONSTANT_CASE" | "PascalCase") | {
|
|
56
|
-
allowAllCaps?: boolean;
|
|
57
|
-
allowLeadingUnderscore?: boolean;
|
|
58
|
-
allowNamespace?: boolean;
|
|
59
|
-
excepts?: readonly string[];
|
|
60
|
-
rule?: "CONSTANT_CASE" | "PascalCase";
|
|
61
|
-
} | undefined], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
62
|
-
readonly "context-name": _typescript_eslint_utils_ts_eslint.RuleModule<"invalid", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
63
|
-
readonly filename: _typescript_eslint_utils_ts_eslint.RuleModule<MessageID, readonly [("PascalCase" | "camelCase" | "kebab-case" | "snake_case") | {
|
|
64
|
-
excepts?: readonly string[];
|
|
65
|
-
extensions?: readonly string[];
|
|
66
|
-
rule?: "PascalCase" | "camelCase" | "kebab-case" | "snake_case";
|
|
67
|
-
} | undefined], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
68
|
-
readonly "filename-extension": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID$1, readonly [("always" | "as-needed") | {
|
|
69
|
-
allow?: "always" | "as-needed";
|
|
70
|
-
extensions?: readonly string[];
|
|
71
|
-
ignoreFilesWithoutCode?: boolean;
|
|
72
|
-
} | undefined], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
73
|
-
readonly "use-state": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID$2, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
74
|
-
};
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export { _default as default };
|
package/dist/index.mjs
DELETED
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
import * as AST from '@eslint-react/ast';
|
|
2
|
-
import * as ER2 from '@eslint-react/core';
|
|
3
|
-
import { RegExp } from '@eslint-react/kit';
|
|
4
|
-
import { getDocsUrl, getSettingsFromContext } from '@eslint-react/shared';
|
|
5
|
-
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
6
|
-
import { identity, isObject } from '@eslint-react/eff';
|
|
7
|
-
import { AST_NODE_TYPES } from '@typescript-eslint/types';
|
|
8
|
-
import { match, P } from 'ts-pattern';
|
|
9
|
-
import path from 'node:path';
|
|
10
|
-
import { snakeCase, pascalCase, camelCase, kebabCase } from 'string-ts';
|
|
11
|
-
|
|
12
|
-
var __defProp = Object.defineProperty;
|
|
13
|
-
var __export = (target, all) => {
|
|
14
|
-
for (var name3 in all)
|
|
15
|
-
__defProp(target, name3, { get: all[name3], enumerable: true });
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// src/configs/recommended.ts
|
|
19
|
-
var recommended_exports = {};
|
|
20
|
-
__export(recommended_exports, {
|
|
21
|
-
name: () => name,
|
|
22
|
-
rules: () => rules
|
|
23
|
-
});
|
|
24
|
-
var name = "react-naming-convention/recommended";
|
|
25
|
-
var rules = {
|
|
26
|
-
"react-naming-convention/context-name": "warn"
|
|
27
|
-
// "react-naming-convention/use-state": "warn",
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// package.json
|
|
31
|
-
var name2 = "eslint-plugin-react-naming-convention";
|
|
32
|
-
var version = "2.0.0-next.0";
|
|
33
|
-
var createRule = ESLintUtils.RuleCreator(getDocsUrl("naming-convention"));
|
|
34
|
-
|
|
35
|
-
// src/rules/component-name.ts
|
|
36
|
-
var defaultOptions = [
|
|
37
|
-
{
|
|
38
|
-
allowAllCaps: false,
|
|
39
|
-
excepts: [],
|
|
40
|
-
rule: "PascalCase"
|
|
41
|
-
}
|
|
42
|
-
];
|
|
43
|
-
var schema = [
|
|
44
|
-
{
|
|
45
|
-
anyOf: [
|
|
46
|
-
{
|
|
47
|
-
type: "string",
|
|
48
|
-
enum: ["PascalCase", "CONSTANT_CASE"]
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
type: "object",
|
|
52
|
-
additionalProperties: false,
|
|
53
|
-
properties: {
|
|
54
|
-
allowAllCaps: { type: "boolean" },
|
|
55
|
-
/**
|
|
56
|
-
* @todo Remove in the next major version
|
|
57
|
-
* @deprecated
|
|
58
|
-
*/
|
|
59
|
-
allowLeadingUnderscore: { type: "boolean" },
|
|
60
|
-
/**
|
|
61
|
-
* @todo Remove in the next major version
|
|
62
|
-
* @deprecated
|
|
63
|
-
*/
|
|
64
|
-
allowNamespace: { type: "boolean" },
|
|
65
|
-
excepts: {
|
|
66
|
-
type: "array",
|
|
67
|
-
items: { type: "string", format: "regex" }
|
|
68
|
-
},
|
|
69
|
-
rule: {
|
|
70
|
-
type: "string",
|
|
71
|
-
enum: ["PascalCase", "CONSTANT_CASE"]
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
]
|
|
76
|
-
}
|
|
77
|
-
];
|
|
78
|
-
var RULE_NAME = "component-name";
|
|
79
|
-
var component_name_default = createRule({
|
|
80
|
-
meta: {
|
|
81
|
-
type: "problem",
|
|
82
|
-
defaultOptions: [...defaultOptions],
|
|
83
|
-
docs: {
|
|
84
|
-
description: "Enforces naming conventions for components."
|
|
85
|
-
},
|
|
86
|
-
messages: {
|
|
87
|
-
invalid: "A component name '{{name}}' does not match {{rule}}."
|
|
88
|
-
},
|
|
89
|
-
schema
|
|
90
|
-
},
|
|
91
|
-
name: RULE_NAME,
|
|
92
|
-
create,
|
|
93
|
-
defaultOptions
|
|
94
|
-
});
|
|
95
|
-
function create(context) {
|
|
96
|
-
const options = normalizeOptions(context.options);
|
|
97
|
-
const { rule } = options;
|
|
98
|
-
const collector = ER2.useComponentCollector(context);
|
|
99
|
-
const collectorLegacy = ER2.useComponentCollectorLegacy();
|
|
100
|
-
return {
|
|
101
|
-
...collector.listeners,
|
|
102
|
-
...collectorLegacy.listeners,
|
|
103
|
-
"Program:exit"(program) {
|
|
104
|
-
const functionComponents = collector.ctx.getAllComponents(program);
|
|
105
|
-
const classComponents = collectorLegacy.ctx.getAllComponents(program);
|
|
106
|
-
for (const { node: component } of functionComponents.values()) {
|
|
107
|
-
const id = AST.getFunctionId(component);
|
|
108
|
-
if (id?.name == null) continue;
|
|
109
|
-
const name3 = id.name;
|
|
110
|
-
if (isValidName(name3, options)) return;
|
|
111
|
-
context.report({
|
|
112
|
-
messageId: "invalid",
|
|
113
|
-
node: id,
|
|
114
|
-
data: { name: name3, rule }
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
for (const { node: component } of classComponents.values()) {
|
|
118
|
-
const id = AST.getClassId(component);
|
|
119
|
-
if (id?.name == null) continue;
|
|
120
|
-
const name3 = id.name;
|
|
121
|
-
if (isValidName(name3, options)) continue;
|
|
122
|
-
context.report({
|
|
123
|
-
messageId: "invalid",
|
|
124
|
-
node: id,
|
|
125
|
-
data: { name: name3, rule }
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
function normalizeOptions(options) {
|
|
132
|
-
const opts = options[0];
|
|
133
|
-
const defaultOpts = defaultOptions[0];
|
|
134
|
-
if (opts == null) return defaultOpts;
|
|
135
|
-
return {
|
|
136
|
-
...defaultOpts,
|
|
137
|
-
...typeof opts === "string" ? { rule: opts } : {
|
|
138
|
-
...opts,
|
|
139
|
-
excepts: opts.excepts?.map((s) => RegExp.toRegExp(s)) ?? []
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
function isValidName(name3, options) {
|
|
144
|
-
if (name3 == null) return true;
|
|
145
|
-
if (options.excepts.some((regex) => regex.test(name3))) return true;
|
|
146
|
-
const normalized = name3.split(".").at(-1) ?? name3;
|
|
147
|
-
switch (options.rule) {
|
|
148
|
-
case "CONSTANT_CASE":
|
|
149
|
-
return RegExp.CONSTANT_CASE.test(normalized);
|
|
150
|
-
case "PascalCase":
|
|
151
|
-
if (normalized.length > 3 && /^[A-Z]+$/u.test(normalized)) {
|
|
152
|
-
return options.allowAllCaps;
|
|
153
|
-
}
|
|
154
|
-
return RegExp.PASCAL_CASE.test(normalized);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
var RULE_NAME2 = "context-name";
|
|
158
|
-
var context_name_default = createRule({
|
|
159
|
-
meta: {
|
|
160
|
-
type: "problem",
|
|
161
|
-
docs: {
|
|
162
|
-
description: "Enforces context name to be a valid component name with the suffix `Context`."
|
|
163
|
-
},
|
|
164
|
-
messages: {
|
|
165
|
-
invalid: "A context name must be a valid component name with the suffix 'Context'."
|
|
166
|
-
},
|
|
167
|
-
schema: []
|
|
168
|
-
},
|
|
169
|
-
name: RULE_NAME2,
|
|
170
|
-
create: create2,
|
|
171
|
-
defaultOptions: []
|
|
172
|
-
});
|
|
173
|
-
function create2(context) {
|
|
174
|
-
if (!context.sourceCode.text.includes("createContext")) return {};
|
|
175
|
-
return {
|
|
176
|
-
CallExpression(node) {
|
|
177
|
-
if (!ER2.isCreateContextCall(context, node)) return;
|
|
178
|
-
const id = ER2.getInstanceId(node);
|
|
179
|
-
if (id == null) return;
|
|
180
|
-
const name3 = match(id).with({ type: AST_NODE_TYPES.Identifier, name: P.select() }, identity).with({ type: AST_NODE_TYPES.MemberExpression, property: { name: P.select(P.string) } }, identity).otherwise(() => null);
|
|
181
|
-
if (name3 != null && ER2.isComponentName(name3) && name3.endsWith("Context")) return;
|
|
182
|
-
context.report({
|
|
183
|
-
messageId: "invalid",
|
|
184
|
-
node: id
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
var RULE_NAME3 = "filename";
|
|
190
|
-
var defaultOptions2 = [
|
|
191
|
-
{
|
|
192
|
-
excepts: ["^index$"],
|
|
193
|
-
extensions: [".js", ".jsx", ".ts", ".tsx"],
|
|
194
|
-
rule: "PascalCase"
|
|
195
|
-
}
|
|
196
|
-
];
|
|
197
|
-
var schema2 = [
|
|
198
|
-
{
|
|
199
|
-
anyOf: [
|
|
200
|
-
{
|
|
201
|
-
type: "string",
|
|
202
|
-
enum: ["PascalCase", "camelCase", "kebab-case", "snake_case"]
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
type: "object",
|
|
206
|
-
additionalProperties: false,
|
|
207
|
-
properties: {
|
|
208
|
-
excepts: {
|
|
209
|
-
type: "array",
|
|
210
|
-
items: { type: "string", format: "regex" }
|
|
211
|
-
},
|
|
212
|
-
extensions: {
|
|
213
|
-
type: "array",
|
|
214
|
-
items: { type: "string" },
|
|
215
|
-
uniqueItems: true
|
|
216
|
-
},
|
|
217
|
-
rule: {
|
|
218
|
-
type: "string",
|
|
219
|
-
enum: ["PascalCase", "camelCase", "kebab-case", "snake_case"]
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
]
|
|
224
|
-
}
|
|
225
|
-
];
|
|
226
|
-
var filename_default = createRule({
|
|
227
|
-
meta: {
|
|
228
|
-
type: "problem",
|
|
229
|
-
defaultOptions: [...defaultOptions2],
|
|
230
|
-
docs: {
|
|
231
|
-
description: "Enforces consistent file naming conventions."
|
|
232
|
-
},
|
|
233
|
-
messages: {
|
|
234
|
-
filenameEmpty: "A file must have non-empty name.",
|
|
235
|
-
filenameInvalid: "A file with name '{{name}}' does not match {{rule}}. Rename it to '{{suggestion}}'."
|
|
236
|
-
},
|
|
237
|
-
schema: schema2
|
|
238
|
-
},
|
|
239
|
-
name: RULE_NAME3,
|
|
240
|
-
create: create3,
|
|
241
|
-
defaultOptions: defaultOptions2
|
|
242
|
-
});
|
|
243
|
-
function create3(context) {
|
|
244
|
-
const options = context.options[0] ?? defaultOptions2[0];
|
|
245
|
-
const rule = typeof options === "string" ? options : options.rule ?? "PascalCase";
|
|
246
|
-
const excepts = typeof options === "string" ? [] : options.excepts ?? [];
|
|
247
|
-
function validate(name3, casing = rule, ignores = excepts) {
|
|
248
|
-
const shouldIgnore = ignores.map((s) => RegExp.toRegExp(s)).some((pattern) => pattern.test(name3));
|
|
249
|
-
if (shouldIgnore) return true;
|
|
250
|
-
return match(casing).with("PascalCase", () => RegExp.PASCAL_CASE.test(name3)).with("camelCase", () => RegExp.CAMEL_CASE.test(name3)).with("kebab-case", () => RegExp.KEBAB_CASE.test(name3)).with("snake_case", () => RegExp.SNAKE_CASE.test(name3)).exhaustive();
|
|
251
|
-
}
|
|
252
|
-
function getSuggestion(name3, casing = rule) {
|
|
253
|
-
return match(casing).with("PascalCase", () => pascalCase(name3)).with("camelCase", () => camelCase(name3)).with("kebab-case", () => kebabCase(name3)).with("snake_case", () => snakeCase(name3)).exhaustive();
|
|
254
|
-
}
|
|
255
|
-
return {
|
|
256
|
-
Program(node) {
|
|
257
|
-
const [basename = "", ...rest] = path.basename(context.filename).split(".");
|
|
258
|
-
if (basename.length === 0) {
|
|
259
|
-
context.report({ messageId: "filenameEmpty", node });
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
if (validate(basename)) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
context.report({
|
|
266
|
-
messageId: "filenameInvalid",
|
|
267
|
-
node,
|
|
268
|
-
data: {
|
|
269
|
-
name: context.filename,
|
|
270
|
-
rule,
|
|
271
|
-
suggestion: [getSuggestion(basename), ...rest].join(".")
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
var RULE_NAME4 = "filename-extension";
|
|
278
|
-
var defaultOptions3 = [{
|
|
279
|
-
allow: "as-needed",
|
|
280
|
-
extensions: [".jsx", ".tsx"],
|
|
281
|
-
ignoreFilesWithoutCode: false
|
|
282
|
-
}];
|
|
283
|
-
var schema3 = [
|
|
284
|
-
{
|
|
285
|
-
anyOf: [
|
|
286
|
-
{
|
|
287
|
-
type: "string",
|
|
288
|
-
enum: ["always", "as-needed"]
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
type: "object",
|
|
292
|
-
additionalProperties: false,
|
|
293
|
-
properties: {
|
|
294
|
-
allow: {
|
|
295
|
-
type: "string",
|
|
296
|
-
enum: ["always", "as-needed"]
|
|
297
|
-
},
|
|
298
|
-
extensions: {
|
|
299
|
-
type: "array",
|
|
300
|
-
items: {
|
|
301
|
-
type: "string"
|
|
302
|
-
},
|
|
303
|
-
uniqueItems: true
|
|
304
|
-
},
|
|
305
|
-
ignoreFilesWithoutCode: {
|
|
306
|
-
type: "boolean"
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
]
|
|
311
|
-
}
|
|
312
|
-
];
|
|
313
|
-
var filename_extension_default = createRule({
|
|
314
|
-
meta: {
|
|
315
|
-
type: "problem",
|
|
316
|
-
defaultOptions: [...defaultOptions3],
|
|
317
|
-
docs: {
|
|
318
|
-
description: "Enforces consistent file naming conventions."
|
|
319
|
-
},
|
|
320
|
-
messages: {
|
|
321
|
-
useJsxFileExtension: "Use {{extensions}} file extension for JSX files.",
|
|
322
|
-
useNonJsxFileExtension: "Do not use {{extensions}} file extension for files without JSX."
|
|
323
|
-
},
|
|
324
|
-
schema: schema3
|
|
325
|
-
},
|
|
326
|
-
name: RULE_NAME4,
|
|
327
|
-
create: create4,
|
|
328
|
-
defaultOptions: defaultOptions3
|
|
329
|
-
});
|
|
330
|
-
function create4(context) {
|
|
331
|
-
const options = context.options[0] ?? defaultOptions3[0];
|
|
332
|
-
const allow = isObject(options) ? options.allow : options;
|
|
333
|
-
const extensions = isObject(options) && "extensions" in options ? options.extensions : defaultOptions3[0].extensions;
|
|
334
|
-
const extensionsString = extensions.map((ext) => `'${ext}'`).join(", ");
|
|
335
|
-
const filename = context.filename;
|
|
336
|
-
let hasJSXNode = false;
|
|
337
|
-
return {
|
|
338
|
-
JSXElement() {
|
|
339
|
-
hasJSXNode = true;
|
|
340
|
-
},
|
|
341
|
-
JSXFragment() {
|
|
342
|
-
hasJSXNode = true;
|
|
343
|
-
},
|
|
344
|
-
"Program:exit"(program) {
|
|
345
|
-
const fileNameExt = filename.slice(filename.lastIndexOf("."));
|
|
346
|
-
const isJSXExt = extensions.includes(fileNameExt);
|
|
347
|
-
if (hasJSXNode && !isJSXExt) {
|
|
348
|
-
context.report({
|
|
349
|
-
messageId: "useJsxFileExtension",
|
|
350
|
-
node: program,
|
|
351
|
-
data: {
|
|
352
|
-
extensions: extensionsString
|
|
353
|
-
}
|
|
354
|
-
});
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
const hasCode = program.body.length > 0;
|
|
358
|
-
const ignoreFilesWithoutCode = isObject(options) && options.ignoreFilesWithoutCode === true;
|
|
359
|
-
if (!hasCode && ignoreFilesWithoutCode) {
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
if (!hasJSXNode && isJSXExt && allow === "as-needed") {
|
|
363
|
-
context.report({
|
|
364
|
-
messageId: "useNonJsxFileExtension",
|
|
365
|
-
node: program,
|
|
366
|
-
data: {
|
|
367
|
-
extensions: extensionsString
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
var RULE_NAME5 = "use-state";
|
|
375
|
-
var RULE_FEATURES = [];
|
|
376
|
-
var use_state_default = createRule({
|
|
377
|
-
meta: {
|
|
378
|
-
type: "problem",
|
|
379
|
-
docs: {
|
|
380
|
-
description: "Enforces destructuring and symmetric naming of `useState` hook value and setter.",
|
|
381
|
-
[Symbol.for("rule_features")]: RULE_FEATURES
|
|
382
|
-
},
|
|
383
|
-
messages: {
|
|
384
|
-
invalidSetterNaming: "The setter should be named 'set' followed by the capitalized state variable name, e.g., 'setState' for 'state'.",
|
|
385
|
-
missingDestructuring: "useState should be destructured into a value and setter pair, e.g., const [state, setState] = useState(...)."
|
|
386
|
-
},
|
|
387
|
-
schema: []
|
|
388
|
-
},
|
|
389
|
-
name: RULE_NAME5,
|
|
390
|
-
create: create5,
|
|
391
|
-
defaultOptions: []
|
|
392
|
-
});
|
|
393
|
-
function create5(context) {
|
|
394
|
-
const alias = getSettingsFromContext(context).additionalHooks.useState ?? [];
|
|
395
|
-
const isUseStateCall = ER2.isReactHookCallWithNameAlias(context, "useState", alias);
|
|
396
|
-
return {
|
|
397
|
-
CallExpression(node) {
|
|
398
|
-
if (!isUseStateCall(node)) {
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
if (node.parent.type !== AST_NODE_TYPES.VariableDeclarator) {
|
|
402
|
-
context.report({
|
|
403
|
-
messageId: "missingDestructuring",
|
|
404
|
-
node
|
|
405
|
-
});
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
const id = ER2.getInstanceId(node);
|
|
409
|
-
if (id?.type !== AST_NODE_TYPES.ArrayPattern) {
|
|
410
|
-
context.report({
|
|
411
|
-
messageId: "missingDestructuring",
|
|
412
|
-
node: id ?? node
|
|
413
|
-
});
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
const [value, setter] = id.elements;
|
|
417
|
-
if (value == null || setter == null) {
|
|
418
|
-
context.report({
|
|
419
|
-
messageId: "missingDestructuring",
|
|
420
|
-
node: id
|
|
421
|
-
});
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
const setterName = match(setter).with({ type: AST_NODE_TYPES.Identifier }, (id2) => id2.name).otherwise(() => null);
|
|
425
|
-
if (setterName == null || !setterName.startsWith("set")) {
|
|
426
|
-
context.report({
|
|
427
|
-
messageId: "invalidSetterNaming",
|
|
428
|
-
node: setter
|
|
429
|
-
});
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
const valueName = match(value).with({ type: AST_NODE_TYPES.Identifier }, ({ name: name3 }) => snakeCase(name3)).with({ type: AST_NODE_TYPES.ObjectPattern }, ({ properties }) => {
|
|
433
|
-
const values = properties.reduce((acc, prop) => {
|
|
434
|
-
if (prop.type === AST_NODE_TYPES.Property && prop.key.type === AST_NODE_TYPES.Identifier) {
|
|
435
|
-
return [...acc, prop.key.name];
|
|
436
|
-
}
|
|
437
|
-
return acc;
|
|
438
|
-
}, []);
|
|
439
|
-
return values.join("_");
|
|
440
|
-
}).otherwise(() => null);
|
|
441
|
-
if (valueName == null) {
|
|
442
|
-
context.report({
|
|
443
|
-
messageId: "invalidSetterNaming",
|
|
444
|
-
node: value
|
|
445
|
-
});
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
if (snakeCase(setterName) !== `set_${valueName}`) {
|
|
449
|
-
context.report({
|
|
450
|
-
messageId: "invalidSetterNaming",
|
|
451
|
-
node: setter
|
|
452
|
-
});
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// src/plugin.ts
|
|
460
|
-
var plugin = {
|
|
461
|
-
meta: {
|
|
462
|
-
name: name2,
|
|
463
|
-
version
|
|
464
|
-
},
|
|
465
|
-
rules: {
|
|
466
|
-
["component-name"]: component_name_default,
|
|
467
|
-
["context-name"]: context_name_default,
|
|
468
|
-
["filename"]: filename_default,
|
|
469
|
-
["filename-extension"]: filename_extension_default,
|
|
470
|
-
["use-state"]: use_state_default
|
|
471
|
-
}
|
|
472
|
-
};
|
|
473
|
-
|
|
474
|
-
// src/index.ts
|
|
475
|
-
function makeConfig(config) {
|
|
476
|
-
return {
|
|
477
|
-
...config,
|
|
478
|
-
plugins: {
|
|
479
|
-
"react-naming-convention": plugin
|
|
480
|
-
}
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
function makeLegacyConfig({ rules: rules2 }) {
|
|
484
|
-
return {
|
|
485
|
-
plugins: ["react-naming-convention"],
|
|
486
|
-
rules: rules2
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
var index_default = {
|
|
490
|
-
...plugin,
|
|
491
|
-
configs: {
|
|
492
|
-
["recommended"]: makeConfig(recommended_exports),
|
|
493
|
-
["recommended-legacy"]: makeLegacyConfig(recommended_exports)
|
|
494
|
-
}
|
|
495
|
-
};
|
|
496
|
-
|
|
497
|
-
export { index_default as default };
|