chore: automated housekeeping...

This commit is contained in:
Pieter Vander Vennet 2024-10-19 14:44:55 +02:00
parent c9ce29f206
commit 40e894df8b
294 changed files with 14209 additions and 4192 deletions

View file

@ -10,7 +10,10 @@ import {
SetDefault,
} from "./Conversion"
import { LayerConfigJson } from "../Json/LayerConfigJson"
import { MinimalTagRenderingConfigJson, TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
import {
MinimalTagRenderingConfigJson,
TagRenderingConfigJson,
} from "../Json/TagRenderingConfigJson"
import { Utils } from "../../../Utils"
import RewritableConfigJson from "../Json/RewritableConfigJson"
import SpecialVisualizations from "../../../UI/SpecialVisualizations"
@ -31,23 +34,26 @@ import { ConversionContext } from "./ConversionContext"
import { ExpandRewrite } from "./ExpandRewrite"
import { TagUtils } from "../../../Logic/Tags/TagUtils"
class AddFiltersFromTagRenderings extends DesugaringStep<LayerConfigJson> {
constructor() {
super("Inspects all the tagRenderings. If some tagRenderings have the `filter` attribute set, introduce those filters. This step might introduce shorthand filter names, thus 'ExpandFilter' should be run afterwards. Can be disabled with \"#filter\":\"no-auto\"", ["filter"], "AddFiltersFromTagRenderings")
super(
'Inspects all the tagRenderings. If some tagRenderings have the `filter` attribute set, introduce those filters. This step might introduce shorthand filter names, thus \'ExpandFilter\' should be run afterwards. Can be disabled with "#filter":"no-auto"',
["filter"],
"AddFiltersFromTagRenderings"
)
}
convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson {
const noAutoFilters = json["#filter"] === "no-auto"
if(noAutoFilters){
if (noAutoFilters) {
return json
}
if(json.filter?.["sameAs"]){
if (json.filter?.["sameAs"]) {
return json
}
const filters: (FilterConfigJson | string)[] = [...<any>json.filter ?? []]
const filters: (FilterConfigJson | string)[] = [...(<any>json.filter ?? [])]
function filterExists(filterName: string): boolean {
return filters.some((existing) => {
@ -59,8 +65,6 @@ class AddFiltersFromTagRenderings extends DesugaringStep<LayerConfigJson> {
})
}
for (let i = 0; i < json.tagRenderings?.length; i++) {
const tagRendering = <TagRenderingConfigJson>json.tagRenderings[i]
if (!tagRendering?.filter) {
@ -70,7 +74,12 @@ class AddFiltersFromTagRenderings extends DesugaringStep<LayerConfigJson> {
if (filterExists(tagRendering["id"])) {
continue
}
filters.push(ExpandFilter.buildFilterFromTagRendering(tagRendering, context.enters("tagRenderings", i, "filter")))
filters.push(
ExpandFilter.buildFilterFromTagRendering(
tagRendering,
context.enters("tagRenderings", i, "filter")
)
)
continue
}
for (const filterName of tagRendering.filter ?? []) {
@ -89,7 +98,7 @@ class AddFiltersFromTagRenderings extends DesugaringStep<LayerConfigJson> {
}
}
if(filters.length === 0){
if (filters.length === 0) {
return json
}
@ -102,10 +111,12 @@ class ExpandFilter extends DesugaringStep<LayerConfigJson> {
constructor(state: DesugaringContext) {
super(
["Expands filters: replaces a shorthand by the value found in 'filters.json'.",
"If the string is formatted 'layername.filtername, it will be looked up into that layer instead."].join(" "),
[
"Expands filters: replaces a shorthand by the value found in 'filters.json'.",
"If the string is formatted 'layername.filtername, it will be looked up into that layer instead.",
].join(" "),
["filter"],
"ExpandFilter",
"ExpandFilter"
)
this._state = state
}
@ -118,10 +129,13 @@ class ExpandFilter extends DesugaringStep<LayerConfigJson> {
return filters
}
public static buildFilterFromTagRendering(tr: TagRenderingConfigJson, context: ConversionContext): FilterConfigJson {
public static buildFilterFromTagRendering(
tr: TagRenderingConfigJson,
context: ConversionContext
): FilterConfigJson {
if (!(tr.mappings?.length >= 1)) {
context.err(
"Found a matching tagRendering to base a filter on, but this tagRendering does not contain any mappings",
"Found a matching tagRendering to base a filter on, but this tagRendering does not contain any mappings"
)
}
const options = (<QuestionableTagRenderingConfigJson>tr).mappings.map((mapping) => {
@ -131,12 +145,13 @@ class ExpandFilter extends DesugaringStep<LayerConfigJson> {
emoji = icon
icon = undefined
}
return (<FilterConfigOptionJson>{
return <FilterConfigOptionJson>{
question: mapping.then,
osmTags: mapping.if,
searchTerms: mapping.searchTerms,
icon, emoji,
})
icon,
emoji,
}
})
// Add default option
options.unshift({
@ -144,10 +159,10 @@ class ExpandFilter extends DesugaringStep<LayerConfigJson> {
osmTags: undefined,
searchTerms: undefined,
})
return ({
return {
id: tr["id"],
options,
})
}
}
convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson {
@ -159,11 +174,9 @@ class ExpandFilter extends DesugaringStep<LayerConfigJson> {
return json // Nothing to change here
}
const newFilters: FilterConfigJson[] = []
const filters = <(FilterConfigJson | string)[]>json.filter
/**
* Create filters based on builtin filters or create them based on the tagRendering
*/
@ -181,7 +194,10 @@ class ExpandFilter extends DesugaringStep<LayerConfigJson> {
json.tagRenderings.find((tr) => !!tr && tr["id"] === filter)
)
if (matchingTr) {
const filter = ExpandFilter.buildFilterFromTagRendering(matchingTr, context.enters("filter", i))
const filter = ExpandFilter.buildFilterFromTagRendering(
matchingTr,
context.enters("filter", i)
)
newFilters.push(filter)
continue
}
@ -194,7 +210,7 @@ class ExpandFilter extends DesugaringStep<LayerConfigJson> {
const split = filter.split(".")
if (split.length > 2) {
context.err(
"invalid filter name: " + filter + ", expected `layername.filterid`",
"invalid filter name: " + filter + ", expected `layername.filterid`"
)
}
const layer = this._state.sharedLayers.get(split[0])
@ -203,7 +219,7 @@ class ExpandFilter extends DesugaringStep<LayerConfigJson> {
}
const expectedId = split[1]
const expandedFilter = (<(FilterConfigJson | string)[]>layer.filter).find(
(f) => typeof f !== "string" && f.id === expectedId,
(f) => typeof f !== "string" && f.id === expectedId
)
if (expandedFilter === undefined) {
context.err("Did not find filter with name " + filter)
@ -218,15 +234,15 @@ class ExpandFilter extends DesugaringStep<LayerConfigJson> {
const suggestions = Utils.sortedByLevenshteinDistance(
filter,
Array.from(ExpandFilter.predefinedFilters.keys()),
(t) => t,
(t) => t
)
context
.enter(filter)
.err(
"While searching for predefined filter " +
filter +
": this filter is not found. Perhaps you meant one of: " +
suggestions,
filter +
": this filter is not found. Perhaps you meant one of: " +
suggestions
)
}
newFilters.push(found)
@ -239,9 +255,9 @@ class ExpandTagRendering extends Conversion<
| string
| TagRenderingConfigJson
| {
builtin: string | string[]
override: any
},
builtin: string | string[]
override: any
},
TagRenderingConfigJson[]
> {
private readonly _state: DesugaringContext
@ -263,12 +279,12 @@ class ExpandTagRendering extends Conversion<
noHardcodedStrings?: false | boolean
// If set, a question will be added to the 'sharedTagRenderings'. Should only be used for 'questions.json'
addToContext?: false | boolean
},
}
) {
super(
"Converts a tagRenderingSpec into the full tagRendering, e.g. by substituting the tagRendering by the shared-question and reusing the builtins",
[],
"ExpandTagRendering",
"ExpandTagRendering"
)
this._state = state
this._self = self
@ -288,7 +304,7 @@ class ExpandTagRendering extends Conversion<
public convert(
spec: string | any,
ctx: ConversionContext,
ctx: ConversionContext
): QuestionableTagRenderingConfigJson[] {
const trs = this.convertOnce(spec, ctx)
@ -401,8 +417,8 @@ class ExpandTagRendering extends Conversion<
found,
ConversionContext.construct(
[layer.id, "tagRenderings", found["id"]],
["AddContextToTranslations"],
),
["AddContextToTranslations"]
)
)
matchingTrs[i] = found
}
@ -430,17 +446,17 @@ class ExpandTagRendering extends Conversion<
ctx.warn(
`A literal rendering was detected: ${tr}
Did you perhaps forgot to add a layer name as 'layername.${tr}'? ` +
Array.from(state.sharedLayers.keys()).join(", "),
Array.from(state.sharedLayers.keys()).join(", ")
)
}
if (this._options?.noHardcodedStrings && this._state?.sharedLayers?.size > 0) {
ctx.err(
"Detected an invocation to a builtin tagRendering, but this tagrendering was not found: " +
tr +
" \n Did you perhaps forget to add the layer as prefix, such as `icons." +
tr +
"`? ",
tr +
" \n Did you perhaps forget to add the layer as prefix, such as `icons." +
tr +
"`? "
)
}
@ -475,9 +491,9 @@ class ExpandTagRendering extends Conversion<
}
ctx.err(
"An object calling a builtin can only have keys `builtin` or `override`, but a key with name `" +
key +
"` was found. This won't be picked up! The full object is: " +
JSON.stringify(tr),
key +
"` was found. This won't be picked up! The full object is: " +
JSON.stringify(tr)
)
}
@ -496,39 +512,39 @@ class ExpandTagRendering extends Conversion<
const candidates = Utils.sortedByLevenshteinDistance(
layerName,
Array.from(state.sharedLayers.keys()),
(s) => s,
(s) => s
)
if (state.sharedLayers.size === 0) {
ctx.warn(
"BOOTSTRAPPING. Rerun generate layeroverview. While reusing tagrendering: " +
name +
": layer " +
layerName +
" not found for now, but ignoring as this is a bootstrapping run. ",
name +
": layer " +
layerName +
" not found for now, but ignoring as this is a bootstrapping run. "
)
} else {
ctx.err(
": While reusing tagrendering: " +
name +
": layer " +
layerName +
" not found. Maybe you meant one of " +
candidates.slice(0, 3).join(", "),
name +
": layer " +
layerName +
" not found. Maybe you meant one of " +
candidates.slice(0, 3).join(", ")
)
}
continue
}
candidates = Utils.NoNull(layer.tagRenderings.map((tr) => tr["id"])).map(
(id) => layerName + "." + id,
(id) => layerName + "." + id
)
}
candidates = Utils.sortedByLevenshteinDistance(name, candidates, (i) => i)
ctx.err(
"The tagRendering with identifier " +
name +
" was not found.\n\tDid you mean one of " +
candidates.join(", ") +
"?\n(Hint: did you add a new label and are you trying to use this label at the same time? Run 'reset:layeroverview' first",
name +
" was not found.\n\tDid you mean one of " +
candidates.join(", ") +
"?\n(Hint: did you add a new label and are you trying to use this label at the same time? Run 'reset:layeroverview' first"
)
continue
}
@ -553,13 +569,13 @@ class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> {
super(
"If no 'inline' is set on the freeform key, it will be automatically added. If no special renderings are used, it'll be set to true",
["freeform.inline"],
"DetectInline",
"DetectInline"
)
}
convert(
json: QuestionableTagRenderingConfigJson,
context: ConversionContext,
context: ConversionContext
): QuestionableTagRenderingConfigJson {
if (json.freeform === undefined) {
return json
@ -582,7 +598,7 @@ class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> {
if (json.freeform.inline === true) {
context.err(
"'inline' is set, but the rendering contains a special visualisation...\n " +
spec[key],
spec[key]
)
}
json = JSON.parse(JSON.stringify(json))
@ -596,17 +612,18 @@ class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> {
return json
}
if(json.render === undefined){
if (json.render === undefined) {
context.err("No 'render' defined")
return json
}
if(!Object.values(json?.render)?.some(render => render !== "{"+json.freeform.key+"}")){
if (
!Object.values(json?.render)?.some((render) => render !== "{" + json.freeform.key + "}")
) {
// We only render the current value, without anything more. Not worth inlining
return json
}
json.freeform.inline ??= true
return json
}
@ -617,7 +634,7 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
super(
"Adds a 'questions'-object if no question element is added yet",
["tagRenderings"],
"AddQuestionBox",
"AddQuestionBox"
)
}
@ -641,18 +658,18 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
json.tagRenderings = [...json.tagRenderings]
const allSpecials: Exclude<RenderingSpecification, string>[] = <any>(
ValidationUtils.getAllSpecialVisualisations(
<QuestionableTagRenderingConfigJson[]>json.tagRenderings,
<QuestionableTagRenderingConfigJson[]>json.tagRenderings
).filter((spec) => typeof spec !== "string")
)
const questionSpecials = allSpecials.filter((sp) => sp.func.funcName === "questions")
const noLabels = questionSpecials.filter(
(sp) => sp.args.length === 0 || sp.args[0].trim() === "",
(sp) => sp.args.length === 0 || sp.args[0].trim() === ""
)
if (noLabels.length > 1) {
context.err(
"Multiple 'questions'-visualisations found which would show _all_ questions. Don't do this",
"Multiple 'questions'-visualisations found which would show _all_ questions. Don't do this"
)
}
@ -660,9 +677,9 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
const allLabels = new Set(
[].concat(
...json.tagRenderings.map(
(tr) => (<QuestionableTagRenderingConfigJson>tr).labels ?? [],
),
),
(tr) => (<QuestionableTagRenderingConfigJson>tr).labels ?? []
)
)
)
const seen: Set<string> = new Set()
for (const questionSpecial of questionSpecials) {
@ -680,20 +697,20 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
if (blacklisted?.length > 0 && used?.length > 0) {
context.err(
"The {questions()}-special rendering only supports either a blacklist OR a whitelist, but not both." +
"\n Whitelisted: " +
used.join(", ") +
"\n Blacklisted: " +
blacklisted.join(", "),
"\n Whitelisted: " +
used.join(", ") +
"\n Blacklisted: " +
blacklisted.join(", ")
)
}
for (const usedLabel of used) {
if (!allLabels.has(usedLabel)) {
context.err(
"This layers specifies a special question element for label `" +
usedLabel +
"`, but this label doesn't exist.\n" +
" Available labels are " +
Array.from(allLabels).join(", "),
usedLabel +
"`, but this label doesn't exist.\n" +
" Available labels are " +
Array.from(allLabels).join(", ")
)
}
seen.add(usedLabel)
@ -726,7 +743,7 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
super(
"Add some editing elements, such as the delete button or the move button if they are configured. These used to be handled by the feature info box, but this has been replaced by special visualisation elements",
[],
"AddEditingElements",
"AddEditingElements"
)
this._desugaring = desugaring
this.builtinQuestions = Array.from(this._desugaring.tagRenderings?.values() ?? [])
@ -756,13 +773,13 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
json.tagRenderings = [...(json.tagRenderings ?? [])]
const allIds = new Set<string>(json.tagRenderings.map((tr) => tr["id"]))
const specialVisualisations = ValidationUtils.getAllSpecialVisualisations(
<any>json.tagRenderings,
<any>json.tagRenderings
)
const usedSpecialFunctions = new Set(
specialVisualisations.map((sv) =>
typeof sv === "string" ? undefined : sv.func.funcName,
),
typeof sv === "string" ? undefined : sv.func.funcName
)
)
/***** ADD TO TOP ****/
@ -830,7 +847,7 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
super(
"Converts a 'special' translation into a regular translation which uses parameters",
["special"],
"RewriteSpecial",
"RewriteSpecial"
)
}
@ -921,12 +938,12 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
private static convertIfNeeded(
input:
| (object & {
special: {
type: string
}
})
special: {
type: string
}
})
| any,
context: ConversionContext,
context: ConversionContext
): any {
const special = input["special"]
if (special === undefined) {
@ -936,7 +953,7 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
const type = special["type"]
if (type === undefined) {
context.err(
"A 'special'-block should define 'type' to indicate which visualisation should be used",
"A 'special'-block should define 'type' to indicate which visualisation should be used"
)
return undefined
}
@ -946,10 +963,10 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
const options = Utils.sortedByLevenshteinDistance(
type,
SpecialVisualizations.specialVisualizations,
(sp) => sp.funcName,
(sp) => sp.funcName
)
context.err(
`Special visualisation '${type}' not found. Did you perhaps mean ${options[0].funcName}, ${options[1].funcName} or ${options[2].funcName}?\n\tFor all known special visualisations, please see https://github.com/pietervdvn/MapComplete/blob/develop/Docs/SpecialRenderings.md`,
`Special visualisation '${type}' not found. Did you perhaps mean ${options[0].funcName}, ${options[1].funcName} or ${options[2].funcName}?\n\tFor all known special visualisations, please see https://github.com/pietervdvn/MapComplete/blob/develop/Docs/SpecialRenderings.md`
)
return undefined
}
@ -970,7 +987,7 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
const byDistance = Utils.sortedByLevenshteinDistance(
wrongArg,
argNamesList,
(x) => x,
(x) => x
)
return `Unexpected argument in special block at ${context} with name '${wrongArg}'. Did you mean ${
byDistance[0]
@ -989,8 +1006,8 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
`Obligated parameter '${arg.name}' in special rendering of type ${
vis.funcName
} not found.\n The full special rendering specification is: '${JSON.stringify(
input,
)}'\n ${arg.name}: ${arg.doc}`,
input
)}'\n ${arg.name}: ${arg.doc}`
)
}
}
@ -1092,7 +1109,7 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
continue
}
Utils.WalkPath(path.path, json, (leaf, travelled) =>
RewriteSpecial.convertIfNeeded(leaf, context.enter(travelled)),
RewriteSpecial.convertIfNeeded(leaf, context.enter(travelled))
)
}
@ -1126,7 +1143,7 @@ class ExpandIconBadges extends DesugaringStep<PointRenderingConfigJson> {
} = badgesJson[i]
const expanded = this._expand.convert(
<QuestionableTagRenderingConfigJson>iconBadge.then,
context.enters("iconBadges", i),
context.enters("iconBadges", i)
)
if (expanded === undefined) {
iconBadges.push(iconBadge)
@ -1137,7 +1154,7 @@ class ExpandIconBadges extends DesugaringStep<PointRenderingConfigJson> {
...expanded.map((resolved) => ({
if: iconBadge.if,
then: <MinimalTagRenderingConfigJson>resolved,
})),
}))
)
}
@ -1154,11 +1171,11 @@ class PreparePointRendering extends Fuse<PointRenderingConfigJson> {
new Each(
new On(
"icon",
new FirstOf(new ExpandTagRendering(state, layer, { applyCondition: false })),
),
),
new FirstOf(new ExpandTagRendering(state, layer, { applyCondition: false }))
)
)
),
new ExpandIconBadges(state, layer),
new ExpandIconBadges(state, layer)
)
}
}
@ -1168,7 +1185,7 @@ class SetFullNodeDatabase extends DesugaringStep<LayerConfigJson> {
super(
"sets the fullNodeDatabase-bit if needed",
["fullNodeDatabase"],
"SetFullNodeDatabase",
"SetFullNodeDatabase"
)
}
@ -1197,7 +1214,7 @@ class ExpandMarkerRenderings extends DesugaringStep<IconConfigJson> {
super(
"Expands tagRenderings in the icons, if needed",
["icon", "color"],
"ExpandMarkerRenderings",
"ExpandMarkerRenderings"
)
this._layer = layer
this._state = state
@ -1229,7 +1246,7 @@ class AddFavouriteBadges extends DesugaringStep<LayerConfigJson> {
super(
"Adds the favourite heart to the title and the rendering badges",
[],
"AddFavouriteBadges",
"AddFavouriteBadges"
)
}
@ -1254,7 +1271,7 @@ export class AddRatingBadge extends DesugaringStep<LayerConfigJson> {
super(
"Adds the 'rating'-element if a reviews-element is used in the tagRenderings",
["titleIcons"],
"AddRatingBadge",
"AddRatingBadge"
)
}
@ -1273,8 +1290,8 @@ export class AddRatingBadge extends DesugaringStep<LayerConfigJson> {
const specialVis: Exclude<RenderingSpecification, string>[] = <
Exclude<RenderingSpecification, string>[]
>ValidationUtils.getAllSpecialVisualisations(<any>json.tagRenderings).filter(
(rs) => typeof rs !== "string",
>ValidationUtils.getAllSpecialVisualisations(<any>json.tagRenderings).filter(
(rs) => typeof rs !== "string"
)
const funcs = new Set<string>(specialVis.map((rs) => rs.func.funcName))
@ -1290,12 +1307,12 @@ export class AutoTitleIcon extends DesugaringStep<LayerConfigJson> {
super(
"The auto-icon creates a (non-clickable) title icon based on a tagRendering which has icons",
["titleIcons"],
"AutoTitleIcon",
"AutoTitleIcon"
)
}
private createTitleIconsBasedOn(
tr: QuestionableTagRenderingConfigJson,
tr: QuestionableTagRenderingConfigJson
): TagRenderingConfigJson | undefined {
const mappings: { if: TagConfigJson; then: string }[] = tr.mappings
?.filter((m) => m.icon !== undefined)
@ -1325,7 +1342,7 @@ export class AutoTitleIcon extends DesugaringStep<LayerConfigJson> {
return undefined
}
return this.createTitleIconsBasedOn(<any>tr)
}),
})
)
json.titleIcons.splice(allAutoIndex, 1, ...generated)
return json
@ -1354,8 +1371,8 @@ export class AutoTitleIcon extends DesugaringStep<LayerConfigJson> {
.enters("titleIcons", i)
.warn(
"TagRendering with id " +
trId +
" does not have any icons, not generating an icon for this",
trId +
" does not have any icons, not generating an icon for this"
)
continue
}
@ -1370,7 +1387,7 @@ class DeriveSource extends DesugaringStep<LayerConfigJson> {
super(
"If no source is given, automatically derives the osmTags by 'or'-ing all the preset tags",
["source"],
"DeriveSource",
"DeriveSource"
)
}
@ -1380,7 +1397,7 @@ class DeriveSource extends DesugaringStep<LayerConfigJson> {
}
if (!json.presets) {
context.err(
"No source tags given. Trying to derive the source-tags based on the presets, but no presets are given",
"No source tags given. Trying to derive the source-tags based on the presets, but no presets are given"
)
return json
}
@ -1406,7 +1423,7 @@ class DeriveSource extends DesugaringStep<LayerConfigJson> {
export class PrepareLayer extends Fuse<LayerConfigJson> {
constructor(
state: DesugaringContext,
options?: { addTagRenderingsToContext?: false | boolean },
options?: { addTagRenderingsToContext?: false | boolean }
) {
super(
"Fully prepares and expands a layer for the LayerConfig.",
@ -1419,8 +1436,8 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
new Concat(
new ExpandTagRendering(state, layer, {
addToContext: options?.addTagRenderingsToContext ?? false,
}),
),
})
)
),
new On("tagRenderings", new Each(new DetectInline())),
new AddQuestionBox(),
@ -1433,11 +1450,11 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
new On<PointRenderingConfigJson[], LayerConfigJson>(
"pointRendering",
(layer) =>
new Each(new On("marker", new Each(new ExpandMarkerRenderings(state, layer)))),
new Each(new On("marker", new Each(new ExpandMarkerRenderings(state, layer))))
),
new On<PointRenderingConfigJson[], LayerConfigJson>(
"pointRendering",
(layer) => new Each(new PreparePointRendering(state, layer)),
(layer) => new Each(new PreparePointRendering(state, layer))
),
new SetDefault("titleIcons", ["icons.defaults"]),
new AddRatingBadge(),
@ -1446,10 +1463,10 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
new On(
"titleIcons",
(layer) =>
new Concat(new ExpandTagRendering(state, layer, { noHardcodedStrings: true })),
new Concat(new ExpandTagRendering(state, layer, { noHardcodedStrings: true }))
),
new AddFiltersFromTagRenderings(),
new ExpandFilter(state),
new ExpandFilter(state)
)
}
}

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 } from "./PrepareLayer"
import { LayerConfigJson } from "../Json/LayerConfigJson"
@ -18,7 +28,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
}
@ -28,7 +38,7 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
function reportNotFound(name: string) {
const knownLayers = Array.from(state.sharedLayers.keys())
const withDistance:[string,number][] = knownLayers.map((lname) => [
const withDistance: [string, number][] = knownLayers.map((lname) => [
lname,
Utils.levenshteinDistance(name, lname),
])
@ -74,14 +84,14 @@ 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",
(tr) => tr["id"] === "leftover-questions"
)
if (index < 0) {
index = found.tagRenderings.length
@ -95,8 +105,8 @@ 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"]
)}`
)
}
@ -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
}
@ -131,7 +141,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
}
@ -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
@ -172,7 +182,7 @@ class AddDefaultLayers extends DesugaringStep<ThemeConfigJson> {
super(
"Adds the default layers, namely: " + Constants.added_by_default.join(", "),
["layers"],
"AddDefaultLayers",
"AddDefaultLayers"
)
this._state = state
}
@ -195,10 +205,10 @@ 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
}
@ -214,7 +224,7 @@ class AddContextToTranslationsInLayout extends DesugaringStep<ThemeConfigJson> {
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"
)
}
@ -223,7 +233,7 @@ class AddContextToTranslationsInLayout extends DesugaringStep<ThemeConfigJson> {
// The context is used to generate the 'context' in the translation .It _must_ be `json.id` to correctly link into weblate
return conversion.convert(
json,
ConversionContext.construct([json.id], ["AddContextToTranslation"]),
ConversionContext.construct([json.id], ["AddContextToTranslation"])
)
}
}
@ -233,7 +243,7 @@ class ApplyOverrideAll extends DesugaringStep<ThemeConfigJson> {
super(
"Applies 'overrideAll' onto every 'layer'. The 'overrideAll'-field is removed afterwards",
["overrideAll", "layers"],
"ApplyOverrideAll",
"ApplyOverrideAll"
)
}
@ -262,7 +272,7 @@ class ApplyOverrideAll extends DesugaringStep<ThemeConfigJson> {
layer.tagRenderings = tagRenderingsPlus
} else {
let index = layer.tagRenderings.findIndex(
(tr) => tr["id"] === "leftover-questions",
(tr) => tr["id"] === "leftover-questions"
)
if (index < 0) {
index = layer.tagRenderings.length - 1
@ -285,7 +295,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
super(
`If a layer has a dependency on another layer, these layers are added automatically on the theme. (For example: defibrillator depends on 'walls_and_buildings' to snap onto. This layer is added automatically)`,
["layers"],
"AddDependencyLayersToTheme",
"AddDependencyLayersToTheme"
)
this._state = state
}
@ -294,7 +304,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
alreadyLoaded: LayerConfigJson[],
allKnownLayers: Map<string, LayerConfigJson>,
themeId: string,
context: ConversionContext,
context: ConversionContext
): { config: LayerConfigJson; reason: string }[] {
const dependenciesToAdd: { config: LayerConfigJson; reason: string }[] = []
const loadedLayerIds: Set<string> = new Set<string>(alreadyLoaded.map((l) => l?.id))
@ -318,7 +328,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
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) {
@ -337,8 +347,16 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
// We mark the needed layer as 'mustLoad'
const loadedLayer = alreadyLoaded.find((l) => l.id === dependency.neededLayer)
loadedLayer.forceLoad = true
if(dependency.checkHasSnapName && !loadedLayer.snapName){
context.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}`")
if (dependency.checkHasSnapName && !loadedLayer.snapName) {
context
.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}`"
)
}
}
}
@ -362,10 +380,10 @@ 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(","),
]
@ -381,12 +399,11 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
})
loadedLayerIds.add(dep.id)
unmetDependencies = unmetDependencies.filter(
(d) => d.neededLayer !== unmetDependency.neededLayer,
(d) => d.neededLayer !== unmetDependency.neededLayer
)
}
} while (unmetDependencies.length > 0)
return dependenciesToAdd
}
@ -404,12 +421,12 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
layers,
allKnownLayers,
theme.id,
context,
context
)
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
)
}
}
@ -457,7 +474,7 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<ThemeConfigJson>
super(
"Generates a warning if a theme uses an unsubstituted layer",
["layers"],
"WarnForUnsubstitutedLayersInTheme",
"WarnForUnsubstitutedLayersInTheme"
)
}
@ -469,7 +486,7 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<ThemeConfigJson>
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
}
@ -493,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
@ -523,13 +540,13 @@ class PostvalidateTheme extends DesugaringStep<ThemeConfigJson> {
}
const sameBasedOn = <LayerConfigJson[]>(
json.layers.filter(
(l) => l["_basedOn"] === layer["_basedOn"] && l["id"] !== layer.id,
(l) => l["_basedOn"] === layer["_basedOn"] && l["id"] !== layer.id
)
)
const minZoomAll = Math.min(...sameBasedOn.map((sbo) => sbo.minzoom))
const sameNameDetected = sameBasedOn.some(
(same) => JSON.stringify(layer["name"]) === JSON.stringify(same["name"]),
(same) => JSON.stringify(layer["name"]) === JSON.stringify(same["name"])
)
if (!sameNameDetected) {
// The name is unique, so it'll won't be confusing
@ -538,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."
)
}
}
@ -563,17 +580,17 @@ class PostvalidateTheme extends DesugaringStep<ThemeConfigJson> {
const closeLayers = Utils.sortedByLevenshteinDistance(
sameAs,
json.layers,
(l) => l["id"],
(l) => l["id"]
).map((l) => l["id"])
context
.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(", ")
)
}
}
@ -589,7 +606,7 @@ export class PrepareTheme extends Fuse<ThemeConfigJson> {
state: DesugaringContext,
options?: {
skipDefaultLayers: false | boolean
},
}
) {
super(
"Fully prepares and expands a theme",
@ -610,8 +627,8 @@ export class PrepareTheme extends Fuse<ThemeConfigJson> {
? new Pass("AddDefaultLayers is disabled due to the set flag")
: new AddDefaultLayers(state),
new AddDependencyLayersToTheme(state),
// new AddImportLayers(),
new PostvalidateTheme(state),
// new AddImportLayers(),
new PostvalidateTheme(state)
)
this.state = state
}
@ -626,13 +643,13 @@ export class PrepareTheme extends Fuse<ThemeConfigJson> {
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
}

View file

@ -88,7 +88,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
}
}
if(json["doCount"] !== undefined){
if (json["doCount"] !== undefined) {
context.err("Detected 'doCount'. did you mean: isCounted ?")
}
@ -145,7 +145,11 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
}
}
if(this._isBuiltin && json.allowMove === undefined && json.source["geoJson"] === undefined) {
if (
this._isBuiltin &&
json.allowMove === undefined &&
json.source["geoJson"] === undefined
) {
if (!Constants.priviliged_layers.find((x) => x == json.id)) {
context.err("Layer " + json.id + " does not have an explicit 'allowMove'")
}

View file

@ -30,7 +30,7 @@ export class ValidateLanguageCompleteness extends DesugaringStep<ThemeConfig> {
super(
"Checks that the given object is fully translated in the specified languages",
[],
"ValidateLanguageCompleteness",
"ValidateLanguageCompleteness"
)
this._languages = languages ?? ["en"]
}
@ -44,18 +44,18 @@ export class ValidateLanguageCompleteness extends DesugaringStep<ThemeConfig> {
.filter(
(t) =>
t.tr.translations[neededLanguage] === undefined &&
t.tr.translations["*"] === undefined,
t.tr.translations["*"] === undefined
)
.forEach((missing) => {
context
.enter(missing.context.split("."))
.err(
`The theme ${obj.id} should be translation-complete for ` +
neededLanguage +
", but it lacks a translation for " +
missing.context +
".\n\tThe known translation is " +
missing.tr.textFor("en"),
neededLanguage +
", but it lacks a translation for " +
missing.context +
".\n\tThe known translation is " +
missing.tr.textFor("en")
)
})
}
@ -72,7 +72,7 @@ export class DoesImageExist extends DesugaringStep<string> {
constructor(
knownImagePaths: Set<string>,
checkExistsSync: (path: string) => boolean = undefined,
ignore?: Set<string>,
ignore?: Set<string>
) {
super("Checks if an image exists", [], "DoesImageExist")
this._ignore = ignore
@ -112,15 +112,15 @@ export class DoesImageExist extends DesugaringStep<string> {
if (!this._knownImagePaths.has(image)) {
if (this.doesPathExist === undefined) {
context.err(
`Image with path ${image} not found or not attributed; it is used in ${context}`,
`Image with path ${image} not found or not attributed; it is used in ${context}`
)
} else if (!this.doesPathExist(image)) {
context.err(
`Image with path ${image} does not exist.\n Check for typo's and missing directories in the path.`,
`Image with path ${image} does not exist.\n Check for typo's and missing directories in the path.`
)
} else {
context.err(
`Image with path ${image} is not attributed (but it exists); execute 'npm run query:licenses' to add the license information and/or run 'npm run generate:licenses' to compile all the license info`,
`Image with path ${image} is not attributed (but it exists); execute 'npm run query:licenses' to add the license information and/or run 'npm run generate:licenses' to compile all the license info`
)
}
}
@ -133,7 +133,7 @@ class OverrideShadowingCheck extends DesugaringStep<ThemeConfigJson> {
super(
"Checks that an 'overrideAll' does not override a single override",
[],
"OverrideShadowingCheck",
"OverrideShadowingCheck"
)
}
@ -183,7 +183,7 @@ class MiscThemeChecks extends DesugaringStep<ThemeConfigJson> {
context
.enter("layers")
.err(
"The 'layers'-field should be an array, but it is not. Did you pase a layer identifier and forget to add the '[' and ']'?",
"The 'layers'-field should be an array, but it is not. Did you pase a layer identifier and forget to add the '[' and ']'?"
)
}
if (json.socialImage === "") {
@ -217,23 +217,37 @@ class MiscThemeChecks extends DesugaringStep<ThemeConfigJson> {
context
.enter("overideAll")
.err(
"'overrideAll' is spelled with _two_ `r`s. You only wrote a single one of them.",
"'overrideAll' is spelled with _two_ `r`s. You only wrote a single one of them."
)
}
if (json.defaultBackgroundId
&& ![AvailableRasterLayers.osmCartoProperties.id, ...eliCategory ]
.find(l => l === json.defaultBackgroundId) ) {
if (
json.defaultBackgroundId &&
![AvailableRasterLayers.osmCartoProperties.id, ...eliCategory].find(
(l) => l === json.defaultBackgroundId
)
) {
const background = json.defaultBackgroundId
const match = AvailableRasterLayers.globalLayers.find(l => l.properties.id === background)
const match = AvailableRasterLayers.globalLayers.find(
(l) => l.properties.id === background
)
if (!match) {
const suggestions = Utils.sortedByLevenshteinDistance(background,
AvailableRasterLayers.globalLayers, l => l.properties.id)
context.enter("defaultBackgroundId")
.warn("The default background layer with id", background, "does not exist or is not a global layer. Perhaps you meant one of:",
suggestions.slice(0, 5).map(l => l.properties.id).join(", "),
"If you want to use a certain category of background image, use", AvailableRasterLayers.globalLayers.join(", ")
)
const suggestions = Utils.sortedByLevenshteinDistance(
background,
AvailableRasterLayers.globalLayers,
(l) => l.properties.id
)
context.enter("defaultBackgroundId").warn(
"The default background layer with id",
background,
"does not exist or is not a global layer. Perhaps you meant one of:",
suggestions
.slice(0, 5)
.map((l) => l.properties.id)
.join(", "),
"If you want to use a certain category of background image, use",
AvailableRasterLayers.globalLayers.join(", ")
)
}
}
return json
@ -245,7 +259,7 @@ export class PrevalidateTheme extends Fuse<ThemeConfigJson> {
super(
"Various consistency checks on the raw JSON",
new MiscThemeChecks(),
new OverrideShadowingCheck(),
new OverrideShadowingCheck()
)
}
}
@ -255,7 +269,7 @@ export class DetectConflictingAddExtraTags extends DesugaringStep<TagRenderingCo
super(
"The `if`-part in a mapping might set some keys. Those keys are not allowed to be set in the `addExtraTags`, as this might result in conflicting values",
[],
"DetectConflictingAddExtraTags",
"DetectConflictingAddExtraTags"
)
}
@ -282,7 +296,7 @@ export class DetectConflictingAddExtraTags extends DesugaringStep<TagRenderingCo
.enters("mappings", i)
.err(
"AddExtraTags overrides a key that is set in the `if`-clause of this mapping. Selecting this answer might thus first set one value (needed to match as answer) and then override it with a different value, resulting in an unsaveable question. The offending `addExtraTags` is " +
duplicateKeys.join(", "),
duplicateKeys.join(", ")
)
}
}
@ -300,13 +314,13 @@ export class DetectNonErasedKeysInMappings extends DesugaringStep<QuestionableTa
super(
"A tagRendering might set a freeform key (e.g. `name` and have an option that _should_ erase this name, e.g. `noname=yes`). Under normal circumstances, every mapping/freeform should affect all touched keys",
[],
"DetectNonErasedKeysInMappings",
"DetectNonErasedKeysInMappings"
)
}
convert(
json: QuestionableTagRenderingConfigJson,
context: ConversionContext,
context: ConversionContext
): QuestionableTagRenderingConfigJson {
if (json.multiAnswer) {
// No need to check this here, this has its own validation
@ -360,8 +374,8 @@ export class DetectNonErasedKeysInMappings extends DesugaringStep<QuestionableTa
.enters("freeform")
.warn(
"The freeform block does not modify the key `" +
neededKey +
"` which is set in a mapping. Use `addExtraTags` to overwrite it",
neededKey +
"` which is set in a mapping. Use `addExtraTags` to overwrite it"
)
}
}
@ -379,8 +393,8 @@ export class DetectNonErasedKeysInMappings extends DesugaringStep<QuestionableTa
.enters("mappings", i)
.warn(
"This mapping does not modify the key `" +
neededKey +
"` which is set in a mapping or by the freeform block. Use `addExtraTags` to overwrite it",
neededKey +
"` which is set in a mapping or by the freeform block. Use `addExtraTags` to overwrite it"
)
}
}
@ -397,7 +411,7 @@ export class DetectMappingsShadowedByCondition extends DesugaringStep<TagRenderi
super(
"Checks that, if the tagrendering has a condition, that a mapping is not contradictory to it, i.e. that there are no dead mappings",
[],
"DetectMappingsShadowedByCondition",
"DetectMappingsShadowedByCondition"
)
this._forceError = forceError
}
@ -469,7 +483,7 @@ export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJso
* DetectShadowedMappings.extractCalculatedTagNames({calculatedTags: ["_abc=js()"]}) // => ["_abc"]
*/
private static extractCalculatedTagNames(
layerConfig?: LayerConfigJson | { calculatedTags: string[] },
layerConfig?: LayerConfigJson | { calculatedTags: string[] }
) {
return (
layerConfig?.calculatedTags?.map((ct) => {
@ -555,16 +569,16 @@ export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJso
json.mappings[i]["hideInAnswer"] !== true
) {
context.warn(
`Mapping ${i} is shadowed by mapping ${j}. However, mapping ${j} has 'hideInAnswer' set, which will result in a different rendering in question-mode.`,
`Mapping ${i} is shadowed by mapping ${j}. However, mapping ${j} has 'hideInAnswer' set, which will result in a different rendering in question-mode.`
)
} else if (doesMatch) {
// The current mapping is shadowed!
context.err(`Mapping ${i} is shadowed by mapping ${j} and will thus never be shown:
The mapping ${parsedConditions[i].asHumanString(
false,
false,
{},
)} is fully matched by a previous mapping (namely ${j}), which matches:
false,
false,
{}
)} is fully matched by a previous mapping (namely ${j}), which matches:
${parsedConditions[j].asHumanString(false, false, {})}.
To fix this problem, you can try to:
@ -589,7 +603,7 @@ export class ValidatePossibleLinks extends DesugaringStep<string | Record<string
super(
"Given a possible set of translations, validates that <a href=... target='_blank'> does have `rel='noopener'` set",
[],
"ValidatePossibleLinks",
"ValidatePossibleLinks"
)
}
@ -619,21 +633,21 @@ export class ValidatePossibleLinks extends DesugaringStep<string | Record<string
convert(
json: string | Record<string, string>,
context: ConversionContext,
context: ConversionContext
): string | Record<string, string> {
if (typeof json === "string") {
if (this.isTabnabbingProne(json)) {
context.err(
"The string " +
json +
" has a link targeting `_blank`, but it doesn't have `rel='noopener'` set. This gives rise to reverse tabnapping",
json +
" has a link targeting `_blank`, but it doesn't have `rel='noopener'` set. This gives rise to reverse tabnapping"
)
}
} else {
for (const k in json) {
if (this.isTabnabbingProne(json[k])) {
context.err(
`The translation for ${k} '${json[k]}' has a link targeting \`_blank\`, but it doesn't have \`rel='noopener'\` set. This gives rise to reverse tabnapping`,
`The translation for ${k} '${json[k]}' has a link targeting \`_blank\`, but it doesn't have \`rel='noopener'\` set. This gives rise to reverse tabnapping`
)
}
}
@ -651,7 +665,7 @@ export class CheckTranslation extends DesugaringStep<Translatable> {
super(
"Checks that a translation is valid and internally consistent",
["*"],
"CheckTranslation",
"CheckTranslation"
)
this._allowUndefined = allowUndefined
}
@ -698,7 +712,7 @@ export class ValidateLayerConfig extends DesugaringStep<LayerConfigJson> {
isBuiltin: boolean,
doesImageExist: DoesImageExist,
studioValidations: boolean = false,
skipDefaultLayers: boolean = false,
skipDefaultLayers: boolean = false
) {
super("Thin wrapper around 'ValidateLayer", [], "ValidateLayerConfig")
this.validator = new ValidateLayer(
@ -706,7 +720,7 @@ export class ValidateLayerConfig extends DesugaringStep<LayerConfigJson> {
isBuiltin,
doesImageExist,
studioValidations,
skipDefaultLayers,
skipDefaultLayers
)
}
@ -734,7 +748,7 @@ export class ValidatePointRendering extends DesugaringStep<PointRenderingConfigJ
context
.enter("markers")
.err(
`Detected a field 'markerS' in pointRendering. It is written as a singular case`,
`Detected a field 'markerS' in pointRendering. It is written as a singular case`
)
}
if (json.marker && !Array.isArray(json.marker)) {
@ -744,7 +758,7 @@ export class ValidatePointRendering extends DesugaringStep<PointRenderingConfigJ
context
.enter("location")
.err(
"A pointRendering should have at least one 'location' to defined where it should be rendered. ",
"A pointRendering should have at least one 'location' to defined where it should be rendered. "
)
}
return json
@ -763,26 +777,26 @@ export class ValidateLayer extends Conversion<
isBuiltin: boolean,
doesImageExist: DoesImageExist,
studioValidations: boolean = false,
skipDefaultLayers: boolean = false,
skipDefaultLayers: boolean = false
) {
super("Doesn't change anything, but emits warnings and errors", [], "ValidateLayer")
this._prevalidation = new PrevalidateLayer(
path,
isBuiltin,
doesImageExist,
studioValidations,
studioValidations
)
this._skipDefaultLayers = skipDefaultLayers
}
convert(
json: LayerConfigJson,
context: ConversionContext,
context: ConversionContext
): { parsed: LayerConfig; raw: LayerConfigJson } {
context = context.inOperation(this.name)
if (typeof json === "string") {
context.err(
`Not a valid layer: the layerConfig is a string. 'npm run generate:layeroverview' might be needed`,
`Not a valid layer: the layerConfig is a string. 'npm run generate:layeroverview' might be needed`
)
return undefined
}
@ -814,7 +828,7 @@ export class ValidateLayer extends Conversion<
context
.enters("calculatedTags", i)
.err(
`Invalid function definition: the custom javascript is invalid:${e}. The offending javascript code is:\n ${code}`,
`Invalid function definition: the custom javascript is invalid:${e}. The offending javascript code is:\n ${code}`
)
}
}
@ -862,7 +876,7 @@ export class ValidateLayer extends Conversion<
context
.enters("allowMove", "enableAccuracy")
.err(
"`enableAccuracy` is written with two C in the first occurrence and only one in the last",
"`enableAccuracy` is written with two C in the first occurrence and only one in the last"
)
}
@ -893,8 +907,8 @@ export class ValidateFilter extends DesugaringStep<FilterConfigJson> {
.enters("fields", i)
.err(
`Invalid filter: ${type} is not a valid textfield type.\n\tTry one of ${Array.from(
Validators.availableTypes,
).join(",")}`,
Validators.availableTypes
).join(",")}`
)
}
}
@ -911,13 +925,13 @@ export class DetectDuplicateFilters extends DesugaringStep<{
super(
"Tries to detect layers where a shared filter can be used (or where similar filters occur)",
[],
"DetectDuplicateFilters",
"DetectDuplicateFilters"
)
}
convert(
json: { layers: LayerConfigJson[]; themes: ThemeConfigJson[] },
context: ConversionContext,
context: ConversionContext
): { layers: LayerConfigJson[]; themes: ThemeConfigJson[] } {
const { layers, themes } = json
const perOsmTag = new Map<
@ -981,7 +995,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{
filter: FilterConfigJson
}[]
>,
theme?: ThemeConfigJson | undefined,
theme?: ThemeConfigJson | undefined
): void {
if (layer.filter === undefined || layer.filter === null) {
return
@ -1021,7 +1035,7 @@ export class DetectDuplicatePresets extends DesugaringStep<ThemeConfig> {
super(
"Detects mappings which have identical (english) names or identical mappings.",
["presets"],
"DetectDuplicatePresets",
"DetectDuplicatePresets"
)
}
@ -1032,13 +1046,13 @@ export class DetectDuplicatePresets extends DesugaringStep<ThemeConfig> {
if (new Set(enNames).size != enNames.length) {
const dups = Utils.Duplicates(enNames)
const layersWithDup = json.layers.filter((l) =>
l.presets.some((p) => dups.indexOf(p.title.textFor("en")) >= 0),
l.presets.some((p) => dups.indexOf(p.title.textFor("en")) >= 0)
)
const layerIds = layersWithDup.map((l) => l.id)
context.err(
`This theme has multiple presets which are named:${dups}, namely layers ${layerIds.join(
", ",
)} this is confusing for contributors and is probably the result of reusing the same layer multiple times. Use \`{"override": {"=presets": []}}\` to remove some presets`,
", "
)} this is confusing for contributors and is probably the result of reusing the same layer multiple times. Use \`{"override": {"=presets": []}}\` to remove some presets`
)
}
@ -1053,17 +1067,17 @@ export class DetectDuplicatePresets extends DesugaringStep<ThemeConfig> {
Utils.SameObject(presetATags, presetBTags) &&
Utils.sameList(
presetA.preciseInput.snapToLayers,
presetB.preciseInput.snapToLayers,
presetB.preciseInput.snapToLayers
)
) {
context.err(
`This theme has multiple presets with the same tags: ${presetATags.asHumanString(
false,
false,
{},
{}
)}, namely the preset '${presets[i].title.textFor("en")}' and '${presets[
j
].title.textFor("en")}'`,
].title.textFor("en")}'`
)
}
}
@ -1088,13 +1102,13 @@ export class ValidateThemeEnsemble extends Conversion<
super(
"Validates that all themes together are logical, i.e. no duplicate ids exists within (overriden) themes",
[],
"ValidateThemeEnsemble",
"ValidateThemeEnsemble"
)
}
convert(
json: ThemeConfig[],
context: ConversionContext,
context: ConversionContext
): Map<
string,
{
@ -1145,11 +1159,11 @@ export class ValidateThemeEnsemble extends Conversion<
context.err(
[
"The layer with id '" +
id +
"' is found in multiple themes with different tag definitions:",
id +
"' is found in multiple themes with different tag definitions:",
"\t In theme " + oldTheme + ":\t" + oldTags.asHumanString(false, false, {}),
"\tIn theme " + theme.id + ":\t" + tags.asHumanString(false, false, {}),
].join("\n"),
].join("\n")
)
}
}