@sarj/eslint-plugin 16.2.6 → 16.3.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/dist/index.cjs CHANGED
@@ -14840,7 +14840,8 @@ var PREFER_IMMUTABLE_MODULE_CONSTANT_DOCUMENTATION = {
14840
14840
  remediation: "Expose literals with `as const` or a readonly type, and expose Set or Map values through ReadonlySet or ReadonlyMap.",
14841
14841
  category: "correctness",
14842
14842
  limitations: [
14843
- "Generated, test and JavaScript files are skipped. Private constants with observed direct or alias mutation are excluded; exported mutable collections remain advisory candidates. Reassigned aliases are conservatively followed, not flow-proven."
14843
+ "Generated, test and JavaScript files are skipped. Private constants with observed direct or alias mutation are excluded; exported mutable collections remain advisory candidates. Reassigned aliases are conservatively followed, not flow-proven.",
14844
+ "Local type aliases are resolved before recognizing standard readonly types; imported or otherwise unknown type declarations are not inferred from their names. Readonly types have no runtime enforcement."
14844
14845
  ],
14845
14846
  examples: [
14846
14847
  {
@@ -14860,6 +14861,26 @@ var PREFER_IMMUTABLE_MODULE_CONSTANT_DOCUMENTATION = {
14860
14861
  focusPath: "src/constants.ts",
14861
14862
  expectedCount: 1,
14862
14863
  public: true
14864
+ },
14865
+ {
14866
+ id: "shadowed-readonly-alias",
14867
+ scenarioId: "type-binding",
14868
+ title: "A type named Readonly still exposes mutable properties",
14869
+ outcome: "match",
14870
+ files: [{ path: "src/constants.ts", source: "type Readonly = { voice: boolean };\nexport const TOOL_SUPPORT: Readonly = { voice: true };" }],
14871
+ focusPath: "src/constants.ts",
14872
+ expectedCount: 1,
14873
+ public: true
14874
+ },
14875
+ {
14876
+ id: "resolved-readonly-alias",
14877
+ scenarioId: "type-binding",
14878
+ title: "A local alias resolves to readonly properties",
14879
+ outcome: "no-match",
14880
+ files: [{ path: "src/constants.ts", source: "type ToolSupport = Readonly<{ voice: boolean }>;\nexport const TOOL_SUPPORT: ToolSupport = { voice: true };" }],
14881
+ focusPath: "src/constants.ts",
14882
+ expectedCount: 0,
14883
+ public: true
14863
14884
  }
14864
14885
  ]
14865
14886
  };
@@ -14914,39 +14935,84 @@ function collectionKind(node, isUnshadowedGlobal3) {
14914
14935
  }
14915
14936
  return null;
14916
14937
  }
