eslint-plugin-nextfriday 5.0.1 → 5.0.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # eslint-plugin-nextfriday
2
2
 
3
+ ## 5.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#141](https://github.com/next-friday/eslint-plugin-nextfriday/pull/141) [`9a9ba41`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/9a9ba415a7a188ec0f0180cefd807e562a489bc8) Thanks [@joetakara](https://github.com/joetakara)! - Expand `enforce-constant-case` to enforce SCREAMING_SNAKE_CASE on RegExp and bigint constants, and refine `new RegExp()` detection to skip dynamic arguments.
8
+ - RegExp literals (`/foo/`) and `new RegExp("foo", "g")` (all args must be string literals) are now flagged when named in camelCase at global scope.
9
+ - BigInt literals (`100n`, `-1n`) are now flagged consistently with number literals.
10
+ - `new RegExp(userInput)` and other non-literal RegExp constructors are intentionally ignored — only statically-constructed patterns are treated as magic values.
11
+
3
12
  ## 5.0.1
4
13
 
5
14
  ### Patch Changes
@@ -1,23 +1,25 @@
1
1
  # enforce-constant-case
2
2
 
3
- Enforce SCREAMING_SNAKE_CASE for global magic-number and magic-text constants.
3
+ Enforce SCREAMING_SNAKE_CASE for global magic-number, magic-text, bigint, and RegExp constants.
4
4
 
5
5
  ## Rule Details
6
6
 
7
- This rule ensures that global-scope `const` declarations bound to a **magic number** or **magic text** literal use SCREAMING_SNAKE_CASE. The rule scope is intentionally narrow:
7
+ This rule ensures that global-scope `const` declarations bound to a **magic literal** value use SCREAMING_SNAKE_CASE. The rule scope is intentionally narrow:
8
8
 
9
9
  - A magic text constant is a string literal: `const API_URL = "https://api.example.com"`
10
10
  - A magic number constant is a number literal (including a unary `-`/`+` over a numeric literal): `const PAGE_LIMIT = 10`, `const OFFSET = -1`
11
+ - A bigint constant is a bigint literal (including a unary `-`/`+` over a bigint literal): `const BIG_LIMIT = 9007199254740993n`, `const NEGATIVE_BIG = -1n`
12
+ - A RegExp constant is a regex literal, or a `new RegExp(...)` call whose arguments are **all** string literals: `const PHONE_REGEX = /^[0-9]+$/`, `const EMAIL_PATTERN = new RegExp("^.+@.+$", "g")`
11
13
 
12
- Anything else is **not** checked: booleans, RegExp, template literals (static or dynamic), arrays, objects, `as const` assertions, function calls, identifiers, member expressions, JSX. Use whatever name fits the value (`metadata`, `viewport`, `statusMap`, `phoneRegex`, `isEnabled`, etc.) — the rule will not flag it.
14
+ Anything else is **not** checked: booleans, template literals (static or dynamic), arrays, objects, `as const` assertions, function calls, identifiers, member expressions, JSX, and `new RegExp(dynamicValue)` (where the pattern argument is non-literal). Use whatever name fits the value (`metadata`, `viewport`, `statusMap`, `isEnabled`, `dynamicPattern`, etc.) — the rule will not flag it.
13
15
 
14
16
  Only global scope (top-level of a file) is checked. Local scope constants inside functions are not checked by this rule.
15
17
 
16
18
  **Config files are exempt.** Files matching `*.config.{ts,mjs,cjs,js}`, `*.rc.*`, `*.setup.*`, `*.spec.*`, `*.test.*`, `.eslintrc*`, `.babelrc*`, and `.prettierrc*` skip this rule entirely. This avoids conflicts with framework conventions that require specific identifier names — e.g. Next.js expects `nextConfig` (not `NEXT_CONFIG`) in `next.config.ts`, Vite expects `config`, Tailwind expects `config`, etc.
17
19
 
18
- ### Why magic numbers and magic text only?
20
+ ### Why only static literal values?
19
21
 
20
- Reserved framework export names commonly bind to objects (Next.js App Router exports `metadata`, `viewport`, `generateStaticParams`, `dynamic`, `revalidate`, `runtime`, `fetchCache`, `dynamicParams`, `preferredRegion`, `maxDuration`; React Server Components and others have similar patterns). Forcing SCREAMING_SNAKE_CASE on any static-shaped initializer would rename those exports and break framework integration. Restricting the rule to bare number and string literals keeps the convention where it adds value (avoiding magic constants scattered through code) without colliding with framework-owned names.
22
+ Reserved framework export names commonly bind to objects (Next.js App Router exports `metadata`, `viewport`, `generateStaticParams`, `dynamic`, `revalidate`, `runtime`, `fetchCache`, `dynamicParams`, `preferredRegion`, `maxDuration`; React Server Components and others have similar patterns). Forcing SCREAMING_SNAKE_CASE on any static-shaped initializer would rename those exports and break framework integration. Restricting the rule to bare number, string, bigint, and statically-constructed RegExp values keeps the convention where it adds value (avoiding magic constants scattered through code) without colliding with framework-owned names or flagging dynamically-constructed patterns.
21
23
 
