@sarj/eslint-plugin 16.2.2 → 16.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -356,7 +356,7 @@ var REQUIRE_EXPLICIT_CONTRACT_IMPLEMENTATION_DOCUMENTATION = {
356
356
  autofix: "none",
357
357
  limitations: [
358
358
  "Only class instances whose concrete class and selected constructor parameter resolve through the TypeScript checker are checked.",
359
- "Object literals, casts, any, unresolved heritage, dynamic factories, and third-party classes are excluded."
359
+ "Object literals, casts, any, callback-valued data properties, unresolved heritage, dynamic factories, and third-party classes are excluded."
360
360
  ],
361
361
  examples: [
362
362
  {
@@ -415,14 +415,20 @@ var REQUIRE_EXPLICIT_CONTRACT_IMPLEMENTATION_DOCUMENTATION = {
415
415
  }
416
416
  ]
417
417
  };
418
- function isBehavioralContract(checker, type) {
419
- if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0) return false;
418
+ function behavioralContract(checker, type) {
419
+ if (type.isUnion()) {
420
+ const substantive = type.types.filter((member) => (member.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0);
421
+ if (substantive.length !== 1) return null;
422
+ return behavioralContract(checker, substantive[0]);
423
+ }
424
+ if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0) return null;
420
425
  const declaration = type.getSymbol()?.declarations?.find((item) => ts.isInterfaceDeclaration(item) || ts.isTypeAliasDeclaration(item) || ts.isClassDeclaration(item) && item.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AbstractKeyword));
421
- if (declaration === void 0) return false;
426
+ if (declaration === void 0) return null;
422
427
  return checker.getPropertiesOfType(type).some((property) => {
428
+ if (!property.declarations?.some((member) => ts.isMethodSignature(member) || ts.isMethodDeclaration(member))) return false;
423
429
  const propertyType = checker.getTypeOfSymbolAtLocation(property, declaration);
424
430
  return propertyType.getCallSignatures().length > 0;
425
- });
431
+ }) ? type : null;
426
432
  }
427
433
  function ownedClass(type) {
428
434
  if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0) return null;
@@ -450,7 +456,7 @@ function explicitlyImplements(checker, declaration, target, seen) {
450
456
  unknown = true;
451
457
  continue;
452
458
  }
453
- const relationship = explicitlyImplements(checker, inherited, target, seen);
459
+ const relationship = explicitlyImplements(checker, inherited, target, new Set(seen));
454
460
  if (relationship === true) return true;
455
461
  unknown ||= relationship === null;
456
462
  }
