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.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
|
|
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:
|
|
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:
|
|
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:
|
|
110
|
+
node: thirdParam,
|
|
111
111
|
messageId: "invalidConstructorProperty"
|
|
112
112
|
});
|
|
113
113
|
return;
|
|
114
114
|
}
|
|
115
115
|
};
|
|
116
116
|
|
|
117
|
-
const
|
|
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
|
|
121
|
+
description: "Disallow CDK Construct types in interface properties"
|
|
130
122
|
},
|
|
131
123
|
messages: {
|
|
132
|
-
|
|
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
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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
|
|
380
|
+
const noMutablePropertyOfPropsInterface = utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
309
381
|
meta: {
|
|
310
382
|
type: "problem",
|
|
311
383
|
docs: {
|
|
312
|
-
description: "Disallow mutable properties
|
|
384
|
+
description: "Disallow mutable properties of Construct Props (interface)"
|
|
313
385
|
},
|
|
314
386
|
fixable: "code",
|
|
315
387
|
messages: {
|
|
316
|
-
|
|
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: "
|
|
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
|
|
420
|
+
const noMutablePublicPropertyOfConstruct = utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
349
421
|
meta: {
|
|
350
422
|
type: "problem",
|
|
351
423
|
docs: {
|
|
352
|
-
description: "Disallow mutable public
|
|
424
|
+
description: "Disallow mutable public properties of Construct"
|
|
353
425
|
},
|
|
354
426
|
fixable: "code",
|
|
355
427
|
messages: {
|
|
356
|
-
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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:
|
|
732
|
-
messageId: "
|
|
751
|
+
node: secondArg,
|
|
752
|
+
messageId: "invalidConstructId",
|
|
733
753
|
data: {
|
|
734
|
-
|
|
735
|
-
|
|
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
|
-
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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: "
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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/
|
|
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-
|
|
1112
|
-
"cdk/no-public-
|
|
1113
|
-
"cdk/
|
|
1114
|
-
|
|
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/
|
|
1117
|
-
"cdk/
|
|
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/
|
|
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-
|
|
1124
|
-
"cdk/no-
|
|
1125
|
-
"cdk/
|
|
1126
|
-
"cdk/
|
|
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/
|
|
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/
|
|
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,
|