marko
Version:
UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.
236 lines (196 loc) • 7.09 kB
JavaScript
;var copyProps = require("raptor-util/copyProps");
var beginComponent = require("@internal/components-beginComponent");
var endComponent = require("@internal/components-endComponent");
var registry = require("@internal/components-registry");
var componentsUtil = require("@internal/components-util");
var componentLookup = componentsUtil._n_;
var ComponentsContext = require("./ComponentsContext");
var getComponentsContext = ComponentsContext.T_;
var isServer = componentsUtil._L_ === true;
var COMPONENT_BEGIN_ASYNC_ADDED_KEY = "$wa";
function resolveComponentKey(key, parentComponentDef) {
if (key[0] === "#") {
return key.substring(1);
} else {
return parentComponentDef.id + "-" + parentComponentDef.aP_(key);
}
}
function trackAsyncComponents(out) {
if (out.isSync() || out.global[COMPONENT_BEGIN_ASYNC_ADDED_KEY]) {
return;
}
out.on("beginAsync", handleBeginAsync);
out.on("beginDetachedAsync", handleBeginDetachedAsync);
out.global[COMPONENT_BEGIN_ASYNC_ADDED_KEY] = true;
}
function handleBeginAsync(event) {
var parentOut = event.parentOut;
var asyncOut = event.out;
var componentsContext = parentOut.b_;
if (componentsContext !== undefined) {
// We are going to start a nested ComponentsContext
asyncOut.b_ = new ComponentsContext(asyncOut, componentsContext);
}
// Carry along the component arguments
asyncOut.c(
parentOut.ab_,
parentOut.ac_,
parentOut.be_
);
}
function handleBeginDetachedAsync(event) {
var asyncOut = event.out;
handleBeginAsync(event);
asyncOut.on("beginAsync", handleBeginAsync);
asyncOut.on("beginDetachedAsync", handleBeginDetachedAsync);
}
function createRendererFunc(
templateRenderFunc,
componentProps,
renderingLogic)
{
var onInput = renderingLogic && renderingLogic.onInput;
var typeName = componentProps.t;
var isSplit = componentProps.s === true;
var isImplicitComponent = componentProps.i === true;
var shouldApplySplitMixins = renderingLogic && isSplit;
// eslint-disable-next-line no-constant-condition
if (componentProps.d) {
throw new Error("Runtime/NODE_ENV Mismatch");
}
return function renderer(input, out) {
trackAsyncComponents(out);
var componentsContext = getComponentsContext(out);
var globalComponentsContext = componentsContext.p_;
var component = globalComponentsContext.aB_;
var isRerender = component !== undefined;
var id;
var isExisting;
var customEvents;
var parentComponentDef = componentsContext.o_;
var ownerComponentDef = out.ab_;
var ownerComponentId = ownerComponentDef && ownerComponentDef.id;
var key = out.ac_;
if (component) {
// If component is provided then we are currently rendering
// the top-level UI component as part of a re-render
id = component.id; // We will use the ID of the component being re-rendered
isExisting = true; // This is a re-render so we know the component is already in the DOM
globalComponentsContext.aB_ = null;
} else {
// Otherwise, we are rendering a nested UI component. We will need
// to match up the UI component with the component already in the
// DOM (if any) so we will need to resolve the component ID from
// the assigned key. We also need to handle any custom event bindings
// that were provided.
if (parentComponentDef) {
// console.log('componentArgs:', componentArgs);
customEvents = out.be_;
if (key != null) {
id = resolveComponentKey(key.toString(), parentComponentDef);
} else {
id = parentComponentDef.aQ_();
}
} else {
id = globalComponentsContext.aQ_();
}
}
if (isServer) {
// If we are rendering on the server then things are simplier since
// we don't need to match up the UI component with a previously
// rendered component already mounted to the DOM. We also create
// a lightweight ServerComponent
component = registry._I_(
renderingLogic,
id,
input,
out,
typeName,
customEvents,
ownerComponentId
);
// This is the final input after running the lifecycle methods.
// We will be passing the input to the template for the `input` param
input = component._b_;
} else {
if (!component) {
if (
isRerender && (
component = componentLookup[id]) &&
component._s_ !== typeName)
{
// Destroy the existing component since
component.destroy();
component = undefined;
}
if (component) {
isExisting = true;
} else {
isExisting = false;
// We need to create a new instance of the component
component = registry._I_(typeName, id);
if (shouldApplySplitMixins === true) {
shouldApplySplitMixins = false;
var renderingLogicProps =
typeof renderingLogic == "function" ?
renderingLogic.prototype :
renderingLogic;
copyProps(renderingLogicProps, component.constructor.prototype);
}
}
// Set this flag to prevent the component from being queued for update
// based on the new input. The component is about to be rerendered
// so we don't want to queue it up as a result of calling `setInput()`
component._c_ = true;
if (customEvents) {
component.aF_(customEvents, ownerComponentId);
}
if (isExisting === false) {
component.aH_(input, out);
}
input = component._k_(input, onInput, out);
if (isExisting === true) {
if (
component.aw_ === false ||
component.shouldUpdate(input, component.A_) === false)
{
// We put a placeholder element in the output stream to ensure that the existing
// DOM node is matched up correctly when using morphdom. We flag the VElement
// node to track that it is a preserve marker
out.bf_(component);
globalComponentsContext.q_[id] = true;
component._v_(); // The component is no longer dirty so reset internal flags
return;
}
}
}
component.am_ = out.global;
component.aI_(out);
}
var componentDef = beginComponent(
componentsContext,
component,
key,
ownerComponentDef,
isSplit,
isImplicitComponent
);
componentDef._w_ = isExisting;
// Render the template associated with the component using the final template
// data that we constructed
templateRenderFunc(
input,
out,
componentDef,
component,
component.aE_,
out.global
);
endComponent(out, componentDef);
componentsContext.o_ = parentComponentDef;
};
}
module.exports = createRendererFunc;
// exports used by the legacy renderer
createRendererFunc.aZ_ = resolveComponentKey;
createRendererFunc.bc_ = trackAsyncComponents;