-- website-microdata Soupault Plugin -- SPDX-FileCopyrightText: 2026 toastal -- SPDX-License-Identifier: MPL-2.0 -- -- (Re)applies the Schema.org WebSite scope to #DocWrapper & the WebPage -- scope to main#Main, along with the WebSite properties. Plugin.require_version("5.0.0") -- Shaped in Nickel (soupault.ncl custom_options.website_props); this plugin -- only applies the entries to the DOM. website_props = soupault_config["custom_options"]["website_props"] if Value.is_nil(website_props) or Table.is_empty(website_props) then Plugin.fail("custom_options.website_props is missing or empty: cannot apply WebSite microdata") end function upsert_prop(parent, prop) if not Value.is_table(prop) or not Value.is_string(prop["tag"]) or not Value.is_string(prop["attr"]) or not Value.is_string(prop["itemprop"]) or not Value.is_string(prop["value"]) then Plugin.fail("custom_options.website_props holds a malformed entry") end local existing = HTML.select(parent, format("%s[itemprop=\"%s\"]", prop["tag"], prop["itemprop"])) if Table.is_empty(existing) then local elem = HTML.create_element(prop["tag"]) HTML.set_attribute(elem, "itemprop", prop["itemprop"]) HTML.set_attribute(elem, prop["attr"], prop["value"]) HTML.prepend_child(parent, elem) else -- Nickel is source of truth: normalize survivors (meaning. from -- template/main.html on raw-HTML pages) to the configured value. local idx = 1 while existing[idx] do HTML.set_attribute(existing[idx], prop["attr"], prop["value"]) idx = idx + 1 end end end doc_wrapper = HTML.select_one(page, "#DocWrapper") if HTML.is_element(doc_wrapper) then HTML.set_attribute(doc_wrapper, "itemscope", "") HTML.set_attribute(doc_wrapper, "itemtype", "https://schema.org/WebSite") -- Prepend in reverse so the final order matches template/main.html. local idx = Table.length(website_props) while idx >= 1 do upsert_prop(doc_wrapper, website_props[idx]) idx = idx - 1 end else Log.warning(format("No #DocWrapper found in ā€œ%sā€, skipping WebSite scope", page_file)) end main = HTML.select_one(page, "main#Main") if HTML.is_element(main) then HTML.set_attribute(main, "itemscope", "") HTML.set_attribute(main, "itemprop", "mainEntity") HTML.set_attribute(main, "itemtype", "https://schema.org/WebPage") else Log.warning(format("No main#Main found in ā€œ%sā€, skipping WebPage scope", page_file)) end