@nemmtor/ts-databuilders 0.0.1-alpha.20 → 0.0.1-alpha.22
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 +5 -5
- package/dist/main.js +2 -23
- package/dist/templates/base-builder.hbs +17 -0
- package/dist/templates/builder.hbs +34 -0
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Configuration is optional - it fallbacks to sensible defaults.
|
|
|
19
19
|
|
|
20
20
|
### Configure via CLI flags (optional):
|
|
21
21
|
```bash
|
|
22
|
-
pnpm ts-databuilders --include "src/**/*.ts{,x}" --output-dir src/__generated__ --jsdoc-tag DataBuilder
|
|
22
|
+
pnpm ts-databuilders --include "src/**/*.ts{,x}" --output-dir src/__generated__ --builder-jsdoc-tag-name DataBuilder
|
|
23
23
|
```
|
|
24
24
|
You can also provide configuration by going through interactive wizard:
|
|
25
25
|
```bash
|
|
@@ -34,8 +34,8 @@ Example of default config file:
|
|
|
34
34
|
```json
|
|
35
35
|
{
|
|
36
36
|
"$schema": "https://raw.githubusercontent.com/nemmtor/ts-databuilders/refs/heads/main/schema.json",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
37
|
+
"builderJsDocTagName": "DataBuilder",
|
|
38
|
+
"inlineDefaultJsDocTagName": "DataBuilderDefault",
|
|
39
39
|
"withNestedBuilders": true,
|
|
40
40
|
"outputDir": "generated/builders",
|
|
41
41
|
"include": "src/**/*.ts{,x}",
|
|
@@ -64,8 +64,8 @@ pnpm ts-databuilders init --wizard
|
|
|
64
64
|
|
|
65
65
|
| Name (in config file) | Flag (cli flags) | Description | Default |
|
|
66
66
|
|---------------|-------------------------------------------------------|-----------------------------------------|----------------------|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
67
|
+
| builderJsDocTagName | `--builder-jsdoc-tag-name` | JSDoc tag to mark types for generation | `DataBuilder` |
|
|
68
|
+
| inlineDefaultJsDocTagName | `--inline-default-jsdoc-tag-name` | JSDoc tag used to set default value of given field | `DataBuilderDefault` |
|
|
69
69
|
| withNestedBuilders | `--with-nested-builders` | When set to true ts-databuilders will use nested builders approach | `true` |
|
|
70
70
|
| outputDir | `--output-dir -o` | Output directory for generated builders | `generated/builders` |
|
|
71
71
|
| include | `--include -i` | Glob pattern for source files | `src/**/*.ts{,x}` |
|
package/dist/main.js
CHANGED
|
@@ -1,24 +1,3 @@
|
|
|
1
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
|
-
`)
|
|
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.20";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);
|
|
2
|
+
import*as ii from"@effect/platform-node/NodeContext";import*as oi from"@effect/platform-node/NodeRuntime";import*as ni from"effect/Effect";import*as I from"@effect/cli/Command";import*as l from"@effect/cli/Options";import*as oe from"effect/Layer";import*as _r from"@effect/platform/FileSystem";import*as Wr from"@effect/platform/Path";import*as se from"effect/Effect";import*as Dr from"@effect/platform/FileSystem";import*as Rr from"@effect/platform/Path";import*as vr from"effect/Context";import*as T from"effect/Effect";import*as D from"effect/Option";import*as o from"effect/Schema";import*as Ar from"effect/Data";import*as Q from"effect/Effect";var N=class extends Q.Service()("@TSDataBuilders/Process",{effect:Q.gen(function*(){return yield*Q.logDebug("[Process]: Instantiating"),{cwd:Q.try({try:()=>process.cwd(),catch:t=>new Cr({cause:t})})}})}){},Cr=class extends Ar.TaggedError("GetProcessCwdError"){};var ci=r=>r!==void 0,be=r=>Object.fromEntries(Object.entries(r).filter(([t,n])=>ci(n)));var m={builderJsDocTagName:"JSDoc tag used to mark types for data building generation.",inlineDefaultJsDocTagName:"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."};var Pr="ts-databuilders.json",fi=o.Struct({builderJsDocTagName:o.NonEmptyTrimmedString,inlineDefaultJsDocTagName:o.NonEmptyTrimmedString,withNestedBuilders:o.Boolean,outputDir:o.NonEmptyTrimmedString,include:o.NonEmptyTrimmedString,fileSuffix:o.NonEmptyTrimmedString,fileCase:o.Literal("kebab","camel","pascal"),builderSuffix:o.NonEmptyTrimmedString,defaults:o.Struct({string:o.String,number:o.Number,boolean:o.Boolean})}),w=fi.make({builderJsDocTagName:"DataBuilder",inlineDefaultJsDocTagName:"DataBuilderDefault",withNestedBuilders:!0,outputDir:"generated/builders",include:"src/**/*.ts{,x}",fileSuffix:".builder",fileCase:"kebab",builderSuffix:"Builder",defaults:{string:"",number:0,boolean:!1}}),Nr=o.Struct({$schema:o.optional(o.String),builderJsDocTagName:o.String.pipe(o.annotations({description:m.builderJsDocTagName})),inlineDefaultJsDocTagName:o.String.pipe(o.annotations({description:m.inlineDefaultJsDocTagName})),withNestedBuilders:o.Boolean.pipe(o.annotations({description:m.withNestedbuilders})),outputDir:o.String.pipe(o.annotations({description:m.outputDir})),include:o.String.pipe(o.annotations({description:m.include})),fileSuffix:o.String.pipe(o.annotations({description:m.fileSuffix})),fileCase:o.Literal("kebab","camel","pascal").pipe(o.annotations({description:m.fileCase})),builderSuffix:o.String.pipe(o.annotations({description:m.builderSuffix})),defaults:o.Struct({string:o.String.pipe(o.annotations({description:m.defaultString})),number:o.Number.pipe(o.annotations({description:m.defaultNumber})),boolean:o.Boolean.pipe(o.annotations({description:m.defaultBoolean}))}).pipe(o.partial,o.annotations({description:m.defaults}))}).pipe(o.partial),x=class extends vr.Tag("Configuration")(){},F=o.Struct({builderJsDocTagName:o.NonEmptyTrimmedString,inlineDefaultJsDocTagName:o.NonEmptyTrimmedString,withNestedBuilders:o.BooleanFromString,outputDir:o.NonEmptyTrimmedString,include:o.NonEmptyTrimmedString,fileSuffix:o.NonEmptyTrimmedString,fileCase:o.Literal("kebab","camel","pascal"),builderSuffix:o.NonEmptyTrimmedString,defaultString:o.String,defaultNumber:o.NumberFromString,defaultBoolean:o.BooleanFromString}),Kr=T.fnUntraced(function*(r){yield*T.logDebug("[Configuration]: Loading configuration");let n=yield*(yield*N).cwd,a=(yield*Rr.Path).join(n,Pr);yield*T.logDebug("[Configuration]: Attempting to resolve config file");let s=yield*di(a);yield*T.logDebug("[Configuration]: Attempting to resolve config");let e=yield*li({fromCLI:r,fromConfigFile:s});return yield*T.logDebug(`[Configuration]: Config resolved with: ${yield*o.encode(o.parseJson({replacer:null,space:2}))(e)}`),x.of(e)}),li=T.fnUntraced(function*(r){let t=pi(r),n=D.flatMap(r.fromConfigFile,e=>D.fromNullable(e.defaults)).pipe(D.map(e=>be(e)),D.getOrElse(()=>({}))),i=be({string:r.fromCLI.defaultString.pipe(D.getOrUndefined),number:r.fromCLI.defaultNumber.pipe(D.getOrUndefined),boolean:r.fromCLI.defaultBoolean.pipe(D.getOrUndefined)}),a={...n,...i};return{builderSuffix:yield*t("builderSuffix"),include:yield*t("include"),withNestedBuilders:yield*t("withNestedBuilders"),fileSuffix:yield*t("fileSuffix"),fileCase:yield*t("fileCase"),builderJsDocTagName:yield*t("builderJsDocTagName"),inlineDefaultJsDocTagName:yield*t("inlineDefaultJsDocTagName"),outputDir:yield*t("outputDir"),defaults:{...w.defaults,...a}}}),pi=r=>t=>r.fromCLI[t].pipe(T.orElse(()=>D.flatMap(r.fromConfigFile,n=>D.fromNullable(n[t]))),T.orElseSucceed(()=>w[t])),di=T.fnUntraced(function*(r){let t=yield*Dr.FileSystem;if(yield*T.logDebug(`[Configuration]: Checking if config file exists at ${r}`),yield*T.orDie(t.exists(r))){yield*T.logDebug("[Configuration]: Found config file - attempting to read it");let i=yield*ui(r),a=yield*o.decodeUnknown(Nr)(i);return D.some(a)}else return yield*T.logDebug("[Configuration]: No config file found"),D.none()}),ui=T.fnUntraced(function*(r){let t=yield*Dr.FileSystem,n=yield*T.orDie(t.readFileString(r));return yield*o.decodeUnknown(o.parseJson())(n)},T.orDie);import*as jr from"effect/Effect";import*as q from"effect/Match";import*as y from"effect/Schema";var Mr=y.transform(y.String,y.String,{decode:r=>r.replace(/^['"]|['"]$/g,""),encode:r=>r}),mi=y.transform(y.String,y.String.pipe(y.brand("KebabCase")),{decode:r=>r.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/_/g,"-").toLowerCase(),encode:r=>r}),xr=y.transform(y.String,y.String.pipe(y.brand("PascalCase")),{decode:r=>r.split(/[-_\s]+/).map(t=>t===t.toUpperCase()?t.charAt(0).toUpperCase()+t.slice(1).toLowerCase():t.split(/(?=[A-Z])/).filter(Boolean).map(n=>n.charAt(0).toUpperCase()+n.slice(1).toLowerCase()).join("")).join(""),encode:r=>r}),yi=y.transform(y.String,y.String.pipe(y.brand("CamelCase")),{decode:r=>r.split(/[-_\s]+/).flatMap(t=>t.split(/(?=[A-Z])/)).filter(Boolean).map((t,n)=>{let i=t.toLowerCase();return n===0?i:i.charAt(0).toUpperCase()+i.slice(1)}).join(""),encode:r=>r}),Ve=jr.fnUntraced(function*(r){return q.value(r).pipe(q.when("kebab",()=>mi.pipe(y.decode)),q.when("pascal",()=>xr.pipe(y.decode)),q.when("camel",()=>yi.pipe(y.decode)),q.exhaustive)});import gi from"handlebars";import*as $r from"effect/Data";import*as Y from"effect/Effect";var z=class extends Y.Service()("@TSDataBuilders/TemplateCompiler",{effect:Y.gen(function*(){return{compile:Y.fnUntraced(function*(t,n){return yield*Y.try({try:()=>gi.compile(t)(n),catch:i=>new br({cause:i})})})}})}){},br=class extends $r.TaggedError("TemplateCompileError"){};var ce=class extends se.Service()("@TSDataBuilders/BaseBuilderGenerator",{effect:se.gen(function*(){let t=yield*Wr.Path,n=yield*N,i=yield*x,a=yield*_r.FileSystem,s=yield*z,e=yield*Ve(i.fileCase);return{generate:se.gen(function*(){let c=t.join(yield*n.cwd,i.outputDir),f=t.resolve(c,`${yield*e("dataBuilder")}.ts`),d=yield*a.readFileString(t.join(import.meta.dirname,"templates/base-builder.hbs"),"utf-8");yield*se.logDebug(`[Builders]: Creating base builder at ${f}`),yield*a.writeFileString(f,yield*s.compile(d))})}}),dependencies:[z.Default]}){};import*as Z from"effect/Effect";import{Chunk as Xe}from"effect";import*as xt from"@effect/platform/FileSystem";import*as bt from"@effect/platform/Path";import*as h from"effect/Effect";import*as Se from"effect/Ref";import*as wr from"effect/Schema";import*as H from"effect/Effect";import*as Te from"effect/Effect";import*as p from"effect/Effect";import*as S from"effect/Match";import*as Nt from"effect/Option";import{createPrinter as qr,createProgram as zr,createSourceFile as Yr,EmitHint as Hr,findConfigFile as Zr,getJSDocTags as Xr,isArrayTypeNode as et,isIndexedAccessTypeNode as rt,isIndexSignatureDeclaration as tt,isInterfaceDeclaration as it,isIntersectionTypeNode as ot,isMappedTypeNode as nt,isNamedTupleMember as Or,isOptionalTypeNode as at,isParenthesizedTypeNode as st,isPropertySignature as ct,isTemplateLiteralTypeNode as ft,isTupleTypeNode as lt,isTypeAliasDeclaration as pt,isTypeLiteralNode as We,isTypeOperatorNode as dt,isTypeQueryNode as ut,isTypeReferenceNode as mt,isUnionTypeNode as yt,NodeBuilderFlags as Oe,parseJsonConfigFileContent as gt,readConfigFile as Tt,ScriptTarget as St,SymbolFlags as Et,SyntaxKind as E,sys as Qe}from"typescript";import{SyntaxKind as Ti}from"typescript";var Qr=r=>r.kind===Ti.LiteralType,Si=["Date","Promise","Map","Set","WeakMap","WeakSet","RegExp","Error","BigInt","Array"],_e=r=>Si.includes(r);var qe=r=>{let t=Object.entries(r);return t.length===0?"{}":`{${t.map(([i,a])=>`'${i.replace(/^['"](.*)['"]$/,"$1")}':${a}`).join(",")}}`};import*as Ze from"effect/Data";var ze=class extends Ze.TaggedError("CannotGeneratorDefaultValueForNeverError"){},Ye=class extends Ze.TaggedError("UnionMembersIsEmptyError"){},He=class extends Ze.TaggedError("MissingBuiltinTypeDefaultError"){};import*as ht from"effect/Context";var fe=class extends ht.Tag("@TSDataBuilders/TypeReferenceSideEffect")(){};var hi={Date:"new Date()",Promise:"new Promise(() => {})",Map:"new Map()",Set:"new Set()",WeakMap:"new WeakMap()",WeakSet:"new WeakSet()",RegExp:'new RegExp("")',Error:"new Error()",BigInt:"BigInt(0)",Array:"[]"},we=class extends p.Service()("@TSDataBuilders/TypeNodeDefaultValueGenerator",{effect:p.gen(function*(){let{defaults:t,builderJsDocTagName:n}=yield*x,i=p.fnUntraced(function*(a,s){return yield*S.value(a).pipe(S.when({kind:"StringKeyword"},()=>p.succeed(`'${t.string}'`)),S.when({kind:"NumberKeyword"},()=>p.succeed(t.number.toString(10))),S.when({kind:"BooleanKeyword"},()=>p.succeed(String(t.boolean))),S.when({kind:"UndefinedKeyword"},()=>p.succeed("undefined")),S.when({kind:"LiteralType"},({literal:e})=>p.succeed(e)),S.when({kind:"BigIntKeyword"},()=>p.succeed("0n")),S.when({kind:"SymbolKeyword"},()=>p.succeed("Symbol('')")),S.when({kind:"AnyKeyword"},()=>p.succeed(String(Ci))),S.when({kind:"UnknownKeyword"},()=>p.succeed(String(Di))),S.when({kind:"NeverKeyword"},()=>new ze),S.when({kind:"TypeLiteral"},({propertySignatures:e})=>p.gen(function*(){let c=yield*p.all(e.map(d=>s.onPropertySignature(d)),{concurrency:"unbounded"}),f=Object.assign({},...c);return qe(f)})),S.when({kind:"ArrayType"},()=>p.succeed("[]")),S.when({kind:"TupleType"},({elements:e})=>p.all(e.map(c=>i(c,s)),{concurrency:"unbounded"}).pipe(p.map(c=>`[${c.join(", ")}]`))),S.when({kind:"UnionType"},({members:e})=>Pt(e).pipe(p.flatMap(c=>i(c,s)))),S.when({kind:"IntersectionType"},({members:e})=>p.gen(function*(){return e.some(u=>u.kind!=="TypeLiteral"&&u.kind!=="TypeReference")?`${yield*Pt(e).pipe(p.flatMap(P=>i(P,s)))} as any`:`{${(yield*p.all(e.map(u=>i(u,s)),{concurrency:"unbounded"})).map(u=>u.replace(/^\{|\}$/g,"").trim()).filter(u=>u.length>0).join(", ")}}`})),S.when({kind:"TypeReference"},e=>p.gen(function*(){let{onTypeReference:c}=yield*fe;return yield*c(e),e.jsDocTags.some(f=>f.name===n)?`new ${e.referenceName}Builder().build()`:yield*Nt.match(e.inlineType,{onNone:()=>_e(e.referenceName)?p.succeed(hi[e.referenceName]):p.fail(new He({referenceName:e.referenceName})),onSome:f=>i(f,s)})})),S.exhaustive)});return{generate:i}})}){},Dt={UndefinedKeyword:0,BooleanKeyword:1,NumberKeyword:2,StringKeyword:3,SymbolKeyword:4,BigIntKeyword:5,LiteralType:6,TypeLiteral:7,ArrayType:8,TupleType:9,UnionType:10,IntersectionType:11,UnknownKeyword:12,AnyKeyword:13,TypeReference:14,NeverKeyword:15},Ci=void 0,Di=void 0,Pt=p.fnUntraced(function*(r){let t=r.toSorted((n,i)=>Dt[n.kind]-Dt[i.kind])[0];return t||(yield*new Ye({memberKinds:r.map(n=>n.kind)}))});var Fe=class extends Te.Service()("@TSDataBuilders/PropertySignatureDefaultValueGenerator",{effect:Te.gen(function*(){let t=yield*we,{inlineDefaultJsDocTagName:n}=yield*x,i=Te.fnUntraced(function*(a){if(a.hasQuestionToken)return{};let s=a.jsDocTags.find(e=>e.name===n)?.comment??(yield*t.generate(a.typeNode,{onPropertySignature:i}));return{[a.name]:s}});return{generate:i}}),dependencies:[we.Default]}){};var ke=class extends H.Service()("@TSDataBuilders/DeclarationDefaultValueGenerator",{effect:H.gen(function*(){let t=yield*Fe;return{generate:H.fnUntraced(function*(n){let i=yield*H.all(n.propertySignatures.map(s=>t.generate(s))),a=Object.assign({},...i);return qe(a)})}}),dependencies:[Fe.Default]}){};var Le=class extends h.Service()("@TSDataBuilders/DeclarationBuilderGenerator",{effect:h.gen(function*(){let t=yield*ke,n=yield*N,i=yield*bt.Path,a=yield*xt.FileSystem,s=yield*z,e=yield*x,c=yield*Ve(e.fileCase);return{generate:h.fnUntraced(function*(f){let{declaration:d,fileAbsolutePath:u}=f,P=yield*Se.make(Xe.empty()),ye=yield*t.generate(d).pipe(h.provideService(fe,fe.of({onTypeReference:v=>h.if(v.jsDocTags.some(xe=>xe.name===e.builderJsDocTagName),{onTrue:()=>Se.update(P,Xe.append(v.referenceName)),onFalse:()=>h.void})}))),Tr=yield*Se.get(P).pipe(h.map(Xe.dedupe)),ge=yield*h.all(Tr.pipe(Xe.map(v=>c(v).pipe(h.map(xe=>({referenceName:v,path:`${xe}${e.fileSuffix}`}))))),{concurrency:"unbounded"}),$e=d.name,Ir=i.join(yield*n.cwd,e.outputDir),Pe=i.resolve(Ir,`${yield*c($e)}${e.fileSuffix}.ts`),Sr=i.resolve(u),Ne=i.relative(i.dirname(Pe),Sr).replace(/\.ts$/,""),Er=yield*h.all(d.propertySignatures.map(h.fnUntraced(function*(v){let xe=v.typeNode.kind==="TypeReference"&&v.typeNode.jsDocTags.some(si=>si.name===e.builderJsDocTagName),Br=yield*Mr.pipe(wr.decode)(v.name);return{name:{original:Br,pascal:yield*xr.pipe(wr.decode)(Br)},hasQuestionToken:v.hasQuestionToken,isBuilder:xe}})),{concurrency:"unbounded"}),hr=yield*a.readFileString(i.join(import.meta.dirname,"templates/builder.hbs"),"utf-8"),ai=yield*s.compile(hr,{baseDataBuilderFileName:yield*c("dataBuilder"),builderSuffix:e.builderSuffix,typeName:$e,originalFileImportPath:Ne,defaultValues:ye,properties:Er,subBuilders:ge});yield*a.writeFileString(Pe,ai)})}}),dependencies:[ke.Default,z.Default]}){};var le=class extends Z.Service()("@TSDataBuilders/FileBuilderGenerator",{effect:Z.gen(function*(){let t=yield*Le;return{generate:Z.fnUntraced(function*(n){yield*Z.all(n.declarations.map(i=>t.generate({fileAbsolutePath:n.fileAbsolutePath,declaration:i})),{concurrency:"unbounded"})})}}),dependencies:[Le.Default]}){};import*as Ee from"effect/Chunk";import*as C from"effect/Effect";import*as er from"effect/Option";import*as rr from"effect/Stream";import*as wt from"@effect/platform/FileSystem";import*as Ft from"effect/Chunk";import*as k from"effect/Effect";import*as L from"effect/Stream";var Ie=class extends k.Service()("@TSDataBuilders/FileContentChecker",{effect:k.gen(function*(){yield*k.logDebug("[FileContentChecker]: Instantiating");let t=yield*wt.FileSystem,n=new TextDecoder;return{check:k.fnUntraced(function*(i){let{content:a,filePath:s}=i;return yield*k.logDebug(`[FileContentChecker]: Checking file content of ${s}`),yield*L.orDie(t.stream(s,{chunkSize:16*1024})).pipe(L.map(f=>n.decode(f,{stream:!0})),L.mapAccum("",(f,d)=>{let u=f+d;return[u.slice(-a.length+1),u.includes(a)]}),L.find(f=>!!f),L.tap(()=>k.logDebug(`[FileContentChecker]: Found expected content in ${s}`)),L.runCollect,k.map(f=>f.pipe(Ft.get(0))))})}})}){};import*as It from"effect/Data";import*as $ from"effect/Effect";import*as Bt from"effect/Stream";import*as kt from"glob";import*as Lt from"effect/Data";import*as B from"effect/Effect";var Be=class extends B.Service()("@TSDataBuilders/Glob",{effect:B.gen(function*(){return yield*B.logDebug("[Glob]: Instantiating"),{iterate:B.fnUntraced(function*(t){return yield*B.logDebug(`[Glob]: Iterating ${t.path} in ${t.cwd}`),yield*B.try({try:()=>kt.glob.iterate(t.path,{cwd:t.cwd,nodir:!0}),catch:n=>new Fr({cause:n})})})}})}){},Fr=class extends Lt.TaggedError("GlobIterationError"){};var Ae=class extends $.Service()("@TSDataBuilders/FileTreeWalker",{effect:$.gen(function*(){yield*$.logDebug("[FileTreeWalker]: Instantiating");let t=yield*Be,n=yield*N;return{walk:$.fnUntraced(function*(i){let a=yield*n.cwd;return yield*$.logDebug(`[FileTreeWalker]: Walking path: ${a}/${i}`),Bt.fromAsyncIterable(yield*t.iterate({path:i,cwd:a}),s=>new kr({cause:s}))})}}),dependencies:[Be.Default]}){},kr=class extends It.TaggedError("FileTreeWalkError"){};var pe=class extends C.Service()("@TSDataBuilders/Finder",{effect:C.gen(function*(){yield*C.logDebug("[Finder]: Instantiating");let t=yield*Ie,n=yield*Ae;return{find:C.fnUntraced(function*(i){yield*C.logDebug(`[Finder]: Searching file paths in ${i.include}`);let a=yield*n.walk(i.include);yield*C.logDebug(`[Finder]: Checking which files include at least 1 occurance of ${i.pattern}`);let s=yield*a.pipe(rr.mapEffect(e=>t.check({filePath:e,content:i.pattern}).pipe(C.map(c=>c.pipe(er.map(()=>e)))),{concurrency:"unbounded"}),rr.runCollect,C.map(Ee.filter(e=>er.isSome(e))),C.map(Ee.map(e=>e.value)));return yield*C.logDebug(`[Finder]: Found files with expected pattern: ${s.pipe(Ee.toArray).join(", ")}`),s},C.catchTag("FileTreeWalkError",i=>C.die(i)))}}),dependencies:[Ae.Default,Ie.Default]}){};import*as Ut from"effect/Context";var V=class extends Ut.Tag("@TSDataBuilders/ParseDeclarationPredicate")(){};import*as _t from"effect/Chunk";import*as J from"effect/Effect";import*as Kt from"@effect/platform/Path";import*as A from"effect/Effect";import*as vt from"@effect/platform/Path";import*as _ from"effect/Effect";import*as X from"effect/Data";var tr=class extends X.TaggedError("TSConfigNotFoundError"){},he=class extends X.TaggedError("GetTSConfigError"){},ir=class extends X.TaggedError("CreateProgramError"){},or=class extends X.TaggedError("GetTypeCheckerError"){},nr=class extends X.TaggedError("GetSourceFileError"){},ar=class extends X.TaggedError("CreateSourceFileError"){},sr=class extends X.TaggedError("SourceFileNotFoundError"){};var Ue=class extends _.Service()("@TSDataBuilders/TSConfigResolver",{effect:_.gen(function*(){let t=yield*vt.Path,i=yield*(yield*N).cwd;return{resolve:_.gen(function*(){let a=yield*_.try({try:()=>Zr(i,Qe.fileExists,"tsconfig.json"),catch:e=>new he({cause:e})});if(!a)return yield*new tr;let s=yield*_.try({try:()=>{let{config:e,error:c}=Tt(a,Qe.readFile);if(c)throw c;return e},catch:e=>new he({cause:e})});return yield*_.try({try:()=>gt(s,Qe,t.dirname(a)),catch:e=>new he({cause:e})})})}})}){};var W=class extends A.Service()("@TSDataBuilders/TypescriptProgram",{effect:A.gen(function*(){let t=yield*Kt.Path,i=yield*(yield*Ue).resolve,a=yield*A.try({try:()=>zr(i.fileNames,i.options),catch:f=>new ir({cause:f})}),s=yield*A.try({try:()=>a.getTypeChecker(),catch:f=>new or({cause:f})}),e=A.fnUntraced(function*(f){let d=t.resolve(f),u=yield*A.try({try:()=>a.getSourceFile(d),catch:P=>new nr({cause:P})});return u||(yield*new sr)});return{program:a,typeChecker:s,getSourceFile:e,createSourceFile:f=>A.try({try:()=>Yr(f.fileName,f.sourceText,i.options.target??St.Latest),catch:d=>new ar({cause:d})})}}),dependencies:[Ue.Default]}){};import*as Vt from"@effect/platform/Path";import*as R from"effect/Effect";import*as U from"effect/Data";var Ce=class extends U.TaggedError("GetNodeChildrenError"){},cr=class extends U.TaggedError("SyntaxListNodeNotFoundError"){},fr=class extends U.TaggedError("UnsupportedTypeAliasDeclarationTypeNodeError"){},lr=class extends U.TaggedError("GetJsDocTagsNamesError"){},pr=class extends U.TaggedError("UnsupportedTypeLiteralNodeMemberError"){},dr=class extends U.TaggedError("MissingPropertySignatureTypeNodeError"){},ee=class extends U.TaggedError("GetTypeNodeNameTextError"){},de=class extends U.TaggedError("CannotGetNodeTextError"){},ur=class extends U.TaggedError("UnsupportedTypeNodeError"){},Ge=class extends U.TaggedError("CannotCreateSyntheticTypeNodeError"){};import*as G from"effect/Effect";import*as gr from"effect/Option";import*as te from"effect/Effect";import*as yr from"effect/Option";import*as Mt from"effect/Effect";var j=r=>Mt.try({try:()=>Xr(r).map(t=>({name:t.tagName.getText(),comment:typeof t.comment=="string"?t.comment:void 0})),catch:t=>new lr({cause:t})});import*as M from"effect/Effect";import*as g from"effect/Effect";import*as Re from"effect/Option";var ve=class extends g.Service()("@TSDataBuilders/TypeNodeParser",{effect:g.gen(function*(){yield*g.logDebug("[TypeNodeParser]: Instantiating");let t=yield*W,n=qr(),i=yield*t.createSourceFile({fileName:"temp.ts",sourceText:""}),a=g.fnUntraced(function*(e){let c=t.typeChecker.typeToTypeNode(e.type,e.typeNode,Oe.InTypeAlias|Oe.NoTruncation);return c?yield*s(c,{onTypeLiteral:e.onTypeLiteral}):yield*new Ge}),s=g.fnUntraced(function*(e,c){if(e.kind===E.StringKeyword)return{kind:"StringKeyword"};if(e.kind===E.NumberKeyword)return{kind:"NumberKeyword"};if(e.kind===E.BooleanKeyword)return{kind:"BooleanKeyword"};if(e.kind===E.UndefinedKeyword)return{kind:"UndefinedKeyword"};if(e.kind===E.BigIntKeyword)return{kind:"BigIntKeyword"};if(e.kind===E.SymbolKeyword)return{kind:"SymbolKeyword"};if(e.kind===E.AnyKeyword)return{kind:"AnyKeyword"};if(e.kind===E.UnknownKeyword)return{kind:"UnknownKeyword"};if(e.kind===E.NeverKeyword)return{kind:"NeverKeyword"};if(e.kind===E.ObjectKeyword)return{kind:"TypeLiteral",propertySignatures:[]};if(Qr(e))return{kind:"LiteralType",literal:yield*g.try({try:()=>e.pos===-1?n.printNode(Hr.Unspecified,e,i):e.getText(),catch:f=>new de({cause:f})})};if(We(e))return{kind:"TypeLiteral",propertySignatures:yield*c.onTypeLiteral(e)};if(et(e))return{kind:"ArrayType"};if(lt(e))return{kind:"TupleType",elements:yield*g.all(e.elements.filter(d=>!(Or(d)&&d.dotDotDotToken)).map(d=>s(d,c)),{concurrency:"unbounded"})};if(Or(e))return yield*s(e.type,c);if(yt(e))return{kind:"UnionType",members:yield*g.all(e.types.map(f=>s(f,c)),{concurrency:"unbounded"})};if(mt(e)){let f=yield*g.try({try:()=>"escapedText"in e.typeName?e.typeName.escapedText.toString():e.typeName.getText(),catch:ge=>new de({cause:ge})});if(_e(f))return{kind:"TypeReference",referenceName:f,inlineType:Re.none(),jsDocTags:[]};if(["Awaited","Exclude","Extract","ReturnType","Parameters","ConstructorParameters","InstanceType"].includes(f)){let ge=t.typeChecker.getTypeAtLocation(e);return yield*a({type:ge,typeNode:e,onTypeLiteral:c.onTypeLiteral})}let u=t.typeChecker.getTypeAtLocation(e),P=u.getProperties();if(P.length>0){let $e={kind:"TypeLiteral",propertySignatures:yield*g.all(P.map(g.fn(function*(Ne){let Er=t.typeChecker.getTypeOfSymbol(Ne),hr=yield*a({type:Er,typeNode:e,onTypeLiteral:c.onTypeLiteral});return{name:Ne.getName(),typeNode:hr,hasQuestionToken:!!(Ne.flags&Et.Optional),jsDocTags:[]}})),{concurrency:"unbounded"})},Pe=(u.aliasSymbol??u.getSymbol())?.declarations?.[0],Sr=Pe?yield*j(Pe):[];return{kind:"TypeReference",referenceName:f,inlineType:Re.some($e),jsDocTags:Sr}}let ye=t.typeChecker.typeToTypeNode(u,e,Oe.InTypeAlias|Oe.NoTruncation);if(!ye)return yield*new Ge;let Tr=yield*s(ye,c);return{kind:"TypeReference",referenceName:f,inlineType:Re.some(Tr),jsDocTags:[]}}if(dt(e)){if(e.operator===E.KeyOfKeyword){let f=t.typeChecker.getTypeAtLocation(e);return yield*a({type:f,typeNode:e,onTypeLiteral:c.onTypeLiteral})}if(e.operator===E.ReadonlyKeyword)return yield*s(e.type,c)}if(ut(e)){let f=t.typeChecker.getTypeAtLocation(e);return yield*a({type:f,typeNode:e,onTypeLiteral:c.onTypeLiteral})}if(rt(e)){let f=t.typeChecker.getTypeAtLocation(e);return yield*a({type:f,typeNode:e,onTypeLiteral:c.onTypeLiteral})}if(nt(e)){let f=t.typeChecker.getTypeAtLocation(e);return yield*a({type:f,typeNode:e,onTypeLiteral:c.onTypeLiteral})}if(ot(e))return{kind:"IntersectionType",members:yield*g.all(e.types.map(f=>s(f,c)),{concurrency:"unbounded"})};if(ft(e)){let f=yield*g.try({try:()=>e.head.text,catch:P=>new de({cause:P})}),d=yield*g.all(e.templateSpans.map(P=>g.try({try:()=>P.literal.text,catch:ye=>new de({cause:ye})})),{concurrency:"unbounded"});return{kind:"LiteralType",literal:`'${f+d.join("")}'`}}return at(e)?yield*s(e.type,c):st(e)?yield*s(e.type,c):yield*new ur({kind:E[e.kind]})});return{parse:s}}),dependencies:[W.Default]}){};var re=class extends M.Service()("@TSDataBuilders/TypeLiteralParser",{effect:M.gen(function*(){let t=yield*ve,n=(a,s)=>M.all(a.filter(e=>!tt(e)).map(M.fn(function*(e){if(!ct(e))return yield*new pr;if(!e.type)return yield*new dr;let c=yield*t.parse(e.type,{onTypeLiteral:s.onTypeLiteral});return{name:yield*M.try({try:()=>"escapedText"in e.name?e.name.escapedText.toString():e.name.getText(),catch:d=>new ee({cause:d})}),typeNode:c,hasQuestionToken:!!e.questionToken,jsDocTags:yield*j(e)}})),{concurrency:"unbounded"}),i=a=>n(a.members,{onTypeLiteral:i});return{parseMembers:n,parse:i}}),dependencies:[ve.Default]}){};var Ke=class extends te.Service()("@TSDataBuilders/InterfaceDeclarationParser",{effect:te.gen(function*(){let t=yield*V,n=yield*re;return{parse:te.fnUntraced(function*(i){let a=i.modifiers?.some(c=>c.kind===E.ExportKeyword)??!1,s=yield*j(i);if(!(yield*t({isExported:a,jsDocTags:s})))return yr.none();let e=yield*te.try({try:()=>i.name.getText(),catch:c=>new ee({cause:c})});return yr.some({name:e,propertySignatures:yield*n.parseMembers(i.members,{onTypeLiteral:n.parse}),jsDocTags:yield*j(i)})})}}),dependencies:[re.Default]}){};import{Option as $t}from"effect";import*as ie from"effect/Effect";var je=class extends ie.Service()("@TSDataBuilders/TypeAliasDeclarationParser",{effect:ie.gen(function*(){let t=yield*re,n=yield*V;return{parse:ie.fnUntraced(function*(i){let a=i.modifiers?.some(c=>c.kind===E.ExportKeyword)??!1,s=yield*j(i);if(!(yield*n({isExported:a,jsDocTags:s})))return $t.none();if(!We(i.type))return yield*new fr;let e=yield*ie.try({try:()=>i.name.getText(),catch:c=>new ee({cause:c})});return $t.some({name:e,propertySignatures:yield*t.parse(i.type),jsDocTags:yield*j(i)})})}}),dependencies:[re.Default]}){};var Me=class extends G.Service()("@TSDataBuilders/SyntaxListParser",{effect:G.gen(function*(){let t=yield*je,n=yield*Ke;return{parse:G.fnUntraced(function*(i){let a=yield*G.try({try:()=>i.getChildren(),catch:e=>new Ce({cause:e})});return(yield*G.all(a.map(G.fnUntraced(function*(e){return pt(e)?yield*t.parse(e):it(e)?yield*n.parse(e):gr.none()})),{concurrency:"unbounded"})).filter(e=>gr.isSome(e)).map(e=>e.value)})}}),dependencies:[Ke.Default,je.Default]}){};var Je=class extends R.Service()("@TSDataBuilders/SourceFileParser",{effect:R.gen(function*(){let t=yield*Me,n=yield*Vt.Path;return{parse:i=>R.gen(function*(){let s=(yield*R.try({try:()=>i.getChildren(),catch:e=>new Ce({cause:e})})).find(e=>e.kind===E.SyntaxList);return s?{fileAbsolutePath:i.fileName,declarations:yield*t.parse(s)}:yield*new cr}).pipe(R.catchTags({UnsupportedTypeNodeError:a=>R.dieMessage(`Unsupported type node of kind ${a.kind} found in file ${n.relative(process.cwd(),i.fileName)}`)}))}}),dependencies:[Me.Default]}){};var me=class extends J.Service()("@TSDataBuilders/ProjectParser",{effect:J.gen(function*(){let t=yield*W,n=yield*Je;return{parseFiles:J.fnUntraced(function*(i){return yield*J.all(i.pipe(_t.map(s=>t.getSourceFile(s).pipe(J.flatMap(e=>n.parse(e))))),{concurrency:"unbounded"})})}}),dependencies:[W.Default,Je.Default]}){};import*as Qt from"@effect/platform/FileSystem";import*as qt from"@effect/platform/Path";import*as zt from"effect/Effect";import*as O from"effect/Option";import*as De from"effect/Schema";var Yt=zt.fnUntraced(function*(r){let t=yield*N,n=yield*Qt.FileSystem,i=yield*t.cwd,s=(yield*qt.Path).join(i,Pr),e=be({string:r.defaultString.pipe(O.getOrUndefined),number:r.defaultNumber.pipe(O.getOrUndefined),boolean:r.defaultBoolean.pipe(O.getOrUndefined)}),c=yield*De.decode(Nr)({$schema:"https://raw.githubusercontent.com/nemmtor/ts-databuilders/refs/heads/main/schema.json",builderSuffix:r.builderSuffix.pipe(O.getOrElse(()=>w.builderSuffix)),fileSuffix:r.fileSuffix.pipe(O.getOrElse(()=>w.fileSuffix)),fileCase:r.fileCase.pipe(O.getOrElse(()=>w.fileCase)),include:r.include.pipe(O.getOrElse(()=>w.include)),builderJsDocTagName:r.builderJsDocTagName.pipe(O.getOrElse(()=>w.builderJsDocTagName)),inlineDefaultJsDocTagName:r.inlineDefaultJsDocTagName.pipe(O.getOrElse(()=>w.inlineDefaultJsDocTagName)),withNestedBuilders:r.withNestedBuilders.pipe(O.getOrElse(()=>w.withNestedBuilders)),outputDir:r.outputDir.pipe(O.getOrElse(()=>w.outputDir)),defaults:{...w.defaults,...e}});yield*n.writeFileString(s,`${yield*De.encode(De.parseJson({replacer:null,space:2}))(c)}
|
|
3
|
+
`)});import*as Ht from"@effect/platform/FileSystem";import*as b from"effect/Effect";import*as Zt from"effect/Layer";var Xt=b.gen(function*(){let r=yield*Ht.FileSystem,t=yield*pe,n=yield*me,i=yield*le,a=yield*ce,{include:s,builderJsDocTagName:e,outputDir:c}=yield*x;yield*b.logInfo(`Scanning '${s}' for files including types annotated with '@${e}'.`);let f=yield*t.find({include:s,pattern:`@${e}`});if(f.length===0){yield*b.logInfo("Nothing to generate.");return}yield*b.logInfo(`Found ${f.length} file(s), parsing it to understand data shape.`);let d=yield*n.parseFiles(f);yield*b.logInfo("Generating builders."),yield*r.makeDirectory(c,{recursive:!0}),yield*a.generate,yield*b.all(d.flatMap(P=>i.generate(P)),{concurrency:"unbounded"});let u=d.flatMap(P=>P.declarations).length;yield*b.logInfo(`Created ${u} builder(s).`)}),ei=Zt.effect(V,b.gen(function*(){let r=yield*x;return t=>t.isExported?t.jsDocTags.some(n=>n.name===r.builderJsDocTagName)?b.succeed(!0):b.succeed(!1):b.succeed(!1)}));var ri="0.0.1-alpha.21";var Ki=l.text("builder-jsdoc-tag-name").pipe(l.withDescription(m.builderJsDocTagName),l.withSchema(F.fields.builderJsDocTagName),l.optional),ji=l.text("inline-default-jsdoc-tag-name").pipe(l.withDescription(m.inlineDefaultJsDocTagName),l.withSchema(F.fields.inlineDefaultJsDocTagName),l.optional),Mi=l.text("with-nested-builders").pipe(l.withDescription(m.withNestedbuilders),l.withSchema(F.fields.withNestedBuilders),l.optional),Ji=l.text("output-dir").pipe(l.withAlias("o"),l.withDescription(m.outputDir),l.withSchema(F.fields.outputDir),l.optional),$i=l.text("include").pipe(l.withAlias("i"),l.withDescription(m.include),l.withSchema(F.fields.include),l.optional),Vi=l.text("file-case").pipe(l.withDescription(m.fileCase),l.withSchema(F.fields.fileCase),l.optional),_i=l.text("file-suffix").pipe(l.withDescription(m.fileSuffix),l.withSchema(F.fields.fileSuffix),l.optional),Wi=l.text("builder-suffix").pipe(l.withDescription(m.builderSuffix),l.withSchema(F.fields.builderSuffix),l.optional),Qi=l.text("default-string").pipe(l.withDescription(m.defaultString),l.withSchema(F.fields.defaultString),l.optional),qi=l.text("default-number").pipe(l.withDescription(m.defaultNumber),l.withSchema(F.fields.defaultNumber),l.optional),zi=l.text("default-boolean").pipe(l.withDescription(m.defaultBoolean),l.withSchema(F.fields.defaultBoolean),l.optional),ti={builderJsDocTagName:Ki,outputDir:Ji,withNestedBuilders:Mi,include:$i,fileSuffix:_i,fileCase:Vi,builderSuffix:Wi,defaultString:Qi,defaultNumber:qi,defaultBoolean:zi,inlineDefaultJsDocTagName:ji},Yi=I.make("init",ti).pipe(I.withHandler(Yt)),Hi=I.make("ts-databuilders",ti),Lr=Hi.pipe(I.withHandler(()=>Xt),I.withSubcommands([Yi]),I.provide(r=>oe.mergeAll(pe.Default,me.Default,le.Default,ce.Default).pipe(oe.provideMerge(ei),oe.provideMerge(oe.effect(x,Kr(r))),oe.provideMerge(N.Default))),I.run({name:"Typescript Databuilders generator",version:ri}));Lr(process.argv).pipe(ni.provide(ii.layer),oi.runMain);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export abstract class DataBuilder<T> {
|
|
2
|
+
private data: T;
|
|
3
|
+
|
|
4
|
+
constructor(initialData: T) {
|
|
5
|
+
this.data = initialData;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
public build(): Readonly<T> {
|
|
9
|
+
return structuredClone(this.data);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
protected with(partial: Partial<T>): this {
|
|
13
|
+
this.data = { ...this.data, ...partial };
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { DataBuilder } from './{{baseDataBuilderFileName}}';
|
|
2
|
+
import type { {{typeName}} } from '{{originalFileImportPath}}';
|
|
3
|
+
|
|
4
|
+
{{#each subBuilders}}
|
|
5
|
+
import { {{referenceName}}{{../builderSuffix}} } from './{{path}}';
|
|
6
|
+
{{/each}}
|
|
7
|
+
|
|
8
|
+
export class {{typeName}}{{builderSuffix}} extends DataBuilder<{{typeName}}> {
|
|
9
|
+
constructor() {
|
|
10
|
+
super({{{defaultValues}}});
|
|
11
|
+
}
|
|
12
|
+
{{#each properties}}
|
|
13
|
+
|
|
14
|
+
{{#if isBuilder}}
|
|
15
|
+
with{{{name.pascal}}}(builder: DataBuilder<{{../typeName}}['{{{name.original}}}']>) {
|
|
16
|
+
{{#if hasQuestionToken}}
|
|
17
|
+
const value = builder?.build();
|
|
18
|
+
{{else}}
|
|
19
|
+
const value = builder.build();
|
|
20
|
+
{{/if}}
|
|
21
|
+
{{else}}
|
|
22
|
+
with{{{name.pascal}}}(value: {{../typeName}}['{{{name.original}}}']) {
|
|
23
|
+
{{/if}}
|
|
24
|
+
{{#if hasQuestionToken}}
|
|
25
|
+
if (value === undefined) {
|
|
26
|
+
const { "{{{name.original}}}": _unused, ...rest } = this.build();
|
|
27
|
+
return this.with(rest);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
{{/if}}
|
|
31
|
+
return this.with({ '{{{name.original}}}': value });
|
|
32
|
+
}
|
|
33
|
+
{{/each}}
|
|
34
|
+
}
|
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.22",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"description": "CLI tool that automatically generates builder classes from annotated TypeScript types.",
|
|
@@ -33,11 +33,13 @@
|
|
|
33
33
|
"@commitlint/cli": "^20.1.0",
|
|
34
34
|
"@commitlint/config-conventional": "^20.0.0",
|
|
35
35
|
"@commitlint/prompt-cli": "^20.1.0",
|
|
36
|
-
"@effect/language-service": "^0.
|
|
36
|
+
"@effect/language-service": "^0.72.0",
|
|
37
37
|
"@effect/vitest": "^0.27.0",
|
|
38
|
+
"@stryker-mutator/core": "^9.3.0",
|
|
39
|
+
"@stryker-mutator/vitest-runner": "^9.3.0",
|
|
38
40
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
39
41
|
"@types/node": "^24.0.0",
|
|
40
|
-
"@vitest/coverage-v8": "4.0.
|
|
42
|
+
"@vitest/coverage-v8": "4.0.18",
|
|
41
43
|
"dependency-cruiser": "^17.2.0",
|
|
42
44
|
"knip": "^5.66.4",
|
|
43
45
|
"lefthook": "^2.0.2",
|
|
@@ -45,16 +47,14 @@
|
|
|
45
47
|
"tsx": "^4.20.6",
|
|
46
48
|
"vitest": "^4.0.6"
|
|
47
49
|
},
|
|
48
|
-
"peerDependencies": {
|
|
49
|
-
"typescript": "^5.9.3"
|
|
50
|
-
},
|
|
51
50
|
"dependencies": {
|
|
52
|
-
"@effect/cli": "^0.
|
|
53
|
-
"@effect/platform": "^0.
|
|
54
|
-
"@effect/platform-node": "^0.
|
|
51
|
+
"@effect/cli": "^0.73.0",
|
|
52
|
+
"@effect/platform": "^0.94.0",
|
|
53
|
+
"@effect/platform-node": "^0.104.0",
|
|
55
54
|
"effect": "^3.18.4",
|
|
56
55
|
"glob": "^13.0.0",
|
|
57
|
-
"
|
|
56
|
+
"handlebars": "^4.7.8",
|
|
57
|
+
"typescript": "^5.9.3"
|
|
58
58
|
},
|
|
59
59
|
"engines": {
|
|
60
60
|
"node": ">=20.0.0"
|