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