eslint-plugin-code-style 2.2.0 → 2.2.1

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.
Files changed (2) hide show
  1. package/dist/index.js +2 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -55,7 +55,7 @@ ${t} }`)},message:"Destructured properties in array callback should each be o
55
55
  `+T),message:"First props type property must be on a new line when there are multiple properties",node:E})}if(l.length>1&&w){let E=l[l.length-1];w.loc.start.line===E.loc.end.line&&e.report({fix:L=>L.replaceTextRange([E.range[1],w.range[0]],`
56
56
  `+u),message:"Closing brace must be on its own line when there are multiple properties",node:w})}if(l.length===1&&y&&w){let E=l[0];if(y.loc.end.line!==w.loc.start.line){let L=n.getText(E);L=L.replace(/[,;]\s*$/,""),e.report({fix:C=>C.replaceTextRange([y.range[0],w.range[1]],`{ ${L} }`),message:"Single props type property should be on a single line",node:o})}}if(l.forEach((E,L)=>{let C=n.getText(E);if(E.type==="TSPropertySignature"&&E.optional){let g=n.getFirstToken(E),f=n.getTokenAfter(g);f&&f.value==="?"&&n.getText().slice(g.range[1],f.range[0])!==""&&e.report({fix:$=>$.replaceTextRange([g.range[1],f.range[0]],""),message:'No space allowed before "?" in optional property',node:E})}if(C.trimEnd().endsWith(";")&&e.report({fix:g=>{let f=C.lastIndexOf(";"),S=E.range[0]+f;return g.replaceTextRange([S,S+1],",")},message:"Props type properties must end with comma (,) not semicolon (;)",node:E}),l.length>1&&L>0){let g=l[L-1];E.loc.start.line===g.loc.end.line&&e.report({fix:f=>{let S=n.getTokenAfter(g);for(;S&&S.value!==","&&S.range[0]<E.range[0];)S=n.getTokenAfter(S);let $=S&&S.value===","?S.range[1]:g.range[1];return f.replaceTextRange([$,E.range[0]],`
57
57
  `+T)},message:"Each props type property must be on its own line when there are multiple properties",node:E}),E.loc.start.line-g.loc.end.line>1&&e.report({fix:f=>{let $=n.getText().slice(g.range[1],E.range[0]).replace(/\n\s*\n/g,`
58
- `);return f.replaceTextRange([g.range[1],E.range[0]],$)},message:"No empty lines allowed between props type properties",node:E})}}),l.length>1){let E=l[l.length-1];n.getText(E).trimEnd().endsWith(",")||e.report({fix:C=>C.insertTextAfter(E,","),message:"Last props type property must have trailing comma",node:E})}if(l.length===1){let E=l[0],L=n.getText(E);L.trimEnd().endsWith(",")&&e.report({fix:C=>{let g=L.lastIndexOf(","),f=E.range[0]+g;return C.removeRange([f,f+1])},message:"Single props type property should not have trailing comma",node:E})}}},r=d=>{if(!d.returnType||!d.returnType.typeAnnotation)return;let i=d.returnType,s=i.typeAnnotation;if(!v(d)&&s.type==="TSTypeLiteral"){e.report({message:"Function return type must be a type reference (interface, type, or built-in type), not an inline object type. Define the return type separately.",node:s});return}let o;if(d.params.length>0){let a=d.params[d.params.length-1];o=n.getTokenAfter(a,u=>u.value===")")}else{let u=n.getFirstToken(d);for(;u&&u.value!=="(";)u=n.getTokenAfter(u);u&&(o=n.getTokenAfter(u,T=>T.value===")"))}if(!o)return;let t=n.getFirstToken(i);if(!t||t.value!==":")return;let h=n.getFirstToken(s);if(!h)return;let p=n.getText().slice(o.range[1],t.range[0]),l=n.getText().slice(t.range[1],h.range[0]);(p!==""||l!==" ")&&e.report({fix:a=>a.replaceTextRange([o.range[1],h.range[0]],": "),message:'Return type annotation must have no space before colon and one space after: "): TypeName"',node:i})};return{ArrowFunctionExpression(d){x(d),r(d)},FunctionDeclaration(d){x(d),r(d)},FunctionExpression(d){x(d),r(d)}}},meta:{docs:{description:"Enforce inline type annotation for React component props and return types with proper formatting"},fixable:"code",schema:[],type:"suggestion"}},Xe={create(e){let n=d=>d.parent&&d.parent.type==="VariableDeclarator"&&d.parent.id&&d.parent.id.type==="Identifier"?d.parent.id.name:d.id&&d.id.type==="Identifier"?d.id.name:null,I=d=>d&&/^[A-Z]/.test(d),v=d=>d&&d.endsWith("Icon"),x=d=>{let i=d.body;if(!i)return!1;if(i.type==="JSXElement")return i.openingElement&&i.openingElement.name&&i.openingElement.name.name==="svg";if(i.type==="ParenthesizedExpression"&&i.expression&&i.expression.type==="JSXElement")return i.expression.openingElement&&i.expression.openingElement.name&&i.expression.openingElement.name.name==="svg";if(i.type==="BlockStatement"){let s=i.body.filter(c=>c.type==="ReturnStatement"&&c.argument);if(s.length===1){let c=s[0].argument;if(c.type==="JSXElement")return c.openingElement&&c.openingElement.name&&c.openingElement.name.name==="svg";if(c.type==="ParenthesizedExpression"&&c.expression&&c.expression.type==="JSXElement")return c.expression.openingElement&&c.expression.openingElement.name&&c.expression.openingElement.name.name==="svg"}}return!1},r=d=>{let i=n(d);if(!I(i))return;let s=x(d),c=v(i);s&&!c&&e.report({message:`Component "${i}" returns an SVG element and should end with "Icon" suffix (e.g., "${i}Icon")`,node:d.parent&&d.parent.type==="VariableDeclarator"?d.parent.id:d.id||d}),c&&!s&&e.report({message:`Component "${i}" has "Icon" suffix but doesn't return an SVG element. Either rename it or make it return an SVG.`,node:d.parent&&d.parent.type==="VariableDeclarator"?d.parent.id:d.id||d})};return{ArrowFunctionExpression:r,FunctionDeclaration:r,FunctionExpression:r}},meta:{docs:{description:"Enforce SVG components to have 'Icon' suffix and vice versa"},fixable:null,schema:[],type:"suggestion"}},ze={create(e){let I=(e.filename||e.getFilename()).replace(/\\/g,"/"),v=e.options[0]||{},x=v.chainOrder||"child-parent",r=v.files||[],i=(()=>{for(let f of r)if(f.paths.some(S=>I.includes(S)))return f.chainOrder;return x})(),s={atoms:"",components:"",constants:"Constants",contexts:"Context",data:"Data",layouts:"Layout",pages:"Page",providers:"Provider",reducers:"Reducer",schemas:"Schema",services:"Service",strings:"Strings",theme:"Theme",themes:"Theme",views:"View"},c=new Set(["atoms","components","layouts","pages","views"]),o=new Set(["constants","data","reducers","schemas","services","strings"]),t=f=>f.split("-").map(S=>S.charAt(0).toUpperCase()+S.slice(1)).join(""),h=f=>f.charAt(0).toLowerCase()+f.slice(1),p=Object.keys(s).join("|"),l=()=>{let f=new RegExp(`\\/(${p})\\/(.+)\\.(jsx?|tsx?)$`),S=I.match(f);if(!S)return null;let $=S[1],P=s[$],F=S[2].split("/"),B=F[F.length-1],j=F.slice(0,-1);return{fileName:B,folder:$,intermediateFolders:j,suffix:P}},a=new Set(["shared","common","ui","base","general","core"]),u=f=>{let{fileName:S,folder:$,intermediateFolders:P,suffix:R}=f;if(S==="index"&&P.length===0)return null;let F=P.filter(H=>!a.has(H)),B;if(S==="index"){if(B=i==="parent-child"?[...F]:[...F].reverse(),B.length===0)return null}else{let H=F.map(Ee);B=i==="parent-child"?[...H,S]:[S,...[...H].reverse()]}let j=B.map(t).join("")+R;return o.has($)?h(j):j},T=f=>f.parent&&f.parent.type==="VariableDeclarator"&&f.parent.id&&f.parent.id.type==="Identifier"?{name:f.parent.id.name,identifierNode:f.parent.id}:f.id&&f.id.type==="Identifier"?{name:f.id.name,identifierNode:f.id}:null,y=f=>f&&/^[A-Z]/.test(f),b=f=>{let S=f.body;return S?S.type==="JSXElement"||S.type==="JSXFragment"||S.type==="ParenthesizedExpression"&&S.expression&&(S.expression.type==="JSXElement"||S.expression.type==="JSXFragment")?!0:S.type==="BlockStatement"?S.body.some(P=>{if(P.type==="ReturnStatement"&&P.argument){let R=P.argument;return R.type==="JSXElement"||R.type==="JSXFragment"||R.type==="ParenthesizedExpression"&&R.expression&&(R.expression.type==="JSXElement"||R.expression.type==="JSXFragment")}return!1}):!1:!1},m=(f,S,$,P)=>R=>{let F=e.sourceCode?e.sourceCode.getScope(f):e.getScope(),B=(D,J)=>{let _=D.variables.find(W=>W.name===J);return _||(D.upper?B(D.upper,J):null)},j=B(F,S);if(!j)return R.replaceText(P,$);let H=[],M=new Set;return j.defs.forEach(D=>{let J=`${D.name.range[0]}-${D.name.range[1]}`;M.has(J)||(M.add(J),H.push(R.replaceText(D.name,$)))}),j.references.forEach(D=>{let J=`${D.identifier.range[0]}-${D.identifier.range[1]}`;M.has(J)||(M.add(J),H.push(R.replaceText(D.identifier,$)))}),H},k=(f,S,$,P)=>$?`"${f}" in "${S}" folder must be named "${P}" (expected "${$}" suffix with chained folder names)`:`"${f}" in "${S}" folder must be named "${P}" (expected chained folder names)`,A=(f,S)=>{let $=Object.entries(s).filter(([P,R])=>P!==S&&R&&f.endsWith(R)).sort((P,R)=>R[1].length-P[1].length)[0];return $?$[0]:null},w=f=>f&&/^[a-z]/.test(f),E=(f,S)=>{let $=f.length,P=S.length;if(Math.abs($-P)>2)return 3;let R=Array.from({length:$+1},()=>Array(P+1).fill(0));for(let F=0;F<=$;F++)R[F][0]=F;for(let F=0;F<=P;F++)R[0][F]=F;for(let F=1;F<=$;F++)for(let B=1;B<=P;B++)R[F][B]=f[F-1]===S[B-1]?R[F-1][B-1]:1+Math.min(R[F-1][B-1],R[F-1][B],R[F][B-1]);return R[$][P]},L=(f,S,$,P,R,F)=>{let B=F.fileName==="index",j=B?"":t(F.fileName),H=F.intermediateFolders.filter(V=>!a.has(V)),M=B?H:H.map(Ee),J=(i==="parent-child"?[...M]:[...M].reverse()).map(t).join(""),_=i==="parent-child"?J+j:j+J,W=_+$,K=h(W);if(f.endsWith(W)||f===K)return;let ee,X=f.endsWith($)?f.slice(0,-$.length):f,q=X.charAt(0).toUpperCase()+X.slice(1);_.startsWith(q)||E(X,h(j))<=2?ee=K:ee=X+W,e.report({fix:m(R,f,ee,P),message:`"${f}" in "${S}" folder must end with "${W}" (should be "${ee}")`,node:P})},C=f=>{let S=l();if(!S)return;let $=T(f);if(!$)return;let{name:P,identifierNode:R}=$,{folder:F,suffix:B}=S;if(o.has(F)){if(!B)return;if(w(P))L(P,F,B,R,f,S);else if(y(P)){let H=h(P);e.report({fix:m(f,P,H,R),message:`"${P}" in "${F}" folder should be camelCase. Rename to "${H}"`,node:R})}return}if(!y(P)){let H=f.parent&&f.parent.type==="VariableDeclarator"&&f.parent.parent;if(H&&H.parent&&H.parent.type==="ExportNamedDeclaration"&&w(P)){let D=u(S);D&&e.report({fix:m(f,P,D,R),message:`"${P}" in "${F}" folder should be PascalCase. Rename to "${D}"`,node:R})}return}if(c.has(F)&&!b(f))return;let j=u(S);if(j&&P!==j){let H=A(P,F);if(H){e.report({message:`"${P}" belongs in "${H}/" folder, not "${F}/". Move it to the correct folder.`,node:R});return}e.report({fix:m(f,P,j,R),message:k(P,F,B,j),node:R})}};return{ArrowFunctionExpression:C,FunctionDeclaration:C,FunctionExpression:C,VariableDeclarator:f=>{if(!f.id||f.id.type!=="Identifier"||f.init&&(f.init.type==="ArrowFunctionExpression"||f.init.type==="FunctionExpression"))return;let S=l();if(!S)return;let{folder:$,suffix:P}=S;if(c.has($))return;let R=f.id.name;if(o.has($)){if(!P)return;if(w(R))L(R,$,P,f.id,f,S);else if(y(R)){let B=h(R);e.report({fix:m(f,R,B,f.id),message:`"${R}" in "${$}" folder should be camelCase. Rename to "${B}"`,node:f.id})}return}if(!y(R)){if(f.parent&&f.parent.parent&&f.parent.parent.type==="ExportNamedDeclaration"&&w(R)){let j=u(S);j&&e.report({fix:m(f,R,j,f.id),message:`"${R}" in "${$}" folder should be PascalCase. Rename to "${j}"`,node:f.id})}return}let F=u(S);if(F&&R!==F){let B=A(R,$);if(B){e.report({message:`"${R}" belongs in "${B}/" folder, not "${$}/". Move it to the correct folder.`,node:f.id});return}e.report({fix:m(f,R,F,f.id),message:k(R,$,P,F),node:f.id})}}}},meta:{docs:{description:"Enforce naming conventions based on folder location \u2014 suffix for views/layouts/pages/providers/reducers/contexts/themes, chained folder names for nested files"},fixable:"code",schema:[{additionalProperties:!1,properties:{chainOrder:{default:"child-parent",description:"Order of folder chain in names: child-parent (LoginAuth) or parent-child (AuthLogin)",enum:["child-parent","parent-child"],type:"string"},files:{description:"Per-path chainOrder overrides",items:{additionalProperties:!1,properties:{chainOrder:{enum:["child-parent","parent-child"],type:"string"},paths:{items:{type:"string"},type:"array"}},required:["chainOrder","paths"],type:"object"},type:"array"}},type:"object"}],type:"suggestion"}},We={create(e){let I=(e.filename||e.getFilename()).replace(/\\/g,"/"),v=e.options[0]||{},x=["actions","apis","assets","atoms","components","config","configs","constants","contexts","data","enums","helpers","hooks","interfaces","layouts","lib","middlewares","molecules","organisms","pages","providers","reducers","redux","requests","routes","schemas","sections","services","store","strings","styles","theme","thunks","types","ui","utils","utilities","views","widgets"],r=v.moduleFolders||[...x,...v.extraModuleFolders||[]],i=(()=>{for(let w of r){let E=new RegExp(`(.*/${w})/`),L=I.match(E);if(L)return{folder:w,fullPath:L[1]}}return null})();if(!i){let w=I.replace(/.*\//,"").replace(/\.(jsx?|tsx?)$/,"");return r.includes(w)?{Program(E){e.report({message:`"${w}" should be a folder, not a standalone file. Use "${w}/" folder with an index file instead.`,node:E})}}:{}}let s=/\.(tsx?|jsx?)$/,c=(w,E)=>{let L;try{L=Se.readdirSync(w,{withFileTypes:!0})}catch{return null}let C=L.filter(F=>F.isFile()&&s.test(F.name)&&!F.name.startsWith("index.")),g=L.filter(F=>F.isDirectory());if(C.length===0&&g.length===0||C.length<=1&&g.length===0)return null;let f=()=>{for(let F of g)try{let B=`${w}/${F.name}`,j=Se.readdirSync(B,{withFileTypes:!0}),H=j.filter(D=>D.isFile()&&s.test(D.name));if(H.length>=2||H.some(D=>D.name.startsWith("index."))||j.filter(D=>D.isDirectory()).length>0)return!0}catch{}return!1},S=C.length>0,$=g.length>0,P=S&&$,R=$?f():!1;return{folderLabel:E,hasDirectFiles:S,hasSubdirectories:$,isMixed:P,wrappedJustified:R}},{fullPath:o}=i,t=I.split("/"),h=t.slice(0,-1).join("/"),p=t[t.length-2],l=h,a=p,u=c(l,a),T=[],y=h;for(;y.length>o.length;){let w=y.slice(0,y.lastIndexOf("/"));if(w.length<o.length)break;try{let E=Se.readdirSync(w,{withFileTypes:!0}),L=E.filter(g=>g.isDirectory()),C=E.filter(g=>g.isFile()&&s.test(g.name)&&!g.name.startsWith("index."));if(L.length===1&&C.length===0){let g=w.split("/").pop(),f=L[0].name;T.push({folderName:g,subfolderName:f,suggestedName:`${g}-${f}`})}}catch{}y=w}if(!u&&T.length===0)return{};let{hasDirectFiles:b,hasSubdirectories:m,isMixed:k,wrappedJustified:A}=u||{};return{Program(w){if(u){if(!b&&m&&!A){e.report({message:`Unnecessary wrapper folders in "${a}/". Each item has only one file, use direct files instead (e.g., ${a}/component.tsx).`,node:w});return}if(k&&A){!I.slice(l.length+1).includes("/")&&e.report({message:`Since some items in "${a}/" contain multiple files or subfolders, all items should be wrapped in folders.`,node:w});return}k&&!A&&I.slice(l.length+1).includes("/")&&e.report({message:`Unnecessary wrapper folder. Each item in "${a}/" has only one file, use direct files instead.`,node:w})}for(let E of T)e.report({message:`"${E.folderName}/" has only one subfolder "${E.subfolderName}/". Flatten to "${E.suggestedName}/" to reduce nesting.`,node:w})}}},meta:{docs:{description:"Enforce consistent folder structure (flat vs wrapped) in module folders like atoms, components, hooks, enums, views, layouts, and pages"},fixable:null,schema:[{additionalProperties:!1,properties:{extraModuleFolders:{items:{type:"string"},type:"array"},moduleFolders:{items:{type:"string"},type:"array"}},type:"object"}],type:"suggestion"}},Ve={create(e){let v=(e.filename||e.getFilename()).replace(/\\/g,"/").split("/"),x=v[v.length-1],r=x.replace(/\.(jsx?|tsx?)$/,""),d=v.indexOf("src");if(d===-1)return{};let i=v.slice(d+1,v.length-1);if(i.length===0)return{};let s=[];for(let t=1;t<i.length;t++){let h=i[t];for(let p=0;p<t;p++){let l=i[p],a=Ee(l),u=`-${l}`,T=`-${a}`,y=h.endsWith(u)?u:l!==a&&h.endsWith(T)?T:null;y&&s.push({ancestorFolder:l,folderName:h,singular:a,suffix:y,suggestedName:h.slice(0,-y.length)})}}let c=null;if(r!=="index"&&i.length>=2){let t=i[i.length-1];r===t&&(c=t)}let o=null;if(r!=="index"&&!c&&!(r.startsWith("use-")&&i.includes("hooks")))for(let t of i){let h=Ee(t),p=`-${t}`,l=`-${h}`;if(r.endsWith(p)){o={folder:t,singular:h,suffix:p};break}if(t!==h&&r.endsWith(l)){o={folder:t,singular:h,suffix:l};break}}return s.length===0&&!o&&!c?{}:{Program(t){for(let h of s)e.report({message:`Folder name "${h.folderName}" has redundant suffix "${h.suffix}" \u2014 the "${h.ancestorFolder}/" ancestor folder already provides this context. Rename to "${h.suggestedName}".`,node:t});c&&e.report({message:`File name "${r}" is the same as its parent folder "${c}/". Use "index" instead (e.g., "${c}/index${x.match(/\.\w+$/)[0]}").`,node:t}),o&&e.report({message:`File name "${r}" has redundant suffix "${o.suffix}" \u2014 the "${o.folder}/" folder already provides this context. Rename to "${r.slice(0,-o.suffix.length)}".`,node:t})}}},meta:{docs:{description:"Disallow file and folder names that redundantly include the parent or ancestor folder name as a suffix"},fixable:null,schema:[],type:"suggestion"}};var Ue={create(e){let n=e.sourceCode||e.getSourceCode();return{BlockStatement:v=>{let{body:x}=v;if(x.length===0)return;let r=n.getFirstToken(v),d=n.getLastToken(v),i=x[0],s=x[x.length-1],c=" ".repeat(i.loc.start.column),o=" ".repeat(r.loc.start.column);r.loc.end.line===i.loc.start.line&&e.report({fix:t=>t.replaceTextRange([r.range[1],i.range[0]],`
58
+ `);return f.replaceTextRange([g.range[1],E.range[0]],$)},message:"No empty lines allowed between props type properties",node:E})}}),l.length>1){let E=l[l.length-1];n.getText(E).trimEnd().endsWith(",")||e.report({fix:C=>C.insertTextAfter(E,","),message:"Last props type property must have trailing comma",node:E})}if(l.length===1){let E=l[0],L=n.getText(E);L.trimEnd().endsWith(",")&&e.report({fix:C=>{let g=L.lastIndexOf(","),f=E.range[0]+g;return C.removeRange([f,f+1])},message:"Single props type property should not have trailing comma",node:E})}}},r=d=>{if(!d.returnType||!d.returnType.typeAnnotation)return;let i=d.returnType,s=i.typeAnnotation;if(!v(d)&&s.type==="TSTypeLiteral"){e.report({message:"Function return type must be a type reference (interface, type, or built-in type), not an inline object type. Define the return type separately.",node:s});return}let o;if(d.params.length>0){let a=d.params[d.params.length-1];o=n.getTokenAfter(a,u=>u.value===")")}else{let u=n.getFirstToken(d);for(;u&&u.value!=="(";)u=n.getTokenAfter(u);u&&(o=n.getTokenAfter(u,T=>T.value===")"))}if(!o)return;let t=n.getFirstToken(i);if(!t||t.value!==":")return;let h=n.getFirstToken(s);if(!h)return;let p=n.getText().slice(o.range[1],t.range[0]),l=n.getText().slice(t.range[1],h.range[0]);(p!==""||l!==" ")&&e.report({fix:a=>a.replaceTextRange([o.range[1],h.range[0]],": "),message:'Return type annotation must have no space before colon and one space after: "): TypeName"',node:i})};return{ArrowFunctionExpression(d){x(d),r(d)},FunctionDeclaration(d){x(d),r(d)},FunctionExpression(d){x(d),r(d)}}},meta:{docs:{description:"Enforce inline type annotation for React component props and return types with proper formatting"},fixable:"code",schema:[],type:"suggestion"}},Xe={create(e){let n=d=>d.parent&&d.parent.type==="VariableDeclarator"&&d.parent.id&&d.parent.id.type==="Identifier"?d.parent.id.name:d.id&&d.id.type==="Identifier"?d.id.name:null,I=d=>d&&/^[A-Z]/.test(d),v=d=>d&&d.endsWith("Icon"),x=d=>{let i=d.body;if(!i)return!1;if(i.type==="JSXElement")return i.openingElement&&i.openingElement.name&&i.openingElement.name.name==="svg";if(i.type==="ParenthesizedExpression"&&i.expression&&i.expression.type==="JSXElement")return i.expression.openingElement&&i.expression.openingElement.name&&i.expression.openingElement.name.name==="svg";if(i.type==="BlockStatement"){let s=i.body.filter(c=>c.type==="ReturnStatement"&&c.argument);if(s.length===1){let c=s[0].argument;if(c.type==="JSXElement")return c.openingElement&&c.openingElement.name&&c.openingElement.name.name==="svg";if(c.type==="ParenthesizedExpression"&&c.expression&&c.expression.type==="JSXElement")return c.expression.openingElement&&c.expression.openingElement.name&&c.expression.openingElement.name.name==="svg"}}return!1},r=d=>{let i=n(d);if(!I(i))return;let s=x(d),c=v(i);s&&!c&&e.report({message:`Component "${i}" returns an SVG element and should end with "Icon" suffix (e.g., "${i}Icon")`,node:d.parent&&d.parent.type==="VariableDeclarator"?d.parent.id:d.id||d}),c&&!s&&e.report({message:`Component "${i}" has "Icon" suffix but doesn't return an SVG element. Either rename it or make it return an SVG.`,node:d.parent&&d.parent.type==="VariableDeclarator"?d.parent.id:d.id||d})};return{ArrowFunctionExpression:r,FunctionDeclaration:r,FunctionExpression:r}},meta:{docs:{description:"Enforce SVG components to have 'Icon' suffix and vice versa"},fixable:null,schema:[],type:"suggestion"}},ze={create(e){let I=(e.filename||e.getFilename()).replace(/\\/g,"/"),v=e.options[0]||{},x=v.chainOrder||"child-parent",r=v.files||[],i=(()=>{for(let f of r)if(f.paths.some(S=>I.includes(S)))return f.chainOrder;return x})(),s={atoms:"",components:"",constants:"Constants",contexts:"Context",data:"Data",layouts:"Layout",pages:"Page",providers:"Provider",reducers:"Reducer",schemas:"Schema",services:"Service",strings:"Strings",theme:"Theme",themes:"Theme",views:"View"},c=new Set(["atoms","components","layouts","pages","views"]),o=new Set(["constants","data","reducers","schemas","services","strings"]),t=f=>f.split("-").map(S=>S.charAt(0).toUpperCase()+S.slice(1)).join(""),h=f=>f.charAt(0).toLowerCase()+f.slice(1),p=Object.keys(s).join("|"),l=()=>{let f=new RegExp(`\\/(${p})\\/(.+)\\.(jsx?|tsx?)$`),S=I.match(f);if(!S)return null;let $=S[1],P=s[$],F=S[2].split("/"),B=F[F.length-1],j=F.slice(0,-1);return{fileName:B,folder:$,intermediateFolders:j,suffix:P}},a=new Set(["shared","common","ui","base","general","core"]),u=f=>{let{fileName:S,folder:$,intermediateFolders:P,suffix:R}=f;if(S==="index"&&P.length===0)return null;let F=P.filter(H=>!a.has(H)),B;if(S==="index"){if(B=i==="parent-child"?[...F]:[...F].reverse(),B.length===0)return null}else{let H=F.map(Ee);B=i==="parent-child"?[...H,S]:[S,...[...H].reverse()]}let j=B.map(t).join("")+R;return o.has($)?h(j):j},T=f=>f.parent&&f.parent.type==="VariableDeclarator"&&f.parent.id&&f.parent.id.type==="Identifier"?{name:f.parent.id.name,identifierNode:f.parent.id}:f.id&&f.id.type==="Identifier"?{name:f.id.name,identifierNode:f.id}:null,y=f=>f&&/^[A-Z]/.test(f),b=f=>{let S=f.body;return S?S.type==="JSXElement"||S.type==="JSXFragment"||S.type==="ParenthesizedExpression"&&S.expression&&(S.expression.type==="JSXElement"||S.expression.type==="JSXFragment")?!0:S.type==="BlockStatement"?S.body.some(P=>{if(P.type==="ReturnStatement"&&P.argument){let R=P.argument;return R.type==="JSXElement"||R.type==="JSXFragment"||R.type==="ParenthesizedExpression"&&R.expression&&(R.expression.type==="JSXElement"||R.expression.type==="JSXFragment")}return!1}):!1:!1},m=(f,S,$,P)=>R=>{let F=e.sourceCode?e.sourceCode.getScope(f):e.getScope(),B=(D,J)=>{let _=D.variables.find(W=>W.name===J);return _||(D.upper?B(D.upper,J):null)},j=B(F,S);if(!j)return R.replaceText(P,$);let H=[],M=new Set;return j.defs.forEach(D=>{let J=`${D.name.range[0]}-${D.name.range[1]}`;M.has(J)||(M.add(J),H.push(R.replaceText(D.name,$)))}),j.references.forEach(D=>{let J=`${D.identifier.range[0]}-${D.identifier.range[1]}`;M.has(J)||(M.add(J),H.push(R.replaceText(D.identifier,$)))}),H},k=(f,S,$,P)=>$?`"${f}" in "${S}" folder must be named "${P}" (expected "${$}" suffix with chained folder names)`:`"${f}" in "${S}" folder must be named "${P}" (expected chained folder names)`,A=(f,S)=>{let $=Object.entries(s).filter(([P,R])=>P!==S&&R&&f.endsWith(R)).sort((P,R)=>R[1].length-P[1].length)[0];return $?$[0]:null},w=f=>f&&/^[a-z]/.test(f),E=(f,S)=>{let $=f.length,P=S.length;if(Math.abs($-P)>2)return 3;let R=Array.from({length:$+1},()=>Array(P+1).fill(0));for(let F=0;F<=$;F++)R[F][0]=F;for(let F=0;F<=P;F++)R[0][F]=F;for(let F=1;F<=$;F++)for(let B=1;B<=P;B++)R[F][B]=f[F-1]===S[B-1]?R[F-1][B-1]:1+Math.min(R[F-1][B-1],R[F-1][B],R[F][B-1]);return R[$][P]},L=(f,S,$,P,R,F)=>{let B=F.fileName==="index",j=B?"":t(F.fileName),H=F.intermediateFolders.filter(V=>!a.has(V)),M=B?H:H.map(Ee),J=(i==="parent-child"?[...M]:[...M].reverse()).map(t).join(""),_=i==="parent-child"?J+j:j+J,W=_+$,K=h(W);if(f.endsWith(W)||f===K)return;let ee,X=f.endsWith($)?f.slice(0,-$.length):f,q=X.charAt(0).toUpperCase()+X.slice(1);_.startsWith(q)||E(X,h(j))<=2?ee=K:ee=X+W,e.report({fix:m(R,f,ee,P),message:`"${f}" in "${S}" folder must end with "${W}" (should be "${ee}")`,node:P})},C=f=>{let S=l();if(!S)return;let $=T(f);if(!$)return;let{name:P,identifierNode:R}=$,{folder:F,suffix:B}=S;if(o.has(F)){if(!B)return;if(w(P))L(P,F,B,R,f,S);else if(y(P)){let H=h(P);e.report({fix:m(f,P,H,R),message:`"${P}" in "${F}" folder should be camelCase. Rename to "${H}"`,node:R})}return}if(!y(P)){let H=f.parent&&f.parent.type==="VariableDeclarator"&&f.parent.parent;if(H&&H.parent&&H.parent.type==="ExportNamedDeclaration"&&w(P)){let D=u(S);D&&e.report({fix:m(f,P,D,R),message:`"${P}" in "${F}" folder should be PascalCase. Rename to "${D}"`,node:R})}return}if(c.has(F)&&!b(f))return;let j=u(S);if(j&&P!==j){let H=A(P,F);if(H){e.report({message:`"${P}" belongs in "${H}/" folder, not "${F}/". Move it to the correct folder.`,node:R});return}e.report({fix:m(f,P,j,R),message:k(P,F,B,j),node:R})}};return{ArrowFunctionExpression:C,FunctionDeclaration:C,FunctionExpression:C,VariableDeclarator:f=>{if(!f.id||f.id.type!=="Identifier"||f.init&&(f.init.type==="ArrowFunctionExpression"||f.init.type==="FunctionExpression"))return;let S=l();if(!S)return;let{folder:$,suffix:P}=S;if(c.has($))return;let R=f.id.name;if(o.has($)){if(!P)return;if(w(R))L(R,$,P,f.id,f,S);else if(y(R)){let B=h(R);e.report({fix:m(f,R,B,f.id),message:`"${R}" in "${$}" folder should be camelCase. Rename to "${B}"`,node:f.id})}return}if(!y(R)){if(f.parent&&f.parent.parent&&f.parent.parent.type==="ExportNamedDeclaration"&&w(R)){let j=u(S);j&&e.report({fix:m(f,R,j,f.id),message:`"${R}" in "${$}" folder should be PascalCase. Rename to "${j}"`,node:f.id})}return}let F=u(S);if(F&&R!==F){let B=A(R,$);if(B){e.report({message:`"${R}" belongs in "${B}/" folder, not "${$}/". Move it to the correct folder.`,node:f.id});return}e.report({fix:m(f,R,F,f.id),message:k(R,$,P,F),node:f.id})}}}},meta:{docs:{description:"Enforce naming conventions based on folder location \u2014 suffix for views/layouts/pages/providers/reducers/contexts/themes, chained folder names for nested files"},fixable:"code",schema:[{additionalProperties:!1,properties:{chainOrder:{default:"child-parent",description:"Order of folder chain in names: child-parent (LoginAuth) or parent-child (AuthLogin)",enum:["child-parent","parent-child"],type:"string"},files:{description:"Per-path chainOrder overrides",items:{additionalProperties:!1,properties:{chainOrder:{enum:["child-parent","parent-child"],type:"string"},paths:{items:{type:"string"},type:"array"}},required:["chainOrder","paths"],type:"object"},type:"array"}},type:"object"}],type:"suggestion"}},We={create(e){let I=(e.filename||e.getFilename()).replace(/\\/g,"/"),v=e.options[0]||{},x=["actions","apis","assets","atoms","components","config","configs","constants","contexts","data","enums","helpers","hooks","interfaces","layouts","lib","middlewares","molecules","organisms","pages","providers","reducers","redux","requests","routes","schemas","sections","services","store","strings","styles","theme","thunks","types","ui","utils","utilities","views","widgets"],r=v.moduleFolders||[...x,...v.extraModuleFolders||[]],i=(()=>{for(let w of r){let E=new RegExp(`(.*/${w})/`),L=I.match(E);if(L)return{folder:w,fullPath:L[1]}}return null})();if(!i){let w=I.replace(/.*\//,"").replace(/\.(jsx?|tsx?)$/,"");return r.includes(w)?{Program(E){e.report({message:`"${w}" should be a folder, not a standalone file. Use "${w}/" folder with an index file instead.`,node:E})}}:{}}let s=/\.(tsx?|jsx?)$/,c=(w,E)=>{let L;try{L=Se.readdirSync(w,{withFileTypes:!0})}catch{return null}let C=L.filter(F=>F.isFile()&&s.test(F.name)&&!F.name.startsWith("index.")),g=L.filter(F=>F.isDirectory());if(C.length===0&&g.length===0||C.length<=1&&g.length===0)return null;let f=()=>{for(let F of g)try{let B=`${w}/${F.name}`,j=Se.readdirSync(B,{withFileTypes:!0}),H=j.filter(D=>D.isFile()&&s.test(D.name));if(H.length>=2||H.some(D=>D.name.startsWith("index."))||j.filter(D=>D.isDirectory()).length>0)return!0}catch{}return!1},S=C.length>0,$=g.length>0,P=S&&$,R=$?f():!1;return{folderLabel:E,hasDirectFiles:S,hasSubdirectories:$,isMixed:P,wrappedJustified:R}},{fullPath:o}=i,t=I.split("/"),h=t.slice(0,-1).join("/"),p=t[t.length-2],l=h,a=p,u=c(l,a),T=[],y=h;for(;y.length>o.length;){let w=y.slice(0,y.lastIndexOf("/"));if(w.length<o.length)break;try{let E=Se.readdirSync(w,{withFileTypes:!0}),L=E.filter(g=>g.isDirectory()),C=E.filter(g=>g.isFile()&&s.test(g.name)&&!g.name.startsWith("index."));if(L.length===1&&C.length===0){let g=w.split("/").pop(),f=L[0].name,S=["shared","common"],$=w===o,P=S.includes(g)||S.includes(f);!$&&!P&&T.push({folderName:g,subfolderName:f,suggestedName:`${g}-${f}`})}}catch{}y=w}if(!u&&T.length===0)return{};let{hasDirectFiles:b,hasSubdirectories:m,isMixed:k,wrappedJustified:A}=u||{};return{Program(w){if(u){if(!b&&m&&!A){e.report({message:`Unnecessary wrapper folders in "${a}/". Each item has only one file, use direct files instead (e.g., ${a}/component.tsx).`,node:w});return}if(k&&A){!I.slice(l.length+1).includes("/")&&e.report({message:`Since some items in "${a}/" contain multiple files or subfolders, all items should be wrapped in folders.`,node:w});return}k&&!A&&I.slice(l.length+1).includes("/")&&e.report({message:`Unnecessary wrapper folder. Each item in "${a}/" has only one file, use direct files instead.`,node:w})}for(let E of T)e.report({message:`"${E.folderName}/" has only one subfolder "${E.subfolderName}/". Flatten to "${E.suggestedName}/" to reduce nesting.`,node:w})}}},meta:{docs:{description:"Enforce consistent folder structure (flat vs wrapped) in module folders like atoms, components, hooks, enums, views, layouts, and pages"},fixable:null,schema:[{additionalProperties:!1,properties:{extraModuleFolders:{items:{type:"string"},type:"array"},moduleFolders:{items:{type:"string"},type:"array"}},type:"object"}],type:"suggestion"}},Ve={create(e){let v=(e.filename||e.getFilename()).replace(/\\/g,"/").split("/"),x=v[v.length-1],r=x.replace(/\.(jsx?|tsx?)$/,""),d=v.indexOf("src");if(d===-1)return{};let i=v.slice(d+1,v.length-1);if(i.length===0)return{};let s=[];for(let t=1;t<i.length;t++){let h=i[t];for(let p=0;p<t;p++){let l=i[p],a=Ee(l),u=`-${l}`,T=`-${a}`,y=h.endsWith(u)?u:l!==a&&h.endsWith(T)?T:null;y&&s.push({ancestorFolder:l,folderName:h,singular:a,suffix:y,suggestedName:h.slice(0,-y.length)})}}let c=null;if(r!=="index"&&i.length>=2){let t=i[i.length-1];r===t&&(c=t)}let o=null;if(r!=="index"&&!c&&!(r.startsWith("use-")&&i.includes("hooks")))for(let t of i){let h=Ee(t),p=`-${t}`,l=`-${h}`;if(r.endsWith(p)){o={folder:t,singular:h,suffix:p};break}if(t!==h&&r.endsWith(l)){o={folder:t,singular:h,suffix:l};break}}return s.length===0&&!o&&!c?{}:{Program(t){for(let h of s)e.report({message:`Folder name "${h.folderName}" has redundant suffix "${h.suffix}" \u2014 the "${h.ancestorFolder}/" ancestor folder already provides this context. Rename to "${h.suggestedName}".`,node:t});c&&e.report({message:`File name "${r}" is the same as its parent folder "${c}/". Use "index" instead (e.g., "${c}/index${x.match(/\.\w+$/)[0]}").`,node:t}),o&&e.report({message:`File name "${r}" has redundant suffix "${o.suffix}" \u2014 the "${o.folder}/" folder already provides this context. Rename to "${r.slice(0,-o.suffix.length)}".`,node:t})}}},meta:{docs:{description:"Disallow file and folder names that redundantly include the parent or ancestor folder name as a suffix"},fixable:null,schema:[],type:"suggestion"}};var Ue={create(e){let n=e.sourceCode||e.getSourceCode();return{BlockStatement:v=>{let{body:x}=v;if(x.length===0)return;let r=n.getFirstToken(v),d=n.getLastToken(v),i=x[0],s=x[x.length-1],c=" ".repeat(i.loc.start.column),o=" ".repeat(r.loc.start.column);r.loc.end.line===i.loc.start.line&&e.report({fix:t=>t.replaceTextRange([r.range[1],i.range[0]],`
59
59
  `+c),message:"Statement should be on its own line after opening brace",node:i}),d.loc.start.line===s.loc.end.line&&e.report({fix:t=>t.replaceTextRange([s.range[1],d.range[0]],`
60
60
  `+o),message:"Closing brace should be on its own line",node:d})}}},meta:{docs:{description:"Enforce newlines after opening brace and before closing brace in blocks"},fixable:"whitespace",schema:[],type:"layout"}},_e={create(e){let n=e.sourceCode||e.getSourceCode(),I=(x,r)=>["Identifier","MemberExpression","UnaryExpression","Literal"].includes(x.type)||x.type==="CallExpression"&&x.arguments.length===0?!0:x.type==="LogicalExpression"?/\n\s*(\|\||&&)/.test(r)?!1:r.replace(/\s+/g," ").trim().length<=80:!1;return{IfStatement:x=>{let{consequent:r,test:d}=x,i=r.type==="BlockStatement",s=n.getFirstToken(x),c=n.getTokenAfter(s,l=>l.value==="("),o=n.getTokenAfter(d,l=>l.value===")");if(!c||!o)return;let t=i?n.getFirstToken(r):null;if(s.loc.end.line!==c.loc.start.line){e.report({fix:l=>l.replaceTextRange([s.range[1],c.range[0]]," "),message:"Opening parenthesis should be on the same line as 'if'",node:c});return}let h=c.loc.end.line!==o.loc.start.line,p=n.getText(d);if(h&&I(d,p)){let l=p.replace(/\s+/g," ").trim();e.report({fix:a=>a.replaceTextRange([c.range[1],o.range[0]],l),message:"If condition should be on a single line",node:d});return}t&&o.loc.end.line!==t.loc.start.line&&e.report({fix:l=>l.replaceTextRange([o.range[1],t.range[0]]," "),message:"Opening brace should be on the same line as closing parenthesis",node:t})}}},meta:{docs:{description:"Ensure if statement has proper formatting: if (...) {"},fixable:"whitespace",schema:[],type:"layout"}},qe={create(e){let n=e.sourceCode||e.getSourceCode(),I=r=>{if(r.type!=="IfStatement")return!1;let{consequent:d}=r;return d.type!=="BlockStatement"?!0:d.loc.start.line===d.loc.end.line},v=r=>{let{alternate:d}=r;if(!d||!I(r))return;let{consequent:i}=r,s=(i.type==="BlockStatement",n.getLastToken(i)),c=n.getTokenAfter(s,t=>t.value==="else");if(!c)return;c.loc.start.line-s.loc.end.line>1&&e.report({fix:t=>t.replaceTextRange([s.range[1],c.range[0]],`
61
61
  `+" ".repeat(c.loc.start.column)),message:"No empty line allowed between single-line if and else",node:c})},x=r=>{let{body:d}=r;if(!(!d||!Array.isArray(d)))for(let i=0;i<d.length-1;i+=1){let s=d[i],c=d[i+1];if(s.type!=="IfStatement"||c.type!=="IfStatement")continue;let o=s;for(;o.alternate&&o.alternate.type==="IfStatement";)o=o.alternate;let t=o.alternate||o.consequent,h=t.type==="BlockStatement",p=c.consequent.type==="BlockStatement";(h||p)&&c.loc.start.line-t.loc.end.line===1&&e.report({fix:a=>a.insertTextAfter(t,`
@@ -281,4 +281,4 @@ ${h})`;e.report({fix:u=>u.replaceTextRange([d.range[0],i.range[1]],a),message:"F
281
281
  `);return b.replaceTextRange([y.range[1],a.range[0]],k)},message:"No empty lines allowed between interface properties",node:a})}}),s.length>1){let a=n.getText(l),u=a.trimEnd(),T=n.getTokenAfter(l),y=u.endsWith(",")||T&&T.value===",",b=u.endsWith(";"),m=h.loc.start.line===l.loc.end.line;if(b){let k=a.lastIndexOf(";"),A=l.range[0]+k;m?e.report({fix:w=>w.replaceTextRange([A,h.range[0]],`,
282
282
  `+o),message:"Last interface property must end with comma and closing brace must be on its own line",node:l}):e.report({fix:w=>w.replaceTextRange([A,A+1],","),message:"Interface properties must end with comma (,) not semicolon (;)",node:l})}else!y&&m?e.report({fix:k=>k.replaceTextRange([l.range[1],h.range[0]],`,
283
283
  `+o),message:"Last interface property must have trailing comma and closing brace must be on its own line",node:l}):y?m&&e.report({fix:k=>k.replaceTextRange([l.range[1],h.range[0]],`
284
- `+o),message:"Closing brace must be on its own line",node:h}):e.report({fix:k=>k.insertTextAfter(l,","),message:"Last interface property must have trailing comma",node:l})}}}},meta:{docs:{description:"Enforce interface naming (PascalCase + Interface suffix), camelCase properties, proper formatting, and trailing commas"},fixable:"code",schema:[],type:"suggestion"}},Zt={create(e){let I=(e.filename||e.getFilename()).replace(/\\/g,"/"),v=r=>new RegExp(`/${r}/[^/]+\\.(ts|tsx)$`).test(I),x=()=>/\.(ts|tsx)$/.test(I);return{TSInterfaceDeclaration(r){x()&&(v("interfaces")||e.report({message:'Interfaces must be declared in files inside the "interfaces" folder',node:r.id||r}))},TSEnumDeclaration(r){x()&&(v("enums")||e.report({message:'Enums must be declared in files inside the "enums" folder',node:r.id||r}))},TSTypeAliasDeclaration(r){x()&&(v("types")||e.report({message:'Type aliases must be declared in files inside the "types" folder',node:r.id||r}))}}},meta:{docs:{description:"Enforce that interfaces are in interfaces folder, enums in enums folder, and types in types folder"},schema:[],type:"suggestion"}};var Kt={create(e){let n=e.sourceCode||e.getSourceCode(),I=/^[a-z][a-zA-Z0-9]*$/,v=/^[A-Z][a-zA-Z0-9]*$/,x=/^use[A-Z][a-zA-Z0-9]*$/,r=/^[A-Z][A-Z0-9_]*$/,d=m=>/^[A-Z][A-Z0-9_]*$/.test(m)?m.toLowerCase().replace(/_([a-z0-9])/g,(k,A)=>A.toUpperCase()):/_/.test(m)?m.toLowerCase().replace(/_([a-z0-9])/g,(k,A)=>A.toUpperCase()):/^[A-Z]/.test(m)?m[0].toLowerCase()+m.slice(1):m,i=m=>{let A=(n.getScope?n.getScope(m):e.getScope()).variables.find(w=>w.name===m.name);return A?A.references.map(w=>w.identifier):[]},s=["ArrowFunctionExpression","CallExpression","FunctionDeclaration","FunctionExpression","Property","VariableDeclarator","JSXElement","JSXOpeningElement","ReturnStatement","SwitchCase","SwitchStatement","ObjectExpression","ObjectPattern","BlockStatement","IfStatement","Identifier","RestElement","AssignmentPattern","ArrayPattern","MemberExpression","JSXText","JSXAttribute","JSXExpressionContainer","Function","Object","Array","String","Number","Boolean","Symbol","BigInt","Date","RegExp","Error","Map","Set","WeakMap","WeakSet","Promise"],c=m=>{if(!m||m.type!=="CallExpression")return!1;let{callee:k}=m;if(k.type==="CallExpression"){let A=k.callee;if(A.type==="Identifier"&&A.name==="styled"||A.type==="MemberExpression"&&A.object.name==="styled")return!0}return!1},o=m=>{if(m.type!=="CallExpression")return!1;let{callee:k}=m;return k.type==="Identifier"&&k.name==="styled"},t=m=>{if(!m)return!1;if(m.type==="ArrowFunctionExpression"||m.type==="FunctionExpression")return!0;if(m.type==="CallExpression"&&m.callee){let k=m.callee.name||m.callee.property&&m.callee.property.name;return["memo","forwardRef","lazy","createContext"].includes(k)}return!1},h=m=>{if(!m.init)return!1;let k=m.id.name;if(/^[A-Z]/.test(k)&&t(m.init)||/^[A-Z]/.test(k)&&m.init.type==="MemberExpression"&&m.init.object.type==="Identifier"&&/^[A-Z]/.test(m.init.object.name))return!0;if(/^[A-Z]/.test(k)&&m.init.type==="ConditionalExpression"){let{consequent:A,alternate:w}=m.init,E=L=>L.type==="Identifier"&&/^[A-Z]/.test(L.name)||L.type==="Literal"&&typeof L.value=="string"||L.type==="MemberExpression"&&L.object.type==="Identifier"&&/^[A-Z]/.test(L.object.name);if(E(A)||E(w))return!0}return!1},p=m=>{if(!m.init)return!1;let k=m.id.name;return k.startsWith("use")&&/^use[A-Z]/.test(k)&&t(m.init)},l=["Icon","Component","FormComponent","Layout","Wrapper","Container","Provider"],a=(m,k)=>{if(m.type==="Identifier"){let A=m.name;if(A.startsWith("_")||r.test(A)||s.includes(A)||l.includes(A))return;I.test(A)||e.report({message:`${k} "${A}" should be camelCase`,node:m})}else m.type==="ObjectPattern"?m.properties.forEach(A=>{A.type==="Property"?a(A.value,k):A.type==="RestElement"&&a(A.argument,k)}):m.type==="ArrayPattern"?m.elements.forEach(A=>{A&&a(A,k)}):m.type==="AssignmentPattern"?a(m.left,k):m.type==="RestElement"&&a(m.argument,k)},u=m=>{if(m.id.type!=="Identifier"){a(m.id,"Variable");return}let k=m.id.name;if(c(m.init)){v.test(k)||e.report({message:`Styled component "${k}" should be PascalCase (e.g., StyledCard instead of styledCard)`,node:m.id});return}if(k.startsWith("_")||h(m))return;let A=["Component","Icon","Layout","Wrapper","Container","Provider","View","Screen","Page"];if(v.test(k)&&A.some(E=>k.endsWith(E)))return;if(p(m)){x.test(k)||e.report({message:`Hook "${k}" should start with "use" followed by PascalCase (e.g., useEventsList)`,node:m.id});return}if(!([/^[A-Z][a-zA-Z]*Data$/,/^[A-Z][a-zA-Z]*Config$/,/^Routes$/].some(E=>E.test(k))&&m.init&&(m.init.type==="ArrayExpression"||m.init.type==="ObjectExpression"||m.init.type==="CallExpression"))&&!I.test(k)){let E=d(k),L=i(m.id);e.report({fix:C=>{let g=[];return L.forEach(f=>{g.push(C.replaceText(f,E))}),g},message:`Variable "${k}" should be camelCase (e.g., ${E} instead of ${k})`,node:m.id})}},T=m=>{if(m.computed||m.key.type!=="Identifier")return;let k=m.key.name;if(!(k.startsWith("_")||s.includes(k))){if(m.value&&m.value.type==="Identifier"){let A=m.value.name;if(v.test(A)&&v.test(k))return}if(!l.includes(k)&&!k.startsWith("Mui")&&!I.test(k)){let A=d(k);e.report({fix:w=>w.replaceText(m.key,A),message:`Property "${k}" should be camelCase (e.g., ${A} instead of ${k})`,node:m.key})}}},y=m=>{m.params.forEach(k=>a(k,"Parameter"))};return{ArrowFunctionExpression:y,CallExpression:m=>{o(m)||m.arguments.forEach(k=>{if(k.type==="Identifier"){let A=k.name;if(A.startsWith("_")||r.test(A)||s.includes(A)||l.includes(A)||v.test(A))return;if(!I.test(A)){let w=d(A);e.report({fix(E){let L=n.getScope?n.getScope(k):e.getScope(),C=L.variables.find(S=>S.name===A)||L.upper&&L.upper.variables.find(S=>S.name===A);if(!C)return E.replaceText(k,w);let g=[],f=new Set;return C.references.forEach(S=>{let $=`${S.identifier.range[0]}-${S.identifier.range[1]}`;f.has($)||(f.add($),g.push(E.replaceText(S.identifier,w)))}),g},message:`Argument "${A}" should be camelCase (e.g., ${w} instead of ${A})`,node:k})}}})},FunctionDeclaration:y,FunctionExpression:y,Property:T,VariableDeclarator:u}},meta:{docs:{description:"Enforce naming conventions: camelCase for variables/properties/params/arguments, PascalCase for components, useXxx for hooks"},fixable:"code",schema:[],type:"suggestion"}};var Jn={meta:{name:"eslint-plugin-code-style",version:"2.2.0"},rules:{"array-callback-destructure":ve,"array-items-per-line":we,"array-objects-on-new-lines":Ce,"arrow-function-block-body":Ae,"arrow-function-simple-jsx":$e,"arrow-function-simplify":Le,"curried-arrow-same-line":Ie,"function-arguments-format":Pe,"nested-call-closing-brackets":Re,"no-empty-lines-in-function-calls":Fe,"opening-brackets-same-line":Be,"simple-call-single-line":He,"single-argument-on-one-line":Oe,"comment-format":Ne,"component-props-destructure":De,"component-props-inline-type":Je,"folder-based-naming-convention":ze,"folder-structure-consistency":We,"no-redundant-folder-suffix":Ve,"svg-icon-naming-convention":Xe,"react-code-order":Mt,"block-statement-newlines":Ue,"empty-line-after-block":Ye,"if-else-spacing":qe,"if-statement-format":_e,"logical-expression-multiline":Ge,"multiline-if-conditions":Ze,"no-empty-lines-in-switch-cases":Qe,"ternary-condition-multiline":Ke,"class-method-definition-format":Me,"class-naming-convention":je,"function-call-spacing":et,"function-declaration-style":tt,"function-naming-convention":nt,"function-object-destructure":it,"function-params-per-line":rt,"no-empty-lines-in-function-params":st,"hook-callback-format":at,"hook-deps-per-line":ot,"hook-file-naming-convention":ct,"hook-function-naming-convention":pt,"use-state-naming-convention":lt,"absolute-imports-only":ft,"export-format":ut,"import-format":gt,"import-source-spacing":mt,"index-export-style":yt,"index-exports-only":ht,"inline-export-declaration":xt,"module-index-exports":dt,"classname-dynamic-at-end":Ct,"classname-multiline":Lt,"classname-no-extra-spaces":At,"classname-order":$t,"jsx-children-on-new-line":Tt,"jsx-closing-bracket-spacing":bt,"jsx-element-child-new-line":kt,"jsx-logical-expression-simplify":Et,"jsx-parentheses-position":St,"jsx-prop-naming-convention":wt,"jsx-simple-element-one-line":vt,"jsx-string-value-trim":It,"jsx-ternary-format":Pt,"no-empty-lines-in-jsx":Rt,"no-empty-lines-in-objects":Ft,"object-property-per-line":Bt,"object-property-value-brace":Ht,"object-property-value-format":Ot,"string-property-spacing":jt,"assignment-value-same-line":Nt,"member-expression-bracket-spacing":Dt,"enum-format":_t,"enum-type-enforcement":Xt,"interface-format":qt,"no-inline-type-definitions":Wt,"prop-naming-convention":zt,"type-annotation-spacing":Ut,"type-format":Vt,"typescript-definition-location":Zt,"no-hardcoded-strings":Jt,"variable-naming-convention":Kt}};export{Jn as default};
284
+ `+o),message:"Closing brace must be on its own line",node:h}):e.report({fix:k=>k.insertTextAfter(l,","),message:"Last interface property must have trailing comma",node:l})}}}},meta:{docs:{description:"Enforce interface naming (PascalCase + Interface suffix), camelCase properties, proper formatting, and trailing commas"},fixable:"code",schema:[],type:"suggestion"}},Zt={create(e){let I=(e.filename||e.getFilename()).replace(/\\/g,"/"),v=r=>new RegExp(`/${r}/[^/]+\\.(ts|tsx)$`).test(I),x=()=>/\.(ts|tsx)$/.test(I);return{TSInterfaceDeclaration(r){x()&&(v("interfaces")||e.report({message:'Interfaces must be declared in files inside the "interfaces" folder',node:r.id||r}))},TSEnumDeclaration(r){x()&&(v("enums")||e.report({message:'Enums must be declared in files inside the "enums" folder',node:r.id||r}))},TSTypeAliasDeclaration(r){x()&&(v("types")||e.report({message:'Type aliases must be declared in files inside the "types" folder',node:r.id||r}))}}},meta:{docs:{description:"Enforce that interfaces are in interfaces folder, enums in enums folder, and types in types folder"},schema:[],type:"suggestion"}};var Kt={create(e){let n=e.sourceCode||e.getSourceCode(),I=/^[a-z][a-zA-Z0-9]*$/,v=/^[A-Z][a-zA-Z0-9]*$/,x=/^use[A-Z][a-zA-Z0-9]*$/,r=/^[A-Z][A-Z0-9_]*$/,d=m=>/^[A-Z][A-Z0-9_]*$/.test(m)?m.toLowerCase().replace(/_([a-z0-9])/g,(k,A)=>A.toUpperCase()):/_/.test(m)?m.toLowerCase().replace(/_([a-z0-9])/g,(k,A)=>A.toUpperCase()):/^[A-Z]/.test(m)?m[0].toLowerCase()+m.slice(1):m,i=m=>{let A=(n.getScope?n.getScope(m):e.getScope()).variables.find(w=>w.name===m.name);return A?A.references.map(w=>w.identifier):[]},s=["ArrowFunctionExpression","CallExpression","FunctionDeclaration","FunctionExpression","Property","VariableDeclarator","JSXElement","JSXOpeningElement","ReturnStatement","SwitchCase","SwitchStatement","ObjectExpression","ObjectPattern","BlockStatement","IfStatement","Identifier","RestElement","AssignmentPattern","ArrayPattern","MemberExpression","JSXText","JSXAttribute","JSXExpressionContainer","Function","Object","Array","String","Number","Boolean","Symbol","BigInt","Date","RegExp","Error","Map","Set","WeakMap","WeakSet","Promise"],c=m=>{if(!m||m.type!=="CallExpression")return!1;let{callee:k}=m;if(k.type==="CallExpression"){let A=k.callee;if(A.type==="Identifier"&&A.name==="styled"||A.type==="MemberExpression"&&A.object.name==="styled")return!0}return!1},o=m=>{if(m.type!=="CallExpression")return!1;let{callee:k}=m;return k.type==="Identifier"&&k.name==="styled"},t=m=>{if(!m)return!1;if(m.type==="ArrowFunctionExpression"||m.type==="FunctionExpression")return!0;if(m.type==="CallExpression"&&m.callee){let k=m.callee.name||m.callee.property&&m.callee.property.name;return["memo","forwardRef","lazy","createContext"].includes(k)}return!1},h=m=>{if(!m.init)return!1;let k=m.id.name;if(/^[A-Z]/.test(k)&&t(m.init)||/^[A-Z]/.test(k)&&m.init.type==="MemberExpression"&&m.init.object.type==="Identifier"&&/^[A-Z]/.test(m.init.object.name))return!0;if(/^[A-Z]/.test(k)&&m.init.type==="ConditionalExpression"){let{consequent:A,alternate:w}=m.init,E=L=>L.type==="Identifier"&&/^[A-Z]/.test(L.name)||L.type==="Literal"&&typeof L.value=="string"||L.type==="MemberExpression"&&L.object.type==="Identifier"&&/^[A-Z]/.test(L.object.name);if(E(A)||E(w))return!0}return!1},p=m=>{if(!m.init)return!1;let k=m.id.name;return k.startsWith("use")&&/^use[A-Z]/.test(k)&&t(m.init)},l=["Icon","Component","FormComponent","Layout","Wrapper","Container","Provider"],a=(m,k)=>{if(m.type==="Identifier"){let A=m.name;if(A.startsWith("_")||r.test(A)||s.includes(A)||l.includes(A))return;I.test(A)||e.report({message:`${k} "${A}" should be camelCase`,node:m})}else m.type==="ObjectPattern"?m.properties.forEach(A=>{A.type==="Property"?a(A.value,k):A.type==="RestElement"&&a(A.argument,k)}):m.type==="ArrayPattern"?m.elements.forEach(A=>{A&&a(A,k)}):m.type==="AssignmentPattern"?a(m.left,k):m.type==="RestElement"&&a(m.argument,k)},u=m=>{if(m.id.type!=="Identifier"){a(m.id,"Variable");return}let k=m.id.name;if(c(m.init)){v.test(k)||e.report({message:`Styled component "${k}" should be PascalCase (e.g., StyledCard instead of styledCard)`,node:m.id});return}if(k.startsWith("_")||h(m))return;let A=["Component","Icon","Layout","Wrapper","Container","Provider","View","Screen","Page"];if(v.test(k)&&A.some(E=>k.endsWith(E)))return;if(p(m)){x.test(k)||e.report({message:`Hook "${k}" should start with "use" followed by PascalCase (e.g., useEventsList)`,node:m.id});return}if(!([/^[A-Z][a-zA-Z]*Data$/,/^[A-Z][a-zA-Z]*Config$/,/^Routes$/].some(E=>E.test(k))&&m.init&&(m.init.type==="ArrayExpression"||m.init.type==="ObjectExpression"||m.init.type==="CallExpression"))&&!I.test(k)){let E=d(k),L=i(m.id);e.report({fix:C=>{let g=[];return L.forEach(f=>{g.push(C.replaceText(f,E))}),g},message:`Variable "${k}" should be camelCase (e.g., ${E} instead of ${k})`,node:m.id})}},T=m=>{if(m.computed||m.key.type!=="Identifier")return;let k=m.key.name;if(!(k.startsWith("_")||s.includes(k))){if(m.value&&m.value.type==="Identifier"){let A=m.value.name;if(v.test(A)&&v.test(k))return}if(!l.includes(k)&&!k.startsWith("Mui")&&!I.test(k)){let A=d(k);e.report({fix:w=>w.replaceText(m.key,A),message:`Property "${k}" should be camelCase (e.g., ${A} instead of ${k})`,node:m.key})}}},y=m=>{m.params.forEach(k=>a(k,"Parameter"))};return{ArrowFunctionExpression:y,CallExpression:m=>{o(m)||m.arguments.forEach(k=>{if(k.type==="Identifier"){let A=k.name;if(A.startsWith("_")||r.test(A)||s.includes(A)||l.includes(A)||v.test(A))return;if(!I.test(A)){let w=d(A);e.report({fix(E){let L=n.getScope?n.getScope(k):e.getScope(),C=L.variables.find(S=>S.name===A)||L.upper&&L.upper.variables.find(S=>S.name===A);if(!C)return E.replaceText(k,w);let g=[],f=new Set;return C.references.forEach(S=>{let $=`${S.identifier.range[0]}-${S.identifier.range[1]}`;f.has($)||(f.add($),g.push(E.replaceText(S.identifier,w)))}),g},message:`Argument "${A}" should be camelCase (e.g., ${w} instead of ${A})`,node:k})}}})},FunctionDeclaration:y,FunctionExpression:y,Property:T,VariableDeclarator:u}},meta:{docs:{description:"Enforce naming conventions: camelCase for variables/properties/params/arguments, PascalCase for components, useXxx for hooks"},fixable:"code",schema:[],type:"suggestion"}};var Jn={meta:{name:"eslint-plugin-code-style",version:"2.2.1"},rules:{"array-callback-destructure":ve,"array-items-per-line":we,"array-objects-on-new-lines":Ce,"arrow-function-block-body":Ae,"arrow-function-simple-jsx":$e,"arrow-function-simplify":Le,"curried-arrow-same-line":Ie,"function-arguments-format":Pe,"nested-call-closing-brackets":Re,"no-empty-lines-in-function-calls":Fe,"opening-brackets-same-line":Be,"simple-call-single-line":He,"single-argument-on-one-line":Oe,"comment-format":Ne,"component-props-destructure":De,"component-props-inline-type":Je,"folder-based-naming-convention":ze,"folder-structure-consistency":We,"no-redundant-folder-suffix":Ve,"svg-icon-naming-convention":Xe,"react-code-order":Mt,"block-statement-newlines":Ue,"empty-line-after-block":Ye,"if-else-spacing":qe,"if-statement-format":_e,"logical-expression-multiline":Ge,"multiline-if-conditions":Ze,"no-empty-lines-in-switch-cases":Qe,"ternary-condition-multiline":Ke,"class-method-definition-format":Me,"class-naming-convention":je,"function-call-spacing":et,"function-declaration-style":tt,"function-naming-convention":nt,"function-object-destructure":it,"function-params-per-line":rt,"no-empty-lines-in-function-params":st,"hook-callback-format":at,"hook-deps-per-line":ot,"hook-file-naming-convention":ct,"hook-function-naming-convention":pt,"use-state-naming-convention":lt,"absolute-imports-only":ft,"export-format":ut,"import-format":gt,"import-source-spacing":mt,"index-export-style":yt,"index-exports-only":ht,"inline-export-declaration":xt,"module-index-exports":dt,"classname-dynamic-at-end":Ct,"classname-multiline":Lt,"classname-no-extra-spaces":At,"classname-order":$t,"jsx-children-on-new-line":Tt,"jsx-closing-bracket-spacing":bt,"jsx-element-child-new-line":kt,"jsx-logical-expression-simplify":Et,"jsx-parentheses-position":St,"jsx-prop-naming-convention":wt,"jsx-simple-element-one-line":vt,"jsx-string-value-trim":It,"jsx-ternary-format":Pt,"no-empty-lines-in-jsx":Rt,"no-empty-lines-in-objects":Ft,"object-property-per-line":Bt,"object-property-value-brace":Ht,"object-property-value-format":Ot,"string-property-spacing":jt,"assignment-value-same-line":Nt,"member-expression-bracket-spacing":Dt,"enum-format":_t,"enum-type-enforcement":Xt,"interface-format":qt,"no-inline-type-definitions":Wt,"prop-naming-convention":zt,"type-annotation-spacing":Ut,"type-format":Vt,"typescript-definition-location":Zt,"no-hardcoded-strings":Jt,"variable-naming-convention":Kt}};export{Jn as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-code-style",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "A custom ESLint plugin for enforcing consistent code formatting and style rules in React/JSX projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "index.d.ts",