@v4fire/client
Version:
V4Fire client core library
294 lines (230 loc) • 6.16 kB
text/typescript
/*!
* 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;
}