chore: automated housekeeping...

This commit is contained in:
Pieter Vander Vennet 2025-03-17 02:54:12 +01:00
parent 92352ed274
commit 535e36a006
68 changed files with 2734 additions and 382 deletions

View file

@ -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 { ThemeConfigJson } from "../Json/ThemeConfigJson"
import { PrepareLayer, RewriteSpecial } from "./PrepareLayer"
import { LayerConfigJson } from "../Json/LayerConfigJson"
@ -30,7 +40,7 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
const knownLayers = Array.from(state.sharedLayers.keys())
const withDistance: [string, number][] = knownLayers.map((lname) => [
lname,
Utils.levenshteinDistance(name, lname)
Utils.levenshteinDistance(name, lname),
])
withDistance.sort((a, b) => a[1] - b[1])
const ids = withDistance.map((n) => n[0])
@ -120,9 +130,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
}
@ -140,10 +150,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
}
@ -154,8 +164,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
@ -195,10 +205,10 @@ export class AddDefaultLayers extends DesugaringStep<ThemeConfigJson> {
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
}
@ -342,10 +352,10 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
.enters("layer dependency")
.err(
"Layer " +
dependency.neededLayer +
" is loaded because " +
dependency.reason +
"; so it must specify a `snapName`. This is used in the sentence `move this point to snap it to {snapName}`"
dependency.neededLayer +
" is loaded because " +
dependency.reason +
"; so it must specify a `snapName`. This is used in the sentence `move this point to snap it to {snapName}`"
)
}
}
@ -370,12 +380,12 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
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(",")
"Loaded layers are: " + alreadyLoaded.map((l) => l.id).join(","),
]
throw message.join("\n\t")
}
@ -385,7 +395,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
dep.description = reason
dependenciesToAdd.unshift({
config: dep,
reason
reason,
})
loadedLayerIds.add(dep.id)
unmetDependencies = unmetDependencies.filter(
@ -430,7 +440,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
return {
...theme,
layers: layers
layers: layers,
}
}
}
@ -500,10 +510,10 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<ThemeConfigJson>
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
@ -545,12 +555,12 @@ class PostvalidateTheme extends DesugaringStep<ThemeConfigJson> {
if (minZoomAll < layer.minzoom) {
context.err(
"There are multiple layers based on " +
basedOn +
". The layer with id " +
layer.id +
" has a minzoom of " +
layer.minzoom +
", and has a name set. Another similar layer has a lower minzoom. As such, the layer selection might show 'zoom in to see features' even though some of the features are already visible. Set `\"name\": null` for this layer and eventually remove the 'name':null for the other layer."
basedOn +
". The layer with id " +
layer.id +
" has a minzoom of " +
layer.minzoom +
", and has a name set. Another similar layer has a lower minzoom. As such, the layer selection might show 'zoom in to see features' even though some of the features are already visible. Set `\"name\": null` for this layer and eventually remove the 'name':null for the other layer."
)
}
}
@ -576,11 +586,11 @@ class PostvalidateTheme extends DesugaringStep<ThemeConfigJson> {
.enters("layers", config.id, "filter", "sameAs")
.err(
"The layer " +
config.id +
" follows the filter state of layer " +
sameAs +
", but no layer with this name was found.\n\tDid you perhaps mean one of: " +
closeLayers.slice(0, 3).join(", ")
config.id +
" follows the filter state of layer " +
sameAs +
", but no layer with this name was found.\n\tDid you perhaps mean one of: " +
closeLayers.slice(0, 3).join(", ")
)
}
}
@ -608,12 +618,16 @@ export class PrepareTheme extends Fuse<ThemeConfigJson> {
new SetDefault("socialImage", "assets/SocialImage.png", true),
// We expand all tagrenderings first...
new On("layers", new Each(new PrepareLayer(state))),
new On("popup", new Each(
new Fuse("Prepare popups",
new On("body", new Each(new RewriteSpecial())),
new On("title", new RewriteSpecial())
new On(
"popup",
new Each(
new Fuse(
"Prepare popups",
new On("body", new Each(new RewriteSpecial())),
new On("title", new RewriteSpecial())
)
)
)),
),
// Then we apply the override all. We must first expand everything in case that we override something in an expanded tag
// Note that it'll cheat with tagRenderings+

View file

@ -483,11 +483,11 @@ export interface ThemeConfigJson {
*/
dismissible?: boolean
condition?: TagConfigJson
title: TagRenderingConfigJson,
body: TagRenderingConfigJson[],
title: TagRenderingConfigJson
body: TagRenderingConfigJson[]
/**
* id of the popup, mostly to keep the translations in check
*/
id: string,
id: string
}[]
}

View file

@ -5,7 +5,10 @@ import { TagUtils } from "../../Logic/Tags/TagUtils"
import { And } from "../../Logic/Tags/And"
import { Utils } from "../../Utils"
import { Tag } from "../../Logic/Tags/Tag"
import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "./Json/QuestionableTagRenderingConfigJson"
import {
MappingConfigJson,
QuestionableTagRenderingConfigJson,
} from "./Json/QuestionableTagRenderingConfigJson"
import Validators, { ValidatorType } from "../../UI/InputElement/Validators"
import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson"
import { RegexTag } from "../../Logic/Tags/RegexTag"
@ -223,7 +226,7 @@ export default class TagRenderingConfig {
inline: json.freeform.inline ?? false,
default: json.freeform.default,
postfixDistinguished: json.freeform.postfixDistinguished?.trim(),
args: json.freeform.helperArgs
args: json.freeform.helperArgs,
}
if (json.freeform["extraTags"] !== undefined) {
throw `Freeform.extraTags is defined. This should probably be 'freeform.addExtraTag' (at ${context})`
@ -447,7 +450,7 @@ export default class TagRenderingConfig {
iconClass,
addExtraTags,
searchTerms: mapping.searchTerms,
priorityIf: prioritySearch
priorityIf: prioritySearch,
}
if (isQuestionable) {
if (hideInAnswer !== true && mp.if !== undefined && !mp.if.isUsableAsAnswer()) {
@ -554,7 +557,7 @@ export default class TagRenderingConfig {
then: new TypedTranslation<object>(
this.render.replace("{" + this.freeform.key + "}", leftover).translations,
this.render.context
)
),
})
}
}
@ -607,7 +610,7 @@ export default class TagRenderingConfig {
return {
then: this.render.PartialSubs({ [this.freeform.key]: v.trim() }),
icon: this.renderIcon,
iconClass: this.renderIconClass
iconClass: this.renderIconClass,
}
}
}
@ -662,7 +665,7 @@ export default class TagRenderingConfig {
key: commonKey,
values: Utils.NoNull(
values.map((arr) => arr.filter((item) => item.k === commonKey)[0]?.v)
)
),
}
}
@ -677,7 +680,7 @@ export default class TagRenderingConfig {
return {
key,
type: this.freeform.type,
values
values,
}
} catch (e) {
console.error("Could not create FreeformValues for tagrendering", this.id)
@ -753,7 +756,7 @@ export default class TagRenderingConfig {
const allValues = v.split(";").map((s) => s.trim())
const perPostfix: Record<string, string> = {}
for (const value of allValues) {
const [v, postfix] = value.split("/").map(s => s.trim())
const [v, postfix] = value.split("/").map((s) => s.trim())
perPostfix[postfix ?? pf] = v.trim()
}
if (freeformValue === "" || freeformValue === undefined) {
@ -790,7 +793,7 @@ export default class TagRenderingConfig {
// Either no mappings, or this is a radio-button selected freeform value
const tag = [
new Tag(this.freeform.key, freeformValue),
...(this.freeform.addExtraTags ?? [])
...(this.freeform.addExtraTags ?? []),
]
const newProperties = new And(tag).applyOn(currentProperties)
if (this.invalidValues?.matchesProperties(newProperties)) {
@ -814,7 +817,7 @@ export default class TagRenderingConfig {
selectedMappings.push(
new And([
new Tag(this.freeform.key, freeformValue),
...(this.freeform.addExtraTags ?? [])
...(this.freeform.addExtraTags ?? []),
])
)
}
@ -847,12 +850,12 @@ export default class TagRenderingConfig {
if (useFreeform) {
return [
new Tag(this.freeform.key, freeformValue),
...(this.freeform.addExtraTags ?? [])
...(this.freeform.addExtraTags ?? []),
]
} else if (singleSelectedMapping !== undefined) {
return [
this.mappings[singleSelectedMapping].if,
...(this.mappings[singleSelectedMapping].addExtraTags ?? [])
...(this.mappings[singleSelectedMapping].addExtraTags ?? []),
]
} else {
console.error("TagRenderingConfig.ConstructSpecification has a weird fallback for", {
@ -860,7 +863,7 @@ export default class TagRenderingConfig {
singleSelectedMapping,
multiSelectedMapping,
currentProperties,
useFreeform
useFreeform,
})
return undefined
}
@ -889,11 +892,11 @@ export default class TagRenderingConfig {
}
const msgs: string[] = [
icon +
" " +
"*" +
m.then.textFor(lang) +
"* is shown if with " +
m.if.asHumanString(true, false, {})
" " +
"*" +
m.then.textFor(lang) +
"* is shown if with " +
m.if.asHumanString(true, false, {}),
]
if (m.hideInAnswer === true) {
@ -902,7 +905,7 @@ export default class TagRenderingConfig {
if (m.ifnot !== undefined) {
msgs.push(
"Unselecting this answer will add " +
m.ifnot.asHumanString(true, false, {})
m.ifnot.asHumanString(true, false, {})
)
}
return msgs.join(". ")
@ -926,7 +929,7 @@ export default class TagRenderingConfig {
if (this.labels?.length > 0) {
labels = [
"This tagrendering has labels ",
...this.labels.map((label) => "`" + label + "`")
...this.labels.map((label) => "`" + label + "`"),
].join("\n")
}
@ -939,7 +942,7 @@ export default class TagRenderingConfig {
freeform,
mappings,
condition,
labels
labels,
].join("\n")
}
@ -983,7 +986,7 @@ export default class TagRenderingConfig {
if (part.indexOf("/") < 0) {
continue
}
const [v, denom] = part.split("/").map(s => s.trim())
const [v, denom] = part.split("/").map((s) => s.trim())
if (denom === distinguish) {
return v
}
@ -1043,19 +1046,21 @@ export default class TagRenderingConfig {
/**
* Gives all the tags that should be applied to "reset" the freeform key to an "unknown" state
*/
public markUnknown(layer: LayerConfig, currentProperties: Record<string, string>): UploadableTag[] {
public markUnknown(
layer: LayerConfig,
currentProperties: Record<string, string>
): UploadableTag[] {
if (this.freeform?.postfixDistinguished) {
const v = currentProperties[this.freeform.key] ?? ""
const allValues = v.split(";").filter(
part => part.split("/")[1]?.trim() !== this.freeform.postfixDistinguished
)
const allValues = v
.split(";")
.filter((part) => part.split("/")[1]?.trim() !== this.freeform.postfixDistinguished)
return [new Tag(this.freeform.key, allValues.join(";"))]
}
const keys = this.removeToSetUnknown(layer, currentProperties)
return keys?.map(k => new Tag(k, ""))
return keys?.map((k) => new Tag(k, ""))
}
}
@ -1095,7 +1100,7 @@ export class TagRenderingConfigUtils {
clone.mappings?.map((m) => {
const mapping = {
...m,
priorityIf: m.priorityIf ?? TagUtils.Tag("id~*")
priorityIf: m.priorityIf ?? TagUtils.Tag("id~*"),
}
if (m.if.usedKeys().indexOf("nobrand") < 0) {
// Erase 'nobrand=yes', unless this option explicitly sets it

View file

@ -98,10 +98,10 @@ export default class ThemeConfig implements ThemeInformation {
public readonly enableCache: boolean
public readonly popups: Readonly<{
id: string,
dismissible?: boolean,
condition: TagsFilter,
title: TagRenderingConfig,
id: string
dismissible?: boolean
condition: TagsFilter
title: TagRenderingConfig
body: TagRenderingConfig[]
}>[]
@ -205,7 +205,7 @@ export default class ThemeConfig implements ThemeInformation {
icon: "./assets/svg/pop-out.svg",
href: "https://{basepath}/{theme}.html?lat={lat}&lon={lon}&z={zoom}&language={language}",
newTab: true,
requirements: ["iframe", "no-welcome-message"]
requirements: ["iframe", "no-welcome-message"],
},
context + ".extraLink"
)
@ -213,7 +213,7 @@ export default class ThemeConfig implements ThemeInformation {
this.popups = (json.popup ?? []).map((p, i) => {
const ctx = context + ".popup." + i
if (!p.id) {
throw (ctx + ": an id is required")
throw ctx + ": an id is required"
}
const body: TagRenderingConfigJson[] = Array.isArray(p.body) ? p.body : [p.body]
return {
@ -221,7 +221,7 @@ export default class ThemeConfig implements ThemeInformation {
dismissible: p.dismissible ?? false,
condition: TagUtils.Tag(p.condition),
title: new TagRenderingConfig(p.title, ctx + ".title"),
body: body.map((body, i) => new TagRenderingConfig(body, ctx + ".body." + i))
body: body.map((body, i) => new TagRenderingConfig(body, ctx + ".body." + i)),
}
})
@ -378,7 +378,7 @@ export default class ThemeConfig implements ThemeInformation {
// The 'favourite'-layer contains pretty much all images as it bundles all layers, so we exclude it
const jsonNoFavourites = {
...json,
layers: json.layers.filter((l) => l["id"] !== "favourite")
layers: json.layers.filter((l) => l["id"] !== "favourite"),
}
const usedImages = jsonNoFavourites._usedImages
usedImages.sort()