@@ -489,12 +495,13 @@ var require_explicit_contract_implementation_default = createRule({
489
495
  const parameter = parameters[index];
490
496
  if (parameter === void 0) continue;
491
497
  const parameterType = checker.getTypeOfSymbolAtLocation(parameter, tsNode);
492
- if (!isBehavioralContract(checker, parameterType)) continue;
493
- const contractSymbol = parameterType.getSymbol();
498
+ const contractType = behavioralContract(checker, parameterType);
499
+ if (contractType === null) continue;
500
+ const contractSymbol = contractType.getSymbol();
494
501
  if (contractSymbol === void 0) continue;
495
502
  const tsArgument = services.esTreeNodeToTSNodeMap.get(argument);
496
503
  const actualType = checker.getTypeAtLocation(tsArgument);
497
- if (!checker.isTypeAssignableTo(actualType, parameterType)) continue;
504
+ if (!checker.isTypeAssignableTo(actualType, contractType)) continue;
498
505
  const implementation = ownedClass(actualType);
499
506
  if (implementation === null) continue;
500
507
  const relationship = explicitlyImplements(checker, implementation, contractSymbol, /* @__PURE__ */ new Set());
@@ -23801,7 +23808,7 @@ var RULES = {
23801
23808
  };
23802
23809
  var meta = {
23803
23810
  name: "@sarj/eslint-plugin",
23804
- version: "16.2.2"
23811
+ version: "16.2.3"
23805
23812
  };
23806
23813
  var APPLICATION_ONLY_RULES = [];
23807
23814
  var LIBRARY_IMPORT_POLICY = ["error", {
package/dist/index.d.cts CHANGED
@@ -922,7 +922,7 @@ type FlatPreset = {
922
922
  declare const PLUGIN: {
923
923
  readonly meta: {
924
924
  readonly name: "@sarj/eslint-plugin";
925
- readonly version: "16.2.2";
925
+ readonly version: "16.2.3";
926
926
  };
927
927
  readonly rules: {
928
928
  readonly "require-explicit-contract-implementation": DocumentedRule<readonly [], "declareActualContract">;
package/dist/index.d.ts CHANGED
@@ -922,7 +922,7 @@ type FlatPreset = {
922
922
  declare const PLUGIN: {
923
923
  readonly meta: {
924
924
  readonly name: "@sarj/eslint-plugin";
925
- readonly version: "16.2.2";
925
+ readonly version: "16.2.3";
926
926
  };
927
927
  readonly rules: {
928
928
  readonly "require-explicit-contract-implementation": DocumentedRule<readonly [], "declareActualContract">;
package/dist/index.js CHANGED
@@ -305,7 +305,7 @@ var REQUIRE_EXPLICIT_CONTRACT_IMPLEMENTATION_DOCUMENTATION = {
305
305
  autofix: "none",
306
306
  limitations: [
307
307
  "Only class instances whose concrete class and selected constructor parameter resolve through the TypeScript checker are checked.",
308
- "Object literals, casts, any, unresolved heritage, dynamic factories, and third-party classes are excluded."
308
+ "Object literals, casts, any, callback-valued data properties, unresolved heritage, dynamic factories, and third-party classes are excluded."
309
309
  ],
310
310
  examples: [
311
311
  {
@@ -364,14 +364,20 @@ var REQUIRE_EXPLICIT_CONTRACT_IMPLEMENTATION_DOCUMENTATION = {
364
364
  }
365
365
  ]
366
366
  };
367
- function isBehavioralContract(checker, type) {
368
- if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0) return false;
367
+ function behavioralContract(checker, type) {
368
+ if (type.isUnion()) {
369
+ const substantive = type.types.filter((member) => (member.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0);
370
+ if (substantive.length !== 1) return null;
371
+ return behavioralContract(checker, substantive[0]);
372
+ }
373
+ if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0) return null;
369
374
  const declaration = type.getSymbol()?.declarations?.find((item) => ts.isInterfaceDeclaration(item) || ts.isTypeAliasDeclaration(item) || ts.isClassDeclaration(item) && item.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AbstractKeyword));
370
- if (declaration === void 0) return false;
375
+ if (declaration === void 0) return null;
371
376
  return checker.getPropertiesOfType(type).some((property) => {
377
+ if (!property.declarations?.some((member) => ts.isMethodSignature(member) || ts.isMethodDeclaration(member))) return false;
372
378
  const propertyType = checker.getTypeOfSymbolAtLocation(property, declaration);
373
379
  return propertyType.getCallSignatures().length > 0;
374
- });
380
+ }) ? type : null;
375
381
  }
376
382
  function ownedClass(type) {
377
383
  if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0) return null;
@@ -399,7 +405,7 @@ function explicitlyImplements(checker, declaration, target, seen) {
399
405
  unknown = true;
400
406
  continue;
401
407
  }
402
- const relationship = explicitlyImplements(checker, inherited, target, seen);
408
+ const relationship = explicitlyImplements(checker, inherited, target, new Set(seen));
403
409
  if (relationship === true) return true;
404
410
  unknown ||= relationship === null;
405
411
  }
@@ -438,12 +444,13 @@ var require_explicit_contract_implementation_default = createRule({
438
444
  const parameter = parameters[index];
439
445
  if (parameter === void 0) continue;
440
446
  const parameterType = checker.getTypeOfSymbolAtLocation(parameter, tsNode);
441
- if (!isBehavioralContract(checker, parameterType)) continue;
442
- const contractSymbol = parameterType.getSymbol();
447
+ const contractType = behavioralContract(checker, parameterType);
448
+ if (contractType === null) continue;
449
+ const contractSymbol = contractType.getSymbol();
443
450
  if (contractSymbol === void 0) continue;
444
451
  const tsArgument = services.esTreeNodeToTSNodeMap.get(argument);
445
452
  const actualType = checker.getTypeAtLocation(tsArgument);
446
- if (!checker.isTypeAssignableTo(actualType, parameterType)) continue;
453
+ if (!checker.isTypeAssignableTo(actualType, contractType)) continue;
447
454
  const implementation = ownedClass(actualType);
448
455
  if (implementation === null) continue;
449
456
  const relationship = explicitlyImplements(checker, implementation, contractSymbol, /* @__PURE__ */ new Set());
@@ -23822,7 +23829,7 @@ var RULES = {
23822
23829
  };
23823
23830
  var meta = {
23824
23831
  name: "@sarj/eslint-plugin",
23825
- version: "16.2.2"
23832
+ version: "16.2.3"
23826
23833
  };
23827
23834
  var APPLICATION_ONLY_RULES = [];
23828
23835
  var LIBRARY_IMPORT_POLICY = ["error", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarj/eslint-plugin",
3
- "version": "16.2.2",
3
+ "version": "16.2.3",
4
4
  "packageManager": "npm@12.1.0",
5
5
  "description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",