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/README.md +1 -1
- package/dist/index.cjs +223 -197
- package/dist/index.d.ts +45 -39
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +223 -197
- package/package.json +12 -11
- package/src/index.ts +37 -30
- package/src/rules/construct-constructor-property.ts +4 -4
- package/src/rules/{no-class-in-interface.ts → no-construct-in-interface.ts} +8 -14
- package/src/rules/no-construct-in-public-property-of-construct.ts +145 -0
- package/src/rules/no-construct-stack-suffix.ts +6 -6
- package/src/rules/no-import-private.ts +2 -2
- package/src/rules/no-mutable-property-of-props-interface.ts +60 -0
- package/src/rules/no-mutable-public-property-of-construct.ts +76 -0
- package/src/rules/no-parent-name-construct-id-match.ts +69 -30
- package/src/rules/no-variable-construct-id.ts +4 -4
- package/src/rules/pascal-case-construct-id.ts +4 -4
- package/src/rules/require-passing-this.ts +6 -6
- package/src/rules/no-mutable-props-interface.ts +0 -58
- package/src/rules/no-mutable-public-fields.ts +0 -75
- package/src/rules/no-public-class-fields.ts +0 -154
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
|
|
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:
|
|
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:
|
|
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:
|
|
87
|
+
node: thirdParam,
|
|
88
88
|
messageId: "invalidConstructorProperty"
|
|
89
89
|
});
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
-
const
|
|
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
|
|
98
|
+
description: "Disallow CDK Construct types in interface properties"
|
|
107
99
|
},
|
|
108
100
|
messages: {
|
|
109
|
-
|
|
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
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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
|
|
357
|
+
const noMutablePropertyOfPropsInterface = ESLintUtils.RuleCreator.withoutDocs({
|
|
286
358
|
meta: {
|
|
287
359
|
type: "problem",
|
|
288
360
|
docs: {
|
|
289
|
-
description: "Disallow mutable properties
|
|
361
|
+
description: "Disallow mutable properties of Construct Props (interface)"
|
|
290
362
|
},
|
|
291
363
|
fixable: "code",
|
|
292
364
|
messages: {
|
|
293
|
-
|
|
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: "
|
|
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
|
|
397
|
+
const noMutablePublicPropertyOfConstruct = ESLintUtils.RuleCreator.withoutDocs({
|
|
326
398
|
meta: {
|
|
327
399
|
type: "problem",
|
|
328
400
|
docs: {
|
|
329
|
-
description: "Disallow mutable public
|
|
401
|
+
description: "Disallow mutable public properties of Construct"
|
|
330
402
|
},
|
|
331
403
|
fixable: "code",
|
|
332
404
|
messages: {
|
|
333
|
-
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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:
|
|
709
|
-
messageId: "
|
|
728
|
+
node: secondArg,
|
|
729
|
+
messageId: "invalidConstructId",
|
|
710
730
|
data: {
|
|
711
|
-
|
|
712
|
-
|
|
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
|
-
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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: "
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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/
|
|
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-
|
|
1089
|
-
"cdk/no-public-
|
|
1090
|
-
"cdk/
|
|
1091
|
-
|
|
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/
|
|
1094
|
-
"cdk/
|
|
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/
|
|
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-
|
|
1101
|
-
"cdk/no-
|
|
1102
|
-
"cdk/
|
|
1103
|
-
"cdk/
|
|
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/
|
|
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/
|
|
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,
|