eslint-cdk-plugin 3.4.2 → 3.4.4
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/bin/migration.mjs +15 -4
- package/dist/index.cjs +299 -138
- package/dist/index.mjs +293 -132
- package/package.json +13 -10
- package/src/rules/no-unused-props/index.ts +152 -0
- package/src/rules/no-unused-props/props-usage-analyzer.ts +297 -0
- package/src/{utils/propsUsageTracker.ts → rules/no-unused-props/props-usage-tracker.ts} +96 -104
- package/src/utils/get-child-nodes.ts +25 -0
- package/src/utils/getPropertyNames.ts +1 -5
- package/src/utils/is-resource-with-readonly-interface.ts +5 -5
- package/src/utils/typecheck/ts-node.ts +2 -37
- package/src/utils/typecheck/ts-type.ts +2 -8
- package/src/constants/tsInternalFlags.ts +0 -18
- package/src/rules/no-unused-props.ts +0 -176
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import tsParser from '@typescript-eslint/parser';
|
|
2
2
|
import { ESLintUtils, AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils';
|
|
3
|
+
import { SymbolFlags, SyntaxKind, isClassDeclaration, isIdentifier, isPropertyAccessExpression, isConstructorDeclaration } from 'typescript';
|
|
3
4
|
import * as path from 'path';
|
|
4
5
|
|
|
5
6
|
var name = "eslint-cdk-plugin";
|
|
6
|
-
var version = "3.4.
|
|
7
|
+
var version = "3.4.4";
|
|
7
8
|
|
|
8
9
|
const createRule = ESLintUtils.RuleCreator(
|
|
9
10
|
(name) => `https://eslint-cdk-plugin.dev/rules/${name}`
|
|
@@ -120,21 +121,11 @@ const validateConstructorProperty = (constructor, context, parserServices) => {
|
|
|
120
121
|
}
|
|
121
122
|
};
|
|
122
123
|
|
|
123
|
-
const SYMBOL_FLAGS = {
|
|
124
|
-
CLASS: 32};
|
|
125
|
-
const SYNTAX_KIND = {
|
|
126
|
-
CLASS_DECLARATION: 263,
|
|
127
|
-
CONSTRUCTOR: 176,
|
|
128
|
-
IMPLEMENTS_KEYWORD: 119,
|
|
129
|
-
IDENTIFIER: 80,
|
|
130
|
-
PROPERTY_ACCESS_EXPRESSION: 211
|
|
131
|
-
};
|
|
132
|
-
|
|
133
124
|
const getSymbol = (type) => {
|
|
134
125
|
return type.getSymbol?.() ?? type.symbol;
|
|
135
126
|
};
|
|
136
127
|
const isClassType = (type) => {
|
|
137
|
-
return getSymbol(type)?.flags ===
|
|
128
|
+
return getSymbol(type)?.flags === SymbolFlags.Class;
|
|
138
129
|
};
|
|
139
130
|
const isArrayType = (type) => {
|
|
140
131
|
const symbol = getSymbol(type);
|
|
@@ -170,20 +161,8 @@ const getGenericTypeArgument = (type) => {
|
|
|
170
161
|
return void 0;
|
|
171
162
|
};
|
|
172
163
|
|
|
173
|
-
const isClassDeclaration = (node) => {
|
|
174
|
-
return node.kind === SYNTAX_KIND.CLASS_DECLARATION;
|
|
175
|
-
};
|
|
176
|
-
const isIdentifier = (node) => {
|
|
177
|
-
return node.kind === SYNTAX_KIND.IDENTIFIER;
|
|
178
|
-
};
|
|
179
|
-
const isPropertyAccessExpression = (node) => {
|
|
180
|
-
return node.kind === SYNTAX_KIND.PROPERTY_ACCESS_EXPRESSION;
|
|
181
|
-
};
|
|
182
164
|
const checkHeritageClauseIsImplements = (node) => {
|
|
183
|
-
return node.token ===
|
|
184
|
-
};
|
|
185
|
-
const isConstructorDeclaration = (node) => {
|
|
186
|
-
return node.kind === SYNTAX_KIND.CONSTRUCTOR;
|
|
165
|
+
return node.token === SyntaxKind.ImplementsKeyword;
|
|
187
166
|
};
|
|
188
167
|
|
|
189
168
|
const isResourceWithReadonlyInterface = (type) => {
|
|
@@ -948,76 +927,272 @@ const validateConstructId$2 = ({
|
|
|
948
927
|
}
|
|
949
928
|
};
|
|
950
929
|
|
|
951
|
-
const
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
if (
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
930
|
+
const getChildNodes = (node) => {
|
|
931
|
+
return Object.entries(node).reduce((acc, [key, value]) => {
|
|
932
|
+
if (["parent", "range", "loc"].includes(key)) return acc;
|
|
933
|
+
if (isESTreeNode(value)) return [...acc, value];
|
|
934
|
+
if (Array.isArray(value)) return [...acc, ...value.filter(isESTreeNode)];
|
|
935
|
+
return acc;
|
|
936
|
+
}, []);
|
|
937
|
+
};
|
|
938
|
+
const isESTreeNode = (value) => {
|
|
939
|
+
return value !== null && typeof value === "object" && "type" in value && typeof value.type === "string";
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
class PropsUsageAnalyzer {
|
|
943
|
+
propsUsageTracker;
|
|
944
|
+
visitedNodes;
|
|
945
|
+
propsAliases;
|
|
946
|
+
constructor(propsUsageTracker) {
|
|
947
|
+
this.propsUsageTracker = propsUsageTracker;
|
|
948
|
+
this.visitedNodes = /* @__PURE__ */ new Set();
|
|
949
|
+
this.propsAliases = /* @__PURE__ */ new Set();
|
|
950
|
+
}
|
|
951
|
+
analyze(constructor, propsParam) {
|
|
952
|
+
this.initialize();
|
|
953
|
+
const constructorBody = constructor.value.body;
|
|
954
|
+
const classNode = constructor.parent;
|
|
955
|
+
const propsParamName = propsParam.name;
|
|
956
|
+
if (!constructorBody) return;
|
|
957
|
+
this.visitNodes(constructorBody, propsParamName);
|
|
958
|
+
this.analyzeClassBody(classNode, constructor, propsParamName);
|
|
959
|
+
this.analyzePrivateMethodsCalledFromConstructor(
|
|
960
|
+
constructorBody,
|
|
961
|
+
classNode,
|
|
962
|
+
propsParamName
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
initialize() {
|
|
966
|
+
this.visitedNodes.clear();
|
|
967
|
+
this.propsAliases.clear();
|
|
968
|
+
}
|
|
969
|
+
analyzeClassBody(classBody, constructor, propsParamName) {
|
|
970
|
+
if (!constructor.value.body) return;
|
|
971
|
+
const instanceVarName = this.findPropsInstanceVariable(
|
|
972
|
+
constructor.value.body,
|
|
973
|
+
propsParamName
|
|
974
|
+
);
|
|
975
|
+
if (!instanceVarName) return;
|
|
976
|
+
for (const member of classBody.body) {
|
|
977
|
+
if (member.type === AST_NODE_TYPES.MethodDefinition && member.value.body) {
|
|
978
|
+
this.visitNodes(member.value.body, instanceVarName);
|
|
979
|
+
}
|
|
959
980
|
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
981
|
+
}
|
|
982
|
+
analyzePrivateMethodsCalledFromConstructor(constructorBody, classBody, propsParamName) {
|
|
983
|
+
const methodCallsWithProps = this.findMethodCallsWithProps(
|
|
984
|
+
constructorBody,
|
|
985
|
+
propsParamName
|
|
986
|
+
);
|
|
987
|
+
for (const { methodName, propsArgIndices } of methodCallsWithProps) {
|
|
988
|
+
const methodDef = this.findMethodDefinition(classBody, methodName);
|
|
989
|
+
if (!methodDef?.value.body) continue;
|
|
990
|
+
for (const argIndex of propsArgIndices) {
|
|
991
|
+
const param = methodDef.value.params[argIndex];
|
|
992
|
+
if (param && param.type === AST_NODE_TYPES.Identifier) {
|
|
993
|
+
this.visitNodes(methodDef.value.body, param.name);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
visitNodes(node, propsParamName) {
|
|
999
|
+
if (this.visitedNodes.has(node)) return;
|
|
1000
|
+
this.visitedNodes.add(node);
|
|
1001
|
+
switch (node.type) {
|
|
1002
|
+
case AST_NODE_TYPES.MemberExpression:
|
|
1003
|
+
this.propsUsageTracker.markAsUsedForMemberExpression(
|
|
1004
|
+
node,
|
|
1005
|
+
propsParamName
|
|
1006
|
+
);
|
|
1007
|
+
if (node.object.type === AST_NODE_TYPES.Identifier && this.propsAliases.has(node.object.name) && node.property.type === AST_NODE_TYPES.Identifier) {
|
|
1008
|
+
this.propsUsageTracker.markAsUsed(node.property.name);
|
|
1009
|
+
}
|
|
1010
|
+
break;
|
|
1011
|
+
case AST_NODE_TYPES.VariableDeclarator:
|
|
1012
|
+
this.propsUsageTracker.markAsUsedForVariableDeclarator(
|
|
1013
|
+
node,
|
|
1014
|
+
propsParamName
|
|
1015
|
+
);
|
|
1016
|
+
break;
|
|
1017
|
+
case AST_NODE_TYPES.AssignmentExpression:
|
|
1018
|
+
this.propsUsageTracker.markAsUsedForAssignmentExpression(
|
|
1019
|
+
node,
|
|
1020
|
+
propsParamName
|
|
1021
|
+
);
|
|
1022
|
+
break;
|
|
1023
|
+
case AST_NODE_TYPES.Identifier:
|
|
1024
|
+
if (node.name === propsParamName) this.handlePropsIdentifier(node);
|
|
1025
|
+
break;
|
|
1026
|
+
}
|
|
1027
|
+
const children = getChildNodes(node);
|
|
1028
|
+
for (const child of children) {
|
|
1029
|
+
this.visitNodes(child, propsParamName);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Handles cases where props is used as a whole identifier
|
|
1034
|
+
*/
|
|
1035
|
+
handlePropsIdentifier(node) {
|
|
1036
|
+
const parent = node.parent;
|
|
1037
|
+
if (!parent) return;
|
|
1038
|
+
switch (parent.type) {
|
|
1039
|
+
case AST_NODE_TYPES.AssignmentExpression:
|
|
1040
|
+
case AST_NODE_TYPES.MemberExpression: {
|
|
1041
|
+
break;
|
|
1042
|
+
}
|
|
1043
|
+
case AST_NODE_TYPES.VariableDeclarator: {
|
|
1044
|
+
if (parent.init === node && parent.id.type === AST_NODE_TYPES.Identifier) {
|
|
1045
|
+
this.propsAliases.add(parent.id.name);
|
|
1046
|
+
}
|
|
1047
|
+
break;
|
|
1048
|
+
}
|
|
1049
|
+
case AST_NODE_TYPES.CallExpression: {
|
|
1050
|
+
if (!parent.arguments.includes(node)) break;
|
|
1051
|
+
if (parent.callee.type === AST_NODE_TYPES.MemberExpression && parent.callee.object.type === AST_NODE_TYPES.ThisExpression) {
|
|
1052
|
+
break;
|
|
1053
|
+
}
|
|
1054
|
+
this.propsUsageTracker.markAllAsUsed();
|
|
1055
|
+
break;
|
|
1056
|
+
}
|
|
1057
|
+
case AST_NODE_TYPES.ReturnStatement: {
|
|
1058
|
+
if (parent.argument === node) {
|
|
1059
|
+
this.propsUsageTracker.markAllAsUsed();
|
|
1060
|
+
}
|
|
1061
|
+
break;
|
|
1062
|
+
}
|
|
1063
|
+
case AST_NODE_TYPES.ArrayExpression: {
|
|
1064
|
+
if (parent.elements.includes(node)) {
|
|
1065
|
+
this.propsUsageTracker.markAllAsUsed();
|
|
1066
|
+
}
|
|
1067
|
+
break;
|
|
1068
|
+
}
|
|
1069
|
+
case AST_NODE_TYPES.Property: {
|
|
1070
|
+
if (parent.value === node) {
|
|
1071
|
+
this.propsUsageTracker.markAllAsUsed();
|
|
1072
|
+
}
|
|
1073
|
+
break;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
findPropsInstanceVariable(body, propsParamName) {
|
|
1078
|
+
for (const statement of body.body) {
|
|
1079
|
+
if (statement.type === AST_NODE_TYPES.ExpressionStatement && statement.expression.type === AST_NODE_TYPES.AssignmentExpression && statement.expression.left.type === AST_NODE_TYPES.MemberExpression && statement.expression.left.object.type === AST_NODE_TYPES.ThisExpression && statement.expression.left.property.type === AST_NODE_TYPES.Identifier && statement.expression.right.type === AST_NODE_TYPES.Identifier && statement.expression.right.name === propsParamName) {
|
|
1080
|
+
return statement.expression.left.property.name;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Finds method definition in class body
|
|
1087
|
+
*/
|
|
1088
|
+
findMethodDefinition(classBody, methodName) {
|
|
1089
|
+
for (const member of classBody.body) {
|
|
1090
|
+
if (member.type === AST_NODE_TYPES.MethodDefinition && member.key.type === AST_NODE_TYPES.Identifier && member.key.name === methodName) {
|
|
1091
|
+
return member;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
return null;
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Finds method calls in constructor body that receive props as argument
|
|
1098
|
+
*/
|
|
1099
|
+
findMethodCallsWithProps(body, propsParamName) {
|
|
1100
|
+
const result = [];
|
|
1101
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1102
|
+
const visitNode = (node) => {
|
|
1103
|
+
if (visited.has(node)) return;
|
|
1104
|
+
visited.add(node);
|
|
1105
|
+
if (node.type === AST_NODE_TYPES.CallExpression && node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.object.type === AST_NODE_TYPES.ThisExpression && node.callee.property.type === AST_NODE_TYPES.Identifier) {
|
|
1106
|
+
const methodName = node.callee.property.name;
|
|
1107
|
+
const propsArgIndices = node.arguments.reduce(
|
|
1108
|
+
(acc, arg, index) => arg.type === AST_NODE_TYPES.Identifier && arg.name === propsParamName ? (
|
|
1109
|
+
// NOTE: props is passed directly (e.g., this.method(props))
|
|
1110
|
+
[...acc, index]
|
|
1111
|
+
) : acc,
|
|
1112
|
+
[]
|
|
1113
|
+
);
|
|
1114
|
+
if (propsArgIndices.length) {
|
|
1115
|
+
result.push({ methodName, propsArgIndices });
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
const children = getChildNodes(node);
|
|
1119
|
+
for (const child of children) {
|
|
1120
|
+
visitNode(child);
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
visitNode(body);
|
|
1124
|
+
return result;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
class PropsUsageTracker {
|
|
1129
|
+
propUsageMap;
|
|
1130
|
+
constructor(propType) {
|
|
1131
|
+
this.propUsageMap = new Map(
|
|
1132
|
+
this.getPropsPropertyNames(propType).map((name) => [name, false])
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
getUnusedProperties() {
|
|
1136
|
+
return Array.from(this.propUsageMap.entries()).reduce(
|
|
978
1137
|
(acc, [name, used]) => !used ? [...acc, name] : acc,
|
|
979
1138
|
[]
|
|
980
1139
|
);
|
|
981
|
-
}
|
|
982
|
-
|
|
1140
|
+
}
|
|
1141
|
+
markAsUsed(propertyName) {
|
|
1142
|
+
if (this.propUsageMap.has(propertyName)) {
|
|
1143
|
+
this.propUsageMap.set(propertyName, true);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
markAllAsUsed() {
|
|
1147
|
+
for (const key of this.propUsageMap.keys()) {
|
|
1148
|
+
this.propUsageMap.set(key, true);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
markAsUsedForMemberExpression(node, propsParamName) {
|
|
983
1152
|
if (node.object.type === AST_NODE_TYPES.Identifier && node.object.name === propsParamName && node.property.type === AST_NODE_TYPES.Identifier) {
|
|
984
|
-
markAsUsed(node.property.name);
|
|
1153
|
+
this.markAsUsed(node.property.name);
|
|
985
1154
|
return;
|
|
986
1155
|
}
|
|
987
|
-
if (node.object.type === AST_NODE_TYPES.MemberExpression && node.object.object.type === AST_NODE_TYPES.ThisExpression && node.object.property.type === AST_NODE_TYPES.Identifier && node.object.property.name ===
|
|
988
|
-
markAsUsed(node.property.name);
|
|
1156
|
+
if (node.object.type === AST_NODE_TYPES.MemberExpression && node.object.object.type === AST_NODE_TYPES.ThisExpression && node.object.property.type === AST_NODE_TYPES.Identifier && node.object.property.name === propsParamName && node.property.type === AST_NODE_TYPES.Identifier) {
|
|
1157
|
+
this.markAsUsed(node.property.name);
|
|
989
1158
|
return;
|
|
990
1159
|
}
|
|
991
|
-
}
|
|
992
|
-
|
|
1160
|
+
}
|
|
1161
|
+
markAsUsedForVariableDeclarator(node, propsParamName) {
|
|
993
1162
|
if (node.id.type !== AST_NODE_TYPES.ObjectPattern || node.init?.type !== AST_NODE_TYPES.Identifier || node.init.name !== propsParamName) {
|
|
994
1163
|
return;
|
|
995
1164
|
}
|
|
996
1165
|
const propertyNames = getPropertyNames(node.id.properties);
|
|
997
1166
|
for (const name of propertyNames) {
|
|
998
|
-
markAsUsed(name);
|
|
1167
|
+
this.markAsUsed(name);
|
|
999
1168
|
}
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1169
|
+
}
|
|
1170
|
+
markAsUsedForAssignmentExpression(node, propsParamName) {
|
|
1002
1171
|
if (node.right.type !== AST_NODE_TYPES.MemberExpression || node.right.object.type !== AST_NODE_TYPES.Identifier || node.right.object.name !== propsParamName || node.right.property.type !== AST_NODE_TYPES.Identifier) {
|
|
1003
1172
|
return;
|
|
1004
1173
|
}
|
|
1005
|
-
markAsUsed(node.right.property.name);
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1174
|
+
this.markAsUsed(node.right.property.name);
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Gets the property names from the props type
|
|
1178
|
+
*/
|
|
1179
|
+
getPropsPropertyNames(propsType) {
|
|
1180
|
+
const isInternalProperty = (propertyName) => propertyName.startsWith("_") || propertyName === "constructor" || propertyName === "prototype";
|
|
1181
|
+
const typeProperties = propsType.getProperties();
|
|
1182
|
+
if (typeProperties.length) {
|
|
1183
|
+
return typeProperties.reduce(
|
|
1184
|
+
(acc, prop) => !isInternalProperty(prop.name) ? [...acc, prop.name] : acc,
|
|
1185
|
+
[]
|
|
1186
|
+
);
|
|
1010
1187
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
};
|
|
1020
|
-
};
|
|
1188
|
+
const symbol = propsType.getSymbol();
|
|
1189
|
+
if (!symbol?.members) return [];
|
|
1190
|
+
return Array.from(symbol.members.keys()).reduce((acc, key) => {
|
|
1191
|
+
const name = String(key);
|
|
1192
|
+
return !isInternalProperty(name) ? [...acc, name] : acc;
|
|
1193
|
+
}, []);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1021
1196
|
|
|
1022
1197
|
const noUnusedProps = createRule({
|
|
1023
1198
|
name: "no-unused-props",
|
|
@@ -1036,64 +1211,61 @@ const noUnusedProps = createRule({
|
|
|
1036
1211
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
1037
1212
|
return {
|
|
1038
1213
|
ClassDeclaration(node) {
|
|
1214
|
+
if (node.abstract) return;
|
|
1039
1215
|
const type = parserServices.getTypeAtLocation(node);
|
|
1040
1216
|
if (!isConstructType(type)) return;
|
|
1041
1217
|
const constructor = getConstructor(node);
|
|
1042
1218
|
if (!constructor) return;
|
|
1043
|
-
|
|
1219
|
+
const propsParam = getPropsParam(constructor, parserServices);
|
|
1220
|
+
if (!propsParam) return;
|
|
1221
|
+
const { node: propsNode, type: propsType } = propsParam;
|
|
1222
|
+
if (isPropsUsedInSuperCall(constructor, propsNode.name)) return;
|
|
1223
|
+
const tracker = new PropsUsageTracker(propsType);
|
|
1224
|
+
const analyzer = new PropsUsageAnalyzer(tracker);
|
|
1225
|
+
analyzer.analyze(constructor, propsNode);
|
|
1226
|
+
reportUnusedProperties(tracker, propsNode, context);
|
|
1044
1227
|
}
|
|
1045
1228
|
};
|
|
1046
1229
|
}
|
|
1047
1230
|
});
|
|
1048
|
-
const
|
|
1231
|
+
const getPropsParam = (constructor, parserServices) => {
|
|
1049
1232
|
const params = constructor.value.params;
|
|
1050
|
-
if (params.length < 3) return;
|
|
1233
|
+
if (params.length < 3) return null;
|
|
1051
1234
|
const propsParam = params[2];
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
analyzeConstructorBody(constructor.value.body, propsParam.name, tracker);
|
|
1058
|
-
reportUnusedProperties(tracker, propsParam, context);
|
|
1059
|
-
return;
|
|
1060
|
-
}
|
|
1061
|
-
case AST_NODE_TYPES.ObjectPattern: {
|
|
1062
|
-
const typeAnnotation = propsParam.typeAnnotation?.typeAnnotation;
|
|
1063
|
-
if (!typeAnnotation) return;
|
|
1064
|
-
const propsType = parserServices.getTypeAtLocation(typeAnnotation);
|
|
1065
|
-
const tracker = propsUsageTrackerFactory(propsType);
|
|
1066
|
-
if (!tracker) return;
|
|
1067
|
-
tracker.markAsUsedForObjectPattern(propsParam);
|
|
1068
|
-
reportUnusedProperties(tracker, propsParam, context);
|
|
1069
|
-
return;
|
|
1070
|
-
}
|
|
1071
|
-
default:
|
|
1072
|
-
return;
|
|
1073
|
-
}
|
|
1235
|
+
if (propsParam.type !== AST_NODE_TYPES.Identifier) return null;
|
|
1236
|
+
return {
|
|
1237
|
+
node: propsParam,
|
|
1238
|
+
type: parserServices.getTypeAtLocation(propsParam)
|
|
1239
|
+
};
|
|
1074
1240
|
};
|
|
1075
|
-
const
|
|
1076
|
-
|
|
1077
|
-
const
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
tracker.markAsUsedForMemberExpression(node, propsParamName);
|
|
1083
|
-
break;
|
|
1084
|
-
case AST_NODE_TYPES.VariableDeclarator:
|
|
1085
|
-
tracker.markAsUsedForVariableDeclarator(node, propsParamName);
|
|
1086
|
-
break;
|
|
1087
|
-
case AST_NODE_TYPES.AssignmentExpression:
|
|
1088
|
-
tracker.markAsUsedForAssignmentExpression(node, propsParamName);
|
|
1089
|
-
break;
|
|
1241
|
+
const isPropsUsedInSuperCall = (constructor, propsPropertyName) => {
|
|
1242
|
+
if (constructor.kind !== "constructor") return false;
|
|
1243
|
+
const body = constructor.value.body;
|
|
1244
|
+
if (!body) return false;
|
|
1245
|
+
for (const expr of body.body) {
|
|
1246
|
+
if (expr.type !== AST_NODE_TYPES.ExpressionStatement || expr.expression.type !== AST_NODE_TYPES.CallExpression || expr.expression.callee.type !== AST_NODE_TYPES.Super) {
|
|
1247
|
+
continue;
|
|
1090
1248
|
}
|
|
1091
|
-
const
|
|
1092
|
-
|
|
1093
|
-
|
|
1249
|
+
const visitNode = (node, propsName) => {
|
|
1250
|
+
const nodeValue = node.type === AST_NODE_TYPES.Property ? node.value : node;
|
|
1251
|
+
switch (nodeValue.type) {
|
|
1252
|
+
case AST_NODE_TYPES.Identifier: {
|
|
1253
|
+
return nodeValue.name === propsName;
|
|
1254
|
+
}
|
|
1255
|
+
case AST_NODE_TYPES.ObjectExpression: {
|
|
1256
|
+
for (const prop of nodeValue.properties) {
|
|
1257
|
+
if (visitNode(prop, propsName)) return true;
|
|
1258
|
+
}
|
|
1259
|
+
break;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
return false;
|
|
1263
|
+
};
|
|
1264
|
+
for (const arg of expr.expression.arguments) {
|
|
1265
|
+
if (visitNode(arg, propsPropertyName)) return true;
|
|
1094
1266
|
}
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1267
|
+
}
|
|
1268
|
+
return false;
|
|
1097
1269
|
};
|
|
1098
1270
|
const reportUnusedProperties = (tracker, propsParam, context) => {
|
|
1099
1271
|
for (const propName of tracker.getUnusedProperties()) {
|
|
@@ -1106,17 +1278,6 @@ const reportUnusedProperties = (tracker, propsParam, context) => {
|
|
|
1106
1278
|
});
|
|
1107
1279
|
}
|
|
1108
1280
|
};
|
|
1109
|
-
const getChildNodes = (node) => {
|
|
1110
|
-
return Object.entries(node).reduce((acc, [key, value]) => {
|
|
1111
|
-
if (["parent", "range", "loc"].includes(key)) return acc;
|
|
1112
|
-
if (isESTreeNode(value)) return [...acc, value];
|
|
1113
|
-
if (Array.isArray(value)) return [...acc, ...value.filter(isESTreeNode)];
|
|
1114
|
-
return acc;
|
|
1115
|
-
}, []);
|
|
1116
|
-
};
|
|
1117
|
-
const isESTreeNode = (value) => {
|
|
1118
|
-
return value !== null && typeof value === "object" && "type" in value && typeof value.type === "string";
|
|
1119
|
-
};
|
|
1120
1281
|
|
|
1121
1282
|
const noVariableConstructId = createRule({
|
|
1122
1283
|
name: "no-variable-construct-id",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-cdk-plugin",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.4",
|
|
4
4
|
"description": "eslint plugin for AWS CDK projects",
|
|
5
5
|
"main": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -30,25 +30,28 @@
|
|
|
30
30
|
"prebuild": "rolldown -c"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@eslint/js": "^9.
|
|
33
|
+
"@eslint/js": "^9.36.0",
|
|
34
34
|
"@secretlint/secretlint-rule-preset-recommend": "^11.2.4",
|
|
35
|
-
"@types/node": "^
|
|
36
|
-
"@typescript-eslint/rule-tester": "^8.
|
|
35
|
+
"@types/node": "^24.9.2",
|
|
36
|
+
"@typescript-eslint/rule-tester": "^8.46.2",
|
|
37
37
|
"@vitest/coverage-v8": "3.2.4",
|
|
38
38
|
"commander": "^14.0.1",
|
|
39
39
|
"consola": "^3.4.2",
|
|
40
|
-
"eslint": "9.
|
|
40
|
+
"eslint": "^9.38.0",
|
|
41
41
|
"eslint-plugin-import": "^2.32.0",
|
|
42
|
-
"pkgroll": "^2.
|
|
42
|
+
"pkgroll": "^2.20.1",
|
|
43
43
|
"rolldown": "1.0.0-beta.45",
|
|
44
44
|
"secretlint": "^11.2.4",
|
|
45
|
-
"typescript": "^5.
|
|
46
|
-
"typescript-eslint": "^8.
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"typescript-eslint": "^8.46.2",
|
|
47
47
|
"vitest": "^3.2.4"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@typescript-eslint/parser": "^8.
|
|
51
|
-
"@typescript-eslint/utils": "^8.
|
|
50
|
+
"@typescript-eslint/parser": "^8.46.2",
|
|
51
|
+
"@typescript-eslint/utils": "^8.46.2"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"typescript": ">=5.0.0"
|
|
52
55
|
},
|
|
53
56
|
"engines": {
|
|
54
57
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|