What is Module Monkey

For the built-in modules of CabloyJS and the modules provided by the third party, if we want to modify some pages, functions and logic provided by these modules, there are generally two methods:

  1. Fork Branch: If the module provides the source code, we can fork a branch and modify it on this branch
  2. Module Monkey: Using the module monkey mechanism provided by CabloyJS, it’s easy to replace some functionalities of modules just like a monkey🐒

Next, we use the module test-partymonkey-monkey to monkey another module test-party

Create a Module Monkey

To demonstrate, when a new project is created, the module test-partymonkey-monkey will be automatically added to the project. If you want to create a new module monkey, you can do the following:

$ npm init cabloy src/module/test-partymonkey-monkey --type=module

Module Suffix: -monkey

  • The module must be suffixed with -monkey to inform the system that this module is a monkey

Monkey at Frontend

Through the monkey mechanism, you can replace the Page Routes, Vuex Store, Config and Components at frontend, etc.

Injection of Monkey

src/module/test-partymonkey-monkey/front/src/main.js

  return cb({
    ...
    monkey: require('./monkey.js').default(Vue),
  });

Definition of Monkey

src/module/test-partymonkey-monkey/front/src/monkey.js

import monkeyerPage from './pages/monkeyer.vue';
import monkeyerComponent from './components/monkeyerComponent.vue';

// eslint-disable-next-line
export default function(Vue) {

  function monkeyRoute(moduleSelf, module, routePath, routeComponent) {
    const route = module.options.routes.find(item => item.path === routePath);
    if (route) {
      route.module = moduleSelf;
      route.component = routeComponent;
    }
  }

  function monkeyStore(moduleSelf, module) {
    const store = module.options.store;
    // monkey getters: message2
    const _message2 = store.getters.message2;
    store.getters.message2 = function(state) {
      const res = _message2(state);
      console.log('monkey-store message2:', res);
      return res;
    };
    // monkey mutations: setMessage
    const _setMessage = store.mutations.setMessage;
    store.mutations.setMessage = function(state, message) {
      _setMessage(state, message);
      console.log('monkey-store setMessage:', state.message);
    };
  }

  function monkeyConfig(moduleSelf, module) {
    const config = module.options.config;
    config.monkeyed = true;
  }

  function monkeyComponent(moduleSelf, module, componentName, component) {
    component.module = moduleSelf;
    module.options.components[componentName] = component;
  }

  return {
    moduleLoaded({ module }) {
      if (module.name !== 'test-party') return;
      const moduleSelf = Vue.prototype.$meta.module.get('test-partymonkey');
      // route
      monkeyRoute(moduleSelf, module, 'kitchen-sink/monkey/monkeyee', monkeyerPage);
      // store
      monkeyStore(moduleSelf, module);
      // config
      monkeyConfig(moduleSelf, module);
      // component
      monkeyComponent(moduleSelf, module, 'monkeyeeComponent', monkeyerComponent);
    },
  };

}

Monkey at Backend

Through the monkey mechanism, you can replace the API Routes, and Config at backend, etc.

Injection of Monkey

src/module/test-partymonkey-monkey/backend/src/main.js

  ...
  // monkey
  const monkey = require('./monkey.js')(app);

  return {
    ...
    monkey,
  };

Definition of Monkey

src/module/test-partymonkey-monkey/backend/src/monkey.js

const controllerMonkeyer = require('./controller/monkeyer.js');

module.exports = app => {
  // eslint-disable-next-line
  const moduleInfo = app.meta.mockUtil.parseInfoFromPackage(__dirname);

  function monkeyRoute(module, routePath, routeController) {
    const route = module.main.routes.find(item => item.path === routePath);
    if (route) {
      route.controller = routeController;
    }
  }

  function monkeyConfig(module, config) {
    config.monkeyed = true;
  }

  const monkey = {
    moduleLoaded({ module }) {
      if (module.info.relativeName !== 'test-party') return;
      // route
      monkeyRoute(module, 'test/monkey/monkeyee/test', controllerMonkeyer);
    },
    configLoaded({ module, config }) {
      if (module.info.relativeName !== 'test-party') return;
      // config
      monkeyConfig(module, config);
    },
  };
  return monkey;
};