UNPKG

@v4fire/client

Version:

V4Fire client core library

294 lines (230 loc) • 6.16 kB
/*! * V4Fire Client Core * https://github.com/V4Fire/Client * * Released under the MIT license * https://github.com/V4Fire/Client/blob/master/LICENSE */ import { defaultWrapper } from 'core/component/const'; import { getComponentMods, isAbstractComponent } from 'core/component/reflection'; import { isTypeCanBeFunc } from 'core/component/prop'; import { wrapRender } from 'core/component/render-function'; import { inheritMeta } from 'core/component/meta/inherit'; import { addMethodsToMeta } from 'core/component/meta/method'; import type { ComponentMeta, ComponentConstructor, ComponentConstructorInfo, ComponentProp, ComponentField, WatchObject, RenderFunction } from 'core/component/interface'; /** * Creates a meta object for the specified component and returns it * @param component - component constructor info */ export function createMeta(component: ComponentConstructorInfo): ComponentMeta { const meta = { name: component.name, componentName: component.componentName, parentMeta: component.parentMeta, constructor: component.constructor, instance: {}, params: component.params, props: {}, mods: getComponentMods(component), fields: {}, tiedFields: {}, computedFields: {}, systemFields: {}, tiedSystemFields: {}, accessors: {}, methods: {}, watchers: {}, watchDependencies: new Map(), hooks: { beforeRuntime: [], beforeCreate: [], beforeDataCreate: [], created: [], beforeMount: [], mounted: [], beforeUpdate: [], beforeUpdated: [], updated: [], beforeActivated: [], activated: [], deactivated: [], beforeDestroy: [], destroyed: [], errorCaptured: [] }, component: { name: component.name, mods: {}, props: {}, methods: {}, staticRenderFns: [], render: <RenderFunction>(() => { throw new ReferenceError(`A render function for the component "${component.componentName}" is not specified`); }) } }; meta.component.render = wrapRender(meta); if (component.parentMeta) { inheritMeta(meta, component.parentMeta); } return meta; } /** * Fills a meta object with methods and properties from the specified component class * * @param meta * @param [constructor] - component constructor */ export function fillMeta( meta: ComponentMeta, constructor: ComponentConstructor = meta.constructor ): ComponentMeta { addMethodsToMeta(meta, constructor); const {component, methods, watchers, hooks} = meta; const instance = Object.cast<Dictionary>(new constructor()); meta.instance = instance; if (isAbstractComponent.test(meta.componentName)) { return meta; } const isFunctional = meta.params.functional === true; // Methods for (let o = methods, keys = Object.keys(o), i = 0; i < keys.length; i++) { const nm = keys[i], method = o[nm]; if (!method) { continue; } component.methods[nm] = method.fn; if (method.watchers) { for (let o = method.watchers, keys = Object.keys(o), i = 0; i < keys.length; i++) { const key = keys[i], watcher = <NonNullable<WatchObject>>o[key]; if (isFunctional && watcher.functional === false) { continue; } const watcherListeners = watchers[key] ?? []; watchers[key] = watcherListeners; watcherListeners.push({ ...watcher, method: nm, args: Array.concat([], watcher.args), handler: Object.cast(method.fn) }); } } // Hooks if (method.hooks) { for (let o = method.hooks, keys = Object.keys(o), i = 0; i < keys.length; i++) { const key = keys[i], watcher = o[key]; if (isFunctional && watcher.functional === false) { continue; } hooks[key].push({...watcher, fn: method.fn}); } } } // Props const defaultProps = meta.params.defaultProps !== false; for (let o = meta.props, keys = Object.keys(o), i = 0; i < keys.length; i++) { const key = keys[i], prop = <NonNullable<ComponentProp>>o[key]; let def, defWrapper, skipDefault = true; if (defaultProps || prop.forceDefault) { skipDefault = false; def = instance[key]; defWrapper = def; if (def != null && typeof def === 'object' && (!isTypeCanBeFunc(prop.type) || !Object.isFunction(def))) { defWrapper = () => Object.fastClone(def); defWrapper[defaultWrapper] = true; } } let defValue; if (!skipDefault) { defValue = prop.default !== undefined ? prop.default : defWrapper; } component.props[key] = { type: prop.type, required: prop.required !== false && defaultProps && defValue === undefined, // eslint-disable-next-line @typescript-eslint/unbound-method validator: prop.validator, functional: prop.functional, default: defValue }; if (Object.size(prop.watchers) > 0) { const watcherListeners = watchers[key] ?? []; watchers[key] = watcherListeners; for (let w = prop.watchers.values(), el = w.next(); !el.done; el = w.next()) { const watcher = el.value; if (isFunctional && watcher.functional === false) { continue; } watcherListeners.push(watcher); } } } // Fields for (let fields = [meta.systemFields, meta.fields], i = 0; i < fields.length; i++) { for (let o = fields[i], keys = Object.keys(o), j = 0; j < keys.length; j++) { const key = keys[j], field = <NonNullable<ComponentField>>o[key]; if (field.watchers) { for (let w = field.watchers.values(), el = w.next(); !el.done; el = w.next()) { const watcher = el.value; if (isFunctional && watcher.functional === false) { continue; } const watcherListeners = watchers[key] ?? []; watchers[key] = watcherListeners; watcherListeners.push(watcher); } } } } // Modifiers const {mods} = component; for (let o = meta.mods, keys = Object.keys(o), i = 0; i < keys.length; i++) { const key = keys[i], mod = o[key]; let def; if (mod) { for (let i = 0; i < mod.length; i++) { const el = mod[i]; if (Object.isArray(el)) { def = el; break; } } mods[key] = def !== undefined ? String(def[0]) : undefined; } } return meta; }