22
24
  ## Examples
23
25
 
@@ -29,6 +31,9 @@ const pageLimit = 10;
29
31
  const apiBaseUrl = "https://api.example.com";
30
32
  const negativeOne = -1;
31
33
  const default_theme = "dark";
34
+ const phoneRegex = /^[0-9]{10}$/;
35
+ const emailPattern = new RegExp("^.+@.+$");
36
+ const bigLimit = 9007199254740993n;
32
37
  ```
33
38
 
34
39
  ### Correct
@@ -39,12 +44,15 @@ const PAGE_LIMIT = 10;
39
44
  const API_BASE_URL = "https://api.example.com";
40
45
  const NEGATIVE_ONE = -1;
41
46
  const DEFAULT_THEME = "dark";
47
+ const PHONE_REGEX = /^[0-9]{10}$/;
48
+ const EMAIL_PATTERN = new RegExp("^.+@.+$", "g");
49
+ const BIG_LIMIT = 9007199254740993n;
42
50
 
43
51
  const isProduction = true;
44
52
  const hasAccess = false;
45
53
  const featureEnabled = true;
46
54
 
47
- const phoneRegex = /^[0-9]{10}$/;
55
+ const dynamicPattern = new RegExp(userInput);
48
56
  const template = `hello world`;
49
57
  const skeletonItems = [1, 2, 3, 4, 5];
50
58
  const mapStyle = { height: "320px", width: "100%" };
package/lib/index.cjs CHANGED
@@ -32,7 +32,7 @@ let emoji_regex = require("emoji-regex");
32
32
  emoji_regex = __toESM(emoji_regex, 1);
33
33
  //#region package.json
34
34
  var name = "eslint-plugin-nextfriday";
35
- var version = "5.0.1";
35
+ var version = "5.0.2";
36
36
  //#endregion
37
37
  //#region src/rules/boolean-naming-prefix.ts
38
38
  const createRule$26 = _typescript_eslint_utils.ESLintUtils.RuleCreator((name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`);
@@ -162,10 +162,15 @@ const SCREAMING_SNAKE_CASE_REGEX$1 = /^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$/;
162
162
  const SNAKE_CASE_REGEX = /^[a-z]+_[a-z0-9_]*$/;
163
163
  const toScreamingSnakeCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toUpperCase();
164
164
  const isMagicLiteral = (init) => {
165
- if (init.type === _typescript_eslint_utils.AST_NODE_TYPES.Literal) return typeof init.value === "string" || typeof init.value === "number";
165
+ if (init.type === _typescript_eslint_utils.AST_NODE_TYPES.Literal) return typeof init.value === "string" || typeof init.value === "number" || typeof init.value === "bigint" || "regex" in init;
166
166
  if (init.type === _typescript_eslint_utils.AST_NODE_TYPES.UnaryExpression) {
167
167
  const { argument, operator } = init;
168
- return (operator === "-" || operator === "+") && argument.type === _typescript_eslint_utils.AST_NODE_TYPES.Literal && typeof argument.value === "number";
168
+ if (operator !== "-" && operator !== "+") return false;
169
+ return argument.type === _typescript_eslint_utils.AST_NODE_TYPES.Literal && (typeof argument.value === "number" || typeof argument.value === "bigint");
170
+ }
171
+ if (init.type === _typescript_eslint_utils.AST_NODE_TYPES.NewExpression) {
172
+ if (init.callee.type !== _typescript_eslint_utils.AST_NODE_TYPES.Identifier || init.callee.name !== "RegExp") return false;
173
+ return init.arguments.every((arg) => arg.type === _typescript_eslint_utils.AST_NODE_TYPES.Literal && typeof arg.value === "string");
169
174
  }
170
175
  return false;
171
176
  };
@@ -179,7 +184,7 @@ const enforceConstantCase = createRule$25({
179
184
  name: "enforce-constant-case",
180
185
  meta: {
181
186
  type: "suggestion",
182
- docs: { description: "Enforce SCREAMING_SNAKE_CASE for global magic-number and magic-text constants" },
187
+ docs: { description: "Enforce SCREAMING_SNAKE_CASE for global magic-number, magic-text, bigint, and RegExp constants" },
183
188
  messages: {
184
189
  useScreamingSnakeCase: "Constant '{{ name }}' should use SCREAMING_SNAKE_CASE. Rename to '{{ suggestion }}'.",
185
190
  noSnakeCase: "Global constant '{{ name }}' should not use snake_case. Rename to '{{ suggestion }}'."