mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
191 lines
6.7 KiB
JavaScript
191 lines
6.7 KiB
JavaScript
var componentLookup = require('../util').$__componentLookup;
|
|
var ComponentsContext = require('../ComponentsContext');
|
|
var registry = require('../registry');
|
|
var modernRenderer = require('../renderer');
|
|
var resolveComponentRef = modernRenderer.$__resolveComponentRef;
|
|
var preserveComponentEls = modernRenderer.$__preserveComponentEls;
|
|
var handleBeginAsync = modernRenderer.$__handleBeginAsync;
|
|
|
|
var WIDGETS_BEGIN_ASYNC_ADDED_KEY = '$wa';
|
|
|
|
function createRendererFunc(templateRenderFunc, componentProps) {
|
|
var typeName = componentProps.type;
|
|
var roots = componentProps.roots;
|
|
var assignedId = componentProps.id;
|
|
|
|
return function renderer(input, out, renderingLogic) {
|
|
var outGlobal = out.global;
|
|
|
|
if (!outGlobal[WIDGETS_BEGIN_ASYNC_ADDED_KEY]) {
|
|
outGlobal[WIDGETS_BEGIN_ASYNC_ADDED_KEY] = true;
|
|
out.on('beginAsync', handleBeginAsync);
|
|
}
|
|
|
|
var getInitialProps;
|
|
var getTemplateData;
|
|
var getInitialState;
|
|
var getComponentConfig;
|
|
var getInitialBody;
|
|
|
|
if (renderingLogic) {
|
|
getInitialProps = renderingLogic.getInitialProps;
|
|
getTemplateData = renderingLogic.getTemplateData;
|
|
getInitialState = renderingLogic.getInitialState;
|
|
getComponentConfig = renderingLogic.getComponentConfig;
|
|
getInitialBody = renderingLogic.getInitialBody;
|
|
}
|
|
|
|
var componentConfig;
|
|
var componentBody;
|
|
var componentState;
|
|
|
|
var component = outGlobal.$w;
|
|
var fakeComponent;
|
|
var isRerender = component !== undefined;
|
|
var id = assignedId;
|
|
var isExisting;
|
|
var customEvents;
|
|
var scope;
|
|
|
|
if (component) {
|
|
id = component.id;
|
|
isExisting = true;
|
|
outGlobal.$w = null;
|
|
} else {
|
|
var componentArgs = input && input.$w || out.data.$w;
|
|
|
|
if (componentArgs) {
|
|
scope = componentArgs[0];
|
|
|
|
if (scope) {
|
|
scope = scope.id;
|
|
}
|
|
|
|
var ref = componentArgs[1];
|
|
if (ref != null) {
|
|
ref = ref.toString();
|
|
}
|
|
id = id || resolveComponentRef(out, ref, scope);
|
|
customEvents = componentArgs[2];
|
|
delete input.$w;
|
|
}
|
|
}
|
|
|
|
var componentsContext = ComponentsContext.$__getComponentsContext(out);
|
|
id = id || componentsContext.$__nextComponentId();
|
|
|
|
if (registry.$__isServer && typeName) {
|
|
component = { id:id, typeName:typeName };
|
|
} else {
|
|
if (!component) {
|
|
if (isRerender) {
|
|
// Look in in the DOM to see if a component with the same ID and type already exists.
|
|
component = componentLookup[id];
|
|
if (component && component.$__type !== typeName) {
|
|
component = undefined;
|
|
}
|
|
}
|
|
|
|
if (component) {
|
|
isExisting = true;
|
|
} else {
|
|
isExisting = false;
|
|
// We need to create a new instance of the component
|
|
if (typeName) {
|
|
component = registry.$__createComponent(typeName, id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (input) {
|
|
if (getComponentConfig) {
|
|
// If getComponentConfig() was implemented then use that to
|
|
// get the component config. The component config will be passed
|
|
// to the component constructor. If rendered on the server the
|
|
// component config will be serialized to a JSON-like data
|
|
// structure and stored in a "data-w-config" attribute.
|
|
componentConfig = getComponentConfig(input, out);
|
|
} else {
|
|
componentConfig = input.componentConfig;
|
|
}
|
|
|
|
if (componentConfig) {
|
|
component.$c = componentConfig;
|
|
}
|
|
|
|
if (getInitialBody) {
|
|
// If we have component a component body then pass it to the template
|
|
// so that it is available to the component tag and can be inserted
|
|
// at the w-body marker
|
|
componentBody = getInitialBody(input, out);
|
|
}
|
|
|
|
// If we do not have state then we need to go through the process
|
|
// of converting the input to a component state, or simply normalizing
|
|
// the input using getInitialProps
|
|
|
|
if (getInitialProps) {
|
|
// This optional method is used to normalize input state
|
|
input = getInitialProps(input, out) || {};
|
|
}
|
|
|
|
if (getInitialState) {
|
|
// This optional method is used to derive the component state
|
|
// from the input properties
|
|
component.state = componentState = getInitialState(input, out);
|
|
}
|
|
|
|
if (!componentBody) {
|
|
// Default to using the nested content as the component body
|
|
componentBody = input.renderBody;
|
|
}
|
|
}
|
|
|
|
if (component && isExisting) {
|
|
if (!component.$__isDirty || !component.shouldUpdate(input, component.$__state)) {
|
|
preserveComponentEls(component, out, componentsContext);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!component) {
|
|
fakeComponent = {};
|
|
} else {
|
|
componentState = component.$__rawState;
|
|
}
|
|
|
|
var templateInput = getTemplateData ?
|
|
getTemplateData(componentState, input, out) :
|
|
componentState || input || {};
|
|
|
|
var componentDef = componentsContext.$__beginComponent(component || fakeComponent);
|
|
componentDef.$__customEvents = customEvents;
|
|
componentDef.$__scope = scope;
|
|
componentDef.$__roots = roots;
|
|
componentDef.$__component = fakeComponent ? null : component;
|
|
componentDef.$__isExisting = isExisting;
|
|
componentDef.b = componentBody;
|
|
componentDef.c = function(componentConfig) {
|
|
component.$c = componentConfig;
|
|
};
|
|
componentDef.t = function(typeName) {
|
|
if (typeName) {
|
|
this.$__component = component = registry.$__createComponent(typeName, fakeComponent.id);
|
|
}
|
|
};
|
|
|
|
if (component && isExisting) {
|
|
component.$__emitLifecycleEvent('$__legacyRender');
|
|
}
|
|
|
|
// Render the template associated with the component using the final template
|
|
// data that we constructed
|
|
templateRenderFunc(templateInput, out, componentDef);
|
|
|
|
componentDef.$__end();
|
|
};
|
|
}
|
|
|
|
module.exports = createRendererFunc;
|