@adpt/core
Version:
AdaptJS core library
218 lines • 7.94 kB
JavaScript
;
/*
* Copyright 2018-2019 Unbounded Systems, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const ld = tslib_1.__importStar(require("lodash"));
const util = tslib_1.__importStar(require("util"));
const xmlbuilder = tslib_1.__importStar(require("xmlbuilder"));
const error_1 = require("./error");
const jsx_1 = require("./jsx");
const reanimate_1 = require("./reanimate");
const defaultSerializeOptions = {
reanimateable: false,
props: "all",
};
function serializeAny(val, reanimateable) {
return JSON.stringify(val, serializeSpecials(reanimateable), 2);
}
function serializeSpecials(reanimateable) {
return function (key, value) {
return value;
};
}
function serializedShortPropIsString(propVal) {
return !(/^\d/.test(propVal));
}
function canBeShort(propName, propVal) {
if (propName === "xmlns" || propName.startsWith("xmlns:"))
return false;
if (ld.isNumber(propVal))
return true;
if (ld.isString(propVal)) {
const json = JSON.stringify(propVal);
return (json.length < 10) && serializedShortPropIsString(json.slice(1, -1));
}
return false;
}
function serializeShortPropVal(propVal) {
const long = serializeLongPropVal(propVal, false, false);
if (ld.isString(long) && ld.isString(propVal)) {
return long.slice(1, -1);
}
return long;
}
function serializeLongPropVal(propVal, pretty = true, reanimateable = true) {
const json = JSON.stringify(propVal, serializeSpecials(reanimateable), pretty ? 2 : undefined);
if (json != null)
return json;
return propVal.toString();
}
function collectProps(elem, options) {
const props = elem.props;
const shortProps = {};
let longProps = null;
let propNames;
switch (options.props) {
case "all":
propNames = Object.keys(props).sort();
break;
case "none":
propNames = [];
break;
default:
if (!Array.isArray(options.props)) {
throw new error_1.InternalError(`Invalid value '${options.props}' for options.props`);
}
propNames = options.props;
}
for (const propName of propNames) {
if (propName === "children" || propName === "handle")
continue;
const prop = props[propName];
if (prop === undefined)
continue;
if (canBeShort(propName, prop)) {
shortProps[propName] = serializeShortPropVal(prop);
}
else {
if (longProps == null) {
longProps = {};
}
longProps[propName] = serializeLongPropVal(prop, true, options.reanimateable);
}
}
return { shortProps, longProps };
}
function addPropsNode(node, props) {
const propsNode = node.ele("__props__", {});
for (const propName in props) {
if (!props.hasOwnProperty(propName))
continue;
const prop = props[propName];
propsNode.ele("prop", { name: propName }, prop);
}
}
function serializeChildren(context, node, children, options) {
for (const child of children) {
switch (true) {
case jsx_1.isElement(child):
serializeElement(context, node, child, options);
break;
default:
const serChild = serializeAny(child, options.reanimateable);
if (serChild == null) {
node.ele("typescript", {}).cdata(child.toString());
}
else {
node.ele("json", {}, serChild);
}
}
}
}
function serializeChildrenFromElem(context, node, elem, options) {
const children = jsx_1.childrenToArray(elem.props.children);
serializeChildren(context, node, children, options);
}
function getUrn(elem) {
if (!jsx_1.isComponentElement(elem)) {
throw new Error(`Unable to create reanimateable representation of ` +
`'${elem.componentName}' because it doesn't extend ` +
`Adapt.Component`);
}
try {
return reanimate_1.findMummyUrn(elem.componentType);
}
catch ( /**/_a) { /**/ }
// Ensure component is registered by constructing one
try {
new elem.componentType({});
}
catch ( /**/_b) { /**/ }
return reanimate_1.findMummyUrn(elem.componentType);
}
function serializeBuildData(context, parent, elem, options) {
const bdNode = parent.ele("buildData", {});
const succ = elem.buildData.successor;
const origChildren = elem.buildData.origChildren;
if (succ !== undefined) {
const isNull = succ === null;
const succNode = bdNode.ele("successor", { isNull });
//We can just serialize here because only an element or its successor can appear in a dom, not both
if (succ !== null)
serializeElement(context, succNode, succ, options);
}
if (origChildren !== undefined) {
const origChildrenNode = bdNode.ele("origChildren", {});
serializeChildren(context, origChildrenNode, origChildren, options);
}
}
function addLifecycleNode(context, parent, elem, options) {
if (!jsx_1.isElementImpl(elem))
throw new Error(`Element not an ElementImpl: ${util.inspect(elem)}`);
const lcNode = parent.ele("__lifecycle__", {});
lcNode.ele("field", { name: "stateNamespace" }, JSON.stringify(elem.stateNamespace));
lcNode.ele("field", { name: "keyPath" }, JSON.stringify(elem.keyPath));
lcNode.ele("field", { name: "path" }, JSON.stringify(elem.path));
if ("Enable this when we figure out how to serialize and reanimate SFCs".length === 0) {
serializeBuildData(context, lcNode, elem, options);
}
}
function serializeElement(context, parent, elem, options) {
if (context.serializedElements.has(elem) && jsx_1.isMountedElement(elem)) {
parent.ele("__elementRef__", { ref: elem.id });
return;
}
const { shortProps, longProps } = collectProps(elem, options);
let node;
if (options.reanimateable) {
const urn = getUrn(elem);
node = parent.ele(elem.componentName, Object.assign({}, shortProps, { xmlns: urn }));
}
else {
node = parent.ele(elem.componentName, shortProps);
}
if (longProps != null) {
addPropsNode(node, longProps);
}
serializeChildrenFromElem(context, node, elem, options);
if (jsx_1.isMountedElement(elem) && options.reanimateable) {
context.work.push(() => addLifecycleNode(context, node, elem, options));
}
}
function serializeDom(root, options = {}) {
const opts = Object.assign({}, defaultSerializeOptions, options);
if (opts.reanimateable && opts.props !== "all") {
throw new Error(`Invalid options for serializeDom: props must be "all" when reanimateable is true`);
}
const context = {
serializedElements: new Set(),
work: []
};
const doc = xmlbuilder.create("Adapt", { headless: true });
if (root != null)
serializeElement(context, doc, root, opts);
while (context.work.length > 0) {
const toDo = context.work.shift();
if (toDo)
toDo();
}
return doc.end({
pretty: true
}) + "\n";
}
exports.serializeDom = serializeDom;
//# sourceMappingURL=dom_serialize.js.map