eslint-plugin-nextfriday 1.5.3 → 1.6.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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "eslint-plugin-nextfriday",
4
- version: "1.5.3",
4
+ version: "1.6.0",
5
5
  description: "A comprehensive ESLint plugin providing custom rules and configurations for Next Friday development workflows.",
6
6
  keywords: [
7
7
  "eslint",
@@ -118,7 +118,7 @@ var package_default = {
118
118
  // src/rules/enforce-readonly-component-props.ts
119
119
  import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
120
120
  var createRule = ESLintUtils.RuleCreator(
121
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
121
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
122
122
  );
123
123
  var enforceReadonlyComponentProps = createRule({
124
124
  name: "enforce-readonly-component-props",
@@ -207,11 +207,132 @@ var enforceReadonlyComponentProps = createRule({
207
207
  });
208
208
  var enforce_readonly_component_props_default = enforceReadonlyComponentProps;
209
209
 
210
+ // src/rules/enforce-sorted-destructuring.ts
211
+ import { AST_NODE_TYPES as AST_NODE_TYPES2, ESLintUtils as ESLintUtils2 } from "@typescript-eslint/utils";
212
+ var createRule2 = ESLintUtils2.RuleCreator(
213
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
214
+ );
215
+ var enforceSortedDestructuring = createRule2({
216
+ name: "enforce-sorted-destructuring",
217
+ meta: {
218
+ type: "suggestion",
219
+ docs: {
220
+ description: "Enforce alphabetical sorting of destructured properties with defaults first"
221
+ },
222
+ schema: [],
223
+ messages: {
224
+ unsortedDestructuring: "Destructured properties should be sorted alphabetically. Properties with defaults should come first, sorted by type (string, number, boolean, object) then alphabetically."
225
+ }
226
+ },
227
+ defaultOptions: [],
228
+ create(context) {
229
+ function getPropertyName(property) {
230
+ if (property.type === AST_NODE_TYPES2.RestElement) {
231
+ return null;
232
+ }
233
+ if (property.key.type === AST_NODE_TYPES2.Identifier) {
234
+ return property.key.name;
235
+ }
236
+ return null;
237
+ }
238
+ function hasDefaultValue(property) {
239
+ return property.value.type === AST_NODE_TYPES2.AssignmentPattern && Boolean(property.value.right);
240
+ }
241
+ function getDefaultValueType(property) {
242
+ if (!hasDefaultValue(property)) {
243
+ return "none";
244
+ }
245
+ const assignmentPattern = property.value;
246
+ const { right } = assignmentPattern;
247
+ if (!right) {
248
+ return "none";
249
+ }
250
+ switch (right.type) {
251
+ case AST_NODE_TYPES2.Literal:
252
+ if (typeof right.value === "string") {
253
+ return "string";
254
+ }
255
+ if (typeof right.value === "number") {
256
+ return "number";
257
+ }
258
+ if (typeof right.value === "boolean") {
259
+ return "boolean";
260
+ }
261
+ return "other";
262
+ case AST_NODE_TYPES2.TemplateLiteral:
263
+ return "string";
264
+ case AST_NODE_TYPES2.ObjectExpression:
265
+ case AST_NODE_TYPES2.ArrayExpression:
266
+ return "object";
267
+ default:
268
+ return "other";
269
+ }
270
+ }
271
+ function getTypeSortOrder(type) {
272
+ const order = {
273
+ string: 0,
274
+ number: 1,
275
+ boolean: 2,
276
+ object: 3,
277
+ other: 4,
278
+ none: 5
279
+ };
280
+ return order[type] ?? 5;
281
+ }
282
+ function checkVariableDeclarator(node) {
283
+ if (node.id.type !== AST_NODE_TYPES2.ObjectPattern) {
284
+ return;
285
+ }
286
+ const { properties } = node.id;
287
+ if (properties.length < 2) {
288
+ return;
289
+ }
290
+ const propertyInfo = properties.map((prop) => {
291
+ if (prop.type === AST_NODE_TYPES2.RestElement) {
292
+ return null;
293
+ }
294
+ return {
295
+ property: prop,
296
+ name: getPropertyName(prop),
297
+ hasDefault: hasDefaultValue(prop),
298
+ defaultType: getDefaultValueType(prop)
299
+ };
300
+ }).filter((info) => info !== null && info.name !== null);
301
+ const sorted = [...propertyInfo].sort((a, b) => {
302
+ if (a.hasDefault && !b.hasDefault) {
303
+ return -1;
304
+ }
305
+ if (!a.hasDefault && b.hasDefault) {
306
+ return 1;
307
+ }
308
+ if (a.hasDefault && b.hasDefault) {
309
+ const typeComparison = getTypeSortOrder(a.defaultType) - getTypeSortOrder(b.defaultType);
310
+ if (typeComparison !== 0) {
311
+ return typeComparison;
312
+ }
313
+ }
314
+ return a.name.localeCompare(b.name);
315
+ });
316
+ const isSorted = propertyInfo.every((info, index) => info.name === sorted[index].name);
317
+ if (!isSorted) {
318
+ context.report({
319
+ node: node.id,
320
+ messageId: "unsortedDestructuring"
321
+ });
322
+ }
323
+ }
324
+ return {
325
+ VariableDeclarator: checkVariableDeclarator
326
+ };
327
+ }
328
+ });
329
+ var enforce_sorted_destructuring_default = enforceSortedDestructuring;
330
+
210
331
  // src/rules/file-kebab-case.ts
211
332
  import path from "path";
212
- import { ESLintUtils as ESLintUtils2 } from "@typescript-eslint/utils";
213
- var createRule2 = ESLintUtils2.RuleCreator(
214
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
333
+ import { ESLintUtils as ESLintUtils3 } from "@typescript-eslint/utils";
334
+ var createRule3 = ESLintUtils3.RuleCreator(
335
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
215
336
  );
216
337
  var isKebabCase = (str) => {
217
338
  if (/\.(config|rc|setup|spec|test)$/.test(str) || /^[a-z0-9]+(?:-[a-z0-9]+)*\.[a-z0-9]+(?:-[a-z0-9]+)*$/.test(str)) {
@@ -219,7 +340,7 @@ var isKebabCase = (str) => {
219
340
  }
220
341
  return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(str);
221
342
  };
222
- var fileKebabCase = createRule2({
343
+ var fileKebabCase = createRule3({
223
344
  name: "file-kebab-case",
224
345
  meta: {
225
346
  type: "problem",
@@ -240,8 +361,8 @@ var fileKebabCase = createRule2({
240
361
  if (ext !== ".ts" && ext !== ".js") {
241
362
  return;
242
363
  }
243
- const basename = path.basename(filename, ext);
244
- if (!isKebabCase(basename)) {
364
+ const basename2 = path.basename(filename, ext);
365
+ if (!isKebabCase(basename2)) {
245
366
  context.report({
246
367
  loc: { line: 1, column: 0 },
247
368
  messageId: "fileKebabCase"
@@ -255,12 +376,12 @@ var file_kebab_case_default = fileKebabCase;
255
376
 
256
377
  // src/rules/jsx-pascal-case.ts
257
378
  import path2 from "path";
258
- import { ESLintUtils as ESLintUtils3 } from "@typescript-eslint/utils";
259
- var createRule3 = ESLintUtils3.RuleCreator(
260
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
379
+ import { ESLintUtils as ESLintUtils4 } from "@typescript-eslint/utils";
380
+ var createRule4 = ESLintUtils4.RuleCreator(
381
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
261
382
  );
262
383
  var isPascalCase = (str) => /^[A-Z][a-zA-Z0-9]*$/.test(str) && !/^[A-Z]+$/.test(str);
263
- var jsxPascalCase = createRule3({
384
+ var jsxPascalCase = createRule4({
264
385
  name: "jsx-pascal-case",
265
386
  meta: {
266
387
  type: "problem",
@@ -281,8 +402,8 @@ var jsxPascalCase = createRule3({
281
402
  if (ext !== ".jsx" && ext !== ".tsx") {
282
403
  return;
283
404
  }
284
- const basename = path2.basename(filename, ext);
285
- if (!isPascalCase(basename)) {
405
+ const basename2 = path2.basename(filename, ext);
406
+ if (!isPascalCase(basename2)) {
286
407
  context.report({
287
408
  loc: { line: 1, column: 0 },
288
409
  messageId: "jsxPascalCase"
@@ -294,13 +415,84 @@ var jsxPascalCase = createRule3({
294
415
  });
295
416
  var jsx_pascal_case_default = jsxPascalCase;
296
417
 
418
+ // src/rules/jsx-no-variable-in-callback.ts
419
+ import { AST_NODE_TYPES as AST_NODE_TYPES3, ESLintUtils as ESLintUtils5 } from "@typescript-eslint/utils";
420
+ var createRule5 = ESLintUtils5.RuleCreator(
421
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
422
+ );
423
+ var jsxNoVariableInCallback = createRule5({
424
+ name: "jsx-no-variable-in-callback",
425
+ meta: {
426
+ type: "suggestion",
427
+ docs: {
428
+ description: "Disallow variable declarations inside callback functions within JSX"
429
+ },
430
+ schema: [],
431
+ messages: {
432
+ noVariableInCallback: "Variable declarations should not be inside callback functions within JSX. Extract the logic to a separate function outside the JSX."
433
+ }
434
+ },
435
+ defaultOptions: [],
436
+ create(context) {
437
+ function isInsideJSX(node) {
438
+ let current = node.parent;
439
+ while (current) {
440
+ if (current.type === AST_NODE_TYPES3.JSXElement || current.type === AST_NODE_TYPES3.JSXFragment) {
441
+ return true;
442
+ }
443
+ current = current.parent;
444
+ }
445
+ return false;
446
+ }
447
+ function isCallbackInJSX(node) {
448
+ if (!node.parent) {
449
+ return false;
450
+ }
451
+ if (!isInsideJSX(node)) {
452
+ return false;
453
+ }
454
+ if (node.parent.type === AST_NODE_TYPES3.CallExpression || node.parent.type === AST_NODE_TYPES3.JSXExpressionContainer) {
455
+ return true;
456
+ }
457
+ if (node.parent.type === AST_NODE_TYPES3.ArrayExpression && node.parent.parent) {
458
+ if (node.parent.parent.type === AST_NODE_TYPES3.CallExpression || node.parent.parent.type === AST_NODE_TYPES3.JSXExpressionContainer) {
459
+ return true;
460
+ }
461
+ }
462
+ return false;
463
+ }
464
+ function checkFunctionBody(node) {
465
+ if (!isCallbackInJSX(node)) {
466
+ return;
467
+ }
468
+ const { body } = node;
469
+ if (body.type !== AST_NODE_TYPES3.BlockStatement) {
470
+ return;
471
+ }
472
+ body.body.forEach((statement) => {
473
+ if (statement.type === AST_NODE_TYPES3.VariableDeclaration) {
474
+ context.report({
475
+ node: statement,
476
+ messageId: "noVariableInCallback"
477
+ });
478
+ }
479
+ });
480
+ }
481
+ return {
482
+ ArrowFunctionExpression: checkFunctionBody,
483
+ FunctionExpression: checkFunctionBody
484
+ };
485
+ }
486
+ });
487
+ var jsx_no_variable_in_callback_default = jsxNoVariableInCallback;
488
+
297
489
  // src/rules/md-filename-case-restriction.ts
298
490
  import path3 from "path";
299
- import { ESLintUtils as ESLintUtils4 } from "@typescript-eslint/utils";
300
- var createRule4 = ESLintUtils4.RuleCreator(
301
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
491
+ import { ESLintUtils as ESLintUtils6 } from "@typescript-eslint/utils";
492
+ var createRule6 = ESLintUtils6.RuleCreator(
493
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
302
494
  );
303
- var mdFilenameCaseRestriction = createRule4({
495
+ var mdFilenameCaseRestriction = createRule6({
304
496
  name: "md-filename-case-restriction",
305
497
  meta: {
306
498
  type: "problem",
@@ -320,18 +512,18 @@ var mdFilenameCaseRestriction = createRule4({
320
512
  if (!filename.endsWith(".md")) {
321
513
  return;
322
514
  }
323
- const basename = path3.basename(filename, ".md");
515
+ const basename2 = path3.basename(filename, ".md");
324
516
  function isSnakeCase(text) {
325
517
  return /^[A-Z][A-Z0-9_]*$/.test(text);
326
518
  }
327
519
  function isValidCase(text) {
328
520
  return isSnakeCase(text);
329
521
  }
330
- if (!isValidCase(basename)) {
522
+ if (!isValidCase(basename2)) {
331
523
  context.report({
332
524
  node: context.sourceCode.ast,
333
525
  messageId: "invalidFilenameCase",
334
- data: { filename: basename }
526
+ data: { filename: basename2 }
335
527
  });
336
528
  }
337
529
  }
@@ -341,11 +533,11 @@ var mdFilenameCaseRestriction = createRule4({
341
533
  var md_filename_case_restriction_default = mdFilenameCaseRestriction;
342
534
 
343
535
  // src/rules/no-complex-inline-return.ts
344
- import { ESLintUtils as ESLintUtils5, AST_NODE_TYPES as AST_NODE_TYPES2 } from "@typescript-eslint/utils";
345
- var createRule5 = ESLintUtils5.RuleCreator(
346
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
536
+ import { ESLintUtils as ESLintUtils7, AST_NODE_TYPES as AST_NODE_TYPES4 } from "@typescript-eslint/utils";
537
+ var createRule7 = ESLintUtils7.RuleCreator(
538
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
347
539
  );
348
- var noComplexInlineReturn = createRule5({
540
+ var noComplexInlineReturn = createRule7({
349
541
  name: "no-complex-inline-return",
350
542
  meta: {
351
543
  type: "suggestion",
@@ -361,13 +553,13 @@ var noComplexInlineReturn = createRule5({
361
553
  create(context) {
362
554
  const isComplexExpression = (node) => {
363
555
  if (!node) return false;
364
- if (node.type === AST_NODE_TYPES2.ConditionalExpression) {
556
+ if (node.type === AST_NODE_TYPES4.ConditionalExpression) {
365
557
  return true;
366
558
  }
367
- if (node.type === AST_NODE_TYPES2.LogicalExpression) {
559
+ if (node.type === AST_NODE_TYPES4.LogicalExpression) {
368
560
  return true;
369
561
  }
370
- if (node.type === AST_NODE_TYPES2.NewExpression) {
562
+ if (node.type === AST_NODE_TYPES4.NewExpression) {
371
563
  return true;
372
564
  }
373
565
  return false;
@@ -388,11 +580,11 @@ var no_complex_inline_return_default = noComplexInlineReturn;
388
580
 
389
581
  // src/rules/no-emoji.ts
390
582
  import emojiRegex from "emoji-regex";
391
- import { ESLintUtils as ESLintUtils6 } from "@typescript-eslint/utils";
392
- var createRule6 = ESLintUtils6.RuleCreator(
393
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
583
+ import { ESLintUtils as ESLintUtils8 } from "@typescript-eslint/utils";
584
+ var createRule8 = ESLintUtils8.RuleCreator(
585
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
394
586
  );
395
- var noEmoji = createRule6({
587
+ var noEmoji = createRule8({
396
588
  name: "no-emoji",
397
589
  meta: {
398
590
  type: "problem",
@@ -426,11 +618,11 @@ var noEmoji = createRule6({
426
618
  var no_emoji_default = noEmoji;
427
619
 
428
620
  // src/rules/no-env-fallback.ts
429
- import { ESLintUtils as ESLintUtils7, AST_NODE_TYPES as AST_NODE_TYPES3 } from "@typescript-eslint/utils";
430
- var createRule7 = ESLintUtils7.RuleCreator(
431
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
621
+ import { ESLintUtils as ESLintUtils9, AST_NODE_TYPES as AST_NODE_TYPES5 } from "@typescript-eslint/utils";
622
+ var createRule9 = ESLintUtils9.RuleCreator(
623
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
432
624
  );
433
- var noEnvFallback = createRule7({
625
+ var noEnvFallback = createRule9({
434
626
  name: "no-env-fallback",
435
627
  meta: {
436
628
  type: "problem",
@@ -445,16 +637,16 @@ var noEnvFallback = createRule7({
445
637
  defaultOptions: [],
446
638
  create(context) {
447
639
  const isProcessEnvAccess = (node) => {
448
- if (node.type !== AST_NODE_TYPES3.MemberExpression) {
640
+ if (node.type !== AST_NODE_TYPES5.MemberExpression) {
449
641
  return false;
450
642
  }
451
643
  const { object } = node;
452
- if (object.type !== AST_NODE_TYPES3.MemberExpression) {
644
+ if (object.type !== AST_NODE_TYPES5.MemberExpression) {
453
645
  return false;
454
646
  }
455
647
  const processNode = object.object;
456
648
  const envNode = object.property;
457
- return processNode.type === AST_NODE_TYPES3.Identifier && processNode.name === "process" && envNode.type === AST_NODE_TYPES3.Identifier && envNode.name === "env";
649
+ return processNode.type === AST_NODE_TYPES5.Identifier && processNode.name === "process" && envNode.type === AST_NODE_TYPES5.Identifier && envNode.name === "env";
458
650
  };
459
651
  return {
460
652
  LogicalExpression(node) {
@@ -479,11 +671,11 @@ var noEnvFallback = createRule7({
479
671
  var no_env_fallback_default = noEnvFallback;
480
672
 
481
673
  // src/rules/no-explicit-return-type.ts
482
- import { ESLintUtils as ESLintUtils8 } from "@typescript-eslint/utils";
483
- var createRule8 = ESLintUtils8.RuleCreator(
484
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
674
+ import { ESLintUtils as ESLintUtils10 } from "@typescript-eslint/utils";
675
+ var createRule10 = ESLintUtils10.RuleCreator(
676
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
485
677
  );
486
- var noExplicitReturnType = createRule8({
678
+ var noExplicitReturnType = createRule10({
487
679
  name: "no-explicit-return-type",
488
680
  meta: {
489
681
  type: "suggestion",
@@ -522,12 +714,97 @@ var noExplicitReturnType = createRule8({
522
714
  });
523
715
  var no_explicit_return_type_default = noExplicitReturnType;
524
716
 
717
+ // src/rules/jsx-no-non-component-function.ts
718
+ import { AST_NODE_TYPES as AST_NODE_TYPES7, ESLintUtils as ESLintUtils11 } from "@typescript-eslint/utils";
719
+
720
+ // src/utils.ts
721
+ import { basename, extname } from "path";
722
+ import { AST_NODE_TYPES as AST_NODE_TYPES6 } from "@typescript-eslint/utils";
723
+ var getFileExtension = (filename) => extname(filename).slice(1);
724
+
725
+ // src/rules/jsx-no-non-component-function.ts
726
+ var createRule11 = ESLintUtils11.RuleCreator(
727
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
728
+ );
729
+ var jsxNoNonComponentFunction = createRule11({
730
+ name: "jsx-no-non-component-function",
731
+ meta: {
732
+ type: "problem",
733
+ docs: {
734
+ description: "Disallow non-component functions defined at top level in .tsx and .jsx files"
735
+ },
736
+ schema: [],
737
+ messages: {
738
+ 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."
739
+ }
740
+ },
741
+ defaultOptions: [],
742
+ create(context) {
743
+ const { filename } = context;
744
+ const extension = getFileExtension(filename);
745
+ if (extension !== "tsx" && extension !== "jsx") {
746
+ return {};
747
+ }
748
+ function isReactComponent(node) {
749
+ const functionName = node.type === AST_NODE_TYPES7.FunctionDeclaration && node.id ? node.id.name : null;
750
+ if (functionName && /^[A-Z]/.test(functionName)) {
751
+ return true;
752
+ }
753
+ if (node.returnType?.typeAnnotation) {
754
+ const returnTypeNode = node.returnType.typeAnnotation;
755
+ if (returnTypeNode.type === AST_NODE_TYPES7.TSTypeReference && returnTypeNode.typeName.type === AST_NODE_TYPES7.Identifier) {
756
+ const typeName = returnTypeNode.typeName.name;
757
+ if (typeName === "JSX" || typeName === "ReactElement" || typeName === "ReactNode") {
758
+ return true;
759
+ }
760
+ }
761
+ }
762
+ return false;
763
+ }
764
+ function checkTopLevelFunction(node, declaratorNode) {
765
+ if (isReactComponent(node)) {
766
+ return;
767
+ }
768
+ const { parent } = node;
769
+ if (!parent) {
770
+ return;
771
+ }
772
+ if (parent.type === AST_NODE_TYPES7.ExportDefaultDeclaration || parent.type === AST_NODE_TYPES7.ExportNamedDeclaration) {
773
+ return;
774
+ }
775
+ if (declaratorNode?.parent?.parent?.type === AST_NODE_TYPES7.ExportNamedDeclaration) {
776
+ return;
777
+ }
778
+ if (declaratorNode?.id.type === AST_NODE_TYPES7.Identifier) {
779
+ const varName = declaratorNode.id.name;
780
+ if (/^[A-Z]/.test(varName)) {
781
+ return;
782
+ }
783
+ }
784
+ context.report({
785
+ node: declaratorNode || node,
786
+ messageId: "noTopLevelFunction"
787
+ });
788
+ }
789
+ return {
790
+ "Program > VariableDeclaration > VariableDeclarator > ArrowFunctionExpression": function checkArrowFunction(node) {
791
+ const declarator = node.parent;
792
+ checkTopLevelFunction(node, declarator);
793
+ },
794
+ "Program > FunctionDeclaration": function checkFunctionDeclaration(node) {
795
+ checkTopLevelFunction(node);
796
+ }
797
+ };
798
+ }
799
+ });
800
+ var jsx_no_non_component_function_default = jsxNoNonComponentFunction;
801
+
525
802
  // src/rules/no-logic-in-params.ts
526
- import { ESLintUtils as ESLintUtils9, AST_NODE_TYPES as AST_NODE_TYPES4 } from "@typescript-eslint/utils";
527
- var createRule9 = ESLintUtils9.RuleCreator(
528
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
803
+ import { ESLintUtils as ESLintUtils12, AST_NODE_TYPES as AST_NODE_TYPES8 } from "@typescript-eslint/utils";
804
+ var createRule12 = ESLintUtils12.RuleCreator(
805
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
529
806
  );
530
- var noLogicInParams = createRule9({
807
+ var noLogicInParams = createRule12({
531
808
  name: "no-logic-in-params",
532
809
  meta: {
533
810
  type: "suggestion",
@@ -542,20 +819,20 @@ var noLogicInParams = createRule9({
542
819
  defaultOptions: [],
543
820
  create(context) {
544
821
  const isComplexExpression = (node) => {
545
- if (node.type === AST_NODE_TYPES4.SpreadElement) {
822
+ if (node.type === AST_NODE_TYPES8.SpreadElement) {
546
823
  return false;
547
824
  }
548
- if (node.type === AST_NODE_TYPES4.ConditionalExpression) {
825
+ if (node.type === AST_NODE_TYPES8.ConditionalExpression) {
549
826
  return true;
550
827
  }
551
- if (node.type === AST_NODE_TYPES4.LogicalExpression) {
828
+ if (node.type === AST_NODE_TYPES8.LogicalExpression) {
552
829
  return true;
553
830
  }
554
- if (node.type === AST_NODE_TYPES4.BinaryExpression) {
831
+ if (node.type === AST_NODE_TYPES8.BinaryExpression) {
555
832
  const logicalOperators = ["==", "===", "!=", "!==", "<", ">", "<=", ">=", "in", "instanceof"];
556
833
  return logicalOperators.includes(node.operator);
557
834
  }
558
- if (node.type === AST_NODE_TYPES4.UnaryExpression) {
835
+ if (node.type === AST_NODE_TYPES8.UnaryExpression) {
559
836
  return node.operator === "!";
560
837
  }
561
838
  return false;
@@ -587,11 +864,11 @@ var noLogicInParams = createRule9({
587
864
  var no_logic_in_params_default = noLogicInParams;
588
865
 
589
866
  // src/rules/prefer-destructuring-params.ts
590
- import { AST_NODE_TYPES as AST_NODE_TYPES5, ESLintUtils as ESLintUtils10 } from "@typescript-eslint/utils";
591
- var createRule10 = ESLintUtils10.RuleCreator(
592
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
867
+ import { AST_NODE_TYPES as AST_NODE_TYPES9, ESLintUtils as ESLintUtils13 } from "@typescript-eslint/utils";
868
+ var createRule13 = ESLintUtils13.RuleCreator(
869
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
593
870
  );
594
- var preferDestructuringParams = createRule10({
871
+ var preferDestructuringParams = createRule13({
595
872
  name: "prefer-destructuring-params",
596
873
  meta: {
597
874
  type: "suggestion",
@@ -607,18 +884,18 @@ var preferDestructuringParams = createRule10({
607
884
  create(context) {
608
885
  const isCallbackFunction = (node) => {
609
886
  const { parent } = node;
610
- return parent?.type === AST_NODE_TYPES5.CallExpression;
887
+ return parent?.type === AST_NODE_TYPES9.CallExpression;
611
888
  };
612
889
  const isDeveloperFunction = (node) => {
613
- if (node.type === AST_NODE_TYPES5.FunctionDeclaration) {
890
+ if (node.type === AST_NODE_TYPES9.FunctionDeclaration) {
614
891
  return true;
615
892
  }
616
- if (node.type === AST_NODE_TYPES5.FunctionExpression || node.type === AST_NODE_TYPES5.ArrowFunctionExpression) {
893
+ if (node.type === AST_NODE_TYPES9.FunctionExpression || node.type === AST_NODE_TYPES9.ArrowFunctionExpression) {
617
894
  if (isCallbackFunction(node)) {
618
895
  return false;
619
896
  }
620
897
  const { parent } = node;
621
- return parent?.type === AST_NODE_TYPES5.VariableDeclarator || parent?.type === AST_NODE_TYPES5.AssignmentExpression || parent?.type === AST_NODE_TYPES5.Property || parent?.type === AST_NODE_TYPES5.MethodDefinition;
898
+ return parent?.type === AST_NODE_TYPES9.VariableDeclarator || parent?.type === AST_NODE_TYPES9.AssignmentExpression || parent?.type === AST_NODE_TYPES9.Property || parent?.type === AST_NODE_TYPES9.MethodDefinition;
622
899
  }
623
900
  return false;
624
901
  };
@@ -630,7 +907,7 @@ var preferDestructuringParams = createRule10({
630
907
  if (!isDeveloperFunction(node)) {
631
908
  return;
632
909
  }
633
- if (node.type === AST_NODE_TYPES5.FunctionDeclaration && node.id) {
910
+ if (node.type === AST_NODE_TYPES9.FunctionDeclaration && node.id) {
634
911
  const functionName = node.id.name;
635
912
  if (functionName.startsWith("_") || functionName.includes("$") || /^[A-Z][a-zA-Z]*$/.test(functionName)) {
636
913
  return;
@@ -640,7 +917,7 @@ var preferDestructuringParams = createRule10({
640
917
  return;
641
918
  }
642
919
  const hasNonDestructuredParams = node.params.some(
643
- (param) => param.type !== AST_NODE_TYPES5.ObjectPattern && param.type !== AST_NODE_TYPES5.RestElement
920
+ (param) => param.type !== AST_NODE_TYPES9.ObjectPattern && param.type !== AST_NODE_TYPES9.RestElement
644
921
  );
645
922
  if (hasNonDestructuredParams) {
646
923
  context.report({
@@ -659,11 +936,11 @@ var preferDestructuringParams = createRule10({
659
936
  var prefer_destructuring_params_default = preferDestructuringParams;
660
937
 
661
938
  // src/rules/prefer-import-type.ts
662
- import { AST_NODE_TYPES as AST_NODE_TYPES6, ESLintUtils as ESLintUtils11 } from "@typescript-eslint/utils";
663
- var createRule11 = ESLintUtils11.RuleCreator(
664
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
939
+ import { AST_NODE_TYPES as AST_NODE_TYPES10, ESLintUtils as ESLintUtils14 } from "@typescript-eslint/utils";
940
+ var createRule14 = ESLintUtils14.RuleCreator(
941
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
665
942
  );
666
- var preferImportType = createRule11({
943
+ var preferImportType = createRule14({
667
944
  name: "prefer-import-type",
668
945
  meta: {
669
946
  type: "suggestion",
@@ -678,6 +955,81 @@ var preferImportType = createRule11({
678
955
  },
679
956
  defaultOptions: [],
680
957
  create(context) {
958
+ function isInTypeContext(node) {
959
+ let current = node;
960
+ while (current) {
961
+ switch (current.type) {
962
+ case AST_NODE_TYPES10.TSTypeReference:
963
+ case AST_NODE_TYPES10.TSTypeAnnotation:
964
+ case AST_NODE_TYPES10.TSTypeParameterInstantiation:
965
+ case AST_NODE_TYPES10.TSInterfaceHeritage:
966
+ case AST_NODE_TYPES10.TSClassImplements:
967
+ case AST_NODE_TYPES10.TSTypeQuery:
968
+ case AST_NODE_TYPES10.TSTypeAssertion:
969
+ case AST_NODE_TYPES10.TSAsExpression:
970
+ case AST_NODE_TYPES10.TSSatisfiesExpression:
971
+ case AST_NODE_TYPES10.TSTypeAliasDeclaration:
972
+ case AST_NODE_TYPES10.TSInterfaceDeclaration:
973
+ case AST_NODE_TYPES10.TSTypeParameter:
974
+ case AST_NODE_TYPES10.TSQualifiedName:
975
+ return true;
976
+ case AST_NODE_TYPES10.MemberExpression:
977
+ case AST_NODE_TYPES10.Identifier:
978
+ current = current.parent;
979
+ break;
980
+ default:
981
+ return false;
982
+ }
983
+ }
984
+ return false;
985
+ }
986
+ function isUsedAsValue(localName, scope) {
987
+ const variable = scope.set.get(localName);
988
+ if (!variable) {
989
+ return false;
990
+ }
991
+ if (variable.references.length === 0) {
992
+ return false;
993
+ }
994
+ return variable.references.some((ref) => {
995
+ if (ref.isWrite()) {
996
+ return false;
997
+ }
998
+ const { identifier } = ref;
999
+ const { parent } = identifier;
1000
+ if (!parent) {
1001
+ return false;
1002
+ }
1003
+ if (isInTypeContext(parent)) {
1004
+ return false;
1005
+ }
1006
+ switch (parent.type) {
1007
+ case AST_NODE_TYPES10.CallExpression:
1008
+ case AST_NODE_TYPES10.NewExpression:
1009
+ case AST_NODE_TYPES10.JSXOpeningElement:
1010
+ case AST_NODE_TYPES10.JSXClosingElement:
1011
+ case AST_NODE_TYPES10.MemberExpression:
1012
+ case AST_NODE_TYPES10.VariableDeclarator:
1013
+ case AST_NODE_TYPES10.TaggedTemplateExpression:
1014
+ case AST_NODE_TYPES10.SpreadElement:
1015
+ case AST_NODE_TYPES10.ExportSpecifier:
1016
+ case AST_NODE_TYPES10.ArrayExpression:
1017
+ case AST_NODE_TYPES10.ObjectExpression:
1018
+ case AST_NODE_TYPES10.BinaryExpression:
1019
+ case AST_NODE_TYPES10.LogicalExpression:
1020
+ case AST_NODE_TYPES10.UnaryExpression:
1021
+ case AST_NODE_TYPES10.ReturnStatement:
1022
+ case AST_NODE_TYPES10.ArrowFunctionExpression:
1023
+ case AST_NODE_TYPES10.ConditionalExpression:
1024
+ case AST_NODE_TYPES10.AwaitExpression:
1025
+ case AST_NODE_TYPES10.YieldExpression:
1026
+ case AST_NODE_TYPES10.Property:
1027
+ return true;
1028
+ default:
1029
+ return false;
1030
+ }
1031
+ });
1032
+ }
681
1033
  function checkImportDeclaration(node) {
682
1034
  if (node.importKind === "type") {
683
1035
  return;
@@ -693,19 +1045,17 @@ var preferImportType = createRule11({
693
1045
  if (isRuntimeImport) {
694
1046
  return;
695
1047
  }
1048
+ const scope = context.sourceCode.getScope(node);
696
1049
  const isTypeOnlyImport = node.specifiers.every((specifier) => {
697
- if (specifier.type === AST_NODE_TYPES6.ImportDefaultSpecifier) {
1050
+ if (specifier.type === AST_NODE_TYPES10.ImportDefaultSpecifier) {
698
1051
  return false;
699
1052
  }
700
- if (specifier.type === AST_NODE_TYPES6.ImportNamespaceSpecifier) {
1053
+ if (specifier.type === AST_NODE_TYPES10.ImportNamespaceSpecifier) {
701
1054
  return false;
702
1055
  }
703
- if (specifier.type === AST_NODE_TYPES6.ImportSpecifier) {
704
- const importedName = specifier.imported.type === AST_NODE_TYPES6.Identifier ? specifier.imported.name : specifier.imported.value;
705
- const isKnownTypeOnly = node.source.value === "@typescript-eslint/utils" && ["TSESTree", "RuleContext"].includes(importedName) || node.source.value === "react" && ["Component", "ComponentProps", "ReactNode", "FC", "JSX", "ReactElement", "PropsWithChildren"].includes(
706
- importedName
707
- ) || importedName.endsWith("Type") || importedName.endsWith("Interface") || importedName.endsWith("Props");
708
- return isKnownTypeOnly;
1056
+ if (specifier.type === AST_NODE_TYPES10.ImportSpecifier) {
1057
+ const localName = specifier.local.name;
1058
+ return !isUsedAsValue(localName, scope);
709
1059
  }
710
1060
  return false;
711
1061
  });
@@ -729,11 +1079,11 @@ var preferImportType = createRule11({
729
1079
  var prefer_import_type_default = preferImportType;
730
1080
 
731
1081
  // src/rules/prefer-interface-over-inline-types.ts
732
- import { AST_NODE_TYPES as AST_NODE_TYPES7, ESLintUtils as ESLintUtils12 } from "@typescript-eslint/utils";
733
- var createRule12 = ESLintUtils12.RuleCreator(
734
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
1082
+ import { AST_NODE_TYPES as AST_NODE_TYPES11, ESLintUtils as ESLintUtils15 } from "@typescript-eslint/utils";
1083
+ var createRule15 = ESLintUtils15.RuleCreator(
1084
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
735
1085
  );
736
- var preferInterfaceOverInlineTypes = createRule12({
1086
+ var preferInterfaceOverInlineTypes = createRule15({
737
1087
  name: "prefer-interface-over-inline-types",
738
1088
  meta: {
739
1089
  type: "suggestion",
@@ -749,54 +1099,54 @@ var preferInterfaceOverInlineTypes = createRule12({
749
1099
  defaultOptions: [],
750
1100
  create(context) {
751
1101
  function hasJSXInConditional(node) {
752
- return node.consequent.type === AST_NODE_TYPES7.JSXElement || node.consequent.type === AST_NODE_TYPES7.JSXFragment || node.alternate.type === AST_NODE_TYPES7.JSXElement || node.alternate.type === AST_NODE_TYPES7.JSXFragment;
1102
+ return node.consequent.type === AST_NODE_TYPES11.JSXElement || node.consequent.type === AST_NODE_TYPES11.JSXFragment || node.alternate.type === AST_NODE_TYPES11.JSXElement || node.alternate.type === AST_NODE_TYPES11.JSXFragment;
753
1103
  }
754
1104
  function hasJSXInLogical(node) {
755
- return node.right.type === AST_NODE_TYPES7.JSXElement || node.right.type === AST_NODE_TYPES7.JSXFragment;
1105
+ return node.right.type === AST_NODE_TYPES11.JSXElement || node.right.type === AST_NODE_TYPES11.JSXFragment;
756
1106
  }
757
1107
  function hasJSXReturn(block) {
758
1108
  return block.body.some((stmt) => {
759
- if (stmt.type === AST_NODE_TYPES7.ReturnStatement && stmt.argument) {
760
- return stmt.argument.type === AST_NODE_TYPES7.JSXElement || stmt.argument.type === AST_NODE_TYPES7.JSXFragment || stmt.argument.type === AST_NODE_TYPES7.ConditionalExpression && hasJSXInConditional(stmt.argument) || stmt.argument.type === AST_NODE_TYPES7.LogicalExpression && hasJSXInLogical(stmt.argument);
1109
+ if (stmt.type === AST_NODE_TYPES11.ReturnStatement && stmt.argument) {
1110
+ return stmt.argument.type === AST_NODE_TYPES11.JSXElement || stmt.argument.type === AST_NODE_TYPES11.JSXFragment || stmt.argument.type === AST_NODE_TYPES11.ConditionalExpression && hasJSXInConditional(stmt.argument) || stmt.argument.type === AST_NODE_TYPES11.LogicalExpression && hasJSXInLogical(stmt.argument);
761
1111
  }
762
1112
  return false;
763
1113
  });
764
1114
  }
765
1115
  function isReactComponent(node) {
766
- if (node.type === AST_NODE_TYPES7.ArrowFunctionExpression) {
767
- if (node.body.type === AST_NODE_TYPES7.JSXElement || node.body.type === AST_NODE_TYPES7.JSXFragment) {
1116
+ if (node.type === AST_NODE_TYPES11.ArrowFunctionExpression) {
1117
+ if (node.body.type === AST_NODE_TYPES11.JSXElement || node.body.type === AST_NODE_TYPES11.JSXFragment) {
768
1118
  return true;
769
1119
  }
770
- if (node.body.type === AST_NODE_TYPES7.BlockStatement) {
1120
+ if (node.body.type === AST_NODE_TYPES11.BlockStatement) {
771
1121
  return hasJSXReturn(node.body);
772
1122
  }
773
- } else if (node.type === AST_NODE_TYPES7.FunctionExpression || node.type === AST_NODE_TYPES7.FunctionDeclaration) {
774
- if (node.body && node.body.type === AST_NODE_TYPES7.BlockStatement) {
1123
+ } else if (node.type === AST_NODE_TYPES11.FunctionExpression || node.type === AST_NODE_TYPES11.FunctionDeclaration) {
1124
+ if (node.body && node.body.type === AST_NODE_TYPES11.BlockStatement) {
775
1125
  return hasJSXReturn(node.body);
776
1126
  }
777
1127
  }
778
1128
  return false;
779
1129
  }
780
1130
  function isInlineTypeAnnotation(node) {
781
- if (node.type === AST_NODE_TYPES7.TSTypeLiteral) {
1131
+ if (node.type === AST_NODE_TYPES11.TSTypeLiteral) {
782
1132
  return true;
783
1133
  }
784
- if (node.type === AST_NODE_TYPES7.TSTypeReference && node.typeArguments) {
785
- return node.typeArguments.params.some((param) => param.type === AST_NODE_TYPES7.TSTypeLiteral);
1134
+ if (node.type === AST_NODE_TYPES11.TSTypeReference && node.typeArguments) {
1135
+ return node.typeArguments.params.some((param) => param.type === AST_NODE_TYPES11.TSTypeLiteral);
786
1136
  }
787
- if (node.type === AST_NODE_TYPES7.TSUnionType) {
1137
+ if (node.type === AST_NODE_TYPES11.TSUnionType) {
788
1138
  return node.types.some((type) => isInlineTypeAnnotation(type));
789
1139
  }
790
1140
  return false;
791
1141
  }
792
1142
  function hasInlineObjectType(node) {
793
- if (node.type === AST_NODE_TYPES7.TSTypeLiteral) {
1143
+ if (node.type === AST_NODE_TYPES11.TSTypeLiteral) {
794
1144
  return true;
795
1145
  }
796
- if (node.type === AST_NODE_TYPES7.TSTypeReference && node.typeArguments) {
797
- return node.typeArguments.params.some((param) => param.type === AST_NODE_TYPES7.TSTypeLiteral);
1146
+ if (node.type === AST_NODE_TYPES11.TSTypeReference && node.typeArguments) {
1147
+ return node.typeArguments.params.some((param) => param.type === AST_NODE_TYPES11.TSTypeLiteral);
798
1148
  }
799
- if (node.type === AST_NODE_TYPES7.TSUnionType) {
1149
+ if (node.type === AST_NODE_TYPES11.TSUnionType) {
800
1150
  return node.types.some((type) => hasInlineObjectType(type));
801
1151
  }
802
1152
  return false;
@@ -809,7 +1159,7 @@ var preferInterfaceOverInlineTypes = createRule12({
809
1159
  return;
810
1160
  }
811
1161
  const param = node.params[0];
812
- if (param.type === AST_NODE_TYPES7.Identifier && param.typeAnnotation) {
1162
+ if (param.type === AST_NODE_TYPES11.Identifier && param.typeAnnotation) {
813
1163
  const { typeAnnotation } = param.typeAnnotation;
814
1164
  if (isInlineTypeAnnotation(typeAnnotation) && hasInlineObjectType(typeAnnotation)) {
815
1165
  context.report({
@@ -828,12 +1178,100 @@ var preferInterfaceOverInlineTypes = createRule12({
828
1178
  });
829
1179
  var prefer_interface_over_inline_types_default = preferInterfaceOverInlineTypes;
830
1180
 
1181
+ // src/rules/prefer-jsx-template-literals.ts
1182
+ import { AST_NODE_TYPES as AST_NODE_TYPES12, ESLintUtils as ESLintUtils16 } from "@typescript-eslint/utils";
1183
+ var createRule16 = ESLintUtils16.RuleCreator(
1184
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
1185
+ );
1186
+ var preferJSXTemplateLiterals = createRule16({
1187
+ name: "prefer-jsx-template-literals",
1188
+ meta: {
1189
+ type: "suggestion",
1190
+ docs: {
1191
+ description: "Enforce using template literals instead of mixing text and JSX expressions"
1192
+ },
1193
+ fixable: "code",
1194
+ schema: [],
1195
+ messages: {
1196
+ preferTemplate: "Use template literal instead of mixing text with JSX expressions"
1197
+ }
1198
+ },
1199
+ defaultOptions: [],
1200
+ create(context) {
1201
+ function handleTextBeforeExpression(textNode, exprNode) {
1202
+ const textValue = textNode.value;
1203
+ const trimmedText = textValue.trim();
1204
+ if (!trimmedText) {
1205
+ return;
1206
+ }
1207
+ const hasTextContent = trimmedText.length > 0 && textValue !== trimmedText;
1208
+ const hasNoTrailingSpace = trimmedText.length > 0 && /\S$/.test(textValue);
1209
+ if (!hasTextContent && !hasNoTrailingSpace) {
1210
+ return;
1211
+ }
1212
+ context.report({
1213
+ node: textNode,
1214
+ messageId: "preferTemplate",
1215
+ fix(fixer) {
1216
+ const textPart = textValue.trimEnd();
1217
+ const exprText = context.sourceCode.getText(exprNode.expression);
1218
+ const templateLiteral = `{\`${textPart}\${${exprText}}\`}`;
1219
+ return [fixer.replaceText(textNode, templateLiteral), fixer.remove(exprNode)];
1220
+ }
1221
+ });
1222
+ }
1223
+ function handleExpressionBeforeText(exprNode, textNode) {
1224
+ const textValue = textNode.value;
1225
+ const trimmedText = textValue.trim();
1226
+ if (!trimmedText) {
1227
+ return;
1228
+ }
1229
+ const startsWithNonWhitespace = /^\S/.test(trimmedText);
1230
+ if (!startsWithNonWhitespace) {
1231
+ return;
1232
+ }
1233
+ context.report({
1234
+ node: textNode,
1235
+ messageId: "preferTemplate",
1236
+ fix(fixer) {
1237
+ const exprText = context.sourceCode.getText(exprNode.expression);
1238
+ const textPart = textValue.trim();
1239
+ const templateLiteral = `{\`\${${exprText}}${textPart}\`}`;
1240
+ return [fixer.replaceText(exprNode, templateLiteral), fixer.remove(textNode)];
1241
+ }
1242
+ });
1243
+ }
1244
+ function checkJSXElement(node) {
1245
+ const { children } = node;
1246
+ if (children.length < 2) {
1247
+ return;
1248
+ }
1249
+ for (let i = 0; i < children.length - 1; i += 1) {
1250
+ const child = children[i];
1251
+ const nextChild = children[i + 1];
1252
+ if (!child || !nextChild) {
1253
+ return;
1254
+ }
1255
+ if (child.type === AST_NODE_TYPES12.JSXText && nextChild.type === AST_NODE_TYPES12.JSXExpressionContainer) {
1256
+ handleTextBeforeExpression(child, nextChild);
1257
+ } else if (child.type === AST_NODE_TYPES12.JSXExpressionContainer && nextChild.type === AST_NODE_TYPES12.JSXText) {
1258
+ handleExpressionBeforeText(child, nextChild);
1259
+ }
1260
+ }
1261
+ }
1262
+ return {
1263
+ JSXElement: checkJSXElement
1264
+ };
1265
+ }
1266
+ });
1267
+ var prefer_jsx_template_literals_default = preferJSXTemplateLiterals;
1268
+
831
1269
  // src/rules/prefer-named-param-types.ts
832
- import { AST_NODE_TYPES as AST_NODE_TYPES8, ESLintUtils as ESLintUtils13 } from "@typescript-eslint/utils";
833
- var createRule13 = ESLintUtils13.RuleCreator(
834
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
1270
+ import { AST_NODE_TYPES as AST_NODE_TYPES13, ESLintUtils as ESLintUtils17 } from "@typescript-eslint/utils";
1271
+ var createRule17 = ESLintUtils17.RuleCreator(
1272
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
835
1273
  );
836
- var preferNamedParamTypes = createRule13({
1274
+ var preferNamedParamTypes = createRule17({
837
1275
  name: "prefer-named-param-types",
838
1276
  meta: {
839
1277
  type: "suggestion",
@@ -848,16 +1286,16 @@ var preferNamedParamTypes = createRule13({
848
1286
  defaultOptions: [],
849
1287
  create(context) {
850
1288
  function hasInlineObjectType(param) {
851
- if (param.type === AST_NODE_TYPES8.AssignmentPattern) {
1289
+ if (param.type === AST_NODE_TYPES13.AssignmentPattern) {
852
1290
  return hasInlineObjectType(param.left);
853
1291
  }
854
- if (param.type === AST_NODE_TYPES8.ObjectPattern) {
855
- if (param.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES8.TSTypeLiteral) {
1292
+ if (param.type === AST_NODE_TYPES13.ObjectPattern) {
1293
+ if (param.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES13.TSTypeLiteral) {
856
1294
  return true;
857
1295
  }
858
1296
  }
859
- if (param.type === AST_NODE_TYPES8.Identifier) {
860
- if (param.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES8.TSTypeLiteral) {
1297
+ if (param.type === AST_NODE_TYPES13.Identifier) {
1298
+ if (param.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES13.TSTypeLiteral) {
861
1299
  return true;
862
1300
  }
863
1301
  }
@@ -891,11 +1329,11 @@ var preferNamedParamTypes = createRule13({
891
1329
  var prefer_named_param_types_default = preferNamedParamTypes;
892
1330
 
893
1331
  // src/rules/prefer-react-import-types.ts
894
- import { AST_NODE_TYPES as AST_NODE_TYPES9, ESLintUtils as ESLintUtils14 } from "@typescript-eslint/utils";
895
- var createRule14 = ESLintUtils14.RuleCreator(
896
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
1332
+ import { AST_NODE_TYPES as AST_NODE_TYPES14, ESLintUtils as ESLintUtils18 } from "@typescript-eslint/utils";
1333
+ var createRule18 = ESLintUtils18.RuleCreator(
1334
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
897
1335
  );
898
- var preferReactImportTypes = createRule14({
1336
+ var preferReactImportTypes = createRule18({
899
1337
  name: "prefer-react-import-types",
900
1338
  meta: {
901
1339
  type: "suggestion",
@@ -971,7 +1409,7 @@ var preferReactImportTypes = createRule14({
971
1409
  ]);
972
1410
  const allReactExports = /* @__PURE__ */ new Set([...reactTypes, ...reactRuntimeExports]);
973
1411
  function checkMemberExpression(node) {
974
- if (node.object.type === AST_NODE_TYPES9.Identifier && node.object.name === "React" && node.property.type === AST_NODE_TYPES9.Identifier && allReactExports.has(node.property.name)) {
1412
+ if (node.object.type === AST_NODE_TYPES14.Identifier && node.object.name === "React" && node.property.type === AST_NODE_TYPES14.Identifier && allReactExports.has(node.property.name)) {
975
1413
  const typeName = node.property.name;
976
1414
  const isType = reactTypes.has(typeName);
977
1415
  const importStatement = isType ? `import type { ${typeName} } from "react"` : `import { ${typeName} } from "react"`;
@@ -988,7 +1426,7 @@ var preferReactImportTypes = createRule14({
988
1426
  return {
989
1427
  MemberExpression: checkMemberExpression,
990
1428
  "TSTypeReference > TSQualifiedName": (node) => {
991
- if (node.left.type === AST_NODE_TYPES9.Identifier && node.left.name === "React" && node.right.type === AST_NODE_TYPES9.Identifier && allReactExports.has(node.right.name)) {
1429
+ if (node.left.type === AST_NODE_TYPES14.Identifier && node.left.name === "React" && node.right.type === AST_NODE_TYPES14.Identifier && allReactExports.has(node.right.name)) {
992
1430
  const typeName = node.right.name;
993
1431
  const isType = reactTypes.has(typeName);
994
1432
  const importStatement = isType ? `import type { ${typeName} } from "react"` : `import { ${typeName} } from "react"`;
@@ -1008,11 +1446,11 @@ var preferReactImportTypes = createRule14({
1008
1446
  var prefer_react_import_types_default = preferReactImportTypes;
1009
1447
 
1010
1448
  // src/rules/react-props-destructure.ts
1011
- import { AST_NODE_TYPES as AST_NODE_TYPES10, ESLintUtils as ESLintUtils15 } from "@typescript-eslint/utils";
1012
- var createRule15 = ESLintUtils15.RuleCreator(
1013
- (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replace(/-/g, "_").toUpperCase()}.md`
1449
+ import { AST_NODE_TYPES as AST_NODE_TYPES15, ESLintUtils as ESLintUtils19 } from "@typescript-eslint/utils";
1450
+ var createRule19 = ESLintUtils19.RuleCreator(
1451
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
1014
1452
  );
1015
- var reactPropsDestructure = createRule15({
1453
+ var reactPropsDestructure = createRule19({
1016
1454
  name: "react-props-destructure",
1017
1455
  meta: {
1018
1456
  type: "suggestion",
@@ -1028,29 +1466,29 @@ var reactPropsDestructure = createRule15({
1028
1466
  defaultOptions: [],
1029
1467
  create(context) {
1030
1468
  function hasJSXInConditional(node) {
1031
- return node.consequent.type === AST_NODE_TYPES10.JSXElement || node.consequent.type === AST_NODE_TYPES10.JSXFragment || node.alternate.type === AST_NODE_TYPES10.JSXElement || node.alternate.type === AST_NODE_TYPES10.JSXFragment;
1469
+ return node.consequent.type === AST_NODE_TYPES15.JSXElement || node.consequent.type === AST_NODE_TYPES15.JSXFragment || node.alternate.type === AST_NODE_TYPES15.JSXElement || node.alternate.type === AST_NODE_TYPES15.JSXFragment;
1032
1470
  }
1033
1471
  function hasJSXInLogical(node) {
1034
- return node.right.type === AST_NODE_TYPES10.JSXElement || node.right.type === AST_NODE_TYPES10.JSXFragment;
1472
+ return node.right.type === AST_NODE_TYPES15.JSXElement || node.right.type === AST_NODE_TYPES15.JSXFragment;
1035
1473
  }
1036
1474
  function hasJSXReturn(block) {
1037
1475
  return block.body.some((stmt) => {
1038
- if (stmt.type === AST_NODE_TYPES10.ReturnStatement && stmt.argument) {
1039
- return stmt.argument.type === AST_NODE_TYPES10.JSXElement || stmt.argument.type === AST_NODE_TYPES10.JSXFragment || stmt.argument.type === AST_NODE_TYPES10.ConditionalExpression && hasJSXInConditional(stmt.argument) || stmt.argument.type === AST_NODE_TYPES10.LogicalExpression && hasJSXInLogical(stmt.argument);
1476
+ if (stmt.type === AST_NODE_TYPES15.ReturnStatement && stmt.argument) {
1477
+ return stmt.argument.type === AST_NODE_TYPES15.JSXElement || stmt.argument.type === AST_NODE_TYPES15.JSXFragment || stmt.argument.type === AST_NODE_TYPES15.ConditionalExpression && hasJSXInConditional(stmt.argument) || stmt.argument.type === AST_NODE_TYPES15.LogicalExpression && hasJSXInLogical(stmt.argument);
1040
1478
  }
1041
1479
  return false;
1042
1480
  });
1043
1481
  }
1044
1482
  function isReactComponent(node) {
1045
- if (node.type === AST_NODE_TYPES10.ArrowFunctionExpression) {
1046
- if (node.body.type === AST_NODE_TYPES10.JSXElement || node.body.type === AST_NODE_TYPES10.JSXFragment) {
1483
+ if (node.type === AST_NODE_TYPES15.ArrowFunctionExpression) {
1484
+ if (node.body.type === AST_NODE_TYPES15.JSXElement || node.body.type === AST_NODE_TYPES15.JSXFragment) {
1047
1485
  return true;
1048
1486
  }
1049
- if (node.body.type === AST_NODE_TYPES10.BlockStatement) {
1487
+ if (node.body.type === AST_NODE_TYPES15.BlockStatement) {
1050
1488
  return hasJSXReturn(node.body);
1051
1489
  }
1052
- } else if (node.type === AST_NODE_TYPES10.FunctionExpression || node.type === AST_NODE_TYPES10.FunctionDeclaration) {
1053
- if (node.body && node.body.type === AST_NODE_TYPES10.BlockStatement) {
1490
+ } else if (node.type === AST_NODE_TYPES15.FunctionExpression || node.type === AST_NODE_TYPES15.FunctionDeclaration) {
1491
+ if (node.body && node.body.type === AST_NODE_TYPES15.BlockStatement) {
1054
1492
  return hasJSXReturn(node.body);
1055
1493
  }
1056
1494
  }
@@ -1064,9 +1502,9 @@ var reactPropsDestructure = createRule15({
1064
1502
  return;
1065
1503
  }
1066
1504
  const param = node.params[0];
1067
- if (param.type === AST_NODE_TYPES10.ObjectPattern) {
1068
- const properties = param.properties.filter((prop) => prop.type === AST_NODE_TYPES10.Property).map((prop) => {
1069
- if (prop.key.type === AST_NODE_TYPES10.Identifier) {
1505
+ if (param.type === AST_NODE_TYPES15.ObjectPattern) {
1506
+ const properties = param.properties.filter((prop) => prop.type === AST_NODE_TYPES15.Property).map((prop) => {
1507
+ if (prop.key.type === AST_NODE_TYPES15.Identifier) {
1070
1508
  return prop.key.name;
1071
1509
  }
1072
1510
  return null;
@@ -1099,8 +1537,11 @@ var meta = {
1099
1537
  };
1100
1538
  var rules = {
1101
1539
  "enforce-readonly-component-props": enforce_readonly_component_props_default,
1540
+ "enforce-sorted-destructuring": enforce_sorted_destructuring_default,
1102
1541
  "file-kebab-case": file_kebab_case_default,
1103
1542
  "jsx-pascal-case": jsx_pascal_case_default,
1543
+ "jsx-no-non-component-function": jsx_no_non_component_function_default,
1544
+ "jsx-no-variable-in-callback": jsx_no_variable_in_callback_default,
1104
1545
  "md-filename-case-restriction": md_filename_case_restriction_default,
1105
1546
  "no-complex-inline-return": no_complex_inline_return_default,
1106
1547
  "no-emoji": no_emoji_default,
@@ -1110,6 +1551,7 @@ var rules = {
1110
1551
  "prefer-destructuring-params": prefer_destructuring_params_default,
1111
1552
  "prefer-import-type": prefer_import_type_default,
1112
1553
  "prefer-interface-over-inline-types": prefer_interface_over_inline_types_default,
1554
+ "prefer-jsx-template-literals": prefer_jsx_template_literals_default,
1113
1555
  "prefer-named-param-types": prefer_named_param_types_default,
1114
1556
  "prefer-react-import-types": prefer_react_import_types_default,
1115
1557
  "react-props-destructure": react_props_destructure_default
@@ -1120,6 +1562,7 @@ var plugin = {
1120
1562
  };
1121
1563
  var baseRules = {
1122
1564
  "nextfriday/no-emoji": "warn",
1565
+ "nextfriday/enforce-sorted-destructuring": "warn",
1123
1566
  "nextfriday/file-kebab-case": "warn",
1124
1567
  "nextfriday/md-filename-case-restriction": "warn",
1125
1568
  "nextfriday/prefer-destructuring-params": "warn",
@@ -1133,6 +1576,7 @@ var baseRules = {
1133
1576
  };
1134
1577
  var baseRecommendedRules = {
1135
1578
  "nextfriday/no-emoji": "error",
1579
+ "nextfriday/enforce-sorted-destructuring": "error",
1136
1580
  "nextfriday/file-kebab-case": "error",
1137
1581
  "nextfriday/md-filename-case-restriction": "error",
1138
1582
  "nextfriday/prefer-destructuring-params": "error",
@@ -1146,13 +1590,19 @@ var baseRecommendedRules = {
1146
1590
  };
1147
1591
  var jsxRules = {
1148
1592
  "nextfriday/jsx-pascal-case": "warn",
1593
+ "nextfriday/jsx-no-non-component-function": "warn",
1594
+ "nextfriday/jsx-no-variable-in-callback": "warn",
1149
1595
  "nextfriday/prefer-interface-over-inline-types": "warn",
1596
+ "nextfriday/prefer-jsx-template-literals": "warn",
1150
1597
  "nextfriday/react-props-destructure": "warn",
1151
1598
  "nextfriday/enforce-readonly-component-props": "warn"
1152
1599
  };
1153
1600
  var jsxRecommendedRules = {
1154
1601
  "nextfriday/jsx-pascal-case": "error",
1602
+ "nextfriday/jsx-no-non-component-function": "error",
1603
+ "nextfriday/jsx-no-variable-in-callback": "error",
1155
1604
  "nextfriday/prefer-interface-over-inline-types": "error",
1605
+ "nextfriday/prefer-jsx-template-literals": "error",
1156
1606
  "nextfriday/react-props-destructure": "error",
1157
1607
  "nextfriday/enforce-readonly-component-props": "error"
1158
1608
  };