eslint-plugin-check-file
Version:
ESLint rules for consistent filename and folder. Allows you to enforce a consistent naming pattern for the filename and folder
3 lines (2 loc) • 7.8 kB
JavaScript
/*! @author Huan Luo <dukeluo@outlook.com> (https://shaiwang.life) */
import{posix as e,join as t,sep as n}from"path";import o from"micromatch";import a from"is-glob";const r='The naming pattern object "{{ value }}" does not appear to be an Object type, please double-check it and try again',i='There is an invalid pattern "{{ value }}", please double-check it and try again',s=e=>`https://github.com/dukeluo/eslint-plugin-check-file/blob/main/docs/rules/${e}.md`,l=/^<(\d+)>$/,c=/^[A-Za-z]:\\/,d=e=>"[object Object]"===Object.prototype.toString.call(e),p=e=>null==e,m=e=>""===e||(e=>null!=e&&e.length>=0&&"[object Array]"===Object.prototype.toString.call(e))(e)&&0===e.length||d(e)&&0===Object.keys(e).length,g=(...e)=>t=>e.reduce(((e,t)=>t(e)),t),f=g(m,(e=>!e)),u=t=>e.basename(t),h=t=>e.join(e.dirname(t),e.sep),y=(e,t=!1)=>e.substring(0,t?e.indexOf("."):e.lastIndexOf(".")),b=t=>t.split(e.sep).filter(f),v=t=>{const n=b(t);let o=[];for(let t=0;t<n.length;t++)n.slice(t).reduce(((t,n,a)=>{const r=e.join(t,n,e.sep);return a>=1&&o.push(r),r}),"");return o.unshift(e.join(n[0],e.sep)),o},_=t=>t.split(n).join(e.sep),P=e=>e.replace(c,""),A=e=>{const o=(a=e.physicalFilename,r=e.cwd,a.replace(t(r,n),""));var a,r;return g(P,_)(o)},E="+([a-z])*([a-z0-9])*([A-Z]*([a-z0-9]))",M="+([a-z])*([a-z0-9])*(-+([a-z0-9]))";var j=Object.freeze({__proto__:null,CAMEL_CASE:E,FLAT_CASE:"+([a-z0-9])",KEBAB_CASE:M,PASCAL_CASE:"*([A-Z]*([a-z0-9]))",SCREAMING_SNAKE_CASE:"+([A-Z])*([A-Z0-9])*(_+([A-Z0-9]))",SNAKE_CASE:"+([a-z])*([a-z0-9])*(_+([a-z0-9]))"});const S=`\\[${E}\\]`,x=`\\[...${E}\\]`,O=`\\[\\[...${E}\\]\\]`,T=`@(${M}|+([a-z])?(.+([a-z]))|${S}|${x}|${O}|${`\\(${M}\\)`}|${`\\@${M}`}|${`\\_${M}`})`,$=`@(_app|_document|404|500|_error|index|${E}|${S}|${x}|${O})`;var k=Object.freeze({__proto__:null,NEXT_JS_APP_ROUTER_CASE:T,NEXT_JS_PAGE_ROUTER_FILENAME_CASE:$});const z=([e,t],n)=>{const a=o.capture(e,n);if(p(a))return[e,t];const r=[...t.matchAll(new RegExp(l,"g"))];if(m(r))return[e,t];const i=r.reduce(((n,o)=>{const r=+o[1];if(p(a[r]))throw{type:"invalidPrefinedMatch",payload:{namingPattern:t,filenamePattern:e}};return n.replace(o[0],a[r])}),t);return[e,i]},I=(e,t,n,a)=>o.isMatch(e,t)?n&&a&&o.isMatch(n,j[a]||k[a]||a)?void 0:{path:e,pattern:a}:void 0,C=(e,t,n)=>{if(!d(e))return{type:"invalidObject",payload:e};for(const[o,a]of Object.entries(e)){if(!t(o))return{type:"invalidPattern",payload:o};if(!n(a))return{type:"invalidPattern",payload:a}}},L=e=>Object.keys(j).includes(e),N=a,R=e=>N(e)||L(e)||(e=>["NEXT_JS_APP_ROUTER_CASE"].includes(e))(e);var w={meta:{type:"layout",docs:{description:"The filename should be blocklisted",category:"Layout & Formatting",recommended:!1,url:s("filename-blocklist")},fixable:null,schema:[{additionalProperties:{type:"string"}},{type:"object",properties:{errorMessage:{type:"string"}}}],messages:{invalidObject:r,invalidPattern:i,noMatch:'The filename "{{ filename }}" matches the blocklisted "{{ blockListPattern }}" pattern, use a pattern like "{{ suggestion }}" instead'}},create(e){const t=t=>{const n=e.options[0],o=e.options[1]?.errorMessage??"",a=C(n,N,m(o)?N:()=>!0);if(a)return void e.report({node:t,messageId:a.type,data:{value:a.payload}});const r=A(e),i=u(r);for(const[a,s]of Object.entries(n)){if(I(r,a))return void(f(o)?e.report({node:t,message:o,data:{target:i,pattern:a}}):e.report({node:t,messageId:"noMatch",data:{filename:i,blockListPattern:a,suggestion:s}}))}};return{Document:t,Program:t,root:t,StyleSheet:t}}},F={meta:{type:"layout",docs:{description:"The filename should follow the filename naming convention",category:"Layout & Formatting",recommended:!1,url:s("filename-naming-convention")},fixable:null,schema:[{additionalProperties:{type:"string"}},{type:"object",properties:{ignoreMiddleExtensions:{type:"boolean"},errorMessage:{type:"string"}}}],messages:{invalidObject:r,invalidPattern:i,invalidPrefinedMatch:'The prefined match "{{ namingPattern }}" is not found in the pattern "{{ filenamePattern }}", please double-check it and try again',noMatch:'The filename "{{ filename }}" does not match the "{{ originalNamingPattern }}" pattern'}},create(e){const t=t=>{const n=e.options[0],o=C(n,N,(e=>{return N(t=e)||L(t)||l.test(t)||(e=>["NEXT_JS_PAGE_ROUTER_FILENAME_CASE"].includes(e))(e);var t}));if(o)return void e.report({node:t,messageId:o.type,data:{value:o.payload}});const a=A(e),r=u(a),i=e.options[1]?.ignoreMiddleExtensions??!1,s=e.options[1]?.errorMessage??"";for(const[l,c]of Object.entries(n))try{const[e,t]=z([l,c],a);if(I(a,e,y(r,i),t))throw{type:"noMatch",payload:{filename:r,originalNamingPattern:c}}}catch(o){f(s)&&"noMatch"===o.type?e.report({node:t,message:s,data:{target:o.payload.filename,pattern:o.payload.originalNamingPattern}}):e.report({node:t,messageId:o.type,data:o.payload})}};return{Document:t,Program:t,root:t,StyleSheet:t}}},Z={meta:{type:"layout",docs:{description:"The folder should match the naming pattern specified by its file",category:"Layout & Formatting",recommended:!1,url:s("folder-match-with-fex")},fixable:null,schema:[{additionalProperties:{type:"string"}},{type:"object",properties:{errorMessage:{type:"string"}}}],messages:{invalidObject:r,invalidPattern:i,noMatch:'The folder of the file "{{ filename }}" does not match the "{{ folderPattern }}" pattern'}},create(e){const t=t=>{const n=e.options[0],o=C(n,N,N);if(o)return void e.report({node:t,messageId:o.type,data:{value:o.payload}});const a=A(e),r=u(a),i=h(a),s=e.options[1]?.errorMessage??"";for(const[o,a]of Object.entries(n)){if(I(r,o,i,a))return void(f(s)?e.report({node:t,message:s,data:{target:r,pattern:a}}):e.report({node:t,messageId:"noMatch",data:{filename:r,folderPattern:a}}))}};return{Document:t,Program:t,root:t,StyleSheet:t}}},D={meta:{type:"layout",docs:{description:"The folder should follow the folder naming convention",category:"Layout & Formatting",recommended:!1,url:s("folder-naming-convention")},fixable:null,schema:[{additionalProperties:{type:"string"}},{type:"object",properties:{errorMessage:{type:"string"},ignoreWords:{type:"array",items:{type:"string"}}}}],messages:{invalidObject:r,invalidPattern:i,noMatch:'The folder "{{ folder }}" does not match the "{{ namingPattern }}" pattern'}},create(e){const t=t=>{const n=e.options[0],a=C(n,N,R);if(a)return void e.report({node:t,messageId:a.type,data:{value:a.payload}});const r=A(e),i=h(r),s=e.options[1]?.errorMessage??"",l=e.options[1]?.ignoreWords??[];for(const[a,r]of Object.entries(n))if(o.isMatch(i,a,{contains:!0}))for(const n of v(i)){const i=o.capture(a,n,{dot:!0});if(p(i))continue;const c=i.filter(f).reduce(((e,t)=>e.concat(b(t))),[]);for(const n of c)if(!l.includes(n)&&!o.isMatch(n,j[r]||k[r]||r))return void(f(s)?e.report({node:t,message:s,data:{target:n,pattern:r}}):e.report({node:t,messageId:"noMatch",data:{folder:n,namingPattern:r}}))}};return{Document:t,Program:t,root:t,StyleSheet:t}}};const J={meta:{name:"eslint-plugin-check-file",version:"3.3.0"},rules:{"filename-blocklist":w,"filename-naming-convention":F,"folder-match-with-fex":Z,"folder-naming-convention":D,"no-index":{meta:{type:"layout",docs:{description:'A file cannot be named "index"',category:"Layout & Formatting",recommended:!1,url:s("no-index")},fixable:null,schema:[{type:"object",properties:{ignoreMiddleExtensions:{type:"boolean"},errorMessage:{type:"string"}}}],messages:{noIndex:'The filename "index" is not allowed'}},create(e){const t=t=>{const n=e.options[0]?.ignoreMiddleExtensions??!1,o=e.options[0]?.errorMessage??"",a=A(e),r=u(a);"index"!==y(r,n)||(f(o)?e.report({node:t,message:o,data:{target:r}}):e.report({node:t,messageId:"noIndex"}))};return{Document:t,Program:t,root:t,StyleSheet:t}}}},processors:{"eslint-processor-check-file":{preprocess:(e,t)=>[{text:"",filename:t}],postprocess:e=>[].concat(...e),supportsAutofix:!0}}};export{J as default};