14917
- function declaredReadonlyType(node, kind, aliases) {
14938
+ function declaredReadonlyType(node, kind, resolveLocalType, isUnshadowedType) {
14918
14939
  const annotation = node.id.type === import_utils89.AST_NODE_TYPES.Identifier ? node.id.typeAnnotation : void 0;
14919
- if (annotation !== void 0 && isReadonlyTypeResolved(annotation.typeAnnotation, kind, aliases)) {
14940
+ if (annotation !== void 0 && readonlyTypeState(annotation.typeAnnotation, kind, resolveLocalType, isUnshadowedType) === "readonly") {
14920
14941
  return true;
14921
14942
  }
14922
- return node.init?.type === import_utils89.AST_NODE_TYPES.TSAsExpression && isReadonlyTypeResolved(node.init.typeAnnotation, kind, aliases);
14943
+ return node.init?.type === import_utils89.AST_NODE_TYPES.TSAsExpression && readonlyTypeState(node.init.typeAnnotation, kind, resolveLocalType, isUnshadowedType) === "readonly";
14923
14944
  }
14924
- function isReadonlyTypeResolved(node, kind, aliases, seen = /* @__PURE__ */ new Set()) {
14925
- if (isReadonlyType(node, kind)) return true;
14926
- if (node.type !== import_utils89.AST_NODE_TYPES.TSTypeReference || node.typeName.type !== import_utils89.AST_NODE_TYPES.Identifier) return false;
14927
- const name = node.typeName.name;
14928
- const target = aliases.get(name);
14929
- if (target === void 0 || seen.has(name)) return false;
14930
- return isReadonlyTypeResolved(target, kind, aliases, /* @__PURE__ */ new Set([...seen, name]));
14945
+ function readonlyTypeState(node, kind, resolveLocalType, isUnshadowedType, arguments_ = /* @__PURE__ */ new Map(), seen = /* @__PURE__ */ new Set()) {
14946
+ if (seen.has(node)) return "unknown";
14947
+ if (node.type !== import_utils89.AST_NODE_TYPES.TSTypeReference || node.typeName.type !== import_utils89.AST_NODE_TYPES.Identifier) {
14948
+ return directReadonlyState(node, kind, isUnshadowedType);
14949
+ }
14950
+ const declaration = resolveLocalType(node.typeName);
14951
+ if (declaration === void 0) return directReadonlyState(node, kind, isUnshadowedType);
14952
+ if (declaration.type === import_utils89.AST_NODE_TYPES.TSTypeParameter) {
14953
+ const argument = arguments_.get(declaration);
14954
+ return argument === void 0 ? "unknown" : readonlyTypeState(
14955
+ argument.node,
14956
+ kind,
14957
+ resolveLocalType,
14958
+ isUnshadowedType,
14959
+ argument.arguments,
14960
+ argument.seen
14961
+ );
14962
+ }
14963
+ const expanded = /* @__PURE__ */ new Set([...seen, node]);
14964
+ return readonlyTypeState(
14965
+ declaration.typeAnnotation,
14966
+ kind,
14967
+ resolveLocalType,
14968
+ isUnshadowedType,
14969
+ bindTypeArguments(declaration, node, arguments_, expanded),
14970
+ expanded
14971
+ );
14931
14972
  }
14932
- function isReadonlyType(node, kind) {
14973
+ function bindTypeArguments(declaration, reference, arguments_, seen) {
14974
+ const bindings = new Map(arguments_);
14975
+ for (const [index, parameter] of (declaration.typeParameters?.params ?? []).entries()) {
14976
+ const argument = reference.typeArguments?.params[index];
14977
+ const node = argument ?? parameter.default;
14978
+ if (node !== void 0) {
14979
+ bindings.set(parameter, { node, arguments: argument === void 0 ? new Map(bindings) : arguments_, seen });
14980
+ }
14981
+ }
14982
+ return bindings;
14983
+ }
14984
+ function directReadonlyState(node, kind, isUnshadowedType) {
14933
14985
  if (node.type === import_utils89.AST_NODE_TYPES.TSTypeOperator && node.operator === "readonly") {
14934
- return true;
14986
+ return "readonly";
14935
14987
  }
14936
- if (node.type !== import_utils89.AST_NODE_TYPES.TSTypeReference || node.typeName.type !== import_utils89.AST_NODE_TYPES.Identifier) {
14937
- return false;
14988
+ if (node.type === import_utils89.AST_NODE_TYPES.TSTypeLiteral) {
14989
+ if (node.members.length === 0) return "unknown";
14990
+ const readonly2 = kind === "literal" && node.members.length > 0 && node.members.every(
14991
+ (member) => (member.type === import_utils89.AST_NODE_TYPES.TSPropertySignature || member.type === import_utils89.AST_NODE_TYPES.TSIndexSignature) && member.readonly
14992
+ );
14993
+ return readonly2 ? "readonly" : "mutable";
14994
+ }
14995
+ if (node.type === import_utils89.AST_NODE_TYPES.TSArrayType || node.type === import_utils89.AST_NODE_TYPES.TSTupleType) return "mutable";
14996
+ if (node.type !== import_utils89.AST_NODE_TYPES.TSTypeReference || node.typeName.type !== import_utils89.AST_NODE_TYPES.Identifier || !isUnshadowedType(node.typeName)) {
14997
+ return "unknown";
14938
14998
  }
14939
14999
  if (node.typeName.name === "Readonly") {
14940
- return kind === "literal";
15000
+ return kind === "literal" ? "readonly" : "mutable";
14941
15001
  }
14942
- return kind === "literal" ? node.typeName.name === "ReadonlyArray" : node.typeName.name === `Readonly${kind}`;
15002
+ if (["Array", "Map", "Set"].includes(node.typeName.name)) return "mutable";
15003
+ if (!["ReadonlyArray", "ReadonlyMap", "ReadonlySet"].includes(node.typeName.name)) return "unknown";
15004
+ const readonly = kind === "literal" ? node.typeName.name === "ReadonlyArray" : node.typeName.name === `Readonly${kind}`;
15005
+ return readonly ? "readonly" : "mutable";
14943
15006
  }
14944
- function hasUnknownExplicitType(node, aliases) {
14945
- const annotation = node.id.type === import_utils89.AST_NODE_TYPES.Identifier ? node.id.typeAnnotation?.typeAnnotation : void 0;
15007
+ function hasUnknownExplicitType(node, kind, resolveLocalType, isUnshadowedType) {
15008
+ const annotation = (node.id.type === import_utils89.AST_NODE_TYPES.Identifier ? node.id.typeAnnotation?.typeAnnotation : void 0) ?? (node.init?.type === import_utils89.AST_NODE_TYPES.TSAsExpression ? node.init.typeAnnotation : void 0);
14946
15009
  if (annotation === void 0) return false;
14947
15010
  if (annotation.type === import_utils89.AST_NODE_TYPES.TSArrayType || annotation.type === import_utils89.AST_NODE_TYPES.TSTypeOperator) return false;
14948
15011
  if (annotation.type !== import_utils89.AST_NODE_TYPES.TSTypeReference || annotation.typeName.type !== import_utils89.AST_NODE_TYPES.Identifier) return true;
14949
- return !aliases.has(annotation.typeName.name) && !["Array", "Map", "Readonly", "ReadonlyArray", "ReadonlyMap", "ReadonlySet", "Set"].includes(annotation.typeName.name);
15012
+ if (resolveLocalType(annotation.typeName) !== void 0) {
15013
+ return readonlyTypeState(annotation, kind, resolveLocalType, isUnshadowedType) === "unknown";
15014
+ }
15015
+ return resolveLocalType(annotation.typeName) === void 0 && (!isUnshadowedType(annotation.typeName) || !["Array", "Map", "Readonly", "ReadonlyArray", "ReadonlyMap", "ReadonlySet", "Set"].includes(annotation.typeName.name));
14950
15016
  }
14951
15017
  function referenceMutates(identifier, isUnshadowedGlobal3) {
14952
15018
  let member = identifier.parent;
@@ -14989,11 +15055,20 @@ var prefer_immutable_module_constant_default = createRule({
14989
15055
  const variable = import_utils89.ASTUtils.findVariable(sourceCode.getScope(identifier), identifier.name);
14990
15056
  return variable === null || variable.defs.length === 0;
14991
15057
  };
15058
+ const isUnshadowedType = (identifier) => {
15059
+ const variable = import_utils89.ASTUtils.findVariable(sourceCode.getScope(identifier), identifier.name);
15060
+ return variable === null || !variable.defs.some((definition) => definition.isTypeDefinition);
15061
+ };
15062
+ const resolveLocalType = (identifier) => {
15063
+ const variable = import_utils89.ASTUtils.findVariable(sourceCode.getScope(identifier), identifier.name);
15064
+ const definitions = variable?.defs.filter((definition) => definition.isTypeDefinition) ?? [];
15065
+ const declaration = definitions.length === 1 ? definitions[0]?.node : void 0;
15066
+ return declaration?.type === import_utils89.AST_NODE_TYPES.TSTypeAliasDeclaration || declaration?.type === import_utils89.AST_NODE_TYPES.TSTypeParameter ? declaration : void 0;
15067
+ };
14992
15068
  if (JAVASCRIPT_FILE_RE.test(context.filename) || isTestFile(context.filename) || isGeneratedFile(context.filename, sourceCode.getText())) {
14993
15069
  return {};
14994
15070
  }
14995
15071
  const exportedNames2 = /* @__PURE__ */ new Set();
14996
- const typeAliases2 = /* @__PURE__ */ new Map();
14997
15072
  const mutatesThroughAlias = (root) => {
14998
15073
  const pending = [root];
14999
15074
  const seen = /* @__PURE__ */ new Set();
@@ -15018,10 +15093,6 @@ var prefer_immutable_module_constant_default = createRule({
15018
15093
  return {
15019
15094
  Program(node) {
15020
15095
  function collectModuleBindings(statement) {
15021
- const declaration = statement.type === import_utils89.AST_NODE_TYPES.ExportNamedDeclaration ? statement.declaration : statement;
15022
- if (declaration?.type === import_utils89.AST_NODE_TYPES.TSTypeAliasDeclaration) {
15023
- typeAliases2.set(declaration.id.name, declaration.typeAnnotation);
15024
- }
15025
15096
  if (statement.type === import_utils89.AST_NODE_TYPES.ExportNamedDeclaration) {
15026
15097
  if (statement.source !== null || statement.exportKind === "type") return;
15027
15098
  for (const specifier of statement.specifiers) {
@@ -15054,7 +15125,7 @@ var prefer_immutable_module_constant_default = createRule({
15054
15125
  return;
15055
15126
  }
15056
15127
  const kind = collectionKind(node.init, isUnshadowedGlobal3);
15057
- if (kind === null || declaredReadonlyType(node, kind, typeAliases2) || hasUnknownExplicitType(node, typeAliases2)) {
15128
+ if (kind === null || declaredReadonlyType(node, kind, resolveLocalType, isUnshadowedType) || hasUnknownExplicitType(node, kind, resolveLocalType, isUnshadowedType)) {
15058
15129
  return;
15059
15130
  }
15060
15131
  const variable = sourceCode.getDeclaredVariables(node)[0];
@@ -23949,7 +24020,7 @@ var RULES = {
23949
24020
  };
23950
24021
  var meta = {
23951
24022
  name: "@sarj/eslint-plugin",
23952
- version: "16.2.6"
24023
+ version: "16.3.0"
23953
24024
  };
23954
24025
  var APPLICATION_ONLY_RULES = [];
23955
24026
  var LIBRARY_IMPORT_POLICY = ["error", {
package/dist/index.d.cts CHANGED
@@ -924,7 +924,7 @@ type FlatPreset = {
924
924
  declare const PLUGIN: {
925
925
  readonly meta: {
926
926
  readonly name: "@sarj/eslint-plugin";
927
- readonly version: "16.2.6";
927
+ readonly version: "16.3.0";
928
928
  };
929
929
  readonly rules: {
930
930
  readonly "require-explicit-contract-implementation": DocumentedRule<readonly [], "declareActualContract">;
package/dist/index.d.ts CHANGED
@@ -924,7 +924,7 @@ type FlatPreset = {
924
924
  declare const PLUGIN: {
925
925
  readonly meta: {
926
926
  readonly name: "@sarj/eslint-plugin";
927
- readonly version: "16.2.6";
927
+ readonly version: "16.3.0";
928
928
  };
929
929
  readonly rules: {
930
930
  readonly "require-explicit-contract-implementation": DocumentedRule<readonly [], "declareActualContract">;
package/dist/index.js CHANGED
@@ -14835,7 +14835,8 @@ var PREFER_IMMUTABLE_MODULE_CONSTANT_DOCUMENTATION = {
14835
14835
  remediation: "Expose literals with `as const` or a readonly type, and expose Set or Map values through ReadonlySet or ReadonlyMap.",
14836
14836
  category: "correctness",
14837
14837
  limitations: [
14838
- "Generated, test and JavaScript files are skipped. Private constants with observed direct or alias mutation are excluded; exported mutable collections remain advisory candidates. Reassigned aliases are conservatively followed, not flow-proven."
14838
+ "Generated, test and JavaScript files are skipped. Private constants with observed direct or alias mutation are excluded; exported mutable collections remain advisory candidates. Reassigned aliases are conservatively followed, not flow-proven.",
14839
+ "Local type aliases are resolved before recognizing standard readonly types; imported or otherwise unknown type declarations are not inferred from their names. Readonly types have no runtime enforcement."
14839
14840
  ],
14840
14841
  examples: [
14841
14842
  {
@@ -14855,6 +14856,26 @@ var PREFER_IMMUTABLE_MODULE_CONSTANT_DOCUMENTATION = {
14855
14856
  focusPath: "src/constants.ts",
14856
14857
  expectedCount: 1,
14857
14858
  public: true
14859
+ },
14860
+ {
14861
+ id: "shadowed-readonly-alias",
14862
+ scenarioId: "type-binding",
14863
+ title: "A type named Readonly still exposes mutable properties",
14864
+ outcome: "match",
14865
+ files: [{ path: "src/constants.ts", source: "type Readonly = { voice: boolean };\nexport const TOOL_SUPPORT: Readonly = { voice: true };" }],
14866
+ focusPath: "src/constants.ts",
14867
+ expectedCount: 1,
14868
+ public: true
14869
+ },
14870
+ {
14871
+ id: "resolved-readonly-alias",
14872
+ scenarioId: "type-binding",
14873
+ title: "A local alias resolves to readonly properties",
14874
+ outcome: "no-match",
14875
+ files: [{ path: "src/constants.ts", source: "type ToolSupport = Readonly<{ voice: boolean }>;\nexport const TOOL_SUPPORT: ToolSupport = { voice: true };" }],
14876
+ focusPath: "src/constants.ts",
14877
+ expectedCount: 0,
14878
+ public: true
14858
14879
  }
14859
14880
  ]
14860
14881
  };
@@ -14909,39 +14930,84 @@ function collectionKind(node, isUnshadowedGlobal3) {
14909
14930
  }
14910
14931
  return null;
14911
14932
  }
14912
- function declaredReadonlyType(node, kind, aliases) {
14933
+ function declaredReadonlyType(node, kind, resolveLocalType, isUnshadowedType) {
14913
14934
  const annotation = node.id.type === AST_NODE_TYPES72.Identifier ? node.id.typeAnnotation : void 0;
14914
- if (annotation !== void 0 && isReadonlyTypeResolved(annotation.typeAnnotation, kind, aliases)) {
14935
+ if (annotation !== void 0 && readonlyTypeState(annotation.typeAnnotation, kind, resolveLocalType, isUnshadowedType) === "readonly") {
14915
14936
  return true;
14916
14937
  }
14917
- return node.init?.type === AST_NODE_TYPES72.TSAsExpression && isReadonlyTypeResolved(node.init.typeAnnotation, kind, aliases);
14938
+ return node.init?.type === AST_NODE_TYPES72.TSAsExpression && readonlyTypeState(node.init.typeAnnotation, kind, resolveLocalType, isUnshadowedType) === "readonly";
14918
14939
  }
14919
- function isReadonlyTypeResolved(node, kind, aliases, seen = /* @__PURE__ */ new Set()) {
14920
- if (isReadonlyType(node, kind)) return true;
14921
- if (node.type !== AST_NODE_TYPES72.TSTypeReference || node.typeName.type !== AST_NODE_TYPES72.Identifier) return false;
14922
- const name = node.typeName.name;
14923
- const target = aliases.get(name);
14924
- if (target === void 0 || seen.has(name)) return false;
14925
- return isReadonlyTypeResolved(target, kind, aliases, /* @__PURE__ */ new Set([...seen, name]));
14940
+ function readonlyTypeState(node, kind, resolveLocalType, isUnshadowedType, arguments_ = /* @__PURE__ */ new Map(), seen = /* @__PURE__ */ new Set()) {
14941
+ if (seen.has(node)) return "unknown";
14942
+ if (node.type !== AST_NODE_TYPES72.TSTypeReference || node.typeName.type !== AST_NODE_TYPES72.Identifier) {
14943
+ return directReadonlyState(node, kind, isUnshadowedType);
14944
+ }
14945
+ const declaration = resolveLocalType(node.typeName);
14946
+ if (declaration === void 0) return directReadonlyState(node, kind, isUnshadowedType);
14947
+ if (declaration.type === AST_NODE_TYPES72.TSTypeParameter) {
14948
+ const argument = arguments_.get(declaration);
14949
+ return argument === void 0 ? "unknown" : readonlyTypeState(
14950
+ argument.node,
14951
+ kind,
14952
+ resolveLocalType,
14953
+ isUnshadowedType,
14954
+ argument.arguments,
14955
+ argument.seen
14956
+ );
14957
+ }
14958
+ const expanded = /* @__PURE__ */ new Set([...seen, node]);
14959
+ return readonlyTypeState(
14960
+ declaration.typeAnnotation,
14961
+ kind,
14962
+ resolveLocalType,
14963
+ isUnshadowedType,
14964
+ bindTypeArguments(declaration, node, arguments_, expanded),
14965
+ expanded
14966
+ );
14926
14967
  }
14927
- function isReadonlyType(node, kind) {
14968
+ function bindTypeArguments(declaration, reference, arguments_, seen) {
14969
+ const bindings = new Map(arguments_);
14970
+ for (const [index, parameter] of (declaration.typeParameters?.params ?? []).entries()) {
14971
+ const argument = reference.typeArguments?.params[index];
14972
+ const node = argument ?? parameter.default;
14973
+ if (node !== void 0) {
14974
+ bindings.set(parameter, { node, arguments: argument === void 0 ? new Map(bindings) : arguments_, seen });
14975
+ }
14976
+ }
14977
+ return bindings;
14978
+ }
14979
+ function directReadonlyState(node, kind, isUnshadowedType) {
14928
14980
  if (node.type === AST_NODE_TYPES72.TSTypeOperator && node.operator === "readonly") {
14929
- return true;
14981
+ return "readonly";
14930
14982
  }
14931
- if (node.type !== AST_NODE_TYPES72.TSTypeReference || node.typeName.type !== AST_NODE_TYPES72.Identifier) {
14932
- return false;
14983
+ if (node.type === AST_NODE_TYPES72.TSTypeLiteral) {
14984
+ if (node.members.length === 0) return "unknown";
14985
+ const readonly2 = kind === "literal" && node.members.length > 0 && node.members.every(
14986
+ (member) => (member.type === AST_NODE_TYPES72.TSPropertySignature || member.type === AST_NODE_TYPES72.TSIndexSignature) && member.readonly
14987
+ );
14988
+ return readonly2 ? "readonly" : "mutable";
14989
+ }
14990
+ if (node.type === AST_NODE_TYPES72.TSArrayType || node.type === AST_NODE_TYPES72.TSTupleType) return "mutable";
14991
+ if (node.type !== AST_NODE_TYPES72.TSTypeReference || node.typeName.type !== AST_NODE_TYPES72.Identifier || !isUnshadowedType(node.typeName)) {
14992
+ return "unknown";
14933
14993
  }
14934
14994
  if (node.typeName.name === "Readonly") {
14935
- return kind === "literal";
14995
+ return kind === "literal" ? "readonly" : "mutable";
14936
14996
  }
14937
- return kind === "literal" ? node.typeName.name === "ReadonlyArray" : node.typeName.name === `Readonly${kind}`;
14997
+ if (["Array", "Map", "Set"].includes(node.typeName.name)) return "mutable";
14998
+ if (!["ReadonlyArray", "ReadonlyMap", "ReadonlySet"].includes(node.typeName.name)) return "unknown";
14999
+ const readonly = kind === "literal" ? node.typeName.name === "ReadonlyArray" : node.typeName.name === `Readonly${kind}`;
15000
+ return readonly ? "readonly" : "mutable";
14938
15001
  }
14939
- function hasUnknownExplicitType(node, aliases) {
14940
- const annotation = node.id.type === AST_NODE_TYPES72.Identifier ? node.id.typeAnnotation?.typeAnnotation : void 0;
15002
+ function hasUnknownExplicitType(node, kind, resolveLocalType, isUnshadowedType) {
15003
+ const annotation = (node.id.type === AST_NODE_TYPES72.Identifier ? node.id.typeAnnotation?.typeAnnotation : void 0) ?? (node.init?.type === AST_NODE_TYPES72.TSAsExpression ? node.init.typeAnnotation : void 0);
14941
15004
  if (annotation === void 0) return false;
14942
15005
  if (annotation.type === AST_NODE_TYPES72.TSArrayType || annotation.type === AST_NODE_TYPES72.TSTypeOperator) return false;
14943
15006
  if (annotation.type !== AST_NODE_TYPES72.TSTypeReference || annotation.typeName.type !== AST_NODE_TYPES72.Identifier) return true;
14944
- return !aliases.has(annotation.typeName.name) && !["Array", "Map", "Readonly", "ReadonlyArray", "ReadonlyMap", "ReadonlySet", "Set"].includes(annotation.typeName.name);
15007
+ if (resolveLocalType(annotation.typeName) !== void 0) {
15008
+ return readonlyTypeState(annotation, kind, resolveLocalType, isUnshadowedType) === "unknown";
15009
+ }
15010
+ return resolveLocalType(annotation.typeName) === void 0 && (!isUnshadowedType(annotation.typeName) || !["Array", "Map", "Readonly", "ReadonlyArray", "ReadonlyMap", "ReadonlySet", "Set"].includes(annotation.typeName.name));
14945
15011
  }
14946
15012
  function referenceMutates(identifier, isUnshadowedGlobal3) {
14947
15013
  let member = identifier.parent;
@@ -14984,11 +15050,20 @@ var prefer_immutable_module_constant_default = createRule({
14984
15050
  const variable = ASTUtils32.findVariable(sourceCode.getScope(identifier), identifier.name);
14985
15051
  return variable === null || variable.defs.length === 0;
14986
15052
  };
15053
+ const isUnshadowedType = (identifier) => {
15054
+ const variable = ASTUtils32.findVariable(sourceCode.getScope(identifier), identifier.name);
15055
+ return variable === null || !variable.defs.some((definition) => definition.isTypeDefinition);
15056
+ };
15057
+ const resolveLocalType = (identifier) => {
15058
+ const variable = ASTUtils32.findVariable(sourceCode.getScope(identifier), identifier.name);
15059
+ const definitions = variable?.defs.filter((definition) => definition.isTypeDefinition) ?? [];
15060
+ const declaration = definitions.length === 1 ? definitions[0]?.node : void 0;
15061
+ return declaration?.type === AST_NODE_TYPES72.TSTypeAliasDeclaration || declaration?.type === AST_NODE_TYPES72.TSTypeParameter ? declaration : void 0;
15062
+ };
14987
15063
  if (JAVASCRIPT_FILE_RE.test(context.filename) || isTestFile(context.filename) || isGeneratedFile(context.filename, sourceCode.getText())) {
14988
15064
  return {};
14989
15065
  }
14990
15066
  const exportedNames2 = /* @__PURE__ */ new Set();
14991
- const typeAliases2 = /* @__PURE__ */ new Map();
14992
15067
  const mutatesThroughAlias = (root) => {
14993
15068
  const pending = [root];
14994
15069
  const seen = /* @__PURE__ */ new Set();
@@ -15013,10 +15088,6 @@ var prefer_immutable_module_constant_default = createRule({
15013
15088
  return {
15014
15089
  Program(node) {
15015
15090
  function collectModuleBindings(statement) {
15016
- const declaration = statement.type === AST_NODE_TYPES72.ExportNamedDeclaration ? statement.declaration : statement;
15017
- if (declaration?.type === AST_NODE_TYPES72.TSTypeAliasDeclaration) {
15018
- typeAliases2.set(declaration.id.name, declaration.typeAnnotation);
15019
- }
15020
15091
  if (statement.type === AST_NODE_TYPES72.ExportNamedDeclaration) {
15021
15092
  if (statement.source !== null || statement.exportKind === "type") return;
15022
15093
  for (const specifier of statement.specifiers) {
@@ -15049,7 +15120,7 @@ var prefer_immutable_module_constant_default = createRule({
15049
15120
  return;
15050
15121
  }
15051
15122
  const kind = collectionKind(node.init, isUnshadowedGlobal3);
15052
- if (kind === null || declaredReadonlyType(node, kind, typeAliases2) || hasUnknownExplicitType(node, typeAliases2)) {
15123
+ if (kind === null || declaredReadonlyType(node, kind, resolveLocalType, isUnshadowedType) || hasUnknownExplicitType(node, kind, resolveLocalType, isUnshadowedType)) {
15053
15124
  return;
15054
15125
  }
15055
15126
  const variable = sourceCode.getDeclaredVariables(node)[0];
@@ -23970,7 +24041,7 @@ var RULES = {
23970
24041
  };
23971
24042
  var meta = {
23972
24043
  name: "@sarj/eslint-plugin",
23973
- version: "16.2.6"
24044
+ version: "16.3.0"
23974
24045
  };
23975
24046
  var APPLICATION_ONLY_RULES = [];
23976
24047
  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.6",
3
+ "version": "16.3.0",
4
4
  "packageManager": "npm@12.1.0",
5
5
  "description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",