/* * Copyright 2025 the original author or authors. *

* Licensed under the Moderne Source Available License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* https://docs.moderne.io/licensing/moderne-source-available-license *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import {mapAsync, updateIfChanged} from "../util"; import {TreeVisitor, ValidRecipeReturnType} from "../visitor"; import {SourceFile} from "../tree"; import {isJson, Json} from "./tree"; import {create, Draft} from "mutative"; export class JsonVisitor

extends TreeVisitor { async isAcceptable(sourceFile: SourceFile): Promise { return isJson(sourceFile); } protected async visitArray(array: Json.Array, p: P): Promise { const updates: any = { prefix: await this.visitSpace(array.prefix, p), markers: await this.visitMarkers(array.markers, p), values: await mapAsync(array.values, value => this.visitRightPadded(value, p)) }; return updateIfChanged(array, updates); } protected async visitDocument(document: Json.Document, p: P): Promise { const updates: any = { prefix: await this.visitSpace(document.prefix, p), markers: await this.visitMarkers(document.markers, p), value: await this.visitDefined(document.value, p), eof: await this.visitSpace(document.eof, p) }; return updateIfChanged(document, updates); } protected async visitEmpty(empty: Json.Empty, p: P): Promise { const updates: any = { prefix: await this.visitSpace(empty.prefix, p), markers: await this.visitMarkers(empty.markers, p) }; return updateIfChanged(empty, updates); } protected async visitIdentifier(identifier: Json.Identifier, p: P): Promise { const updates: any = { prefix: await this.visitSpace(identifier.prefix, p), markers: await this.visitMarkers(identifier.markers, p) }; return updateIfChanged(identifier, updates); } protected async visitLiteral(literal: Json.Literal, p: P): Promise { const updates: any = { prefix: await this.visitSpace(literal.prefix, p), markers: await this.visitMarkers(literal.markers, p) }; return updateIfChanged(literal, updates); } protected async visitMember(member: Json.Member, p: P): Promise { const updates: any = { prefix: await this.visitSpace(member.prefix, p), markers: await this.visitMarkers(member.markers, p), key: (await this.visitRightPadded(member.key, p))!, value: await this.visitDefined(member.value, p) }; return updateIfChanged(member, updates); } protected async visitObject(jsonObject: Json.Object, p: P): Promise { const updates: any = { prefix: await this.visitSpace(jsonObject.prefix, p), markers: await this.visitMarkers(jsonObject.markers, p), members: await mapAsync(jsonObject.members, member => this.visitRightPadded(member, p)) }; return updateIfChanged(jsonObject, updates); } public async visitRightPadded(right: Json.RightPadded, p: P): Promise | undefined> { const updates: any = { element: await this.visitDefined(right.element, p), after: await this.visitSpace(right.after, p) }; return updateIfChanged(right, updates); } public async visitSpace(space: Json.Space, p: P): Promise { return space; } protected async produceJson( before: Json | undefined, p: P, recipe?: (draft: Draft) => ValidRecipeReturnType> | PromiseLike>> ): Promise { if (before === undefined) { return undefined; } const [draft, finishDraft] = create(before as J); (draft as Draft).prefix = await this.visitSpace(before!.prefix, p); (draft as Draft).markers = await this.visitMarkers(before!.markers, p); if (recipe) { await recipe(draft); } return finishDraft() as J; } protected accept(t: Json, p: P): Promise { switch (t.kind) { case Json.Kind.Array: return this.visitArray(t as Json.Array, p); case Json.Kind.Document: return this.visitDocument(t as Json.Document, p); case Json.Kind.Empty: return this.visitEmpty(t as Json.Empty, p); case Json.Kind.Identifier: return this.visitIdentifier(t as Json.Identifier, p); case Json.Kind.Literal: return this.visitLiteral(t as Json.Literal, p); case Json.Kind.Member: return this.visitMember(t as Json.Member, p); case Json.Kind.Object: return this.visitObject(t as Json.Object, p); default: throw new Error(`Unexpected JSON kind ${t.kind}`); } } }