UNPKG

docxml

Version:

TypeScript (component) library for building and parsing a DOCX file

76 lines (75 loc) 3.32 kB
import { Component, isComponentDefinition, } from '../classes/Component.js'; import { Text } from '../components/Text.js'; /** * The JSX pragma with which you can write `<Paragraph>` instead of `new Paragraph({})`. * * Also exposed as the `jsx` prop on the (static) class as well as instance of this library's top- * level API -- see also {@link Api}. */ export async function jsx(component, props, ...children) { const flattenedChildren = await children // Flatten the children, which may themselves have been wrapped in an array because they // contained invalid children. // Moreover, any component might at this point still be only the promise thereof. Resolve all. .reduce(async function flatten(flatPromise, childPromise) { const child = await childPromise; const flat = await flatPromise; return Array.isArray(child) ? [...flat, ...(await child.reduce(flatten, Promise.resolve([])))] : [...flat, child]; }, Promise.resolve([])); return (flattenedChildren // Add the node, if it is valid, or add the node split into pieces with the invalid children // vertically inserted between .reduce((nodes, child) => { if (typeof child === 'string' && isComponentDefinition(component) && !component.mixed) { child = new Text({}, child); } const isValid = !isComponentDefinition(component) || (component.mixed && typeof child === 'string') || component.children.includes(child.constructor.name); if (!isValid) { if (child.constructor === Text && component === Text) { Object.assign(child.props, props); } nodes.push(child); } else { const lastQueuedItem = nodes[nodes.length - 1]; if (typeof lastQueuedItem === 'string' || lastQueuedItem instanceof Component) { // Queue this item as a simple object, so that its children can be changed // in the next iteration. nodes.push({ component, props, children: [child], }); } else { lastQueuedItem.children.push(child); } } return nodes; }, [{ component, props, children: [] }]) // Instantiate the "queued" items (props/children that haven't been instantiated yet so that // their children could be shuffled around). .map((node) => { if (typeof node === 'string') { return node; } if (node instanceof Component) { return node; } if (isComponentDefinition(component)) { return new component(node.props || {}, ...(node.children || [])); } else { const x = component({ ...props, children: node.children || [] }); return x; } }) // Flatten again, no telling what came out of a ComponentFunction .reduce((flat, thing) => (Array.isArray(thing) ? [...flat, ...thing] : [...flat, thing]), []) // Remove empty Text components, they don't do anything .filter((node) => !(node.constructor === Text && !node.children.length))); }