eslint-plugin-conventions 6.0.1 → 6.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-conventions",
3
- "version": "6.0.1",
3
+ "version": "6.0.3",
4
4
  "description": "ESLint plugin for team code conventions — enforces filename case, magic-number bans, commented-out code, expiring TODOs, and deprecated-API usage.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -50,7 +50,7 @@
50
50
  "node": ">=18.0.0"
51
51
  },
52
52
  "dependencies": {
53
- "@interlace/eslint-devkit": "^1.19.1"
53
+ "@interlace/eslint-devkit": "^1.19.5"
54
54
  },
55
55
  "repository": {
56
56
  "type": "git",
package/src/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.rules={get"no-commented-code"(){return require("./rules/conventions/no-commented-code").noCommentedCode},get"expiring-todo-comments"(){return require("./rules/conventions/expiring-todo-comments").expiringTodoComments},get"prefer-code-point"(){return require("./rules/conventions/prefer-code-point").preferCodePoint},get"prefer-dom-node-text-content"(){return require("./rules/conventions/prefer-dom-node-text-content").preferDomNodeTextContent},get"no-console-spaces"(){return require("./rules/conventions/no-console-spaces").noConsoleSpaces},get"no-deprecated-api"(){return require("./rules/deprecation/no-deprecated-api").noDeprecatedApi},get"prefer-dependency-version-strategy"(){return require("./rules/conventions/prefer-dependency-version-strategy").preferDependencyVersionStrategy},get"filename-case"(){return require("./rules/conventions/filename-case").filenameCase},get"consistent-existence-index-check"(){return require("./rules/conventions/consistent-existence-index-check").consistentExistenceIndexCheck},get"no-json-schema-tags"(){return require("./rules/conventions/no-json-schema-tags").noJsonSchemaTags},get"require-data-testid"(){return require("./rules/conventions/require-data-testid").requireDataTestId},get"utm-taxonomy"(){return require("./rules/conventions/utm-taxonomy").utmTaxonomy},get"no-raw-cross-property-href"(){return require("./rules/conventions/no-raw-cross-property-href").noRawCrossPropertyHref},get"analytics-event-naming"(){return require("./rules/conventions/analytics-event-naming").analyticsEventNaming},get"no-magic-numbers"(){return require("./rules/conventions/no-magic-numbers").noMagicNumbers}};(0,eslint_devkit_1.withCanonicalDocsUrls)("plugin-conventions",exports.rules);exports.plugin={meta:{name:"eslint-plugin-conventions",version:"6.0.1"},rules:exports.rules};exports.configs={recommended:{plugins:{conventions:exports.plugin},rules:{"conventions/no-commented-code":"warn","conventions/expiring-todo-comments":"warn","conventions/no-deprecated-api":"warn","conventions/utm-taxonomy":"error","conventions/no-raw-cross-property-href":"error","conventions/analytics-event-naming":"error"}}};exports.default=exports.plugin;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.rules={get"no-commented-code"(){return require("./rules/conventions/no-commented-code").noCommentedCode},get"expiring-todo-comments"(){return require("./rules/conventions/expiring-todo-comments").expiringTodoComments},get"prefer-code-point"(){return require("./rules/conventions/prefer-code-point").preferCodePoint},get"prefer-dom-node-text-content"(){return require("./rules/conventions/prefer-dom-node-text-content").preferDomNodeTextContent},get"no-console-spaces"(){return require("./rules/conventions/no-console-spaces").noConsoleSpaces},get"no-deprecated-api"(){return require("./rules/deprecation/no-deprecated-api").noDeprecatedApi},get"prefer-dependency-version-strategy"(){return require("./rules/conventions/prefer-dependency-version-strategy").preferDependencyVersionStrategy},get"filename-case"(){return require("./rules/conventions/filename-case").filenameCase},get"consistent-existence-index-check"(){return require("./rules/conventions/consistent-existence-index-check").consistentExistenceIndexCheck},get"no-json-schema-tags"(){return require("./rules/conventions/no-json-schema-tags").noJsonSchemaTags},get"require-data-testid"(){return require("./rules/conventions/require-data-testid").requireDataTestId},get"utm-taxonomy"(){return require("./rules/conventions/utm-taxonomy").utmTaxonomy},get"no-raw-cross-property-href"(){return require("./rules/conventions/no-raw-cross-property-href").noRawCrossPropertyHref},get"analytics-event-naming"(){return require("./rules/conventions/analytics-event-naming").analyticsEventNaming},get"no-magic-numbers"(){return require("./rules/conventions/no-magic-numbers").noMagicNumbers}};(0,eslint_devkit_1.withCanonicalDocsUrls)("plugin-conventions",exports.rules);exports.plugin={meta:{name:"eslint-plugin-conventions",version:"6.0.3"},rules:exports.rules};exports.configs={recommended:{plugins:{conventions:exports.plugin},rules:{"conventions/no-commented-code":"warn","conventions/expiring-todo-comments":"warn","conventions/no-deprecated-api":"warn","conventions/utm-taxonomy":"error","conventions/no-raw-cross-property-href":"error","conventions/analytics-event-naming":"error"}}};exports.default=exports.plugin;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.consistentExistenceIndexCheck=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.consistentExistenceIndexCheck=(0,eslint_devkit_1.createRule)({name:"consistent-existence-index-check",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/consistent-existence-index-check.md",description:"Enforce consistent style for checking object property existence"},fixable:"code",messages:{consistentExistenceCheck:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Inconsistent Property Check",description:"Use consistent method for property existence checks",severity:"MEDIUM",fix:'Use "{{preferred}}" instead of "{{current}}" for property checks',documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in"})},schema:[{type:"object",properties:{preferred:{type:"string",enum:["in","hasOwnProperty","Object.hasOwn"],default:"Object.hasOwn"}},additionalProperties:false}]},defaultOptions:[{preferred:"Object.hasOwn"}],create(context){const[options]=context.options;const{preferred="Object.hasOwn"}=options||{};function reportInconsistentCheck(node,currentMethod,object,property,surplusArguments=false){let fix;const crossesPrototypeBoundary=currentMethod==="in"!==(preferred==="in");const crossesDispatchBoundary=currentMethod==="hasOwnProperty"||preferred==="hasOwnProperty";const spliceWouldChangeArity=object.type==="SequenceExpression"||property.type==="SequenceExpression";const parent=node.parent;const isStandaloneExpression=!parent||parent.type==="ExpressionStatement"||parent.type==="VariableDeclarator"&&parent.init===node||parent.type==="AssignmentExpression"&&parent.right===node||parent.type==="ReturnStatement"&&parent.argument===node||parent.type==="ArrowFunctionExpression"&&parent.body===node||parent.type==="IfStatement"&&parent.test===node||parent.type==="WhileStatement"&&parent.test===node||parent.type==="DoWhileStatement"&&parent.test===node||parent.type==="ForStatement"&&parent.test===node||parent.type==="ConditionalExpression"&&parent.test===node;if(!crossesPrototypeBoundary&&!crossesDispatchBoundary&&!spliceWouldChangeArity&&!surplusArguments&&preferred==="Object.hasOwn"&&isStandaloneExpression){fix=function(fixer){const objectText=context.sourceCode.getText(object);const propertyText=context.sourceCode.getText(property);return fixer.replaceText(node,`Object.hasOwn(${objectText}, ${propertyText})`)}}context.report({node,messageId:"consistentExistenceCheck",data:{current:currentMethod,preferred},fix})}return{CallExpression(node){if(node.callee.type==="MemberExpression"&&(0,eslint_devkit_1.propertyName)(node.callee)==="hasOwnProperty"&&node.arguments.length===1&&preferred!=="hasOwnProperty"){reportInconsistentCheck(node,"hasOwnProperty",node.callee.object,node.arguments[0])}if(node.callee.type==="MemberExpression"&&node.callee.object.type==="MemberExpression"&&node.callee.object.object.type==="MemberExpression"&&node.callee.object.object.object.type==="Identifier"&&node.callee.object.object.object.name==="Object"&&(0,eslint_devkit_1.propertyName)(node.callee.object.object)==="prototype"&&(0,eslint_devkit_1.propertyName)(node.callee.object)==="hasOwnProperty"&&(0,eslint_devkit_1.propertyName)(node.callee)==="call"&&node.arguments.length>=2&&preferred!=="hasOwnProperty"){reportInconsistentCheck(node,"Object.prototype.hasOwnProperty.call",node.arguments[0],node.arguments[1],node.arguments.length>2)}if(node.callee.type==="MemberExpression"&&node.callee.object.type==="Identifier"&&node.callee.object.name==="Object"&&(0,eslint_devkit_1.propertyName)(node.callee)==="hasOwn"&&node.arguments.length===2&&preferred!=="Object.hasOwn"){reportInconsistentCheck(node,"Object.hasOwn",node.arguments[0],node.arguments[1])}},BinaryExpression(node){if(node.operator==="in"&&preferred!=="in"){reportInconsistentCheck(node,"in",node.right,node.left)}}}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.consistentExistenceIndexCheck=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.consistentExistenceIndexCheck=(0,eslint_devkit_1.createRule)({name:"consistent-existence-index-check",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/consistent-existence-index-check.md",description:"Enforce consistent style for checking object property existence"},fixable:"code",messages:{consistentExistenceCheck:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Inconsistent Property Check",description:"Use consistent method for property existence checks",severity:"MEDIUM",fix:'Use "{{preferred}}" instead of "{{current}}" for property checks',documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in"})},schema:[{type:"object",properties:{preferred:{type:"string",enum:["in","hasOwnProperty","Object.hasOwn"],default:"Object.hasOwn"}},additionalProperties:false}]},defaultOptions:[{preferred:"Object.hasOwn"}],create(context){const[options]=context.options;const{preferred="Object.hasOwn"}=options||{};function reportInconsistentCheck(node,currentMethod,object,property,surplusArguments=false){let fix;const crossesPrototypeBoundary=currentMethod==="in"!==(preferred==="in");const crossesDispatchBoundary=currentMethod==="hasOwnProperty"||preferred==="hasOwnProperty";const spliceWouldChangeArity=object.type==="SequenceExpression"||property.type==="SequenceExpression";if(!crossesPrototypeBoundary&&!crossesDispatchBoundary&&!spliceWouldChangeArity&&!surplusArguments&&preferred==="Object.hasOwn"){fix=function(fixer){const objectText=context.sourceCode.getText(object);const propertyText=context.sourceCode.getText(property);return fixer.replaceText(node,`Object.hasOwn(${objectText}, ${propertyText})`)}}context.report({node,messageId:"consistentExistenceCheck",data:{current:currentMethod,preferred},fix})}return{CallExpression(node){if(node.callee.type==="MemberExpression"&&(0,eslint_devkit_1.propertyName)(node.callee)==="hasOwnProperty"&&node.arguments.length===1&&preferred!=="hasOwnProperty"){reportInconsistentCheck(node,"hasOwnProperty",node.callee.object,node.arguments[0])}if(node.callee.type==="MemberExpression"&&node.callee.object.type==="MemberExpression"&&node.callee.object.object.type==="MemberExpression"&&node.callee.object.object.object.type==="Identifier"&&node.callee.object.object.object.name==="Object"&&(0,eslint_devkit_1.propertyName)(node.callee.object.object)==="prototype"&&(0,eslint_devkit_1.propertyName)(node.callee.object)==="hasOwnProperty"&&(0,eslint_devkit_1.propertyName)(node.callee)==="call"&&node.arguments.length>=2&&preferred!=="hasOwnProperty"){reportInconsistentCheck(node,"Object.prototype.hasOwnProperty.call",node.arguments[0],node.arguments[1],node.arguments.length>2)}if(node.callee.type==="MemberExpression"&&node.callee.object.type==="Identifier"&&node.callee.object.name==="Object"&&(0,eslint_devkit_1.propertyName)(node.callee)==="hasOwn"&&node.arguments.length===2&&preferred!=="Object.hasOwn"){reportInconsistentCheck(node,"Object.hasOwn",node.arguments[0],node.arguments[1])}},BinaryExpression(node){if(node.operator==="in"&&preferred!=="in"){reportInconsistentCheck(node,"in",node.right,node.left)}}}}});
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noConsoleSpaces=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.noConsoleSpaces=(0,eslint_devkit_1.createRule)({name:"no-console-spaces",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/no-console-spaces.md",description:"Prevent leading/trailing space between console.log parameters"},fixable:"code",messages:{noConsoleSpaces:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Console Spaces",description:"Remove leading/trailing spaces in console method parameters",severity:"MEDIUM",fix:"Remove spaces from console method arguments",documentationLink:"https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-console-spaces.md"})},schema:[]},defaultOptions:[],create(context){const consoleMethods=new Set(["log","debug","info","warn","error","table","trace","group","groupCollapsed"]);function isInAllowedContext(){return false}function isConsoleMethodCall(node){if(node.callee.type==="MemberExpression"&&node.callee.object.type==="Identifier"&&node.callee.object.name==="console"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(node.callee),consoleMethods)){return true}return false}function getConsoleMethodName(node){return(0,eslint_devkit_1.propertyName)(node.callee)}function hasLeadingOrTrailingSpaces(text){const trimmed=text.trim();return trimmed.length>0&&/^\s|\s$/.test(text)}function templateHasLeadingOrTrailingSpaces(node){const first=node.quasis[0].value.raw;const last=node.quasis[node.quasis.length-1].value.raw;return/^\s/.test(first)||/\s$/.test(last)}return{CallExpression(node){if(isConsoleMethodCall(node)&&!isInAllowedContext()){for(const arg of node.arguments){const staticText=arg.type==="TemplateLiteral"?null:(0,eslint_devkit_1.staticString)(arg);if(staticText!==null){if(hasLeadingOrTrailingSpaces(staticText)){context.report({node:arg,messageId:"noConsoleSpaces",data:{method:getConsoleMethodName(node),arg:staticText},fix(fixer){const trimmed=staticText.trim();return fixer.replaceText(arg,`'${trimmed}'`)}})}}else if(arg.type==="TemplateLiteral"){if(templateHasLeadingOrTrailingSpaces(arg)){context.report({node:arg,messageId:"noConsoleSpaces",data:{method:getConsoleMethodName(node),arg:"template literal with spaces"},fix:null})}}}}}}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noConsoleSpaces=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.noConsoleSpaces=(0,eslint_devkit_1.createRule)({name:"no-console-spaces",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/no-console-spaces.md",description:"Prevent leading/trailing space between console.log parameters"},fixable:"code",messages:{noConsoleSpaces:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Console Spaces",description:"Remove leading/trailing spaces in console method parameters",severity:"MEDIUM",fix:"Remove spaces from console method arguments",documentationLink:"https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-console-spaces.md"})},schema:[]},defaultOptions:[],create(context){const consoleMethods=new Set(["log","debug","info","warn","error","table","trace","group","groupCollapsed"]);function isInAllowedContext(){return false}function isConsoleMethodCall(node){if(node.callee.type==="MemberExpression"&&node.callee.object.type==="Identifier"&&node.callee.object.name==="console"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(node.callee),consoleMethods)){return true}return false}function getConsoleMethodName(node){return(0,eslint_devkit_1.propertyName)(node.callee)}function quoteSingle(text){const escaped=text.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029");return`'${escaped}'`}function hasLeadingOrTrailingSpaces(text){const trimmed=text.trim();return trimmed.length>0&&/^\s|\s$/.test(text)}function templateHasLeadingOrTrailingSpaces(node){const first=node.quasis[0].value.raw;const last=node.quasis[node.quasis.length-1].value.raw;return/^\s/.test(first)||/\s$/.test(last)}return{CallExpression(node){if(isConsoleMethodCall(node)&&!isInAllowedContext()){for(const arg of node.arguments){const staticText=arg.type==="TemplateLiteral"?null:(0,eslint_devkit_1.staticString)(arg);if(staticText!==null){if(hasLeadingOrTrailingSpaces(staticText)){context.report({node:arg,messageId:"noConsoleSpaces",data:{method:getConsoleMethodName(node),arg:staticText},fix(fixer){const trimmed=staticText.trim();return fixer.replaceText(arg,quoteSingle(trimmed))}})}}else if(arg.type==="TemplateLiteral"){if(templateHasLeadingOrTrailingSpaces(arg)){context.report({node:arg,messageId:"noConsoleSpaces",data:{method:getConsoleMethodName(node),arg:"template literal with spaces"},fix:null})}}}}}}}});
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noMagicNumbers=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const DEFAULT_IGNORE=new Set([-1,0,1,2]);function constNameFor(value){const prefix=value<0?"MAGIC_NEG_":"MAGIC_";const digits=String(Math.abs(value)).toUpperCase().replaceAll(/[^0-9A-Z]+/g,"_").replace(/_+$/,"");return`${prefix}${digits}`}function nearestStatement(node){const STATEMENT_TYPES=new Set(["ExpressionStatement","VariableDeclaration","ReturnStatement","IfStatement","WhileStatement","ForStatement","ForInStatement","ForOfStatement","ThrowStatement","SwitchStatement"]);let current=node.parent;while(current){if(STATEMENT_TYPES.has(current.type))return current;current=current.parent}return null}exports.noMagicNumbers=(0,eslint_devkit_1.createRule)({name:"no-magic-numbers",skipGeneratedFiles:true,skipMinifiedFiles:true,meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/no-magic-numbers.md",description:"Disallow magic numbers (numeric literals without a named constant)"},hasSuggestions:true,messages:{noMagicNumber:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.INFO,issueName:"Magic Number",description:"The number {{value}} is a magic literal. Extract it to a named constant to make the intent clear.",severity:"LOW",fix:"const TIMEOUT_MS = {{value}}; // use the named constant everywhere",documentationLink:"https://refactoring.guru/replace-magic-literal"}),extractConst:"Extract {{value}} to a named constant ({{constName}})"},schema:[{type:"object",properties:{ignore:{type:"array",items:{type:"number"},description:"Additional numbers to allow"},ignoreArrayIndexes:{type:"boolean",default:true},detectObjects:{type:"boolean",default:false},ignoreLoopBounds:{type:"boolean",default:true},ignoreLengthComparisons:{type:"boolean",default:true},ignoreNumericArrays:{type:"boolean",default:true},namedConstArithmetic:{type:"boolean",default:true},ignoreDefaultValues:{type:"boolean",default:true},ignoreEnums:{type:"boolean",default:true},ignoreBitwiseExpressions:{type:"boolean",default:false}},additionalProperties:false}]},defaultOptions:[{ignore:[],ignoreArrayIndexes:true,detectObjects:false,ignoreLoopBounds:true,ignoreLengthComparisons:true,ignoreNumericArrays:true,namedConstArithmetic:true,ignoreDefaultValues:true,ignoreEnums:true,ignoreBitwiseExpressions:false}],create(context){const[options={}]=context.options;const{ignore=[],ignoreArrayIndexes=true,detectObjects=false,ignoreLoopBounds=true,ignoreLengthComparisons=true,ignoreNumericArrays=true,namedConstArithmetic=true,ignoreDefaultValues=true,ignoreEnums=true,ignoreBitwiseExpressions=false}=options;const ignoredValues=new Set([...DEFAULT_IGNORE,...ignore]);function isIgnoredNumber(value){return ignoredValues.has(value)}const MAX_ARRAY_INDEX=2**32-2;function isArrayIndex(node){if(!ignoreArrayIndexes)return false;const parent=node.parent;const inIndexPosition=parent?.type==="MemberExpression"&&parent.computed&&parent.property===node;if(!inIndexPosition)return false;const value=node.value;return typeof value==="number"&&Number.isInteger(value)&&value>=0&&value<=MAX_ARRAY_INDEX}function isObjectPropertyValue(node){if(detectObjects)return false;const parent=node.parent;return parent?.type==="Property"&&parent.value===node}function isLoopBound(node){if(!ignoreLoopBounds)return false;let current=node;let parent=current.parent;while(parent){if(parent.type==="ForStatement"){const loop=parent;return loop.test===current||loop.update===current||loop.init===current}if(parent.type==="BlockStatement"||parent.type==="Program")return false;current=parent;parent=current.parent}return false}function isLengthComparison(node){if(!ignoreLengthComparisons)return false;const parent=node.parent;if(parent?.type!=="BinaryExpression")return false;const comparison=parent;const EQUALITY=new Set(["===","!==","==","!="]);if(!EQUALITY.has(comparison.operator))return false;const other=comparison.left===node?comparison.right:comparison.left;return other.type==="MemberExpression"&&(0,eslint_devkit_1.propertyName)(other)==="length"}function isDefaultValue(node){if(!ignoreDefaultValues)return false;const parent=node.parent;return parent?.type==="AssignmentPattern"&&parent.right===node}function isEnumMember(node){if(!ignoreEnums)return false;const parent=node.parent;return parent?.type==="TSEnumMember"}function isBitwiseContext(node){if(!ignoreBitwiseExpressions)return false;const parent=node.parent;const BITWISE_OPS=new Set(["&","|","^","<<",">>",">>>"]);return parent?.type==="BinaryExpression"&&BITWISE_OPS.has(parent.operator)}const allLiteralOperands=node=>{if(!node||typeof node!=="object")return false;if(node.type==="Literal")return typeof node.value==="number";if(node.type==="UnaryExpression")return allLiteralOperands(node.argument);if(node.type==="TSAsExpression"||node.type==="TSSatisfiesExpression"){return allLiteralOperands(node.expression)}if(node.type==="BinaryExpression"){return allLiteralOperands(node.left)&&allLiteralOperands(node.right)}return false};function isVariableDeclarator(node){let current=node;let parent=current.parent;while(parent&&(parent.type==="TSAsExpression"||parent.type==="TSSatisfiesExpression"||parent.type==="UnaryExpression"||parent.type==="BinaryExpression"&&namedConstArithmetic&&allLiteralOperands(parent))){current=parent;parent=current.parent}return parent?.type==="VariableDeclarator"&&parent.id.type==="Identifier"}function isNumericDataArray(node){if(!ignoreNumericArrays)return false;const parent=node.parent;const array=parent?.type==="ArrayExpression"?parent:parent?.type==="UnaryExpression"&&parent.parent?.type==="ArrayExpression"?parent.parent:null;if(array===null)return false;const numeric=element=>{if(element===null)return false;if(element.type==="Literal")return typeof element.value==="number";if(element.type==="UnaryExpression")return numeric(element.argument);return false};return array.elements.length>=2&&array.elements.every(numeric)}function isExportedConst(node){let current=node;while(current){if(current.type==="ExportNamedDeclaration")return true;if(current.type==="VariableDeclaration"||current.type==="VariableDeclarator"){current=current.parent;continue}break}return false}function isPropertyKey(node){const parent=node.parent;return parent?.type==="Property"&&parent.key===node}return{Literal(node){if(typeof node.value!=="number")return;const value=node.value;if(isIgnoredNumber(value))return;if(!isFinite(value))return;if(isVariableDeclarator(node))return;if(isExportedConst(node))return;if(isArrayIndex(node))return;if(isObjectPropertyValue(node))return;if(isLoopBound(node))return;if(isLengthComparison(node))return;if(isDefaultValue(node))return;if(isEnumMember(node))return;if(isBitwiseContext(node))return;if(isPropertyKey(node))return;if(isNumericDataArray(node))return;const constName=constNameFor(value);const sourceCode=context.sourceCode;context.report({node,messageId:"noMagicNumber",data:{value:String(value)},suggest:[{messageId:"extractConst",data:{value:String(value),constName},fix(fixer){const stmt=nearestStatement(node);if(!stmt)return null;const firstToken=sourceCode.getFirstToken(stmt);if(!firstToken)return null;const col=firstToken.loc.start.column;const indent=" ".repeat(col);return[fixer.insertTextBefore(stmt,`const ${constName} = ${value};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noMagicNumbers=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const DEFAULT_IGNORE=new Set([-1,0,1,2]);const STATEMENT_LIST_PARENTS=new Set(["BlockStatement","Program","SwitchCase","StaticBlock","TSModuleBlock"]);function isStatementListSlot(stmt){return STATEMENT_LIST_PARENTS.has(stmt.parent?.type??"")}function constNameFor(value){const prefix=value<0?"MAGIC_NEG_":"MAGIC_";const digits=String(Math.abs(value)).toUpperCase().replaceAll(/[^0-9A-Z]+/g,"_").replace(/_+$/,"");return`${prefix}${digits}`}function nearestStatement(node){const STATEMENT_TYPES=new Set(["ExpressionStatement","VariableDeclaration","ReturnStatement","IfStatement","WhileStatement","ForStatement","ForInStatement","ForOfStatement","ThrowStatement","SwitchStatement"]);let current=node.parent;while(current){if(STATEMENT_TYPES.has(current.type))return current;current=current.parent}return null}exports.noMagicNumbers=(0,eslint_devkit_1.createRule)({name:"no-magic-numbers",skipGeneratedFiles:true,skipMinifiedFiles:true,meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/no-magic-numbers.md",description:"Disallow magic numbers (numeric literals without a named constant)"},hasSuggestions:true,messages:{noMagicNumber:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.INFO,issueName:"Magic Number",description:"The number {{value}} is a magic literal. Extract it to a named constant to make the intent clear.",severity:"LOW",fix:"const TIMEOUT_MS = {{value}}; // use the named constant everywhere",documentationLink:"https://refactoring.guru/replace-magic-literal"}),extractConst:"Extract {{value}} to a named constant ({{constName}})"},schema:[{type:"object",properties:{ignore:{type:"array",items:{type:"number"},description:"Additional numbers to allow"},ignoreArrayIndexes:{type:"boolean",default:true},detectObjects:{type:"boolean",default:false},ignoreLoopBounds:{type:"boolean",default:true},ignoreLengthComparisons:{type:"boolean",default:true},ignoreNumericArrays:{type:"boolean",default:true},namedConstArithmetic:{type:"boolean",default:true},ignoreDefaultValues:{type:"boolean",default:true},ignoreEnums:{type:"boolean",default:true},ignoreBitwiseExpressions:{type:"boolean",default:false}},additionalProperties:false}]},defaultOptions:[{ignore:[],ignoreArrayIndexes:true,detectObjects:false,ignoreLoopBounds:true,ignoreLengthComparisons:true,ignoreNumericArrays:true,namedConstArithmetic:true,ignoreDefaultValues:true,ignoreEnums:true,ignoreBitwiseExpressions:false}],create(context){const[options={}]=context.options;const{ignore=[],ignoreArrayIndexes=true,detectObjects=false,ignoreLoopBounds=true,ignoreLengthComparisons=true,ignoreNumericArrays=true,namedConstArithmetic=true,ignoreDefaultValues=true,ignoreEnums=true,ignoreBitwiseExpressions=false}=options;const ignoredValues=new Set([...DEFAULT_IGNORE,...ignore]);function isIgnoredNumber(value){return ignoredValues.has(value)}const MAX_ARRAY_INDEX=2**32-2;function isArrayIndex(node){if(!ignoreArrayIndexes)return false;const parent=node.parent;const inIndexPosition=parent?.type==="MemberExpression"&&parent.computed&&parent.property===node;if(!inIndexPosition)return false;const value=node.value;return typeof value==="number"&&Number.isInteger(value)&&value>=0&&value<=MAX_ARRAY_INDEX}function isObjectPropertyValue(node){if(detectObjects)return false;const parent=node.parent;return parent?.type==="Property"&&parent.value===node}function isLoopBound(node){if(!ignoreLoopBounds)return false;let current=node;let parent=current.parent;while(parent){if(parent.type==="ForStatement"){const loop=parent;return loop.test===current||loop.update===current||loop.init===current}if(parent.type==="BlockStatement"||parent.type==="Program")return false;current=parent;parent=current.parent}return false}function isLengthComparison(node){if(!ignoreLengthComparisons)return false;const parent=node.parent;if(parent?.type!=="BinaryExpression")return false;const comparison=parent;const EQUALITY=new Set(["===","!==","==","!="]);if(!EQUALITY.has(comparison.operator))return false;const other=comparison.left===node?comparison.right:comparison.left;return other.type==="MemberExpression"&&(0,eslint_devkit_1.propertyName)(other)==="length"}function isDefaultValue(node){if(!ignoreDefaultValues)return false;const parent=node.parent;return parent?.type==="AssignmentPattern"&&parent.right===node}function isEnumMember(node){if(!ignoreEnums)return false;const parent=node.parent;return parent?.type==="TSEnumMember"}function isBitwiseContext(node){if(!ignoreBitwiseExpressions)return false;const parent=node.parent;const BITWISE_OPS=new Set(["&","|","^","<<",">>",">>>"]);return parent?.type==="BinaryExpression"&&BITWISE_OPS.has(parent.operator)}const allLiteralOperands=node=>{if(!node||typeof node!=="object")return false;if(node.type==="Literal")return typeof node.value==="number";if(node.type==="UnaryExpression")return allLiteralOperands(node.argument);if(node.type==="TSAsExpression"||node.type==="TSSatisfiesExpression"){return allLiteralOperands(node.expression)}if(node.type==="BinaryExpression"){return allLiteralOperands(node.left)&&allLiteralOperands(node.right)}return false};function isVariableDeclarator(node){let current=node;let parent=current.parent;while(parent&&(parent.type==="TSAsExpression"||parent.type==="TSSatisfiesExpression"||parent.type==="UnaryExpression"||parent.type==="BinaryExpression"&&namedConstArithmetic&&allLiteralOperands(parent))){current=parent;parent=current.parent}return parent?.type==="VariableDeclarator"&&parent.id.type==="Identifier"}function isNumericDataArray(node){if(!ignoreNumericArrays)return false;const parent=node.parent;const array=parent?.type==="ArrayExpression"?parent:parent?.type==="UnaryExpression"&&parent.parent?.type==="ArrayExpression"?parent.parent:null;if(array===null)return false;const numeric=element=>{if(element===null)return false;if(element.type==="Literal")return typeof element.value==="number";if(element.type==="UnaryExpression")return numeric(element.argument);return false};return array.elements.length>=2&&array.elements.every(numeric)}function isExportedConst(node){let current=node;while(current){if(current.type==="ExportNamedDeclaration")return true;if(current.type==="VariableDeclaration"||current.type==="VariableDeclarator"){current=current.parent;continue}break}return false}function isPropertyKey(node){const parent=node.parent;return parent?.type==="Property"&&parent.key===node}return{Literal(node){if(typeof node.value!=="number")return;const value=node.value;if(isIgnoredNumber(value))return;if(!isFinite(value))return;if(isVariableDeclarator(node))return;if(isExportedConst(node))return;if(isArrayIndex(node))return;if(isObjectPropertyValue(node))return;if(isLoopBound(node))return;if(isLengthComparison(node))return;if(isDefaultValue(node))return;if(isEnumMember(node))return;if(isBitwiseContext(node))return;if(isPropertyKey(node))return;if(isNumericDataArray(node))return;const constName=constNameFor(value);context.report({node,messageId:"noMagicNumber",data:{value:String(value)},suggest:[{messageId:"extractConst",data:{value:String(value),constName},fix(fixer){const stmt=nearestStatement(node);if(!stmt)return null;if(!isStatementListSlot(stmt))return null;const indent=" ".repeat(stmt.loc.start.column);return[fixer.insertTextBefore(stmt,`const ${constName} = ${value};
2
2
  ${indent}`),fixer.replaceText(node,constName)]}}]})}}}});
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.preferDependencyVersionStrategy=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const VERSION_SPECIFIER=/^(?:[\^~<>=]*\s*\d|[*x]$|latest$|next$|(?:workspace|file|link|npm|git\+[a-z]+|github|https?):)/i;exports.preferDependencyVersionStrategy=(0,eslint_devkit_2.createRule)({name:"prefer-dependency-version-strategy",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/prefer-dependency-version-strategy.md",description:"Enforce consistent version strategy (caret, tilde, exact, etc.) for package.json dependencies"},fixable:"code",messages:{preferStrategy:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.PACKAGE,issueName:"Dependency Version Strategy",description:'Dependency "{{name}}" should use {{strategy}} version',severity:"MEDIUM",fix:'Change "{{current}}" to "{{expected}}" for version flexibility',documentationLink:"https://docs.npmjs.com/cli/v10/configuring-npm/package-json#dependencies"}),invalidStrategy:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Invalid Version Strategy",description:'Strategy "{{strategy}}" is not valid',severity:"MEDIUM",fix:"Use one of: caret (^), tilde (~), exact (no prefix), range (<, >, ||), or any",documentationLink:"https://docs.npmjs.com/cli/v10/configuring-npm/package-json#dependencies"})},schema:[{type:"object",properties:{strategy:{type:"string",enum:["caret","tilde","exact","range","any"],description:"Version strategy to enforce: caret (^), tilde (~), exact (no prefix), range (allows <, >, ||), or any (allows all)"},allowWorkspace:{type:"boolean",description:"Allow workspace: protocol versions",default:true},allowFile:{type:"boolean",description:"Allow file: protocol versions",default:true},allowLink:{type:"boolean",description:"Allow link: protocol versions",default:true},overrides:{type:"object",additionalProperties:{type:"string",enum:["caret","tilde","exact","range","any"]},description:'Package-specific strategy overrides. Key is package name, value is strategy. Example: { "react": "exact", "lodash": "tilde" }'}},additionalProperties:false}]},defaultOptions:[{strategy:"caret",allowWorkspace:true,allowFile:true,allowLink:true,overrides:{}}],create(context){const options=context.options[0]||{};const{strategy="caret",allowWorkspace=true,allowFile=true,allowLink=true,overrides={}}=options;const validStrategies=["caret","tilde","exact","range","any"];if(!validStrategies.includes(strategy)){context.report({loc:{line:1,column:0},messageId:"invalidStrategy",data:{strategy}});return{}}if(strategy==="any"){return{}}function checkVersion(node,depName,version){if(allowWorkspace&&version.startsWith("workspace:"))return;if(allowFile&&version.startsWith("file:"))return;if(allowLink&&version.startsWith("link:"))return;if(!version.match(/^[\^~<>=]?\d+\.\d+\.\d+/))return;const packageStrategy=overrides[depName]||strategy;if(packageStrategy==="any")return;let expectedVersion=version;let needsFix=false;const baseVersion=version.replace(/^[\^~<>=]+/,"");switch(packageStrategy){case"caret":if(!version.startsWith("^")){expectedVersion=`^${baseVersion}`;needsFix=true}break;case"tilde":if(!version.startsWith("~")){expectedVersion=`~${baseVersion}`;needsFix=true}break;case"exact":{if(version!==baseVersion){expectedVersion=baseVersion;needsFix=true}break}case"range":if(!version.includes("||")&&!version.includes(">=")&&!version.includes("<=")&&!version.includes(">")&&!version.includes("<")&&!version.includes(" - ")){expectedVersion=`^${baseVersion}`;needsFix=true}break}if(needsFix){context.report({node:node.value,messageId:"preferStrategy",data:{name:depName,strategy:packageStrategy,current:version,expected:expectedVersion},fix(fixer){return fixer.replaceText(node.value,JSON.stringify(expectedVersion))}})}}const checkObjectExpression=node=>{for(const prop of node.properties){if(prop.type==="Property"&&prop.key&&prop.value&&prop.value.type==="Literal"){const depName=prop.key.type==="Identifier"?prop.key.name:prop.key.type==="Literal"?String(prop.key.value):null;if(depName&&typeof prop.value.value==="string"){checkVersion(prop,depName,prop.value.value)}}}};return{'Property[key.value="dependencies"], Property[key.value="devDependencies"], Property[key.value="peerDependencies"]'(node){if(node.value.type!=="ObjectExpression")return;checkObjectExpression(node.value)},ObjectExpression(node){let sawSpecifier=false;for(const prop of node.properties){if(prop.type!=="Property")continue;if(prop.value.type!=="Literal"||typeof prop.value.value!=="string"||!VERSION_SPECIFIER.test(prop.value.value)){return}sawSpecifier=true}if(sawSpecifier){checkObjectExpression(node)}}}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.preferDependencyVersionStrategy=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const VERSION_SPECIFIER=/^(?:[\^~<>=]*\s*\d|[*x]$|latest$|next$|(?:workspace|file|link|npm|git\+[a-z]+|github|https?):)/i;const MANIFEST_FIELDS=new Set(["name","version","description","main","module","types","typings","license","author","homepage","repository","keywords","files","type","private","man","sideEffects","publishConfig"]);const NON_DEPENDENCY_BLOCKS=new Set(["dist-tags","versions","engines","scripts","exports","imports","bin","browser"]);exports.preferDependencyVersionStrategy=(0,eslint_devkit_2.createRule)({name:"prefer-dependency-version-strategy",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/prefer-dependency-version-strategy.md",description:"Enforce consistent version strategy (caret, tilde, exact, etc.) for package.json dependencies"},fixable:"code",messages:{preferStrategy:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.PACKAGE,issueName:"Dependency Version Strategy",description:'Dependency "{{name}}" should use {{strategy}} version',severity:"MEDIUM",fix:'Change "{{current}}" to "{{expected}}" for version flexibility',documentationLink:"https://docs.npmjs.com/cli/v10/configuring-npm/package-json#dependencies"}),invalidStrategy:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Invalid Version Strategy",description:'Strategy "{{strategy}}" is not valid',severity:"MEDIUM",fix:"Use one of: caret (^), tilde (~), exact (no prefix), range (<, >, ||), or any",documentationLink:"https://docs.npmjs.com/cli/v10/configuring-npm/package-json#dependencies"})},schema:[{type:"object",properties:{strategy:{type:"string",enum:["caret","tilde","exact","range","any"],description:"Version strategy to enforce: caret (^), tilde (~), exact (no prefix), range (allows <, >, ||), or any (allows all)"},allowWorkspace:{type:"boolean",description:"Allow workspace: protocol versions",default:true},allowFile:{type:"boolean",description:"Allow file: protocol versions",default:true},allowLink:{type:"boolean",description:"Allow link: protocol versions",default:true},overrides:{type:"object",additionalProperties:{type:"string",enum:["caret","tilde","exact","range","any"]},description:'Package-specific strategy overrides. Key is package name, value is strategy. Example: { "react": "exact", "lodash": "tilde" }'}},additionalProperties:false}]},defaultOptions:[{strategy:"caret",allowWorkspace:true,allowFile:true,allowLink:true,overrides:{}}],create(context){const options=context.options[0]||{};const{strategy="caret",allowWorkspace=true,allowFile=true,allowLink=true,overrides={}}=options;const validStrategies=["caret","tilde","exact","range","any"];if(!validStrategies.includes(strategy)){context.report({loc:{line:1,column:0},messageId:"invalidStrategy",data:{strategy}});return{}}if(strategy==="any"){return{}}function checkVersion(node,depName,version){if(allowWorkspace&&version.startsWith("workspace:"))return;if(allowFile&&version.startsWith("file:"))return;if(allowLink&&version.startsWith("link:"))return;if(!version.match(/^[\^~<>=]?\d+\.\d+\.\d+/))return;const packageStrategy=overrides[depName]||strategy;if(packageStrategy==="any")return;let expectedVersion=version;let needsFix=false;const isRangeSpecifier=version.includes("||")||version.includes(">")||version.includes("<")||version.includes(" - ");if(isRangeSpecifier&&packageStrategy!=="range")return;const baseVersion=version.replace(/^[\^~<>=]+/,"");switch(packageStrategy){case"caret":if(!version.startsWith("^")){expectedVersion=`^${baseVersion}`;needsFix=true}break;case"tilde":if(!version.startsWith("~")){expectedVersion=`~${baseVersion}`;needsFix=true}break;case"exact":{if(version!==baseVersion){expectedVersion=baseVersion;needsFix=true}break}case"range":if(!version.includes("||")&&!version.includes(">=")&&!version.includes("<=")&&!version.includes(">")&&!version.includes("<")&&!version.includes(" - ")){expectedVersion=`^${baseVersion}`;needsFix=true}break}if(needsFix){context.report({node:node.value,messageId:"preferStrategy",data:{name:depName,strategy:packageStrategy,current:version,expected:expectedVersion},fix(fixer){return fixer.replaceText(node.value,JSON.stringify(expectedVersion))}})}}const checkObjectExpression=node=>{for(const prop of node.properties){if(prop.type==="Property"&&prop.key&&prop.value&&prop.value.type==="Literal"){const depName=prop.key.type==="Identifier"?prop.key.name:prop.key.type==="Literal"?String(prop.key.value):null;if(depName&&typeof prop.value.value==="string"){checkVersion(prop,depName,prop.value.value)}}}};return{'Property[key.value="dependencies"], Property[key.value="devDependencies"], Property[key.value="peerDependencies"]'(node){if(node.value.type!=="ObjectExpression")return;checkObjectExpression(node.value)},ObjectExpression(node){const parent=node.parent;if(parent?.type==="Property"&&NON_DEPENDENCY_BLOCKS.has((0,eslint_devkit_1.objectKeyName)(parent)??"")){return}let sawSpecifier=false;for(const prop of node.properties){if(prop.type!=="Property")continue;if(MANIFEST_FIELDS.has((0,eslint_devkit_1.objectKeyName)(prop)??""))return;if(prop.value.type!=="Literal"||typeof prop.value.value!=="string"||!VERSION_SPECIFIER.test(prop.value.value)){return}sawSpecifier=true}if(sawSpecifier){checkObjectExpression(node)}}}}});