@nemmtor/ts-databuilders 0.0.1-alpha.14 → 0.0.1-alpha.17
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.
- package/README.md +1 -1
- package/dist/main.js +24 -0
- package/package.json +11 -5
package/README.md
CHANGED
package/dist/main.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import*as ft from"@effect/platform-node/NodeContext";import*as pt from"@effect/platform-node/NodeRuntime";import*as ut from"effect/Effect";import*as mt from"effect/Layer";import*as gt from"effect/Logger";import*as St from"effect/LogLevel";import*as J from"@effect/cli/Command";import*as r from"@effect/cli/Options";import*as ne from"effect/Layer";import{Project as xt}from"ts-morph";import*as Ie from"@effect/platform/FileSystem";import*as ke from"@effect/platform/Path";import*as m from"effect/Effect";import*as D from"effect/Match";import*as Ye from"effect/Option";import*as H from"effect/Schema";import*as Ce from"@effect/platform/FileSystem";import*as je from"@effect/platform/Path";import*as Ge from"effect/Context";import*as x from"effect/Effect";import*as k from"effect/Option";import*as i from"effect/Schema";var C={jsdocTag:"JSDoc tag used to mark types for data building generation.",inlineDefaultJsdocTag:"JSDoc tag used to set default value of given field.",withNestedbuilders:"When set to true ts-databuilders will use nested builders approach.",outputDir:"Output directory for generated builders.",include:"Glob pattern for files included while searching for jsdoc tag.",fileSuffix:"File suffix for created builder files.",fileCase:"Naming convention for generated builder file",builderSuffix:"Suffix for generated classes.",defaults:"Default values to be used in data builder constructor.",defaultString:"Default string value to be used in data builder constructor.",defaultNumber:"Default number value to be used in data builder constructor.",defaultBoolean:"Default boolean value to be used in data builder constructor."};import*as ue from"effect/Effect";var R=class extends ue.Service()("@TSDataBuilders/Process",{succeed:{cwd:ue.sync(()=>process.cwd())}}){};var Tt=e=>e!==void 0,re=e=>Object.fromEntries(Object.entries(e).filter(([o,c])=>Tt(c)));var Oe="ts-databuilders.json",Et=i.Struct({jsdocTag:i.NonEmptyTrimmedString,inlineDefaultJsdocTag:i.NonEmptyTrimmedString,withNestedBuilders:i.Boolean,outputDir:i.NonEmptyTrimmedString,include:i.NonEmptyTrimmedString,fileSuffix:i.NonEmptyTrimmedString,fileCase:i.Literal("kebab","camel","pascal"),builderSuffix:i.NonEmptyTrimmedString,defaults:i.Struct({string:i.String,number:i.Number,boolean:i.Boolean})}),j=Et.make({jsdocTag:"DataBuilder",inlineDefaultJsdocTag:"DataBuilderDefault",withNestedBuilders:!0,outputDir:"generated/builders",include:"src/**/*.ts{,x}",fileSuffix:".builder",fileCase:"kebab",builderSuffix:"Builder",defaults:{string:"",number:0,boolean:!1}}),De=i.Struct({$schema:i.optional(i.String),jsdocTag:i.String.pipe(i.annotations({description:C.jsdocTag})),inlineDefaultJsdocTag:i.String.pipe(i.annotations({description:C.inlineDefaultJsdocTag})),withNestedBuilders:i.Boolean.pipe(i.annotations({description:C.withNestedbuilders})),outputDir:i.String.pipe(i.annotations({description:C.outputDir})),include:i.String.pipe(i.annotations({description:C.include})),fileSuffix:i.String.pipe(i.annotations({description:C.fileSuffix})),fileCase:i.Literal("kebab","camel","pascal").pipe(i.annotations({description:C.fileCase})),builderSuffix:i.String.pipe(i.annotations({description:C.builderSuffix})),defaults:i.Struct({string:i.String.pipe(i.annotations({description:C.defaultString})),number:i.Number.pipe(i.annotations({description:C.defaultNumber})),boolean:i.Boolean.pipe(i.annotations({description:C.defaultBoolean}))}).pipe(i.partial,i.annotations({description:C.defaults}))}).pipe(i.partial),L=class extends Ge.Tag("Configuration")(){},G=i.Struct({jsdocTag:i.NonEmptyTrimmedString,inlineDefaultJsdocTag:i.NonEmptyTrimmedString,withNestedBuilders:i.BooleanFromString,outputDir:i.NonEmptyTrimmedString,include:i.NonEmptyTrimmedString,fileSuffix:i.NonEmptyTrimmedString,fileCase:i.Literal("kebab","camel","pascal"),builderSuffix:i.NonEmptyTrimmedString,defaultString:i.String,defaultNumber:i.NumberFromString,defaultBoolean:i.BooleanFromString}),_e=e=>x.gen(function*(){yield*x.logDebug("[Configuration]: Loading configuration");let c=yield*(yield*R).cwd,d=(yield*je.Path).join(c,Oe),a=yield*Ot(d),E=yield*Nt({fromCLI:e,fromConfigFile:a});return L.of(E)}),Nt=e=>x.gen(function*(){let o=Ct(e),c=k.flatMap(e.fromConfigFile,E=>k.fromNullable(E.defaults)).pipe(k.map(E=>re(E)),k.getOrElse(()=>({}))),f=re({string:e.fromCLI.defaultString.pipe(k.getOrUndefined),number:e.fromCLI.defaultNumber.pipe(k.getOrUndefined),boolean:e.fromCLI.defaultBoolean.pipe(k.getOrUndefined)}),d={...c,...f},a={builderSuffix:yield*o("builderSuffix"),include:yield*o("include"),withNestedBuilders:yield*o("withNestedBuilders"),fileSuffix:yield*o("fileSuffix"),fileCase:yield*o("fileCase"),jsdocTag:yield*o("jsdocTag"),inlineDefaultJsdocTag:yield*o("inlineDefaultJsdocTag"),outputDir:yield*o("outputDir"),defaults:{...j.defaults,...d}};return yield*x.logDebug(`[Configuration]: Resolving config with value: ${JSON.stringify(a,null,4)}`),a}),Ct=e=>o=>e.fromCLI[o].pipe(x.orElse(()=>k.flatMap(e.fromConfigFile,c=>k.fromNullable(c[o]))),x.orElseSucceed(()=>j[o])),Ot=e=>x.gen(function*(){let o=yield*Ce.FileSystem;if(yield*x.orDie(o.exists(e))){yield*x.logDebug("[Configuration]: Found config file - attempting to read it");let f=yield*Dt(e),d=yield*i.decodeUnknown(De)(f);return k.some(d)}else return yield*x.logDebug("[Configuration]: No config file found"),k.none()}),Dt=e=>x.gen(function*(){let o=yield*Ce.FileSystem,c=yield*x.orDie(o.readFileString(e));return yield*x.try({try:()=>JSON.parse(c),catch:f=>`[FileSystem] Unable to read and parse JSON file from '${e}': ${String(f)}`})}).pipe(x.orDie);import*as B from"effect/Schema";var be=B.transform(B.String,B.String.pipe(B.brand("KebabCase")),{decode:e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/_/g,"-").toLowerCase(),encode:e=>e}),we=B.transform(B.String,B.String.pipe(B.brand("PascalCase")),{decode:e=>e.split(/[-_\s]+/).flatMap(o=>o.split(/(?=[A-Z])/)).filter(Boolean).map(o=>o.charAt(0).toUpperCase()+o.slice(1).toLowerCase()).join(""),encode:e=>e}),xe=B.transform(B.String,B.String.pipe(B.brand("CamelCase")),{decode:e=>e.split(/[-_\s]+/).flatMap(o=>o.split(/(?=[A-Z])/)).filter(Boolean).map((o,c)=>{let f=o.toLowerCase();return c===0?f:f.charAt(0).toUpperCase()+f.slice(1)}).join(""),encode:e=>e});import{randomUUID as wt}from"crypto";import*as me from"effect/Effect";var z=class extends me.Service()("@TSDataBuilders/IdGenerator",{succeed:{generateUuid:me.sync(()=>wt())}}){};var ge=class extends m.Service()("@TSDataBuilders/BuilderGenerator",{effect:m.gen(function*(){let o=yield*Ie.FileSystem,c=yield*ke.Path,f=yield*R,d=yield*L,a=yield*z,{fileSuffix:E,builderSuffix:O,defaults:g,fileCase:T}=d,u=m.fnUntraced(function*(n){return yield*{kebab:be.pipe(H.decode),pascal:we.pipe(H.decode),camel:xe.pipe(H.decode)}[T](n)}),s=n=>Ye.getOrUndefined(n.inlineDefault)??D.value(n).pipe(D.when({kind:"STRING"},()=>`"${g.string}"`),D.when({kind:"NUMBER"},()=>g.number),D.when({kind:"BOOLEAN"},()=>g.boolean),D.when({kind:"UNDEFINED"},()=>{}),D.when({kind:"BIGINT"},()=>"BigInt(0)"),D.when({kind:"SYMBOL"},()=>"Symbol('')"),D.when({kind:"ANY"},()=>{}),D.when({kind:"UNKNOWN"},()=>{}),D.when({kind:"NULL"},()=>null),D.when({kind:"DATE"},()=>"new Date()"),D.when({kind:"ARRAY"},()=>"[]"),D.when({kind:"LITERAL"},l=>l.literalValue),D.when({kind:"TYPE_CAST"},l=>s(l.baseTypeMetadata)),D.when({kind:"TUPLE"},l=>`[${l.members.map(P=>s(P)).map(P=>`${P}`).join(", ")}]`),D.when({kind:"TYPE_LITERAL"},l=>`{${Object.entries(l.metadata).filter(([P,{optional:y}])=>!y).map(([P,y])=>`${P}: ${s(y)}`).join(", ")}}`),D.when({kind:"RECORD"},l=>{if(l.keyType.kind==="STRING"||l.keyType.kind==="NUMBER")return"{}";let S=s(l.keyType),P=s(l.valueType);return`{${S}: ${P}}`}),D.when({kind:"UNION"},l=>{let P=l.members.slice().sort((y,N)=>{let I=Je.indexOf(y.kind),w=Je.indexOf(N.kind);return(I===-1?1/0:I)-(w===-1?1/0:w)})[0];return P?s(P):"never"}),D.when({kind:"BUILDER"},l=>`new ${l.name}${O}().build()`),D.exhaustive);return{generateBaseBuilder:m.fnUntraced(function*(){let n=c.join(yield*f.cwd,d.outputDir),l=c.resolve(n,`${yield*u("dataBuilder")}.ts`);yield*m.logDebug(`[Builders]: Creating base builder at ${l}`),yield*m.orDie(o.writeFileString(l,`${It}
|
|
3
|
+
`))}),generateBuilder:m.fnUntraced(function*(n){let l=new xt,S=n.name;yield*m.logDebug(`[Builders]: Creating builder for ${S}`);let P=c.join(yield*f.cwd,d.outputDir),y=c.resolve(P,`${yield*u(S)}${E}.ts`);yield*m.logDebug(`[Builders]: Creating builder content for ${S}`);let N=l.createSourceFile(`__temp_${yield*a.generateUuid}.ts`,"",{overwrite:!0}),I=c.resolve(n.path),w=c.relative(c.dirname(y),I).replace(/\.ts$/,"");if(N.addImportDeclaration({namedImports:[S],isTypeOnly:!0,moduleSpecifier:w}),N.addImportDeclaration({namedImports:["DataBuilder"],moduleSpecifier:"./data-builder"}),n.shape.kind!=="TYPE_LITERAL")return yield*m.dieMessage("[BuilderGenerator]: Expected root type to be type literal");let b=[...new Set(kt(n.shape.metadata))];yield*m.forEach(b,F=>be.pipe(H.decode)(F).pipe(m.andThen(A=>N.addImportDeclaration({namedImports:[`${F}${O}`],moduleSpecifier:`./${A}${E}`}))),{concurrency:"unbounded"});let v=Object.entries(n.shape.metadata).filter(([F,{optional:A}])=>!A).map(([F,A])=>`${F}: ${A.kind==="TYPE_CAST"?`${s(A)} as ${S}['${F}']`:s(A)}`),Z=yield*m.all(Object.entries(n.shape.metadata).map(([F,{optional:A,kind:fe}])=>Pt({fieldName:F,optional:A,typeName:S,isNestedBuilder:fe==="BUILDER"})),{concurrency:"unbounded"}),W=`{
|
|
4
|
+
${v.join(`,
|
|
5
|
+
`)}
|
|
6
|
+
}`;N.addClass({name:`${S}${O}`,isExported:!0,extends:`DataBuilder<${S}>`,methods:[{name:"constructor",statements:[`super(${W});`]},...Z]}),yield*m.logDebug(`[Builders]: Saving builder content at ${y}`),yield*o.writeFileString(y,`${N.getText()}
|
|
7
|
+
`)})}}),dependencies:[z.Default]}){},Je=["UNDEFINED","BOOLEAN","NUMBER","STRING","DATE","LITERAL","TYPE_LITERAL","ARRAY","TUPLE","RECORD"],It=`export abstract class DataBuilder<T> {
|
|
8
|
+
private data: T;
|
|
9
|
+
|
|
10
|
+
constructor(initialData: T) {
|
|
11
|
+
this.data = initialData;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public build(): Readonly<T> {
|
|
15
|
+
return structuredClone(this.data);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
protected with(partial: Partial<T>): this {
|
|
19
|
+
this.data = { ...this.data, ...partial };
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
`;function kt(e){let o=[];function c(f){switch(f.kind){case"BUILDER":o.push(f.name);break;case"TYPE_LITERAL":Object.values(f.metadata).forEach(c);break;case"UNION":case"TUPLE":f.members.forEach(c);break;case"RECORD":c(f.keyType),c(f.valueType);break}}return Object.values(e).forEach(c),o}var Pt=e=>m.gen(function*(){let{fieldName:o,optional:c,typeName:f,isNestedBuilder:d}=e,a=o.replaceAll("'","").replaceAll('"',""),E=yield*xe.pipe(H.decode)(a),O=`with${yield*we.pipe(H.decode)(a)}`,g=[`return this.with({ ${o}: ${E} });`],T=[`return this.with({ ${o}: ${E}.build() });`],u=d?T:g,s=[`if (!${E}) {`,` const { "${a}": _unused, ...rest } = this.build();`," return this.with(rest);","}"],n=c?[...s,...u]:u,l=`${f}['${a}']`;return{name:O,isPublic:!0,parameters:[{name:E,type:d?`DataBuilder<${l}>`:l}],statements:n}}),Q=class extends m.Service()("@TSDataBuilders/BuildersGenerator",{effect:m.gen(function*(){let o=yield*Ie.FileSystem,c=yield*ge,f=yield*R,d=yield*ke.Path,a=yield*L;return{create:m.fnUntraced(function*(E){let O=d.join(yield*f.cwd,a.outputDir);(yield*m.orDie(o.exists(O)))&&(yield*m.logDebug(`[Builders]: Removing already existing output directory at ${O}`),yield*m.orDie(o.remove(O,{recursive:!0}))),yield*m.logDebug(`[Builders]: Creating output directory at ${O}`),yield*m.orDie(o.makeDirectory(O,{recursive:!0})),yield*c.generateBaseBuilder();let T=E.map(n=>n.name),u=T.filter((n,l)=>T.indexOf(n)!==l),s=[...new Set(u)];if(u.length>0)return yield*m.dieMessage(`Duplicated builders: ${s.join(", ")}`);yield*m.all(E.map(n=>c.generateBuilder(n)),{concurrency:"unbounded"})})}}),dependencies:[ge.Default]}){};import*as Ve from"@effect/platform/FileSystem";import*as Ze from"@effect/platform/Path";import*as ze from"effect/Effect";import*as U from"effect/Option";import*as qe from"effect/Schema";var He=e=>ze.gen(function*(){let o=yield*R,c=yield*Ve.FileSystem,f=yield*o.cwd,a=(yield*Ze.Path).join(f,Oe),E=re({string:e.defaultString.pipe(U.getOrUndefined),number:e.defaultNumber.pipe(U.getOrUndefined),boolean:e.defaultBoolean.pipe(U.getOrUndefined)}),O=yield*qe.decode(De)({$schema:"https://raw.githubusercontent.com/nemmtor/ts-databuilders/refs/heads/main/schema.json",builderSuffix:e.builderSuffix.pipe(U.getOrElse(()=>j.builderSuffix)),fileSuffix:e.fileSuffix.pipe(U.getOrElse(()=>j.fileSuffix)),fileCase:e.fileCase.pipe(U.getOrElse(()=>j.fileCase)),include:e.include.pipe(U.getOrElse(()=>j.include)),jsdocTag:e.jsdocTag.pipe(U.getOrElse(()=>j.jsdocTag)),inlineDefaultJsdocTag:e.inlineDefaultJsdocTag.pipe(U.getOrElse(()=>j.inlineDefaultJsdocTag)),withNestedBuilders:e.withNestedBuilders.pipe(U.getOrElse(()=>j.withNestedBuilders)),outputDir:e.outputDir.pipe(U.getOrElse(()=>j.outputDir)),defaults:{...j.defaults,...E}});yield*c.writeFileString(a,`${JSON.stringify(O,null,2)}
|
|
24
|
+
`)});import*as ee from"effect/Chunk";import*as M from"effect/Effect";import*as ye from"effect/Option";import*as he from"effect/Stream";import*as Qe from"@effect/platform/FileSystem";import*as Xe from"effect/Chunk";import*as Y from"effect/Effect";import*as _ from"effect/Stream";var se=class extends Y.Service()("@TSDataBuilders/FileContentChecker",{effect:Y.gen(function*(){let o=yield*Qe.FileSystem,c=new TextDecoder;return{check:Y.fnUntraced(function*(f){let{content:d,filePath:a}=f;return yield*Y.logDebug(`[FileContentChecker](${a}): Checking file content`),yield*_.orDie(o.stream(a,{chunkSize:16*1024})).pipe(_.map(g=>c.decode(g,{stream:!0})),_.mapAccum("",(g,T)=>{let u=g+T;return[u.slice(-d.length+1),u.includes(d)]}),_.find(g=>!!g),_.tap(()=>Y.logDebug(`[FileContentChecker](${a}): found expected content`)),_.runCollect,Y.map(g=>g.pipe(Xe.get(0))))})}})}){};import*as et from"effect/Data";import*as q from"effect/Effect";import*as tt from"effect/Stream";import{glob as Ft}from"glob";import*as Se from"effect/Effect";var ce=class extends Se.Service()("@TSDataBuilders/Glob",{succeed:{iterate:o=>Se.sync(()=>Ft.iterate(o.path,{cwd:o.cwd,nodir:!0}))}}){};var de=class extends q.Service()("@TSDataBuilders/TreeWalker",{effect:q.gen(function*(){let o=yield*ce,c=yield*R;return{walk:q.fnUntraced(function*(f){let d=yield*c.cwd;return yield*q.logDebug(`[TreeWalker]: Walking path: ${d}/${f}`),tt.fromAsyncIterable(yield*o.iterate({path:f,cwd:d}),a=>new Pe({cause:a}))})}}),dependencies:[ce.Default]}){},Pe=class extends et.TaggedError("TreeWalkerError"){};var X=class extends M.Service()("@TSDataBuilders/Finder",{effect:M.gen(function*(){let o=yield*se,c=yield*de,{include:f,jsdocTag:d}=yield*L,a=`@${d}`;return{find:M.gen(function*(){yield*M.logDebug("[Finder]: Attempting to find files with builders");let O=yield*(yield*c.walk(f)).pipe(he.mapEffect(g=>o.check({filePath:g,content:a}).pipe(M.map(T=>T.pipe(ye.map(()=>g)))),{concurrency:"unbounded"}),he.runCollect,M.map(ee.filter(g=>ye.isSome(g))),M.map(ee.map(g=>g.value)));return yield*M.logDebug(`[Finder]: Found builders in files: ${O.pipe(ee.toArray).join(", ")}`),O}).pipe(M.catchTag("TreeWalkerError",E=>M.die(E)))}}),dependencies:[de.Default,se.Default]}){};import{Node as Rt,Project as Lt,SyntaxKind as h}from"ts-morph";import*as nt from"@effect/platform/FileSystem";import*as V from"effect/Data";import*as t from"effect/Effect";import*as le from"effect/Either";import*as p from"effect/Match";import*as K from"effect/Option";var Te=class extends t.Service()("@TSDataBuilders/TypeNodeParser",{effect:t.gen(function*(){let{jsdocTag:o,inlineDefaultJsdocTag:c,withNestedBuilders:f}=yield*L,d=yield*z,a=t.fnUntraced(function*(T){let u=T.getJsDocs();for(let s of u){let l=s.getTags().find(S=>S.getTagName()===c);if(l){let S=l.getComment();if(typeof S=="string")return K.some(S.trim())}}return K.none()}),E=t.fnUntraced(function*(T){let{type:u,contextNode:s}=T,n=u.getProperties(),l={};for(let S of n){let P=S.getName(),y=S.getTypeAtLocation(s),N=S.isOptional(),I=s.getProject().createSourceFile(`__temp_${yield*d.generateUuid}.ts`,`type __T = ${y.getText()}`,{overwrite:!0}),w=I.getTypeAliasOrThrow("__T").getTypeNodeOrThrow(),b=yield*t.suspend(()=>g({typeNode:w,optional:N,inlineDefault:K.none()}));l[P]=b,s.getProject().removeSourceFile(I)}return l}),O=t.fnUntraced(function*(T){let{type:u,contextNode:s,optional:n,inlineDefault:l}=T;return!u.isObject()||u.getProperties().length===0?yield*new Fe({raw:u.getText(),kind:s.getKind()}):{kind:"TYPE_LITERAL",metadata:yield*E({type:u,contextNode:s}),inlineDefault:l,optional:n}}),g=T=>t.gen(function*(){let{typeNode:u,optional:s,inlineDefault:n}=T,l=u.getKind(),S=p.value(l).pipe(p.when(p.is(h.StringKeyword),()=>t.succeed({kind:"STRING",inlineDefault:n,optional:s})),p.when(p.is(h.NumberKeyword),()=>t.succeed({kind:"NUMBER",inlineDefault:n,optional:s})),p.when(p.is(h.BooleanKeyword),()=>t.succeed({kind:"BOOLEAN",inlineDefault:n,optional:s})),p.when(p.is(h.UndefinedKeyword),()=>t.succeed({kind:"UNDEFINED",inlineDefault:n,optional:s})),p.when(p.is(h.BigIntKeyword),()=>t.succeed({kind:"BIGINT",inlineDefault:n,optional:s})),p.when(p.is(h.SymbolKeyword),()=>t.succeed({kind:"SYMBOL",inlineDefault:n,optional:s})),p.when(p.is(h.AnyKeyword),()=>t.succeed({kind:"ANY",inlineDefault:n,optional:s})),p.when(p.is(h.UnknownKeyword),()=>t.succeed({kind:"UNKNOWN",inlineDefault:n,optional:s})),p.when(p.is(h.ArrayType),()=>t.succeed({kind:"ARRAY",inlineDefault:n,optional:s})),p.when(p.is(h.LiteralType),()=>{let y=u.asKindOrThrow(h.LiteralType).getLiteral().getText();return y==="null"?t.succeed({kind:"NULL",inlineDefault:n,optional:s}):t.succeed({kind:"LITERAL",inlineDefault:n,literalValue:y,optional:s})}),p.when(p.is(h.TypeLiteral),()=>t.gen(function*(){let N=u.asKindOrThrow(h.TypeLiteral).getMembers(),I=yield*t.reduce(N,{},(w,b)=>t.gen(function*(){if(!b.isKind(h.PropertySignature))return w;let v=b.getTypeNode();if(!v)return w;let Z=b.getNameNode().getText(),W=b.hasQuestionToken(),F=yield*a(b),A=yield*t.suspend(()=>g({typeNode:v,optional:W,inlineDefault:F}));return{...w,[Z]:A}}));return{kind:"TYPE_LITERAL",inlineDefault:n,metadata:I,optional:s}})),p.when(p.is(h.ImportType),()=>t.gen(function*(){let y=u.asKindOrThrow(h.ImportType),N=y.getType(),I=N.getSymbol(),w=N.getText();if(!I)return yield*new Le({raw:w});let b=I.getDeclarations();if(b&&b.length>1)return yield*new Ne({raw:w});let[v]=b;return v?yield*O({type:N,contextNode:y,inlineDefault:n,optional:s}):yield*new Ee({raw:w})})),p.when(p.is(h.TupleType),()=>t.gen(function*(){let N=u.asKindOrThrow(h.TupleType).getElements(),I=yield*t.all(N.map(w=>t.suspend(()=>g({typeNode:w,optional:!1,inlineDefault:K.none()}))),{concurrency:"unbounded"});return{kind:"TUPLE",inlineDefault:n,optional:s,members:I}})),p.when(p.is(h.TypeReference),()=>t.gen(function*(){let y=u.asKindOrThrow(h.TypeReference),N=y.getTypeName().getText();if(N==="Date")return{kind:"DATE",optional:s,inlineDefault:n};if(N==="Array")return{kind:"ARRAY",optional:s,inlineDefault:n};let I=y.getTypeArguments();if(N==="Record"){let[pe,Ue]=I;if(!pe||!Ue)return yield*new te({kind:l,raw:u.getText()});let yt=yield*t.suspend(()=>g({typeNode:pe,optional:!1,inlineDefault:K.none()})),ht=yield*t.suspend(()=>g({typeNode:Ue,optional:!1,inlineDefault:K.none()}));return{kind:"RECORD",keyType:yt,valueType:ht,optional:s,inlineDefault:n}}if(["Pick","Omit","Partial","Required","Readonly","Extract","NonNullable"].includes(N))return yield*O({type:y.getType(),contextNode:y,optional:s,inlineDefault:n});let b=y.getType(),v=b.getText(),Z=b.getAliasSymbol();if(!Z)return yield*O({type:b,contextNode:y,optional:s,inlineDefault:n});let W=Z.getDeclarations();if(W&&W.length>1)return yield*new Ne({raw:v});let[F]=W;if(!F)return yield*new Ee({raw:v});let A=Z?.getJsDocTags().map(pe=>pe.getName()).includes(o);if(!Rt.isTypeAliasDeclaration(F))return yield*new Me;let fe=F.getTypeNode();return fe?!A||!f?yield*t.suspend(()=>g({typeNode:fe,optional:s,inlineDefault:n})):{kind:"BUILDER",name:F.getName(),inlineDefault:n,optional:s}:yield*new te({kind:l,raw:v})})),p.when(p.is(h.UnionType),()=>t.gen(function*(){let y=yield*t.all(u.asKindOrThrow(h.UnionType).getTypeNodes().map(N=>t.suspend(()=>g({typeNode:N,optional:!1,inlineDefault:K.none()}))),{concurrency:"unbounded"});return{kind:"UNION",optional:s,members:y,inlineDefault:n}})),p.when(p.is(h.IntersectionType),()=>t.gen(function*(){let y=u.asKindOrThrow(h.IntersectionType),N=y.getTypeNodes(),I=[h.StringKeyword,h.NumberKeyword,h.BooleanKeyword],w=N.find(W=>I.includes(W.getKind()));if(w&&N.length>1)return{kind:"TYPE_CAST",baseTypeMetadata:yield*t.suspend(()=>g({typeNode:w,optional:!1,inlineDefault:n})),inlineDefault:n,optional:s};let b=y.getType();return b.getProperties().length===0?yield*new te({kind:l,raw:u.getText()}):{kind:"TYPE_LITERAL",metadata:yield*E({type:b,contextNode:y}),inlineDefault:n,optional:s}})),p.option);return K.isNone(S)?yield*new te({kind:l,raw:u.getText()}):yield*S.value});return{generateMetadata:g}}),dependencies:[z.Default]}){},ie=class extends t.Service()("@TSDataBuilders/Parser",{effect:t.gen(function*(){let o=yield*nt.FileSystem,c=yield*Te,{jsdocTag:f}=yield*L;return{generateBuildersMetadata:d=>t.gen(function*(){yield*t.logDebug(`[Parser](${d}): Generating builder metadata`),yield*t.logDebug(`[Parser](${d}): Reading source code`);let a=yield*t.orDie(o.readFileString(d)),E=yield*t.try({try:()=>new Lt().createSourceFile(d,a,{overwrite:!0}).getTypeAliases().filter(n=>n.getJsDocs().flatMap(l=>l.getTags().flatMap(S=>S.getTagName())).includes(f)).map(n=>{let l=n.getName();if(!n.isExported())return le.left(new Ae({typeName:l}));let S=n.getTypeNode();return S?.isKind(h.TypeLiteral)||S?.isKind(h.TypeReference)?le.right({name:n.getName(),node:S}):le.left(new Re({typeName:n.getName()}))}).filter(Boolean),catch:T=>new Be({cause:T})}),O=yield*t.all(E.map(T=>T),{concurrency:"unbounded"});return yield*t.logDebug(`[Parser](${d}): Generating metadata for types: ${O.map(({name:T})=>T).join(", ")}`),yield*t.all(O.map(({name:T,node:u})=>c.generateMetadata({typeNode:u,optional:!1,inlineDefault:K.none()}).pipe(t.tap(()=>t.logDebug(`[Parser](${d}): Finished generating metadata for type: ${T}`)),t.map(s=>({name:T,shape:s,path:d})))),{concurrency:"unbounded"})}).pipe(t.catchTags({ParserError:a=>t.die(a),MissingSymbolDeclarationError:a=>t.dieMessage(`[Parser](${d}): Missing symbol declaration for type: ${a.raw}`),UnsupportedTypeAliasDeclarationError:()=>t.dieMessage(`[Parser](${d}): Unsupported type alias declaration`),MultipleSymbolDeclarationsError:a=>t.dieMessage(`[Parser](${d}): Missing symbol declaration error for type: ${a.raw}`),MissingSymbolError:a=>t.dieMessage(`[Parser](${d}): Missing symbol error for type: ${a.raw}`),UnexportedDatabuilderError:a=>t.dieMessage(`[Parser](${d}): Unexported databuilder ${a.typeName}`),UnsupportedSyntaxKindError:a=>t.dieMessage(`[Parser](${d}): Unsupported syntax kind of id: ${a.kind} for type: ${a.raw}`),CannotBuildTypeReferenceMetadataError:a=>t.dieMessage(`[Parser](${d}): Cannot build type reference metadata with kind of id: ${a.kind} for type: ${a.raw}. Is it a root of databuilder?`),UnsupportedBuilderTypeError:a=>t.dieMessage(`[Parser](${d}): Unsupported builder type ${a.typeName}`)}))}}),dependencies:[Te.Default]}){},te=class extends V.TaggedError("UnsupportedSyntaxKindError"){},Me=class extends V.TaggedError("UnsupportedTypeAliasDeclarationError"){},Fe=class extends V.TaggedError("CannotBuildTypeReferenceMetadataError"){},Be=class extends V.TaggedError("ParserError"){},Ae=class extends V.TaggedError("UnexportedDatabuilderError"){},Re=class extends V.TaggedError("UnsupportedBuilderTypeError"){},Le=class extends V.TaggedError("MissingSymbolError"){},Ee=class extends V.TaggedError("MissingSymbolDeclarationError"){},Ne=class extends V.TaggedError("MultipleSymbolDeclarationsError"){};import*as rt from"effect/Chunk";import*as $ from"effect/Effect";import*as at from"effect/Function";var st=$.gen(function*(){let e=yield*X,o=yield*ie,c=yield*Q;yield*$.logInfo("[TSDatabuilders]: Generating builders for your project.");let f=yield*e.find;yield*$.logInfo(`[TSDatabuilders]: Found builders in ${f.length} file(s).`),yield*$.logDebug("[TSDatabuilders]: Attempting to generate builders metadata");let d=yield*$.all(rt.map(f,a=>o.generateBuildersMetadata(a)),{concurrency:"unbounded"}).pipe($.map(a=>a.flatMap(at.identity)));d.length!==0&&(yield*$.logDebug("[TSDatabuilders]: Attempting to create builders files"),yield*c.create(d),yield*$.logInfo(`[TSDatabuilders]: Created ${d.length} builder(s).`))});var ct="0.0.1-alpha.17";var $t=r.text("jsdoc-tag").pipe(r.withDescription(C.jsdocTag),r.withSchema(G.fields.jsdocTag),r.optional),vt=r.text("inline-default-jsdoc-tag").pipe(r.withDescription(C.inlineDefaultJsdocTag),r.withSchema(G.fields.inlineDefaultJsdocTag),r.optional),jt=r.text("with-nested-builders").pipe(r.withDescription(C.withNestedbuilders),r.withSchema(G.fields.withNestedBuilders),r.optional),Gt=r.text("output-dir").pipe(r.withAlias("o"),r.withDescription(C.outputDir),r.withSchema(G.fields.outputDir),r.optional),_t=r.text("include").pipe(r.withAlias("i"),r.withDescription(C.include),r.withSchema(G.fields.include),r.optional),Kt=r.text("file-case").pipe(r.withDescription(C.fileCase),r.withSchema(G.fields.fileCase),r.optional),Jt=r.text("file-suffix").pipe(r.withDescription(C.fileSuffix),r.withSchema(G.fields.fileSuffix),r.optional),Yt=r.text("builder-suffix").pipe(r.withDescription(C.builderSuffix),r.withSchema(G.fields.builderSuffix),r.optional),Wt=r.text("default-string").pipe(r.withDescription(C.defaultString),r.withSchema(G.fields.defaultString),r.optional),Vt=r.text("default-number").pipe(r.withDescription(C.defaultNumber),r.withSchema(G.fields.defaultNumber),r.optional),Zt=r.text("default-boolean").pipe(r.withDescription(C.defaultBoolean),r.withSchema(G.fields.defaultBoolean),r.optional),dt={jsdocTag:$t,outputDir:Gt,withNestedBuilders:jt,include:_t,fileSuffix:Jt,fileCase:Kt,builderSuffix:Yt,defaultString:Wt,defaultNumber:Vt,defaultBoolean:Zt,inlineDefaultJsdocTag:vt},zt=J.make("init",dt).pipe(J.withHandler(He)),qt=J.make("ts-databuilders",dt),lt=qt.pipe(J.withHandler(()=>st),J.withSubcommands([zt]),J.provide(e=>ne.mergeAll(X.Default,ie.Default,Q.Default).pipe(ne.provide(ne.effect(L,_e(e))))),J.run({name:"Typescript Databuilders generator",version:ct}));var Ht=mt.mergeAll(gt.minimumLogLevel(St.Info),R.Default,ft.layer);lt(process.argv).pipe(ut.provide(Ht),pt.runMain);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@nemmtor/ts-databuilders",
|
|
4
|
-
"version": "0.0.1-alpha.
|
|
4
|
+
"version": "0.0.1-alpha.17",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"description": "CLI tool that automatically generates builder classes from annotated TypeScript types.",
|
|
@@ -28,13 +28,16 @@
|
|
|
28
28
|
],
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@biomejs/biome": "^2.3.2",
|
|
31
|
-
"@changesets/cli": "^2.29.7",
|
|
32
31
|
"@changesets/changelog-github": "^0.5.1",
|
|
33
|
-
"@
|
|
32
|
+
"@changesets/cli": "^2.29.7",
|
|
33
|
+
"@commitlint/cli": "^20.1.0",
|
|
34
|
+
"@commitlint/config-conventional": "^20.0.0",
|
|
35
|
+
"@commitlint/prompt-cli": "^20.1.0",
|
|
36
|
+
"@effect/language-service": "^0.56.0",
|
|
34
37
|
"@effect/vitest": "^0.27.0",
|
|
35
38
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
36
39
|
"@types/node": "^24.0.0",
|
|
37
|
-
"@vitest/coverage-v8": "4.0.
|
|
40
|
+
"@vitest/coverage-v8": "4.0.9",
|
|
38
41
|
"dependency-cruiser": "^17.2.0",
|
|
39
42
|
"knip": "^5.66.4",
|
|
40
43
|
"lefthook": "^2.0.2",
|
|
@@ -71,9 +74,12 @@
|
|
|
71
74
|
"check-types": "tsc -b tsconfig.json",
|
|
72
75
|
"check-knip": "knip",
|
|
73
76
|
"check-dep": "depcruise --output-type err-long src",
|
|
77
|
+
"check-commits": "commitlint",
|
|
74
78
|
"generate:schema": "tsx scripts/generate-schema.ts",
|
|
75
79
|
"generate:version": "node scripts/generate-version.mjs",
|
|
76
80
|
"package:version": "changeset version && pnpm generate:version && git add --all",
|
|
77
|
-
"package:release": "changeset publish"
|
|
81
|
+
"package:release": "pnpm build && changeset publish",
|
|
82
|
+
"postinstall": "pnpm lefthook install",
|
|
83
|
+
"commit": "commit"
|
|
78
84
|
}
|
|
79
85
|
}
|