UNPKG

@merkur/plugin-component

Version:

Merkur component plugin.

193 lines (164 loc) 4.59 kB
'use strict'; var core = require('@merkur/core'); function componentPlugin() { return { async setup(widget, widgetDefinition) { let { info, bootstrap, load, mount, unmount, update, ...widgetProperties } = widgetDefinition; const lifeCycle = { info, bootstrap, load, mount, unmount, update, }; widget.$in.component = { lifeCycle, isMounted: false, isHydrated: false, suspendedTasks: [], resolvedViews: new Map(), }; core.assignMissingKeys(widget, componentAPI(), widgetProperties); widget = core.setDefaultValueForUndefined(widget, ['props', 'state']); widget = core.setDefaultValueForUndefined(widget, ['assets'], []); widget = core.setDefaultValueForUndefined(widget, ['containerSelector'], null); return widget; }, create(widget) { return widget; // @TODO bind events }, }; } async function callLifeCycleMethod(widget, methodName, args) { const { lifeCycle } = widget.$in.component; if (typeof lifeCycle[methodName] === 'function') { return lifeCycle[methodName](widget, ...args); } } function componentAPI() { return { async info(widget, ...args) { const { name, version, props, state, assets, containerSelector } = widget; const componentInfo = (await callLifeCycleMethod(widget, 'info', args)) || {}; return { name, version, props, state, assets, containerSelector, ...componentInfo, }; }, async mount(widget, ...args) { await widget.bootstrap(...args); await widget.load(...args); const html = await callLifeCycleMethod(widget, 'mount', args); widget.$in.component.isMounted = true; for (let task of widget.$in.component.suspendedTasks) { await task(); } return html; }, async unmount(widget, ...args) { widget.$in.component.isMounted = false; widget.$in.component.isHydrated = false; return callLifeCycleMethod(widget, 'unmount', args); }, async bootstrap(widget, ...args) { return callLifeCycleMethod(widget, 'bootstrap', args); }, async load(widget, ...args) { const { $in, state } = widget; if ( $in.component.isMounted === false && $in.component.isHydrated === false && state && Object.keys(state).length !== 0 ) { $in.component.isHydrated = true; return; } widget.state = await callLifeCycleMethod(widget, 'load', args); }, async update(widget, ...args) { if (!widget.$in.component.isMounted) { widget.$in.component.suspendedTasks.push(() => widget.update(...args)); return; } return callLifeCycleMethod(widget, 'update', args); }, async setState(widget, stateSetter) { widget.state = { ...widget.state, ...(typeof stateSetter === 'function' ? stateSetter(widget.state) : stateSetter), }; return widget.update(); }, async setProps(widget, propsSetter) { if (!widget.$in.component.isMounted) { widget.$in.component.suspendedTasks.push(() => widget.setProps(propsSetter), ); return; } widget.props = { ...widget.props, ...(typeof propsSetter === 'function' ? propsSetter(widget.props) : propsSetter), }; await widget.load(); return widget.update(); }, }; } /** * Typed helper to make it easier to define widget properties. * * @type import('@merkur/plugin-component').createSlotFactory */ function createSlotFactory(creator) { return async (widget) => creator(widget); } /** * Typed helper to make it easier to define widget properties. * * @type import('@merkur/plugin-component').createViewFactory */ function createViewFactory(creator) { return async (widget) => { const { slotFactories, ...restParams } = await creator(widget); if (!slotFactories) { return { ...restParams, }; } const slot = ( await Promise.all(slotFactories.map((creator) => creator(widget))) ).reduce((acc, cur) => { acc[cur.name] = cur; return acc; }, {}); return { ...restParams, slot, }; }; } exports.componentPlugin = componentPlugin; exports.createSlotFactory = createSlotFactory; exports.createViewFactory = createViewFactory;