eslint-plugin-nextfriday 1.5.3 → 1.7.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/lib/index.cjs CHANGED
@@ -40,7 +40,7 @@ module.exports = __toCommonJS(index_exports);
40
40
  // package.json
41
41
  var package_default = {
42
42
  name: "eslint-plugin-nextfriday",
43
- version: "1.5.3",
43
+ version: "1.7.0",
44
44
  description: "A comprehensive ESLint plugin providing custom rules and configurations for Next Friday development workflows.",
45
45
  keywords: [
46
46
  "eslint",
@@ -154,12 +154,143 @@ var package_default = {
154
154
  }
155
155
  };
156
156
 
157
- // src/rules/enforce-readonly-component-props.ts
157
+ // src/rules/boolean-naming-prefix.ts
158
158
  var import_utils = require("@typescript-eslint/utils");
159
159
  var createRule = import_utils.ESLintUtils.RuleCreator(
160
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
160
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
161
+ );
162
+ var BOOLEAN_PREFIXES = ["is", "has", "should", "can", "did", "will", "was", "are", "does", "had"];
163
+ var startsWithBooleanPrefix = (name) => {
164
+ const lowerName = name.toLowerCase();
165
+ return BOOLEAN_PREFIXES.some((prefix) => {
166
+ if (!lowerName.startsWith(prefix)) {
167
+ return false;
168
+ }
169
+ if (name.length === prefix.length) {
170
+ return true;
171
+ }
172
+ const nextChar = name.charAt(prefix.length);
173
+ return nextChar === nextChar.toUpperCase();
174
+ });
175
+ };
176
+ var isBooleanLiteral = (node) => node.type === import_utils.AST_NODE_TYPES.Literal && typeof node.value === "boolean";
177
+ var isBooleanExpression = (node) => {
178
+ if (isBooleanLiteral(node)) {
179
+ return true;
180
+ }
181
+ if (node.type === import_utils.AST_NODE_TYPES.UnaryExpression && node.operator === "!") {
182
+ return true;
183
+ }
184
+ if (node.type === import_utils.AST_NODE_TYPES.BinaryExpression) {
185
+ const comparisonOperators = ["===", "!==", "==", "!=", "<", ">", "<=", ">=", "in", "instanceof"];
186
+ return comparisonOperators.includes(node.operator);
187
+ }
188
+ if (node.type === import_utils.AST_NODE_TYPES.LogicalExpression) {
189
+ return node.operator === "&&" || node.operator === "||";
190
+ }
191
+ return false;
192
+ };
193
+ var hasBooleanTypeAnnotation = (node) => {
194
+ if (node.type === import_utils.AST_NODE_TYPES.Identifier) {
195
+ const { typeAnnotation } = node;
196
+ if (typeAnnotation?.typeAnnotation.type === import_utils.AST_NODE_TYPES.TSBooleanKeyword) {
197
+ return true;
198
+ }
199
+ }
200
+ if ("id" in node && node.id.type === import_utils.AST_NODE_TYPES.Identifier) {
201
+ const { typeAnnotation } = node.id;
202
+ if (typeAnnotation?.typeAnnotation.type === import_utils.AST_NODE_TYPES.TSBooleanKeyword) {
203
+ return true;
204
+ }
205
+ }
206
+ return false;
207
+ };
208
+ var booleanNamingPrefix = createRule({
209
+ name: "boolean-naming-prefix",
210
+ meta: {
211
+ type: "suggestion",
212
+ docs: {
213
+ description: "Enforce boolean variables and parameters to have a prefix like is, has, should, can, did, will for better readability"
214
+ },
215
+ messages: {
216
+ missingPrefix: "Boolean variable '{{name}}' should have a prefix like is, has, should, can, did, or will. Example: 'is{{suggestedName}}' or 'has{{suggestedName}}'."
217
+ },
218
+ schema: []
219
+ },
220
+ defaultOptions: [],
221
+ create(context) {
222
+ const checkBooleanNaming = (name, node) => {
223
+ if (startsWithBooleanPrefix(name)) {
224
+ return;
225
+ }
226
+ const suggestedName = name.charAt(0).toUpperCase() + name.slice(1);
227
+ context.report({
228
+ node,
229
+ messageId: "missingPrefix",
230
+ data: { name, suggestedName }
231
+ });
232
+ };
233
+ return {
234
+ VariableDeclarator(node) {
235
+ if (node.id.type !== import_utils.AST_NODE_TYPES.Identifier) {
236
+ return;
237
+ }
238
+ const { name } = node.id;
239
+ if (hasBooleanTypeAnnotation(node)) {
240
+ checkBooleanNaming(name, node.id);
241
+ return;
242
+ }
243
+ if (node.init && isBooleanExpression(node.init)) {
244
+ checkBooleanNaming(name, node.id);
245
+ }
246
+ },
247
+ FunctionDeclaration(node) {
248
+ node.params.forEach((param) => {
249
+ if (param.type === import_utils.AST_NODE_TYPES.Identifier && hasBooleanTypeAnnotation(param)) {
250
+ checkBooleanNaming(param.name, param);
251
+ }
252
+ if (param.type === import_utils.AST_NODE_TYPES.AssignmentPattern) {
253
+ if (param.left.type === import_utils.AST_NODE_TYPES.Identifier && param.right && isBooleanLiteral(param.right)) {
254
+ checkBooleanNaming(param.left.name, param.left);
255
+ }
256
+ }
257
+ });
258
+ },
259
+ FunctionExpression(node) {
260
+ node.params.forEach((param) => {
261
+ if (param.type === import_utils.AST_NODE_TYPES.Identifier && hasBooleanTypeAnnotation(param)) {
262
+ checkBooleanNaming(param.name, param);
263
+ }
264
+ if (param.type === import_utils.AST_NODE_TYPES.AssignmentPattern) {
265
+ if (param.left.type === import_utils.AST_NODE_TYPES.Identifier && param.right && isBooleanLiteral(param.right)) {
266
+ checkBooleanNaming(param.left.name, param.left);
267
+ }
268
+ }
269
+ });
270
+ },
271
+ ArrowFunctionExpression(node) {
272
+ node.params.forEach((param) => {
273
+ if (param.type === import_utils.AST_NODE_TYPES.Identifier && hasBooleanTypeAnnotation(param)) {
274
+ checkBooleanNaming(param.name, param);
275
+ }
276
+ if (param.type === import_utils.AST_NODE_TYPES.AssignmentPattern) {
277
+ if (param.left.type === import_utils.AST_NODE_TYPES.Identifier && param.right && isBooleanLiteral(param.right)) {
278
+ checkBooleanNaming(param.left.name, param.left);
279
+ }
280
+ }
281
+ });
282
+ }
283
+ };
284
+ }
285
+ });
286
+ var boolean_naming_prefix_default = booleanNamingPrefix;
287
+
288
+ // src/rules/enforce-readonly-component-props.ts
289
+ var import_utils2 = require("@typescript-eslint/utils");
290
+ var createRule2 = import_utils2.ESLintUtils.RuleCreator(
291
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
161
292
  );
162
- var enforceReadonlyComponentProps = createRule({
293
+ var enforceReadonlyComponentProps = createRule2({
163
294
  name: "enforce-readonly-component-props",
164
295
  meta: {
165
296
  type: "suggestion",
@@ -175,54 +306,54 @@ var enforceReadonlyComponentProps = createRule({
175
306
  defaultOptions: [],
176
307
  create(context) {
177
308
  function hasJSXInConditional(node) {
178
- return node.consequent.type === import_utils.AST_NODE_TYPES.JSXElement || node.consequent.type === import_utils.AST_NODE_TYPES.JSXFragment || node.alternate.type === import_utils.AST_NODE_TYPES.JSXElement || node.alternate.type === import_utils.AST_NODE_TYPES.JSXFragment;
309
+ return node.consequent.type === import_utils2.AST_NODE_TYPES.JSXElement || node.consequent.type === import_utils2.AST_NODE_TYPES.JSXFragment || node.alternate.type === import_utils2.AST_NODE_TYPES.JSXElement || node.alternate.type === import_utils2.AST_NODE_TYPES.JSXFragment;
179
310
  }
180
311
  function hasJSXInLogical(node) {
181
- return node.right.type === import_utils.AST_NODE_TYPES.JSXElement || node.right.type === import_utils.AST_NODE_TYPES.JSXFragment;
312
+ return node.right.type === import_utils2.AST_NODE_TYPES.JSXElement || node.right.type === import_utils2.AST_NODE_TYPES.JSXFragment;
182
313
  }
183
314
  function hasJSXReturn(block) {
184
315
  return block.body.some((stmt) => {
185
- if (stmt.type === import_utils.AST_NODE_TYPES.ReturnStatement && stmt.argument) {
186
- return stmt.argument.type === import_utils.AST_NODE_TYPES.JSXElement || stmt.argument.type === import_utils.AST_NODE_TYPES.JSXFragment || stmt.argument.type === import_utils.AST_NODE_TYPES.ConditionalExpression && hasJSXInConditional(stmt.argument) || stmt.argument.type === import_utils.AST_NODE_TYPES.LogicalExpression && hasJSXInLogical(stmt.argument);
316
+ if (stmt.type === import_utils2.AST_NODE_TYPES.ReturnStatement && stmt.argument) {
317
+ return stmt.argument.type === import_utils2.AST_NODE_TYPES.JSXElement || stmt.argument.type === import_utils2.AST_NODE_TYPES.JSXFragment || stmt.argument.type === import_utils2.AST_NODE_TYPES.ConditionalExpression && hasJSXInConditional(stmt.argument) || stmt.argument.type === import_utils2.AST_NODE_TYPES.LogicalExpression && hasJSXInLogical(stmt.argument);
187
318
  }
188
319
  return false;
189
320
  });
190
321
  }
191
- function isReactComponent(node) {
192
- if (node.type === import_utils.AST_NODE_TYPES.ArrowFunctionExpression) {
193
- if (node.body.type === import_utils.AST_NODE_TYPES.JSXElement || node.body.type === import_utils.AST_NODE_TYPES.JSXFragment) {
322
+ function isReactComponent2(node) {
323
+ if (node.type === import_utils2.AST_NODE_TYPES.ArrowFunctionExpression) {
324
+ if (node.body.type === import_utils2.AST_NODE_TYPES.JSXElement || node.body.type === import_utils2.AST_NODE_TYPES.JSXFragment) {
194
325
  return true;
195
326
  }
196
- if (node.body.type === import_utils.AST_NODE_TYPES.BlockStatement) {
327
+ if (node.body.type === import_utils2.AST_NODE_TYPES.BlockStatement) {
197
328
  return hasJSXReturn(node.body);
198
329
  }
199
- } else if (node.type === import_utils.AST_NODE_TYPES.FunctionExpression || node.type === import_utils.AST_NODE_TYPES.FunctionDeclaration) {
200
- if (node.body && node.body.type === import_utils.AST_NODE_TYPES.BlockStatement) {
330
+ } else if (node.type === import_utils2.AST_NODE_TYPES.FunctionExpression || node.type === import_utils2.AST_NODE_TYPES.FunctionDeclaration) {
331
+ if (node.body && node.body.type === import_utils2.AST_NODE_TYPES.BlockStatement) {
201
332
  return hasJSXReturn(node.body);
202
333
  }
203
334
  }
204
335
  return false;
205
336
  }
206
337
  function isNamedType(node) {
207
- return node.type === import_utils.AST_NODE_TYPES.TSTypeReference;
338
+ return node.type === import_utils2.AST_NODE_TYPES.TSTypeReference;
208
339
  }
209
340
  function isAlreadyReadonly(node) {
210
- if (node.type === import_utils.AST_NODE_TYPES.TSTypeReference && node.typeName) {
211
- if (node.typeName.type === import_utils.AST_NODE_TYPES.Identifier && node.typeName.name === "Readonly") {
341
+ if (node.type === import_utils2.AST_NODE_TYPES.TSTypeReference && node.typeName) {
342
+ if (node.typeName.type === import_utils2.AST_NODE_TYPES.Identifier && node.typeName.name === "Readonly") {
212
343
  return true;
213
344
  }
214
345
  }
215
346
  return false;
216
347
  }
217
348
  function checkFunction(node) {
218
- if (!isReactComponent(node)) {
349
+ if (!isReactComponent2(node)) {
219
350
  return;
220
351
  }
221
352
  if (node.params.length !== 1) {
222
353
  return;
223
354
  }
224
355
  const param = node.params[0];
225
- if (param.type === import_utils.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
356
+ if (param.type === import_utils2.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
226
357
  const { typeAnnotation } = param.typeAnnotation;
227
358
  if (isNamedType(typeAnnotation) && !isAlreadyReadonly(typeAnnotation)) {
228
359
  const { sourceCode } = context;
@@ -246,11 +377,132 @@ var enforceReadonlyComponentProps = createRule({
246
377
  });
247
378
  var enforce_readonly_component_props_default = enforceReadonlyComponentProps;
248
379
 
380
+ // src/rules/enforce-sorted-destructuring.ts
381
+ var import_utils3 = require("@typescript-eslint/utils");
382
+ var createRule3 = import_utils3.ESLintUtils.RuleCreator(
383
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
384
+ );
385
+ var enforceSortedDestructuring = createRule3({
386
+ name: "enforce-sorted-destructuring",
387
+ meta: {
388
+ type: "suggestion",
389
+ docs: {
390
+ description: "Enforce alphabetical sorting of destructured properties with defaults first"
391
+ },
392
+ schema: [],
393
+ messages: {
394
+ unsortedDestructuring: "Destructured properties should be sorted alphabetically. Properties with defaults should come first, sorted by type (string, number, boolean, object) then alphabetically."
395
+ }
396
+ },
397
+ defaultOptions: [],
398
+ create(context) {
399
+ function getPropertyName(property) {
400
+ if (property.type === import_utils3.AST_NODE_TYPES.RestElement) {
401
+ return null;
402
+ }
403
+ if (property.key.type === import_utils3.AST_NODE_TYPES.Identifier) {
404
+ return property.key.name;
405
+ }
406
+ return null;
407
+ }
408
+ function hasDefaultValue(property) {
409
+ return property.value.type === import_utils3.AST_NODE_TYPES.AssignmentPattern && Boolean(property.value.right);
410
+ }
411
+ function getDefaultValueType(property) {
412
+ if (!hasDefaultValue(property)) {
413
+ return "none";
414
+ }
415
+ const assignmentPattern = property.value;
416
+ const { right } = assignmentPattern;
417
+ if (!right) {
418
+ return "none";
419
+ }
420
+ switch (right.type) {
421
+ case import_utils3.AST_NODE_TYPES.Literal:
422
+ if (typeof right.value === "string") {
423
+ return "string";
424
+ }
425
+ if (typeof right.value === "number") {
426
+ return "number";
427
+ }
428
+ if (typeof right.value === "boolean") {
429
+ return "boolean";
430
+ }
431
+ return "other";
432
+ case import_utils3.AST_NODE_TYPES.TemplateLiteral:
433
+ return "string";
434
+ case import_utils3.AST_NODE_TYPES.ObjectExpression:
435
+ case import_utils3.AST_NODE_TYPES.ArrayExpression:
436
+ return "object";
437
+ default:
438
+ return "other";
439
+ }
440
+ }
441
+ function getTypeSortOrder(type) {
442
+ const order = {
443
+ string: 0,
444
+ number: 1,
445
+ boolean: 2,
446
+ object: 3,
447
+ other: 4,
448
+ none: 5
449
+ };
450
+ return order[type] ?? 5;
451
+ }
452
+ function checkVariableDeclarator(node) {
453
+ if (node.id.type !== import_utils3.AST_NODE_TYPES.ObjectPattern) {
454
+ return;
455
+ }
456
+ const { properties } = node.id;
457
+ if (properties.length < 2) {
458
+ return;
459
+ }
460
+ const propertyInfo = properties.map((prop) => {
461
+ if (prop.type === import_utils3.AST_NODE_TYPES.RestElement) {
462
+ return null;
463
+ }
464
+ return {
465
+ property: prop,
466
+ name: getPropertyName(prop),
467
+ hasDefault: hasDefaultValue(prop),
468
+ defaultType: getDefaultValueType(prop)
469
+ };
470
+ }).filter((info) => info !== null && info.name !== null);
471
+ const sorted = [...propertyInfo].sort((a, b) => {
472
+ if (a.hasDefault && !b.hasDefault) {
473
+ return -1;
474
+ }
475
+ if (!a.hasDefault && b.hasDefault) {
476
+ return 1;
477
+ }
478
+ if (a.hasDefault && b.hasDefault) {
479
+ const typeComparison = getTypeSortOrder(a.defaultType) - getTypeSortOrder(b.defaultType);
480
+ if (typeComparison !== 0) {
481
+ return typeComparison;
482
+ }
483
+ }
484
+ return a.name.localeCompare(b.name);
485
+ });
486
+ const isSorted = propertyInfo.every((info, index) => info.name === sorted[index].name);
487
+ if (!isSorted) {
488
+ context.report({
489
+ node: node.id,
490
+ messageId: "unsortedDestructuring"
491
+ });
492
+ }
493
+ }
494
+ return {
495
+ VariableDeclarator: checkVariableDeclarator
496
+ };
497
+ }
498
+ });
499
+ var enforce_sorted_destructuring_default = enforceSortedDestructuring;
500
+
249
501
  // src/rules/file-kebab-case.ts
250
502
  var import_path = __toESM(require("path"), 1);
251
- var import_utils2 = require("@typescript-eslint/utils");
252
- var createRule2 = import_utils2.ESLintUtils.RuleCreator(
253
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
503
+ var import_utils4 = require("@typescript-eslint/utils");
504
+ var createRule4 = import_utils4.ESLintUtils.RuleCreator(
505
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
254
506
  );
255
507
  var isKebabCase = (str) => {
256
508
  if (/\.(config|rc|setup|spec|test)$/.test(str) || /^[a-z0-9]+(?:-[a-z0-9]+)*\.[a-z0-9]+(?:-[a-z0-9]+)*$/.test(str)) {
@@ -258,7 +510,7 @@ var isKebabCase = (str) => {
258
510
  }
259
511
  return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(str);
260
512
  };
261
- var fileKebabCase = createRule2({
513
+ var fileKebabCase = createRule4({
262
514
  name: "file-kebab-case",
263
515
  meta: {
264
516
  type: "problem",
@@ -279,8 +531,8 @@ var fileKebabCase = createRule2({
279
531
  if (ext !== ".ts" && ext !== ".js") {
280
532
  return;
281
533
  }
282
- const basename = import_path.default.basename(filename, ext);
283
- if (!isKebabCase(basename)) {
534
+ const basename2 = import_path.default.basename(filename, ext);
535
+ if (!isKebabCase(basename2)) {
284
536
  context.report({
285
537
  loc: { line: 1, column: 0 },
286
538
  messageId: "fileKebabCase"
@@ -294,12 +546,12 @@ var file_kebab_case_default = fileKebabCase;
294
546
 
295
547
  // src/rules/jsx-pascal-case.ts
296
548
  var import_path2 = __toESM(require("path"), 1);
297
- var import_utils3 = require("@typescript-eslint/utils");
298
- var createRule3 = import_utils3.ESLintUtils.RuleCreator(
299
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
549
+ var import_utils5 = require("@typescript-eslint/utils");
550
+ var createRule5 = import_utils5.ESLintUtils.RuleCreator(
551
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
300
552
  );
301
553
  var isPascalCase = (str) => /^[A-Z][a-zA-Z0-9]*$/.test(str) && !/^[A-Z]+$/.test(str);
302
- var jsxPascalCase = createRule3({
554
+ var jsxPascalCase = createRule5({
303
555
  name: "jsx-pascal-case",
304
556
  meta: {
305
557
  type: "problem",
@@ -320,8 +572,8 @@ var jsxPascalCase = createRule3({
320
572
  if (ext !== ".jsx" && ext !== ".tsx") {
321
573
  return;
322
574
  }
323
- const basename = import_path2.default.basename(filename, ext);
324
- if (!isPascalCase(basename)) {
575
+ const basename2 = import_path2.default.basename(filename, ext);
576
+ if (!isPascalCase(basename2)) {
325
577
  context.report({
326
578
  loc: { line: 1, column: 0 },
327
579
  messageId: "jsxPascalCase"
@@ -333,13 +585,136 @@ var jsxPascalCase = createRule3({
333
585
  });
334
586
  var jsx_pascal_case_default = jsxPascalCase;
335
587
 
588
+ // src/rules/no-direct-date.ts
589
+ var import_utils6 = require("@typescript-eslint/utils");
590
+ var createRule6 = import_utils6.ESLintUtils.RuleCreator(
591
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
592
+ );
593
+ var noDirectDate = createRule6({
594
+ name: "no-direct-date",
595
+ meta: {
596
+ type: "problem",
597
+ docs: {
598
+ description: "Disallow direct usage of Date constructor and methods to enforce centralized date utilities"
599
+ },
600
+ messages: {
601
+ noNewDate: "Avoid using 'new Date()'. Use a centralized date utility like dayjs instead.",
602
+ noDateNow: "Avoid using 'Date.now()'. Use a centralized date utility like dayjs instead.",
603
+ noDateParse: "Avoid using 'Date.parse()'. Use a centralized date utility like dayjs instead."
604
+ },
605
+ schema: []
606
+ },
607
+ defaultOptions: [],
608
+ create(context) {
609
+ return {
610
+ NewExpression(node) {
611
+ if (node.callee.type === import_utils6.AST_NODE_TYPES.Identifier && node.callee.name === "Date") {
612
+ context.report({
613
+ node,
614
+ messageId: "noNewDate"
615
+ });
616
+ }
617
+ },
618
+ CallExpression(node) {
619
+ if (node.callee.type === import_utils6.AST_NODE_TYPES.MemberExpression && node.callee.object.type === import_utils6.AST_NODE_TYPES.Identifier && node.callee.object.name === "Date" && node.callee.property.type === import_utils6.AST_NODE_TYPES.Identifier) {
620
+ const methodName = node.callee.property.name;
621
+ if (methodName === "now") {
622
+ context.report({
623
+ node,
624
+ messageId: "noDateNow"
625
+ });
626
+ }
627
+ if (methodName === "parse") {
628
+ context.report({
629
+ node,
630
+ messageId: "noDateParse"
631
+ });
632
+ }
633
+ }
634
+ }
635
+ };
636
+ }
637
+ });
638
+ var no_direct_date_default = noDirectDate;
639
+
640
+ // src/rules/jsx-no-variable-in-callback.ts
641
+ var import_utils7 = require("@typescript-eslint/utils");
642
+ var createRule7 = import_utils7.ESLintUtils.RuleCreator(
643
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
644
+ );
645
+ var jsxNoVariableInCallback = createRule7({
646
+ name: "jsx-no-variable-in-callback",
647
+ meta: {
648
+ type: "suggestion",
649
+ docs: {
650
+ description: "Disallow variable declarations inside callback functions within JSX"
651
+ },
652
+ schema: [],
653
+ messages: {
654
+ noVariableInCallback: "Variable declarations should not be inside callback functions within JSX. Extract the logic to a separate function outside the JSX."
655
+ }
656
+ },
657
+ defaultOptions: [],
658
+ create(context) {
659
+ function isInsideJSX(node) {
660
+ let current = node.parent;
661
+ while (current) {
662
+ if (current.type === import_utils7.AST_NODE_TYPES.JSXElement || current.type === import_utils7.AST_NODE_TYPES.JSXFragment) {
663
+ return true;
664
+ }
665
+ current = current.parent;
666
+ }
667
+ return false;
668
+ }
669
+ function isCallbackInJSX(node) {
670
+ if (!node.parent) {
671
+ return false;
672
+ }
673
+ if (!isInsideJSX(node)) {
674
+ return false;
675
+ }
676
+ if (node.parent.type === import_utils7.AST_NODE_TYPES.CallExpression || node.parent.type === import_utils7.AST_NODE_TYPES.JSXExpressionContainer) {
677
+ return true;
678
+ }
679
+ if (node.parent.type === import_utils7.AST_NODE_TYPES.ArrayExpression && node.parent.parent) {
680
+ if (node.parent.parent.type === import_utils7.AST_NODE_TYPES.CallExpression || node.parent.parent.type === import_utils7.AST_NODE_TYPES.JSXExpressionContainer) {
681
+ return true;
682
+ }
683
+ }
684
+ return false;
685
+ }
686
+ function checkFunctionBody(node) {
687
+ if (!isCallbackInJSX(node)) {
688
+ return;
689
+ }
690
+ const { body } = node;
691
+ if (body.type !== import_utils7.AST_NODE_TYPES.BlockStatement) {
692
+ return;
693
+ }
694
+ body.body.forEach((statement) => {
695
+ if (statement.type === import_utils7.AST_NODE_TYPES.VariableDeclaration) {
696
+ context.report({
697
+ node: statement,
698
+ messageId: "noVariableInCallback"
699
+ });
700
+ }
701
+ });
702
+ }
703
+ return {
704
+ ArrowFunctionExpression: checkFunctionBody,
705
+ FunctionExpression: checkFunctionBody
706
+ };
707
+ }
708
+ });
709
+ var jsx_no_variable_in_callback_default = jsxNoVariableInCallback;
710
+
336
711
  // src/rules/md-filename-case-restriction.ts
337
712
  var import_path3 = __toESM(require("path"), 1);
338
- var import_utils4 = require("@typescript-eslint/utils");
339
- var createRule4 = import_utils4.ESLintUtils.RuleCreator(
340
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
713
+ var import_utils8 = require("@typescript-eslint/utils");
714
+ var createRule8 = import_utils8.ESLintUtils.RuleCreator(
715
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
341
716
  );
342
- var mdFilenameCaseRestriction = createRule4({
717
+ var mdFilenameCaseRestriction = createRule8({
343
718
  name: "md-filename-case-restriction",
344
719
  meta: {
345
720
  type: "problem",
@@ -359,18 +734,18 @@ var mdFilenameCaseRestriction = createRule4({
359
734
  if (!filename.endsWith(".md")) {
360
735
  return;
361
736
  }
362
- const basename = import_path3.default.basename(filename, ".md");
737
+ const basename2 = import_path3.default.basename(filename, ".md");
363
738
  function isSnakeCase(text) {
364
739
  return /^[A-Z][A-Z0-9_]*$/.test(text);
365
740
  }
366
741
  function isValidCase(text) {
367
742
  return isSnakeCase(text);
368
743
  }
369
- if (!isValidCase(basename)) {
744
+ if (!isValidCase(basename2)) {
370
745
  context.report({
371
746
  node: context.sourceCode.ast,
372
747
  messageId: "invalidFilenameCase",
373
- data: { filename: basename }
748
+ data: { filename: basename2 }
374
749
  });
375
750
  }
376
751
  }
@@ -380,11 +755,11 @@ var mdFilenameCaseRestriction = createRule4({
380
755
  var md_filename_case_restriction_default = mdFilenameCaseRestriction;
381
756
 
382
757
  // src/rules/no-complex-inline-return.ts
383
- var import_utils5 = require("@typescript-eslint/utils");
384
- var createRule5 = import_utils5.ESLintUtils.RuleCreator(
385
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
758
+ var import_utils9 = require("@typescript-eslint/utils");
759
+ var createRule9 = import_utils9.ESLintUtils.RuleCreator(
760
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
386
761
  );
387
- var noComplexInlineReturn = createRule5({
762
+ var noComplexInlineReturn = createRule9({
388
763
  name: "no-complex-inline-return",
389
764
  meta: {
390
765
  type: "suggestion",
@@ -400,13 +775,13 @@ var noComplexInlineReturn = createRule5({
400
775
  create(context) {
401
776
  const isComplexExpression = (node) => {
402
777
  if (!node) return false;
403
- if (node.type === import_utils5.AST_NODE_TYPES.ConditionalExpression) {
778
+ if (node.type === import_utils9.AST_NODE_TYPES.ConditionalExpression) {
404
779
  return true;
405
780
  }
406
- if (node.type === import_utils5.AST_NODE_TYPES.LogicalExpression) {
781
+ if (node.type === import_utils9.AST_NODE_TYPES.LogicalExpression) {
407
782
  return true;
408
783
  }
409
- if (node.type === import_utils5.AST_NODE_TYPES.NewExpression) {
784
+ if (node.type === import_utils9.AST_NODE_TYPES.NewExpression) {
410
785
  return true;
411
786
  }
412
787
  return false;
@@ -427,11 +802,11 @@ var no_complex_inline_return_default = noComplexInlineReturn;
427
802
 
428
803
  // src/rules/no-emoji.ts
429
804
  var import_emoji_regex = __toESM(require("emoji-regex"), 1);
430
- var import_utils6 = require("@typescript-eslint/utils");
431
- var createRule6 = import_utils6.ESLintUtils.RuleCreator(
432
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
805
+ var import_utils10 = require("@typescript-eslint/utils");
806
+ var createRule10 = import_utils10.ESLintUtils.RuleCreator(
807
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
433
808
  );
434
- var noEmoji = createRule6({
809
+ var noEmoji = createRule10({
435
810
  name: "no-emoji",
436
811
  meta: {
437
812
  type: "problem",
@@ -465,11 +840,11 @@ var noEmoji = createRule6({
465
840
  var no_emoji_default = noEmoji;
466
841
 
467
842
  // src/rules/no-env-fallback.ts
468
- var import_utils7 = require("@typescript-eslint/utils");
469
- var createRule7 = import_utils7.ESLintUtils.RuleCreator(
470
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
843
+ var import_utils11 = require("@typescript-eslint/utils");
844
+ var createRule11 = import_utils11.ESLintUtils.RuleCreator(
845
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
471
846
  );
472
- var noEnvFallback = createRule7({
847
+ var noEnvFallback = createRule11({
473
848
  name: "no-env-fallback",
474
849
  meta: {
475
850
  type: "problem",
@@ -484,16 +859,16 @@ var noEnvFallback = createRule7({
484
859
  defaultOptions: [],
485
860
  create(context) {
486
861
  const isProcessEnvAccess = (node) => {
487
- if (node.type !== import_utils7.AST_NODE_TYPES.MemberExpression) {
862
+ if (node.type !== import_utils11.AST_NODE_TYPES.MemberExpression) {
488
863
  return false;
489
864
  }
490
865
  const { object } = node;
491
- if (object.type !== import_utils7.AST_NODE_TYPES.MemberExpression) {
866
+ if (object.type !== import_utils11.AST_NODE_TYPES.MemberExpression) {
492
867
  return false;
493
868
  }
494
869
  const processNode = object.object;
495
870
  const envNode = object.property;
496
- return processNode.type === import_utils7.AST_NODE_TYPES.Identifier && processNode.name === "process" && envNode.type === import_utils7.AST_NODE_TYPES.Identifier && envNode.name === "env";
871
+ return processNode.type === import_utils11.AST_NODE_TYPES.Identifier && processNode.name === "process" && envNode.type === import_utils11.AST_NODE_TYPES.Identifier && envNode.name === "env";
497
872
  };
498
873
  return {
499
874
  LogicalExpression(node) {
@@ -517,38 +892,93 @@ var noEnvFallback = createRule7({
517
892
  });
518
893
  var no_env_fallback_default = noEnvFallback;
519
894
 
520
- // src/rules/no-explicit-return-type.ts
521
- var import_utils8 = require("@typescript-eslint/utils");
522
- var createRule8 = import_utils8.ESLintUtils.RuleCreator(
523
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
895
+ // src/rules/require-explicit-return-type.ts
896
+ var import_utils12 = require("@typescript-eslint/utils");
897
+ var createRule12 = import_utils12.ESLintUtils.RuleCreator(
898
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
524
899
  );
525
- var noExplicitReturnType = createRule8({
526
- name: "no-explicit-return-type",
900
+ var isReactComponent = (node) => {
901
+ if (node.type === import_utils12.AST_NODE_TYPES.ArrowFunctionExpression) {
902
+ const { parent } = node;
903
+ if (parent?.type === import_utils12.AST_NODE_TYPES.VariableDeclarator) {
904
+ const { id } = parent;
905
+ if (id.type === import_utils12.AST_NODE_TYPES.Identifier) {
906
+ return /^[A-Z]/.test(id.name);
907
+ }
908
+ }
909
+ }
910
+ if (node.type === import_utils12.AST_NODE_TYPES.FunctionDeclaration && node.id) {
911
+ return /^[A-Z]/.test(node.id.name);
912
+ }
913
+ return false;
914
+ };
915
+ var isCallbackFunction = (node) => {
916
+ if (node.type === import_utils12.AST_NODE_TYPES.FunctionDeclaration) {
917
+ return false;
918
+ }
919
+ const { parent } = node;
920
+ if (!parent) {
921
+ return false;
922
+ }
923
+ if (parent.type === import_utils12.AST_NODE_TYPES.CallExpression && parent.arguments.includes(node)) {
924
+ return true;
925
+ }
926
+ if (parent.type === import_utils12.AST_NODE_TYPES.Property) {
927
+ return true;
928
+ }
929
+ if (parent.type === import_utils12.AST_NODE_TYPES.ArrayExpression) {
930
+ return true;
931
+ }
932
+ return false;
933
+ };
934
+ var getFunctionName = (node) => {
935
+ if (node.type === import_utils12.AST_NODE_TYPES.FunctionDeclaration && node.id) {
936
+ return node.id.name;
937
+ }
938
+ if (node.type === import_utils12.AST_NODE_TYPES.FunctionExpression && node.id) {
939
+ return node.id.name;
940
+ }
941
+ if ((node.type === import_utils12.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils12.AST_NODE_TYPES.FunctionExpression) && node.parent?.type === import_utils12.AST_NODE_TYPES.VariableDeclarator && node.parent.id.type === import_utils12.AST_NODE_TYPES.Identifier) {
942
+ return node.parent.id.name;
943
+ }
944
+ return null;
945
+ };
946
+ var requireExplicitReturnType = createRule12({
947
+ name: "require-explicit-return-type",
527
948
  meta: {
528
949
  type: "suggestion",
529
950
  docs: {
530
- description: "Disallow explicit return types on functions"
951
+ description: "Require explicit return types on functions for better code documentation and type safety"
531
952
  },
532
- fixable: "code",
533
- schema: [],
534
953
  messages: {
535
- noExplicitReturnType: "Remove explicit return type '{{returnType}}' - TypeScript can infer it automatically"
536
- }
954
+ missingReturnType: "Function '{{name}}' is missing an explicit return type. Add a return type annotation for better documentation.",
955
+ missingReturnTypeAnonymous: "Function is missing an explicit return type. Add a return type annotation for better documentation."
956
+ },
957
+ schema: []
537
958
  },
538
959
  defaultOptions: [],
539
960
  create(context) {
540
961
  const checkFunction = (node) => {
541
962
  if (node.returnType) {
542
- const returnTypeText = context.sourceCode.getText(node.returnType);
963
+ return;
964
+ }
965
+ if (isCallbackFunction(node)) {
966
+ return;
967
+ }
968
+ if (isReactComponent(node)) {
969
+ return;
970
+ }
971
+ const functionName = getFunctionName(node);
972
+ if (functionName) {
543
973
  context.report({
544
- node: node.returnType,
545
- messageId: "noExplicitReturnType",
546
- data: {
547
- returnType: returnTypeText
548
- },
549
- fix(fixer) {
550
- return fixer.remove(node.returnType);
551
- }
974
+ node,
975
+ messageId: "missingReturnType",
976
+ data: { name: functionName }
977
+ });
978
+ } else {
979
+ context.report({
980
+ node,
981
+ messageId: "missingReturnTypeAnonymous"
552
982
  });
553
983
  }
554
984
  };
@@ -559,14 +989,99 @@ var noExplicitReturnType = createRule8({
559
989
  };
560
990
  }
561
991
  });
562
- var no_explicit_return_type_default = noExplicitReturnType;
992
+ var require_explicit_return_type_default = requireExplicitReturnType;
993
+
994
+ // src/rules/jsx-no-non-component-function.ts
995
+ var import_utils14 = require("@typescript-eslint/utils");
996
+
997
+ // src/utils.ts
998
+ var import_node_path = require("path");
999
+ var import_utils13 = require("@typescript-eslint/utils");
1000
+ var getFileExtension = (filename) => (0, import_node_path.extname)(filename).slice(1);
1001
+
1002
+ // src/rules/jsx-no-non-component-function.ts
1003
+ var createRule13 = import_utils14.ESLintUtils.RuleCreator(
1004
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
1005
+ );
1006
+ var jsxNoNonComponentFunction = createRule13({
1007
+ name: "jsx-no-non-component-function",
1008
+ meta: {
1009
+ type: "problem",
1010
+ docs: {
1011
+ description: "Disallow non-component functions defined at top level in .tsx and .jsx files"
1012
+ },
1013
+ schema: [],
1014
+ messages: {
1015
+ noTopLevelFunction: "Non-component functions should not be defined at top level in .tsx/.jsx files. Either move it inside the component or extract it to a separate file."
1016
+ }
1017
+ },
1018
+ defaultOptions: [],
1019
+ create(context) {
1020
+ const { filename } = context;
1021
+ const extension = getFileExtension(filename);
1022
+ if (extension !== "tsx" && extension !== "jsx") {
1023
+ return {};
1024
+ }
1025
+ function isReactComponent2(node) {
1026
+ const functionName = node.type === import_utils14.AST_NODE_TYPES.FunctionDeclaration && node.id ? node.id.name : null;
1027
+ if (functionName && /^[A-Z]/.test(functionName)) {
1028
+ return true;
1029
+ }
1030
+ if (node.returnType?.typeAnnotation) {
1031
+ const returnTypeNode = node.returnType.typeAnnotation;
1032
+ if (returnTypeNode.type === import_utils14.AST_NODE_TYPES.TSTypeReference && returnTypeNode.typeName.type === import_utils14.AST_NODE_TYPES.Identifier) {
1033
+ const typeName = returnTypeNode.typeName.name;
1034
+ if (typeName === "JSX" || typeName === "ReactElement" || typeName === "ReactNode") {
1035
+ return true;
1036
+ }
1037
+ }
1038
+ }
1039
+ return false;
1040
+ }
1041
+ function checkTopLevelFunction(node, declaratorNode) {
1042
+ if (isReactComponent2(node)) {
1043
+ return;
1044
+ }
1045
+ const { parent } = node;
1046
+ if (!parent) {
1047
+ return;
1048
+ }
1049
+ if (parent.type === import_utils14.AST_NODE_TYPES.ExportDefaultDeclaration || parent.type === import_utils14.AST_NODE_TYPES.ExportNamedDeclaration) {
1050
+ return;
1051
+ }
1052
+ if (declaratorNode?.parent?.parent?.type === import_utils14.AST_NODE_TYPES.ExportNamedDeclaration) {
1053
+ return;
1054
+ }
1055
+ if (declaratorNode?.id.type === import_utils14.AST_NODE_TYPES.Identifier) {
1056
+ const varName = declaratorNode.id.name;
1057
+ if (/^[A-Z]/.test(varName)) {
1058
+ return;
1059
+ }
1060
+ }
1061
+ context.report({
1062
+ node: declaratorNode || node,
1063
+ messageId: "noTopLevelFunction"
1064
+ });
1065
+ }
1066
+ return {
1067
+ "Program > VariableDeclaration > VariableDeclarator > ArrowFunctionExpression": function checkArrowFunction(node) {
1068
+ const declarator = node.parent;
1069
+ checkTopLevelFunction(node, declarator);
1070
+ },
1071
+ "Program > FunctionDeclaration": function checkFunctionDeclaration(node) {
1072
+ checkTopLevelFunction(node);
1073
+ }
1074
+ };
1075
+ }
1076
+ });
1077
+ var jsx_no_non_component_function_default = jsxNoNonComponentFunction;
563
1078
 
564
1079
  // src/rules/no-logic-in-params.ts
565
- var import_utils9 = require("@typescript-eslint/utils");
566
- var createRule9 = import_utils9.ESLintUtils.RuleCreator(
567
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
1080
+ var import_utils16 = require("@typescript-eslint/utils");
1081
+ var createRule14 = import_utils16.ESLintUtils.RuleCreator(
1082
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
568
1083
  );
569
- var noLogicInParams = createRule9({
1084
+ var noLogicInParams = createRule14({
570
1085
  name: "no-logic-in-params",
571
1086
  meta: {
572
1087
  type: "suggestion",
@@ -581,20 +1096,20 @@ var noLogicInParams = createRule9({
581
1096
  defaultOptions: [],
582
1097
  create(context) {
583
1098
  const isComplexExpression = (node) => {
584
- if (node.type === import_utils9.AST_NODE_TYPES.SpreadElement) {
1099
+ if (node.type === import_utils16.AST_NODE_TYPES.SpreadElement) {
585
1100
  return false;
586
1101
  }
587
- if (node.type === import_utils9.AST_NODE_TYPES.ConditionalExpression) {
1102
+ if (node.type === import_utils16.AST_NODE_TYPES.ConditionalExpression) {
588
1103
  return true;
589
1104
  }
590
- if (node.type === import_utils9.AST_NODE_TYPES.LogicalExpression) {
1105
+ if (node.type === import_utils16.AST_NODE_TYPES.LogicalExpression) {
591
1106
  return true;
592
1107
  }
593
- if (node.type === import_utils9.AST_NODE_TYPES.BinaryExpression) {
1108
+ if (node.type === import_utils16.AST_NODE_TYPES.BinaryExpression) {
594
1109
  const logicalOperators = ["==", "===", "!=", "!==", "<", ">", "<=", ">=", "in", "instanceof"];
595
1110
  return logicalOperators.includes(node.operator);
596
1111
  }
597
- if (node.type === import_utils9.AST_NODE_TYPES.UnaryExpression) {
1112
+ if (node.type === import_utils16.AST_NODE_TYPES.UnaryExpression) {
598
1113
  return node.operator === "!";
599
1114
  }
600
1115
  return false;
@@ -625,12 +1140,127 @@ var noLogicInParams = createRule9({
625
1140
  });
626
1141
  var no_logic_in_params_default = noLogicInParams;
627
1142
 
1143
+ // src/rules/no-single-char-variables.ts
1144
+ var import_utils17 = require("@typescript-eslint/utils");
1145
+ var createRule15 = import_utils17.ESLintUtils.RuleCreator(
1146
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
1147
+ );
1148
+ var ALLOWED_IN_FOR_LOOPS = /* @__PURE__ */ new Set(["i", "j", "k", "n"]);
1149
+ var ALLOWED_UNDERSCORE = "_";
1150
+ var isForLoopInit = (node) => {
1151
+ let current = node;
1152
+ while (current) {
1153
+ const parentNode = current.parent;
1154
+ if (!parentNode) {
1155
+ return false;
1156
+ }
1157
+ if (parentNode.type === import_utils17.AST_NODE_TYPES.ForStatement) {
1158
+ const { init } = parentNode;
1159
+ if (init && init === current) {
1160
+ return true;
1161
+ }
1162
+ }
1163
+ current = parentNode;
1164
+ }
1165
+ return false;
1166
+ };
1167
+ var isAllowedInContext = (name, node) => {
1168
+ if (name === ALLOWED_UNDERSCORE) {
1169
+ return true;
1170
+ }
1171
+ if (ALLOWED_IN_FOR_LOOPS.has(name) && isForLoopInit(node)) {
1172
+ return true;
1173
+ }
1174
+ return false;
1175
+ };
1176
+ var noSingleCharVariables = createRule15({
1177
+ name: "no-single-char-variables",
1178
+ meta: {
1179
+ type: "suggestion",
1180
+ docs: {
1181
+ description: "Disallow single character variable and parameter names for better code readability"
1182
+ },
1183
+ messages: {
1184
+ noSingleChar: "Avoid single character variable name '{{name}}'. Use a descriptive name that clearly indicates the purpose."
1185
+ },
1186
+ schema: []
1187
+ },
1188
+ defaultOptions: [],
1189
+ create(context) {
1190
+ const checkIdentifier = (node, declarationNode) => {
1191
+ const { name } = node;
1192
+ if (name.length !== 1) {
1193
+ return;
1194
+ }
1195
+ if (isAllowedInContext(name, declarationNode)) {
1196
+ return;
1197
+ }
1198
+ context.report({
1199
+ node,
1200
+ messageId: "noSingleChar",
1201
+ data: { name }
1202
+ });
1203
+ };
1204
+ const checkPattern = (pattern, declarationNode) => {
1205
+ if (pattern.type === import_utils17.AST_NODE_TYPES.Identifier) {
1206
+ checkIdentifier(pattern, declarationNode);
1207
+ } else if (pattern.type === import_utils17.AST_NODE_TYPES.ObjectPattern) {
1208
+ pattern.properties.forEach((prop) => {
1209
+ if (prop.type === import_utils17.AST_NODE_TYPES.Property && prop.value.type === import_utils17.AST_NODE_TYPES.Identifier) {
1210
+ checkIdentifier(prop.value, declarationNode);
1211
+ } else if (prop.type === import_utils17.AST_NODE_TYPES.RestElement && prop.argument.type === import_utils17.AST_NODE_TYPES.Identifier) {
1212
+ checkIdentifier(prop.argument, declarationNode);
1213
+ }
1214
+ });
1215
+ } else if (pattern.type === import_utils17.AST_NODE_TYPES.ArrayPattern) {
1216
+ pattern.elements.forEach((element) => {
1217
+ if (element?.type === import_utils17.AST_NODE_TYPES.Identifier) {
1218
+ checkIdentifier(element, declarationNode);
1219
+ } else if (element?.type === import_utils17.AST_NODE_TYPES.RestElement && element.argument.type === import_utils17.AST_NODE_TYPES.Identifier) {
1220
+ checkIdentifier(element.argument, declarationNode);
1221
+ }
1222
+ });
1223
+ } else if (pattern.type === import_utils17.AST_NODE_TYPES.AssignmentPattern && pattern.left.type === import_utils17.AST_NODE_TYPES.Identifier) {
1224
+ checkIdentifier(pattern.left, declarationNode);
1225
+ } else if (pattern.type === import_utils17.AST_NODE_TYPES.RestElement && pattern.argument.type === import_utils17.AST_NODE_TYPES.Identifier) {
1226
+ checkIdentifier(pattern.argument, declarationNode);
1227
+ }
1228
+ };
1229
+ return {
1230
+ VariableDeclarator(node) {
1231
+ checkPattern(node.id, node);
1232
+ },
1233
+ FunctionDeclaration(node) {
1234
+ if (node.id) {
1235
+ checkIdentifier(node.id, node);
1236
+ }
1237
+ node.params.forEach((param) => checkPattern(param, node));
1238
+ },
1239
+ FunctionExpression(node) {
1240
+ if (node.id) {
1241
+ checkIdentifier(node.id, node);
1242
+ }
1243
+ node.params.forEach((param) => checkPattern(param, node));
1244
+ },
1245
+ ArrowFunctionExpression(node) {
1246
+ node.params.forEach((param) => checkPattern(param, node));
1247
+ },
1248
+ CatchClause(node) {
1249
+ if (node.param) {
1250
+ checkPattern(node.param, node);
1251
+ }
1252
+ }
1253
+ };
1254
+ }
1255
+ });
1256
+ var no_single_char_variables_default = noSingleCharVariables;
1257
+
628
1258
  // src/rules/prefer-destructuring-params.ts
629
- var import_utils10 = require("@typescript-eslint/utils");
630
- var createRule10 = import_utils10.ESLintUtils.RuleCreator(
631
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
1259
+ var import_utils18 = require("@typescript-eslint/utils");
1260
+ var createRule16 = import_utils18.ESLintUtils.RuleCreator(
1261
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
632
1262
  );
633
- var preferDestructuringParams = createRule10({
1263
+ var preferDestructuringParams = createRule16({
634
1264
  name: "prefer-destructuring-params",
635
1265
  meta: {
636
1266
  type: "suggestion",
@@ -644,20 +1274,20 @@ var preferDestructuringParams = createRule10({
644
1274
  },
645
1275
  defaultOptions: [],
646
1276
  create(context) {
647
- const isCallbackFunction = (node) => {
1277
+ const isCallbackFunction2 = (node) => {
648
1278
  const { parent } = node;
649
- return parent?.type === import_utils10.AST_NODE_TYPES.CallExpression;
1279
+ return parent?.type === import_utils18.AST_NODE_TYPES.CallExpression;
650
1280
  };
651
1281
  const isDeveloperFunction = (node) => {
652
- if (node.type === import_utils10.AST_NODE_TYPES.FunctionDeclaration) {
1282
+ if (node.type === import_utils18.AST_NODE_TYPES.FunctionDeclaration) {
653
1283
  return true;
654
1284
  }
655
- if (node.type === import_utils10.AST_NODE_TYPES.FunctionExpression || node.type === import_utils10.AST_NODE_TYPES.ArrowFunctionExpression) {
656
- if (isCallbackFunction(node)) {
1285
+ if (node.type === import_utils18.AST_NODE_TYPES.FunctionExpression || node.type === import_utils18.AST_NODE_TYPES.ArrowFunctionExpression) {
1286
+ if (isCallbackFunction2(node)) {
657
1287
  return false;
658
1288
  }
659
1289
  const { parent } = node;
660
- return parent?.type === import_utils10.AST_NODE_TYPES.VariableDeclarator || parent?.type === import_utils10.AST_NODE_TYPES.AssignmentExpression || parent?.type === import_utils10.AST_NODE_TYPES.Property || parent?.type === import_utils10.AST_NODE_TYPES.MethodDefinition;
1290
+ return parent?.type === import_utils18.AST_NODE_TYPES.VariableDeclarator || parent?.type === import_utils18.AST_NODE_TYPES.AssignmentExpression || parent?.type === import_utils18.AST_NODE_TYPES.Property || parent?.type === import_utils18.AST_NODE_TYPES.MethodDefinition;
661
1291
  }
662
1292
  return false;
663
1293
  };
@@ -669,7 +1299,7 @@ var preferDestructuringParams = createRule10({
669
1299
  if (!isDeveloperFunction(node)) {
670
1300
  return;
671
1301
  }
672
- if (node.type === import_utils10.AST_NODE_TYPES.FunctionDeclaration && node.id) {
1302
+ if (node.type === import_utils18.AST_NODE_TYPES.FunctionDeclaration && node.id) {
673
1303
  const functionName = node.id.name;
674
1304
  if (functionName.startsWith("_") || functionName.includes("$") || /^[A-Z][a-zA-Z]*$/.test(functionName)) {
675
1305
  return;
@@ -679,7 +1309,7 @@ var preferDestructuringParams = createRule10({
679
1309
  return;
680
1310
  }
681
1311
  const hasNonDestructuredParams = node.params.some(
682
- (param) => param.type !== import_utils10.AST_NODE_TYPES.ObjectPattern && param.type !== import_utils10.AST_NODE_TYPES.RestElement
1312
+ (param) => param.type !== import_utils18.AST_NODE_TYPES.ObjectPattern && param.type !== import_utils18.AST_NODE_TYPES.RestElement
683
1313
  );
684
1314
  if (hasNonDestructuredParams) {
685
1315
  context.report({
@@ -697,12 +1327,103 @@ var preferDestructuringParams = createRule10({
697
1327
  });
698
1328
  var prefer_destructuring_params_default = preferDestructuringParams;
699
1329
 
1330
+ // src/rules/prefer-function-declaration.ts
1331
+ var import_utils19 = require("@typescript-eslint/utils");
1332
+ var createRule17 = import_utils19.ESLintUtils.RuleCreator(
1333
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
1334
+ );
1335
+ var isTsFile = (filename) => filename.endsWith(".ts") && !filename.endsWith(".d.ts");
1336
+ var isCallbackContext = (node) => {
1337
+ const { parent } = node;
1338
+ if (!parent) {
1339
+ return false;
1340
+ }
1341
+ if (parent.type === import_utils19.AST_NODE_TYPES.CallExpression && parent.arguments.includes(node)) {
1342
+ return true;
1343
+ }
1344
+ if (parent.type === import_utils19.AST_NODE_TYPES.NewExpression && parent.arguments.includes(node)) {
1345
+ return true;
1346
+ }
1347
+ if (parent.type === import_utils19.AST_NODE_TYPES.ReturnStatement) {
1348
+ return true;
1349
+ }
1350
+ if (parent.type === import_utils19.AST_NODE_TYPES.Property) {
1351
+ return true;
1352
+ }
1353
+ if (parent.type === import_utils19.AST_NODE_TYPES.ArrayExpression) {
1354
+ return true;
1355
+ }
1356
+ if (parent.type === import_utils19.AST_NODE_TYPES.ConditionalExpression) {
1357
+ return true;
1358
+ }
1359
+ if (parent.type === import_utils19.AST_NODE_TYPES.LogicalExpression) {
1360
+ return true;
1361
+ }
1362
+ if (parent.type === import_utils19.AST_NODE_TYPES.AssignmentExpression && parent.left !== node) {
1363
+ return true;
1364
+ }
1365
+ return false;
1366
+ };
1367
+ var preferFunctionDeclaration = createRule17({
1368
+ name: "prefer-function-declaration",
1369
+ meta: {
1370
+ type: "suggestion",
1371
+ docs: {
1372
+ description: "Enforce function declarations over arrow functions assigned to variables in .ts files for better readability and hoisting"
1373
+ },
1374
+ messages: {
1375
+ preferDeclaration: "Use function declaration instead of arrow function. Replace 'const {{name}} = () => ...' with 'function {{name}}() ...'",
1376
+ preferDeclarationExpr: "Use function declaration instead of function expression. Replace 'const {{name}} = function() ...' with 'function {{name}}() ...'"
1377
+ },
1378
+ schema: []
1379
+ },
1380
+ defaultOptions: [],
1381
+ create(context) {
1382
+ const { filename } = context;
1383
+ if (!isTsFile(filename)) {
1384
+ return {};
1385
+ }
1386
+ return {
1387
+ VariableDeclarator(node) {
1388
+ if (node.id.type !== import_utils19.AST_NODE_TYPES.Identifier) {
1389
+ return;
1390
+ }
1391
+ const { init } = node;
1392
+ if (!init) {
1393
+ return;
1394
+ }
1395
+ if (init.type === import_utils19.AST_NODE_TYPES.ArrowFunctionExpression) {
1396
+ if (isCallbackContext(init)) {
1397
+ return;
1398
+ }
1399
+ context.report({
1400
+ node: init,
1401
+ messageId: "preferDeclaration",
1402
+ data: { name: node.id.name }
1403
+ });
1404
+ }
1405
+ if (init.type === import_utils19.AST_NODE_TYPES.FunctionExpression) {
1406
+ if (isCallbackContext(init)) {
1407
+ return;
1408
+ }
1409
+ context.report({
1410
+ node: init,
1411
+ messageId: "preferDeclarationExpr",
1412
+ data: { name: node.id.name }
1413
+ });
1414
+ }
1415
+ }
1416
+ };
1417
+ }
1418
+ });
1419
+ var prefer_function_declaration_default = preferFunctionDeclaration;
1420
+
700
1421
  // src/rules/prefer-import-type.ts
701
- var import_utils11 = require("@typescript-eslint/utils");
702
- var createRule11 = import_utils11.ESLintUtils.RuleCreator(
703
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
1422
+ var import_utils20 = require("@typescript-eslint/utils");
1423
+ var createRule18 = import_utils20.ESLintUtils.RuleCreator(
1424
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
704
1425
  );
705
- var preferImportType = createRule11({
1426
+ var preferImportType = createRule18({
706
1427
  name: "prefer-import-type",
707
1428
  meta: {
708
1429
  type: "suggestion",
@@ -717,6 +1438,81 @@ var preferImportType = createRule11({
717
1438
  },
718
1439
  defaultOptions: [],
719
1440
  create(context) {
1441
+ function isInTypeContext(node) {
1442
+ let current = node;
1443
+ while (current) {
1444
+ switch (current.type) {
1445
+ case import_utils20.AST_NODE_TYPES.TSTypeReference:
1446
+ case import_utils20.AST_NODE_TYPES.TSTypeAnnotation:
1447
+ case import_utils20.AST_NODE_TYPES.TSTypeParameterInstantiation:
1448
+ case import_utils20.AST_NODE_TYPES.TSInterfaceHeritage:
1449
+ case import_utils20.AST_NODE_TYPES.TSClassImplements:
1450
+ case import_utils20.AST_NODE_TYPES.TSTypeQuery:
1451
+ case import_utils20.AST_NODE_TYPES.TSTypeAssertion:
1452
+ case import_utils20.AST_NODE_TYPES.TSAsExpression:
1453
+ case import_utils20.AST_NODE_TYPES.TSSatisfiesExpression:
1454
+ case import_utils20.AST_NODE_TYPES.TSTypeAliasDeclaration:
1455
+ case import_utils20.AST_NODE_TYPES.TSInterfaceDeclaration:
1456
+ case import_utils20.AST_NODE_TYPES.TSTypeParameter:
1457
+ case import_utils20.AST_NODE_TYPES.TSQualifiedName:
1458
+ return true;
1459
+ case import_utils20.AST_NODE_TYPES.MemberExpression:
1460
+ case import_utils20.AST_NODE_TYPES.Identifier:
1461
+ current = current.parent;
1462
+ break;
1463
+ default:
1464
+ return false;
1465
+ }
1466
+ }
1467
+ return false;
1468
+ }
1469
+ function isUsedAsValue(localName, scope) {
1470
+ const variable = scope.set.get(localName);
1471
+ if (!variable) {
1472
+ return false;
1473
+ }
1474
+ if (variable.references.length === 0) {
1475
+ return false;
1476
+ }
1477
+ return variable.references.some((ref) => {
1478
+ if (ref.isWrite()) {
1479
+ return false;
1480
+ }
1481
+ const { identifier } = ref;
1482
+ const { parent } = identifier;
1483
+ if (!parent) {
1484
+ return false;
1485
+ }
1486
+ if (isInTypeContext(parent)) {
1487
+ return false;
1488
+ }
1489
+ switch (parent.type) {
1490
+ case import_utils20.AST_NODE_TYPES.CallExpression:
1491
+ case import_utils20.AST_NODE_TYPES.NewExpression:
1492
+ case import_utils20.AST_NODE_TYPES.JSXOpeningElement:
1493
+ case import_utils20.AST_NODE_TYPES.JSXClosingElement:
1494
+ case import_utils20.AST_NODE_TYPES.MemberExpression:
1495
+ case import_utils20.AST_NODE_TYPES.VariableDeclarator:
1496
+ case import_utils20.AST_NODE_TYPES.TaggedTemplateExpression:
1497
+ case import_utils20.AST_NODE_TYPES.SpreadElement:
1498
+ case import_utils20.AST_NODE_TYPES.ExportSpecifier:
1499
+ case import_utils20.AST_NODE_TYPES.ArrayExpression:
1500
+ case import_utils20.AST_NODE_TYPES.ObjectExpression:
1501
+ case import_utils20.AST_NODE_TYPES.BinaryExpression:
1502
+ case import_utils20.AST_NODE_TYPES.LogicalExpression:
1503
+ case import_utils20.AST_NODE_TYPES.UnaryExpression:
1504
+ case import_utils20.AST_NODE_TYPES.ReturnStatement:
1505
+ case import_utils20.AST_NODE_TYPES.ArrowFunctionExpression:
1506
+ case import_utils20.AST_NODE_TYPES.ConditionalExpression:
1507
+ case import_utils20.AST_NODE_TYPES.AwaitExpression:
1508
+ case import_utils20.AST_NODE_TYPES.YieldExpression:
1509
+ case import_utils20.AST_NODE_TYPES.Property:
1510
+ return true;
1511
+ default:
1512
+ return false;
1513
+ }
1514
+ });
1515
+ }
720
1516
  function checkImportDeclaration(node) {
721
1517
  if (node.importKind === "type") {
722
1518
  return;
@@ -732,19 +1528,17 @@ var preferImportType = createRule11({
732
1528
  if (isRuntimeImport) {
733
1529
  return;
734
1530
  }
1531
+ const scope = context.sourceCode.getScope(node);
735
1532
  const isTypeOnlyImport = node.specifiers.every((specifier) => {
736
- if (specifier.type === import_utils11.AST_NODE_TYPES.ImportDefaultSpecifier) {
1533
+ if (specifier.type === import_utils20.AST_NODE_TYPES.ImportDefaultSpecifier) {
737
1534
  return false;
738
1535
  }
739
- if (specifier.type === import_utils11.AST_NODE_TYPES.ImportNamespaceSpecifier) {
1536
+ if (specifier.type === import_utils20.AST_NODE_TYPES.ImportNamespaceSpecifier) {
740
1537
  return false;
741
1538
  }
742
- if (specifier.type === import_utils11.AST_NODE_TYPES.ImportSpecifier) {
743
- const importedName = specifier.imported.type === import_utils11.AST_NODE_TYPES.Identifier ? specifier.imported.name : specifier.imported.value;
744
- const isKnownTypeOnly = node.source.value === "@typescript-eslint/utils" && ["TSESTree", "RuleContext"].includes(importedName) || node.source.value === "react" && ["Component", "ComponentProps", "ReactNode", "FC", "JSX", "ReactElement", "PropsWithChildren"].includes(
745
- importedName
746
- ) || importedName.endsWith("Type") || importedName.endsWith("Interface") || importedName.endsWith("Props");
747
- return isKnownTypeOnly;
1539
+ if (specifier.type === import_utils20.AST_NODE_TYPES.ImportSpecifier) {
1540
+ const localName = specifier.local.name;
1541
+ return !isUsedAsValue(localName, scope);
748
1542
  }
749
1543
  return false;
750
1544
  });
@@ -768,11 +1562,11 @@ var preferImportType = createRule11({
768
1562
  var prefer_import_type_default = preferImportType;
769
1563
 
770
1564
  // src/rules/prefer-interface-over-inline-types.ts
771
- var import_utils12 = require("@typescript-eslint/utils");
772
- var createRule12 = import_utils12.ESLintUtils.RuleCreator(
773
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
1565
+ var import_utils21 = require("@typescript-eslint/utils");
1566
+ var createRule19 = import_utils21.ESLintUtils.RuleCreator(
1567
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
774
1568
  );
775
- var preferInterfaceOverInlineTypes = createRule12({
1569
+ var preferInterfaceOverInlineTypes = createRule19({
776
1570
  name: "prefer-interface-over-inline-types",
777
1571
  meta: {
778
1572
  type: "suggestion",
@@ -788,67 +1582,67 @@ var preferInterfaceOverInlineTypes = createRule12({
788
1582
  defaultOptions: [],
789
1583
  create(context) {
790
1584
  function hasJSXInConditional(node) {
791
- return node.consequent.type === import_utils12.AST_NODE_TYPES.JSXElement || node.consequent.type === import_utils12.AST_NODE_TYPES.JSXFragment || node.alternate.type === import_utils12.AST_NODE_TYPES.JSXElement || node.alternate.type === import_utils12.AST_NODE_TYPES.JSXFragment;
1585
+ return node.consequent.type === import_utils21.AST_NODE_TYPES.JSXElement || node.consequent.type === import_utils21.AST_NODE_TYPES.JSXFragment || node.alternate.type === import_utils21.AST_NODE_TYPES.JSXElement || node.alternate.type === import_utils21.AST_NODE_TYPES.JSXFragment;
792
1586
  }
793
1587
  function hasJSXInLogical(node) {
794
- return node.right.type === import_utils12.AST_NODE_TYPES.JSXElement || node.right.type === import_utils12.AST_NODE_TYPES.JSXFragment;
1588
+ return node.right.type === import_utils21.AST_NODE_TYPES.JSXElement || node.right.type === import_utils21.AST_NODE_TYPES.JSXFragment;
795
1589
  }
796
1590
  function hasJSXReturn(block) {
797
1591
  return block.body.some((stmt) => {
798
- if (stmt.type === import_utils12.AST_NODE_TYPES.ReturnStatement && stmt.argument) {
799
- return stmt.argument.type === import_utils12.AST_NODE_TYPES.JSXElement || stmt.argument.type === import_utils12.AST_NODE_TYPES.JSXFragment || stmt.argument.type === import_utils12.AST_NODE_TYPES.ConditionalExpression && hasJSXInConditional(stmt.argument) || stmt.argument.type === import_utils12.AST_NODE_TYPES.LogicalExpression && hasJSXInLogical(stmt.argument);
1592
+ if (stmt.type === import_utils21.AST_NODE_TYPES.ReturnStatement && stmt.argument) {
1593
+ return stmt.argument.type === import_utils21.AST_NODE_TYPES.JSXElement || stmt.argument.type === import_utils21.AST_NODE_TYPES.JSXFragment || stmt.argument.type === import_utils21.AST_NODE_TYPES.ConditionalExpression && hasJSXInConditional(stmt.argument) || stmt.argument.type === import_utils21.AST_NODE_TYPES.LogicalExpression && hasJSXInLogical(stmt.argument);
800
1594
  }
801
1595
  return false;
802
1596
  });
803
1597
  }
804
- function isReactComponent(node) {
805
- if (node.type === import_utils12.AST_NODE_TYPES.ArrowFunctionExpression) {
806
- if (node.body.type === import_utils12.AST_NODE_TYPES.JSXElement || node.body.type === import_utils12.AST_NODE_TYPES.JSXFragment) {
1598
+ function isReactComponent2(node) {
1599
+ if (node.type === import_utils21.AST_NODE_TYPES.ArrowFunctionExpression) {
1600
+ if (node.body.type === import_utils21.AST_NODE_TYPES.JSXElement || node.body.type === import_utils21.AST_NODE_TYPES.JSXFragment) {
807
1601
  return true;
808
1602
  }
809
- if (node.body.type === import_utils12.AST_NODE_TYPES.BlockStatement) {
1603
+ if (node.body.type === import_utils21.AST_NODE_TYPES.BlockStatement) {
810
1604
  return hasJSXReturn(node.body);
811
1605
  }
812
- } else if (node.type === import_utils12.AST_NODE_TYPES.FunctionExpression || node.type === import_utils12.AST_NODE_TYPES.FunctionDeclaration) {
813
- if (node.body && node.body.type === import_utils12.AST_NODE_TYPES.BlockStatement) {
1606
+ } else if (node.type === import_utils21.AST_NODE_TYPES.FunctionExpression || node.type === import_utils21.AST_NODE_TYPES.FunctionDeclaration) {
1607
+ if (node.body && node.body.type === import_utils21.AST_NODE_TYPES.BlockStatement) {
814
1608
  return hasJSXReturn(node.body);
815
1609
  }
816
1610
  }
817
1611
  return false;
818
1612
  }
819
1613
  function isInlineTypeAnnotation(node) {
820
- if (node.type === import_utils12.AST_NODE_TYPES.TSTypeLiteral) {
1614
+ if (node.type === import_utils21.AST_NODE_TYPES.TSTypeLiteral) {
821
1615
  return true;
822
1616
  }
823
- if (node.type === import_utils12.AST_NODE_TYPES.TSTypeReference && node.typeArguments) {
824
- return node.typeArguments.params.some((param) => param.type === import_utils12.AST_NODE_TYPES.TSTypeLiteral);
1617
+ if (node.type === import_utils21.AST_NODE_TYPES.TSTypeReference && node.typeArguments) {
1618
+ return node.typeArguments.params.some((param) => param.type === import_utils21.AST_NODE_TYPES.TSTypeLiteral);
825
1619
  }
826
- if (node.type === import_utils12.AST_NODE_TYPES.TSUnionType) {
1620
+ if (node.type === import_utils21.AST_NODE_TYPES.TSUnionType) {
827
1621
  return node.types.some((type) => isInlineTypeAnnotation(type));
828
1622
  }
829
1623
  return false;
830
1624
  }
831
1625
  function hasInlineObjectType(node) {
832
- if (node.type === import_utils12.AST_NODE_TYPES.TSTypeLiteral) {
1626
+ if (node.type === import_utils21.AST_NODE_TYPES.TSTypeLiteral) {
833
1627
  return true;
834
1628
  }
835
- if (node.type === import_utils12.AST_NODE_TYPES.TSTypeReference && node.typeArguments) {
836
- return node.typeArguments.params.some((param) => param.type === import_utils12.AST_NODE_TYPES.TSTypeLiteral);
1629
+ if (node.type === import_utils21.AST_NODE_TYPES.TSTypeReference && node.typeArguments) {
1630
+ return node.typeArguments.params.some((param) => param.type === import_utils21.AST_NODE_TYPES.TSTypeLiteral);
837
1631
  }
838
- if (node.type === import_utils12.AST_NODE_TYPES.TSUnionType) {
1632
+ if (node.type === import_utils21.AST_NODE_TYPES.TSUnionType) {
839
1633
  return node.types.some((type) => hasInlineObjectType(type));
840
1634
  }
841
1635
  return false;
842
1636
  }
843
1637
  function checkFunction(node) {
844
- if (!isReactComponent(node)) {
1638
+ if (!isReactComponent2(node)) {
845
1639
  return;
846
1640
  }
847
1641
  if (node.params.length !== 1) {
848
1642
  return;
849
1643
  }
850
1644
  const param = node.params[0];
851
- if (param.type === import_utils12.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
1645
+ if (param.type === import_utils21.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
852
1646
  const { typeAnnotation } = param.typeAnnotation;
853
1647
  if (isInlineTypeAnnotation(typeAnnotation) && hasInlineObjectType(typeAnnotation)) {
854
1648
  context.report({
@@ -867,12 +1661,100 @@ var preferInterfaceOverInlineTypes = createRule12({
867
1661
  });
868
1662
  var prefer_interface_over_inline_types_default = preferInterfaceOverInlineTypes;
869
1663
 
1664
+ // src/rules/prefer-jsx-template-literals.ts
1665
+ var import_utils22 = require("@typescript-eslint/utils");
1666
+ var createRule20 = import_utils22.ESLintUtils.RuleCreator(
1667
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
1668
+ );
1669
+ var preferJSXTemplateLiterals = createRule20({
1670
+ name: "prefer-jsx-template-literals",
1671
+ meta: {
1672
+ type: "suggestion",
1673
+ docs: {
1674
+ description: "Enforce using template literals instead of mixing text and JSX expressions"
1675
+ },
1676
+ fixable: "code",
1677
+ schema: [],
1678
+ messages: {
1679
+ preferTemplate: "Use template literal instead of mixing text with JSX expressions"
1680
+ }
1681
+ },
1682
+ defaultOptions: [],
1683
+ create(context) {
1684
+ function handleTextBeforeExpression(textNode, exprNode) {
1685
+ const textValue = textNode.value;
1686
+ const trimmedText = textValue.trim();
1687
+ if (!trimmedText) {
1688
+ return;
1689
+ }
1690
+ const hasTextContent = trimmedText.length > 0 && textValue !== trimmedText;
1691
+ const hasNoTrailingSpace = trimmedText.length > 0 && /\S$/.test(textValue);
1692
+ if (!hasTextContent && !hasNoTrailingSpace) {
1693
+ return;
1694
+ }
1695
+ context.report({
1696
+ node: textNode,
1697
+ messageId: "preferTemplate",
1698
+ fix(fixer) {
1699
+ const textPart = textValue.trimEnd();
1700
+ const exprText = context.sourceCode.getText(exprNode.expression);
1701
+ const templateLiteral = `{\`${textPart}\${${exprText}}\`}`;
1702
+ return [fixer.replaceText(textNode, templateLiteral), fixer.remove(exprNode)];
1703
+ }
1704
+ });
1705
+ }
1706
+ function handleExpressionBeforeText(exprNode, textNode) {
1707
+ const textValue = textNode.value;
1708
+ const trimmedText = textValue.trim();
1709
+ if (!trimmedText) {
1710
+ return;
1711
+ }
1712
+ const startsWithNonWhitespace = /^\S/.test(trimmedText);
1713
+ if (!startsWithNonWhitespace) {
1714
+ return;
1715
+ }
1716
+ context.report({
1717
+ node: textNode,
1718
+ messageId: "preferTemplate",
1719
+ fix(fixer) {
1720
+ const exprText = context.sourceCode.getText(exprNode.expression);
1721
+ const textPart = textValue.trim();
1722
+ const templateLiteral = `{\`\${${exprText}}${textPart}\`}`;
1723
+ return [fixer.replaceText(exprNode, templateLiteral), fixer.remove(textNode)];
1724
+ }
1725
+ });
1726
+ }
1727
+ function checkJSXElement(node) {
1728
+ const { children } = node;
1729
+ if (children.length < 2) {
1730
+ return;
1731
+ }
1732
+ for (let i = 0; i < children.length - 1; i += 1) {
1733
+ const child = children[i];
1734
+ const nextChild = children[i + 1];
1735
+ if (!child || !nextChild) {
1736
+ return;
1737
+ }
1738
+ if (child.type === import_utils22.AST_NODE_TYPES.JSXText && nextChild.type === import_utils22.AST_NODE_TYPES.JSXExpressionContainer) {
1739
+ handleTextBeforeExpression(child, nextChild);
1740
+ } else if (child.type === import_utils22.AST_NODE_TYPES.JSXExpressionContainer && nextChild.type === import_utils22.AST_NODE_TYPES.JSXText) {
1741
+ handleExpressionBeforeText(child, nextChild);
1742
+ }
1743
+ }
1744
+ }
1745
+ return {
1746
+ JSXElement: checkJSXElement
1747
+ };
1748
+ }
1749
+ });
1750
+ var prefer_jsx_template_literals_default = preferJSXTemplateLiterals;
1751
+
870
1752
  // src/rules/prefer-named-param-types.ts
871
- var import_utils13 = require("@typescript-eslint/utils");
872
- var createRule13 = import_utils13.ESLintUtils.RuleCreator(
873
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
1753
+ var import_utils23 = require("@typescript-eslint/utils");
1754
+ var createRule21 = import_utils23.ESLintUtils.RuleCreator(
1755
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
874
1756
  );
875
- var preferNamedParamTypes = createRule13({
1757
+ var preferNamedParamTypes = createRule21({
876
1758
  name: "prefer-named-param-types",
877
1759
  meta: {
878
1760
  type: "suggestion",
@@ -887,16 +1769,16 @@ var preferNamedParamTypes = createRule13({
887
1769
  defaultOptions: [],
888
1770
  create(context) {
889
1771
  function hasInlineObjectType(param) {
890
- if (param.type === import_utils13.AST_NODE_TYPES.AssignmentPattern) {
1772
+ if (param.type === import_utils23.AST_NODE_TYPES.AssignmentPattern) {
891
1773
  return hasInlineObjectType(param.left);
892
1774
  }
893
- if (param.type === import_utils13.AST_NODE_TYPES.ObjectPattern) {
894
- if (param.typeAnnotation?.typeAnnotation.type === import_utils13.AST_NODE_TYPES.TSTypeLiteral) {
1775
+ if (param.type === import_utils23.AST_NODE_TYPES.ObjectPattern) {
1776
+ if (param.typeAnnotation?.typeAnnotation.type === import_utils23.AST_NODE_TYPES.TSTypeLiteral) {
895
1777
  return true;
896
1778
  }
897
1779
  }
898
- if (param.type === import_utils13.AST_NODE_TYPES.Identifier) {
899
- if (param.typeAnnotation?.typeAnnotation.type === import_utils13.AST_NODE_TYPES.TSTypeLiteral) {
1780
+ if (param.type === import_utils23.AST_NODE_TYPES.Identifier) {
1781
+ if (param.typeAnnotation?.typeAnnotation.type === import_utils23.AST_NODE_TYPES.TSTypeLiteral) {
900
1782
  return true;
901
1783
  }
902
1784
  }
@@ -930,11 +1812,11 @@ var preferNamedParamTypes = createRule13({
930
1812
  var prefer_named_param_types_default = preferNamedParamTypes;
931
1813
 
932
1814
  // src/rules/prefer-react-import-types.ts
933
- var import_utils14 = require("@typescript-eslint/utils");
934
- var createRule14 = import_utils14.ESLintUtils.RuleCreator(
935
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
1815
+ var import_utils24 = require("@typescript-eslint/utils");
1816
+ var createRule22 = import_utils24.ESLintUtils.RuleCreator(
1817
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
936
1818
  );
937
- var preferReactImportTypes = createRule14({
1819
+ var preferReactImportTypes = createRule22({
938
1820
  name: "prefer-react-import-types",
939
1821
  meta: {
940
1822
  type: "suggestion",
@@ -1010,7 +1892,7 @@ var preferReactImportTypes = createRule14({
1010
1892
  ]);
1011
1893
  const allReactExports = /* @__PURE__ */ new Set([...reactTypes, ...reactRuntimeExports]);
1012
1894
  function checkMemberExpression(node) {
1013
- if (node.object.type === import_utils14.AST_NODE_TYPES.Identifier && node.object.name === "React" && node.property.type === import_utils14.AST_NODE_TYPES.Identifier && allReactExports.has(node.property.name)) {
1895
+ if (node.object.type === import_utils24.AST_NODE_TYPES.Identifier && node.object.name === "React" && node.property.type === import_utils24.AST_NODE_TYPES.Identifier && allReactExports.has(node.property.name)) {
1014
1896
  const typeName = node.property.name;
1015
1897
  const isType = reactTypes.has(typeName);
1016
1898
  const importStatement = isType ? `import type { ${typeName} } from "react"` : `import { ${typeName} } from "react"`;
@@ -1027,7 +1909,7 @@ var preferReactImportTypes = createRule14({
1027
1909
  return {
1028
1910
  MemberExpression: checkMemberExpression,
1029
1911
  "TSTypeReference > TSQualifiedName": (node) => {
1030
- if (node.left.type === import_utils14.AST_NODE_TYPES.Identifier && node.left.name === "React" && node.right.type === import_utils14.AST_NODE_TYPES.Identifier && allReactExports.has(node.right.name)) {
1912
+ if (node.left.type === import_utils24.AST_NODE_TYPES.Identifier && node.left.name === "React" && node.right.type === import_utils24.AST_NODE_TYPES.Identifier && allReactExports.has(node.right.name)) {
1031
1913
  const typeName = node.right.name;
1032
1914
  const isType = reactTypes.has(typeName);
1033
1915
  const importStatement = isType ? `import type { ${typeName} } from "react"` : `import { ${typeName} } from "react"`;
@@ -1047,11 +1929,11 @@ var preferReactImportTypes = createRule14({
1047
1929
  var prefer_react_import_types_default = preferReactImportTypes;
1048
1930
 
1049
1931
  // src/rules/react-props-destructure.ts
1050
- var import_utils15 = require("@typescript-eslint/utils");
1051
- var createRule15 = import_utils15.ESLintUtils.RuleCreator(
1052
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
1932
+ var import_utils25 = require("@typescript-eslint/utils");
1933
+ var createRule23 = import_utils25.ESLintUtils.RuleCreator(
1934
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
1053
1935
  );
1054
- var reactPropsDestructure = createRule15({
1936
+ var reactPropsDestructure = createRule23({
1055
1937
  name: "react-props-destructure",
1056
1938
  meta: {
1057
1939
  type: "suggestion",
@@ -1067,45 +1949,45 @@ var reactPropsDestructure = createRule15({
1067
1949
  defaultOptions: [],
1068
1950
  create(context) {
1069
1951
  function hasJSXInConditional(node) {
1070
- return node.consequent.type === import_utils15.AST_NODE_TYPES.JSXElement || node.consequent.type === import_utils15.AST_NODE_TYPES.JSXFragment || node.alternate.type === import_utils15.AST_NODE_TYPES.JSXElement || node.alternate.type === import_utils15.AST_NODE_TYPES.JSXFragment;
1952
+ return node.consequent.type === import_utils25.AST_NODE_TYPES.JSXElement || node.consequent.type === import_utils25.AST_NODE_TYPES.JSXFragment || node.alternate.type === import_utils25.AST_NODE_TYPES.JSXElement || node.alternate.type === import_utils25.AST_NODE_TYPES.JSXFragment;
1071
1953
  }
1072
1954
  function hasJSXInLogical(node) {
1073
- return node.right.type === import_utils15.AST_NODE_TYPES.JSXElement || node.right.type === import_utils15.AST_NODE_TYPES.JSXFragment;
1955
+ return node.right.type === import_utils25.AST_NODE_TYPES.JSXElement || node.right.type === import_utils25.AST_NODE_TYPES.JSXFragment;
1074
1956
  }
1075
1957
  function hasJSXReturn(block) {
1076
1958
  return block.body.some((stmt) => {
1077
- if (stmt.type === import_utils15.AST_NODE_TYPES.ReturnStatement && stmt.argument) {
1078
- return stmt.argument.type === import_utils15.AST_NODE_TYPES.JSXElement || stmt.argument.type === import_utils15.AST_NODE_TYPES.JSXFragment || stmt.argument.type === import_utils15.AST_NODE_TYPES.ConditionalExpression && hasJSXInConditional(stmt.argument) || stmt.argument.type === import_utils15.AST_NODE_TYPES.LogicalExpression && hasJSXInLogical(stmt.argument);
1959
+ if (stmt.type === import_utils25.AST_NODE_TYPES.ReturnStatement && stmt.argument) {
1960
+ return stmt.argument.type === import_utils25.AST_NODE_TYPES.JSXElement || stmt.argument.type === import_utils25.AST_NODE_TYPES.JSXFragment || stmt.argument.type === import_utils25.AST_NODE_TYPES.ConditionalExpression && hasJSXInConditional(stmt.argument) || stmt.argument.type === import_utils25.AST_NODE_TYPES.LogicalExpression && hasJSXInLogical(stmt.argument);
1079
1961
  }
1080
1962
  return false;
1081
1963
  });
1082
1964
  }
1083
- function isReactComponent(node) {
1084
- if (node.type === import_utils15.AST_NODE_TYPES.ArrowFunctionExpression) {
1085
- if (node.body.type === import_utils15.AST_NODE_TYPES.JSXElement || node.body.type === import_utils15.AST_NODE_TYPES.JSXFragment) {
1965
+ function isReactComponent2(node) {
1966
+ if (node.type === import_utils25.AST_NODE_TYPES.ArrowFunctionExpression) {
1967
+ if (node.body.type === import_utils25.AST_NODE_TYPES.JSXElement || node.body.type === import_utils25.AST_NODE_TYPES.JSXFragment) {
1086
1968
  return true;
1087
1969
  }
1088
- if (node.body.type === import_utils15.AST_NODE_TYPES.BlockStatement) {
1970
+ if (node.body.type === import_utils25.AST_NODE_TYPES.BlockStatement) {
1089
1971
  return hasJSXReturn(node.body);
1090
1972
  }
1091
- } else if (node.type === import_utils15.AST_NODE_TYPES.FunctionExpression || node.type === import_utils15.AST_NODE_TYPES.FunctionDeclaration) {
1092
- if (node.body && node.body.type === import_utils15.AST_NODE_TYPES.BlockStatement) {
1973
+ } else if (node.type === import_utils25.AST_NODE_TYPES.FunctionExpression || node.type === import_utils25.AST_NODE_TYPES.FunctionDeclaration) {
1974
+ if (node.body && node.body.type === import_utils25.AST_NODE_TYPES.BlockStatement) {
1093
1975
  return hasJSXReturn(node.body);
1094
1976
  }
1095
1977
  }
1096
1978
  return false;
1097
1979
  }
1098
1980
  function checkFunction(node) {
1099
- if (!isReactComponent(node)) {
1981
+ if (!isReactComponent2(node)) {
1100
1982
  return;
1101
1983
  }
1102
1984
  if (node.params.length !== 1) {
1103
1985
  return;
1104
1986
  }
1105
1987
  const param = node.params[0];
1106
- if (param.type === import_utils15.AST_NODE_TYPES.ObjectPattern) {
1107
- const properties = param.properties.filter((prop) => prop.type === import_utils15.AST_NODE_TYPES.Property).map((prop) => {
1108
- if (prop.key.type === import_utils15.AST_NODE_TYPES.Identifier) {
1988
+ if (param.type === import_utils25.AST_NODE_TYPES.ObjectPattern) {
1989
+ const properties = param.properties.filter((prop) => prop.type === import_utils25.AST_NODE_TYPES.Property).map((prop) => {
1990
+ if (prop.key.type === import_utils25.AST_NODE_TYPES.Identifier) {
1109
1991
  return prop.key.name;
1110
1992
  }
1111
1993
  return null;
@@ -1137,18 +2019,26 @@ var meta = {
1137
2019
  version: package_default.version
1138
2020
  };
1139
2021
  var rules = {
2022
+ "boolean-naming-prefix": boolean_naming_prefix_default,
1140
2023
  "enforce-readonly-component-props": enforce_readonly_component_props_default,
2024
+ "enforce-sorted-destructuring": enforce_sorted_destructuring_default,
1141
2025
  "file-kebab-case": file_kebab_case_default,
1142
2026
  "jsx-pascal-case": jsx_pascal_case_default,
2027
+ "jsx-no-non-component-function": jsx_no_non_component_function_default,
2028
+ "jsx-no-variable-in-callback": jsx_no_variable_in_callback_default,
1143
2029
  "md-filename-case-restriction": md_filename_case_restriction_default,
1144
2030
  "no-complex-inline-return": no_complex_inline_return_default,
2031
+ "no-direct-date": no_direct_date_default,
1145
2032
  "no-emoji": no_emoji_default,
1146
2033
  "no-env-fallback": no_env_fallback_default,
1147
- "no-explicit-return-type": no_explicit_return_type_default,
2034
+ "require-explicit-return-type": require_explicit_return_type_default,
1148
2035
  "no-logic-in-params": no_logic_in_params_default,
2036
+ "no-single-char-variables": no_single_char_variables_default,
1149
2037
  "prefer-destructuring-params": prefer_destructuring_params_default,
2038
+ "prefer-function-declaration": prefer_function_declaration_default,
1150
2039
  "prefer-import-type": prefer_import_type_default,
1151
2040
  "prefer-interface-over-inline-types": prefer_interface_over_inline_types_default,
2041
+ "prefer-jsx-template-literals": prefer_jsx_template_literals_default,
1152
2042
  "prefer-named-param-types": prefer_named_param_types_default,
1153
2043
  "prefer-react-import-types": prefer_react_import_types_default,
1154
2044
  "react-props-destructure": react_props_destructure_default
@@ -1158,40 +2048,56 @@ var plugin = {
1158
2048
  rules
1159
2049
  };
1160
2050
  var baseRules = {
2051
+ "nextfriday/boolean-naming-prefix": "warn",
1161
2052
  "nextfriday/no-emoji": "warn",
2053
+ "nextfriday/enforce-sorted-destructuring": "warn",
1162
2054
  "nextfriday/file-kebab-case": "warn",
1163
2055
  "nextfriday/md-filename-case-restriction": "warn",
1164
2056
  "nextfriday/prefer-destructuring-params": "warn",
1165
- "nextfriday/no-explicit-return-type": "warn",
2057
+ "nextfriday/prefer-function-declaration": "warn",
2058
+ "nextfriday/require-explicit-return-type": "warn",
1166
2059
  "nextfriday/prefer-import-type": "warn",
1167
2060
  "nextfriday/prefer-named-param-types": "warn",
1168
2061
  "nextfriday/prefer-react-import-types": "warn",
1169
2062
  "nextfriday/no-complex-inline-return": "warn",
2063
+ "nextfriday/no-direct-date": "warn",
1170
2064
  "nextfriday/no-logic-in-params": "warn",
1171
- "nextfriday/no-env-fallback": "warn"
2065
+ "nextfriday/no-env-fallback": "warn",
2066
+ "nextfriday/no-single-char-variables": "warn"
1172
2067
  };
1173
2068
  var baseRecommendedRules = {
2069
+ "nextfriday/boolean-naming-prefix": "error",
1174
2070
  "nextfriday/no-emoji": "error",
2071
+ "nextfriday/enforce-sorted-destructuring": "error",
1175
2072
  "nextfriday/file-kebab-case": "error",
1176
2073
  "nextfriday/md-filename-case-restriction": "error",
1177
2074
  "nextfriday/prefer-destructuring-params": "error",
1178
- "nextfriday/no-explicit-return-type": "error",
2075
+ "nextfriday/prefer-function-declaration": "error",
2076
+ "nextfriday/require-explicit-return-type": "error",
1179
2077
  "nextfriday/prefer-import-type": "error",
1180
2078
  "nextfriday/prefer-named-param-types": "error",
1181
2079
  "nextfriday/prefer-react-import-types": "error",
1182
2080
  "nextfriday/no-complex-inline-return": "error",
2081
+ "nextfriday/no-direct-date": "error",
1183
2082
  "nextfriday/no-logic-in-params": "error",
1184
- "nextfriday/no-env-fallback": "error"
2083
+ "nextfriday/no-env-fallback": "error",
2084
+ "nextfriday/no-single-char-variables": "error"
1185
2085
  };
1186
2086
  var jsxRules = {
1187
2087
  "nextfriday/jsx-pascal-case": "warn",
2088
+ "nextfriday/jsx-no-non-component-function": "warn",
2089
+ "nextfriday/jsx-no-variable-in-callback": "warn",
1188
2090
  "nextfriday/prefer-interface-over-inline-types": "warn",
2091
+ "nextfriday/prefer-jsx-template-literals": "warn",
1189
2092
  "nextfriday/react-props-destructure": "warn",
1190
2093
  "nextfriday/enforce-readonly-component-props": "warn"
1191
2094
  };
1192
2095
  var jsxRecommendedRules = {
1193
2096
  "nextfriday/jsx-pascal-case": "error",
2097
+ "nextfriday/jsx-no-non-component-function": "error",
2098
+ "nextfriday/jsx-no-variable-in-callback": "error",
1194
2099
  "nextfriday/prefer-interface-over-inline-types": "error",
2100
+ "nextfriday/prefer-jsx-template-literals": "error",
1195
2101
  "nextfriday/react-props-destructure": "error",
1196
2102
  "nextfriday/enforce-readonly-component-props": "error"
1197
2103
  };