@nest-boot/eslint-plugin 7.0.6 → 7.0.7
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +6 -0
- package/dist/rules/mikro-orm/entity-property-config-from-types.js +51 -17
- package/dist/rules/mikro-orm/entity-property-config-from-types.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/index.spec.ts +8 -0
- package/src/rules/graphql/graphql-field-config-from-types.spec.ts +249 -0
- package/src/rules/graphql/graphql-field-definite-assignment.spec.ts +17 -0
- package/src/rules/index.spec.ts +15 -0
- package/src/rules/mikro-orm/entity-field-definite-assignment.spec.ts +17 -0
- package/src/rules/mikro-orm/entity-property-config-from-types.spec.ts +678 -1
- package/src/rules/mikro-orm/entity-property-config-from-types.ts +94 -17
- package/src/utils/decorators.spec.ts +52 -0
|
@@ -274,8 +274,11 @@ export default createRule<
|
|
|
274
274
|
);
|
|
275
275
|
};
|
|
276
276
|
|
|
277
|
-
// Check if
|
|
278
|
-
const
|
|
277
|
+
// Check if a named MikroORM helper is imported
|
|
278
|
+
const hasMikroOrmImport = (
|
|
279
|
+
importName: string,
|
|
280
|
+
options: { valueOnly?: boolean } = {},
|
|
281
|
+
): boolean => {
|
|
279
282
|
const program = context.sourceCode.ast;
|
|
280
283
|
for (const statement of program.body) {
|
|
281
284
|
if (statement.type === AST_NODE_TYPES.ImportDeclaration) {
|
|
@@ -285,25 +288,46 @@ export default createRule<
|
|
|
285
288
|
typeof importSource === "string" &&
|
|
286
289
|
importSource.startsWith("@mikro-orm/")
|
|
287
290
|
) {
|
|
288
|
-
|
|
291
|
+
if (options.valueOnly && statement.importKind === "type") {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const hasImport = statement.specifiers.some(
|
|
289
296
|
(spec: TSESTree.ImportClause) => {
|
|
290
297
|
return (
|
|
291
298
|
spec.type === AST_NODE_TYPES.ImportSpecifier &&
|
|
292
|
-
|
|
293
|
-
spec.
|
|
299
|
+
(!options.valueOnly || spec.importKind !== "type") &&
|
|
300
|
+
spec.local.name === importName
|
|
294
301
|
);
|
|
295
302
|
},
|
|
296
303
|
);
|
|
297
|
-
if (
|
|
304
|
+
if (hasImport) return true;
|
|
298
305
|
}
|
|
299
306
|
}
|
|
300
307
|
}
|
|
301
308
|
return false;
|
|
302
309
|
};
|
|
303
310
|
|
|
304
|
-
|
|
305
|
-
const
|
|
311
|
+
const hasOptImport = (): boolean => hasMikroOrmImport("Opt");
|
|
312
|
+
const hasEnumImport = (): boolean =>
|
|
313
|
+
hasMikroOrmImport("Enum", { valueOnly: true });
|
|
314
|
+
const hasPropertyImport = (): boolean =>
|
|
315
|
+
hasMikroOrmImport("Property", { valueOnly: true });
|
|
316
|
+
const hasTImport = (): boolean =>
|
|
317
|
+
hasMikroOrmImport("t", { valueOnly: true });
|
|
318
|
+
|
|
319
|
+
// Add named helpers to the @mikro-orm/core import
|
|
320
|
+
const addMikroOrmImports = (
|
|
321
|
+
fixer: RuleFixer,
|
|
322
|
+
importNames: string[],
|
|
323
|
+
options: { valueImport?: boolean } = {},
|
|
324
|
+
): RuleFix | null => {
|
|
306
325
|
const program = context.sourceCode.ast;
|
|
326
|
+
const uniqueImportNames = [...new Set(importNames)];
|
|
327
|
+
|
|
328
|
+
if (uniqueImportNames.length === 0) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
307
331
|
|
|
308
332
|
// Find the @mikro-orm/core import statement
|
|
309
333
|
let coreImport: TSESTree.ImportDeclaration | null = null;
|
|
@@ -311,7 +335,10 @@ export default createRule<
|
|
|
311
335
|
for (const statement of program.body) {
|
|
312
336
|
if (statement.type === AST_NODE_TYPES.ImportDeclaration) {
|
|
313
337
|
const importSource = statement.source.value;
|
|
314
|
-
if (
|
|
338
|
+
if (
|
|
339
|
+
importSource === "@mikro-orm/core" &&
|
|
340
|
+
(!options.valueImport || statement.importKind !== "type")
|
|
341
|
+
) {
|
|
315
342
|
coreImport = statement;
|
|
316
343
|
break;
|
|
317
344
|
}
|
|
@@ -319,7 +346,7 @@ export default createRule<
|
|
|
319
346
|
}
|
|
320
347
|
|
|
321
348
|
if (coreImport) {
|
|
322
|
-
// Already has @mikro-orm/core import, add
|
|
349
|
+
// Already has @mikro-orm/core import, add the helper to the import list
|
|
323
350
|
const lastSpecifier =
|
|
324
351
|
coreImport.specifiers[coreImport.specifiers.length - 1];
|
|
325
352
|
|
|
@@ -330,10 +357,16 @@ export default createRule<
|
|
|
330
357
|
if (isMultiline) {
|
|
331
358
|
// Multiline import: add after the last import item, keeping indentation
|
|
332
359
|
const indent = " "; // Assuming 2-space indentation
|
|
333
|
-
return fixer.insertTextAfter(
|
|
360
|
+
return fixer.insertTextAfter(
|
|
361
|
+
lastSpecifier,
|
|
362
|
+
uniqueImportNames.map((name) => `,\n${indent}${name}`).join(""),
|
|
363
|
+
);
|
|
334
364
|
} else {
|
|
335
365
|
// Single-line import: add directly
|
|
336
|
-
return fixer.insertTextAfter(
|
|
366
|
+
return fixer.insertTextAfter(
|
|
367
|
+
lastSpecifier,
|
|
368
|
+
`, ${uniqueImportNames.join(", ")}`,
|
|
369
|
+
);
|
|
337
370
|
}
|
|
338
371
|
} else {
|
|
339
372
|
// No @mikro-orm/core import, add a new import statement at the top
|
|
@@ -345,13 +378,42 @@ export default createRule<
|
|
|
345
378
|
if (firstImport) {
|
|
346
379
|
return fixer.insertTextBefore(
|
|
347
380
|
firstImport,
|
|
348
|
-
|
|
381
|
+
`import { ${uniqueImportNames.join(", ")} } from '@mikro-orm/core';\n`,
|
|
349
382
|
);
|
|
350
383
|
}
|
|
351
384
|
}
|
|
352
385
|
return null;
|
|
353
386
|
};
|
|
354
387
|
|
|
388
|
+
const addMikroOrmImport = (
|
|
389
|
+
fixer: RuleFixer,
|
|
390
|
+
importName: string,
|
|
391
|
+
options: { valueImport?: boolean } = {},
|
|
392
|
+
): RuleFix | null => addMikroOrmImports(fixer, [importName], options);
|
|
393
|
+
|
|
394
|
+
const addOptImport = (fixer: RuleFixer): RuleFix | null =>
|
|
395
|
+
addMikroOrmImport(fixer, "Opt");
|
|
396
|
+
|
|
397
|
+
const addEnumImport = (fixer: RuleFixer): RuleFix | null =>
|
|
398
|
+
addMikroOrmImport(fixer, "Enum", { valueImport: true });
|
|
399
|
+
|
|
400
|
+
const addPropertyDecoratorImports = (
|
|
401
|
+
fixer: RuleFixer,
|
|
402
|
+
info: TypeInfo,
|
|
403
|
+
): RuleFix | null => {
|
|
404
|
+
const importNames: string[] = [];
|
|
405
|
+
|
|
406
|
+
if (!hasPropertyImport()) {
|
|
407
|
+
importNames.push("Property");
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (info.propertyType?.startsWith("t.") && !hasTImport()) {
|
|
411
|
+
importNames.push("t");
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return addMikroOrmImports(fixer, importNames, { valueImport: true });
|
|
415
|
+
};
|
|
416
|
+
|
|
355
417
|
const computeTypeInfo = (
|
|
356
418
|
property: TSESTree.PropertyDefinition,
|
|
357
419
|
): TypeInfo | null => {
|
|
@@ -1073,10 +1135,15 @@ export default createRule<
|
|
|
1073
1135
|
messageId: "useEnumDecorator",
|
|
1074
1136
|
fix: (fixer) => {
|
|
1075
1137
|
const newDecoratorText = buildEnumDecorator(typeInfo);
|
|
1076
|
-
|
|
1138
|
+
const decoratorFix = fixer.replaceTextRange(
|
|
1077
1139
|
propertyDecorator.range,
|
|
1078
1140
|
newDecoratorText,
|
|
1079
1141
|
);
|
|
1142
|
+
const importFix = hasEnumImport()
|
|
1143
|
+
? null
|
|
1144
|
+
: addEnumImport(fixer);
|
|
1145
|
+
|
|
1146
|
+
return importFix ? [importFix, decoratorFix] : decoratorFix;
|
|
1080
1147
|
},
|
|
1081
1148
|
});
|
|
1082
1149
|
return;
|
|
@@ -1088,10 +1155,13 @@ export default createRule<
|
|
|
1088
1155
|
messageId: "useEnumDecorator",
|
|
1089
1156
|
fix: (fixer) => {
|
|
1090
1157
|
const newDecoratorText = buildEnumDecorator(typeInfo);
|
|
1091
|
-
|
|
1158
|
+
const decoratorFix = fixer.insertTextBeforeRange(
|
|
1092
1159
|
member.range,
|
|
1093
1160
|
newDecoratorText + "\n ",
|
|
1094
1161
|
);
|
|
1162
|
+
const importFix = hasEnumImport() ? null : addEnumImport(fixer);
|
|
1163
|
+
|
|
1164
|
+
return importFix ? [importFix, decoratorFix] : decoratorFix;
|
|
1095
1165
|
},
|
|
1096
1166
|
});
|
|
1097
1167
|
return;
|
|
@@ -1103,8 +1173,15 @@ export default createRule<
|
|
|
1103
1173
|
node: member,
|
|
1104
1174
|
messageId: "alignPropertyDecoratorWithTsType",
|
|
1105
1175
|
fix: (fixer) => {
|
|
1106
|
-
const
|
|
1107
|
-
|
|
1176
|
+
const decoratorFixes = applyFixes(
|
|
1177
|
+
fixer,
|
|
1178
|
+
addPropertyDecorator(member, typeInfo),
|
|
1179
|
+
);
|
|
1180
|
+
const importFix = addPropertyDecoratorImports(fixer, typeInfo);
|
|
1181
|
+
|
|
1182
|
+
return importFix
|
|
1183
|
+
? [importFix, ...decoratorFixes]
|
|
1184
|
+
: decoratorFixes;
|
|
1108
1185
|
},
|
|
1109
1186
|
});
|
|
1110
1187
|
return;
|
|
@@ -84,6 +84,32 @@ describe("decorators", () => {
|
|
|
84
84
|
|
|
85
85
|
expect(hasClassDecorator(classDeclaration, "Entity")).toBe(false);
|
|
86
86
|
});
|
|
87
|
+
|
|
88
|
+
it("should return false for non-call and non-identifier class decorators", () => {
|
|
89
|
+
const classDeclaration = {
|
|
90
|
+
type: AST_NODE_TYPES.ClassDeclaration,
|
|
91
|
+
decorators: [
|
|
92
|
+
{
|
|
93
|
+
type: AST_NODE_TYPES.Decorator,
|
|
94
|
+
expression: {
|
|
95
|
+
type: AST_NODE_TYPES.Identifier,
|
|
96
|
+
name: "Entity",
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: AST_NODE_TYPES.Decorator,
|
|
101
|
+
expression: {
|
|
102
|
+
type: AST_NODE_TYPES.CallExpression,
|
|
103
|
+
callee: {
|
|
104
|
+
type: AST_NODE_TYPES.MemberExpression,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
} as unknown as TSESTree.ClassDeclaration;
|
|
110
|
+
|
|
111
|
+
expect(hasClassDecorator(classDeclaration, "Entity")).toBe(false);
|
|
112
|
+
});
|
|
87
113
|
});
|
|
88
114
|
|
|
89
115
|
describe("hasPropertyDecorator", () => {
|
|
@@ -148,6 +174,32 @@ describe("decorators", () => {
|
|
|
148
174
|
|
|
149
175
|
expect(hasPropertyDecorator(propertyDefinition, "Field")).toBe(false);
|
|
150
176
|
});
|
|
177
|
+
|
|
178
|
+
it("should return false for non-call and non-identifier property decorators", () => {
|
|
179
|
+
const propertyDefinition = {
|
|
180
|
+
type: AST_NODE_TYPES.PropertyDefinition,
|
|
181
|
+
decorators: [
|
|
182
|
+
{
|
|
183
|
+
type: AST_NODE_TYPES.Decorator,
|
|
184
|
+
expression: {
|
|
185
|
+
type: AST_NODE_TYPES.Identifier,
|
|
186
|
+
name: "Field",
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: AST_NODE_TYPES.Decorator,
|
|
191
|
+
expression: {
|
|
192
|
+
type: AST_NODE_TYPES.CallExpression,
|
|
193
|
+
callee: {
|
|
194
|
+
type: AST_NODE_TYPES.MemberExpression,
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
} as unknown as TSESTree.PropertyDefinition;
|
|
200
|
+
|
|
201
|
+
expect(hasPropertyDecorator(propertyDefinition, "Field")).toBe(false);
|
|
202
|
+
});
|
|
151
203
|
});
|
|
152
204
|
|
|
153
205
|
describe("getClassDecorator", () => {
|