gatsby-transformer-ceteicean
Version:
Gastby transformer plugin for TEI XML to CETEIcean Custom Elements
154 lines (132 loc) • 4.64 kB
JavaScript
const { DOMParser, XMLSerializer } = require(`xmldom`)
async function onCreateNode({ node, actions, loadNodeContent, createNodeId, createContentDigest }, pluginOptions) {
if (
[`application/xml`, `text/xml`].includes(node.internal.mediaType)
|| [`xml`, `odd`].includes(node.extension)) {
const options = pluginOptions || {}
const { createNode, createParentChildLink } = actions
const content = await loadNodeContent(node)
const defaultNamespaces = {
"http://www.tei-c.org/ns/1.0": "tei",
"http://www.tei-c.org/ns/Examples": "teieg"
}
const namespaces = options.namespaces || defaultNamespaces
const before = options.applyBefore || []
const after = options.applyAfter || []
const transformQueue = [...before, transform, ...after]
let obj = {
original: content,
prefixed: content,
elements: []
}
for (const transformation of transformQueue) {
obj = await transformation(obj, namespaces)
}
const id = createNodeId(`${node.id}-CETEI`)
const ceteiNode = {
...obj,
id,
children: [],
parent: node.id,
internal: {
content: obj.prefixed,
contentDigest: createContentDigest({obj}),
type: `CETEI`,
},
}
createNode(ceteiNode)
createParentChildLink({ parent: node, child: ceteiNode })
}
}
function getAttributeNames(el) {
const attrs = el.attributes
const names = []
for(var i = attrs.length - 1; i >= 0; i--) {
names.push(attrs[i].name)
}
return names
}
function transform(obj, namespaces){
const dom = new DOMParser().parseFromString(obj.original, 'text/xml')
const elements = new Set()
const convertEl = (el) => {
let newElement
const ns = el.namespaceURI ? el.namespaceURI : ""
if (namespaces.hasOwnProperty(ns)) {
const prefix = namespaces[ns]
newElement = dom.createElement(`${prefix}-${el.localName}`)
elements.add(`${prefix}-${el.localName}`)
} else {
newElement = dom.importNode(el, false)
elements.add(`${prefix}-${el.localName}`)
}
// Copy attributes @xmlns, @xml:id, @xml:lang, and
// @rendition get special handling.
for (const att of Array.from(el.attributes)) {
if (att.name == "xmlns") {
//Strip default namespaces, but hang on to the values
newElement.setAttribute("data-xmlns", att.value)
} else {
newElement.setAttribute(att.name, att.value)
}
if (att.name == "xml:id") {
newElement.setAttribute("id", att.value)
}
if (att.name == "xml:lang") {
newElement.setAttribute("lang", att.value)
}
if (att.name == "rendition") {
newElement.setAttribute("class", att.value.replace(/#/g, ""))
}
}
// Preserve element name so we can use it later
newElement.setAttribute("data-origname", el.localName)
if (el.hasAttributes()) {
newElement.setAttribute("data-origatts", getAttributeNames(el).join(" "))
}
// If element is empty, flag it
if (el.childNodes.length == 0) {
newElement.setAttribute("data-empty", "")
}
// Turn <rendition scheme="css"> elements into HTML styles
if (el.localName == "tagsDecl") {
let style = dom.createElement("style")
for (let node of Array.from(el.childNodes)){
if (node.nodeType == 1 && node.localName == "rendition" && node.getAttribute("scheme") == "css") {
let rule = ""
if (node.hasAttribute("selector")) {
//rewrite element names in selectors
rule += node.getAttribute("selector").replace(/([^#, >]+\w*)/g, "tei-$1").replace(/#tei-/g, "#") + "{\n"
rule += node.textContent
} else {
rule += "." + node.getAttribute("xml:id") + "{\n"
rule += node.textContent
}
rule += "\n}\n"
style.appendChild(dom.createTextNode(rule))
}
}
if (style.childNodes.length > 0) {
newElement.appendChild(style)
}
}
for (let node of Array.from(el.childNodes)) {
if (node.nodeType == 1) {
newElement.appendChild(convertEl(node))
}
else {
newElement.appendChild(node.cloneNode())
}
}
return newElement
}
const data = convertEl(dom.documentElement)
const prefixed = new XMLSerializer().serializeToString(data)
return {
original: obj.original,
prefixed,
elements: Array.from(elements)
}
}
exports.onCreateNode = onCreateNode
exports.onPreInit = () => console.log("Loaded gatsby-transformer-ceteicean")