eslint-cdk-plugin 2.1.0 → 3.0.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/dist/index.cjs CHANGED
@@ -26,7 +26,7 @@ function _interopNamespaceDefault(e) {
26
26
  var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
27
27
 
28
28
  var name = "eslint-cdk-plugin";
29
- var version = "2.0.1";
29
+ var version = "2.2.0";
30
30
 
31
31
  const isConstructOrStackType = (type, ignoredClasses = ["App", "Stage"]) => {
32
32
  if (ignoredClasses.includes(type.symbol?.name ?? "")) return false;
@@ -82,7 +82,7 @@ const validateConstructorProperty = (constructor, context) => {
82
82
  const params = constructor.value.params;
83
83
  if (params.length < 2) {
84
84
  context.report({
85
- node: constructor,
85
+ node: constructor.value,
86
86
  messageId: "invalidConstructorProperty"
87
87
  });
88
88
  return;
@@ -90,7 +90,7 @@ const validateConstructorProperty = (constructor, context) => {
90
90
  const firstParam = params[0];
91
91
  if (firstParam.type === utils.AST_NODE_TYPES.Identifier && firstParam.name !== "scope") {
92
92
  context.report({
93
- node: constructor,
93
+ node: firstParam,
94
94
  messageId: "invalidConstructorProperty"
95
95
  });
96
96
  return;
@@ -98,7 +98,7 @@ const validateConstructorProperty = (constructor, context) => {
98
98
  const secondParam = params[1];
99
99
  if (secondParam.type === utils.AST_NODE_TYPES.Identifier && secondParam.name !== "id") {
100
100
  context.report({
101
- node: constructor,
101
+ node: secondParam,
102
102
  messageId: "invalidConstructorProperty"
103
103
  });
104
104
  return;
@@ -107,29 +107,21 @@ const validateConstructorProperty = (constructor, context) => {
107
107
  const thirdParam = params[2];
108
108
  if (thirdParam.type === utils.AST_NODE_TYPES.Identifier && thirdParam.name !== "props") {
109
109
  context.report({
110
- node: constructor,
110
+ node: thirdParam,
111
111
  messageId: "invalidConstructorProperty"
112
112
  });
113
113
  return;
114
114
  }
115
115
  };
116
116
 
117
- const SYMBOL_FLAGS = {
118
- CLASS: 32
119
- };
120
- const SYNTAX_KIND = {
121
- CLASS_DECLARATION: 263,
122
- CONSTRUCTOR: 176
123
- };
124
-
125
- const noClassInInterface = utils.ESLintUtils.RuleCreator.withoutDocs({
117
+ const noConstructInInterface = utils.ESLintUtils.RuleCreator.withoutDocs({
126
118
  meta: {
127
119
  type: "problem",
128
120
  docs: {
129
- description: "Disallow class types in interface properties"
121
+ description: "Disallow CDK Construct types in interface properties"
130
122
  },
131
123
  messages: {
132
- noClassInInterfaceProps: "Interface property '{{ propertyName }}' should not use class type '{{ typeName }}'. Consider using an interface or type alias instead."
124
+ invalidInterfaceProperty: "Interface property '{{ propertyName }}' should not use CDK Construct type '{{ typeName }}'. Consider using an interface or type alias instead."
133
125
  },
134
126
  schema: []
135
127
  },
@@ -143,12 +135,10 @@ const noClassInInterface = utils.ESLintUtils.RuleCreator.withoutDocs({
143
135
  continue;
144
136
  }
145
137
  const type = parserServices.getTypeAtLocation(property);
146
- if (!type.symbol) continue;
147
- const isClass = type.symbol.flags === SYMBOL_FLAGS.CLASS;
148
- if (!isClass) continue;
138
+ if (!isConstructOrStackType(type)) continue;
149
139
  context.report({
150
140
  node: property,
151
- messageId: "noClassInInterfaceProps",
141
+ messageId: "invalidInterfaceProperty",
152
142
  data: {
153
143
  propertyName: property.key.name,
154
144
  typeName: type.symbol.name
@@ -160,6 +150,83 @@ const noClassInInterface = utils.ESLintUtils.RuleCreator.withoutDocs({
160
150
  }
161
151
  });
162
152
 
153
+ const noConstructInPublicPropertyOfConstruct = utils.ESLintUtils.RuleCreator.withoutDocs({
154
+ meta: {
155
+ type: "problem",
156
+ docs: {
157
+ description: "Disallow Construct types in public property of Construct"
158
+ },
159
+ messages: {
160
+ invalidPublicPropertyOfConstruct: "Public property '{{ propertyName }}' of Construct should not use Construct type '{{ typeName }}'. Consider using an interface or type alias instead."
161
+ },
162
+ schema: []
163
+ },
164
+ defaultOptions: [],
165
+ create(context) {
166
+ const parserServices = utils.ESLintUtils.getParserServices(context);
167
+ return {
168
+ ClassDeclaration(node) {
169
+ const type = parserServices.getTypeAtLocation(node);
170
+ if (!isConstructOrStackType(type)) return;
171
+ validatePublicPropertyOfConstruct(node, context, parserServices);
172
+ const constructor = node.body.body.find(
173
+ (member) => member.type === utils.AST_NODE_TYPES.MethodDefinition && member.kind === "constructor"
174
+ );
175
+ if (!constructor || constructor.value.type !== utils.AST_NODE_TYPES.FunctionExpression) {
176
+ return;
177
+ }
178
+ validateConstructorParameterProperty(
179
+ constructor,
180
+ context,
181
+ parserServices
182
+ );
183
+ }
184
+ };
185
+ }
186
+ });
187
+ const validatePublicPropertyOfConstruct = (node, context, parserServices) => {
188
+ for (const property of node.body.body) {
189
+ if (property.type !== utils.AST_NODE_TYPES.PropertyDefinition || property.key.type !== utils.AST_NODE_TYPES.Identifier) {
190
+ continue;
191
+ }
192
+ if (["private", "protected"].includes(property.accessibility ?? "")) {
193
+ continue;
194
+ }
195
+ if (!property.typeAnnotation) continue;
196
+ const type = parserServices.getTypeAtLocation(property);
197
+ if (!isConstructOrStackType(type)) continue;
198
+ context.report({
199
+ node: property,
200
+ messageId: "invalidPublicPropertyOfConstruct",
201
+ data: {
202
+ propertyName: property.key.name,
203
+ typeName: type.symbol.name
204
+ }
205
+ });
206
+ }
207
+ };
208
+ const validateConstructorParameterProperty = (constructor, context, parserServices) => {
209
+ for (const param of constructor.value.params) {
210
+ if (param.type !== utils.AST_NODE_TYPES.TSParameterProperty || param.parameter.type !== utils.AST_NODE_TYPES.Identifier) {
211
+ continue;
212
+ }
213
+ if (["private", "protected"].includes(param.accessibility ?? "")) {
214
+ continue;
215
+ }
216
+ if (!param.parameter.typeAnnotation) continue;
217
+ const type = parserServices.getTypeAtLocation(param);
218
+ if (!isConstructOrStackType(type)) continue;
219
+ context.report({
220
+ node: param,
221
+ messageId: "invalidPublicPropertyOfConstruct",
222
+ data: {
223
+ propertyName: param.parameter.name,
224
+ typeName: type.symbol.name
225
+ }
226
+ });
227
+ }
228
+ };
229
+
163
230
  const toPascalCase = (str) => {
164
231
  return str.split(/[-_\s]/).map((word) => {
165
232
  return word.replace(/([A-Z])/g, " $1").split(/\s+/).map(
@@ -168,6 +235,11 @@ const toPascalCase = (str) => {
168
235
  }).join("");
169
236
  };
170
237
 
238
+ const SYNTAX_KIND = {
239
+ CLASS_DECLARATION: 263,
240
+ CONSTRUCTOR: 176
241
+ };
242
+
171
243
  const getConstructorPropertyNames = (type) => {
172
244
  const declarations = type.symbol?.declarations;
173
245
  if (!declarations?.length) return [];
@@ -197,7 +269,7 @@ const noConstructStackSuffix = utils.ESLintUtils.RuleCreator.withoutDocs({
197
269
  description: "Effort to avoid using 'Construct' and 'Stack' suffix in construct id."
198
270
  },
199
271
  messages: {
200
- noConstructStackSuffix: "{{ classType }} ID '{{ id }}' should not include {{ suffix }} suffix."
272
+ invalidConstructId: "{{ classType }} ID '{{ id }}' should not include {{ suffix }} suffix."
201
273
  },
202
274
  schema: [
203
275
  {
@@ -248,8 +320,8 @@ const validateConstructId$3 = (node, context, options) => {
248
320
  const disallowedSuffixes = options.disallowedSuffixes;
249
321
  if (disallowedSuffixes.includes(SUFFIX_TYPE.CONSTRUCT) && formattedConstructId.endsWith(SUFFIX_TYPE.CONSTRUCT)) {
250
322
  context.report({
251
- node,
252
- messageId: "noConstructStackSuffix",
323
+ node: secondArg,
324
+ messageId: "invalidConstructId",
253
325
  data: {
254
326
  classType: "Construct",
255
327
  id: secondArg.value,
@@ -258,8 +330,8 @@ const validateConstructId$3 = (node, context, options) => {
258
330
  });
259
331
  } else if (disallowedSuffixes.includes(SUFFIX_TYPE.STACK) && formattedConstructId.endsWith(SUFFIX_TYPE.STACK)) {
260
332
  context.report({
261
- node,
262
- messageId: "noConstructStackSuffix",
333
+ node: secondArg,
334
+ messageId: "invalidConstructId",
263
335
  data: {
264
336
  classType: "Stack",
265
337
  id: secondArg.value,
@@ -276,7 +348,7 @@ const noImportPrivate = {
276
348
  description: "Cannot import modules from private dir at different levels of the hierarchy."
277
349
  },
278
350
  messages: {
279
- noImportPrivate: "Cannot import modules from private dir at different levels of the hierarchy."
351
+ invalidImportPath: "Cannot import modules from private dir at different levels of the hierarchy."
280
352
  },
281
353
  schema: []
282
354
  },
@@ -295,7 +367,7 @@ const noImportPrivate = {
295
367
  if (currentDirSegments.length !== importDirSegments.length || currentDirSegments.some(
296
368
  (segment, index) => segment !== importDirSegments[index]
297
369
  )) {
298
- context.report({ node, messageId: "noImportPrivate" });
370
+ context.report({ node, messageId: "invalidImportPath" });
299
371
  }
300
372
  }
301
373
  };
@@ -305,15 +377,15 @@ const getDirSegments = (dirPath) => {
305
377
  return dirPath.split(path__namespace.sep).filter((segment) => segment !== "");
306
378
  };
307
379
 
308
- const noMutablePropsInterface = utils.ESLintUtils.RuleCreator.withoutDocs({
380
+ const noMutablePropertyOfPropsInterface = utils.ESLintUtils.RuleCreator.withoutDocs({
309
381
  meta: {
310
382
  type: "problem",
311
383
  docs: {
312
- description: "Disallow mutable properties in Props interfaces"
384
+ description: "Disallow mutable properties of Construct Props (interface)"
313
385
  },
314
386
  fixable: "code",
315
387
  messages: {
316
- noMutablePropsInterface: "Property '{{ propertyName }}' in Props interface should be readonly."
388
+ invalidPropertyOfPropsInterface: "Property '{{ propertyName }}' of Construct Props should be readonly."
317
389
  },
318
390
  schema: []
319
391
  },
@@ -330,7 +402,7 @@ const noMutablePropsInterface = utils.ESLintUtils.RuleCreator.withoutDocs({
330
402
  if (property.readonly) continue;
331
403
  context.report({
332
404
  node: property,
333
- messageId: "noMutablePropsInterface",
405
+ messageId: "invalidPropertyOfPropsInterface",
334
406
  data: {
335
407
  propertyName: property.key.name
336
408
  },
@@ -345,15 +417,15 @@ const noMutablePropsInterface = utils.ESLintUtils.RuleCreator.withoutDocs({
345
417
  }
346
418
  });
347
419
 
348
- const noMutablePublicFields = utils.ESLintUtils.RuleCreator.withoutDocs({
420
+ const noMutablePublicPropertyOfConstruct = utils.ESLintUtils.RuleCreator.withoutDocs({
349
421
  meta: {
350
422
  type: "problem",
351
423
  docs: {
352
- description: "Disallow mutable public class fields"
424
+ description: "Disallow mutable public properties of Construct"
353
425
  },
354
426
  fixable: "code",
355
427
  messages: {
356
- noMutablePublicFields: "Public field '{{ propertyName }}' should be readonly. Consider adding the 'readonly' modifier."
428
+ invalidPublicPropertyOfConstruct: "Public property '{{ propertyName }}' should be readonly. Consider adding the 'readonly' modifier."
357
429
  },
358
430
  schema: []
359
431
  },
@@ -375,7 +447,7 @@ const noMutablePublicFields = utils.ESLintUtils.RuleCreator.withoutDocs({
375
447
  if (member.readonly) continue;
376
448
  context.report({
377
449
  node: member,
378
- messageId: "noMutablePublicFields",
450
+ messageId: "invalidPublicPropertyOfConstruct",
379
451
  data: {
380
452
  propertyName: member.key.name
381
453
  },
@@ -404,12 +476,30 @@ const noParentNameConstructIdMatch = utils.ESLintUtils.RuleCreator.withoutDocs(
404
476
  description: "Enforce that construct IDs does not match the parent construct name."
405
477
  },
406
478
  messages: {
407
- noParentNameConstructIdMatch: "Construct ID '{{ constructId }}' should not match parent construct name '{{ parentConstructName }}'. Use a more specific identifier."
479
+ invalidConstructId: "Construct ID '{{ constructId }}' should not match parent construct name '{{ parentConstructName }}'. Use a more specific identifier."
408
480
  },
409
- schema: []
481
+ schema: [
482
+ {
483
+ type: "object",
484
+ properties: {
485
+ disallowContainingParentName: {
486
+ type: "boolean",
487
+ default: false
488
+ }
489
+ },
490
+ additionalProperties: false
491
+ }
492
+ ]
410
493
  },
411
- defaultOptions: [],
494
+ defaultOptions: [
495
+ {
496
+ disallowContainingParentName: false
497
+ }
498
+ ],
412
499
  create(context) {
500
+ const option = context.options[0] || {
501
+ disallowContainingParentName: false
502
+ };
413
503
  const parserServices = utils.ESLintUtils.getParserServices(context);
414
504
  return {
415
505
  ClassBody(node) {
@@ -424,11 +514,11 @@ const noParentNameConstructIdMatch = utils.ESLintUtils.RuleCreator.withoutDocs(
424
514
  continue;
425
515
  }
426
516
  validateConstructorBody({
427
- node,
428
517
  expression: body.value,
429
518
  parentClassName,
430
519
  context,
431
- parserServices
520
+ parserServices,
521
+ option
432
522
  });
433
523
  }
434
524
  }
@@ -437,11 +527,11 @@ const noParentNameConstructIdMatch = utils.ESLintUtils.RuleCreator.withoutDocs(
437
527
  }
438
528
  );
439
529
  const validateConstructorBody = ({
440
- node,
441
530
  expression,
442
531
  parentClassName,
443
532
  context,
444
- parserServices
533
+ parserServices,
534
+ option
445
535
  }) => {
446
536
  for (const statement of expression.body.body) {
447
537
  switch (statement.type) {
@@ -449,32 +539,32 @@ const validateConstructorBody = ({
449
539
  const newExpression = statement.declarations[0].init;
450
540
  if (newExpression?.type !== utils.AST_NODE_TYPES.NewExpression) continue;
451
541
  validateConstructId$2({
452
- node,
453
542
  context,
454
543
  expression: newExpression,
455
544
  parentClassName,
456
- parserServices
545
+ parserServices,
546
+ option
457
547
  });
458
548
  break;
459
549
  }
460
550
  case utils.AST_NODE_TYPES.ExpressionStatement: {
461
551
  if (statement.expression?.type !== utils.AST_NODE_TYPES.NewExpression) break;
462
552
  validateStatement({
463
- node,
464
553
  statement,
465
554
  parentClassName,
466
555
  context,
467
- parserServices
556
+ parserServices,
557
+ option
468
558
  });
469
559
  break;
470
560
  }
471
561
  case utils.AST_NODE_TYPES.IfStatement: {
472
562
  traverseStatements({
473
- node,
474
563
  context,
475
564
  parentClassName,
476
565
  statement: statement.consequent,
477
- parserServices
566
+ parserServices,
567
+ option
478
568
  });
479
569
  break;
480
570
  }
@@ -482,11 +572,11 @@ const validateConstructorBody = ({
482
572
  for (const switchCase of statement.cases) {
483
573
  for (const statement2 of switchCase.consequent) {
484
574
  traverseStatements({
485
- node,
486
575
  context,
487
576
  parentClassName,
488
577
  statement: statement2,
489
- parserServices
578
+ parserServices,
579
+ option
490
580
  });
491
581
  }
492
582
  }
@@ -496,21 +586,21 @@ const validateConstructorBody = ({
496
586
  }
497
587
  };
498
588
  const traverseStatements = ({
499
- node,
500
589
  statement,
501
590
  parentClassName,
502
591
  context,
503
- parserServices
592
+ parserServices,
593
+ option
504
594
  }) => {
505
595
  switch (statement.type) {
506
596
  case utils.AST_NODE_TYPES.BlockStatement: {
507
597
  for (const body of statement.body) {
508
598
  validateStatement({
509
- node,
510
599
  statement: body,
511
600
  parentClassName,
512
601
  context,
513
- parserServices
602
+ parserServices,
603
+ option
514
604
  });
515
605
  }
516
606
  break;
@@ -519,11 +609,11 @@ const traverseStatements = ({
519
609
  const newExpression = statement.expression;
520
610
  if (newExpression?.type !== utils.AST_NODE_TYPES.NewExpression) break;
521
611
  validateStatement({
522
- node,
523
612
  statement,
524
613
  parentClassName,
525
614
  context,
526
- parserServices
615
+ parserServices,
616
+ option
527
617
  });
528
618
  break;
529
619
  }
@@ -531,33 +621,33 @@ const traverseStatements = ({
531
621
  const newExpression = statement.declarations[0].init;
532
622
  if (newExpression?.type !== utils.AST_NODE_TYPES.NewExpression) break;
533
623
  validateConstructId$2({
534
- node,
535
624
  context,
536
625
  expression: newExpression,
537
626
  parentClassName,
538
- parserServices
627
+ parserServices,
628
+ option
539
629
  });
540
630
  break;
541
631
  }
542
632
  }
543
633
  };
544
634
  const validateStatement = ({
545
- node,
546
635
  statement,
547
636
  parentClassName,
548
637
  context,
549
- parserServices
638
+ parserServices,
639
+ option
550
640
  }) => {
551
641
  switch (statement.type) {
552
642
  case utils.AST_NODE_TYPES.VariableDeclaration: {
553
643
  const newExpression = statement.declarations[0].init;
554
644
  if (newExpression?.type !== utils.AST_NODE_TYPES.NewExpression) break;
555
645
  validateConstructId$2({
556
- node,
557
646
  context,
558
647
  expression: newExpression,
559
648
  parentClassName,
560
- parserServices
649
+ parserServices,
650
+ option
561
651
  });
562
652
  break;
563
653
  }
@@ -565,76 +655,76 @@ const validateStatement = ({
565
655
  const newExpression = statement.expression;
566
656
  if (newExpression?.type !== utils.AST_NODE_TYPES.NewExpression) break;
567
657
  validateConstructId$2({
568
- node,
569
658
  context,
570
659
  expression: newExpression,
571
660
  parentClassName,
572
- parserServices
661
+ parserServices,
662
+ option
573
663
  });
574
664
  break;
575
665
  }
576
666
  case utils.AST_NODE_TYPES.IfStatement: {
577
667
  validateIfStatement({
578
- node,
579
668
  statement,
580
669
  parentClassName,
581
670
  context,
582
- parserServices
671
+ parserServices,
672
+ option
583
673
  });
584
674
  break;
585
675
  }
586
676
  case utils.AST_NODE_TYPES.SwitchStatement: {
587
677
  validateSwitchStatement({
588
- node,
589
678
  statement,
590
679
  parentClassName,
591
680
  context,
592
- parserServices
681
+ parserServices,
682
+ option
593
683
  });
594
684
  break;
595
685
  }
596
686
  }
597
687
  };
598
688
  const validateIfStatement = ({
599
- node,
600
689
  statement,
601
690
  parentClassName,
602
691
  context,
603
- parserServices
692
+ parserServices,
693
+ option
604
694
  }) => {
605
695
  traverseStatements({
606
- node,
607
696
  context,
608
697
  parentClassName,
609
698
  statement: statement.consequent,
610
- parserServices
699
+ parserServices,
700
+ option
611
701
  });
612
702
  };
613
703
  const validateSwitchStatement = ({
614
- node,
615
704
  statement,
616
705
  parentClassName,
617
706
  context,
618
- parserServices
707
+ parserServices,
708
+ option
619
709
  }) => {
620
710
  for (const caseStatement of statement.cases) {
621
711
  for (const _consequent of caseStatement.consequent) {
622
712
  traverseStatements({
623
- node,
624
713
  context,
625
714
  parentClassName,
626
715
  statement: _consequent,
627
- parserServices
716
+ parserServices,
717
+ option
628
718
  });
629
719
  }
630
720
  }
631
721
  };
632
722
  const validateConstructId$2 = ({
633
- node,
634
723
  context,
635
724
  expression,
636
725
  parentClassName,
637
- parserServices
726
+ parserServices,
727
+ option
638
728
  }) => {
639
729
  const type = parserServices.getTypeAtLocation(expression);
640
730
  if (expression.arguments.length < 2) return;
@@ -645,94 +735,24 @@ const validateConstructId$2 = ({
645
735
  const formattedConstructId = toPascalCase(secondArg.value);
646
736
  const formattedParentClassName = toPascalCase(parentClassName);
647
737
  if (!isConstructType(type)) return;
648
- if (formattedConstructId.includes(formattedParentClassName)) {
738
+ if (option.disallowContainingParentName && formattedConstructId.includes(formattedParentClassName)) {
649
739
  context.report({
650
- node,
651
- messageId: "noParentNameConstructIdMatch",
740
+ node: secondArg,
741
+ messageId: "invalidConstructId",
652
742
  data: {
653
743
  constructId: secondArg.value,
654
744
  parentConstructName: parentClassName
655
745
  }
656
746
  });
747
+ return;
657
748
  }
658
- };
659
-
660
- const noPublicClassFields = utils.ESLintUtils.RuleCreator.withoutDocs({
661
- meta: {
662
- type: "problem",
663
- docs: {
664
- description: "Disallow class types in public class fields"
665
- },
666
- messages: {
667
- noPublicClassFields: "Public field '{{ propertyName }}' should not use class type '{{ typeName }}'. Consider using an interface or type alias instead."
668
- },
669
- schema: []
670
- },
671
- defaultOptions: [],
672
- create(context) {
673
- const parserServices = utils.ESLintUtils.getParserServices(context);
674
- return {
675
- ClassDeclaration(node) {
676
- const type = parserServices.getTypeAtLocation(node);
677
- if (!isConstructOrStackType(type)) return;
678
- validateClassMember(node, context, parserServices);
679
- const constructor = node.body.body.find(
680
- (member) => member.type === utils.AST_NODE_TYPES.MethodDefinition && member.kind === "constructor"
681
- );
682
- if (!constructor || constructor.value.type !== utils.AST_NODE_TYPES.FunctionExpression) {
683
- return;
684
- }
685
- validateConstructorParameterProperty(
686
- constructor,
687
- context,
688
- parserServices
689
- );
690
- }
691
- };
692
- }
693
- });
694
- const validateClassMember = (node, context, parserServices) => {
695
- for (const member of node.body.body) {
696
- if (member.type !== utils.AST_NODE_TYPES.PropertyDefinition || member.key.type !== utils.AST_NODE_TYPES.Identifier) {
697
- continue;
698
- }
699
- if (["private", "protected"].includes(member.accessibility ?? "")) {
700
- continue;
701
- }
702
- if (!member.typeAnnotation) continue;
703
- const type = parserServices.getTypeAtLocation(member);
704
- if (!type.symbol) continue;
705
- const isClass = type.symbol.flags === SYMBOL_FLAGS.CLASS;
706
- if (!isClass) continue;
707
- context.report({
708
- node: member,
709
- messageId: "noPublicClassFields",
710
- data: {
711
- propertyName: member.key.name,
712
- typeName: type.symbol.name
713
- }
714
- });
715
- }
716
- };
717
- const validateConstructorParameterProperty = (constructor, context, parserServices) => {
718
- for (const param of constructor.value.params) {
719
- if (param.type !== utils.AST_NODE_TYPES.TSParameterProperty || param.parameter.type !== utils.AST_NODE_TYPES.Identifier) {
720
- continue;
721
- }
722
- if (["private", "protected"].includes(param.accessibility ?? "")) {
723
- continue;
724
- }
725
- if (!param.parameter.typeAnnotation) continue;
726
- const type = parserServices.getTypeAtLocation(param);
727
- if (!type.symbol) continue;
728
- const isClass = type.symbol.flags === SYMBOL_FLAGS.CLASS;
729
- if (!isClass) continue;
749
+ if (formattedParentClassName === formattedConstructId) {
730
750
  context.report({
731
- node: param,
732
- messageId: "noPublicClassFields",
751
+ node: secondArg,
752
+ messageId: "invalidConstructId",
733
753
  data: {
734
- propertyName: param.parameter.name,
735
- typeName: type.symbol.name
754
+ constructId: secondArg.value,
755
+ parentConstructName: parentClassName
736
756
  }
737
757
  });
738
758
  }
@@ -745,7 +765,7 @@ const noVariableConstructId = utils.ESLintUtils.RuleCreator.withoutDocs({
745
765
  description: `Enforce using literal strings for Construct ID.`
746
766
  },
747
767
  messages: {
748
- noVariableConstructId: "Shouldn't use a parameter as a Construct ID."
768
+ invalidConstructId: "Shouldn't use a parameter as a Construct ID."
749
769
  },
750
770
  schema: []
751
771
  },
@@ -773,8 +793,8 @@ const validateConstructId$1 = (node, context) => {
773
793
  return;
774
794
  }
775
795
  context.report({
776
- node,
777
- messageId: "noVariableConstructId"
796
+ node: secondArg,
797
+ messageId: "invalidConstructId"
778
798
  });
779
799
  };
780
800
  const isInsideLoop = (node) => {
@@ -810,7 +830,7 @@ const pascalCaseConstructId = utils.ESLintUtils.RuleCreator.withoutDocs({
810
830
  description: "Enforce PascalCase for Construct ID."
811
831
  },
812
832
  messages: {
813
- pascalCaseConstructId: "Construct ID must be PascalCase."
833
+ invalidConstructId: "Construct ID must be PascalCase."
814
834
  },
815
835
  schema: [],
816
836
  fixable: "code"
@@ -843,8 +863,8 @@ const validateConstructId = (node, context) => {
843
863
  const quote = secondArg.raw?.startsWith('"') ? QUOTE_TYPE.DOUBLE : QUOTE_TYPE.SINGLE;
844
864
  if (isPascalCase(secondArg.value)) return;
845
865
  context.report({
846
- node,
847
- messageId: "pascalCaseConstructId",
866
+ node: secondArg,
867
+ messageId: "invalidConstructId",
848
868
  fix: (fixer) => {
849
869
  const pascalCaseValue = toPascalCase(secondArg.value);
850
870
  return fixer.replaceText(secondArg, `${quote}${pascalCaseValue}${quote}`);
@@ -971,7 +991,7 @@ const requirePassingThis = utils.ESLintUtils.RuleCreator.withoutDocs({
971
991
  description: "Require passing `this` in a constructor."
972
992
  },
973
993
  messages: {
974
- requirePassingThis: "Require passing `this` in a constructor."
994
+ missingPassingThis: "Require passing `this` in a constructor."
975
995
  },
976
996
  schema: [
977
997
  {
@@ -1007,8 +1027,8 @@ const requirePassingThis = utils.ESLintUtils.RuleCreator.withoutDocs({
1007
1027
  if (constructorPropertyNames[0] !== "scope") return;
1008
1028
  if (!options.allowNonThisAndDisallowScope) {
1009
1029
  context.report({
1010
- node,
1011
- messageId: "requirePassingThis",
1030
+ node: argument,
1031
+ messageId: "missingPassingThis",
1012
1032
  fix: (fixer) => {
1013
1033
  return fixer.replaceText(argument, "this");
1014
1034
  }
@@ -1017,8 +1037,8 @@ const requirePassingThis = utils.ESLintUtils.RuleCreator.withoutDocs({
1017
1037
  }
1018
1038
  if (argument.type === utils.AST_NODE_TYPES.Identifier && argument.name === "scope") {
1019
1039
  context.report({
1020
- node,
1021
- messageId: "requirePassingThis",
1040
+ node: argument,
1041
+ messageId: "missingPassingThis",
1022
1042
  fix: (fixer) => {
1023
1043
  return fixer.replaceText(argument, "this");
1024
1044
  }
@@ -1072,20 +1092,20 @@ const requirePropsDefaultDoc = utils.ESLintUtils.RuleCreator.withoutDocs({
1072
1092
  });
1073
1093
 
1074
1094
  const rules = {
1075
- "no-class-in-interface": noClassInInterface,
1095
+ "construct-constructor-property": constructConstructorProperty,
1096
+ "no-construct-in-interface": noConstructInInterface,
1097
+ "no-construct-in-public-property-of-construct": noConstructInPublicPropertyOfConstruct,
1076
1098
  "no-construct-stack-suffix": noConstructStackSuffix,
1099
+ "no-import-private": noImportPrivate,
1100
+ "no-mutable-property-of-props-interface": noMutablePropertyOfPropsInterface,
1101
+ "no-mutable-public-property-of-construct": noMutablePublicPropertyOfConstruct,
1077
1102
  "no-parent-name-construct-id-match": noParentNameConstructIdMatch,
1078
- "no-public-class-fields": noPublicClassFields,
1079
- "pascal-case-construct-id": pascalCaseConstructId,
1080
- "require-passing-this": requirePassingThis,
1081
1103
  "no-variable-construct-id": noVariableConstructId,
1082
- "no-mutable-public-fields": noMutablePublicFields,
1083
- "no-mutable-props-interface": noMutablePropsInterface,
1084
- "construct-constructor-property": constructConstructorProperty,
1085
- "require-jsdoc": requireJSDoc,
1086
- "require-props-default-doc": requirePropsDefaultDoc,
1104
+ "pascal-case-construct-id": pascalCaseConstructId,
1087
1105
  "props-name-convention": propsNameConvention,
1088
- "no-import-private": noImportPrivate
1106
+ "require-jsdoc": requireJSDoc,
1107
+ "require-passing-this": requirePassingThis,
1108
+ "require-props-default-doc": requirePropsDefaultDoc
1089
1109
  };
1090
1110
  const cdkPlugin = {
1091
1111
  meta: { name, version },
@@ -1106,32 +1126,38 @@ const createFlatConfig = (rules2) => {
1106
1126
  };
1107
1127
  };
1108
1128
  const recommended = createFlatConfig({
1109
- "cdk/no-class-in-interface": "error",
1129
+ "cdk/construct-constructor-property": "error",
1130
+ "cdk/no-construct-in-interface": "error",
1131
+ "cdk/no-construct-in-public-property-of-construct": "error",
1110
1132
  "cdk/no-construct-stack-suffix": "error",
1111
- "cdk/no-parent-name-construct-id-match": "error",
1112
- "cdk/no-public-class-fields": "error",
1113
- "cdk/pascal-case-construct-id": "error",
1114
- "cdk/require-passing-this": ["error", { allowNonThisAndDisallowScope: true }],
1133
+ "cdk/no-mutable-property-of-props-interface": "warn",
1134
+ "cdk/no-mutable-public-property-of-construct": "warn",
1135
+ "cdk/no-parent-name-construct-id-match": [
1136
+ "error",
1137
+ { disallowContainingParentName: false }
1138
+ ],
1115
1139
  "cdk/no-variable-construct-id": "error",
1116
- "cdk/no-mutable-public-fields": "warn",
1117
- "cdk/no-mutable-props-interface": "warn",
1118
- "cdk/construct-constructor-property": "error"
1140
+ "cdk/pascal-case-construct-id": "error",
1141
+ "cdk/require-passing-this": ["error", { allowNonThisAndDisallowScope: true }]
1119
1142
  });
1120
1143
  const strict = createFlatConfig({
1121
- "cdk/no-class-in-interface": "error",
1144
+ "cdk/construct-constructor-property": "error",
1145
+ "cdk/no-construct-in-interface": "error",
1146
+ "cdk/no-construct-in-public-property-of-construct": "error",
1122
1147
  "cdk/no-construct-stack-suffix": "error",
1123
- "cdk/no-parent-name-construct-id-match": "error",
1124
- "cdk/no-public-class-fields": "error",
1125
- "cdk/pascal-case-construct-id": "error",
1126
- "cdk/require-passing-this": "error",
1148
+ "cdk/no-import-private": "error",
1149
+ "cdk/no-mutable-property-of-props-interface": "error",
1150
+ "cdk/no-mutable-public-property-of-construct": "error",
1151
+ "cdk/no-parent-name-construct-id-match": [
1152
+ "error",
1153
+ { disallowContainingParentName: true }
1154
+ ],
1127
1155
  "cdk/no-variable-construct-id": "error",
1128
- "cdk/no-mutable-public-fields": "error",
1129
- "cdk/no-mutable-props-interface": "error",
1130
- "cdk/construct-constructor-property": "error",
1131
- "cdk/require-jsdoc": "error",
1132
- "cdk/require-props-default-doc": "error",
1156
+ "cdk/pascal-case-construct-id": "error",
1133
1157
  "cdk/props-name-convention": "error",
1134
- "cdk/no-import-private": "error"
1158
+ "cdk/require-jsdoc": "error",
1159
+ "cdk/require-passing-this": "error",
1160
+ "cdk/require-props-default-doc": "error"
1135
1161
  });
1136
1162
  const configs = {
1137
1163
  recommended,