@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 +98 -27
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +98 -27
- package/package.json +1 -1
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,
|
|
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 &&
|
|
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 &&
|
|
14943
|
+
return node.init?.type === import_utils89.AST_NODE_TYPES.TSAsExpression && readonlyTypeState(node.init.typeAnnotation, kind, resolveLocalType, isUnshadowedType) === "readonly";
|
|
14923
14944
|
}
|
|
14924
|
-
function
|
|
14925
|
-
if (
|
|
14926
|
-
if (node.type !== import_utils89.AST_NODE_TYPES.TSTypeReference || node.typeName.type !== import_utils89.AST_NODE_TYPES.Identifier)
|
|
14927
|
-
|
|
14928
|
-
|
|
14929
|
-
|
|
14930
|
-
|
|
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
|
|
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
|
|
14986
|
+
return "readonly";
|
|
14935
14987
|
}
|
|
14936
|
-
if (node.type
|
|
14937
|
-
return
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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.
|
|
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.
|
|
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.
|
|
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,
|
|
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 &&
|
|
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 &&
|
|
14938
|
+
return node.init?.type === AST_NODE_TYPES72.TSAsExpression && readonlyTypeState(node.init.typeAnnotation, kind, resolveLocalType, isUnshadowedType) === "readonly";
|
|
14918
14939
|
}
|
|
14919
|
-
function
|
|
14920
|
-
if (
|
|
14921
|
-
if (node.type !== AST_NODE_TYPES72.TSTypeReference || node.typeName.type !== AST_NODE_TYPES72.Identifier)
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
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
|
|
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
|
|
14981
|
+
return "readonly";
|
|
14930
14982
|
}
|
|
14931
|
-
if (node.type
|
|
14932
|
-
return
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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.
|
|
24044
|
+
version: "16.3.0"
|
|
23974
24045
|
};
|
|
23975
24046
|
var APPLICATION_ONLY_RULES = [];
|
|
23976
24047
|
var LIBRARY_IMPORT_POLICY = ["error", {
|