rpc4next-cli 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +18 -4
  2. package/package.json +5 -6
package/dist/index.js CHANGED
@@ -1,12 +1,26 @@
1
1
  #!/usr/bin/env node
2
- import{Command as jt}from"commander";import $t from"path";var A=["page.tsx","route.ts"],O=0,P=1,F=1,w=20,W="\u2192";import M from"path";var Z=(t,r)=>{let e=x(M.relative(M.dirname(t),r)).replace(/\.tsx?$/,"");return e.startsWith("../")||(e="./"+e),e},x=t=>t.replace(/\\/g,"/"),_=t=>M.relative(process.cwd(),t);import yt from"fs";import Yt from"path";var tt=["Query"];var Y="Endpoint",U="QueryKey",j="ParamsKey",et=[Y,j,U],rt="rpc4next/client";import lt from"fs";import v from"path";import{CATCH_ALL_PREFIX as Dt,DYNAMIC_PREFIX as Ot,HTTP_METHODS_EXCLUDE_OPTIONS as Ft,OPTIONAL_CATCH_ALL_PREFIX as wt}from"rpc4next-shared";import H from"path";var E=new Map,C=new Map,ot=(t,r)=>{let e=H.resolve(r);[...t.keys()].forEach(o=>{let n=H.resolve(o);(n===e||e.startsWith(n+H.sep))&&t.delete(o)})},nt=t=>{ot(E,t)},st=t=>{ot(C,t)};import vt from"fs";import Lt from"crypto";var it=(t,r)=>{let e=Lt.createHash("md5").update(`${t}::${r}`).digest("hex").slice(0,16);return`${r}_${e}`};var R=(t,r)=>!t||!r?"":`Record<${t}, ${r}>`,b=t=>t.length===0||t.some(({name:r,type:e})=>!r||!e)?"":`{ ${t.map(({name:r,type:e})=>`"${r}": ${e}`).join(`${";"} `)}${t.length>1?";":""} }`,L=(t,r,e)=>!t||!r?"":e?`import type { ${t} as ${e} } from "${r}"${";"}`:`import type { ${t} } from "${r}"${";"}`;var pt=(t,r,e,o)=>{let n=vt.readFileSync(r,"utf8"),i=e(n);if(!i)return;let s=Z(t,r),p=it(s,i);return{importName:p,importPath:s,importStatement:L(i,s,p),type:o(i,p)}},ct=(t,r)=>pt(t,r,e=>tt.find(o=>new RegExp(`export (interface ${o} ?{|type ${o} ?=)`).test(e)),(e,o)=>R(U,o)),mt=(t,r,e)=>pt(t,r,o=>[e].find(n=>new RegExp(`export (async )?(function ${n} ?\\(|const ${n} ?=|\\{[^}]*\\b${n}\\b[^}]*\\} ?=|const \\{[^}]*\\b${n}\\b[^}]*\\} ?=|\\{[^}]*\\b${n}\\b[^}]*\\} from)`).test(o)),(o,n)=>b([{name:`$${o.toLowerCase()}`,type:`typeof ${n}`}]));var ut=new Set(A),ft=t=>{if(E.has(t))return E.get(t);let r=lt.readdirSync(t,{withFileTypes:!0});for(let e of r){let{name:o}=e,n=v.join(t,o);if(o==="node_modules"||o.startsWith("_")||o.startsWith("(.)")||o.startsWith("(..)")||o.startsWith("(...)"))return E.set(t,!1),!1;if(e.isFile()&&ut.has(o))return E.set(t,!0),!0;if(e.isDirectory()&&ft(n))return E.set(t,!0),!0}return E.set(t,!1),!1},Wt=(t,{isDynamic:r,isCatchAll:e,isOptionalCatchAll:o})=>{let n=t;return r&&(n=n.replace(/^\[+|\]+$/g,"")),(e||o)&&(n=n.replace(/^\.{3}/,"")),{paramName:n,keyName:`${o?wt:e?Dt:r?Ot:""}${n}`}},X=(t,r,e="",o=[])=>{if(C.has(r))return C.get(r);let n=e,i=e+" ",s=[],p=[],a=[],m=[],c=[...o],$=lt.readdirSync(r,{withFileTypes:!0}).filter(y=>{if(y.isDirectory()){let S=v.join(r,y.name);return ft(S)}return ut.has(y.name)}).sort();for(let y of $){let S=x(v.join(r,y.name));if(y.isDirectory()){let l=y.name,h=l.startsWith("(")&&l.endsWith(")"),u=l.startsWith("@"),f=l.startsWith("[[...")&&l.endsWith("]]"),g=l.startsWith("[...")&&l.endsWith("]"),d=l.startsWith("[")&&l.endsWith("]"),{paramName:Nt,keyName:It}=Wt(l,{isDynamic:d,isCatchAll:g,isOptionalCatchAll:f}),At=d||g||f?[...c,{paramName:Nt,routeType:{isDynamic:d,isCatchAll:g,isOptionalCatchAll:f,isGroup:h,isParallel:u}}]:c,q=h||u,{pathStructure:z,imports:Rt,paramsTypes:bt}=X(t,S,q?n:i,At);if(p.push(...Rt),m.push(...bt),q){let J=z.match(/^\s*\{([\s\S]*)\}\s*$/);J&&s.push(`${i}${J[1].trim()}`)}else s.push(`${i}"${It}": ${z}`)}else{let l=ct(t,S);if(l){let{importStatement:h,importPath:u,type:f}=l;p.push({statement:h,path:u}),a.push(f)}if(Ft.forEach(h=>{let u=mt(t,S,h);if(u){let{importStatement:f,importPath:g,type:d}=u;p.push({statement:f,path:g}),a.push(d)}}),a.push(Y),c.length>0){let h=c.map(({paramName:f,routeType:g})=>{let d=g.isCatchAll?"string[]":g.isOptionalCatchAll?"string[] | undefined":"string";return{name:f,type:d}}),u=b(h);m.push({paramsType:u,dirPath:v.dirname(S)}),a.push(R(j,u))}}}let I=a.join(" & "),D=s.length>0?`{${`
2
+ import kt from"node:path";import{parseArgs as Gt}from"node:util";import _t from"path";var I=["page.tsx","route.ts"],O=0,S=1,F=1,W=20,M="\u2192";import Y from"path";var et=(t,e)=>{let r=P(Y.relative(Y.dirname(t),e)).replace(/\.tsx?$/,"");return r.startsWith("../")||(r="./"+r),r},P=t=>t.replace(/\\/g,"/"),_=t=>Y.relative(process.cwd(),t);import xt from"fs";import jt from"path";var rt=["Query"];var U="Endpoint",j="QueryKey",H="ParamsKey",ot=[U,H,j],nt="rpc4next/client";import ft from"fs";import v from"path";import{CATCH_ALL_PREFIX as Ot,DYNAMIC_PREFIX as Ft,HTTP_METHODS_EXCLUDE_OPTIONS as Wt,OPTIONAL_CATCH_ALL_PREFIX as Mt}from"rpc4next-shared";import k from"path";var d=new Map,C=new Map,st=(t,e)=>{let r=k.resolve(e);[...t.keys()].forEach(o=>{let n=k.resolve(o);(n===r||r.startsWith(n+k.sep))&&t.delete(o)})},it=t=>{st(d,t)},at=t=>{st(C,t)};import Dt from"fs";import wt from"crypto";var pt=(t,e)=>{let r=wt.createHash("md5").update(`${t}::${e}`).digest("hex").slice(0,16);return`${e}_${r}`};var b=(t,e)=>!t||!e?"":`Record<${t}, ${e}>`,R=t=>t.length===0||t.some(({name:e,type:r})=>!e||!r)?"":`{ ${t.map(({name:e,type:r})=>`"${e}": ${r}`).join(`${";"} `)}${t.length>1?";":""} }`,L=(t,e,r)=>!t||!e?"":r?`import type { ${t} as ${r} } from "${e}"${";"}`:`import type { ${t} } from "${e}"${";"}`;var mt=(t,e,r,o)=>{let n=Dt.readFileSync(e,"utf8"),i=r(n);if(!i)return;let s=et(t,e),a=pt(s,i);return{importName:a,importPath:s,importStatement:L(i,s,a),type:o(i,a)}},ut=(t,e)=>mt(t,e,r=>rt.find(o=>new RegExp(`export (interface ${o} ?{|type ${o} ?=)`).test(r)),(r,o)=>b(j,o)),lt=(t,e,r)=>mt(t,e,o=>[r].find(n=>new RegExp(`export (async )?(function ${n} ?\\(|const ${n} ?=|\\{[^}]*\\b${n}\\b[^}]*\\} ?=|const \\{[^}]*\\b${n}\\b[^}]*\\} ?=|\\{[^}]*\\b${n}\\b[^}]*\\} from)`).test(o)),(o,n)=>R([{name:`$${o.toLowerCase()}`,type:`typeof ${n}`}]));var ht=new Set(I),gt=t=>{if(d.has(t))return d.get(t);let e=ft.readdirSync(t,{withFileTypes:!0});for(let r of e){let{name:o}=r,n=v.join(t,o);if(o==="node_modules"||o.startsWith("_")||o.startsWith("(.)")||o.startsWith("(..)")||o.startsWith("(...)"))return d.set(t,!1),!1;if(r.isFile()&&ht.has(o))return d.set(t,!0),!0;if(r.isDirectory()&&gt(n))return d.set(t,!0),!0}return d.set(t,!1),!1},Yt=(t,{isDynamic:e,isCatchAll:r,isOptionalCatchAll:o})=>{let n=t;return e&&(n=n.replace(/^\[+|\]+$/g,"")),(r||o)&&(n=n.replace(/^\.{3}/,"")),{paramName:n,keyName:`${o?Mt:r?Ot:e?Ft:""}${n}`}},X=(t,e,r="",o=[])=>{if(C.has(e))return C.get(e);let n=r,i=r+" ",s=[],a=[],p=[],m=[],c=[...o],$=ft.readdirSync(e,{withFileTypes:!0}).filter(E=>{if(E.isDirectory()){let T=v.join(e,E.name);return gt(T)}return ht.has(E.name)}).sort();for(let E of $){let T=P(v.join(e,E.name));if(E.isDirectory()){let u=E.name,h=u.startsWith("(")&&u.endsWith(")"),l=u.startsWith("@"),f=u.startsWith("[[...")&&u.endsWith("]]"),g=u.startsWith("[...")&&u.endsWith("]"),x=u.startsWith("[")&&u.endsWith("]"),{paramName:It,keyName:bt}=Yt(u,{isDynamic:x,isCatchAll:g,isOptionalCatchAll:f}),Rt=x||g||f?[...c,{paramName:It,routeType:{isDynamic:x,isCatchAll:g,isOptionalCatchAll:f,isGroup:h,isParallel:l}}]:c,J=h||l,{pathStructure:D,imports:Lt,paramsTypes:vt}=X(t,T,J?n:i,Rt);if(a.push(...Lt),m.push(...vt),J){let Z=D.match(/^\s*\{([\s\S]*)\}\s*$/),tt=D.trim();if(Z)s.push(`${i}${Z[1].trim()}`);else if(tt)p.push(tt);else throw new Error(`Invalid empty child path structure in grouped/parallel route: ${T}`)}else s.push(`${i}"${bt}": ${D}`)}else{let u=ut(t,T);if(u){let{importStatement:h,importPath:l,type:f}=u;a.push({statement:h,path:l}),p.push(f)}if(Wt.forEach(h=>{let l=lt(t,T,h);if(l){let{importStatement:f,importPath:g,type:x}=l;a.push({statement:f,path:g}),p.push(x)}}),p.push(U),c.length>0){let h=c.map(({paramName:f,routeType:g})=>{let x=g.isCatchAll?"string[]":g.isOptionalCatchAll?"string[] | undefined":"string";return{name:f,type:x}}),l=R(h);m.push({paramsType:l,dirPath:v.dirname(T)}),p.push(b(H,l))}}}let N=p.join(" & "),w=s.length>0?`{${`
3
3
  `}${s.join(`,${`
4
4
  `}`)}${`
5
- `}${n}}`:"",V={pathStructure:I&&D?`${I} & ${D}`:I||D,imports:p,paramsTypes:m};return C.set(r,V),V};var ht=(t,r)=>{let{pathStructure:e,imports:o,paramsTypes:n}=X(t,r),i=`export type PathStructure = ${e}${";"}`,s=o.length?`${o.sort((c,$)=>c.path.localeCompare($.path,void 0,{numeric:!0})).map(c=>c.statement).join(`
6
- `)}`:"",p=et.filter(c=>e.includes(c)),a=L(p.join(" ,"),rt),m=n.map(({paramsType:c,dirPath:$})=>({paramsType:`export type Params = ${c}${";"}`,dirPath:$}));return{pathStructure:`${a}${`
5
+ `}${n}}`:"",z={pathStructure:N&&w?`${N} & ${w}`:N||w,imports:a,paramsTypes:m};return C.set(e,z),z};var dt=(t,e)=>{let{pathStructure:r,imports:o,paramsTypes:n}=X(t,e),i=`export type PathStructure = ${r}${";"}`,s=o.length?`${o.sort((c,$)=>c.path.localeCompare($.path,void 0,{numeric:!0})).map(c=>c.statement).join(`
6
+ `)}`:"",a=ot.filter(c=>r.includes(c)),p=L(a.join(" ,"),nt),m=n.map(({paramsType:c,dirPath:$})=>({paramsType:`export type Params = ${c}${";"}`,dirPath:$}));return{pathStructure:`${p}${`
7
7
  `}${s}${`
8
8
  `}${`
9
- `}${i}`,paramsTypes:m}};var Mt=()=>!!process.stdout?.isTTY;var k=t=>r=>Mt()?`\x1B[${t}m${r}\x1B[0m`:r,gt=k(36),Et=k(32),K=k(31);var B=(t,r,e="\u2192",o=24)=>t.padEnd(o)+` ${e} ${r}`,Q=(t=0)=>" ".repeat(t),Tt=()=>({info:(t,r={})=>{let{indentLevel:e=0,event:o}=r,n=o?`${gt(`[${o}]`)} `:"";console.log(`${Q(e)}${n}${t}`)},success:(t,r={})=>{let{indentLevel:e=0}=r;console.log(`${Q(e)}${Et("\u2713")} ${t}`)},error:(t,r={})=>{let{indentLevel:e=0}=r;console.error(`${Q(e)}${K("\u2717")} ${K(t)}`)}});var dt=({baseDir:t,outputPath:r,paramsFileName:e,logger:o})=>{o.info("Generating types...",{event:"generate"});let{pathStructure:n,paramsTypes:i}=ht(r,t);yt.writeFileSync(r,n),o.success(B("Path structure type",_(r),W,w),{indentLevel:F}),e&&(i.forEach(({paramsType:s,dirPath:p})=>{let a=Yt.join(p,e);yt.writeFileSync(a,s)}),o.success(B("Params types",e,W,w),{indentLevel:F}))};import Ut from"chokidar";var St=(t,r)=>{let e=null,o=!1,n=null,i=async(...s)=>{o=!0;try{await t(...s)}finally{if(o=!1,n){let p=n;n=null,i(...p)}}};return(...s)=>{e&&clearTimeout(e),e=setTimeout(()=>{if(o){n=s;return}i(...s)},r)}};var xt=(t,r,e)=>{e.info(`${_(t)}`,{event:"watch"});let o=a=>A.some(m=>a.endsWith(m)),n=new Set,i=St(()=>{n.forEach(a=>{nt(a),st(a)}),n.clear(),r()},300),s=Ut.watch(t,{ignoreInitial:!0,ignored:(a,m)=>!!m?.isFile()&&!o(a)});s.on("ready",()=>{i(),s.on("all",(a,m)=>{if(o(m)){let c=_(m);e.info(c,{event:a}),n.add(m),i()}})}),s.on("error",a=>{a instanceof Error?e.error(`Watcher error: ${a.message}`):e.error(`Unknown watcher error: ${String(a)}`)});let p=()=>{s.close().then(()=>{e.info("Watcher closed.",{event:"watch"})}).catch(a=>{e.error(`Failed to close watcher: ${a.message}`)})};process.on("SIGINT",p),process.on("SIGTERM",p)};var Pt=(t,r,e,o)=>{try{return dt({baseDir:t,outputPath:r,paramsFileName:e,logger:o}),O}catch(n){return n instanceof Error?o.error(`Failed to generate: ${n.message}`):o.error(`Unknown error occurred during generate: ${String(n)}`),P}},_t=(t,r,e,o)=>{let n=x($t.resolve(t)),i=x($t.resolve(r)),s=typeof e.paramsFile=="string"?e.paramsFile:null;return e.paramsFile!==void 0&&!s?(o.error("Error: --params-file requires a filename."),P):e.watch?(xt(n,()=>{Pt(n,i,s,o)},o),O):Pt(n,i,s,o)};var Ct=(t,r=Tt())=>{let e=new jt;e.description("Generate RPC client type definitions based on the Next.js path structure.").argument("<baseDir>","Base directory containing Next.js paths for type generation").argument("<outputPath>","Output path for the generated type definitions").option("-w, --watch","Watch mode: regenerate on file changes").option("-p, --params-file [filename]","Generate params types file with specified filename").action(async(o,n,i)=>{try{let s=await _t(o,n,i,r);i.watch||process.exit(s)}catch(s){r.error(`Unexpected error occurred:${s instanceof Error?s.message:String(s)}`),process.exit(P)}}),e.parse(t)};Ct(process.argv);
9
+ `}${i}`,paramsTypes:m}};var Ut=()=>!!process.stdout?.isTTY;var K=t=>e=>Ut()?`\x1B[${t}m${e}\x1B[0m`:e,yt=K(36),Et=K(32),Q=K(31);var V=(t,e,r="\u2192",o=24)=>t.padEnd(o)+` ${r} ${e}`,B=(t=0)=>" ".repeat(t),Tt=()=>({info:(t,e={})=>{let{indentLevel:r=0,event:o}=e,n=o?`${yt(`[${o}]`)} `:"";console.log(`${B(r)}${n}${t}`)},success:(t,e={})=>{let{indentLevel:r=0}=e;console.log(`${B(r)}${Et("\u2713")} ${t}`)},error:(t,e={})=>{let{indentLevel:r=0}=e;console.error(`${B(r)}${Q("\u2717")} ${Q(t)}`)}});var St=({baseDir:t,outputPath:e,paramsFileName:r,logger:o})=>{o.info("Generating types...",{event:"generate"});let{pathStructure:n,paramsTypes:i}=dt(e,t);xt.writeFileSync(e,n),o.success(V("Path structure type",_(e),M,W),{indentLevel:F}),r&&(i.forEach(({paramsType:s,dirPath:a})=>{let p=jt.join(a,r);xt.writeFileSync(p,s)}),o.success(V("Params types",r,M,W),{indentLevel:F}))};import Ht from"chokidar";var Pt=(t,e)=>{let r=null,o=!1,n=null,i=async(...s)=>{o=!0;try{await t(...s)}finally{if(o=!1,n){let a=n;n=null,i(...a)}}};return(...s)=>{r&&clearTimeout(r),r=setTimeout(()=>{if(o){n=s;return}i(...s)},e)}};var $t=(t,e,r)=>{r.info(`${_(t)}`,{event:"watch"});let o=p=>I.some(m=>p.endsWith(m)),n=new Set,i=Pt(async()=>{let p=Array.from(n);n.clear();for(let m of p)it(m),at(m);await e()},300),s=Ht.watch(t,{ignoreInitial:!0,ignored:(p,m)=>!!m?.isFile()&&!o(p)});s.on("ready",()=>{i(),s.on("all",(p,m)=>{if(o(m)){let c=_(m);r.info(c,{event:p}),n.add(m),i()}})}),s.on("error",p=>{p instanceof Error?r.error(`Watcher error: ${p.message}`):r.error(`Unknown watcher error: ${String(p)}`)});let a=()=>{s.close().then(()=>{r.info("Watcher closed.",{event:"watch"})}).catch(p=>{r.error(`Failed to close watcher: ${p.message}`)})};process.on("SIGINT",a),process.on("SIGTERM",a)};var Ct=(t,e,r,o)=>{try{return St({baseDir:t,outputPath:e,paramsFileName:r,logger:o}),O}catch(n){return n instanceof Error?o.error(`Failed to generate: ${n.message}`):o.error(`Unknown error occurred during generate: ${String(n)}`),S}},At=(t,e,r,o)=>{let n=P(_t.resolve(t)),i=P(_t.resolve(e)),s=typeof r.paramsFile=="string"?r.paramsFile:null;return r.paramsFile!==void 0&&!s?(o.error("Error: --params-file requires a filename."),S):r.watch?($t(n,()=>{Ct(n,i,s,o)},o),O):Ct(n,i,s,o)};function Xt(t){if(t.length===0)return[];if(t[0].startsWith("-"))return t;let e=kt.basename(t[0]).toLowerCase();return new Set(["node","node.exe","bun","bun.exe","deno","deno.exe"]).has(e)&&t.length>=2?t.slice(2):t}function q(t){let e=`
10
+ Generate RPC client type definitions based on the Next.js path structure.
11
+
12
+ Usage:
13
+ rpc4next <baseDir> <outputPath> [options]
14
+
15
+ Arguments:
16
+ baseDir Base directory containing Next.js paths for type generation
17
+ outputPath Output path for the generated type definitions
18
+
19
+ Options:
20
+ -w, --watch Watch mode: regenerate on file changes
21
+ -p, --params-file [filename] Generate params types file (optional filename)
22
+ -h, --help Show help
23
+ `.trim();t.info(e)}function Kt(t,e){let r=[],o;for(let n=0;n<t.length;n++){let i=t[n],s=!1;for(let a of e)if(i.startsWith(a+"=")){o=i.slice((a+"=").length)||!0,s=!0;break}if(!s){if(e.includes(i)){let a=t[n+1];typeof a=="string"&&!a.startsWith("-")?(o=a,n++):o=!0;continue}r.push(i)}}return{args:r,value:o}}var Nt=(t,e=Tt())=>{try{let r=Xt(t),{args:o,value:n}=Kt(r,["-p","--params-file"]),{values:i,positionals:s}=Gt({args:o,options:{watch:{type:"boolean",short:"w"},help:{type:"boolean",short:"h"}},allowPositionals:!0,strict:!0});i.help&&(q(e),process.exit(0));let a=s[0],p=s[1];(!a||!p)&&(e.error("Missing required arguments: <baseDir> <outputPath>"),q(e),process.exit(S));let m={watch:!!i.watch,...n!==void 0?{paramsFile:n}:{}};(async()=>{try{let c=await At(a,p,m,e);m.watch||process.exit(c)}catch(c){e.error(`Unexpected error occurred:${c instanceof Error?c.message:String(c)}`),process.exit(S)}})()}catch(r){e.error(r instanceof Error?r.message:`Invalid arguments: ${String(r)}`),q(e),process.exit(S)}};Nt(process.argv);
10
24
  /*!
11
25
  * Inspired by pathpida (https://github.com/aspida/pathpida),
12
26
  * especially the design and UX of its CLI.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rpc4next-cli",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "Command line interface for rpc4next. Generates RPC client type definitions from Next.js routes.",
5
5
  "author": "watanabe-1",
6
6
  "license": "MIT",
@@ -27,14 +27,13 @@
27
27
  "test:coverage": "vitest run --coverage.enabled true",
28
28
  "test:ui": "vitest --ui --coverage.enabled true",
29
29
  "test:watch": "vitest --watch",
30
- "lint": "eslint \"src/**/*.ts\"",
31
- "lint:fix": "eslint \"src/**/*.ts\" --fix",
30
+ "lint": "eslint src",
31
+ "lint:fix": "eslint src --fix",
32
32
  "typecheck": "tsc --noEmit"
33
33
  },
34
34
  "dependencies": {
35
- "chokidar": "^4.0.3",
36
- "commander": "^14.0.1",
37
- "rpc4next-shared": "^0.1.4"
35
+ "chokidar": "^5.0.0",
36
+ "rpc4next-shared": "^0.1.5"
38
37
  },
39
38
  "peerDependencies": {
40
39
  "next": "^14.0.0 || ^15.0.0"