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.mjs CHANGED
@@ -3,7 +3,7 @@ import { ESLintUtils, AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint
3
3
  import * as path from 'path';
4
4
 
5
5
  var name = "eslint-cdk-plugin";
6
- var version = "2.0.1";
6
+ var version = "2.2.0";
7
7
 
8
8
  const isConstructOrStackType = (type, ignoredClasses = ["App", "Stage"]) => {
9
9
  if (ignoredClasses.includes(type.symbol?.name ?? "")) return false;
@@ -59,7 +59,7 @@ const validateConstructorProperty = (constructor, context) => {
59
59
  const params = constructor.value.params;
60
60
  if (params.length < 2) {
61
61
  context.report({
62
- node: constructor,
62
+ node: constructor.value,
63
63
  messageId: "invalidConstructorProperty"
64
64
  });
65
65
  return;
@@ -67,7 +67,7 @@ const validateConstructorProperty = (constructor, context) => {
67
67
  const firstParam = params[0];
68
68
  if (firstParam.type === AST_NODE_TYPES.Identifier && firstParam.name !== "scope") {
69
69
  context.report({
70
- node: constructor,
70
+ node: firstParam,
71
71
  messageId: "invalidConstructorProperty"
72
72
  });
73
73
  return;
@@ -75,7 +75,7 @@ const validateConstructorProperty = (constructor, context) => {
75
75
  const secondParam = params[1];
76
76
  if (secondParam.type === AST_NODE_TYPES.Identifier && secondParam.name !== "id") {
77
77
  context.report({
78
- node: constructor,
78
+ node: secondParam,
79
79
  messageId: "invalidConstructorProperty"
80
80
  });
81
81
  return;
@@ -84,29 +84,21 @@ const validateConstructorProperty = (constructor, context) => {
84
84
  const thirdParam = params[2];
85
85
  if (thirdParam.type === AST_NODE_TYPES.Identifier && thirdParam.name !== "props") {
86
86
  context.report({
87
- node: constructor,
87
+ node: thirdParam,
88
88
  messageId: "invalidConstructorProperty"
89
89
  });
90
90
  return;
91
91
  }
92
92
  };
93
93
 
94
- const SYMBOL_FLAGS = {
95
- CLASS: 32
96
- };
97
- const SYNTAX_KIND = {
98
- CLASS_DECLARATION: 263,
99
- CONSTRUCTOR: 176
100
- };
101
-
102
- const noClassInInterface = ESLintUtils.RuleCreator.withoutDocs({
94
+ const noConstructInInterface = ESLintUtils.RuleCreator.withoutDocs({
103
95
  meta: {
104
96
  type: "problem",
105
97
  docs: {
106
- description: "Disallow class types in interface properties"
98
+ description: "Disallow CDK Construct types in interface properties"
107
99
  },
108
100
  messages: {
109
- noClassInInterfaceProps: "Interface property '{{ propertyName }}' should not use class type '{{ typeName }}'. Consider using an interface or type alias instead."
101
+ invalidInterfaceProperty: "Interface property '{{ propertyName }}' should not use CDK Construct type '{{ typeName }}'. Consider using an interface or type alias instead."
110
102
  },
111
103
  schema: []
112
104
  },
@@ -120,12 +112,10 @@ const noClassInInterface = ESLintUtils.RuleCreator.withoutDocs({
120
112
  continue;
121
113
  }
122
114
  const type = parserServices.getTypeAtLocation(property);
123
- if (!type.symbol) continue;
124
- const isClass = type.symbol.flags === SYMBOL_FLAGS.CLASS;
125
- if (!isClass) continue;
115
+ if (!isConstructOrStackType(type)) continue;
126
116
  context.report({
127
117
  node: property,
128
- messageId: "noClassInInterfaceProps",
118
+ messageId: "invalidInterfaceProperty",
129
119
  data: {
130
120
  propertyName: property.key.name,
131
121
  typeName: type.symbol.name
@@ -137,6 +127,83 @@ const noClassInInterface = ESLintUtils.RuleCreator.withoutDocs({
137
127
  }
138
128
  });
139
129
 
130
+ const noConstructInPublicPropertyOfConstruct = ESLintUtils.RuleCreator.withoutDocs({
131
+ meta: {
132
+ type: "problem",
133
+ docs: {
134
+ description: "Disallow Construct types in public property of Construct"
135
+ },
136
+ messages: {
137
+ invalidPublicPropertyOfConstruct: "Public property '{{ propertyName }}' of Construct should not use Construct type '{{ typeName }}'. Consider using an interface or type alias instead."
138
+ },
139
+ schema: []
140
+ },
141
+ defaultOptions: [],
142
+ create(context) {
143
+ const parserServices = ESLintUtils.getParserServices(context);
144
+ return {
145
+ ClassDeclaration(node) {
146
+ const type = parserServices.getTypeAtLocation(node);
147
+ if (!isConstructOrStackType(type)) return;
148
+ validatePublicPropertyOfConstruct(node, context, parserServices);
149
+ const constructor = node.body.body.find(
150
+ (member) => member.type === AST_NODE_TYPES.MethodDefinition && member.kind === "constructor"
151
+ );
152
+ if (!constructor || constructor.value.type !== AST_NODE_TYPES.FunctionExpression) {
153
+ return;
154
+ }
155
+ validateConstructorParameterProperty(
156
+ constructor,
157
+ context,
158
+ parserServices
159
+ );
160
+ }
161
+ };
162
+ }
163
+ });
164
+ const validatePublicPropertyOfConstruct = (node, context, parserServices) => {
165
+ for (const property of node.body.body) {
166
+ if (property.type !== AST_NODE_TYPES.PropertyDefinition || property.key.type !== AST_NODE_TYPES.Identifier) {
167
+ continue;
168
+ }
169
+ if (["private", "protected"].includes(property.accessibility ?? "")) {
170
+ continue;
171
+ }
172
+ if (!property.typeAnnotation) continue;
173
+ const type = parserServices.getTypeAtLocation(property);
174
+ if (!isConstructOrStackType(type)) continue;
175
+ context.report({
176
+ node: property,
177
+ messageId: "invalidPublicPropertyOfConstruct",
178
+ data: {
179
+ propertyName: property.key.name,
180
+ typeName: type.symbol.name
181
+ }
182
+ });
183
+ }
184
+ };
185
+ const validateConstructorParameterProperty = (constructor, context, parserServices) => {
186
+ for (const param of constructor.value.params) {
187
+ if (param.type !== AST_NODE_TYPES.TSParameterProperty || param.parameter.type !== AST_NODE_TYPES.Identifier) {
188
+ continue;
189
+ }
190
+ if (["private", "protected"].includes(param.accessibility ?? "")) {
191
+ continue;
192
+ }
193
+ if (!param.parameter.typeAnnotation) continue;
194
+ const type = parserServices.getTypeAtLocation(param);
195
+ if (!isConstructOrStackType(type)) continue;
196
+ context.report({
197
+ node: param,
198
+ messageId: "invalidPublicPropertyOfConstruct",
199
+ data: {
200
+ propertyName: param.parameter.name,
201
+ typeName: type.symbol.name
202
+ }
203
+ });
204
+ }
205
+ };
206
+
140
207
  const toPascalCase = (str) => {
141
208
  return str.split(/[-_\s]/).map((word) => {
142
209
  return word.replace(/([A-Z])/g, " $1").split(/\s+/).map(
@@ -145,6 +212,11 @@ const toPascalCase = (str) => {
145
212
  }).join("");
146
213
  };
147
214
 
215
+ const SYNTAX_KIND = {
216
+ CLASS_DECLARATION: 263,
217
+ CONSTRUCTOR: 176
218
+ };
219
+
148
220
  const getConstructorPropertyNames = (type) => {
149
221
  const declarations = type.symbol?.declarations;
150
222
  if (!declarations?.length) return [];
@@ -174,7 +246,7 @@ const noConstructStackSuffix = ESLintUtils.RuleCreator.withoutDocs({
174
246
  description: "Effort to avoid using 'Construct' and 'Stack' suffix in construct id."
175
247
  },
176
248
  messages: {
177
- noConstructStackSuffix: "{{ classType }} ID '{{ id }}' should not include {{ suffix }} suffix."
249
+ invalidConstructId: "{{ classType }} ID '{{ id }}' should not include {{ suffix }} suffix."
178
250
  },
179
251
  schema: [
180
252
  {
@@ -225,8 +297,8 @@ const validateConstructId$3 = (node, context, options) => {
225
297
  const disallowedSuffixes = options.disallowedSuffixes;
226
298
  if (disallowedSuffixes.includes(SUFFIX_TYPE.CONSTRUCT) && formattedConstructId.endsWith(SUFFIX_TYPE.CONSTRUCT)) {
227
299
  context.report({
228
- node,
229
- messageId: "noConstructStackSuffix",
300
+ node: secondArg,
301
+ messageId: "invalidConstructId",
230
302
  data: {
231
303
  classType: "Construct",
232
304
  id: secondArg.value,
@@ -235,8 +307,8 @@ const validateConstructId$3 = (node, context, options) => {
235
307
  });
236
308
  } else if (disallowedSuffixes.includes(SUFFIX_TYPE.STACK) && formattedConstructId.endsWith(SUFFIX_TYPE.STACK)) {
237
309
  context.report({
238
- node,
239
- messageId: "noConstructStackSuffix",
310
+ node: secondArg,
311
+ messageId: "invalidConstructId",
240
312
  data: {
241
313
  classType: "Stack",
242
314
  id: secondArg.value,
@@ -253,7 +325,7 @@ const noImportPrivate = {
253
325
  description: "Cannot import modules from private dir at different levels of the hierarchy."
254
326
  },
255
327
  messages: {
256
- noImportPrivate: "Cannot import modules from private dir at different levels of the hierarchy."
328
+ invalidImportPath: "Cannot import modules from private dir at different levels of the hierarchy."
257
329
  },
258
330
  schema: []
259
331
  },
@@ -272,7 +344,7 @@ const noImportPrivate = {
272
344
  if (currentDirSegments.length !== importDirSegments.length || currentDirSegments.some(
273
345
  (segment, index) => segment !== importDirSegments[index]
274
346
  )) {
275
- context.report({ node, messageId: "noImportPrivate" });
347
+ context.report({ node, messageId: "invalidImportPath" });
276
348
  }
277
349
  }
278
350
  };
@@ -282,15 +354,15 @@ const getDirSegments = (dirPath) => {
282
354
  return dirPath.split(path.sep).filter((segment) => segment !== "");
283
355
  };
284
356
 
285
- const noMutablePropsInterface = ESLintUtils.RuleCreator.withoutDocs({
357
+ const noMutablePropertyOfPropsInterface = ESLintUtils.RuleCreator.withoutDocs({
286
358
  meta: {
287
359
  type: "problem",
288
360
  docs: {
289
- description: "Disallow mutable properties in Props interfaces"
361
+ description: "Disallow mutable properties of Construct Props (interface)"
290
362
  },
291
363
  fixable: "code",
292
364
  messages: {
293
- noMutablePropsInterface: "Property '{{ propertyName }}' in Props interface should be readonly."
365
+ invalidPropertyOfPropsInterface: "Property '{{ propertyName }}' of Construct Props should be readonly."
294
366
  },
295
367
  schema: []
296
368
  },
@@ -307,7 +379,7 @@ const noMutablePropsInterface = ESLintUtils.RuleCreator.withoutDocs({
307
379
  if (property.readonly) continue;
308
380
  context.report({
309
381
  node: property,
310
- messageId: "noMutablePropsInterface",
382
+ messageId: "invalidPropertyOfPropsInterface",
311
383
  data: {
312
384
  propertyName: property.key.name
313
385
  },
@@ -322,15 +394,15 @@ const noMutablePropsInterface = ESLintUtils.RuleCreator.withoutDocs({
322
394
  }
323
395
  });
324
396
 
325
- const noMutablePublicFields = ESLintUtils.RuleCreator.withoutDocs({
397
+ const noMutablePublicPropertyOfConstruct = ESLintUtils.RuleCreator.withoutDocs({
326
398
  meta: {
327
399
  type: "problem",
328
400
  docs: {
329
- description: "Disallow mutable public class fields"
401
+ description: "Disallow mutable public properties of Construct"
330
402
  },
331
403
  fixable: "code",
332
404
  messages: {
333
- noMutablePublicFields: "Public field '{{ propertyName }}' should be readonly. Consider adding the 'readonly' modifier."
405
+ invalidPublicPropertyOfConstruct: "Public property '{{ propertyName }}' should be readonly. Consider adding the 'readonly' modifier."
334
406
  },
335
407
  schema: []
336
408
  },
@@ -352,7 +424,7 @@ const noMutablePublicFields = ESLintUtils.RuleCreator.withoutDocs({
352
424
  if (member.readonly) continue;
353
425
  context.report({
354
426
  node: member,
355
- messageId: "noMutablePublicFields",
427
+ messageId: "invalidPublicPropertyOfConstruct",
356
428
  data: {
357
429
  propertyName: member.key.name
358
430
  },
@@ -381,12 +453,30 @@ const noParentNameConstructIdMatch = ESLintUtils.RuleCreator.withoutDocs(
381
453
  description: "Enforce that construct IDs does not match the parent construct name."
382
454
  },
383
455
  messages: {
384
- noParentNameConstructIdMatch: "Construct ID '{{ constructId }}' should not match parent construct name '{{ parentConstructName }}'. Use a more specific identifier."
456
+ invalidConstructId: "Construct ID '{{ constructId }}' should not match parent construct name '{{ parentConstructName }}'. Use a more specific identifier."
385
457
  },
386
- schema: []
458
+ schema: [
459
+ {
460
+ type: "object",
461
+ properties: {
462
+ disallowContainingParentName: {
463
+ type: "boolean",
464
+ default: false
465
+ }
466
+ },
467
+ additionalProperties: false
468
+ }
469
+ ]
387
470
  },
388
- defaultOptions: [],
471
+ defaultOptions: [
472
+ {
473
+ disallowContainingParentName: false
474
+ }
475
+ ],
389
476
  create(context) {
477
+ const option = context.options[0] || {
478
+ disallowContainingParentName: false
479
+ };
390
480
  const parserServices = ESLintUtils.getParserServices(context);
391
481
  return {
392
482
  ClassBody(node) {
@@ -401,11 +491,11 @@ const noParentNameConstructIdMatch = ESLintUtils.RuleCreator.withoutDocs(
401
491
  continue;
402
492
  }
403
493
  validateConstructorBody({
404
- node,
405
494
  expression: body.value,
406
495
  parentClassName,
407
496
  context,
408
- parserServices
497
+ parserServices,
498
+ option
409
499
  });
410
500
  }
411
501
  }
@@ -414,11 +504,11 @@ const noParentNameConstructIdMatch = ESLintUtils.RuleCreator.withoutDocs(
414
504
  }
415
505
  );
416
506
  const validateConstructorBody = ({
417
- node,
418
507
  expression,
419
508
  parentClassName,
420
509
  context,
421
- parserServices
510
+ parserServices,
511
+ option
422
512
  }) => {
423
513
  for (const statement of expression.body.body) {
424
514
  switch (statement.type) {
@@ -426,32 +516,32 @@ const validateConstructorBody = ({
426
516
  const newExpression = statement.declarations[0].init;
427
517
  if (newExpression?.type !== AST_NODE_TYPES.NewExpression) continue;
428
518
  validateConstructId$2({
429
- node,
430
519
  context,
431
520
  expression: newExpression,
432
521
  parentClassName,
433
- parserServices
522
+ parserServices,
523
+ option
434
524
  });
435
525
  break;
436
526
  }
437
527
  case AST_NODE_TYPES.ExpressionStatement: {
438
528
  if (statement.expression?.type !== AST_NODE_TYPES.NewExpression) break;
439
529
  validateStatement({
440
- node,
441
530
  statement,
442
531
  parentClassName,
443
532
  context,
444
- parserServices
533
+ parserServices,
534
+ option
445
535
  });
446
536
  break;
447
537
  }
448
538
  case AST_NODE_TYPES.IfStatement: {
449
539
  traverseStatements({
450
- node,
451
540
  context,
452
541
  parentClassName,
453
542
  statement: statement.consequent,
454
- parserServices
543
+ parserServices,
544
+ option
455
545
  });
456
546
  break;
457
547
  }
@@ -459,11 +549,11 @@ const validateConstructorBody = ({
459
549
  for (const switchCase of statement.cases) {
460
550
  for (const statement2 of switchCase.consequent) {
461
551
  traverseStatements({
462
- node,
463
552
  context,
464
553
  parentClassName,
465
554
  statement: statement2,
466
- parserServices
555
+ parserServices,
556
+ option
467
557
  });
468
558
  }
469
559
  }
@@ -473,21 +563,21 @@ const validateConstructorBody = ({
473
563
  }
474
564
  };
475
565
  const traverseStatements = ({
476
- node,
477
566
  statement,
478
567
  parentClassName,
479
568
  context,
480
- parserServices
569
+ parserServices,
570
+ option
481
571
  }) => {
482
572
  switch (statement.type) {
483
573
  case AST_NODE_TYPES.BlockStatement: {
484
574
  for (const body of statement.body) {
485
575
  validateStatement({
486
- node,
487
576
  statement: body,
488
577
  parentClassName,
489
578
  context,
490
- parserServices
579
+ parserServices,
580
+ option
491
581
  });
492
582
  }
493
583
  break;
@@ -496,11 +586,11 @@ const traverseStatements = ({
496
586
  const newExpression = statement.expression;
497
587
  if (newExpression?.type !== AST_NODE_TYPES.NewExpression) break;
498
588
  validateStatement({
499
- node,
500
589
  statement,
501
590
  parentClassName,
502
591
  context,
503
- parserServices
592
+ parserServices,
593
+ option
504
594
  });
505
595
  break;
506
596
  }
@@ -508,33 +598,33 @@ const traverseStatements = ({
508
598
  const newExpression = statement.declarations[0].init;
509
599
  if (newExpression?.type !== AST_NODE_TYPES.NewExpression) break;
510
600
  validateConstructId$2({
511
- node,
512
601
  context,
513
602
  expression: newExpression,
514
603
  parentClassName,
515
- parserServices
604
+ parserServices,
605
+ option
516
606
  });
517
607
  break;
518
608
  }
519
609
  }
520
610
  };
521
611
  const validateStatement = ({
522
- node,
523
612
  statement,
524
613
  parentClassName,
525
614
  context,
526
- parserServices
615
+ parserServices,
616
+ option
527
617
  }) => {
528
618
  switch (statement.type) {
529
619
  case AST_NODE_TYPES.VariableDeclaration: {
530
620
  const newExpression = statement.declarations[0].init;
531
621
  if (newExpression?.type !== AST_NODE_TYPES.NewExpression) break;
532
622
  validateConstructId$2({
533
- node,
534
623
  context,
535
624
  expression: newExpression,
536
625
  parentClassName,
537
- parserServices
626
+ parserServices,
627
+ option
538
628
  });
539
629
  break;
540
630
  }
@@ -542,76 +632,76 @@ const validateStatement = ({
542
632
  const newExpression = statement.expression;
543
633
  if (newExpression?.type !== AST_NODE_TYPES.NewExpression) break;
544
634
  validateConstructId$2({
545
- node,
546
635
  context,
547
636
  expression: newExpression,
548
637
  parentClassName,
549
- parserServices
638
+ parserServices,
639
+ option
550
640
  });
551
641
  break;
552
642
  }
553
643
  case AST_NODE_TYPES.IfStatement: {
554
644
  validateIfStatement({
555
- node,
556
645
  statement,
557
646
  parentClassName,
558
647
  context,
559
- parserServices
648
+ parserServices,
649
+ option
560
650
  });
561
651
  break;
562
652
  }
563
653
  case AST_NODE_TYPES.SwitchStatement: {
564
654
  validateSwitchStatement({
565
- node,
566
655
  statement,
567
656
  parentClassName,
568
657
  context,
569
- parserServices
658
+ parserServices,
659
+ option
570
660
  });
571
661
  break;
572
662
  }
573
663
  }
574
664
  };
575
665
  const validateIfStatement = ({
576
- node,
577
666
  statement,
578
667
  parentClassName,
579
668
  context,
580
- parserServices
669
+ parserServices,
670
+ option
581
671
  }) => {
582
672
  traverseStatements({
583
- node,
584
673
  context,
585
674
  parentClassName,
586
675
  statement: statement.consequent,
587
- parserServices
676
+ parserServices,
677
+ option
588
678
  });
589
679
  };
590
680
  const validateSwitchStatement = ({
591
- node,
592
681
  statement,
593
682
  parentClassName,
594
683
  context,
595
- parserServices
684
+ parserServices,
685
+ option
596
686
  }) => {
597
687
  for (const caseStatement of statement.cases) {
598
688
  for (const _consequent of caseStatement.consequent) {
599
689
  traverseStatements({
600
- node,
601
690
  context,
602
691
  parentClassName,
603
692
  statement: _consequent,
604
- parserServices
693
+ parserServices,
694
+ option
605
695
  });
606
696
  }
607
697
  }
608
698
  };
609
699
  const validateConstructId$2 = ({
610
- node,
611
700
  context,
612
701
  expression,
613
702
  parentClassName,
614
- parserServices
703
+ parserServices,
704
+ option
615
705
  }) => {
616
706
  const type = parserServices.getTypeAtLocation(expression);
617
707
  if (expression.arguments.length < 2) return;
@@ -622,94 +712,24 @@ const validateConstructId$2 = ({
622
712
  const formattedConstructId = toPascalCase(secondArg.value);
623
713
  const formattedParentClassName = toPascalCase(parentClassName);
624
714
  if (!isConstructType(type)) return;
625
- if (formattedConstructId.includes(formattedParentClassName)) {
715
+ if (option.disallowContainingParentName && formattedConstructId.includes(formattedParentClassName)) {
626
716
  context.report({
627
- node,
628
- messageId: "noParentNameConstructIdMatch",
717
+ node: secondArg,
718
+ messageId: "invalidConstructId",
629
719
  data: {
630
720
  constructId: secondArg.value,
631
721
  parentConstructName: parentClassName
632
722
  }
633
723
  });
724
+ return;
634
725
  }
635
- };
636
-
637
- const noPublicClassFields = ESLintUtils.RuleCreator.withoutDocs({
638
- meta: {
639
- type: "problem",
640
- docs: {
641
- description: "Disallow class types in public class fields"
642
- },
643
- messages: {
644
- noPublicClassFields: "Public field '{{ propertyName }}' should not use class type '{{ typeName }}'. Consider using an interface or type alias instead."
645
- },
646
- schema: []
647
- },
648
- defaultOptions: [],
649
- create(context) {
650
- const parserServices = ESLintUtils.getParserServices(context);
651
- return {
652
- ClassDeclaration(node) {
653
- const type = parserServices.getTypeAtLocation(node);
654
- if (!isConstructOrStackType(type)) return;
655
- validateClassMember(node, context, parserServices);
656
- const constructor = node.body.body.find(
657
- (member) => member.type === AST_NODE_TYPES.MethodDefinition && member.kind === "constructor"
658
- );
659
- if (!constructor || constructor.value.type !== AST_NODE_TYPES.FunctionExpression) {
660
- return;
661
- }
662
- validateConstructorParameterProperty(
663
- constructor,
664
- context,
665
- parserServices
666
- );
667
- }
668
- };
669
- }
670
- });
671
- const validateClassMember = (node, context, parserServices) => {
672
- for (const member of node.body.body) {
673
- if (member.type !== AST_NODE_TYPES.PropertyDefinition || member.key.type !== AST_NODE_TYPES.Identifier) {
674
- continue;
675
- }
676
- if (["private", "protected"].includes(member.accessibility ?? "")) {
677
- continue;
678
- }
679
- if (!member.typeAnnotation) continue;
680
- const type = parserServices.getTypeAtLocation(member);
681
- if (!type.symbol) continue;
682
- const isClass = type.symbol.flags === SYMBOL_FLAGS.CLASS;
683
- if (!isClass) continue;
684
- context.report({
685
- node: member,
686
- messageId: "noPublicClassFields",
687
- data: {
688
- propertyName: member.key.name,
689
- typeName: type.symbol.name
690
- }
691
- });
692
- }
693
- };
694
- const validateConstructorParameterProperty = (constructor, context, parserServices) => {
695
- for (const param of constructor.value.params) {
696
- if (param.type !== AST_NODE_TYPES.TSParameterProperty || param.parameter.type !== AST_NODE_TYPES.Identifier) {
697
- continue;
698
- }
699
- if (["private", "protected"].includes(param.accessibility ?? "")) {
700
- continue;
701
- }
702
- if (!param.parameter.typeAnnotation) continue;
703
- const type = parserServices.getTypeAtLocation(param);
704
- if (!type.symbol) continue;
705
- const isClass = type.symbol.flags === SYMBOL_FLAGS.CLASS;
706
- if (!isClass) continue;
726
+ if (formattedParentClassName === formattedConstructId) {
707
727
  context.report({
708
- node: param,
709
- messageId: "noPublicClassFields",
728
+ node: secondArg,
729
+ messageId: "invalidConstructId",
710
730
  data: {
711
- propertyName: param.parameter.name,
712
- typeName: type.symbol.name
731
+ constructId: secondArg.value,
732
+ parentConstructName: parentClassName
713
733
  }
714
734
  });
715
735
  }
@@ -722,7 +742,7 @@ const noVariableConstructId = ESLintUtils.RuleCreator.withoutDocs({
722
742
  description: `Enforce using literal strings for Construct ID.`
723
743
  },
724
744
  messages: {
725
- noVariableConstructId: "Shouldn't use a parameter as a Construct ID."
745
+ invalidConstructId: "Shouldn't use a parameter as a Construct ID."
726
746
  },
727
747
  schema: []
728
748
  },
@@ -750,8 +770,8 @@ const validateConstructId$1 = (node, context) => {
750
770
  return;
751
771
  }
752
772
  context.report({
753
- node,
754
- messageId: "noVariableConstructId"
773
+ node: secondArg,
774
+ messageId: "invalidConstructId"
755
775
  });
756
776
  };
757
777
  const isInsideLoop = (node) => {
@@ -787,7 +807,7 @@ const pascalCaseConstructId = ESLintUtils.RuleCreator.withoutDocs({
787
807
  description: "Enforce PascalCase for Construct ID."
788
808
  },
789
809
  messages: {
790
- pascalCaseConstructId: "Construct ID must be PascalCase."
810
+ invalidConstructId: "Construct ID must be PascalCase."
791
811
  },
792
812
  schema: [],
793
813
  fixable: "code"
@@ -820,8 +840,8 @@ const validateConstructId = (node, context) => {
820
840
  const quote = secondArg.raw?.startsWith('"') ? QUOTE_TYPE.DOUBLE : QUOTE_TYPE.SINGLE;
821
841
  if (isPascalCase(secondArg.value)) return;
822
842
  context.report({
823
- node,
824
- messageId: "pascalCaseConstructId",
843
+ node: secondArg,
844
+ messageId: "invalidConstructId",
825
845
  fix: (fixer) => {
826
846
  const pascalCaseValue = toPascalCase(secondArg.value);
827
847
  return fixer.replaceText(secondArg, `${quote}${pascalCaseValue}${quote}`);
@@ -948,7 +968,7 @@ const requirePassingThis = ESLintUtils.RuleCreator.withoutDocs({
948
968
  description: "Require passing `this` in a constructor."
949
969
  },
950
970
  messages: {
951
- requirePassingThis: "Require passing `this` in a constructor."
971
+ missingPassingThis: "Require passing `this` in a constructor."
952
972
  },
953
973
  schema: [
954
974
  {
@@ -984,8 +1004,8 @@ const requirePassingThis = ESLintUtils.RuleCreator.withoutDocs({
984
1004
  if (constructorPropertyNames[0] !== "scope") return;
985
1005
  if (!options.allowNonThisAndDisallowScope) {
986
1006
  context.report({
987
- node,
988
- messageId: "requirePassingThis",
1007
+ node: argument,
1008
+ messageId: "missingPassingThis",
989
1009
  fix: (fixer) => {
990
1010
  return fixer.replaceText(argument, "this");
991
1011
  }
@@ -994,8 +1014,8 @@ const requirePassingThis = ESLintUtils.RuleCreator.withoutDocs({
994
1014
  }
995
1015
  if (argument.type === AST_NODE_TYPES.Identifier && argument.name === "scope") {
996
1016
  context.report({
997
- node,
998
- messageId: "requirePassingThis",
1017
+ node: argument,
1018
+ messageId: "missingPassingThis",
999
1019
  fix: (fixer) => {
1000
1020
  return fixer.replaceText(argument, "this");
1001
1021
  }
@@ -1049,20 +1069,20 @@ const requirePropsDefaultDoc = ESLintUtils.RuleCreator.withoutDocs({
1049
1069
  });
1050
1070
 
1051
1071
  const rules = {
1052
- "no-class-in-interface": noClassInInterface,
1072
+ "construct-constructor-property": constructConstructorProperty,
1073
+ "no-construct-in-interface": noConstructInInterface,
1074
+ "no-construct-in-public-property-of-construct": noConstructInPublicPropertyOfConstruct,
1053
1075
  "no-construct-stack-suffix": noConstructStackSuffix,
1076
+ "no-import-private": noImportPrivate,
1077
+ "no-mutable-property-of-props-interface": noMutablePropertyOfPropsInterface,
1078
+ "no-mutable-public-property-of-construct": noMutablePublicPropertyOfConstruct,
1054
1079
  "no-parent-name-construct-id-match": noParentNameConstructIdMatch,
1055
- "no-public-class-fields": noPublicClassFields,
1056
- "pascal-case-construct-id": pascalCaseConstructId,
1057
- "require-passing-this": requirePassingThis,
1058
1080
  "no-variable-construct-id": noVariableConstructId,
1059
- "no-mutable-public-fields": noMutablePublicFields,
1060
- "no-mutable-props-interface": noMutablePropsInterface,
1061
- "construct-constructor-property": constructConstructorProperty,
1062
- "require-jsdoc": requireJSDoc,
1063
- "require-props-default-doc": requirePropsDefaultDoc,
1081
+ "pascal-case-construct-id": pascalCaseConstructId,
1064
1082
  "props-name-convention": propsNameConvention,
1065
- "no-import-private": noImportPrivate
1083
+ "require-jsdoc": requireJSDoc,
1084
+ "require-passing-this": requirePassingThis,
1085
+ "require-props-default-doc": requirePropsDefaultDoc
1066
1086
  };
1067
1087
  const cdkPlugin = {
1068
1088
  meta: { name, version },
@@ -1083,32 +1103,38 @@ const createFlatConfig = (rules2) => {
1083
1103
  };
1084
1104
  };
1085
1105
  const recommended = createFlatConfig({
1086
- "cdk/no-class-in-interface": "error",
1106
+ "cdk/construct-constructor-property": "error",
1107
+ "cdk/no-construct-in-interface": "error",
1108
+ "cdk/no-construct-in-public-property-of-construct": "error",
1087
1109
  "cdk/no-construct-stack-suffix": "error",
1088
- "cdk/no-parent-name-construct-id-match": "error",
1089
- "cdk/no-public-class-fields": "error",
1090
- "cdk/pascal-case-construct-id": "error",
1091
- "cdk/require-passing-this": ["error", { allowNonThisAndDisallowScope: true }],
1110
+ "cdk/no-mutable-property-of-props-interface": "warn",
1111
+ "cdk/no-mutable-public-property-of-construct": "warn",
1112
+ "cdk/no-parent-name-construct-id-match": [
1113
+ "error",
1114
+ { disallowContainingParentName: false }
1115
+ ],
1092
1116
  "cdk/no-variable-construct-id": "error",
1093
- "cdk/no-mutable-public-fields": "warn",
1094
- "cdk/no-mutable-props-interface": "warn",
1095
- "cdk/construct-constructor-property": "error"
1117
+ "cdk/pascal-case-construct-id": "error",
1118
+ "cdk/require-passing-this": ["error", { allowNonThisAndDisallowScope: true }]
1096
1119
  });
1097
1120
  const strict = createFlatConfig({
1098
- "cdk/no-class-in-interface": "error",
1121
+ "cdk/construct-constructor-property": "error",
1122
+ "cdk/no-construct-in-interface": "error",
1123
+ "cdk/no-construct-in-public-property-of-construct": "error",
1099
1124
  "cdk/no-construct-stack-suffix": "error",
1100
- "cdk/no-parent-name-construct-id-match": "error",
1101
- "cdk/no-public-class-fields": "error",
1102
- "cdk/pascal-case-construct-id": "error",
1103
- "cdk/require-passing-this": "error",
1125
+ "cdk/no-import-private": "error",
1126
+ "cdk/no-mutable-property-of-props-interface": "error",
1127
+ "cdk/no-mutable-public-property-of-construct": "error",
1128
+ "cdk/no-parent-name-construct-id-match": [
1129
+ "error",
1130
+ { disallowContainingParentName: true }
1131
+ ],
1104
1132
  "cdk/no-variable-construct-id": "error",
1105
- "cdk/no-mutable-public-fields": "error",
1106
- "cdk/no-mutable-props-interface": "error",
1107
- "cdk/construct-constructor-property": "error",
1108
- "cdk/require-jsdoc": "error",
1109
- "cdk/require-props-default-doc": "error",
1133
+ "cdk/pascal-case-construct-id": "error",
1110
1134
  "cdk/props-name-convention": "error",
1111
- "cdk/no-import-private": "error"
1135
+ "cdk/require-jsdoc": "error",
1136
+ "cdk/require-passing-this": "error",
1137
+ "cdk/require-props-default-doc": "error"
1112
1138
  });
1113
1139
  const configs = {
1114
1140
  recommended,