UNPKG

destiny

Version:

Prettier for file structures

12 lines (7 loc) 12.1 kB
#!/usr/bin/env node "use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0}),require("core-js/modules/es.array.flat"),require("core-js/modules/es.array.unscopables.flat"),require("core-js/modules/es.promise");var n=e(require("fs")),t=e(require("os")),r=e(require("glob")),s=require("cosmiconfig"),o=require("fs-extra"),i=e(o),l=e(require("path")),c=e(require("chalk")),a=e(require("simple-git/promise")),u=e(require("resolve"));require("core-js/modules/es.symbol.description");let f="";const d=e=>e.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,""),p=(e,n=0)=>{const t=e instanceof Error?e:c.red.bold("ERROR: ")+e;f+=`${d(t.toString())}\n`,console.error(t),console.log("If you think this is a bug, you can report it: https://github.com/benawad/destiny/issues"),process.exit(n)};let g=!1,h=null;const m=(e,...n)=>{if(!g)return;const t=Date.now(),r=c.magenta.bold("DEBUG: ")+c.yellow.bold(`+${h?t-h:0}ms `)+e;f+=`${d(r)}\n`,console.info(r),h=t,n.length>0&&(console.group(),n.forEach(e=>{console.dir(e,{depth:1/0,maxArrayLength:1/0}),console.log(),f+=`${JSON.stringify(e,null,2)}\n`}),console.groupEnd())};var b=p,y=e=>{const n=c.green.bold("INFO: ")+e;f+=`${d(n)}\n`,console.info(n)},v=e=>{f+=`${d(e)}\n`,console.log(e)},$=e=>{const n=c.yellow.bold("WARN: ")+e;f+=`${d(n)}\n`,console.warn(n)},x=()=>{g=!0},j=m,w=e=>{if(!g)return;const t=l.resolve(e);n.existsSync(t)&&p(`The debug file output already exist "${t}".\nPlease give a path to a non existing file.`),n.writeFileSync(t,f,"utf8"),m(`stdout written in "${t}"`)};const S=e=>i.lstatSync(e).isDirectory(),F=e=>i.lstatSync(e).isFile(),E=e=>{const n=r.sync(e).map(e=>l.resolve(e)),t=n.filter(e=>F(e));return j(`glob matches for "${e}":`,n),0===t.length&&b("Could not find any files for: "+e,1),t},O=e=>{const n=[],t=[e];for(;t.length>0;){const e=t.shift();if(null==e||0===e.length)break;if(r.hasMagic(e))n.push(...E(e));else{if(!i.existsSync(e)){b(`Unable to resolve the path: ${e}`);break}S(e)?t.push(l.resolve(e,"./**/*.*")):F(e)&&n.push(e)}}return n};async function k(e,n){return e.silent(!0).raw(["ls-files","--error-unmatch",n]).then(()=>!0).catch(()=>!1)}async function q(e,n){const t=a(n),r=await t.checkIsRepo(),s=Object.entries(e),o=[];for(;s.length||o.length;){const[e,c]=s.length?s.pop():o.pop();if(e.includes(".."))continue;const a=l.resolve(n,e),u=l.resolve(n,c);if(a===u)continue;const f=l.dirname(u);if(i.ensureDirSync(f),i.existsSync(u)){s.length?o.push([e,c]):$(`not moving "${a}" to "${u}" because "${u}" already exists`);continue}const d=r&&await k(t,a);j(`moving "${a}" to "${u}"`),d?await t.mv(a,u):i.renameSync(a,u)}}function R(e){const t=n.readdirSync(e);if(!t)return n.rmdirSync(e);for(const r of t){const t=l.resolve(e,r);n.lstatSync(t).isDirectory()&&(R(t),0===n.readdirSync(t).length&&(n.rmdirSync(t),j(`removing "${t}" as empty folder`)))}}function A(e){const t="(?:(?:import|from)\\s+|(?:import|require)\\s*\\()\\\\?['\"`]((?:\\.{1,2})(?:\\/.+)?)\\\\?['\"`]",r=new RegExp(t,"gm"),s=new RegExp(`["'\`].*(${t}).*["'\`]`,"g"),o=new RegExp(`\`[^\`]*(${t})[^\`]*\``,"gm"),i=e=>" ".repeat(e.length),l=[],c=n.readFileSync(e,{encoding:"utf8"}).replace(/\/\*[\s\S]*?\*\/|\/\/.*/gm,i).replace(s,i).replace(o,i);let a;for(;null!==(a=r.exec(c));)l.push({path:a[1],start:r.lastIndex-1-a[1].length,end:r.lastIndex-1}),a.index===r.lastIndex&&r.lastIndex++;return l}const I=(e,n)=>{const t=l.dirname(e),r=l.relative(t,n),s=l.dirname(r),o=function(e){const n=l.extname(e);return[".js",".jsx",".ts",".tsx"].includes(n)?n:void 0}(r);let i=l.basename(r,o);"index"===i&&(i=".");let c=l.join(s,i);return!c.startsWith(".")&&(c="./"+c),"win32"===process.platform&&(c=c.replace(/\\/g,"/")),c},_=[".js",".json",".jsx",".sass",".scss",".svg",".ts",".d.ts",".tsx",".js.flow",".png",".jpeg",".jpg"],W=(e,n)=>{try{return u.sync(e,{basedir:n,extensions:_})}catch(e){return null}},C=(e,n)=>{for(const{tree:t,parentFolder:r}of n){const n=l.relative(r,e);if(n in t)return l.resolve(l.join(r,t[n]))}return e},D=(e,n,t)=>{for(const{tree:r,parentFolder:s}of t){const t=l.relative(s,e);if(t in r)return I(n,l.resolve(l.join(s,r[t])))}return I(n,e)},M=(e,n)=>{const t=n-e;return e>n?-Math.abs(t):t},N=async(e,n)=>{y("Fixing imports."),((e,n)=>{for(const t of e){j(`checking imports of "${t}"`);const e=A(t);if(0===e.length){j(`no import found in "${t}"`);continue}const r=l.dirname(t),s=C(t,n),i=o.readFileSync(t).toString();let c=i,a=0;for(const o of e){const e=W(o.path,r);if(null==e){b(`Cannot find import ${o.path} for ${r}`);continue}const i=D(e,s,n);null!=i&&o.path!==i&&(j(`replacing import of "${o.path}" by "${i}" in "${t}"`),c=`${c.substr(0,o.start+a)}${i}${c.substring(o.end+a)}`,a+=M(o.path.length,i.length))}c!==i&&(j(`writing new imports of "${t}"`),o.writeFileSync(t,c))}})(e,n);for(const{tree:e,parentFolder:t}of n)y("Moving files."),await q(e,t),R(t);y("Restructure was successful!")},P=e=>e.replace(/([^/]+)(?=\.)/g,String.fromCharCode(Number.MAX_SAFE_INTEGER)+"$1"),B=(e,n)=>P(e).localeCompare(P(n)),G=e=>{const n=e.reduce((e,n)=>{const t=n.split("/");return t.forEach((n,r)=>e.add(((e,n)=>l.join(...e.slice(0,n)))(t,r+1))),e},new Set);return Array.from(n).sort(B)},T=e=>{const n=(e=>{const n=[];let t=[...e];for(;t.length>0;){const e=t.shift();if(null==e)break;t=t.map(n=>{return t=e,n.replace(new RegExp(`^(/*)${t.replace(/\//g,"")}(/+)`),"$1$2");var t}),n.push(e)}return n.map(e=>{var n;const t=e.split("/");return{text:null!=(n=t.pop())?n:"",position:t.length}})})(G(e)),t=n.reduce((e,t,r)=>{var s;const o=null!=(s=e[r-1])?s:"",i=n.slice(r+1),l=i.length>0&&i[0].position>t.position,a=(t.position>0?"│ ".repeat(t.position):"")+(((e,n)=>{for(const t of n)if(!(t.position>e.position))return t.position<e.position;return!0})(t,i)?"└──":"├──")+(l?c.bold.blue(t.text):t.text);return e.push(((e,n)=>Array.from(e).map((e,t)=>"│"===e&&(e=>"└"===e||" "===e)(n[t])?" ":e).join(""))(a,o)),e},[]).join("\n");return v(t),t},U=e=>{if(1===e.length)return l.dirname(e[0]);const[n,t]=e.length>2?e.sort((e,n)=>e.length-n.length):e,r=t.split(l.sep);return n.split(l.sep).filter((e,n)=>e===r[n]).join(l.sep)},V=e=>[/^\.git|node_modules/].some(n=>n.test(e));const z=e=>/\.test\.|\.spec\.|\.story\.|\.stories\./.test(e);function J(e){const n=(e=>{const n={};return Object.entries(e).forEach(([e,t])=>{t.forEach(t=>{t in n||(n[t]=[]),n[t].push(e)})}),n})(e),t=Object.keys(e),r=t.filter(e=>{const t=n[e];return!t||!t.length||t.every(e=>z(e))});if(r.length)return r;const s={};t.forEach(e=>{const n=e.split("/").length;n in s||(s[n]=[]),s[n].push(e)});for(let e=1;e<10;e++)if(e in s)return s[e];return[]}const L=(e,n,t)=>{const r=n[e];if(t.has(e))return[...t,e];if(t.add(e),null==r||0===r.length)return null;for(const e of r){const r=L(e,n,new Set(t));if(r)return r}return null},X=/\.test\.|\.spec\.|\.d\.|@2x\.|@3x\.|\.snap|\.story\.|\.stories\./,Z=e=>l.basename(e,l.extname(e));function H(e,n){const t={},r=new Set,s={},o=new Set;let i=!1;const a=(e,n)=>{Array.isArray(s[e])||(s[e]=[]),s[e].push(n)},u=(e,n,t)=>{if(r.has(e)){const e=l.join(n,t.replace(/\//g,"-"));return y(`File renamed: ${t} -> ${e}`),e}return e},f=(e,n)=>{t[e]=n,r.add(n)},d=(e,n,r)=>{const s=l.basename(e);if(c=s,X.test(c))return void o.add(e);var c;let p=l.basename(e,l.extname(e));const g=l.basename(l.dirname(e)),h=e.includes(".."),m=h?e:l.join(n,"index"===p&&g&&"."!==g?g+l.extname(e):s),b=u(m,n,e);p=l.basename(b,l.extname(b)),h||f(e,b);const y=r[e];if((null==y?void 0:y.length)>0){const e=l.join(n,p);for(const n of y)if(n in t||n.includes("..")){const e=L(n,r,new Set);e?(i=!0,$(`Dependency cycle detected: ${e.join(" -> ")}`)):a(n,b)}else a(n,b),d(n,e,r)}};for(const t of n)d(t,"",e);i||Object.entries(s).forEach(([e,n])=>{if(n.length<=1||e.includes(".."))return;const t=U(n),r=l.basename(e),s=l.dirname(e),o=l.join(t,"shared","index"===l.basename(r,l.extname(r))&&s&&"."!==s?l.join(s+l.extname(r)):r);f(e,o)});const p=Object.keys(t);if(o.size>0)for(const n of o){const r=(g=n).endsWith(".snap")?g.replace(".snap",""):g.replace(X,"."),s=l.join(l.dirname(r),"..",l.basename(r));let o=t[r]||t[s];if(!o){const e=Z(r);for(const n of p)if(l.basename(n).startsWith(e)){o=t[n];break}}if(!o&&z(n)){const[r]=e[n];r&&(o=t[r])}if(!o){$(`could not find source file that is linked to ${c.blueBright(n)} | 2 locations were checked: ${c.blueBright(r)} and ${c.blueBright(s)}`);continue}const i=n.endsWith(".snap"),a=u(l.join(l.dirname(o),i?"__snapshot__":"",l.basename(n)),l.dirname(o),n);f(n,a)}var g;return t}const K=e=>{const n=e.split(l.sep);if(1!==n.length)return n.pop(),n.join(l.sep)},Q=e=>{const n=e.split(l.sep);return[...n.slice(0,n.length-2),n[n.length-1]].join(l.sep)};function Y(e,{avoidSingleFile:n}){return Object.entries(e).reduce((e,[t,r])=>{if(r.length<=1)return e;y(`Generating tree for: ${t}`);const{graph:s,parentFolder:o}=function(e){const n=U(e),t={},r=[];for(let s of e){if(V(s))continue;s=l.resolve(s);const e=l.relative(n,s);r.push(e),Array.isArray(t[e])||(t[e]=[]),A(s).forEach(r=>{const o=W(r.path,l.dirname(s));if(null==o)return void b(`Cannot find import ${r.path} for ${s}`);const i=l.relative(n,o);t[e].includes(i)||t[e].push(i)})}return{files:r,graph:t,parentFolder:n}}(r),i=J(s);let a=H(s,i);n&&(a=(e=>{const n={...e},t={};for(const[e,r]of Object.entries(n))t[r]=e;const r={},s=Object.values(n).sort((e,n)=>n.length-e.length);for(const e of s){const n=K(e);n&&(r[n]=n in r?r[n]+1:1)}for(const e of s){const s=K(e);if(!s)continue;let o=r[s],i=e,l=s;for(;1===o&&(l=K(i),l);)s===l?o=1:l in r&&(o=r[l]+1),r[l]=o,1===o&&(i=Q(i));n[t[e]]=i}return n})(a));const u=i.filter(e=>!(z(e)||e.endsWith(".snap")||e.endsWith(".d.ts")||s[e].length));return v(c.bold.blue(o.split(l.sep).pop())),T(Object.values(a)),u.length>0&&$(`Found ${u.length} unused files:`+"\n"+u.join("\n")),e.push({parentFolder:o,tree:a}),e},[])}const{argv:ee}=process,ne={help:!1,include:[],version:!1,write:!1,avoidSingleFile:!1,debug:!1},te=e=>e.replace(/~\/|~(?!.)/,`${t.homedir()}/`),re=e=>((e=>{console.log(c`{blue destiny} - Prettier for file structures. {bold USAGE} ${" "}{blue destiny} [option...] [{underline path}] ${" "}The {underline path} argument can consist of either a {bold file path} or a {bold glob}. {bold OPTIONS} `);const n=e.map(e=>({...e,flags:e.flags.join(", ")})),[t]=[...n].sort((e,n)=>n.flags.length-e.flags.length),r=`${t.flags}${" ".repeat(2)}`.length,s=n.map(({flags:e,description:n})=>{const t=r-e.length;return` ${e}${" ".repeat(t)}${n}`}).join("\n");console.log(s,"\n")})([{flags:["-V","--version"],description:"Output version number"},{flags:["-h","--help"],description:"Output usage information"},{flags:["-w","--write"],description:"Restructure and edit folders and files"},{flags:["-S","--avoid-single-file"],description:"Flag to indicate if single files in folders should be avoided"},{flags:["--debug [?output file]"],description:"Print debugging info"}]),process.exit(e)),se=async e=>{const t=(e=>{var n,t;const r=null!=(n=null==(t=s.cosmiconfigSync("destiny").search())?void 0:t.config)?n:{};return{...ne,...r,...e}})((e=>{const t={};for(;e.length>0;){const o=e.shift();if(null==o)break;switch(o){case"-h":case"--help":t.help=!0;break;case"-V":case"--version":t.version=!0;break;case"-w":case"--write":t.write=!0;break;case"-S":case"--avoid-single-file":t.avoidSingleFile=!0;break;case"--debug":t.debug=!(e[0]&&!e[0].startsWith("-"))||e.shift();break;default:var s;if(n.existsSync(te(o))||r.hasMagic(o))t.include=[...null!=(s=t.include)?s:[],o]}}return t})(e));if(t.help)return re(0);if(t.version)return console.log("v0.7.1");if(0===t.include.length)return re(1);t.debug&&x(),process.on("exit",()=>{"string"==typeof t.debug&&w(t.debug),j("exiting")}),j("version: 0.7.1"),j("config used:",t),t.include=t.include.map(te);const o=t.include.reduce((e,n)=>({...e,[n]:O(n)}),{});const i=Object.values(o).flat();if(j("restructured map:",o),0===i.length)return void b("Could not find any files to restructure",1);const l=Y(o,t);j("generated tree(s):",l),t.write&&await N(i,l)};se(ee.slice(2,ee.length)),exports.run=se;