UNPKG

@dovenv/convert

Version:
77 lines (56 loc) 11.4 kB
import{getStringsFrom as K,ensureDir as D,writeFileContent as Q,joinPath as v,getTempDir as W,removeDirIfExist as G,html2md as X,md2html as Y,writeFile as F,yaml as Z,process as M,readFile as q,getObjectFromJSONFile as tt,existsFile as _}from"@dovenv/core/utils";import A from"jsdoc-to-markdown";import et from"swagger2openapi";import{Application as st,TSConfigReader as ot,RendererEvent as rt}from"typedoc";const nt="https://dovenv.pigeonposse.com/guide/plugin/convert";class O{props;constructor(t){this.props=t}async _getContent(t){return await K(typeof t=="string"?[t]:t)}async _writeOutput(t,e,s){await D(t),await Q(v(t,e),s)}async _getOutput(){const t=v(W(),"dovenv-convert"),e=this.props.output?this.props.output:t;return await D(e),{dir:e,rmTempIfExist:async()=>{this.props.output||await G(t)}}}}class N extends O{props;constructor(t){super(t),this.props=t}async run(){const t=await this._getContent(this.props.input),e=[];for(const s of t){const n=await X(s.content);e.push({id:s.id,content:n}),this.props.output&&await this._writeOutput(this.props.output,s.id,n)}return e}}class J extends O{props;constructor(t){super(t),this.props=t}async run(){const t=await this._getContent(this.props.input),e=[];for(const s of t){const n=await Y(s.content);e.push({id:s.id,content:n}),this.props.output&&await this._writeOutput(this.props.output,s.id,n)}return e}}class B extends O{props;constructor(t){super(t),this.props=t}async run(){const t=await this._getContent(this.props.input),e=[];for(const s of t){const n=await A.getTemplateData({files:s.content}),i=await A.render({data:n,...this.props?.opts?this.props.opts:{}});this.props.output&&await this._writeOutput(this.props.output,s.id+".md",i),e.push({id:s.id,content:i})}return e}}const it=async o=>{try{return await Z.deserialize(o)}catch{}try{return JSON.parse(o)}catch{}return null},C=o=>o.replace(/\n/g,` `),at=o=>{const t=[];for(const[s,n]of Object.entries(o.paths))if(n)for(const[i,r]of Object.entries(n))i!=="parameters"&&t.push({path:s,method:i.toUpperCase(),operation:r});const e={};if("components"in o&&o.components){const{components:s}=o;Object.entries(s).forEach(([n,i])=>{Object.entries(i).forEach(([r,p])=>{e[`#/components/${n}/${r}`]=p})})}return{document:o,pathMethods:t,references:e}},I=o=>o.replace(/[!@#$%^&*()+|~=`[\]{};':",./<>?]/g,"").replace(/ /g,"-").toLowerCase(),ct=({document:o,pathMethods:t})=>{let e=`# ${o.info.title||"Api-Document"} > Version ${o.info.version||"1.0.0"} ${o.info.description?` `+o.info.description+` `:""} ## Path Table | Method | Path | Description | | --- | --- | --- | `;return e+=t.reduce((s,{path:n,method:i,operation:r})=>s+`| ${i.toUpperCase()} | [${n}](#${i.toLowerCase()}${I(n)}) | ${r.summary||""} | `,""),e+` `},pt=o=>{const{references:t}=o;let e=`## Reference Table | Name | Path | Description | | --- | --- | --- | `;return Object.entries(t).forEach(([s,n])=>{const i=y(o,n);e+=`| ${i.name||i.title||s?s.substr(s.lastIndexOf("/")+1):""} | ${s?`[${s}](#${I(s)})`:""} | ${i.description||""} | `}),e+` `},P=o=>{if(typeof o=="object"&&o&&"$ref"in o)return o.$ref},y=({references:o},t,e)=>{if(P(t)){const s=t.$ref;if(e){if(e.has(s))return t;e.add(s)}return o[s]}return t},T=(o,t)=>{let e="";for(const s of t){const n=y(o,s);e+=j(o,n)}return e},j=(o,t)=>{const e=y(o,t);if(!e)return"";let s="";return"content"in e?Object.entries(e.content).forEach(([n,i])=>{s+=`- ${n} `,s+=j(o,i.schema)}):(s+="```ts\n","schema"in e?(s+=R(t,0),s+=E(o,e.name,e.schema,Array.isArray(e.required)?e.required?.includes(e.name):e.required)):"in"in e?s+=JSON.stringify(e,void 0," ")+` `:e.type==="object"||e.type==="array"?s+=E(o,void 0,e):s+=JSON.stringify(e,void 0," ")+` `,s+="```\n\n"),s},m=o=>"".padEnd(o*2),R=(o,t)=>{const e=P(o);return e?m(t)+`// ${e} `:""},x=(o,t,e)=>{if(o){const s=P(t);if(s)return m(e)+`// ${s} `}return t.description?t.description.split(` `).reduce((s,n)=>s+m(e)+`// ${n} `,""):""},S=(o,t,e,s)=>P(t)||("type"in t?t.type==="object"||t.type==="array"?E(o,void 0,t,void 0,e,s+.5).trimEnd():t.type:""),E=(o,t,e,s,n,i)=>{const r=i||0,p=n||new Set,a=y(o,e,p);if(!a)return"";let c="";if("$ref"in a)c+=m(r)+`${t}:${a.$ref} `;else if(a.type==="object")c+=x(e,a,r),c+=t?m(r)+`${t}: { `:`{ `,a.properties&&Object.entries(a.properties).forEach(([f,l])=>{c+=E(o,f,l,Array.isArray(a.required)?a.required?.includes(f):a.required,p,r+1)}),c+=m(r)+`} `;else if(a.type==="array")c+=R(e,r),c+=E(o,t,a.items,void 0,p,r).trimEnd()+`[] `;else if(a.type){c+=x(e,a,r);const f=Array.isArray(a.type)?a.type:a.type?[a.type]:[];c+=t?m(r)+`${t}${s===!0?"":"?"}: `:"",a.enum?c+=`enum[${a.enum.join(", ")}]`:c+=`${f.reduce((l,h,w)=>l+(w?" | ":"")+h,"")}`,a.default&&(c+=` //default: ${a.default}`),c+=` `}else a.anyOf?(c+=x(e,a,r),c+=m(r)+`${t}${s===!0?"":"?"}: `,a.anyOf.forEach((f,l)=>{const h=S(o,f,p,r);c+=(l?" & ":"")+`Partial(${h})`}),c+=` `):a.allOf?(c+=x(e,a,r),c+=m(r)+`${t}${s===!0?"":"?"}: `,a.allOf.forEach((f,l)=>{const h=S(o,f,p,r);c+=(l?" & ":"")+`${h}`}),c+=` `):a.oneOf&&(c+=x(e,a,r),c+=m(r)+`${t}${s===!0?"":"?"}: `,a.oneOf.forEach((f,l)=>{const h=S(o,f,p,r);c+=(l?" | ":"")+`${h}`}),c+=` `);return c},ut=(o,t)=>{const e={};for(const n of t){const i=y(o,n);i&&(e[i.in]=e[i.in]?[...e[i.in],i]:[i])}let s="";return e.query&&(s+=`#### Parameters(Query) `+T(o,e.query)),e.body&&(s+=`#### Parameters(Body) `+T(o,e.body)),e.header&&(s+=`#### Headers `+T(o,e.header)),s},dt=o=>{let t=`## References `;return Object.entries(o.references).forEach(([e,s])=>{t+=`### ${e} `,t+=j(o,s)}),t},ft=(o,t)=>{const e=y(o,t);let s=`#### RequestBody `;return s+=j(o,e),s},lt=(o,t)=>{const e=y(o,t);if(!e)return"";let s=`- Examples `;return Object.entries(e).forEach(([n,i])=>{const r=y(o,i);s+=` - ${n} `,s+="```json\n"+JSON.stringify(r,void 0," ")+"\n```\n\n"}),s},ht=(o,t)=>{if(!t)return"";const e=y(o,t);let s=`#### Responses `;for(const[n,i]of Object.entries(e)){const r=i;if(s+=`- ${n} ${r.description} `,r.content)for(const[p,a]of Object.entries(r.content))s+=`\`${p}\` `,s+=j(o,a.schema),s+=lt(o,a.examples)}return s},mt=o=>{const{pathMethods:t}=o;let e=`## Path Details `;return e+=t.reduce((s,{path:n,method:i,operation:r})=>s+`*** ### [${i}]${n} `+(r.summary?`- Summary ${C(r.summary)} `:"")+(r.description?`- Description ${C(r.description)} `:"")+(r.security?`- Security ${C(r.security.reduce((p,a)=>p+Object.keys(a)[0]+` `,""))} `:"")+(r.parameters?ut(o,r.parameters):"")+(r.requestBody?ft(o,r.requestBody):"")+(r.responses?ht(o,r.responses):""),""),e},yt=async(o,t,e=!1)=>{const s=await it(o);if(console.debug({document:s}),!s){console.error(`'${o}' is not 'yaml' or 'json'`);return}const n=at("openapi"in s?s:(await et.convertObj(s,{})).openapi);e&&(n.pathMethods.sort((r,p)=>r.path!==p.path?r.path<p.path?-1:1:r.method<p.method?-1:1),n.references=Object.fromEntries(Object.entries(n.references).sort(([r],[p])=>r<p?-1:1)));let i=ct(n);i+=pt(n),i+=mt(n),i+=dt(n),i=i.trimEnd(),t?await F(t,i,"utf8"):console.log(i)};class H extends O{props;constructor(t){super(t),this.props=t}async run(){const t=M.argv,e=await this._getContent(this.props.input);console.debug({input:e});const s=await this._getOutput(),n=s.dir,i=[];for(const r of e){const p=v(n,r.id+".md");await yt(r.content,p,this.props.opts?.sort),i.push({id:r.id,content:await q(p,"utf-8")})}return await s.rmTempIfExist(),M.argv=t,i}}class L extends O{constructor(t){super(t),this.props=t}async getTsConfigPath(){const t=this.props.opts?.tsconfigPath,e=v(process.cwd(),"tsconfig.json"),s=async n=>await _(n)?!0:(console.warn(`Could not find tsconfig.json at ${n}`),!1);return t&&await s(t)?t:await s(e)?e:void 0}async getPackageJsonPath(){const t=this.props.opts?.packageJsonPath,e=v(process.cwd(),"package.json"),s=async n=>await _(n)?!0:(console.warn(`Could not find package.json at ${n}`),!1);return t&&await s(t)?t:await s(e)?e:void 0}async runTypedoc(t){const e=this.props.opts||{},s=await this.getTsConfigPath(),n=this.props.output,{name:i,typedoc:r}=e,p={disableSources:!0,readme:"none",excludePrivate:!0,excludeProtected:!0,excludeExternals:!0,groupOrder:["Classes","Functions","Type Aliases","*"],includeVersion:r?.includeVersion};if(i)p.name=i;else{const w=await this.getPackageJsonPath();if(w){const b=await tt(w);"name"in b&&typeof b.name=="string"&&(p.name=b.name)}}const a={...p,...r||{},...t,entryPoints:typeof this.props.input=="string"?[this.props.input]:this.props.input,tsconfig:s},c=await st.bootstrapWithPlugins(a,s?[new ot]:void 0),f=await c.convert(),l=await this._getOutput(),h=l.dir;if(console.debug({appConfig:a,dir:h}),f){let w=[];c.renderer.on(rt.END,g=>{if(g.outputDirectory&&g.urls){const $=g.outputDirectory;for(const u of g.urls){const d=v($,u.url);w.push(d)}}}),await c.generateDocs(f,h),console.debug({outputPaths:w});const b=[];for(const g of w){const $=await q(g,"utf-8");let u;if(e?.hooks?.before){const d=await e.hooks.before();d&&typeof d=="string"&&d!==""&&(u||(u=$),u=d+u)}if(e?.hooks?.after){const d=await e.hooks.after();d&&typeof d=="string"&&d!==""&&(u||(u=$),u+=d)}if(e?.transform){const d=await e.transform($);d&&typeof d=="string"&&d!==""&&(u||(u=$),u=d)}u?n&&await F(g,u):u=$,b.push({id:g,content:u})}return await l.rmTempIfExist(),b}throw new Error("Inputs not converted")}}class wt extends L{constructor(t){super(t),this.props=t}async run(){return await this.runTypedoc()}}class U extends L{props;constructor(t){super(t),this.props=t}async run(){const t=this.props.opts||{},{typedocMarkdown:e}=t,s={entryFileName:"index",fileExtension:".md",outputFileStrategy:"modules",expandObjects:!0,expandParameters:!0,useCodeBlocks:!0,hideBreadcrumbs:!0,hidePageHeader:!0,indexFormat:"list",classPropertiesFormat:"table",typeDeclarationFormat:"table",parametersFormat:"table",sanitizeComments:!0,...e||{},plugin:["typedoc-plugin-markdown"]};return this.runTypedoc(s)}}class k{config={};async openapi2md(t){return await new H(t).run()}async ts2md(t){return await new U(t).run()}async ts2html(t){return await new wt(t).run()}async html2md(t){return await new N(t).run()}async md2html(t){return await new J(t).run()}async jsdoc2md(t){return await new B(t).run()}async custom(t){if(!t.fn)throw new Error("No function provided");const e=new k;return await t.fn({config:this.config,run:{openapi2md:e.openapi2md,jsdoc2md:e.jsdoc2md,html2md:e.html2md,md2html:e.md2html,ts2md:e.ts2md,ts2html:e.ts2html}})}}class V{convert=new k;utils;opts;constructor({opts:t,utils:e}){this.opts=t,this.utils=e,this.utils.helpURL=nt,this.utils.title="convert"}async#t(t){const e=await this.utils.getOptsKeys({input:this.opts,pattern:t});if(!e||typeof e=="string"||!this.opts)return;const{style:s}=this.utils,n={};for(const i of e){const r=this.opts[i],{type:p,...a}=r;console.log(s.info.h(`Convert ${s.badge(i)} key`),s.info.p("("+p+")"),` `),console.debug({props:r}),n[i]=await this.convert[p](a),console.log(s.success.msg("\u2728 Successful conversion"),` `)}return n}async run(t){return this.convert.config=this.utils.config||{},this.utils.catchFn(this.#t(t))}}const z=o=>({custom:{convert:{desc:"Convert files from one format to another (experimental)",opts:{key:{alias:"k",desc:"Key pattern to convert",type:"array"}},fn:async({utils:t,opts:e})=>{await new V({opts:o,utils:t}).run(e?.key)}}}});export{k as Convert,N as Html2Markdown,B as Jsdoc2Markdown,J as Markdown2Html,V as MultipleConvert,H as Openapi2Markdown,U as Typescript2Markdown,z as convertPlugin,z as default};