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