Docs: add 'used in layers'-information for builtingQuestions + various small refactorings

This commit is contained in:
Pieter Vander Vennet 2025-06-13 02:39:27 +02:00
parent 2545982dbd
commit bc2ea7841f
9 changed files with 83 additions and 47 deletions

View file

@ -3611,7 +3611,8 @@
"it": "In questo posto non è possibile prenotare" "it": "In questo posto non è possibile prenotare"
} }
} }
] ],
"onSoftDelete": "reservation="
}, },
{ {
"id": "ref", "id": "ref",

View file

@ -386,9 +386,11 @@ export class GenerateDocs extends Script {
allLayers.forEach((layer) => { allLayers.forEach((layer) => {
const element = layer.generateDocumentation( const element = layer.generateDocumentation(
themesPerLayer.get(layer.id), {
layerIsNeededBy, usedInThemes: themesPerLayer.get(layer.id),
DependencyCalculator.getLayerDependencies(layer) layerIsNeededBy: layerIsNeededBy,
dependencies: DependencyCalculator.getLayerDependencies(layer),
},
) )
const inlineSource = inlineLayers.get(layer.id) const inlineSource = inlineLayers.get(layer.id)
ScriptUtils.erasableLog("Exporting layer documentation for", layer.id) ScriptUtils.erasableLog("Exporting layer documentation for", layer.id)
@ -481,15 +483,17 @@ export class GenerateDocs extends Script {
"questions.json", "questions.json",
true true
) )
const reusedTagRenderings = DependencyCalculator.tagRenderingImportedBy(qLayer, Array.from(AllSharedLayers.sharedLayers.values()))
const docs = qLayer.generateDocumentation({ reusedTagRenderings })
this.WriteMarkdownFile( this.WriteMarkdownFile(
"./Docs/BuiltinQuestions.md", "./Docs/BuiltinQuestions.md",
qLayer.generateDocumentation([], new Map(), []), docs,
["assets/layers/questions/questions.json"] ["assets/layers/questions/questions.json"]
) )
} }
private generateForTheme(theme: ThemeConfig): void { private generateForTheme(theme: ThemeConfig): void {
const allLayers = AllSharedLayers.getSharedLayersConfigs() const allLayers = AllSharedLayers.sharedLayers
const layersToShow = theme.layers.filter( const layersToShow = theme.layers.filter(
(l) => l.id !== "favourite" && Constants.added_by_default.indexOf(<any>l.id) < 0 (l) => l.id !== "favourite" && Constants.added_by_default.indexOf(<any>l.id) < 0
) )
@ -520,7 +524,7 @@ export class GenerateDocs extends Script {
MarkdownUtils.list(theme.language.filter((ln) => ln !== "_context")), MarkdownUtils.list(theme.language.filter((ln) => ln !== "_context")),
"# Layers defined in this theme configuration file", "# Layers defined in this theme configuration file",
"These layers can not be reused in different themes.", "These layers can not be reused in different themes.",
...layersToInline.map((l) => l.generateDocumentation(null)), ...layersToInline.map((l) => l.generateDocumentation({ usedInThemes: null })),
].join("\n") ].join("\n")
this.WriteMarkdownFile( this.WriteMarkdownFile(
"./Docs/Themes/" + theme.id + ".md", "./Docs/Themes/" + theme.id + ".md",
@ -587,11 +591,13 @@ export class GenerateDocs extends Script {
Constants.priviliged_layers.map((id) => AllSharedLayers.sharedLayers.get(id)) Constants.priviliged_layers.map((id) => AllSharedLayers.sharedLayers.get(id))
).map((l) => ).map((l) =>
l.generateDocumentation( l.generateDocumentation(
themesPerLayer.get(l.id), {
layerIsNeededBy, usedInThemes: themesPerLayer.get(l.id),
DependencyCalculator.getLayerDependencies(l), layerIsNeededBy: layerIsNeededBy,
Constants.added_by_default.indexOf(<any>l.id) >= 0, dependencies: DependencyCalculator.getLayerDependencies(l),
Constants.no_include.indexOf(<any>l.id) < 0 addedByDefault: Constants.added_by_default.indexOf(<any>l.id) >= 0,
canBeIncluded: Constants.no_include.indexOf(<any>l.id) < 0,
},
) )
), ),
"# Normal layers", "# Normal layers",

View file

@ -14,11 +14,7 @@ import {
import { Translation } from "../src/UI/i18n/Translation" import { Translation } from "../src/UI/i18n/Translation"
import { PrepareLayer } from "../src/Models/ThemeConfig/Conversion/PrepareLayer" import { PrepareLayer } from "../src/Models/ThemeConfig/Conversion/PrepareLayer"
import { PrepareTheme } from "../src/Models/ThemeConfig/Conversion/PrepareTheme" import { PrepareTheme } from "../src/Models/ThemeConfig/Conversion/PrepareTheme"
import { import { Conversion, DesugaringContext, DesugaringStep } from "../src/Models/ThemeConfig/Conversion/Conversion"
Conversion,
DesugaringContext,
DesugaringStep,
} from "../src/Models/ThemeConfig/Conversion/Conversion"
import { Utils } from "../src/Utils" import { Utils } from "../src/Utils"
import Script from "./Script" import Script from "./Script"
import { AllSharedLayers } from "../src/Customizations/AllSharedLayers" import { AllSharedLayers } from "../src/Customizations/AllSharedLayers"
@ -35,10 +31,7 @@ import { Translatable } from "../src/Models/ThemeConfig/Json/Translatable"
import { ValidateThemeAndLayers } from "../src/Models/ThemeConfig/Conversion/ValidateThemeAndLayers" import { ValidateThemeAndLayers } from "../src/Models/ThemeConfig/Conversion/ValidateThemeAndLayers"
import { ExtractImages } from "../src/Models/ThemeConfig/Conversion/FixImages" import { ExtractImages } from "../src/Models/ThemeConfig/Conversion/FixImages"
import { TagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/TagRenderingConfigJson" import { TagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/TagRenderingConfigJson"
import { import { LayerConfigDependencyGraph, LevelInfo } from "../src/Models/ThemeConfig/LayerConfigDependencyGraph"
LayerConfigDependencyGraph,
LevelInfo,
} from "../src/Models/ThemeConfig/LayerConfigDependencyGraph"
// This scripts scans 'src/assets/layers/*.json' for layer definition files and 'src/assets/themes/*.json' for theme definition files. // This scripts scans 'src/assets/layers/*.json' for layer definition files and 'src/assets/themes/*.json' for theme definition files.
// It spits out an overview of those to be used to load them // It spits out an overview of those to be used to load them
@ -54,7 +47,7 @@ class ParseLayer extends Conversion<
private readonly _doesImageExist: DoesImageExist private readonly _doesImageExist: DoesImageExist
constructor(prepareLayer: PrepareLayer, doesImageExist: DoesImageExist) { constructor(prepareLayer: PrepareLayer, doesImageExist: DoesImageExist) {
super("ParseLayer", "Parsed a layer from file, validates it", []) super("ParseLayer", "Parsed a layer from file, validates it")
this._prepareLayer = prepareLayer this._prepareLayer = prepareLayer
this._doesImageExist = doesImageExist this._doesImageExist = doesImageExist
} }
@ -113,7 +106,7 @@ class AddIconSummary extends DesugaringStep<{ raw: LayerConfigJson; parsed: Laye
static singleton = new AddIconSummary() static singleton = new AddIconSummary()
constructor() { constructor() {
super("Adds an icon summary for quick reference", ["_layerIcon"], "AddIconSummary") super("AddIconSummary","Adds an icon summary for quick reference")
} }
convert(json: { raw: LayerConfigJson; parsed: LayerConfig }) { convert(json: { raw: LayerConfigJson; parsed: LayerConfig }) {
@ -158,7 +151,7 @@ class LayerBuilder extends Conversion<object, Map<string, LayerConfigJson>> {
states: Map<string, "clean" | "dirty" | "changed">, states: Map<string, "clean" | "dirty" | "changed">,
sharedTagRenderings: QuestionableTagRenderingConfigJson[] sharedTagRenderings: QuestionableTagRenderingConfigJson[]
) { ) {
super("LayerBuilder", "Builds all the layers, writes them to file", []) super("LayerBuilder", "Builds all the layers, writes them to file")
this._levels = levels this._levels = levels
this._dependencies = dependencies this._dependencies = dependencies
this._states = states this._states = states
@ -667,7 +660,7 @@ class LayerOverviewUtils extends Script {
priviliged.delete("last_click") priviliged.delete("last_click")
priviliged.delete("search") priviliged.delete("search")
const isBoostrapping = AllSharedLayers.getSharedLayersConfigs().size == 0 const isBoostrapping = AllSharedLayers.sharedLayers.size == 0
if (!isBoostrapping && priviliged.size > 0) { if (!isBoostrapping && priviliged.size > 0) {
throw ( throw (
"Priviliged layer " + "Priviliged layer " +

View file

@ -4,7 +4,7 @@ import * as known_layers from "../assets/generated/known_layers.json"
import { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson" import { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson"
export class AllSharedLayers { export class AllSharedLayers {
public static sharedLayers: Map<string, LayerConfig> = AllSharedLayers.getSharedLayers() public static sharedLayers: ReadonlyMap<string, LayerConfig> = AllSharedLayers.getSharedLayers()
public static getSharedLayersConfigs(): Map<string, LayerConfigJson> { public static getSharedLayersConfigs(): Map<string, LayerConfigJson> {
const sharedLayers = new Map<string, LayerConfigJson>() const sharedLayers = new Map<string, LayerConfigJson>()
for (const layer of known_layers["layers"]) { for (const layer of known_layers["layers"]) {

View file

@ -9,7 +9,7 @@ export interface DesugaringContext {
* Order of appearance in questions.json * Order of appearance in questions.json
*/ */
tagRenderingOrder: string[] tagRenderingOrder: string[]
sharedLayers: Map<string, LayerConfigJson> sharedLayers: Map<string, Readonly<LayerConfigJson>>
publicLayers?: Set<string> publicLayers?: Set<string>
} }

View file

@ -3,9 +3,38 @@ import { ExtraFuncParams, ExtraFunctions } from "../../Logic/ExtraFunctions"
import LayerConfig from "./LayerConfig" import LayerConfig from "./LayerConfig"
import { SpecialVisualization } from "../../UI/SpecialVisualization" import { SpecialVisualization } from "../../UI/SpecialVisualization"
import SpecialVisualizations from "../../UI/SpecialVisualizations" import SpecialVisualizations from "../../UI/SpecialVisualizations"
import { LayerConfigJson } from "./Json/LayerConfigJson"
export default class DependencyCalculator { export default class DependencyCalculator {
public static GetTagRenderingDependencies(tr: TagRenderingConfig): {
/**
* For every tagRendering in the listed layers, determines in what layers they end up
*/
public static tagRenderingImportedBy(questionedLayer: LayerConfig, layers: LayerConfig[]): Map<string, {
layer: string
}[]> {
const result: Map<string, { layer: string }[]> = new Map()
for (const layer of layers) {
const hasRightContext = layer.tagRenderings.filter(tr => tr._definedIn !== undefined && tr?._definedIn?.[0] === questionedLayer.id)
for (const tr of hasRightContext) {
const id = tr._definedIn[1]
if (!result.has(id)) {
result.set(id, [])
}
result.get(id).push({ layer: layer.id })
}
}
return result
}
/**
* Calculates what layers are introduced by a tagRenderingConfig
* @param tr
* @private
*/
private static getTagRenderingDependencies(tr: TagRenderingConfig): {
id: string id: string
minzoom?: number minzoom?: number
neededBy: string neededBy: string
@ -83,7 +112,7 @@ export default class DependencyCalculator {
} }
for (const tr of layer.AllTagRenderings()) { for (const tr of layer.AllTagRenderings()) {
for (const dep of DependencyCalculator.GetTagRenderingDependencies(tr)) { for (const dep of DependencyCalculator.getTagRenderingDependencies(tr)) {
deps.push({ deps.push({
neededLayer: dep.id, neededLayer: dep.id,
reason: `tagrendering ${dep.neededBy} needs this layer`, reason: `tagrendering ${dep.neededBy} needs this layer`,

View file

@ -439,15 +439,15 @@ export default class LayerConfig extends WithContextLoader {
} }
public generateDocumentation( public generateDocumentation(
usedInThemes: string[], { usedInThemes = [], layerIsNeededBy, dependencies = [], addedByDefault = false, canBeIncluded = true, lang = "en", reusedTagRenderings }: {
layerIsNeededBy?: Map<string, string[]>, usedInThemes?: string[],
dependencies: { layerIsNeededBy?: Map<string, string[]>,
context?: string dependencies?: { context?: string; reason: string; neededLayer: string }[],
reason: string addedByDefault?: boolean,
neededLayer: string canBeIncluded?: boolean,
}[] = [], reusedTagRenderings?: Map<string, {layer: string}[]>,
addedByDefault = false, lang?: string
canBeIncluded = true }
): string { ): string {
const extraProps: string[] = [] const extraProps: string[] = []
extraProps.push("This layer is shown at zoomlevel **" + this.minzoom + "** and higher") extraProps.push("This layer is shown at zoomlevel **" + this.minzoom + "** and higher")
@ -669,7 +669,7 @@ export default class LayerConfig extends WithContextLoader {
this.generateDocumentationQuickTable(), this.generateDocumentationQuickTable(),
...this.tagRenderings ...this.tagRenderings
.filter((tr) => tr.labels.indexOf("ignore_docs") < 0) .filter((tr) => tr.labels.indexOf("ignore_docs") < 0)
.map((tr) => tr.GenerateDocumentation()), .map((tr) => tr.generateDocumentation(lang, reusedTagRenderings?.get(tr.id)?.map(l => l.layer))),
...filterDocs, ...filterDocs,
].join("\n\n") ].join("\n\n")
} }

View file

@ -941,7 +941,7 @@ export default class TagRenderingConfig {
} }
} }
GenerateDocumentation(lang: string = "en"): string { generateDocumentation(lang: string = "en", usedInLayers?: string[]): string {
let freeform: string = undefined let freeform: string = undefined
if (this.render) { if (this.render) {
freeform = "\n*" + this.render.textFor(lang) + "*" freeform = "\n*" + this.render.textFor(lang) + "*"
@ -1016,12 +1016,18 @@ export default class TagRenderingConfig {
let labels: string = undefined let labels: string = undefined
if (this.labels?.length > 0) { if (this.labels?.length > 0) {
labels = [ labels = [
"This tagrendering has labels ", "This tagrendering has labels",
...this.labels.map((label) => "`" + label + "`"), ...this.labels.map((label) => "`" + label + "`"),
].join("\n") ].join(" ")
}
let reuse : string = undefined
if(usedInLayers?.length > 0){
reuse = [`This tagRendering is used in ${usedInLayers.length} layers:`,
...usedInLayers.map(l => `[${l}](./Layers/${l}.md)`)
].join(" ")
} }
return [ return Utils.NoNull([
"### " + this.id, "### " + this.id,
this.description, this.description,
this.question !== undefined this.question !== undefined
@ -1031,7 +1037,9 @@ export default class TagRenderingConfig {
mappings, mappings,
condition, condition,
labels, labels,
].join("\n") "",
reuse
]).join("\n")
} }
public usedTags(): TagsFilter[] { public usedTags(): TagsFilter[] {

View file

@ -283,8 +283,7 @@ class ContextRewritingStep<T> extends Conversion<LayerConfigJson, T> {
) { ) {
super( super(
"ContextRewritingStep", "ContextRewritingStep",
"When validating a layer, the tagRenderings are first expanded. Some builtin tagRendering-calls (e.g. `contact`) will introduce _multiple_ tagRenderings, causing the count to be off. This class rewrites the error messages to fix this", "When validating a layer, the tagRenderings are first expanded. Some builtin tagRendering-calls (e.g. `contact`) will introduce _multiple_ tagRenderings, causing the count to be off. This class rewrites the error messages to fix this"
[]
) )
this._state = state this._state = state
this._step = step this._step = step
@ -460,7 +459,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
} }
const state: DesugaringContext = { const state: DesugaringContext = {
tagRenderings: sharedQuestions, tagRenderings: sharedQuestions,
sharedLayers: layers, sharedLayers: new Map(layers),
tagRenderingOrder: [], tagRenderingOrder: [],
} }
const prepare = this.buildValidation(state) const prepare = this.buildValidation(state)