forked from MapComplete/MapComplete
		
	Merge develop
This commit is contained in:
		
						commit
						94f39e89fe
					
				
					 174 changed files with 3695 additions and 3420 deletions
				
			
		| 
						 | 
				
			
			@ -2,7 +2,6 @@ import { LayerConfigJson } from "../Json/LayerConfigJson"
 | 
			
		|||
import { Utils } from "../../../Utils"
 | 
			
		||||
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
 | 
			
		||||
import { ConversionContext } from "./ConversionContext"
 | 
			
		||||
import { T } from "vitest/dist/types-aac763a5"
 | 
			
		||||
 | 
			
		||||
export interface DesugaringContext {
 | 
			
		||||
    tagRenderings: Map<string, QuestionableTagRenderingConfigJson>
 | 
			
		||||
| 
						 | 
				
			
			@ -11,10 +10,11 @@ export interface DesugaringContext {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
export type ConversionMsgLevel = "debug" | "information" | "warning" | "error"
 | 
			
		||||
 | 
			
		||||
export interface ConversionMessage {
 | 
			
		||||
    context: ConversionContext
 | 
			
		||||
    message: string
 | 
			
		||||
    level: ConversionMsgLevel
 | 
			
		||||
    readonly context: ConversionContext
 | 
			
		||||
    readonly message: string
 | 
			
		||||
    readonly level: ConversionMsgLevel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export abstract class Conversion<TIn, TOut> {
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +85,7 @@ export class Pure<TIn, TOut> extends Conversion<TIn, TOut> {
 | 
			
		|||
export class Bypass<T> extends DesugaringStep<T> {
 | 
			
		||||
    private readonly _applyIf: (t: T) => boolean
 | 
			
		||||
    private readonly _step: DesugaringStep<T>
 | 
			
		||||
 | 
			
		||||
    constructor(applyIf: (t: T) => boolean, step: DesugaringStep<T>) {
 | 
			
		||||
        super("Applies the step on the object, if the object satisfies the predicate", [], "Bypass")
 | 
			
		||||
        this._applyIf = applyIf
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +103,6 @@ export class Bypass<T> extends DesugaringStep<T> {
 | 
			
		|||
export class Each<X, Y> extends Conversion<X[], Y[]> {
 | 
			
		||||
    private readonly _step: Conversion<X, Y>
 | 
			
		||||
    private readonly _msg: string
 | 
			
		||||
    private readonly _filter: (x: X) => boolean
 | 
			
		||||
 | 
			
		||||
    constructor(step: Conversion<X, Y>, options?: { msg?: string }) {
 | 
			
		||||
        super(
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +173,7 @@ export class Pass<T> extends Conversion<T, T> {
 | 
			
		|||
        super(message ?? "Does nothing, often to swap out steps in testing", [], "Pass")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    convert(json: T, context: ConversionContext): T {
 | 
			
		||||
    convert(json: T, _: ConversionContext): T {
 | 
			
		||||
        return json
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -224,6 +224,7 @@ export class FirstOf<T, X> extends Conversion<T, X> {
 | 
			
		|||
export class Cached<TIn, TOut> extends Conversion<TIn, TOut> {
 | 
			
		||||
    private _step: Conversion<TIn, TOut>
 | 
			
		||||
    private readonly key: string
 | 
			
		||||
 | 
			
		||||
    constructor(step: Conversion<TIn, TOut>) {
 | 
			
		||||
        super("Secretly caches the output for the given input", [], "cached")
 | 
			
		||||
        this._step = step
 | 
			
		||||
| 
						 | 
				
			
			@ -242,9 +243,11 @@ export class Cached<TIn, TOut> extends Conversion<TIn, TOut> {
 | 
			
		|||
        return converted
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class Fuse<T> extends DesugaringStep<T> {
 | 
			
		||||
    private readonly steps: DesugaringStep<T>[]
 | 
			
		||||
    protected debug = false
 | 
			
		||||
    private readonly steps: DesugaringStep<T>[]
 | 
			
		||||
 | 
			
		||||
    constructor(doc: string, ...steps: DesugaringStep<T>[]) {
 | 
			
		||||
        super(
 | 
			
		||||
            (doc ?? "") +
 | 
			
		||||
| 
						 | 
				
			
			@ -301,7 +304,7 @@ export class SetDefault<T> extends DesugaringStep<T> {
 | 
			
		|||
        this._overrideEmptyString = overrideEmptyString
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    convert(json: T, context: ConversionContext): T {
 | 
			
		||||
    convert(json: T, _: ConversionContext): T {
 | 
			
		||||
        if (json === undefined) {
 | 
			
		||||
            return undefined
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
import { ConversionMessage, ConversionMsgLevel } from "./Conversion"
 | 
			
		||||
 | 
			
		||||
export class ConversionContext {
 | 
			
		||||
    private static reported = false
 | 
			
		||||
    /**
 | 
			
		||||
     *  The path within the data structure where we are currently operating
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +11,6 @@ export class ConversionContext {
 | 
			
		|||
     */
 | 
			
		||||
    readonly operation: ReadonlyArray<string>
 | 
			
		||||
    readonly messages: ConversionMessage[]
 | 
			
		||||
 | 
			
		||||
    private _hasErrors: boolean = false
 | 
			
		||||
 | 
			
		||||
    private constructor(
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,6 @@ export class ConversionContext {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private static reported = false
 | 
			
		||||
 | 
			
		||||
    public static construct(path: (string | number)[], operation: string[]) {
 | 
			
		||||
        return new ConversionContext([], [...path], [...operation])
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +75,31 @@ export class ConversionContext {
 | 
			
		|||
        return "\x1b[31m" + s + "\x1b[0m"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Does an inline edit of the messages for which a new path is defined
 | 
			
		||||
     * This is a slight hack
 | 
			
		||||
     * @param rewritePath
 | 
			
		||||
     */
 | 
			
		||||
    public rewriteMessages(
 | 
			
		||||
        rewritePath: (
 | 
			
		||||
            p: ReadonlyArray<number | string>
 | 
			
		||||
        ) => undefined | ReadonlyArray<number | string>
 | 
			
		||||
    ): void {
 | 
			
		||||
        for (let i = 0; i < this.messages.length; i++) {
 | 
			
		||||
            const m = this.messages[i]
 | 
			
		||||
            const newPath = rewritePath(m.context.path)
 | 
			
		||||
            if (!newPath) {
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
            const rewrittenContext = new ConversionContext(
 | 
			
		||||
                this.messages,
 | 
			
		||||
                newPath,
 | 
			
		||||
                m.context.operation
 | 
			
		||||
            )
 | 
			
		||||
            this.messages[i] = <ConversionMessage>{ ...m, context: rewrittenContext }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enter(key: string | number | (string | number)[]) {
 | 
			
		||||
        if (!Array.isArray(key)) {
 | 
			
		||||
            if (typeof key === "number" && key < 0) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
 | 
			
		|||
        this._includeClosedNotesDays = includeClosedNotesDays
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    convert(layerJson: LayerConfigJson, context: ConversionContext): LayerConfigJson {
 | 
			
		||||
    convert(layerJson: LayerConfigJson, _: ConversionContext): LayerConfigJson {
 | 
			
		||||
        const t = Translations.t.importLayer
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -589,7 +589,7 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
 | 
			
		|||
        this._desugaring = desugaring
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson {
 | 
			
		||||
    convert(json: LayerConfigJson, _: ConversionContext): LayerConfigJson {
 | 
			
		||||
        if (this._desugaring.tagRenderings === null) {
 | 
			
		||||
            return json
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1088,7 +1088,7 @@ class AddFavouriteBadges extends DesugaringStep<LayerConfigJson> {
 | 
			
		|||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson {
 | 
			
		||||
    convert(json: LayerConfigJson, _: ConversionContext): LayerConfigJson {
 | 
			
		||||
        if (json.source === "special" || json.source === "special:library") {
 | 
			
		||||
            return json
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1113,7 +1113,7 @@ export class AddRatingBadge extends DesugaringStep<LayerConfigJson> {
 | 
			
		|||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson {
 | 
			
		||||
    convert(json: LayerConfigJson, _: ConversionContext): LayerConfigJson {
 | 
			
		||||
        if (!json.tagRenderings) {
 | 
			
		||||
            return json
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,14 @@
 | 
			
		|||
import { Concat, Conversion, DesugaringContext, DesugaringStep, Each, Fuse, On, Pass, SetDefault } from "./Conversion"
 | 
			
		||||
import {
 | 
			
		||||
    Concat,
 | 
			
		||||
    Conversion,
 | 
			
		||||
    DesugaringContext,
 | 
			
		||||
    DesugaringStep,
 | 
			
		||||
    Each,
 | 
			
		||||
    Fuse,
 | 
			
		||||
    On,
 | 
			
		||||
    Pass,
 | 
			
		||||
    SetDefault,
 | 
			
		||||
} from "./Conversion"
 | 
			
		||||
import { LayoutConfigJson } from "../Json/LayoutConfigJson"
 | 
			
		||||
import { PrepareLayer } from "./PrepareLayer"
 | 
			
		||||
import { LayerConfigJson } from "../Json/LayerConfigJson"
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +29,7 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
 | 
			
		|||
        super(
 | 
			
		||||
            "Converts the identifier of a builtin layer into the actual layer, or converts a 'builtin' syntax with override in the fully expanded form. Note that 'tagRenderings+' will be inserted before 'leftover-questions'",
 | 
			
		||||
            [],
 | 
			
		||||
            "SubstituteLayer",
 | 
			
		||||
            "SubstituteLayer"
 | 
			
		||||
        )
 | 
			
		||||
        this._state = state
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -70,15 +80,16 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
 | 
			
		|||
                (found["tagRenderings"] ?? []).length > 0
 | 
			
		||||
            ) {
 | 
			
		||||
                context.err(
 | 
			
		||||
                    `When overriding a layer, an override is not allowed to override into tagRenderings. Use "+tagRenderings" or "tagRenderings+" instead to prepend or append some questions.`,
 | 
			
		||||
                    `When overriding a layer, an override is not allowed to override into tagRenderings. Use "+tagRenderings" or "tagRenderings+" instead to prepend or append some questions.`
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            try {
 | 
			
		||||
 | 
			
		||||
                const trPlus = json["override"]["tagRenderings+"]
 | 
			
		||||
                if(trPlus){
 | 
			
		||||
                    let index = found.tagRenderings.findIndex(tr => tr["id"] === "leftover-questions")
 | 
			
		||||
                    if(index < 0){
 | 
			
		||||
                if (trPlus) {
 | 
			
		||||
                    let index = found.tagRenderings.findIndex(
 | 
			
		||||
                        (tr) => tr["id"] === "leftover-questions"
 | 
			
		||||
                    )
 | 
			
		||||
                    if (index < 0) {
 | 
			
		||||
                        index = found.tagRenderings.length
 | 
			
		||||
                    }
 | 
			
		||||
                    found.tagRenderings.splice(index, 0, ...trPlus)
 | 
			
		||||
| 
						 | 
				
			
			@ -90,14 +101,18 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
 | 
			
		|||
            } catch (e) {
 | 
			
		||||
                context.err(
 | 
			
		||||
                    `Could not apply an override due to: ${e}.\nThe override is: ${JSON.stringify(
 | 
			
		||||
                        json["override"],
 | 
			
		||||
                    )}`,
 | 
			
		||||
                        json["override"]
 | 
			
		||||
                    )}`
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (json["hideTagRenderingsWithLabels"]) {
 | 
			
		||||
                if (typeof json["hideTagRenderingsWithLabels"] === "string") {
 | 
			
		||||
                    throw "At " + context + ".hideTagRenderingsWithLabels should be a list containing strings, you specified a string"
 | 
			
		||||
                    throw (
 | 
			
		||||
                        "At " +
 | 
			
		||||
                        context +
 | 
			
		||||
                        ".hideTagRenderingsWithLabels should be a list containing strings, you specified a string"
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                const hideLabels: Set<string> = new Set(json["hideTagRenderingsWithLabels"])
 | 
			
		||||
                // These labels caused at least one deletion
 | 
			
		||||
| 
						 | 
				
			
			@ -111,9 +126,9 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
 | 
			
		|||
                            usedLabels.add(labels[forbiddenLabel])
 | 
			
		||||
                            context.info(
 | 
			
		||||
                                "Dropping tagRendering " +
 | 
			
		||||
                                tr["id"] +
 | 
			
		||||
                                " as it has a forbidden label: " +
 | 
			
		||||
                                labels[forbiddenLabel],
 | 
			
		||||
                                    tr["id"] +
 | 
			
		||||
                                    " as it has a forbidden label: " +
 | 
			
		||||
                                    labels[forbiddenLabel]
 | 
			
		||||
                            )
 | 
			
		||||
                            continue
 | 
			
		||||
                        }
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +137,7 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
 | 
			
		|||
                    if (hideLabels.has(tr["id"])) {
 | 
			
		||||
                        usedLabels.add(tr["id"])
 | 
			
		||||
                        context.info(
 | 
			
		||||
                            "Dropping tagRendering " + tr["id"] + " as its id is a forbidden label",
 | 
			
		||||
                            "Dropping tagRendering " + tr["id"] + " as its id is a forbidden label"
 | 
			
		||||
                        )
 | 
			
		||||
                        continue
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -131,10 +146,10 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
 | 
			
		|||
                        usedLabels.add(tr["group"])
 | 
			
		||||
                        context.info(
 | 
			
		||||
                            "Dropping tagRendering " +
 | 
			
		||||
                            tr["id"] +
 | 
			
		||||
                            " as its group `" +
 | 
			
		||||
                            tr["group"] +
 | 
			
		||||
                            "` is a forbidden label",
 | 
			
		||||
                                tr["id"] +
 | 
			
		||||
                                " as its group `" +
 | 
			
		||||
                                tr["group"] +
 | 
			
		||||
                                "` is a forbidden label"
 | 
			
		||||
                        )
 | 
			
		||||
                        continue
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -145,8 +160,8 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
 | 
			
		|||
                if (unused.length > 0) {
 | 
			
		||||
                    context.err(
 | 
			
		||||
                        "This theme specifies that certain tagrenderings have to be removed based on forbidden layers. One or more of these layers did not match any tagRenderings and caused no deletions: " +
 | 
			
		||||
                        unused.join(", ") +
 | 
			
		||||
                        "\n   This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore",
 | 
			
		||||
                            unused.join(", ") +
 | 
			
		||||
                            "\n   This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore"
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                found.tagRenderings = filtered
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +178,7 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
        super(
 | 
			
		||||
            "Adds the default layers, namely: " + Constants.added_by_default.join(", "),
 | 
			
		||||
            ["layers"],
 | 
			
		||||
            "AddDefaultLayers",
 | 
			
		||||
            "AddDefaultLayers"
 | 
			
		||||
        )
 | 
			
		||||
        this._state = state
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +193,7 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
            if (v === undefined) {
 | 
			
		||||
                const msg = `Default layer ${layerName} not found. ${state.sharedLayers.size} layers are available`
 | 
			
		||||
                if (layerName === "favourite") {
 | 
			
		||||
                    context.warn(msg)
 | 
			
		||||
                    //  context.warn(msg)
 | 
			
		||||
                    continue
 | 
			
		||||
                }
 | 
			
		||||
                context.err(msg)
 | 
			
		||||
| 
						 | 
				
			
			@ -187,10 +202,10 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
            if (alreadyLoaded.has(v.id)) {
 | 
			
		||||
                context.warn(
 | 
			
		||||
                    "Layout " +
 | 
			
		||||
                    context +
 | 
			
		||||
                    " already has a layer with name " +
 | 
			
		||||
                    v.id +
 | 
			
		||||
                    "; skipping inclusion of this builtin layer",
 | 
			
		||||
                        context +
 | 
			
		||||
                        " already has a layer with name " +
 | 
			
		||||
                        v.id +
 | 
			
		||||
                        "; skipping inclusion of this builtin layer"
 | 
			
		||||
                )
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -206,14 +221,14 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
        super(
 | 
			
		||||
            "For every layer in the 'layers'-list, create a new layer which'll import notes. (Note that priviliged layers and layers which have a geojson-source set are ignored)",
 | 
			
		||||
            ["layers"],
 | 
			
		||||
            "AddImportLayers",
 | 
			
		||||
            "AddImportLayers"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson {
 | 
			
		||||
        if (!(json.enableNoteImports ?? true)) {
 | 
			
		||||
            context.info(
 | 
			
		||||
                "Not creating a note import layers for theme " + json.id + " as they are disabled",
 | 
			
		||||
                "Not creating a note import layers for theme " + json.id + " as they are disabled"
 | 
			
		||||
            )
 | 
			
		||||
            return json
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +263,7 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
            try {
 | 
			
		||||
                const importLayerResult = creator.convert(
 | 
			
		||||
                    layer,
 | 
			
		||||
                    context.inOperation(this.name).enter(i1),
 | 
			
		||||
                    context.inOperation(this.name).enter(i1)
 | 
			
		||||
                )
 | 
			
		||||
                if (importLayerResult !== undefined) {
 | 
			
		||||
                    json.layers.push(importLayerResult)
 | 
			
		||||
| 
						 | 
				
			
			@ -267,7 +282,7 @@ class AddContextToTranslationsInLayout extends DesugaringStep<LayoutConfigJson>
 | 
			
		|||
        super(
 | 
			
		||||
            "Adds context to translations, including the prefix 'themes:json.id'; this is to make sure terms in an 'overrides' or inline layer are linkable too",
 | 
			
		||||
            ["_context"],
 | 
			
		||||
            "AddContextToTranlationsInLayout",
 | 
			
		||||
            "AddContextToTranlationsInLayout"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -282,11 +297,11 @@ class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
        super(
 | 
			
		||||
            "Applies 'overrideAll' onto every 'layer'. The 'overrideAll'-field is removed afterwards",
 | 
			
		||||
            ["overrideAll", "layers"],
 | 
			
		||||
            "ApplyOverrideAll",
 | 
			
		||||
            "ApplyOverrideAll"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson {
 | 
			
		||||
    convert(json: LayoutConfigJson, _: ConversionContext): LayoutConfigJson {
 | 
			
		||||
        const overrideAll = json.overrideAll
 | 
			
		||||
        if (overrideAll === undefined) {
 | 
			
		||||
            return json
 | 
			
		||||
| 
						 | 
				
			
			@ -310,8 +325,9 @@ class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
                if (!layer.tagRenderings) {
 | 
			
		||||
                    layer.tagRenderings = tagRenderingsPlus
 | 
			
		||||
                } else {
 | 
			
		||||
 | 
			
		||||
                    let index = layer.tagRenderings.findIndex(tr => tr["id"] === "leftover-questions")
 | 
			
		||||
                    let index = layer.tagRenderings.findIndex(
 | 
			
		||||
                        (tr) => tr["id"] === "leftover-questions"
 | 
			
		||||
                    )
 | 
			
		||||
                    if (index < 0) {
 | 
			
		||||
                        index = layer.tagRenderings.length - 1
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -338,7 +354,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
            Some layers (e.g. \`all_buildings_and_walls\' or \'streets_with_a_name\') are invisible, so by default, \'force_load\' is set too.
 | 
			
		||||
            `,
 | 
			
		||||
            ["layers"],
 | 
			
		||||
            "AddDependencyLayersToTheme",
 | 
			
		||||
            "AddDependencyLayersToTheme"
 | 
			
		||||
        )
 | 
			
		||||
        this._state = state
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -346,7 +362,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
    private static CalculateDependencies(
 | 
			
		||||
        alreadyLoaded: LayerConfigJson[],
 | 
			
		||||
        allKnownLayers: Map<string, LayerConfigJson>,
 | 
			
		||||
        themeId: string,
 | 
			
		||||
        themeId: string
 | 
			
		||||
    ): { config: LayerConfigJson; reason: string }[] {
 | 
			
		||||
        const dependenciesToAdd: { config: LayerConfigJson; reason: string }[] = []
 | 
			
		||||
        const loadedLayerIds: Set<string> = new Set<string>(alreadyLoaded.map((l) => l.id))
 | 
			
		||||
| 
						 | 
				
			
			@ -369,7 +385,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
            for (const layerConfig of alreadyLoaded) {
 | 
			
		||||
                try {
 | 
			
		||||
                    const layerDeps = DependencyCalculator.getLayerDependencies(
 | 
			
		||||
                        new LayerConfig(layerConfig, themeId + "(dependencies)"),
 | 
			
		||||
                        new LayerConfig(layerConfig, themeId + "(dependencies)")
 | 
			
		||||
                    )
 | 
			
		||||
                    dependencies.push(...layerDeps)
 | 
			
		||||
                } catch (e) {
 | 
			
		||||
| 
						 | 
				
			
			@ -406,10 +422,10 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
                if (dep === undefined) {
 | 
			
		||||
                    const message = [
 | 
			
		||||
                        "Loading a dependency failed: layer " +
 | 
			
		||||
                        unmetDependency.neededLayer +
 | 
			
		||||
                        " is not found, neither as layer of " +
 | 
			
		||||
                        themeId +
 | 
			
		||||
                        " nor as builtin layer.",
 | 
			
		||||
                            unmetDependency.neededLayer +
 | 
			
		||||
                            " is not found, neither as layer of " +
 | 
			
		||||
                            themeId +
 | 
			
		||||
                            " nor as builtin layer.",
 | 
			
		||||
                        reason,
 | 
			
		||||
                        "Loaded layers are: " + alreadyLoaded.map((l) => l.id).join(","),
 | 
			
		||||
                    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -425,7 +441,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
                })
 | 
			
		||||
                loadedLayerIds.add(dep.id)
 | 
			
		||||
                unmetDependencies = unmetDependencies.filter(
 | 
			
		||||
                    (d) => d.neededLayer !== unmetDependency.neededLayer,
 | 
			
		||||
                    (d) => d.neededLayer !== unmetDependency.neededLayer
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        } while (unmetDependencies.length > 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -446,14 +462,12 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
        const dependencies = AddDependencyLayersToTheme.CalculateDependencies(
 | 
			
		||||
            layers,
 | 
			
		||||
            allKnownLayers,
 | 
			
		||||
            theme.id,
 | 
			
		||||
            theme.id
 | 
			
		||||
        )
 | 
			
		||||
        for (const dependency of dependencies) {
 | 
			
		||||
        }
 | 
			
		||||
        if (dependencies.length > 0) {
 | 
			
		||||
            for (const dependency of dependencies) {
 | 
			
		||||
                context.info(
 | 
			
		||||
                    "Added " + dependency.config.id + " to the theme. " + dependency.reason,
 | 
			
		||||
                    "Added " + dependency.config.id + " to the theme. " + dependency.reason
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -495,7 +509,7 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson>
 | 
			
		|||
        super(
 | 
			
		||||
            "Generates a warning if a theme uses an unsubstituted layer",
 | 
			
		||||
            ["layers"],
 | 
			
		||||
            "WarnForUnsubstitutedLayersInTheme",
 | 
			
		||||
            "WarnForUnsubstitutedLayersInTheme"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -507,7 +521,7 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson>
 | 
			
		|||
            context
 | 
			
		||||
                .enter("layers")
 | 
			
		||||
                .err(
 | 
			
		||||
                    "No layers are defined. You must define at least one layer to have a valid theme",
 | 
			
		||||
                    "No layers are defined. You must define at least one layer to have a valid theme"
 | 
			
		||||
                )
 | 
			
		||||
            return json
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -531,10 +545,10 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson>
 | 
			
		|||
 | 
			
		||||
            context.warn(
 | 
			
		||||
                "The theme " +
 | 
			
		||||
                json.id +
 | 
			
		||||
                " has an inline layer: " +
 | 
			
		||||
                layer["id"] +
 | 
			
		||||
                ". This is discouraged.",
 | 
			
		||||
                    json.id +
 | 
			
		||||
                    " has an inline layer: " +
 | 
			
		||||
                    layer["id"] +
 | 
			
		||||
                    ". This is discouraged."
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        return json
 | 
			
		||||
| 
						 | 
				
			
			@ -548,7 +562,7 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> {
 | 
			
		|||
        state: DesugaringContext,
 | 
			
		||||
        options?: {
 | 
			
		||||
            skipDefaultLayers: false | boolean
 | 
			
		||||
        },
 | 
			
		||||
        }
 | 
			
		||||
    ) {
 | 
			
		||||
        super(
 | 
			
		||||
            "Fully prepares and expands a theme",
 | 
			
		||||
| 
						 | 
				
			
			@ -569,7 +583,7 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> {
 | 
			
		|||
                ? new Pass("AddDefaultLayers is disabled due to the set flag")
 | 
			
		||||
                : new AddDefaultLayers(state),
 | 
			
		||||
            new AddDependencyLayersToTheme(state),
 | 
			
		||||
            new AddImportLayers(),
 | 
			
		||||
            new AddImportLayers()
 | 
			
		||||
        )
 | 
			
		||||
        this.state = state
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -584,13 +598,13 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> {
 | 
			
		|||
        const needsNodeDatabase = result.layers?.some((l: LayerConfigJson) =>
 | 
			
		||||
            l.tagRenderings?.some((tr) =>
 | 
			
		||||
                ValidationUtils.getSpecialVisualisations(<any>tr)?.some(
 | 
			
		||||
                    (special) => special.needsNodeDatabase,
 | 
			
		||||
                ),
 | 
			
		||||
            ),
 | 
			
		||||
                    (special) => special.needsNodeDatabase
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        if (needsNodeDatabase) {
 | 
			
		||||
            context.info(
 | 
			
		||||
                "Setting 'enableNodeDatabase' as this theme uses a special visualisation which needs to keep track of _all_ nodes",
 | 
			
		||||
                "Setting 'enableNodeDatabase' as this theme uses a special visualisation which needs to keep track of _all_ nodes"
 | 
			
		||||
            )
 | 
			
		||||
            result.enableNodeDatabase = true
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,10 @@ import { And } from "../../../Logic/Tags/And"
 | 
			
		|||
import Translations from "../../../UI/i18n/Translations"
 | 
			
		||||
import FilterConfigJson from "../Json/FilterConfigJson"
 | 
			
		||||
import DeleteConfig from "../DeleteConfig"
 | 
			
		||||
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
 | 
			
		||||
import {
 | 
			
		||||
    MappingConfigJson,
 | 
			
		||||
    QuestionableTagRenderingConfigJson,
 | 
			
		||||
} from "../Json/QuestionableTagRenderingConfigJson"
 | 
			
		||||
import Validators from "../../../UI/InputElement/Validators"
 | 
			
		||||
import TagRenderingConfig from "../TagRenderingConfig"
 | 
			
		||||
import { parse as parse_html } from "node-html-parser"
 | 
			
		||||
| 
						 | 
				
			
			@ -21,9 +24,7 @@ import PresetConfig from "../PresetConfig"
 | 
			
		|||
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
 | 
			
		||||
import { Translatable } from "../Json/Translatable"
 | 
			
		||||
import { ConversionContext } from "./ConversionContext"
 | 
			
		||||
import * as eli from "../../../assets/editor-layer-index.json"
 | 
			
		||||
import { AvailableRasterLayers } from "../../RasterLayers"
 | 
			
		||||
import Back from "../../../assets/svg/Back.svelte"
 | 
			
		||||
import PointRenderingConfigJson from "../Json/PointRenderingConfigJson"
 | 
			
		||||
 | 
			
		||||
class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> {
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +179,7 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
 | 
			
		|||
        if (!json.title) {
 | 
			
		||||
            context.enter("title").err(`The theme ${json.id} does not have a title defined.`)
 | 
			
		||||
        }
 | 
			
		||||
        if(!json.icon){
 | 
			
		||||
        if (!json.icon) {
 | 
			
		||||
            context.enter("icon").err("A theme should have an icon")
 | 
			
		||||
        }
 | 
			
		||||
        if (this._isBuiltin && this._extractImages !== undefined) {
 | 
			
		||||
| 
						 | 
				
			
			@ -848,13 +849,32 @@ class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJson> {
 | 
			
		|||
                CheckTranslation.allowUndefined.convert(json[key], context.enter(key))
 | 
			
		||||
            }
 | 
			
		||||
            for (let i = 0; i < json.mappings?.length ?? 0; i++) {
 | 
			
		||||
                const mapping = json.mappings[i]
 | 
			
		||||
                const mapping: MappingConfigJson = json.mappings[i]
 | 
			
		||||
                CheckTranslation.noUndefined.convert(
 | 
			
		||||
                    mapping.then,
 | 
			
		||||
                    context.enters("mappings", i, "then")
 | 
			
		||||
                )
 | 
			
		||||
                if (!mapping.if) {
 | 
			
		||||
                    context.enters("mappings", i).err("No `if` is defined")
 | 
			
		||||
                    console.log(
 | 
			
		||||
                        "Checking mappings",
 | 
			
		||||
                        i,
 | 
			
		||||
                        "if",
 | 
			
		||||
                        mapping.if,
 | 
			
		||||
                        context.path.join("."),
 | 
			
		||||
                        mapping.then
 | 
			
		||||
                    )
 | 
			
		||||
                    context.enters("mappings", i, "if").err("No `if` is defined")
 | 
			
		||||
                }
 | 
			
		||||
                if (mapping.addExtraTags) {
 | 
			
		||||
                    for (let j = 0; j < mapping.addExtraTags.length; j++) {
 | 
			
		||||
                        if (!mapping.addExtraTags[j]) {
 | 
			
		||||
                            context
 | 
			
		||||
                                .enters("mappings", i, "addExtraTags", j)
 | 
			
		||||
                                .err(
 | 
			
		||||
                                    "Detected a 'null' or 'undefined' value. Either specify a tag or delete this item"
 | 
			
		||||
                                )
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                const en = mapping?.then?.["en"]
 | 
			
		||||
                if (en && this.detectYesOrNo(en)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -981,6 +1001,9 @@ class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJson> {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (context.hasErrors()) {
 | 
			
		||||
            return undefined
 | 
			
		||||
        }
 | 
			
		||||
        return json
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1000,6 +1023,7 @@ export class ValidateTagRenderings extends Fuse<TagRenderingConfigJson> {
 | 
			
		|||
    constructor(layerConfig?: LayerConfigJson, doesImageExist?: DoesImageExist) {
 | 
			
		||||
        super(
 | 
			
		||||
            "Various validation on tagRenderingConfigs",
 | 
			
		||||
            new MiscTagRenderingChecks(),
 | 
			
		||||
            new DetectShadowedMappings(layerConfig),
 | 
			
		||||
            new DetectConflictingAddExtraTags(),
 | 
			
		||||
            // TODO enable   new DetectNonErasedKeysInMappings(),
 | 
			
		||||
| 
						 | 
				
			
			@ -1007,8 +1031,7 @@ export class ValidateTagRenderings extends Fuse<TagRenderingConfigJson> {
 | 
			
		|||
            new On("render", new ValidatePossibleLinks()),
 | 
			
		||||
            new On("question", new ValidatePossibleLinks()),
 | 
			
		||||
            new On("questionHint", new ValidatePossibleLinks()),
 | 
			
		||||
            new On("mappings", new Each(new On("then", new ValidatePossibleLinks()))),
 | 
			
		||||
            new MiscTagRenderingChecks()
 | 
			
		||||
            new On("mappings", new Each(new On("then", new ValidatePossibleLinks())))
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1023,7 +1046,12 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
 | 
			
		|||
    private readonly _studioValidations: boolean
 | 
			
		||||
    private readonly _validatePointRendering = new ValidatePointRendering()
 | 
			
		||||
 | 
			
		||||
    constructor(path: string, isBuiltin, doesImageExist, studioValidations) {
 | 
			
		||||
    constructor(
 | 
			
		||||
        path: string,
 | 
			
		||||
        isBuiltin: boolean,
 | 
			
		||||
        doesImageExist: DoesImageExist,
 | 
			
		||||
        studioValidations: boolean
 | 
			
		||||
    ) {
 | 
			
		||||
        super("Runs various checks against common mistakes for a layer", [], "PrevalidateLayer")
 | 
			
		||||
        this._path = path
 | 
			
		||||
        this._isBuiltin = isBuiltin
 | 
			
		||||
| 
						 | 
				
			
			@ -1111,7 +1139,9 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
 | 
			
		|||
            context.enter("pointRendering").err("There are no pointRenderings at all...")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        json.pointRendering?.forEach((pr,i) => this._validatePointRendering.convert(pr, context.enters("pointeRendering", i)))
 | 
			
		||||
        json.pointRendering?.forEach((pr, i) =>
 | 
			
		||||
            this._validatePointRendering.convert(pr, context.enters("pointeRendering", i))
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if (json["mapRendering"]) {
 | 
			
		||||
            context.enter("mapRendering").err("This layer has a legacy 'mapRendering'")
 | 
			
		||||
| 
						 | 
				
			
			@ -1138,7 +1168,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        if (json.tagRenderings !== undefined && json.tagRenderings.length > 0) {
 | 
			
		||||
            new On("tagRendering", new Each(new ValidateTagRenderings(json)))
 | 
			
		||||
            new On("tagRenderings", new Each(new ValidateTagRenderings(json)))
 | 
			
		||||
            if (json.title === undefined && json.source !== "special:library") {
 | 
			
		||||
                context
 | 
			
		||||
                    .enter("title")
 | 
			
		||||
| 
						 | 
				
			
			@ -1428,29 +1458,33 @@ class ValidatePointRendering extends DesugaringStep<PointRenderingConfigJson> {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        if (json["markers"]) {
 | 
			
		||||
            context.enter("markers").err(`Detected a field 'markerS' in pointRendering. It is written as a singular case`)
 | 
			
		||||
            context
 | 
			
		||||
                .enter("markers")
 | 
			
		||||
                .err(
 | 
			
		||||
                    `Detected a field 'markerS' in pointRendering. It is written as a singular case`
 | 
			
		||||
                )
 | 
			
		||||
        }
 | 
			
		||||
        if (json.marker && !Array.isArray(json.marker)) {
 | 
			
		||||
            context.enter("marker").err(
 | 
			
		||||
                "The marker in a pointRendering should be an array"
 | 
			
		||||
            )
 | 
			
		||||
            context.enter("marker").err("The marker in a pointRendering should be an array")
 | 
			
		||||
        }
 | 
			
		||||
        if (json.location.length == 0) {
 | 
			
		||||
            context.enter("location").err (
 | 
			
		||||
                "A pointRendering should have at least one 'location' to defined where it should be rendered. "
 | 
			
		||||
            )
 | 
			
		||||
            context
 | 
			
		||||
                .enter("location")
 | 
			
		||||
                .err(
 | 
			
		||||
                    "A pointRendering should have at least one 'location' to defined where it should be rendered. "
 | 
			
		||||
                )
 | 
			
		||||
        }
 | 
			
		||||
        return json
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class ValidateLayer extends Conversion<
 | 
			
		||||
    LayerConfigJson,
 | 
			
		||||
    { parsed: LayerConfig; raw: LayerConfigJson }
 | 
			
		||||
> {
 | 
			
		||||
    private readonly _skipDefaultLayers: boolean
 | 
			
		||||
    private readonly _prevalidation: PrevalidateLayer
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        path: string,
 | 
			
		||||
        isBuiltin: boolean,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,6 @@ import { Utils } from "../../../Utils"
 | 
			
		|||
import SpecialVisualizations from "../../../UI/SpecialVisualizations"
 | 
			
		||||
import { RenderingSpecification, SpecialVisualization } from "../../../UI/SpecialVisualization"
 | 
			
		||||
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
 | 
			
		||||
import { render } from "sass"
 | 
			
		||||
 | 
			
		||||
export default class ValidationUtils {
 | 
			
		||||
    public static getAllSpecialVisualisations(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue