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.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.6.0",
44
44
  description: "A comprehensive ESLint plugin providing custom rules and configurations for Next Friday development workflows.",
45
45
  keywords: [
46
46
  "eslint",
@@ -157,7 +157,7 @@ var package_default = {
157
157
  // src/rules/enforce-readonly-component-props.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
161
  );
162
162
  var enforceReadonlyComponentProps = createRule({
163
163
  name: "enforce-readonly-component-props",
@@ -246,11 +246,132 @@ var enforceReadonlyComponentProps = createRule({
246
246
  });
247
247
  var enforce_readonly_component_props_default = enforceReadonlyComponentProps;
248
248
 
249
- // src/rules/file-kebab-case.ts
250
- var import_path = __toESM(require("path"), 1);
249
+ // src/rules/enforce-sorted-destructuring.ts
251
250
  var import_utils2 = require("@typescript-eslint/utils");
252
251
  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`
252
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
253
+ );
254
+ var enforceSortedDestructuring = createRule2({
255
+ name: "enforce-sorted-destructuring",
256
+ meta: {
257
+ type: "suggestion",
258
+ docs: {
259
+ description: "Enforce alphabetical sorting of destructured properties with defaults first"
260
+ },
261
+ schema: [],
262
+ messages: {
263
+ unsortedDestructuring: "Destructured properties should be sorted alphabetically. Properties with defaults should come first, sorted by type (string, number, boolean, object) then alphabetically."
264
+ }
265
+ },
266
+ defaultOptions: [],
267
+ create(context) {
268
+ function getPropertyName(property) {
269
+ if (property.type === import_utils2.AST_NODE_TYPES.RestElement) {
270
+ return null;
271
+ }
272
+ if (property.key.type === import_utils2.AST_NODE_TYPES.Identifier) {
273
+ return property.key.name;
274
+ }
275
+ return null;
276
+ }
277
+ function hasDefaultValue(property) {
278
+ return property.value.type === import_utils2.AST_NODE_TYPES.AssignmentPattern && Boolean(property.value.right);
279
+ }
280
+ function getDefaultValueType(property) {
281
+ if (!hasDefaultValue(property)) {
282
+ return "none";
283
+ }
284
+ const assignmentPattern = property.value;
285
+ const { right } = assignmentPattern;
286
+ if (!right) {
287
+ return "none";
288
+ }
289
+ switch (right.type) {
290
+ case import_utils2.AST_NODE_TYPES.Literal:
291
+ if (typeof right.value === "string") {
292
+ return "string";
293
+ }
294
+ if (typeof right.value === "number") {
295
+ return "number";
296
+ }
297
+ if (typeof right.value === "boolean") {
298
+ return "boolean";
299
+ }
300
+ return "other";
301
+ case import_utils2.AST_NODE_TYPES.TemplateLiteral:
302
+ return "string";
303
+ case import_utils2.AST_NODE_TYPES.ObjectExpression:
304
+ case import_utils2.AST_NODE_TYPES.ArrayExpression:
305
+ return "object";
306
+ default:
307
+ return "other";
308
+ }
309
+ }
310
+ function getTypeSortOrder(type) {
311
+ const order = {
312
+ string: 0,
313
+ number: 1,
314
+ boolean: 2,
315
+ object: 3,
316
+ other: 4,
317
+ none: 5
318
+ };
319
+ return order[type] ?? 5;
320
+ }
321
+ function checkVariableDeclarator(node) {
322
+ if (node.id.type !== import_utils2.AST_NODE_TYPES.ObjectPattern) {
323
+ return;
324
+ }
325
+ const { properties } = node.id;
326
+ if (properties.length < 2) {
327
+ return;
328
+ }
329
+ const propertyInfo = properties.map((prop) => {
330
+ if (prop.type === import_utils2.AST_NODE_TYPES.RestElement) {
331
+ return null;
332
+ }
333
+ return {
334
+ property: prop,
335
+ name: getPropertyName(prop),
336
+ hasDefault: hasDefaultValue(prop),
337
+ defaultType: getDefaultValueType(prop)
338
+ };
339
+ }).filter((info) => info !== null && info.name !== null);
340
+ const sorted = [...propertyInfo].sort((a, b) => {
341
+ if (a.hasDefault && !b.hasDefault) {
342
+ return -1;
343
+ }
344
+ if (!a.hasDefault && b.hasDefault) {
345
+ return 1;
346
+ }
347
+ if (a.hasDefault && b.hasDefault) {
348
+ const typeComparison = getTypeSortOrder(a.defaultType) - getTypeSortOrder(b.defaultType);
349
+ if (typeComparison !== 0) {
350
+ return typeComparison;
351
+ }
352
+ }
353
+ return a.name.localeCompare(b.name);
354
+ });
355
+ const isSorted = propertyInfo.every((info, index) => info.name === sorted[index].name);
356
+ if (!isSorted) {
357
+ context.report({
358
+ node: node.id,
359
+ messageId: "unsortedDestructuring"
360
+ });
361
+ }
362
+ }
363
+ return {
364
+ VariableDeclarator: checkVariableDeclarator
365
+ };
366
+ }
367
+ });
368
+ var enforce_sorted_destructuring_default = enforceSortedDestructuring;
369
+
370
+ // src/rules/file-kebab-case.ts
371
+ var import_path = __toESM(require("path"), 1);
372
+ var import_utils3 = require("@typescript-eslint/utils");
373
+ var createRule3 = import_utils3.ESLintUtils.RuleCreator(
374
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
254
375
  );
255
376
  var isKebabCase = (str) => {
256
377
  if (/\.(config|rc|setup|spec|test)$/.test(str) || /^[a-z0-9]+(?:-[a-z0-9]+)*\.[a-z0-9]+(?:-[a-z0-9]+)*$/.test(str)) {
@@ -258,7 +379,7 @@ var isKebabCase = (str) => {
258
379
  }
259
380
  return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(str);
260
381
  };
261
- var fileKebabCase = createRule2({
382
+ var fileKebabCase = createRule3({
262
383
  name: "file-kebab-case",
263
384
  meta: {
264
385
  type: "problem",
@@ -279,8 +400,8 @@ var fileKebabCase = createRule2({
279
400
  if (ext !== ".ts" && ext !== ".js") {
280
401
  return;
281
402
  }
282
- const basename = import_path.default.basename(filename, ext);
283
- if (!isKebabCase(basename)) {
403
+ const basename2 = import_path.default.basename(filename, ext);
404
+ if (!isKebabCase(basename2)) {
284
405
  context.report({
285
406
  loc: { line: 1, column: 0 },
286
407
  messageId: "fileKebabCase"
@@ -294,12 +415,12 @@ var file_kebab_case_default = fileKebabCase;
294
415
 
295
416
  // src/rules/jsx-pascal-case.ts
296
417
  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`
418
+ var import_utils4 = require("@typescript-eslint/utils");
419
+ var createRule4 = import_utils4.ESLintUtils.RuleCreator(
420
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
300
421
  );
301
422
  var isPascalCase = (str) => /^[A-Z][a-zA-Z0-9]*$/.test(str) && !/^[A-Z]+$/.test(str);
302
- var jsxPascalCase = createRule3({
423
+ var jsxPascalCase = createRule4({
303
424
  name: "jsx-pascal-case",
304
425
  meta: {
305
426
  type: "problem",
@@ -320,8 +441,8 @@ var jsxPascalCase = createRule3({
320
441
  if (ext !== ".jsx" && ext !== ".tsx") {
321
442
  return;
322
443
  }
323
- const basename = import_path2.default.basename(filename, ext);
324
- if (!isPascalCase(basename)) {
444
+ const basename2 = import_path2.default.basename(filename, ext);
445
+ if (!isPascalCase(basename2)) {
325
446
  context.report({
326
447
  loc: { line: 1, column: 0 },
327
448
  messageId: "jsxPascalCase"
@@ -333,13 +454,84 @@ var jsxPascalCase = createRule3({
333
454
  });
334
455
  var jsx_pascal_case_default = jsxPascalCase;
335
456
 
457
+ // src/rules/jsx-no-variable-in-callback.ts
458
+ var import_utils5 = require("@typescript-eslint/utils");
459
+ var createRule5 = import_utils5.ESLintUtils.RuleCreator(
460
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
461
+ );
462
+ var jsxNoVariableInCallback = createRule5({
463
+ name: "jsx-no-variable-in-callback",
464
+ meta: {
465
+ type: "suggestion",
466
+ docs: {
467
+ description: "Disallow variable declarations inside callback functions within JSX"
468
+ },
469
+ schema: [],
470
+ messages: {
471
+ noVariableInCallback: "Variable declarations should not be inside callback functions within JSX. Extract the logic to a separate function outside the JSX."
472
+ }
473
+ },
474
+ defaultOptions: [],
475
+ create(context) {
476
+ function isInsideJSX(node) {
477
+ let current = node.parent;
478
+ while (current) {
479
+ if (current.type === import_utils5.AST_NODE_TYPES.JSXElement || current.type === import_utils5.AST_NODE_TYPES.JSXFragment) {
480
+ return true;
481
+ }
482
+ current = current.parent;
483
+ }
484
+ return false;
485
+ }
486
+ function isCallbackInJSX(node) {
487
+ if (!node.parent) {
488
+ return false;
489
+ }
490
+ if (!isInsideJSX(node)) {
491
+ return false;
492
+ }
493
+ if (node.parent.type === import_utils5.AST_NODE_TYPES.CallExpression || node.parent.type === import_utils5.AST_NODE_TYPES.JSXExpressionContainer) {
494
+ return true;
495
+ }
496
+ if (node.parent.type === import_utils5.AST_NODE_TYPES.ArrayExpression && node.parent.parent) {
497
+ if (node.parent.parent.type === import_utils5.AST_NODE_TYPES.CallExpression || node.parent.parent.type === import_utils5.AST_NODE_TYPES.JSXExpressionContainer) {
498
+ return true;
499
+ }
500
+ }
501
+ return false;
502
+ }
503
+ function checkFunctionBody(node) {
504
+ if (!isCallbackInJSX(node)) {
505
+ return;
506
+ }
507
+ const { body } = node;
508
+ if (body.type !== import_utils5.AST_NODE_TYPES.BlockStatement) {
509
+ return;
510
+ }
511
+ body.body.forEach((statement) => {
512
+ if (statement.type === import_utils5.AST_NODE_TYPES.VariableDeclaration) {
513
+ context.report({
514
+ node: statement,
515
+ messageId: "noVariableInCallback"
516
+ });
517
+ }
518
+ });
519
+ }
520
+ return {
521
+ ArrowFunctionExpression: checkFunctionBody,
522
+ FunctionExpression: checkFunctionBody
523
+ };
524
+ }
525
+ });
526
+ var jsx_no_variable_in_callback_default = jsxNoVariableInCallback;
527
+
336
528
  // src/rules/md-filename-case-restriction.ts
337
529
  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`
530
+ var import_utils6 = require("@typescript-eslint/utils");
531
+ var createRule6 = import_utils6.ESLintUtils.RuleCreator(
532
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
341
533
  );
342
- var mdFilenameCaseRestriction = createRule4({
534
+ var mdFilenameCaseRestriction = createRule6({
343
535
  name: "md-filename-case-restriction",
344
536
  meta: {
345
537
  type: "problem",
@@ -359,18 +551,18 @@ var mdFilenameCaseRestriction = createRule4({
359
551
  if (!filename.endsWith(".md")) {
360
552
  return;
361
553
  }
362
- const basename = import_path3.default.basename(filename, ".md");
554
+ const basename2 = import_path3.default.basename(filename, ".md");
363
555
  function isSnakeCase(text) {
364
556
  return /^[A-Z][A-Z0-9_]*$/.test(text);
365
557
  }
366
558
  function isValidCase(text) {
367
559
  return isSnakeCase(text);
368
560
  }
369
- if (!isValidCase(basename)) {
561
+ if (!isValidCase(basename2)) {
370
562
  context.report({
371
563
  node: context.sourceCode.ast,
372
564
  messageId: "invalidFilenameCase",
373
- data: { filename: basename }
565
+ data: { filename: basename2 }
374
566
  });
375
567
  }
376
568
  }
@@ -380,11 +572,11 @@ var mdFilenameCaseRestriction = createRule4({
380
572
  var md_filename_case_restriction_default = mdFilenameCaseRestriction;
381
573
 
382
574
  // 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`
575
+ var import_utils7 = require("@typescript-eslint/utils");
576
+ var createRule7 = import_utils7.ESLintUtils.RuleCreator(
577
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
386
578
  );
387
- var noComplexInlineReturn = createRule5({
579
+ var noComplexInlineReturn = createRule7({
388
580
  name: "no-complex-inline-return",
389
581
  meta: {
390
582
  type: "suggestion",
@@ -400,13 +592,13 @@ var noComplexInlineReturn = createRule5({
400
592
  create(context) {
401
593
  const isComplexExpression = (node) => {
402
594
  if (!node) return false;
403
- if (node.type === import_utils5.AST_NODE_TYPES.ConditionalExpression) {
595
+ if (node.type === import_utils7.AST_NODE_TYPES.ConditionalExpression) {
404
596
  return true;
405
597
  }
406
- if (node.type === import_utils5.AST_NODE_TYPES.LogicalExpression) {
598
+ if (node.type === import_utils7.AST_NODE_TYPES.LogicalExpression) {
407
599
  return true;
408
600
  }
409
- if (node.type === import_utils5.AST_NODE_TYPES.NewExpression) {
601
+ if (node.type === import_utils7.AST_NODE_TYPES.NewExpression) {
410
602
  return true;
411
603
  }
412
604
  return false;
@@ -427,11 +619,11 @@ var no_complex_inline_return_default = noComplexInlineReturn;
427
619
 
428
620
  // src/rules/no-emoji.ts
429
621
  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`
622
+ var import_utils8 = require("@typescript-eslint/utils");
623
+ var createRule8 = import_utils8.ESLintUtils.RuleCreator(
624
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
433
625
  );
434
- var noEmoji = createRule6({
626
+ var noEmoji = createRule8({
435
627
  name: "no-emoji",
436
628
  meta: {
437
629
  type: "problem",
@@ -465,11 +657,11 @@ var noEmoji = createRule6({
465
657
  var no_emoji_default = noEmoji;
466
658
 
467
659
  // 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`
660
+ var import_utils9 = require("@typescript-eslint/utils");
661
+ var createRule9 = import_utils9.ESLintUtils.RuleCreator(
662
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
471
663
  );
472
- var noEnvFallback = createRule7({
664
+ var noEnvFallback = createRule9({
473
665
  name: "no-env-fallback",
474
666
  meta: {
475
667
  type: "problem",
@@ -484,16 +676,16 @@ var noEnvFallback = createRule7({
484
676
  defaultOptions: [],
485
677
  create(context) {
486
678
  const isProcessEnvAccess = (node) => {
487
- if (node.type !== import_utils7.AST_NODE_TYPES.MemberExpression) {
679
+ if (node.type !== import_utils9.AST_NODE_TYPES.MemberExpression) {
488
680
  return false;
489
681
  }
490
682
  const { object } = node;
491
- if (object.type !== import_utils7.AST_NODE_TYPES.MemberExpression) {
683
+ if (object.type !== import_utils9.AST_NODE_TYPES.MemberExpression) {
492
684
  return false;
493
685
  }
494
686
  const processNode = object.object;
495
687
  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";
688
+ return processNode.type === import_utils9.AST_NODE_TYPES.Identifier && processNode.name === "process" && envNode.type === import_utils9.AST_NODE_TYPES.Identifier && envNode.name === "env";
497
689
  };
498
690
  return {
499
691
  LogicalExpression(node) {
@@ -518,11 +710,11 @@ var noEnvFallback = createRule7({
518
710
  var no_env_fallback_default = noEnvFallback;
519
711
 
520
712
  // 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`
713
+ var import_utils10 = require("@typescript-eslint/utils");
714
+ var createRule10 = import_utils10.ESLintUtils.RuleCreator(
715
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
524
716
  );
525
- var noExplicitReturnType = createRule8({
717
+ var noExplicitReturnType = createRule10({
526
718
  name: "no-explicit-return-type",
527
719
  meta: {
528
720
  type: "suggestion",
@@ -561,12 +753,97 @@ var noExplicitReturnType = createRule8({
561
753
  });
562
754
  var no_explicit_return_type_default = noExplicitReturnType;
563
755
 
756
+ // src/rules/jsx-no-non-component-function.ts
757
+ var import_utils12 = require("@typescript-eslint/utils");
758
+
759
+ // src/utils.ts
760
+ var import_node_path = require("path");
761
+ var import_utils11 = require("@typescript-eslint/utils");
762
+ var getFileExtension = (filename) => (0, import_node_path.extname)(filename).slice(1);
763
+
764
+ // src/rules/jsx-no-non-component-function.ts
765
+ var createRule11 = import_utils12.ESLintUtils.RuleCreator(
766
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
767
+ );
768
+ var jsxNoNonComponentFunction = createRule11({
769
+ name: "jsx-no-non-component-function",
770
+ meta: {
771
+ type: "problem",
772
+ docs: {
773
+ description: "Disallow non-component functions defined at top level in .tsx and .jsx files"
774
+ },
775
+ schema: [],
776
+ messages: {
777
+ 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."
778
+ }
779
+ },
780
+ defaultOptions: [],
781
+ create(context) {
782
+ const { filename } = context;
783
+ const extension = getFileExtension(filename);
784
+ if (extension !== "tsx" && extension !== "jsx") {
785
+ return {};
786
+ }
787
+ function isReactComponent(node) {
788
+ const functionName = node.type === import_utils12.AST_NODE_TYPES.FunctionDeclaration && node.id ? node.id.name : null;
789
+ if (functionName && /^[A-Z]/.test(functionName)) {
790
+ return true;
791
+ }
792
+ if (node.returnType?.typeAnnotation) {
793
+ const returnTypeNode = node.returnType.typeAnnotation;
794
+ if (returnTypeNode.type === import_utils12.AST_NODE_TYPES.TSTypeReference && returnTypeNode.typeName.type === import_utils12.AST_NODE_TYPES.Identifier) {
795
+ const typeName = returnTypeNode.typeName.name;
796
+ if (typeName === "JSX" || typeName === "ReactElement" || typeName === "ReactNode") {
797
+ return true;
798
+ }
799
+ }
800
+ }
801
+ return false;
802
+ }
803
+ function checkTopLevelFunction(node, declaratorNode) {
804
+ if (isReactComponent(node)) {
805
+ return;
806
+ }
807
+ const { parent } = node;
808
+ if (!parent) {
809
+ return;
810
+ }
811
+ if (parent.type === import_utils12.AST_NODE_TYPES.ExportDefaultDeclaration || parent.type === import_utils12.AST_NODE_TYPES.ExportNamedDeclaration) {
812
+ return;
813
+ }
814
+ if (declaratorNode?.parent?.parent?.type === import_utils12.AST_NODE_TYPES.ExportNamedDeclaration) {
815
+ return;
816
+ }
817
+ if (declaratorNode?.id.type === import_utils12.AST_NODE_TYPES.Identifier) {
818
+ const varName = declaratorNode.id.name;
819
+ if (/^[A-Z]/.test(varName)) {
820
+ return;
821
+ }
822
+ }
823
+ context.report({
824
+ node: declaratorNode || node,
825
+ messageId: "noTopLevelFunction"
826
+ });
827
+ }
828
+ return {
829
+ "Program > VariableDeclaration > VariableDeclarator > ArrowFunctionExpression": function checkArrowFunction(node) {
830
+ const declarator = node.parent;
831
+ checkTopLevelFunction(node, declarator);
832
+ },
833
+ "Program > FunctionDeclaration": function checkFunctionDeclaration(node) {
834
+ checkTopLevelFunction(node);
835
+ }
836
+ };
837
+ }
838
+ });
839
+ var jsx_no_non_component_function_default = jsxNoNonComponentFunction;
840
+
564
841
  // 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`
842
+ var import_utils14 = require("@typescript-eslint/utils");
843
+ var createRule12 = import_utils14.ESLintUtils.RuleCreator(
844
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
568
845
  );
569
- var noLogicInParams = createRule9({
846
+ var noLogicInParams = createRule12({
570
847
  name: "no-logic-in-params",
571
848
  meta: {
572
849
  type: "suggestion",
@@ -581,20 +858,20 @@ var noLogicInParams = createRule9({
581
858
  defaultOptions: [],
582
859
  create(context) {
583
860
  const isComplexExpression = (node) => {
584
- if (node.type === import_utils9.AST_NODE_TYPES.SpreadElement) {
861
+ if (node.type === import_utils14.AST_NODE_TYPES.SpreadElement) {
585
862
  return false;
586
863
  }
587
- if (node.type === import_utils9.AST_NODE_TYPES.ConditionalExpression) {
864
+ if (node.type === import_utils14.AST_NODE_TYPES.ConditionalExpression) {
588
865
  return true;
589
866
  }
590
- if (node.type === import_utils9.AST_NODE_TYPES.LogicalExpression) {
867
+ if (node.type === import_utils14.AST_NODE_TYPES.LogicalExpression) {
591
868
  return true;
592
869
  }
593
- if (node.type === import_utils9.AST_NODE_TYPES.BinaryExpression) {
870
+ if (node.type === import_utils14.AST_NODE_TYPES.BinaryExpression) {
594
871
  const logicalOperators = ["==", "===", "!=", "!==", "<", ">", "<=", ">=", "in", "instanceof"];
595
872
  return logicalOperators.includes(node.operator);
596
873
  }
597
- if (node.type === import_utils9.AST_NODE_TYPES.UnaryExpression) {
874
+ if (node.type === import_utils14.AST_NODE_TYPES.UnaryExpression) {
598
875
  return node.operator === "!";
599
876
  }
600
877
  return false;
@@ -626,11 +903,11 @@ var noLogicInParams = createRule9({
626
903
  var no_logic_in_params_default = noLogicInParams;
627
904
 
628
905
  // 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`
906
+ var import_utils15 = require("@typescript-eslint/utils");
907
+ var createRule13 = import_utils15.ESLintUtils.RuleCreator(
908
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
632
909
  );
633
- var preferDestructuringParams = createRule10({
910
+ var preferDestructuringParams = createRule13({
634
911
  name: "prefer-destructuring-params",
635
912
  meta: {
636
913
  type: "suggestion",
@@ -646,18 +923,18 @@ var preferDestructuringParams = createRule10({
646
923
  create(context) {
647
924
  const isCallbackFunction = (node) => {
648
925
  const { parent } = node;
649
- return parent?.type === import_utils10.AST_NODE_TYPES.CallExpression;
926
+ return parent?.type === import_utils15.AST_NODE_TYPES.CallExpression;
650
927
  };
651
928
  const isDeveloperFunction = (node) => {
652
- if (node.type === import_utils10.AST_NODE_TYPES.FunctionDeclaration) {
929
+ if (node.type === import_utils15.AST_NODE_TYPES.FunctionDeclaration) {
653
930
  return true;
654
931
  }
655
- if (node.type === import_utils10.AST_NODE_TYPES.FunctionExpression || node.type === import_utils10.AST_NODE_TYPES.ArrowFunctionExpression) {
932
+ if (node.type === import_utils15.AST_NODE_TYPES.FunctionExpression || node.type === import_utils15.AST_NODE_TYPES.ArrowFunctionExpression) {
656
933
  if (isCallbackFunction(node)) {
657
934
  return false;
658
935
  }
659
936
  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;
937
+ return parent?.type === import_utils15.AST_NODE_TYPES.VariableDeclarator || parent?.type === import_utils15.AST_NODE_TYPES.AssignmentExpression || parent?.type === import_utils15.AST_NODE_TYPES.Property || parent?.type === import_utils15.AST_NODE_TYPES.MethodDefinition;
661
938
  }
662
939
  return false;
663
940
  };
@@ -669,7 +946,7 @@ var preferDestructuringParams = createRule10({
669
946
  if (!isDeveloperFunction(node)) {
670
947
  return;
671
948
  }
672
- if (node.type === import_utils10.AST_NODE_TYPES.FunctionDeclaration && node.id) {
949
+ if (node.type === import_utils15.AST_NODE_TYPES.FunctionDeclaration && node.id) {
673
950
  const functionName = node.id.name;
674
951
  if (functionName.startsWith("_") || functionName.includes("$") || /^[A-Z][a-zA-Z]*$/.test(functionName)) {
675
952
  return;
@@ -679,7 +956,7 @@ var preferDestructuringParams = createRule10({
679
956
  return;
680
957
  }
681
958
  const hasNonDestructuredParams = node.params.some(
682
- (param) => param.type !== import_utils10.AST_NODE_TYPES.ObjectPattern && param.type !== import_utils10.AST_NODE_TYPES.RestElement
959
+ (param) => param.type !== import_utils15.AST_NODE_TYPES.ObjectPattern && param.type !== import_utils15.AST_NODE_TYPES.RestElement
683
960
  );
684
961
  if (hasNonDestructuredParams) {
685
962
  context.report({
@@ -698,11 +975,11 @@ var preferDestructuringParams = createRule10({
698
975
  var prefer_destructuring_params_default = preferDestructuringParams;
699
976
 
700
977
  // 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`
978
+ var import_utils16 = require("@typescript-eslint/utils");
979
+ var createRule14 = import_utils16.ESLintUtils.RuleCreator(
980
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
704
981
  );
705
- var preferImportType = createRule11({
982
+ var preferImportType = createRule14({
706
983
  name: "prefer-import-type",
707
984
  meta: {
708
985
  type: "suggestion",
@@ -717,6 +994,81 @@ var preferImportType = createRule11({
717
994
  },
718
995
  defaultOptions: [],
719
996
  create(context) {
997
+ function isInTypeContext(node) {
998
+ let current = node;
999
+ while (current) {
1000
+ switch (current.type) {
1001
+ case import_utils16.AST_NODE_TYPES.TSTypeReference:
1002
+ case import_utils16.AST_NODE_TYPES.TSTypeAnnotation:
1003
+ case import_utils16.AST_NODE_TYPES.TSTypeParameterInstantiation:
1004
+ case import_utils16.AST_NODE_TYPES.TSInterfaceHeritage:
1005
+ case import_utils16.AST_NODE_TYPES.TSClassImplements:
1006
+ case import_utils16.AST_NODE_TYPES.TSTypeQuery:
1007
+ case import_utils16.AST_NODE_TYPES.TSTypeAssertion:
1008
+ case import_utils16.AST_NODE_TYPES.TSAsExpression:
1009
+ case import_utils16.AST_NODE_TYPES.TSSatisfiesExpression:
1010
+ case import_utils16.AST_NODE_TYPES.TSTypeAliasDeclaration:
1011
+ case import_utils16.AST_NODE_TYPES.TSInterfaceDeclaration:
1012
+ case import_utils16.AST_NODE_TYPES.TSTypeParameter:
1013
+ case import_utils16.AST_NODE_TYPES.TSQualifiedName:
1014
+ return true;
1015
+ case import_utils16.AST_NODE_TYPES.MemberExpression:
1016
+ case import_utils16.AST_NODE_TYPES.Identifier:
1017
+ current = current.parent;
1018
+ break;
1019
+ default:
1020
+ return false;
1021
+ }
1022
+ }
1023
+ return false;
1024
+ }
1025
+ function isUsedAsValue(localName, scope) {
1026
+ const variable = scope.set.get(localName);
1027
+ if (!variable) {
1028
+ return false;
1029
+ }
1030
+ if (variable.references.length === 0) {
1031
+ return false;
1032
+ }
1033
+ return variable.references.some((ref) => {
1034
+ if (ref.isWrite()) {
1035
+ return false;
1036
+ }
1037
+ const { identifier } = ref;
1038
+ const { parent } = identifier;
1039
+ if (!parent) {
1040
+ return false;
1041
+ }
1042
+ if (isInTypeContext(parent)) {
1043
+ return false;
1044
+ }
1045
+ switch (parent.type) {
1046
+ case import_utils16.AST_NODE_TYPES.CallExpression:
1047
+ case import_utils16.AST_NODE_TYPES.NewExpression:
1048
+ case import_utils16.AST_NODE_TYPES.JSXOpeningElement:
1049
+ case import_utils16.AST_NODE_TYPES.JSXClosingElement:
1050
+ case import_utils16.AST_NODE_TYPES.MemberExpression:
1051
+ case import_utils16.AST_NODE_TYPES.VariableDeclarator:
1052
+ case import_utils16.AST_NODE_TYPES.TaggedTemplateExpression:
1053
+ case import_utils16.AST_NODE_TYPES.SpreadElement:
1054
+ case import_utils16.AST_NODE_TYPES.ExportSpecifier:
1055
+ case import_utils16.AST_NODE_TYPES.ArrayExpression:
1056
+ case import_utils16.AST_NODE_TYPES.ObjectExpression:
1057
+ case import_utils16.AST_NODE_TYPES.BinaryExpression:
1058
+ case import_utils16.AST_NODE_TYPES.LogicalExpression:
1059
+ case import_utils16.AST_NODE_TYPES.UnaryExpression:
1060
+ case import_utils16.AST_NODE_TYPES.ReturnStatement:
1061
+ case import_utils16.AST_NODE_TYPES.ArrowFunctionExpression:
1062
+ case import_utils16.AST_NODE_TYPES.ConditionalExpression:
1063
+ case import_utils16.AST_NODE_TYPES.AwaitExpression:
1064
+ case import_utils16.AST_NODE_TYPES.YieldExpression:
1065
+ case import_utils16.AST_NODE_TYPES.Property:
1066
+ return true;
1067
+ default:
1068
+ return false;
1069
+ }
1070
+ });
1071
+ }
720
1072
  function checkImportDeclaration(node) {
721
1073
  if (node.importKind === "type") {
722
1074
  return;
@@ -732,19 +1084,17 @@ var preferImportType = createRule11({
732
1084
  if (isRuntimeImport) {
733
1085
  return;
734
1086
  }
1087
+ const scope = context.sourceCode.getScope(node);
735
1088
  const isTypeOnlyImport = node.specifiers.every((specifier) => {
736
- if (specifier.type === import_utils11.AST_NODE_TYPES.ImportDefaultSpecifier) {
1089
+ if (specifier.type === import_utils16.AST_NODE_TYPES.ImportDefaultSpecifier) {
737
1090
  return false;
738
1091
  }
739
- if (specifier.type === import_utils11.AST_NODE_TYPES.ImportNamespaceSpecifier) {
1092
+ if (specifier.type === import_utils16.AST_NODE_TYPES.ImportNamespaceSpecifier) {
740
1093
  return false;
741
1094
  }
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;
1095
+ if (specifier.type === import_utils16.AST_NODE_TYPES.ImportSpecifier) {
1096
+ const localName = specifier.local.name;
1097
+ return !isUsedAsValue(localName, scope);
748
1098
  }
749
1099
  return false;
750
1100
  });
@@ -768,11 +1118,11 @@ var preferImportType = createRule11({
768
1118
  var prefer_import_type_default = preferImportType;
769
1119
 
770
1120
  // 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`
1121
+ var import_utils17 = require("@typescript-eslint/utils");
1122
+ var createRule15 = import_utils17.ESLintUtils.RuleCreator(
1123
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
774
1124
  );
775
- var preferInterfaceOverInlineTypes = createRule12({
1125
+ var preferInterfaceOverInlineTypes = createRule15({
776
1126
  name: "prefer-interface-over-inline-types",
777
1127
  meta: {
778
1128
  type: "suggestion",
@@ -788,54 +1138,54 @@ var preferInterfaceOverInlineTypes = createRule12({
788
1138
  defaultOptions: [],
789
1139
  create(context) {
790
1140
  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;
1141
+ return node.consequent.type === import_utils17.AST_NODE_TYPES.JSXElement || node.consequent.type === import_utils17.AST_NODE_TYPES.JSXFragment || node.alternate.type === import_utils17.AST_NODE_TYPES.JSXElement || node.alternate.type === import_utils17.AST_NODE_TYPES.JSXFragment;
792
1142
  }
793
1143
  function hasJSXInLogical(node) {
794
- return node.right.type === import_utils12.AST_NODE_TYPES.JSXElement || node.right.type === import_utils12.AST_NODE_TYPES.JSXFragment;
1144
+ return node.right.type === import_utils17.AST_NODE_TYPES.JSXElement || node.right.type === import_utils17.AST_NODE_TYPES.JSXFragment;
795
1145
  }
796
1146
  function hasJSXReturn(block) {
797
1147
  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);
1148
+ if (stmt.type === import_utils17.AST_NODE_TYPES.ReturnStatement && stmt.argument) {
1149
+ return stmt.argument.type === import_utils17.AST_NODE_TYPES.JSXElement || stmt.argument.type === import_utils17.AST_NODE_TYPES.JSXFragment || stmt.argument.type === import_utils17.AST_NODE_TYPES.ConditionalExpression && hasJSXInConditional(stmt.argument) || stmt.argument.type === import_utils17.AST_NODE_TYPES.LogicalExpression && hasJSXInLogical(stmt.argument);
800
1150
  }
801
1151
  return false;
802
1152
  });
803
1153
  }
804
1154
  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) {
1155
+ if (node.type === import_utils17.AST_NODE_TYPES.ArrowFunctionExpression) {
1156
+ if (node.body.type === import_utils17.AST_NODE_TYPES.JSXElement || node.body.type === import_utils17.AST_NODE_TYPES.JSXFragment) {
807
1157
  return true;
808
1158
  }
809
- if (node.body.type === import_utils12.AST_NODE_TYPES.BlockStatement) {
1159
+ if (node.body.type === import_utils17.AST_NODE_TYPES.BlockStatement) {
810
1160
  return hasJSXReturn(node.body);
811
1161
  }
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) {
1162
+ } else if (node.type === import_utils17.AST_NODE_TYPES.FunctionExpression || node.type === import_utils17.AST_NODE_TYPES.FunctionDeclaration) {
1163
+ if (node.body && node.body.type === import_utils17.AST_NODE_TYPES.BlockStatement) {
814
1164
  return hasJSXReturn(node.body);
815
1165
  }
816
1166
  }
817
1167
  return false;
818
1168
  }
819
1169
  function isInlineTypeAnnotation(node) {
820
- if (node.type === import_utils12.AST_NODE_TYPES.TSTypeLiteral) {
1170
+ if (node.type === import_utils17.AST_NODE_TYPES.TSTypeLiteral) {
821
1171
  return true;
822
1172
  }
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);
1173
+ if (node.type === import_utils17.AST_NODE_TYPES.TSTypeReference && node.typeArguments) {
1174
+ return node.typeArguments.params.some((param) => param.type === import_utils17.AST_NODE_TYPES.TSTypeLiteral);
825
1175
  }
826
- if (node.type === import_utils12.AST_NODE_TYPES.TSUnionType) {
1176
+ if (node.type === import_utils17.AST_NODE_TYPES.TSUnionType) {
827
1177
  return node.types.some((type) => isInlineTypeAnnotation(type));
828
1178
  }
829
1179
  return false;
830
1180
  }
831
1181
  function hasInlineObjectType(node) {
832
- if (node.type === import_utils12.AST_NODE_TYPES.TSTypeLiteral) {
1182
+ if (node.type === import_utils17.AST_NODE_TYPES.TSTypeLiteral) {
833
1183
  return true;
834
1184
  }
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);
1185
+ if (node.type === import_utils17.AST_NODE_TYPES.TSTypeReference && node.typeArguments) {
1186
+ return node.typeArguments.params.some((param) => param.type === import_utils17.AST_NODE_TYPES.TSTypeLiteral);
837
1187
  }
838
- if (node.type === import_utils12.AST_NODE_TYPES.TSUnionType) {
1188
+ if (node.type === import_utils17.AST_NODE_TYPES.TSUnionType) {
839
1189
  return node.types.some((type) => hasInlineObjectType(type));
840
1190
  }
841
1191
  return false;
@@ -848,7 +1198,7 @@ var preferInterfaceOverInlineTypes = createRule12({
848
1198
  return;
849
1199
  }
850
1200
  const param = node.params[0];
851
- if (param.type === import_utils12.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
1201
+ if (param.type === import_utils17.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
852
1202
  const { typeAnnotation } = param.typeAnnotation;
853
1203
  if (isInlineTypeAnnotation(typeAnnotation) && hasInlineObjectType(typeAnnotation)) {
854
1204
  context.report({
@@ -867,12 +1217,100 @@ var preferInterfaceOverInlineTypes = createRule12({
867
1217
  });
868
1218
  var prefer_interface_over_inline_types_default = preferInterfaceOverInlineTypes;
869
1219
 
1220
+ // src/rules/prefer-jsx-template-literals.ts
1221
+ var import_utils18 = require("@typescript-eslint/utils");
1222
+ var createRule16 = import_utils18.ESLintUtils.RuleCreator(
1223
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
1224
+ );
1225
+ var preferJSXTemplateLiterals = createRule16({
1226
+ name: "prefer-jsx-template-literals",
1227
+ meta: {
1228
+ type: "suggestion",
1229
+ docs: {
1230
+ description: "Enforce using template literals instead of mixing text and JSX expressions"
1231
+ },
1232
+ fixable: "code",
1233
+ schema: [],
1234
+ messages: {
1235
+ preferTemplate: "Use template literal instead of mixing text with JSX expressions"
1236
+ }
1237
+ },
1238
+ defaultOptions: [],
1239
+ create(context) {
1240
+ function handleTextBeforeExpression(textNode, exprNode) {
1241
+ const textValue = textNode.value;
1242
+ const trimmedText = textValue.trim();
1243
+ if (!trimmedText) {
1244
+ return;
1245
+ }
1246
+ const hasTextContent = trimmedText.length > 0 && textValue !== trimmedText;
1247
+ const hasNoTrailingSpace = trimmedText.length > 0 && /\S$/.test(textValue);
1248
+ if (!hasTextContent && !hasNoTrailingSpace) {
1249
+ return;
1250
+ }
1251
+ context.report({
1252
+ node: textNode,
1253
+ messageId: "preferTemplate",
1254
+ fix(fixer) {
1255
+ const textPart = textValue.trimEnd();
1256
+ const exprText = context.sourceCode.getText(exprNode.expression);
1257
+ const templateLiteral = `{\`${textPart}\${${exprText}}\`}`;
1258
+ return [fixer.replaceText(textNode, templateLiteral), fixer.remove(exprNode)];
1259
+ }
1260
+ });
1261
+ }
1262
+ function handleExpressionBeforeText(exprNode, textNode) {
1263
+ const textValue = textNode.value;
1264
+ const trimmedText = textValue.trim();
1265
+ if (!trimmedText) {
1266
+ return;
1267
+ }
1268
+ const startsWithNonWhitespace = /^\S/.test(trimmedText);
1269
+ if (!startsWithNonWhitespace) {
1270
+ return;
1271
+ }
1272
+ context.report({
1273
+ node: textNode,
1274
+ messageId: "preferTemplate",
1275
+ fix(fixer) {
1276
+ const exprText = context.sourceCode.getText(exprNode.expression);
1277
+ const textPart = textValue.trim();
1278
+ const templateLiteral = `{\`\${${exprText}}${textPart}\`}`;
1279
+ return [fixer.replaceText(exprNode, templateLiteral), fixer.remove(textNode)];
1280
+ }
1281
+ });
1282
+ }
1283
+ function checkJSXElement(node) {
1284
+ const { children } = node;
1285
+ if (children.length < 2) {
1286
+ return;
1287
+ }
1288
+ for (let i = 0; i < children.length - 1; i += 1) {
1289
+ const child = children[i];
1290
+ const nextChild = children[i + 1];
1291
+ if (!child || !nextChild) {
1292
+ return;
1293
+ }
1294
+ if (child.type === import_utils18.AST_NODE_TYPES.JSXText && nextChild.type === import_utils18.AST_NODE_TYPES.JSXExpressionContainer) {
1295
+ handleTextBeforeExpression(child, nextChild);
1296
+ } else if (child.type === import_utils18.AST_NODE_TYPES.JSXExpressionContainer && nextChild.type === import_utils18.AST_NODE_TYPES.JSXText) {
1297
+ handleExpressionBeforeText(child, nextChild);
1298
+ }
1299
+ }
1300
+ }
1301
+ return {
1302
+ JSXElement: checkJSXElement
1303
+ };
1304
+ }
1305
+ });
1306
+ var prefer_jsx_template_literals_default = preferJSXTemplateLiterals;
1307
+
870
1308
  // 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`
1309
+ var import_utils19 = require("@typescript-eslint/utils");
1310
+ var createRule17 = import_utils19.ESLintUtils.RuleCreator(
1311
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
874
1312
  );
875
- var preferNamedParamTypes = createRule13({
1313
+ var preferNamedParamTypes = createRule17({
876
1314
  name: "prefer-named-param-types",
877
1315
  meta: {
878
1316
  type: "suggestion",
@@ -887,16 +1325,16 @@ var preferNamedParamTypes = createRule13({
887
1325
  defaultOptions: [],
888
1326
  create(context) {
889
1327
  function hasInlineObjectType(param) {
890
- if (param.type === import_utils13.AST_NODE_TYPES.AssignmentPattern) {
1328
+ if (param.type === import_utils19.AST_NODE_TYPES.AssignmentPattern) {
891
1329
  return hasInlineObjectType(param.left);
892
1330
  }
893
- if (param.type === import_utils13.AST_NODE_TYPES.ObjectPattern) {
894
- if (param.typeAnnotation?.typeAnnotation.type === import_utils13.AST_NODE_TYPES.TSTypeLiteral) {
1331
+ if (param.type === import_utils19.AST_NODE_TYPES.ObjectPattern) {
1332
+ if (param.typeAnnotation?.typeAnnotation.type === import_utils19.AST_NODE_TYPES.TSTypeLiteral) {
895
1333
  return true;
896
1334
  }
897
1335
  }
898
- if (param.type === import_utils13.AST_NODE_TYPES.Identifier) {
899
- if (param.typeAnnotation?.typeAnnotation.type === import_utils13.AST_NODE_TYPES.TSTypeLiteral) {
1336
+ if (param.type === import_utils19.AST_NODE_TYPES.Identifier) {
1337
+ if (param.typeAnnotation?.typeAnnotation.type === import_utils19.AST_NODE_TYPES.TSTypeLiteral) {
900
1338
  return true;
901
1339
  }
902
1340
  }
@@ -930,11 +1368,11 @@ var preferNamedParamTypes = createRule13({
930
1368
  var prefer_named_param_types_default = preferNamedParamTypes;
931
1369
 
932
1370
  // 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`
1371
+ var import_utils20 = require("@typescript-eslint/utils");
1372
+ var createRule18 = import_utils20.ESLintUtils.RuleCreator(
1373
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
936
1374
  );
937
- var preferReactImportTypes = createRule14({
1375
+ var preferReactImportTypes = createRule18({
938
1376
  name: "prefer-react-import-types",
939
1377
  meta: {
940
1378
  type: "suggestion",
@@ -1010,7 +1448,7 @@ var preferReactImportTypes = createRule14({
1010
1448
  ]);
1011
1449
  const allReactExports = /* @__PURE__ */ new Set([...reactTypes, ...reactRuntimeExports]);
1012
1450
  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)) {
1451
+ if (node.object.type === import_utils20.AST_NODE_TYPES.Identifier && node.object.name === "React" && node.property.type === import_utils20.AST_NODE_TYPES.Identifier && allReactExports.has(node.property.name)) {
1014
1452
  const typeName = node.property.name;
1015
1453
  const isType = reactTypes.has(typeName);
1016
1454
  const importStatement = isType ? `import type { ${typeName} } from "react"` : `import { ${typeName} } from "react"`;
@@ -1027,7 +1465,7 @@ var preferReactImportTypes = createRule14({
1027
1465
  return {
1028
1466
  MemberExpression: checkMemberExpression,
1029
1467
  "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)) {
1468
+ if (node.left.type === import_utils20.AST_NODE_TYPES.Identifier && node.left.name === "React" && node.right.type === import_utils20.AST_NODE_TYPES.Identifier && allReactExports.has(node.right.name)) {
1031
1469
  const typeName = node.right.name;
1032
1470
  const isType = reactTypes.has(typeName);
1033
1471
  const importStatement = isType ? `import type { ${typeName} } from "react"` : `import { ${typeName} } from "react"`;
@@ -1047,11 +1485,11 @@ var preferReactImportTypes = createRule14({
1047
1485
  var prefer_react_import_types_default = preferReactImportTypes;
1048
1486
 
1049
1487
  // 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`
1488
+ var import_utils21 = require("@typescript-eslint/utils");
1489
+ var createRule19 = import_utils21.ESLintUtils.RuleCreator(
1490
+ (name) => `https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/${name.replaceAll("-", "_").toUpperCase()}.md`
1053
1491
  );
1054
- var reactPropsDestructure = createRule15({
1492
+ var reactPropsDestructure = createRule19({
1055
1493
  name: "react-props-destructure",
1056
1494
  meta: {
1057
1495
  type: "suggestion",
@@ -1067,29 +1505,29 @@ var reactPropsDestructure = createRule15({
1067
1505
  defaultOptions: [],
1068
1506
  create(context) {
1069
1507
  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;
1508
+ 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;
1071
1509
  }
1072
1510
  function hasJSXInLogical(node) {
1073
- return node.right.type === import_utils15.AST_NODE_TYPES.JSXElement || node.right.type === import_utils15.AST_NODE_TYPES.JSXFragment;
1511
+ return node.right.type === import_utils21.AST_NODE_TYPES.JSXElement || node.right.type === import_utils21.AST_NODE_TYPES.JSXFragment;
1074
1512
  }
1075
1513
  function hasJSXReturn(block) {
1076
1514
  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);
1515
+ if (stmt.type === import_utils21.AST_NODE_TYPES.ReturnStatement && stmt.argument) {
1516
+ 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);
1079
1517
  }
1080
1518
  return false;
1081
1519
  });
1082
1520
  }
1083
1521
  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) {
1522
+ if (node.type === import_utils21.AST_NODE_TYPES.ArrowFunctionExpression) {
1523
+ if (node.body.type === import_utils21.AST_NODE_TYPES.JSXElement || node.body.type === import_utils21.AST_NODE_TYPES.JSXFragment) {
1086
1524
  return true;
1087
1525
  }
1088
- if (node.body.type === import_utils15.AST_NODE_TYPES.BlockStatement) {
1526
+ if (node.body.type === import_utils21.AST_NODE_TYPES.BlockStatement) {
1089
1527
  return hasJSXReturn(node.body);
1090
1528
  }
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) {
1529
+ } else if (node.type === import_utils21.AST_NODE_TYPES.FunctionExpression || node.type === import_utils21.AST_NODE_TYPES.FunctionDeclaration) {
1530
+ if (node.body && node.body.type === import_utils21.AST_NODE_TYPES.BlockStatement) {
1093
1531
  return hasJSXReturn(node.body);
1094
1532
  }
1095
1533
  }
@@ -1103,9 +1541,9 @@ var reactPropsDestructure = createRule15({
1103
1541
  return;
1104
1542
  }
1105
1543
  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) {
1544
+ if (param.type === import_utils21.AST_NODE_TYPES.ObjectPattern) {
1545
+ const properties = param.properties.filter((prop) => prop.type === import_utils21.AST_NODE_TYPES.Property).map((prop) => {
1546
+ if (prop.key.type === import_utils21.AST_NODE_TYPES.Identifier) {
1109
1547
  return prop.key.name;
1110
1548
  }
1111
1549
  return null;
@@ -1138,8 +1576,11 @@ var meta = {
1138
1576
  };
1139
1577
  var rules = {
1140
1578
  "enforce-readonly-component-props": enforce_readonly_component_props_default,
1579
+ "enforce-sorted-destructuring": enforce_sorted_destructuring_default,
1141
1580
  "file-kebab-case": file_kebab_case_default,
1142
1581
  "jsx-pascal-case": jsx_pascal_case_default,
1582
+ "jsx-no-non-component-function": jsx_no_non_component_function_default,
1583
+ "jsx-no-variable-in-callback": jsx_no_variable_in_callback_default,
1143
1584
  "md-filename-case-restriction": md_filename_case_restriction_default,
1144
1585
  "no-complex-inline-return": no_complex_inline_return_default,
1145
1586
  "no-emoji": no_emoji_default,
@@ -1149,6 +1590,7 @@ var rules = {
1149
1590
  "prefer-destructuring-params": prefer_destructuring_params_default,
1150
1591
  "prefer-import-type": prefer_import_type_default,
1151
1592
  "prefer-interface-over-inline-types": prefer_interface_over_inline_types_default,
1593
+ "prefer-jsx-template-literals": prefer_jsx_template_literals_default,
1152
1594
  "prefer-named-param-types": prefer_named_param_types_default,
1153
1595
  "prefer-react-import-types": prefer_react_import_types_default,
1154
1596
  "react-props-destructure": react_props_destructure_default
@@ -1159,6 +1601,7 @@ var plugin = {
1159
1601
  };
1160
1602
  var baseRules = {
1161
1603
  "nextfriday/no-emoji": "warn",
1604
+ "nextfriday/enforce-sorted-destructuring": "warn",
1162
1605
  "nextfriday/file-kebab-case": "warn",
1163
1606
  "nextfriday/md-filename-case-restriction": "warn",
1164
1607
  "nextfriday/prefer-destructuring-params": "warn",
@@ -1172,6 +1615,7 @@ var baseRules = {
1172
1615
  };
1173
1616
  var baseRecommendedRules = {
1174
1617
  "nextfriday/no-emoji": "error",
1618
+ "nextfriday/enforce-sorted-destructuring": "error",
1175
1619
  "nextfriday/file-kebab-case": "error",
1176
1620
  "nextfriday/md-filename-case-restriction": "error",
1177
1621
  "nextfriday/prefer-destructuring-params": "error",
@@ -1185,13 +1629,19 @@ var baseRecommendedRules = {
1185
1629
  };
1186
1630
  var jsxRules = {
1187
1631
  "nextfriday/jsx-pascal-case": "warn",
1632
+ "nextfriday/jsx-no-non-component-function": "warn",
1633
+ "nextfriday/jsx-no-variable-in-callback": "warn",
1188
1634
  "nextfriday/prefer-interface-over-inline-types": "warn",
1635
+ "nextfriday/prefer-jsx-template-literals": "warn",
1189
1636
  "nextfriday/react-props-destructure": "warn",
1190
1637
  "nextfriday/enforce-readonly-component-props": "warn"
1191
1638
  };
1192
1639
  var jsxRecommendedRules = {
1193
1640
  "nextfriday/jsx-pascal-case": "error",
1641
+ "nextfriday/jsx-no-non-component-function": "error",
1642
+ "nextfriday/jsx-no-variable-in-callback": "error",
1194
1643
  "nextfriday/prefer-interface-over-inline-types": "error",
1644
+ "nextfriday/prefer-jsx-template-literals": "error",
1195
1645
  "nextfriday/react-props-destructure": "error",
1196
1646
  "nextfriday/enforce-readonly-component-props": "error"
1197
1647
  };