UNPKG

marko

Version:

UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.

236 lines (196 loc) • 7.09 kB
"use strict";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;