mirror of
https://github.com/marko-js/marko.git
synced 2026-01-18 14:55:13 +00:00
* refactor out taglib loader/finder/lookup * add comments for taglib apis that we need to deprecate * move components into runtime/core-tags
235 lines
8.5 KiB
JavaScript
235 lines
8.5 KiB
JavaScript
var componentsUtil = require("./util");
|
|
var componentLookup = componentsUtil.___componentLookup;
|
|
var emitLifecycleEvent = componentsUtil.___emitLifecycleEvent;
|
|
|
|
var ComponentsContext = require("./ComponentsContext");
|
|
var getComponentsContext = ComponentsContext.___getComponentsContext;
|
|
var registry = require("./registry");
|
|
var copyProps = require("raptor-util/copyProps");
|
|
var isServer = componentsUtil.___isServer === true;
|
|
var beginComponent = require("./beginComponent");
|
|
var endComponent = require("./endComponent");
|
|
|
|
var COMPONENT_BEGIN_ASYNC_ADDED_KEY = "$wa";
|
|
|
|
function resolveComponentKey(key, parentComponentDef) {
|
|
if (key[0] === "#") {
|
|
return key.substring(1);
|
|
} else {
|
|
return parentComponentDef.id + "-" + parentComponentDef.___nextKey(key);
|
|
}
|
|
}
|
|
|
|
function handleBeginAsync(event) {
|
|
var parentOut = event.parentOut;
|
|
var asyncOut = event.out;
|
|
var componentsContext = parentOut.___components;
|
|
|
|
if (componentsContext !== undefined) {
|
|
// We are going to start a nested ComponentsContext
|
|
asyncOut.___components = new ComponentsContext(
|
|
asyncOut,
|
|
componentsContext
|
|
);
|
|
}
|
|
// Carry along the component arguments
|
|
asyncOut.c(
|
|
parentOut.___assignedComponentDef,
|
|
parentOut.___assignedKey,
|
|
parentOut.___assignedCustomEvents
|
|
);
|
|
}
|
|
|
|
function createRendererFunc(
|
|
templateRenderFunc,
|
|
componentProps,
|
|
renderingLogic
|
|
) {
|
|
renderingLogic = renderingLogic || {};
|
|
var onInput = renderingLogic.onInput;
|
|
var typeName = componentProps.___type;
|
|
var isSplit = componentProps.___split === true;
|
|
var isImplicitComponent = componentProps.___implicit === true;
|
|
|
|
var shouldApplySplitMixins = isSplit;
|
|
|
|
return function renderer(input, out) {
|
|
var outGlobal = out.global;
|
|
|
|
if (out.isSync() === false) {
|
|
if (!outGlobal[COMPONENT_BEGIN_ASYNC_ADDED_KEY]) {
|
|
outGlobal[COMPONENT_BEGIN_ASYNC_ADDED_KEY] = true;
|
|
out.on("beginAsync", handleBeginAsync);
|
|
}
|
|
}
|
|
|
|
var componentsContext = getComponentsContext(out);
|
|
var globalComponentsContext = componentsContext.___globalContext;
|
|
|
|
var component = globalComponentsContext.___rerenderComponent;
|
|
var isRerender = component !== undefined;
|
|
var id;
|
|
var isExisting;
|
|
var customEvents;
|
|
var parentComponentDef = componentsContext.___componentDef;
|
|
var ownerComponentDef = out.___assignedComponentDef;
|
|
var ownerComponentId = ownerComponentDef && ownerComponentDef.id;
|
|
var key = out.___assignedKey;
|
|
|
|
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.___rerenderComponent = 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.___assignedCustomEvents;
|
|
|
|
if (key != null) {
|
|
id = resolveComponentKey(
|
|
key.toString(),
|
|
parentComponentDef
|
|
);
|
|
} else {
|
|
id = parentComponentDef.___nextComponentId();
|
|
}
|
|
} else {
|
|
id = globalComponentsContext.___nextComponentId();
|
|
}
|
|
}
|
|
|
|
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.___createComponent(
|
|
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.___updatedInput;
|
|
|
|
component.___updatedInput = undefined; // We don't want ___updatedInput to be serialized to the browser
|
|
} else {
|
|
if (!component) {
|
|
if (
|
|
isRerender &&
|
|
(component = componentLookup[id]) &&
|
|
component.___type !== 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.___createComponent(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.___updateQueued = true;
|
|
|
|
if (customEvents !== undefined) {
|
|
component.___setCustomEvents(
|
|
customEvents,
|
|
ownerComponentId
|
|
);
|
|
}
|
|
|
|
if (isExisting === false) {
|
|
emitLifecycleEvent(component, "create", input, out);
|
|
}
|
|
|
|
input = component.___setInput(input, onInput, out);
|
|
|
|
if (isExisting === true) {
|
|
if (
|
|
component.___isDirty === false ||
|
|
component.shouldUpdate(input, component.___state) ===
|
|
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.___preserveComponent(component);
|
|
globalComponentsContext.___renderedComponentsById[
|
|
id
|
|
] = true;
|
|
component.___reset(); // The component is no longer dirty so reset internal flags
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
component.___global = outGlobal;
|
|
|
|
emitLifecycleEvent(component, "render", out);
|
|
}
|
|
|
|
var componentDef = beginComponent(
|
|
componentsContext,
|
|
component,
|
|
key,
|
|
ownerComponentDef,
|
|
isSplit,
|
|
isImplicitComponent
|
|
);
|
|
|
|
componentDef.___isExisting = isExisting;
|
|
|
|
// Render the template associated with the component using the final template
|
|
// data that we constructed
|
|
templateRenderFunc(
|
|
input,
|
|
out,
|
|
componentDef,
|
|
component,
|
|
component.___rawState
|
|
);
|
|
|
|
endComponent(out, componentDef);
|
|
componentsContext.___componentDef = parentComponentDef;
|
|
};
|
|
}
|
|
|
|
module.exports = createRendererFunc;
|
|
|
|
// exports used by the legacy renderer
|
|
createRendererFunc.___resolveComponentKey = resolveComponentKey;
|
|
createRendererFunc.___handleBeginAsync = handleBeginAsync;
|