From 5d0e955bcb9e376a6de13e91fe6d0cb807063a7f Mon Sep 17 00:00:00 2001 From: Wiktor Date: Tue, 27 Dec 2022 14:48:09 +0100 Subject: [PATCH] Introduced immutable query fragments --- src/queryBuilder.ts | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/queryBuilder.ts b/src/queryBuilder.ts index bdae7430..ecb39836 100644 --- a/src/queryBuilder.ts +++ b/src/queryBuilder.ts @@ -2,18 +2,29 @@ export abstract class QueryBuilder { __query: string = "" public constructor() { - this.__attr("__typename") + this.__attr("__typename", true) if (typeof (this as any).id === "function") (this as any).id() } - protected __attr(attr: string): this { - return this._(attr) + protected __attr(attr: string, mutable?: boolean): this { + return this._(attr, mutable) + } + + public __clone() { + return Object.assign(Object.create(Object.getPrototypeOf(this)), this) } // raw is exposed, to be able to add free form gql in the middle - public _(str: string): this { - this.__query += `${str}\n` // TODO: restore depth / formatting by passing base depth to constructor: ${"".padStart(this.__qb.stack.length * 2)} - return this + public _(str: string, mutable?: boolean): this { + if (mutable || str === "id") { + this.__query += `${str}\n` + return this + } else { + const clone = this.__clone() + clone.__query += `${str}\n` + + return clone + } } protected __child( @@ -21,10 +32,7 @@ export abstract class QueryBuilder { childType: new () => T, builder?: string | ((q: T) => T) | T ): this { - this._(`${childName} {\n`) - this.__buildChild(childType, builder) - this._(`}`) - return this + return this._(`${childName} {\n`).__buildChild(childType, builder)._(`}`) } // used for interfaces and unions @@ -45,13 +53,16 @@ export abstract class QueryBuilder { ) { // already instantiated child builder if (builder instanceof QueryBuilder) { - this._(builder.toString()) + return this._(builder.toString()) } else { - const childBuilder = new childType() - if (typeof builder === "string") childBuilder._(builder) - else if (typeof builder === "function") builder(childBuilder) - // undefined is ok as well, no fields at all - this._(childBuilder.toString()) + const baseChildBuilder = new childType() + const childBuilder = + typeof builder === "string" + ? baseChildBuilder._(builder) + : typeof builder === "function" + ? builder(baseChildBuilder) + : baseChildBuilder + return this._(childBuilder.toString()) } }