UNPKG

mobx-roof

Version:

Simple React data management by mobx.

168 lines (163 loc) 5.23 kB
import { compose, isRegExp } from '../common/utils'; import { autorun } from 'mobx'; const emptyFn = () => {}; function spliter(target, keys, fn) { if (keys.length === 0) return fn(target); return target.split(keys[0]).map(item => spliter(item, keys.slice(1), fn)).filter(item => item); } function isActionKey(key) { return key && key.split('.').length === 2; } function checkFilters(filters) { if (typeof filters === 'object') { Object.keys(filters).forEach((key) => { const filter = filters[key]; if (typeof filter !== 'function') { throw new TypeError(`[MobxRelation] filters "${key}" must be a function`); } }); } else { throw new TypeError('[MobxRelation] filters must be an Object.'); } } /** * regexp support */ export default class MobxRelation { constructor(opts = {}) { this._relations = []; this._filters = {}; this._inits = []; this._autoruns = []; this.addFilters(opts.filters); } addFilters(filters = {}) { checkFilters(filters); this._filters = { ...this._filters, ...filters }; } init(initFn) { if (typeof initFn === 'function') { this._inits.push(initFn); } else { throw new Error(`[MobxRelation] Relation init need a function but get ${typeof initFn}.`); } } use(...args) { args.forEach((fn) => { if (typeof fn === 'function') { fn(this); } else { throw new Error(`[MobxRelation] relation.use need functions but get ${typeof fn}.`); } }); } autorun(autorun) { if (typeof autorun === 'function') { this._autoruns.push(autorun); } else { throw new Error('[MobxRelation] Relation autorun need a function.'); } } triggerAutorun(context) { this._autoruns.forEach(fn => { autorun(fn.bind(null, context.data)); }); } triggerInit(context) { this._inits.forEach(fn => fn(context.data)); } listen(patterns, fn, errorFn) { if (typeof patterns === 'string') { patterns = patterns .split(/\r?\n/) // filter "#..." comments .filter(item => item && !/^\s*#.*$/.test(item)).join('') .replace(/\s*/g, '') .split(';') .filter(item => item); if (patterns.length === 0) { throw new Error('[MobxRelation] Relation pattern can not be empty.'); } patterns.forEach(pattern => this._addRelation(pattern, fn, errorFn)); } else if (isRegExp(patterns)) { this._addRelation(patterns, fn, errorFn); } else { throw new Error('[MobxRelation] Listen pattern must be a String or RegExp.'); } return this; } execInMiddleware({ fullname, payload, context }) { context = { ...context.data }; this._relations.forEach(({ pattern, fn, errorFn }) => { let chain = []; if ((!isRegExp(pattern.action) && fullname !== pattern.action) || (isRegExp(pattern.action) && !pattern.action.test(fullname))) return; try { pattern.chain.forEach((item, index) => { chain = chain.concat(item); if (pattern.chain.length - 1 !== index) { chain.push(emptyFn); } }); chain = chain.map((key) => { if (typeof key === 'string') { if (isActionKey(key)) { const [name, action] = key.split('.'); const model = context[name]; if (model && model[action]) { return model[action].bind(model); } throw new Error(`[MobxRelation] Action "${key}" is not defined.`); } return this._filters[key]; } return key; }); compose(chain, payload) .then(res => fn({ context, payload: res, action: fullname })) .catch(e => errorFn({ context, payload: e, action: fullname })); } catch (e) { errorFn({ context, payload: e, action: fullname }); } }); } parsePattern(pattern) { if (isRegExp(pattern)) { return { action: pattern, refs: [], chain: [] }; } pattern = pattern.replace(/\s*/g, ''); if (!pattern) { throw new Error(`[MobxRelation] Relation pattern can not be empty.`); } if (!/^[\#\-\>\=\.a-zA-Z_0-9\|]+$/.test(pattern)) { throw new Error(`[MobxRelation] Relation pattern "${pattern}" illegal.`); } const refs = []; const chain = spliter(pattern, ['->', /\=\>|\|/], (key) => { if (isActionKey(key)) { const modelName = key.split('.')[0]; if (!refs.includes(modelName)) refs.push(modelName); } else if (key && !this._filters[key]) { throw new Error(`[MobxRelation] Undefined filter "${key}"`); } return key; }).filter(item => item.length !== 0); const action = chain[0][0]; if (!action || !isActionKey(action)) { throw new Error('[MobxRelation] Relation pattern need an dispatcher action.'); } chain[0] = chain[0].slice(1); return { action, refs, chain, }; } _addRelation(pattern, fn, errorFn) { pattern = this.parsePattern(pattern); this._relations.push({ pattern, fn: fn || emptyFn, errorFn: errorFn || function ({ payload }) { throw payload; }, }); } }