Created
January 5, 2018 13:10
-
-
Save noobymatze/faf408872cd3aaee2073f468adf8f302 to your computer and use it in GitHub Desktop.
Add some types to allow rendering FOP Xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* `Xml` describes a simple XML document, specific to FOP. | |
* | |
* This type is used as the result for TSX, which allows writing | |
* Xml nodes in TypeScript. Here is an example for valid TypeScript | |
* code, using this library (provided you saved it in a file ending | |
* with .tsx, for example person.tsx): | |
* | |
* ``` | |
* // person.tsx | |
* import { Xml, node } from 'fop-xml'; | |
* | |
* interface Person { | |
* firstName: string; | |
* lastName: string; | |
* } | |
* | |
* const render = (person: Person): Xml => ( | |
* <root> | |
* <layout-master-set master-name="standard" page-width="> | |
* `${person.firstName} ${person.lastName}` | |
* </layout-master-set> | |
* </root> | |
* ) | |
* | |
* render({firstName: 'Matthias', lastName: 'Metzger'}); | |
* ``` | |
* | |
* Now compile with: | |
* | |
* ``` | |
* $ tsc --jsx react --jsxFactory "node" person.tsx | |
* ``` | |
* | |
* In a bigger project, it is probably valuable to add a | |
* `tsconfig.json`. | |
*/ | |
export type Xml | |
= { type: 'node', name: string, attributes: {}, children: Array<Xml | string>} | |
| { type: 'text', value: string } | |
| { type: 'raw', value: string }; | |
/** | |
* Create a new node, with the given name, attributes and children. | |
* | |
* *Note:* This function should be declared in the tsconfig.json as | |
* `jsxFactory` to render FOP and will seldomly be used directly. | |
* | |
* @param name a name of a tag | |
* @param attributes all attributes of this tag | |
* @param children potential children of this tag | |
*/ | |
export function node(name: string, attributes: {}, ...children: Array<Xml | string>) { | |
return { | |
type: 'node', | |
name, | |
attributes, | |
children | |
}; | |
} | |
/** | |
* A simple single text, which will be escaped. | |
* | |
* @param value the actual value | |
*/ | |
export function text(value: string): Xml { | |
return { | |
type: 'text', | |
value | |
}; | |
} | |
/** | |
* A simple single text, which will *not* be escaped. | |
* | |
* *Note:* You should only use this, if you know, what you | |
* are doing. The lengthy name exists for a reason. | |
* | |
* @param value the actual value | |
* @return an `Xml` structure | |
*/ | |
export function dangerouslyUnescapedText(value: string): Xml { | |
return { | |
type: 'raw', | |
value | |
}; | |
} | |
// RENDER | |
/** | |
* Render the given `Xml` structure to a string. | |
* | |
* @param xml an `Xml` value | |
* @return a string | |
*/ | |
export function renderToString(xml: Xml | string): string { | |
if (typeof xml === 'string') { | |
return escape(xml) | |
} | |
switch (xml.type) { | |
case 'text': | |
return escape(xml.value); | |
case 'raw': | |
return xml.value; | |
case 'node': | |
return xml.name === 'root' ? | |
`<?xml version="1.0" encoding="UTF-8" ?>${renderNode( | |
xml.name, | |
{ ...xml.attributes, | |
// We don't want the user to be able to specify the | |
// following attributes, since we would need to make | |
// the `fo:` prefix in the resulting XML configurable. | |
// Therefore override, if they exist, add otherwise. | |
"xml:fo": "http://www.w3.org/1999/XSL/Format", | |
"xmlns:xs": "http://www.w3.org/2001/XMLSchema", | |
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", | |
}, | |
xml.children | |
)}` : | |
renderNode(xml.name, xml.attributes, xml.children) | |
} | |
} | |
function renderNode(name: string, attributes: {}, children: Array<Xml | string>): string { | |
const attrs = Object | |
.keys(attributes) | |
.map(a => ` ${a}="${escape(attributes[a])}"`) | |
.join(''); | |
const kids = children | |
.map(node => renderToString(node)) | |
.join(''); | |
return `<fo:${name}${attrs}>${kids}</fo:${name}>`; | |
} | |
/** | |
* Escape the given value. | |
* | |
* There are some sensitive characters in any XML | |
* document. Therefore, those will be escaped here. | |
* | |
* @param value any unescaped string | |
* @return an escaped string | |
*/ | |
function escape(value: string): string { | |
return value | |
.replace('"', '"') | |
.replace("'", ''') | |
.replace('<', '<') | |
.replace('>', '>') | |
.replace('&', '&'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment