@truenine/eslint9-config
Version:
ESLint 9 configuration package for Compose Client projects with TypeScript, Vue, and modern JavaScript support
1 lines • 6.12 kB
Source Map (JSON)
{"version":3,"file":"prefer-void-zero.mjs","names":[],"sources":["../../../src/rules/code-style/prefer-void-zero.ts"],"sourcesContent":["import type {Rule} from 'eslint'\n\n/**\n * ESLint rule: prefer-void-zero\n *\n * Detects and fixes `undefined` to `void 0` in value positions.\n * Only fixes value-level `undefined`, NOT type annotations.\n */\nconst directTypeContexts = new Set(['TSTypeAnnotation', 'TSTypeAliasDeclaration', 'TSInterfaceDeclaration', 'TSTypeParameterDeclaration', 'TSTypeParameterInstantiation', 'TSTypeLiteral', 'TSPropertySignature', 'TSMethodSignature', 'TSIndexSignature', 'TSFunctionType', 'TSConstructorType', 'TSMappedType', 'TSConditionalType', 'TSInferType', 'TSTypeQuery', 'TSTypePredicate'])\nconst importExportTypes = new Set(['ImportSpecifier', 'ImportDefaultSpecifier', 'ImportNamespaceSpecifier', 'ExportSpecifier'])\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {description: 'Prefer `void 0` over `undefined` in value positions', recommended: false},\n fixable: 'code',\n schema: [],\n messages: {preferVoidZero: 'Use `void 0` instead of `undefined`'}\n },\n create(context) {\n function isInTypeContext(node: Rule.Node): boolean { /* Check if the node is in a type annotation context */\n let current: Rule.Node | null = node\n while (current) {\n const {parent} = current as Rule.Node & {parent?: Rule.Node}\n if (parent === void 0 || parent === null) break\n const parentType = parent.type as string /* 使用 string 类型来绕过 TypeScript 的类型检查 */\n\n if (directTypeContexts.has(parentType)) return true\n if (parentType === 'TSUnionType' || parentType === 'TSIntersectionType') return true /* Union/Intersection types */\n if (parentType === 'TSUndefinedKeyword') return true /* TSUndefinedKeyword is always a type */\n\n if (parentType === 'TSAsExpression') { /* TSAsExpression - check if we're in the type part */\n const asExpr = parent as Rule.Node & {typeAnnotation: Rule.Node}\n if (asExpr.typeAnnotation === current) return true\n }\n\n if (parentType === 'TSSatisfiesExpression') { /* TSSatisfiesExpression - check if we're in the type part */\n const satisfiesExpr = parent as Rule.Node & {typeAnnotation: Rule.Node}\n if (satisfiesExpr.typeAnnotation === current) return true\n }\n if (parentType === 'TSTypeReference') return true /* TSTypeReference - this is a type context */\n current = parent\n }\n return false\n }\n\n function isUndefinedValue(node: Rule.Node): boolean { /* Check if this is the global `undefined` identifier being used as a value */\n if (node.type !== 'Identifier') return false\n const id = node as Rule.Node & {name: string}\n if (id.name !== 'undefined') return false\n\n if (isInTypeContext(node)) return false /* Skip if in type context */\n\n const {parent} = node /* Skip if it's a property key (not value) */\n if (parent?.type === 'Property') {\n const prop = parent as Rule.Node & {key: Rule.Node, shorthand: boolean}\n if (prop.key === node && !prop.shorthand) return false\n }\n if (parent?.type === 'AssignmentExpression') { /* Skip if it's being declared/assigned to */\n const assign = parent as Rule.Node & {left: Rule.Node}\n if (assign.left === node) return false\n }\n if (parent?.type === 'VariableDeclarator') { /* Skip if it's a variable declarator id */\n const decl = parent as Rule.Node & {id: Rule.Node}\n if (decl.id === node) return false\n }\n if (['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression'].includes(parent?.type ?? '')) { /* Skip if it's a function parameter name */\n const fn = parent as Rule.Node & {params: Rule.Node[]}\n if (fn.params.includes(node)) return false\n }\n if (parent?.type && importExportTypes.has(parent.type as string)) return false /* Skip if it's an import/export specifier */\n if (parent?.type !== 'MemberExpression') return true /* Skip member expression property (obj.undefined) */\n\n const member = parent as Rule.Node & {property: Rule.Node, computed: boolean}\n if (member.property === node && !member.computed) return false\n return true\n }\n\n return {\n Identifier(node) {\n if (!isUndefinedValue(node)) return\n context.report({node, messageId: 'preferVoidZero', fix: fixer => fixer.replaceText(node, 'void 0')})\n }\n }\n }\n}\n\nexport default rule\n"],"mappings":";;;;;;;AAQA,MAAM,qBAAqB,IAAI,IAAI;CAAC;CAAoB;CAA0B;CAA0B;CAA8B;CAAgC;CAAiB;CAAuB;CAAqB;CAAoB;CAAkB;CAAqB;CAAgB;CAAqB;CAAe;CAAe;CAAkB,CAAC;AACxX,MAAM,oBAAoB,IAAI,IAAI;CAAC;CAAmB;CAA0B;CAA4B;CAAkB,CAAC;AAE/H,MAAM,OAAwB;CAC5B,MAAM;EACJ,MAAM;EACN,MAAM;GAAC,aAAa;GAAuD,aAAa;GAAM;EAC9F,SAAS;EACT,QAAQ,EAAE;EACV,UAAU,EAAC,gBAAgB,uCAAsC;EAClE;CACD,OAAO,SAAS;EACd,SAAS,gBAAgB,MAA0B;GACjD,IAAI,UAA4B;AAChC,UAAO,SAAS;IACd,MAAM,EAAC,WAAU;AACjB,QAAI,WAAW,KAAK,KAAK,WAAW,KAAM;IAC1C,MAAM,aAAa,OAAO;AAE1B,QAAI,mBAAmB,IAAI,WAAW,CAAE,QAAO;AAC/C,QAAI,eAAe,iBAAiB,eAAe,qBAAsB,QAAO;AAChF,QAAI,eAAe,qBAAsB,QAAO;AAEhD,QAAI,eAAe,kBAEjB;SADe,OACJ,mBAAmB,QAAS,QAAO;;AAGhD,QAAI,eAAe,yBAEjB;SADsB,OACJ,mBAAmB,QAAS,QAAO;;AAEvD,QAAI,eAAe,kBAAmB,QAAO;AAC7C,cAAU;;AAEZ,UAAO;;EAGT,SAAS,iBAAiB,MAA0B;AAClD,OAAI,KAAK,SAAS,aAAc,QAAO;AAEvC,OADW,KACJ,SAAS,YAAa,QAAO;AAEpC,OAAI,gBAAgB,KAAK,CAAE,QAAO;GAElC,MAAM,EAAC,WAAU;AACjB,OAAI,QAAQ,SAAS,YAAY;IAC/B,MAAM,OAAO;AACb,QAAI,KAAK,QAAQ,QAAQ,CAAC,KAAK,UAAW,QAAO;;AAEnD,OAAI,QAAQ,SAAS,wBAEnB;QADe,OACJ,SAAS,KAAM,QAAO;;AAEnC,OAAI,QAAQ,SAAS,sBAEnB;QADa,OACJ,OAAO,KAAM,QAAO;;AAE/B,OAAI;IAAC;IAAuB;IAAsB;IAA0B,CAAC,SAAS,QAAQ,QAAQ,GAAG,EAEvG;QADW,OACJ,OAAO,SAAS,KAAK,CAAE,QAAO;;AAEvC,OAAI,QAAQ,QAAQ,kBAAkB,IAAI,OAAO,KAAe,CAAE,QAAO;AACzE,OAAI,QAAQ,SAAS,mBAAoB,QAAO;GAEhD,MAAM,SAAS;AACf,OAAI,OAAO,aAAa,QAAQ,CAAC,OAAO,SAAU,QAAO;AACzD,UAAO;;AAGT,SAAO,EACL,WAAW,MAAM;AACf,OAAI,CAAC,iBAAiB,KAAK,CAAE;AAC7B,WAAQ,OAAO;IAAC;IAAM,WAAW;IAAkB,MAAK,UAAS,MAAM,YAAY,MAAM,SAAS;IAAC,CAAC;KAEvG;;CAEJ"}