|
| 1 | +//@ts-nocheck |
| 2 | + |
| 3 | +import { |
| 4 | + ELEMENT, |
| 5 | + Node, |
| 6 | + Comment, |
| 7 | + DocumentType, |
| 8 | + Text, |
| 9 | + Fragment, |
| 10 | + Element, |
| 11 | + Component, |
| 12 | + fromJSON, |
| 13 | + props, |
| 14 | +} from '../dom/ish.js'; |
| 15 | + |
| 16 | +import { |
| 17 | + COMPONENT, |
| 18 | + DOTS, |
| 19 | + KEY, |
| 20 | + HOLE, |
| 21 | + update, |
| 22 | +} from './update.js'; |
| 23 | + |
| 24 | +import parser from '../parser/index.js'; |
| 25 | +import resolve from '../json/resolve.js'; |
| 26 | +import { assign } from '../utils.js'; |
| 27 | + |
| 28 | +const textParser = parser({ |
| 29 | + Comment, |
| 30 | + DocumentType, |
| 31 | + Text, |
| 32 | + Fragment, |
| 33 | + Element, |
| 34 | + Component, |
| 35 | + update, |
| 36 | +}); |
| 37 | + |
| 38 | +const { stringify, parse } = JSON; |
| 39 | +const isNode = node => node instanceof Node; |
| 40 | +const get = node => node.props === props ? (node.props = props) : node.props; |
| 41 | + |
| 42 | +export default (jsx, jsxs = jsx) => { |
| 43 | + const twm = new WeakMap; |
| 44 | + const cache = (template, values) => { |
| 45 | + const parsed = textParser(template, values, true); |
| 46 | + parsed[0] = parse(stringify(parsed[0])); |
| 47 | + twm.set(template, parsed); |
| 48 | + return parsed; |
| 49 | + }; |
| 50 | + |
| 51 | + const getProps = (node) => { |
| 52 | + const { children } = node; |
| 53 | + if (children.length) get(node).children = children.map(getValue); |
| 54 | + return get(node); |
| 55 | + }; |
| 56 | + |
| 57 | + const getValue = node => { |
| 58 | + if (isNode(node)) { |
| 59 | + return node.type === ELEMENT ? |
| 60 | + getInvoke(node)(node.name, getProps(node)) : |
| 61 | + node.toString() |
| 62 | + ; |
| 63 | + } |
| 64 | + return node; |
| 65 | + }; |
| 66 | + |
| 67 | + const getInvoke = ({ children }) => (jsx === jsxs || children.every(isNode)) ? jsx : jsxs; |
| 68 | + |
| 69 | + return (template, ...values) => { |
| 70 | + const [json, updates] = twm.get(template) || cache(template, values); |
| 71 | + // TODO: this could be mapped once so that every consecutive call |
| 72 | + // will simply loop over values and updates[length](values[length]) |
| 73 | + // before returning the list or arguments to pass to jsx or jsxs |
| 74 | + // this way it'd be way faster on repeated invokes, if needed/desired |
| 75 | + const root = fromJSON(json); |
| 76 | + let length = values.length, args, prev, node; |
| 77 | + while (length--) { |
| 78 | + const [path, type] = updates[length]; |
| 79 | + const value = values[length]; |
| 80 | + if (prev !== path) { |
| 81 | + node = resolve(root, path); |
| 82 | + prev = path; |
| 83 | + args = [node.name, getProps(node)]; |
| 84 | + } |
| 85 | + if (type === COMPONENT) { |
| 86 | + args[0] = value; |
| 87 | + const { children } = node.parent; |
| 88 | + children[children.indexOf(node)] = getInvoke(node)(...args); |
| 89 | + } |
| 90 | + else if (type === KEY) args.push(value); |
| 91 | + else if (type === DOTS) assign(get(node), value); |
| 92 | + else if (type === HOLE) { |
| 93 | + const { children } = node.parent; |
| 94 | + children[children.indexOf(node)] = value; |
| 95 | + } |
| 96 | + else get(node)[type] = value; |
| 97 | + } |
| 98 | + return getValue(root.children[0]); |
| 99 | + }; |
| 100 | +}; |
0 commit comments