@truenine/eslint9-config
Version:
ESLint 9 configuration package for Compose Client projects with TypeScript, Vue, and modern JavaScript support
105 lines (104 loc) • 3 kB
JavaScript
//#region src/rules/code-style/prefer-void-zero.ts
/**
* ESLint rule: prefer-void-zero
*
* Detects and fixes `undefined` to `void 0` in value positions.
* Only fixes value-level `undefined`, NOT type annotations.
*/
const directTypeContexts = new Set([
"TSTypeAnnotation",
"TSTypeAliasDeclaration",
"TSInterfaceDeclaration",
"TSTypeParameterDeclaration",
"TSTypeParameterInstantiation",
"TSTypeLiteral",
"TSPropertySignature",
"TSMethodSignature",
"TSIndexSignature",
"TSFunctionType",
"TSConstructorType",
"TSMappedType",
"TSConditionalType",
"TSInferType",
"TSTypeQuery",
"TSTypePredicate"
]);
const importExportTypes = new Set([
"ImportSpecifier",
"ImportDefaultSpecifier",
"ImportNamespaceSpecifier",
"ExportSpecifier"
]);
const rule = {
meta: {
type: "suggestion",
docs: {
description: "Prefer `void 0` over `undefined` in value positions",
recommended: false
},
fixable: "code",
schema: [],
messages: { preferVoidZero: "Use `void 0` instead of `undefined`" }
},
create(context) {
function isInTypeContext(node) {
let current = node;
while (current) {
const { parent } = current;
if (parent === void 0 || parent === null) break;
const parentType = parent.type;
if (directTypeContexts.has(parentType)) return true;
if (parentType === "TSUnionType" || parentType === "TSIntersectionType") return true;
if (parentType === "TSUndefinedKeyword") return true;
if (parentType === "TSAsExpression") {
if (parent.typeAnnotation === current) return true;
}
if (parentType === "TSSatisfiesExpression") {
if (parent.typeAnnotation === current) return true;
}
if (parentType === "TSTypeReference") return true;
current = parent;
}
return false;
}
function isUndefinedValue(node) {
if (node.type !== "Identifier") return false;
if (node.name !== "undefined") return false;
if (isInTypeContext(node)) return false;
const { parent } = node;
if (parent?.type === "Property") {
const prop = parent;
if (prop.key === node && !prop.shorthand) return false;
}
if (parent?.type === "AssignmentExpression") {
if (parent.left === node) return false;
}
if (parent?.type === "VariableDeclarator") {
if (parent.id === node) return false;
}
if ([
"FunctionDeclaration",
"FunctionExpression",
"ArrowFunctionExpression"
].includes(parent?.type ?? "")) {
if (parent.params.includes(node)) return false;
}
if (parent?.type && importExportTypes.has(parent.type)) return false;
if (parent?.type !== "MemberExpression") return true;
const member = parent;
if (member.property === node && !member.computed) return false;
return true;
}
return { Identifier(node) {
if (!isUndefinedValue(node)) return;
context.report({
node,
messageId: "preferVoidZero",
fix: (fixer) => fixer.replaceText(node, "void 0")
});
} };
}
};
//#endregion
export { rule as default };
//# sourceMappingURL=prefer-void-zero.mjs.map