@dovenv/workspace
Version:
A dovenv plugin with utilities for the workspace
34 lines (25 loc) • 12.3 kB
JavaScript
import{defineConfig as F}from"@dovenv/core";import{joinUrl as C,validate as E,formatValidationError as D,schema2type as $,serializeValidation as j,getPaths as m,getDirName as k,joinPath as p,existsFile as z,getPathsTree as S,exec as v,removeDirIfExist as O,open as I,getMD as A,md2terminal as M,getMatch as U,getObjectFromJSONFile as N,relativePath as R,getCountFromPaths as T,setDirTree as W}from"@dovenv/core/utils";const V="https://dovenv.pigeonposse.com/guide/plugin/workspace";class u{constructor(t,s){this.opts=t,this.utils=s,this.utils.helpURL=V,this.utils.title="ws",this._themeInfo=`Theme info:
For using this theme, you need to add "pkg" and "workspaceDir" in the section "const" in your dovenv configuration.
Also you nedd to add "engines" to your package.json and add your runtime. Example: "engines": { "node": ">=16" }.
Read more: ${C(this.utils.helpURL)}`}_themeInfo;_title(t){console.log(this.utils.style.section.h(t)+`
`)}_sectionTitle(t){console.log(),console.info(this.utils.style.section.h(t))}async _envolvefn(t){return await this.utils.catchFn(t)}}class b extends u{async#t(t){if(!t?.schema)return;const s=await this.utils.getPkgsData();await Promise.all(s.map(async i=>{const o=await t.schema?.({v:E,path:i.packagePath,content:i.content,utils:this.utils});if(!o)return;const n=o.safeParse(i.content);if(!n.success){const a=D(n.error),e=`
`+await $({schema:j(o),required:!0,noUnknownObject:!0}),l=`Error in ${this.utils.style.b(i.id)} package.json
`,c=this.utils.style.error.ul([["Path",i.packagePath],["Schema must be",e],[a,""]]);throw new Error(l+this.utils.style.indent(c))}}))}async#i(t){const s=t;if(!s)return;const i=await this.utils.getPkgsData(),o=async(n,a=!1)=>{await Promise.all(i.map(async e=>{const l={dir:e.dir,path:e.packagePath,content:e.content,utils:this.utils},c=typeof n=="function"?await n(l):n;if(!c)return;const w={cwd:e.dir,onlyFiles:!0};for(const h of c){const g=await m([h],w);let d=`Error in [${e.id}] file structure.
`;if(d+=this.utils.style.error.lk(`Pattern: ${k(p(e.dir,h))}
`),a&&!g.length)throw d+=this.utils.style.error.lk(`Validation error: Must exists Pattern: ${h}`),new Error(d);if(!a&&g.length)throw d+=this.utils.style.error.lk(`Validation error: Must not exists Pattern: ${h}`),new Error(d);await Promise.all(g.map(async y=>{if(await z(p(e.dir,y))===a)return;const _=await S({input:c,patternOpts:{onlyFiles:!0}});let f=`Error in [${e.id}] file structure.
`;throw f+=this.utils.style.error.lk(`Path: ${e.packagePath}.
`),f+=this.utils.style.error.lk(`Valid structure:
${this.utils.style.box({title:e.id,data:_})}
`),f+=this.utils.style.error.lk(`Validation error: Must ${a?"exists":"not exists"} path: ${y}`),new Error(f)}))}}))};s?.include&&await o(s.include,!0),s?.exclude&&await o(s.exclude,!1),s?.custom&&await Promise.all(i.map(async n=>await s.custom?.({dir:n.dir,path:n.packagePath,content:n.content,utils:this.utils}))),(s?.include||s?.exclude)&&console.log(this.utils.style.success.h("Check passed for routes (include/exclude)"))}async runOne(t){await this.#i(t),await this.#t(t)}async#s(t){const s=this.opts?.check?.pkg;await this.utils.mapOpts({input:s,pattern:t,cb:async({value:i})=>{i&&await this.runOne(i)}})}async run(t){return await this._envolvefn(this.#s(t))}}class L extends u{#t=this.utils.packageManager.cmds;async#i(t,s){this._sectionTitle(t);const i=[],o=console.error;try{console.error=n=>i.push(n),await v(s)}catch(n){i.push(n)}if(i.length){const n=i.map(a=>{const e=!!(a?.stdout||a?.stderr);return e?{isError:!0,error:a instanceof Error?a:new Error("Unknown error")}:{isError:e}});for(const a of n)a.isError&&console.error(a.error?.message)}console.error=o}async#s(){await this.#i("Packages outdated",this.#t.outdated)}async#e(){await this.#i("Audition",this.#t.audit)}async#n(){await this.#i("Fix Audition",this.#t.auditFix)}async#o(t){t?await this.#n():(await this.#e(),await this.#s(),console.log(),console.log(this.utils.style.section.li("For fix audit use",`dovenv ws audit --fix | ${this.#t.auditFix}`)),console.log(this.utils.style.section.li("For outdated dependencies use",this.#t.upDeps)),console.log())}async#a(){this._sectionTitle("Reinstall all workspace"),this.opts?.reinstall?.hook?.before&&await this.opts.reinstall.hook.before();const t=await m([p(this.utils.wsDir,"**/node_modules")],{onlyDirectories:!0,ignore:["**/node_modules/**/node_modules"]});t&&await Promise.all(t.map(async s=>{console.info(`Removing ${s}`),await O(s)})),await v(this.utils.packageManager.cmds.install),this.opts?.reinstall?.hook?.after&&await this.opts.reinstall.hook.after()}async reinstall(){await this._envolvefn(this.#a())}async auditFix(){await this._envolvefn(this.#n())}async audit(){await this._envolvefn(this.#e())}async outdated(){await this._envolvefn(this.#s())}async auditAndOutdated(t){await this._envolvefn(this.#o(t))}}class q extends u{async get(){let t;const s=this.opts?.info?.usefulCmds?this.opts.info.usefulCmds:void 0;if(!s||!s.length)return;const i=s.length-1;t="";for(const[o,n]of s.entries())t+=this.utils.style.table([["Command",this.utils.style.section.b(n.cmd)],["Description",n.desc],...n.info?[["Info",this.utils.style.section.a(n.info)]]:[]],{chars:{top:"","top-mid":"","top-left":"","top-right":"",bottom:"","bottom-mid":"","bottom-left":"","bottom-right":"",left:"","left-mid":"",mid:"","mid-mid":"",right:"","right-mid":"",middle:""}})+`
`,o!==i&&(t+=`
`);return this.utils.style.box({data:t,border:!1})}async#t(){this._title("Useful commands");const t=await this.get();t?console.log(t):console.warn(this.utils.style.warn.p("No commands found"))}async run(){return await this._envolvefn(this.#t())}}class H extends u{async get(){return typeof this.utils.pkg?.funding=="string"?this.utils.pkg.funding:Array.isArray(this.utils.pkg?.funding)?typeof this.utils.pkg.funding[0]=="string"?this.utils.pkg.funding[0]:this.utils.pkg.funding[0]?.url||void 0:typeof this.utils.pkg?.funding=="object"&&typeof this.utils.pkg.funding?.url=="string"?this.utils.pkg.funding.url:void 0}async#t(t){const s=await this.get();t&&s&&await I(s),this._title("Donate information"),console.log(s?this.utils.style.section.a(s):this.utils.style.section.p("There is no funding URL associated with this package"))}async run(t=!0){return await this._envolvefn(this.#t(t))}}class J extends u{async get(){const t=this.opts?.info?.instructions;if(!t)return;const s=await A(t);return(await M(s)).trim()+`
`}async#t(){this._title("Instructions");const t=await this.get();if(!t)return console.warn(this.utils.style.warn.msg("No instructions found"));console.log(t)}async run(){return await this._envolvefn(this.#t())}}class B extends u{async get(t){let s=await this.utils.getPkgPaths(),i="";if(t&&t?.length!==0&&(s=U(s,t)),console.debug({key:t,paths:s}),!s.length)return;const o=s.length-1;for(const[n,a]of s.entries()){const e=await N(a),l=[];if(e.scripts)for(const h in e.scripts)l.push([this.utils.style.section.b(h),this.utils.style.section.lv(e.scripts[h]||"")]);const c=[["Path",this.utils.style.section.lv(R(this.utils.process.cwd(),a))],...l.length?[["",""],...l]:[]],w=this.utils.style.table(c,{chars:{top:"","top-mid":"","top-left":"","top-right":"",bottom:"","bottom-mid":"","bottom-left":"","bottom-right":"",left:"","left-mid":"",mid:"","mid-mid":"",right:"","right-mid":"",middle:""}});i+=this.utils.style.box({data:w,title:this.utils.style.section.msg(e.name||""),border:!1}),o!==n&&(i+=`
`)}return this.utils.style.box({data:i,border:!1})+`
`}async#t(t){this._title("Workspace Scripts");const s=await this.get(t);s?console.log(s):t&&t?.length!==0?console.warn(this.utils.style.warn.msg("No packages found in workspace with patterns:",t?.join(", "))):console.warn(this.utils.style.warn.msg("No packages found in workspace."))}async run(t){await this._envolvefn(this.#t(t))}}class G extends u{async get(){try{return await T({input:[p(this.utils.wsDir,"**/**")],opts:{gitignore:!0,cwd:this.utils.wsDir}})}catch(t){console.warn(this.utils.style.warn.msg("Error getting size",t instanceof Error?t?.message:t));return}}async#t(){this._title("Size information");const t=await this.get();if(!t)return;const s=[["Files",t.files],["Words",t.words],["Chars",t.chars]];console.log(this.utils.style.section.ul(s))}async run(){return await this._envolvefn(this.#t())}}class K extends u{async get(){if(this.opts?.info?.structure)return this.utils.style.box({data:W({structure:this.opts.info.structure}),border:!1})}async#t(){this._title("Workspace Structure");const t=await this.get();t?console.log(t):console.warn(this.utils.style.warn.msg("No structure found"))}async run(){return await this._envolvefn(this.#t())}}class Q extends u{size;instructions;donate;usefulCmds;structure;scripts;constructor(t,s){super(t,s),this.size=new G(t,s),this.instructions=new J(t,s),this.donate=new H(t,s),this.usefulCmds=new q(t,s),this.structure=new K(t,s),this.scripts=new B(t,s)}async#t(){this._title("Workspace Information");const t=(await this.utils.getPkgPaths()).map(c=>k(c)),s=t.length,i=await this.size.get(),o=await this.structure.get(),n=await this.instructions.get(),a=await this.usefulCmds.get(),e=await this.scripts.get(),l=[["Monorepo",this.utils.monorepo],["Runtime",this.utils.runtime.value],["Package manager",this.utils.packageManager.value],["Package Num",s],["Package Paths",s?`
`+this.utils.style.box({data:t.map(c=>this.utils.style.section.p(c)).join(`
`),border:!1})+`
`:"none"],["Structure",o?`
`+o:"none"],["Files",i?.files],["Words",i?.words],["Chars",i?.chars],["Useful commands",a?`
`+a:"none"],["Scripts",e?`
`+e:"none"],["Instructions",n?`
`+n:"none"],["Donate",await this.donate.get()]];for(const c of l)console.log(this.utils.style.section.lk(c[0]),c[1])}async run(){await this.#t()}}class P{#t;#i;#s;opts;utils;constructor({opts:t,utils:s}){this.opts=t,this.utils=s,this.#t=new Q(t,s),this.#i=new b(t,s),this.#s=new L(t,s)}async getPkgPaths(){return await this.#s.getPkgPaths()}async audit(t){await this.#s.auditAndOutdated(t)}async outdated(){await this.#s.outdated()}async reinstall(){await this.#s.reinstall()}async check(){await this.#i.run()}async scripts(t){await this.#t.scripts.run(t)}async usefulCmds(){await this.#t.usefulCmds.run()}async instructions(){await this.#t.instructions.run()}async donate(t=!0){await this.#t.donate.run(t)}async size(){await this.#t.size.run()}async info(){await this.#t.run()}async structure(){await this.#t.structure.run()}}const X=r=>{const t={},s={};if(!r)return{onlyFn:void 0,withoutFn:void 0};for(const i in r){const{fn:o,...n}=r[i];t[i]=o,s[i]=n}return{onlyFn:t,withoutFn:s}},x=r=>{const t=X(r?.custom);return F({check:r?.check?.pkg?Object.fromEntries(Object.entries(r.check.pkg).map(([s,i])=>[`ws.pkg.${s}`,{type:"custom",desc:i.desc?`Check workspace rules: ${i.desc}`:"Check workspace rules",fn:async({utils:o})=>{await new b(r,o).runOne(i)}}])):void 0,custom:{ws:{desc:"Toolkit for Workspace",cmds:{audit:{desc:"Audit the workspace dependencies.",opts:{fix:{type:"boolean",desc:"Add overrides to the package.json file in order to force non-vulnerable versions of the dependencies."}}},outdated:{desc:"Check for outdated packages."},reinstall:{desc:"Reinstall the workspace."},info:{desc:"Set information about the workspace",opts:{scripts:{type:"array",desc:"list workspace scripts from the package.json files"},cmds:{type:"boolean",desc:"Information for some useful commands"},size:{type:"boolean",desc:"Workspace size information"},donate:{type:"boolean",desc:"Donate information for PigeonPosse."},instructions:{type:"boolean",desc:"Instructions for the workspace"},structure:{type:"boolean",desc:"Structure information for the workspace"}}},...t.withoutFn||{}},fn:async s=>{try{const{cmds:i,opts:o,showHelp:n,utils:a}=s,e=new P({opts:r,utils:a}),l=[{key:"audit",fn:async()=>await e.audit(o?.fix)},{key:"outdated",fn:async()=>await e.outdated()},{key:"reinstall",fn:async()=>await e.reinstall()},{key:"info",fn:async()=>{o?.instructions?await e.instructions():o?.structure?await e.structure():o?.donate?await e.donate():o?.size?await e.size():o?.cmds?await e.usefulCmds():o?.scripts?await e.scripts(o?.scripts):await e.info()}}];if(t.onlyFn)for(const c in t.onlyFn)l.push({key:c,fn:async()=>await t.onlyFn[c](s)});for(const c of l)if(i?.includes(c.key)){await c.fn();return}await n()}catch(i){console.error(i?.message||i)}}}}})};export{P as Workspace,x as default,x as workspacePlugin};