@nemmtor/ts-databuilders 0.0.1-alpha.0 → 0.0.1-alpha.1

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/main.js +4 -4
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import*as Se from"@effect/platform-node/NodeContext";import*as Re from"@effect/platform-node/NodeRuntime";import{Effect as Ve}from"effect";import*as R from"@effect/cli/Command";import*as G from"effect/Layer";import*as le from"@effect/platform/FileSystem";import*as L from"effect/Effect";import*as oe from"effect/Context";import*as I from"effect/Schema";var ae={string:"",number:0,boolean:!1},ie=I.Struct({string:I.String,number:I.NumberFromString,boolean:I.BooleanFromString});class k extends oe.Tag("Configuration")(){}import Z from"node:path";import*as me from"@effect/platform/FileSystem";import*as w from"effect/Effect";import*as T from"effect/Match";import{Project as Pe}from"ts-morph";var q=(r)=>r.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/_/g,"-").toLowerCase(),te=(r)=>{return r.split(/[-_\s]+/).flatMap((n)=>n.split(/(?=[A-Z])/)).filter(Boolean).map((n)=>n.charAt(0).toUpperCase()+n.slice(1).toLowerCase()).join("")},re=(r)=>{return r.split(/[-_\s]+/).flatMap((m)=>m.split(/(?=[A-Z])/)).filter(Boolean).map((m,e)=>{let o=m.toLowerCase();return e===0?o:o.charAt(0).toUpperCase()+o.slice(1)}).join("")};var se=(r)=>{let{fieldName:n,optional:m,typeName:e,isNestedBuilder:o}=r,l=n.replaceAll("'","").replaceAll('"',""),h=re(l),i=`with${te(l)}`,t=[`return this.with({ ${n}: ${h} });`],a=[`return this.with({ ${n}: ${h}.build() });`],f=o?a:t,u=[`if (!${h}) {`,` const { "${l}": _unused, ...rest } = this.build();`," return this.with(rest);","}"],C=m?[...u,...f]:f,B=`${e}['${l}']`;return{name:i,isPublic:!0,parameters:[{name:h,type:o?`DataBuilder<${B}>`:B}],statements:C}};class H extends w.Service()("@TSDataBuilders/BuilderGenerator",{effect:w.gen(function*(){let r=yield*me.FileSystem,{outputDir:n,fileSuffix:m,builderSuffix:e,defaults:o}=yield*k,l=(h)=>T.value(h).pipe(T.when({kind:"STRING"},()=>`"${o.string}"`),T.when({kind:"NUMBER"},()=>o.number),T.when({kind:"BOOLEAN"},()=>o.boolean),T.when({kind:"UNDEFINED"},()=>"undefined"),T.when({kind:"DATE"},()=>"new Date()"),T.when({kind:"ARRAY"},()=>"[]"),T.when({kind:"LITERAL"},(i)=>i.literalValue),T.when({kind:"TYPE_CAST"},(i)=>l(i.baseTypeMetadata)),T.when({kind:"TUPLE"},(i)=>{return`[${i.members.map((a)=>l(a)).map((a)=>`${a}`).join(", ")}]`}),T.when({kind:"TYPE_LITERAL"},(i)=>{return`{${Object.entries(i.metadata).filter(([a,{optional:f}])=>!f).map(([a,f])=>`${a}: ${l(f)}`).join(", ")}}`}),T.when({kind:"RECORD"},(i)=>{if(i.keyType.kind==="STRING"||i.keyType.kind==="NUMBER")return"{}";let t=l(i.keyType),a=l(i.valueType);return`{${t}: ${a}}`}),T.when({kind:"UNION"},(i)=>{let a=i.members.slice().sort((f,u)=>{let C=ce.indexOf(f.kind),B=ce.indexOf(u.kind);return(C===-1?1/0:C)-(B===-1?1/0:B)})[0];if(!a)return"never";return l(a)}),T.when({kind:"BUILDER"},(i)=>{return`new ${i.name}${e}().build()`}),T.exhaustive);return{generateBaseBuilder:w.fnUntraced(function*(){let h=Z.resolve(n,"data-builder.ts");yield*r.writeFileString(h,xe)}),generateBuilder:w.fnUntraced(function*(h){let i=new Pe,t=h.name,a=Z.resolve(n,`${q(t)}${m}.ts`),f=i.createSourceFile(a,"",{overwrite:!0}),u=Z.resolve(h.path),C=Z.relative(Z.dirname(a),u).replace(/\.ts$/,"");if(f.addImportDeclaration({namedImports:[t],isTypeOnly:!0,moduleSpecifier:C}),f.addImportDeclaration({namedImports:["DataBuilder"],moduleSpecifier:"./data-builder"}),h.shape.kind!=="TYPE_LITERAL")return yield*w.dieMessage("[BuilderGenerator]: Expected root type to be type literal");[...new Set(Me(h.shape.metadata))].forEach((g)=>{f.addImportDeclaration({namedImports:[`${g}${e}`],moduleSpecifier:`./${q(g)}${m}`})});let M=Object.entries(h.shape.metadata).filter(([g,{optional:b}])=>!b).map(([g,b])=>`${g}: ${b.kind==="TYPE_CAST"?`${l(b)} as ${t}['${g}']`:l(b)}`),P=Object.entries(h.shape.metadata).map(([g,{optional:b,kind:ee}])=>se({fieldName:g,optional:b,typeName:t,isNestedBuilder:ee==="BUILDER"})),F=`{
3
- ${M.join(`,
2
+ import*as Le from"@effect/platform-node/NodeContext";import*as Pe from"@effect/platform-node/NodeRuntime";import{Effect as Qe}from"effect";import*as F from"@effect/cli/Command";import*as G from"effect/Layer";import*as he from"@effect/platform/FileSystem";import*as N from"effect/Effect";import*as le from"effect/Context";import*as M from"effect/Schema";var me={string:"",number:0,boolean:!1},de=M.Struct({string:M.String,number:M.NumberFromString,boolean:M.BooleanFromString});class b extends le.Tag("Configuration")(){}import Z from"node:path";import*as ue from"@effect/platform/FileSystem";import*as $ from"effect/Effect";import*as S from"effect/Match";import{Project as Oe}from"ts-morph";var H=(o)=>o.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/_/g,"-").toLowerCase(),ne=(o)=>{return o.split(/[-_\s]+/).flatMap((p)=>p.split(/(?=[A-Z])/)).filter(Boolean).map((p)=>p.charAt(0).toUpperCase()+p.slice(1).toLowerCase()).join("")},oe=(o)=>{return o.split(/[-_\s]+/).flatMap((i)=>i.split(/(?=[A-Z])/)).filter(Boolean).map((i,e)=>{let s=i.toLowerCase();return e===0?s:s.charAt(0).toUpperCase()+s.slice(1)}).join("")};var pe=(o)=>{let{fieldName:p,optional:i,typeName:e,isNestedBuilder:s}=o,r=p.replaceAll("'","").replaceAll('"',""),m=oe(r),n=`with${ne(r)}`,c=[`return this.with({ ${p}: ${m} });`],t=[`return this.with({ ${p}: ${m}.build() });`],a=s?t:c,u=[`if (!${m}) {`,` const { "${r}": _unused, ...rest } = this.build();`," return this.with(rest);","}"],h=i?[...u,...a]:a,f=`${e}['${r}']`;return{name:n,isPublic:!0,parameters:[{name:m,type:s?`DataBuilder<${f}>`:f}],statements:h}};class J extends $.Service()("@TSDataBuilders/BuilderGenerator",{effect:$.gen(function*(){let o=yield*ue.FileSystem,{outputDir:p,fileSuffix:i,builderSuffix:e,defaults:s}=yield*b,r=(m)=>S.value(m).pipe(S.when({kind:"STRING"},()=>`"${s.string}"`),S.when({kind:"NUMBER"},()=>s.number),S.when({kind:"BOOLEAN"},()=>s.boolean),S.when({kind:"UNDEFINED"},()=>"undefined"),S.when({kind:"DATE"},()=>"new Date()"),S.when({kind:"ARRAY"},()=>"[]"),S.when({kind:"LITERAL"},(n)=>n.literalValue),S.when({kind:"TYPE_CAST"},(n)=>r(n.baseTypeMetadata)),S.when({kind:"TUPLE"},(n)=>{return`[${n.members.map((t)=>r(t)).map((t)=>`${t}`).join(", ")}]`}),S.when({kind:"TYPE_LITERAL"},(n)=>{return`{${Object.entries(n.metadata).filter(([t,{optional:a}])=>!a).map(([t,a])=>`${t}: ${r(a)}`).join(", ")}}`}),S.when({kind:"RECORD"},(n)=>{if(n.keyType.kind==="STRING"||n.keyType.kind==="NUMBER")return"{}";let c=r(n.keyType),t=r(n.valueType);return`{${c}: ${t}}`}),S.when({kind:"UNION"},(n)=>{let t=n.members.slice().sort((a,u)=>{let h=fe.indexOf(a.kind),f=fe.indexOf(u.kind);return(h===-1?1/0:h)-(f===-1?1/0:f)})[0];if(!t)return"never";return r(t)}),S.when({kind:"BUILDER"},(n)=>{return`new ${n.name}${e}().build()`}),S.exhaustive);return{generateBaseBuilder:$.fnUntraced(function*(){let m=Z.resolve(p,"data-builder.ts");yield*o.writeFileString(m,_e)}),generateBuilder:$.fnUntraced(function*(m){let n=new Oe,c=m.name,t=Z.resolve(p,`${H(c)}${i}.ts`),a=n.createSourceFile(t,"",{overwrite:!0}),u=Z.resolve(m.path),h=Z.relative(Z.dirname(t),u).replace(/\.ts$/,"");if(a.addImportDeclaration({namedImports:[c],isTypeOnly:!0,moduleSpecifier:h}),a.addImportDeclaration({namedImports:["DataBuilder"],moduleSpecifier:"./data-builder"}),m.shape.kind!=="TYPE_LITERAL")return yield*$.dieMessage("[BuilderGenerator]: Expected root type to be type literal");[...new Set(je(m.shape.metadata))].forEach((T)=>{a.addImportDeclaration({namedImports:[`${T}${e}`],moduleSpecifier:`./${H(T)}${i}`})});let x=Object.entries(m.shape.metadata).filter(([T,{optional:k}])=>!k).map(([T,k])=>`${T}: ${k.kind==="TYPE_CAST"?`${r(k)} as ${c}['${T}']`:r(k)}`),C=Object.entries(m.shape.metadata).map(([T,{optional:k,kind:L}])=>pe({fieldName:T,optional:k,typeName:c,isNestedBuilder:L==="BUILDER"})),w=`{
3
+ ${x.join(`,
4
4
  `)}
5
- }`;f.addClass({name:`${t}${e}`,isExported:!0,extends:`DataBuilder<${t}>`,methods:[{name:"constructor",statements:[`super(${F});`]},...P]}),f.saveSync()})}})}){}var ce=["UNDEFINED","BOOLEAN","NUMBER","STRING","DATE","LITERAL","TYPE_LITERAL","ARRAY","TUPLE","RECORD"],xe=`export abstract class DataBuilder<T> {
5
+ }`;a.addClass({name:`${c}${e}`,isExported:!0,extends:`DataBuilder<${c}>`,methods:[{name:"constructor",statements:[`super(${w});`]},...C]}),a.saveSync()})}})}){}var fe=["UNDEFINED","BOOLEAN","NUMBER","STRING","DATE","LITERAL","TYPE_LITERAL","ARRAY","TUPLE","RECORD"],_e=`export abstract class DataBuilder<T> {
6
6
  private data: T;
7
7
 
8
8
  constructor(initialData: T) {
@@ -18,4 +18,4 @@ import*as Se from"@effect/platform-node/NodeContext";import*as Re from"@effect/p
18
18
  return this;
19
19
  }
20
20
  }
21
- `;function Me(r){let n=[];function m(e){switch(e.kind){case"BUILDER":n.push(e.name);break;case"TYPE_LITERAL":Object.values(e.metadata).forEach(m);break;case"UNION":case"TUPLE":e.members.forEach(m);break;case"RECORD":m(e.keyType),m(e.valueType);break}}return Object.values(r).forEach(m),n}class O extends L.Service()("@TSDataBuilders/Builders",{effect:L.gen(function*(){let r=yield*le.FileSystem,n=yield*H,{outputDir:m}=yield*k;return{create:L.fnUntraced(function*(e){if(yield*r.exists(m))yield*r.remove(m,{recursive:!0});yield*r.makeDirectory(m,{recursive:!0}),yield*n.generateBaseBuilder(),yield*L.all(e.map((l)=>n.generateBuilder(l)),{concurrency:"unbounded"})})}}),dependencies:[H.Default]}){}import*as s from"@effect/cli/Options";import*as N from"effect/HashMap";import*as J from"effect/Option";import*as $ from"effect/Schema";var Oe=s.text("jsdocTag").pipe(s.withDescription("JSDoc tag used to mark types for data building generation."),s.withSchema($.NonEmptyTrimmedString),s.withDefault("DataBuilder")),Ne=s.text("output-dir").pipe(s.withAlias("o"),s.withDescription("Output directory for generated builders."),s.withSchema($.NonEmptyTrimmedString),s.withDefault("generated/builders")),_e=s.text("include").pipe(s.withAlias("i"),s.withDescription("Glob pattern for files included while searching for jsdoc tag."),s.withSchema($.NonEmptyTrimmedString),s.withDefault("src/**/*.ts{,x}")),Ae=s.text("file-suffix").pipe(s.withDescription("File suffix for created builder files."),s.withSchema($.NonEmptyTrimmedString),s.withDefault(".builder")),Ue=s.text("builder-suffix").pipe(s.withDescription("Suffix for generated classes."),s.withSchema($.NonEmptyTrimmedString),s.withDefault("Builder")),je=s.keyValueMap("defaults").pipe(s.withDescription("Default values to be used in data builder constructor."),s.withSchema($.HashMapFromSelf({key:$.Literal("string","number","boolean"),value:$.String}).pipe($.transform(ie,{decode:(r)=>{return{string:r.pipe(N.get("string"),J.getOrElse(()=>"")),number:r.pipe(N.get("number"),J.getOrElse(()=>"0")),boolean:r.pipe(N.get("boolean"),J.getOrElse(()=>"false"))}},encode:(r)=>{return N.make(["string",r.string],["number",r.number],["boolean",r.boolean])},strict:!1}))),s.withDefault(ae)),fe={jsdocTag:Oe,outputDir:Ne,include:_e,fileSuffix:Ae,builderSuffix:Ue,defaults:je};import*as x from"effect/Chunk";import*as D from"effect/Effect";import*as he from"effect/Function";import*as U from"effect/Option";import*as X from"effect/Stream";import*as ue from"effect/Data";import*as de from"effect/Effect";import*as pe from"effect/Stream";import{glob as We}from"glob";class _ extends de.Service()("@TSDataBuilders/TreeWalker",{succeed:{walk:(r)=>{return pe.fromAsyncIterable(We.stream(r,{cwd:".",nodir:!0}),(n)=>new ne({cause:n}))}}}){}class ne extends ue.TaggedError("TreeWalkerError"){}import{FileSystem as Ye}from"@effect/platform";import*as A from"effect/Effect";import*as S from"effect/Stream";class Q extends A.Service()("@TSDataBuilders/FileContentChecker",{effect:A.gen(function*(){let r=yield*Ye.FileSystem,n=new TextDecoder;return{check:A.fnUntraced(function*(m){let{content:e,filePath:o}=m;return yield*r.stream(o,{chunkSize:16384}).pipe(S.map((i)=>n.decode(i,{stream:!0})),S.mapAccum("",(i,t)=>{let a=i+t;return[a.slice(-e.length+1),a.includes(e)]}),S.find(Boolean),S.runCollect)})}})}){}class j extends D.Service()("@TSDataBuilders/Finder",{effect:D.gen(function*(){let r=yield*Q,{include:n,jsdocTag:m}=yield*k,e=`@${m}`;return{find:D.fnUntraced(function*(){return yield*(yield*_).walk(n).pipe(X.mapEffect((i)=>r.check({filePath:i,content:e}).pipe(D.map(x.map((t)=>t?U.some(i):U.none()))),{concurrency:"unbounded"}),X.runCollect,D.map(x.flatMap(he.identity)),D.map(x.filter((i)=>U.isSome(i))),D.map(x.map((i)=>i.value)))})}}),dependencies:[Q.Default]}){}import*as ke from"@effect/platform/FileSystem";import*as V from"effect/Data";import*as p from"effect/Effect";import*as z from"effect/Either";import{Project as Ze,SyntaxKind as ve}from"ts-morph";import*as v from"effect/Data";import*as c from"effect/Effect";import*as d from"effect/Match";import*as Ce from"effect/Option";import{Node as Ge,SyntaxKind as E}from"ts-morph";class K extends c.Service()("@TSDataBuilders/TypeNodeParser",{effect:c.gen(function*(){let{jsdocTag:r}=yield*k,n=(m)=>c.gen(function*(){let{typeNode:e,optional:o}=m,l=e.getKind(),h=d.value(l).pipe(d.when(d.is(E.StringKeyword),()=>c.succeed({kind:"STRING",optional:o})),d.when(d.is(E.NumberKeyword),()=>c.succeed({kind:"NUMBER",optional:o})),d.when(d.is(E.BooleanKeyword),()=>c.succeed({kind:"BOOLEAN",optional:o})),d.when((t)=>t===E.TypeReference&&e.getText()==="Date",()=>c.succeed({kind:"DATE",optional:o})),d.when((t)=>t===E.UndefinedKeyword,()=>c.succeed({kind:"UNDEFINED",optional:o})),d.when((t)=>t===E.ArrayType,()=>c.succeed({kind:"ARRAY",optional:o})),d.when((t)=>t===E.LiteralType,()=>c.succeed({kind:"LITERAL",literalValue:e.asKindOrThrow(E.LiteralType).getLiteral().getText(),optional:o})),d.when((t)=>t===E.TypeLiteral,()=>c.gen(function*(){let a=e.asKindOrThrow(E.TypeLiteral).getMembers();return{kind:"TYPE_LITERAL",metadata:yield*c.reduce(a,{},(u,C)=>c.gen(function*(){if(!C.isKind(E.PropertySignature))return u;let B=C.getTypeNode();if(!B)return u;let M=C.getNameNode().getText(),P=C.hasQuestionToken(),F=yield*c.suspend(()=>n({typeNode:B,optional:P}));return{...u,[M]:F}})),optional:o}})),d.when(d.is(E.TupleType),()=>c.gen(function*(){let a=e.asKindOrThrow(E.TupleType).getElements(),f=yield*c.all(a.map((u)=>c.suspend(()=>n({typeNode:u,optional:!1}))));return{kind:"TUPLE",optional:o,members:f}})),d.when(d.is(E.TypeReference),()=>c.gen(function*(){let t=e.asKindOrThrow(E.TypeReference),a=t.getTypeName().getText(),f=t.getTypeArguments();if(a==="Record"){let[F,g]=f;if(!F||!g)return yield*new y({kind:l,raw:e.getText()});let b=yield*c.suspend(()=>n({typeNode:F,optional:!1})),ee=yield*c.suspend(()=>n({typeNode:g,optional:!1}));return{kind:"RECORD",keyType:b,valueType:ee,optional:o}}if(a==="Array")return{kind:"ARRAY",optional:o};let u=t.getType().getAliasSymbol();if(!u)return yield*new Te;let C=u.getDeclarations();if(C&&C.length>1)return yield*new Ee;let[B]=C;if(!B)return yield*new Be;let M=u?.getJsDocTags().map((F)=>F.getName()).includes(r);if(!Ge.isTypeAliasDeclaration(B))throw Error("TODO: for non-type-alias declarations (interfaces, etc.)");let P=B.getTypeNode();if(!P)return yield*new y({kind:l,raw:e.getText()});if(!M)return yield*c.suspend(()=>n({typeNode:P,optional:o}));return{kind:"BUILDER",name:B.getName(),optional:o}})),d.when(d.is(E.UnionType),()=>c.gen(function*(){let t=yield*c.all(e.asKindOrThrow(E.UnionType).getTypeNodes().map((a)=>c.suspend(()=>n({typeNode:a,optional:!1}))));return{kind:"UNION",optional:o,members:t}})),d.when(d.is(E.IntersectionType),()=>c.gen(function*(){let a=e.asKindOrThrow(E.IntersectionType).getTypeNodes(),f=[E.StringKeyword,E.NumberKeyword,E.BooleanKeyword],u=a.find((C)=>f.includes(C.getKind()));if(u&&a.length>1)return{kind:"TYPE_CAST",baseTypeMetadata:yield*c.suspend(()=>n({typeNode:u,optional:!1})),optional:o};throw Error("TODO: handle it")})),d.option);if(Ce.isNone(h))return yield*new y({kind:l,raw:e.getText()});return yield*h.value});return{generateMetadata:n}})}){}class y extends v.TaggedError("UnsupportedSyntaxKindError"){}class Ee extends v.TaggedError("MultipleSymbolDeclarationsError"){}class Be extends v.TaggedError("MissingSymbolDeclarationError"){}class Te extends v.TaggedError("MissingSymbolError"){}class W extends p.Service()("@TSDataBuilders/Parser",{effect:p.gen(function*(){let r=yield*ke.FileSystem,n=yield*K,{jsdocTag:m}=yield*k;return{generateBuildersMetadata:p.fnUntraced(function*(e){let o=yield*r.readFileString(e),l=yield*p.try({try:()=>{return new Ze().createSourceFile(e,o,{overwrite:!0}).getTypeAliases().filter((u)=>u.getJsDocs().flatMap((C)=>C.getTags().flatMap((B)=>B.getTagName())).includes(m)).map((u)=>{let C=u.getName();if(!u.isExported())return z.left(new ge({path:e,typeName:C}));let B=u.getTypeNode();if(!B||!B.isKind(ve.TypeLiteral))return z.left(new De({path:e,typeName:u.getName()}));return z.right({name:u.getName(),node:B})}).filter(Boolean)},catch:(t)=>new $e({cause:t})}),h=yield*p.all(l.map((t)=>t));return yield*p.all(h.map(({name:t,node:a})=>n.generateMetadata({typeNode:a,optional:!1}).pipe(p.map((f)=>({name:t,shape:f,path:e})),p.catchTag("UnsupportedSyntaxKindError",(f)=>p.fail(new be({kind:f.kind,raw:f.raw,path:e,typeName:t}))))))},p.catchTags({ParserError:(e)=>p.die(e),UnexportedDatabuilderError:(e)=>p.dieMessage(`[Parser]: Unexported databuilder ${e.typeName} at ${e.path}`),RichUnsupportedSyntaxKindError:(e)=>p.dieMessage(`[Parser]: Unsupported syntax kind of id: ${e.kind} with raw type: ${e.raw} found in type ${e.typeName} in file ${e.path}`),UnsupportedBuilderTypeError:(e)=>p.dieMessage(`[Parser]: Unsupported builder type ${e.typeName} in file ${e.path}.`)}))}}),dependencies:[K.Default]}){}class $e extends V.TaggedError("ParserError"){}class ge extends V.TaggedError("UnexportedDatabuilderError"){}class De extends V.TaggedError("UnsupportedBuilderTypeError"){}class be extends V.TaggedError("RichUnsupportedSyntaxKindError"){}import*as we from"effect/Chunk";import*as Y from"effect/Effect";import*as Fe from"effect/Function";var Ie=Y.gen(function*(){let r=yield*j,n=yield*W,m=yield*O,e=yield*r.find(),o=yield*Y.all(we.map(e,(l)=>n.generateBuildersMetadata(l)),{concurrency:"unbounded"}).pipe(Y.map((l)=>l.flatMap(Fe.identity)));if(o.length===0)return;yield*m.create(o)});var ze=R.make("ts-databuilders",fe),Le=ze.pipe(R.withHandler(()=>Ie),R.provide((r)=>G.mergeAll(j.Default,W.Default,O.Default,_.Default).pipe(G.provide(G.succeed(k,k.of(r))))),R.run({name:"Typescript Databuilders generator",version:"v0.0.1"}));Le(process.argv).pipe(Ve.provide(Se.layer),Re.runMain);
21
+ `;function je(o){let p=[];function i(e){switch(e.kind){case"BUILDER":p.push(e.name);break;case"TYPE_LITERAL":Object.values(e.metadata).forEach(i);break;case"UNION":case"TUPLE":e.members.forEach(i);break;case"RECORD":i(e.keyType),i(e.valueType);break}}return Object.values(o).forEach(i),p}class P extends N.Service()("@TSDataBuilders/Builders",{effect:N.gen(function*(){let o=yield*he.FileSystem,p=yield*J,{outputDir:i}=yield*b;return{create:N.fnUntraced(function*(e){if(yield*o.exists(i))yield*o.remove(i,{recursive:!0});yield*o.makeDirectory(i,{recursive:!0}),yield*p.generateBaseBuilder();let r=e.map((c)=>c.name),m=r.filter((c,t)=>r.indexOf(c)!==t),n=[...new Set(m)];if(m.length>0)return yield*N.dieMessage(`Duplicated builders: ${n.join(", ")}`);yield*N.all(e.map((c)=>p.generateBuilder(c)),{concurrency:"unbounded"})})}}),dependencies:[J.Default]}){}import*as d from"@effect/cli/Options";import*as R from"effect/HashMap";import*as Q from"effect/Option";import*as B from"effect/Schema";var ve=d.text("jsdocTag").pipe(d.withDescription("JSDoc tag used to mark types for data building generation."),d.withSchema(B.NonEmptyTrimmedString),d.withDefault("DataBuilder")),We=d.text("output-dir").pipe(d.withAlias("o"),d.withDescription("Output directory for generated builders."),d.withSchema(B.NonEmptyTrimmedString),d.withDefault("generated/builders")),Ye=d.text("include").pipe(d.withAlias("i"),d.withDescription("Glob pattern for files included while searching for jsdoc tag."),d.withSchema(B.NonEmptyTrimmedString),d.withDefault("src/**/*.ts{,x}")),Ge=d.text("file-suffix").pipe(d.withDescription("File suffix for created builder files."),d.withSchema(B.NonEmptyTrimmedString),d.withDefault(".builder")),Ze=d.text("builder-suffix").pipe(d.withDescription("Suffix for generated classes."),d.withSchema(B.NonEmptyTrimmedString),d.withDefault("Builder")),ze=d.keyValueMap("defaults").pipe(d.withDescription("Default values to be used in data builder constructor."),d.withSchema(B.HashMapFromSelf({key:B.Literal("string","number","boolean"),value:B.String}).pipe(B.transform(de,{decode:(o)=>{return{string:o.pipe(R.get("string"),Q.getOrElse(()=>"")),number:o.pipe(R.get("number"),Q.getOrElse(()=>"0")),boolean:o.pipe(R.get("boolean"),Q.getOrElse(()=>"false"))}},encode:(o)=>{return R.make(["string",o.string],["number",o.number],["boolean",o.boolean])},strict:!1}))),d.withDefault(me)),ye={jsdocTag:ve,outputDir:We,include:Ye,fileSuffix:Ge,builderSuffix:Ze,defaults:ze};import*as I from"effect/Chunk";import*as D from"effect/Effect";import*as Te from"effect/Function";import*as _ from"effect/Option";import*as ee from"effect/Stream";import*as Ee from"effect/Data";import*as ge from"effect/Effect";import*as Se from"effect/Stream";import{glob as Ke}from"glob";class U extends ge.Service()("@TSDataBuilders/TreeWalker",{succeed:{walk:(o)=>{return Se.fromAsyncIterable(Ke.stream(o,{cwd:".",nodir:!0}),(p)=>new ie({cause:p}))}}}){}class ie extends Ee.TaggedError("TreeWalkerError"){}import{FileSystem as Ve}from"@effect/platform";import*as O from"effect/Effect";import*as A from"effect/Stream";class X extends O.Service()("@TSDataBuilders/FileContentChecker",{effect:O.gen(function*(){let o=yield*Ve.FileSystem,p=new TextDecoder;return{check:O.fnUntraced(function*(i){let{content:e,filePath:s}=i;return yield*o.stream(s,{chunkSize:16384}).pipe(A.map((n)=>p.decode(n,{stream:!0})),A.mapAccum("",(n,c)=>{let t=n+c;return[t.slice(-e.length+1),t.includes(e)]}),A.find(Boolean),A.runCollect)})}})}){}class j extends D.Service()("@TSDataBuilders/Finder",{effect:D.gen(function*(){let o=yield*X,{include:p,jsdocTag:i}=yield*b,e=`@${i}`;return{find:D.fnUntraced(function*(){return yield*(yield*U).walk(p).pipe(ee.mapEffect((n)=>o.check({filePath:n,content:e}).pipe(D.map(I.map((c)=>c?_.some(n):_.none()))),{concurrency:"unbounded"}),ee.runCollect,D.map(I.flatMap(Te.identity)),D.map(I.filter((n)=>_.isSome(n))),D.map(I.map((n)=>n.value)))})}}),dependencies:[X.Default]}){}import*as Be from"@effect/platform/FileSystem";import*as v from"effect/Data";import*as E from"effect/Effect";import*as V from"effect/Either";import{Project as He,SyntaxKind as re}from"ts-morph";import{randomUUID as Ce}from"node:crypto";import*as K from"effect/Data";import*as l from"effect/Effect";import*as y from"effect/Match";import*as ke from"effect/Option";import{Node as qe,SyntaxKind as g}from"ts-morph";class te extends l.Service()("@TSDataBuilders/TypeNodeParser",{effect:l.gen(function*(){let{jsdocTag:o}=yield*b,p=(e)=>l.gen(function*(){let{node:s,optional:r}=e,m=s.getType(),n=m.getProperties();if(m.isObject()&&n.length>0){let c={};for(let t of n){let a=t.getName(),u=t.getTypeAtLocation(s),h=t.isOptional(),x=s.getProject().createSourceFile(`__temp_${Ce()}.ts`,`type __T = ${u.getText()}`,{overwrite:!0}).getTypeAliasOrThrow("__T").getTypeNodeOrThrow(),C=yield*l.suspend(()=>i({typeNode:x,optional:h}));c[a]=C}return{kind:"TYPE_LITERAL",metadata:c,optional:r}}return yield*new be({raw:m.getText(),kind:s.getKind()})}),i=(e)=>l.gen(function*(){let{typeNode:s,optional:r}=e,m=s.getKind(),n=y.value(m).pipe(y.when(y.is(g.StringKeyword),()=>l.succeed({kind:"STRING",optional:r})),y.when(y.is(g.NumberKeyword),()=>l.succeed({kind:"NUMBER",optional:r})),y.when(y.is(g.BooleanKeyword),()=>l.succeed({kind:"BOOLEAN",optional:r})),y.when((t)=>t===g.UndefinedKeyword,()=>l.succeed({kind:"UNDEFINED",optional:r})),y.when((t)=>t===g.ArrayType,()=>l.succeed({kind:"ARRAY",optional:r})),y.when((t)=>t===g.LiteralType,()=>l.succeed({kind:"LITERAL",literalValue:s.asKindOrThrow(g.LiteralType).getLiteral().getText(),optional:r})),y.when((t)=>t===g.TypeLiteral,()=>l.gen(function*(){let a=s.asKindOrThrow(g.TypeLiteral).getMembers();return{kind:"TYPE_LITERAL",metadata:yield*l.reduce(a,{},(h,f)=>l.gen(function*(){if(!f.isKind(g.PropertySignature))return h;let x=f.getTypeNode();if(!x)return h;let C=f.getNameNode().getText(),w=f.hasQuestionToken(),T=yield*l.suspend(()=>i({typeNode:x,optional:w}));return{...h,[C]:T}})),optional:r}})),y.when(y.is(g.ImportType),()=>l.gen(function*(){let t=s.asKindOrThrow(g.ImportType),a=t.getType(),u=a.getSymbol();if(!u)throw Error("TODO: missing symbol");let h=u.getDeclarations();if(h&&h.length>1)return yield*new se;let[f]=h;if(!f)return yield*new ce;let x=u.getJsDocTags().map((C)=>C.getName()).includes(o);if(a.isObject()){let C=a.getProperties();if(C.length>0){let w={};for(let T of C){let k=T.getName(),L=T.getTypeAtLocation(t),ae=T.isOptional(),q=t.getProject().createSourceFile(`__temp_${Ce()}.ts`,`type __T = ${L.getText()}`,{overwrite:!0}),Re=q.getTypeAliasOrThrow("__T").getTypeNodeOrThrow(),Ue=yield*l.suspend(()=>i({typeNode:Re,optional:ae}));w[k]=Ue,t.getProject().removeSourceFile(q)}return{kind:"TYPE_LITERAL",metadata:w,optional:r}}}return yield*new z({kind:m,raw:s.getText()})})),y.when(y.is(g.TupleType),()=>l.gen(function*(){let a=s.asKindOrThrow(g.TupleType).getElements(),u=yield*l.all(a.map((h)=>l.suspend(()=>i({typeNode:h,optional:!1}))));return{kind:"TUPLE",optional:r,members:u}})),y.when(y.is(g.TypeReference),()=>l.gen(function*(){let t=s.asKindOrThrow(g.TypeReference),a=t.getTypeName().getText();if(a==="Date")return{kind:"DATE",optional:r};let u=t.getTypeArguments();if(a==="Record"){let[k,L]=u;if(!k||!L)return yield*new z({kind:m,raw:s.getText()});let ae=yield*l.suspend(()=>i({typeNode:k,optional:!1})),q=yield*l.suspend(()=>i({typeNode:L,optional:!1}));return{kind:"RECORD",keyType:ae,valueType:q,optional:r}}if(a==="Array")return{kind:"ARRAY",optional:r};if(["Pick","Omit","Partial","Required","Readonly","Extract","NonNullable"].includes(a))return yield*p({node:t,optional:r});let f=t.getType().getAliasSymbol();if(!f)return yield*p({node:t,optional:r});let x=f.getDeclarations();if(x&&x.length>1)return yield*new se;let[C]=x;if(!C)return yield*new ce;let w=f?.getJsDocTags().map((k)=>k.getName()).includes(o);if(!qe.isTypeAliasDeclaration(C))throw Error("TODO: for non-type-alias declarations (interfaces, etc.)");let T=C.getTypeNode();if(!T)return yield*new z({kind:m,raw:s.getText()});if(!w)return yield*l.suspend(()=>i({typeNode:T,optional:r}));return{kind:"BUILDER",name:C.getName(),optional:r}})),y.when(y.is(g.UnionType),()=>l.gen(function*(){let t=yield*l.all(s.asKindOrThrow(g.UnionType).getTypeNodes().map((a)=>l.suspend(()=>i({typeNode:a,optional:!1}))));return{kind:"UNION",optional:r,members:t}})),y.when(y.is(g.IntersectionType),()=>l.gen(function*(){let a=s.asKindOrThrow(g.IntersectionType).getTypeNodes(),u=[g.StringKeyword,g.NumberKeyword,g.BooleanKeyword],h=a.find((f)=>u.includes(f.getKind()));if(h&&a.length>1)return{kind:"TYPE_CAST",baseTypeMetadata:yield*l.suspend(()=>i({typeNode:h,optional:!1})),optional:r};throw Error("TODO: handle it")})),y.option);if(ke.isNone(n))return yield*new z({kind:m,raw:s.getText()});return yield*n.value});return{generateMetadata:i}})}){}class z extends K.TaggedError("UnsupportedSyntaxKindError"){}class se extends K.TaggedError("MultipleSymbolDeclarationsError"){}class ce extends K.TaggedError("MissingSymbolDeclarationError"){}class be extends K.TaggedError("CannotBuildTypeReferenceMetadata"){}class W extends E.Service()("@TSDataBuilders/Parser",{effect:E.gen(function*(){let o=yield*Be.FileSystem,p=yield*te,{jsdocTag:i}=yield*b;return{generateBuildersMetadata:E.fnUntraced(function*(e){let s=yield*o.readFileString(e),r=yield*E.try({try:()=>{return new He().createSourceFile(e,s,{overwrite:!0}).getTypeAliases().filter((u)=>u.getJsDocs().flatMap((h)=>h.getTags().flatMap((f)=>f.getTagName())).includes(i)).map((u)=>{let h=u.getName();if(!u.isExported())return V.left(new De({path:e,typeName:h}));let f=u.getTypeNode();if(!(f?.isKind(re.TypeLiteral)||f?.isKind(re.TypeReference)))return V.left(new Ne({path:e,typeName:u.getName()}));return V.right({name:u.getName(),node:f})}).filter(Boolean)},catch:(c)=>new xe({cause:c})}),m=yield*E.all(r.map((c)=>c));return yield*E.all(m.map(({name:c,node:t})=>p.generateMetadata({typeNode:t,optional:!1}).pipe(E.map((a)=>({name:c,shape:a,path:e})),E.catchTags({UnsupportedSyntaxKindError:(a)=>E.fail(new $e({kind:a.kind,raw:a.raw,path:e,typeName:c})),CannotBuildTypeReferenceMetadata:(a)=>E.fail(new we({kind:a.kind,raw:a.raw,path:e,typeName:c}))}))))},E.catchTags({ParserError:(e)=>E.die(e),UnexportedDatabuilderError:(e)=>E.dieMessage(`[Parser]: Unexported databuilder ${e.typeName} at ${e.path}`),RichUnsupportedSyntaxKindError:(e)=>E.dieMessage(`[Parser]: Unsupported syntax kind of id: ${e.kind} with raw type: ${e.raw} found in type ${e.typeName} in file ${e.path}`),RichCannotBuildTypeReferenceMetadata:(e)=>E.dieMessage(`[Parser]: Cannot build type reference metadata with kind of id: ${e.kind} with raw type: ${e.raw} found in type ${e.typeName} in file ${e.path}. Is it a root of databuilder?`),UnsupportedBuilderTypeError:(e)=>E.dieMessage(`[Parser]: Unsupported builder type ${e.typeName} in file ${e.path}.`)}))}}),dependencies:[te.Default]}){}class xe extends v.TaggedError("ParserError"){}class De extends v.TaggedError("UnexportedDatabuilderError"){}class Ne extends v.TaggedError("UnsupportedBuilderTypeError"){}class $e extends v.TaggedError("RichUnsupportedSyntaxKindError"){}class we extends v.TaggedError("RichCannotBuildTypeReferenceMetadata"){}import*as Me from"effect/Chunk";import*as Y from"effect/Effect";import*as Ae from"effect/Function";var Fe=Y.gen(function*(){let o=yield*j,p=yield*W,i=yield*P,e=yield*o.find(),s=yield*Y.all(Me.map(e,(r)=>p.generateBuildersMetadata(r)),{concurrency:"unbounded"}).pipe(Y.map((r)=>r.flatMap(Ae.identity)));if(s.length===0)return;yield*i.create(s)});var Je=F.make("ts-databuilders",ye),Ie=Je.pipe(F.withHandler(()=>Fe),F.provide((o)=>G.mergeAll(j.Default,W.Default,P.Default,U.Default).pipe(G.provide(G.succeed(b,b.of(o))))),F.run({name:"Typescript Databuilders generator",version:"v0.0.1"}));Ie(process.argv).pipe(Qe.provide(Le.layer),Pe.runMain);
package/package.json CHANGED
@@ -57,5 +57,5 @@
57
57
  "glob": "^11.0.3",
58
58
  "ts-morph": "^27.0.2"
59
59
  },
60
- "version": "0.0.1-alpha.0"
60
+ "version": "0.0.1-alpha.1"
61
61
  }