title: Progressive Development

Egg provides both Plugin and Framework, and the former has two loading modes which are path and package. Then how should we choose?

Step-by-step example will be provided to demonstrate how to start coding development progressively.

Find detail codes on eggjs/examples/progressive.

Getting Started

Assume that we are writing a code to analyze UA to implement the function below:

  • ctx.isAndroid
  • ctx.isIOS

You can easily write it down after previous tutorials, let’s have a quick review:

Codes refer to step1.

Directory structure:

  1. example-app
  2. ├── app
  3. ├── extend
  4. └── context.js
  5. └── router.js
  6. ├── test
  7. └── index.test.js
  8. └── package.json

Core code:

  1. // app/extend/context.js
  2. module.exports = {
  3. get isIOS() {
  4. const iosReg = /iphone|ipad|ipod/i;
  5. return iosReg.test(this.get('user-agent'));
  6. },
  7. };

Prototype of Plugin

Obviously, the logic is universal that can be written as a plugin.

But since function might not be perfect at the beginning, it might be difficult to maintain if encapsulated into a plugin directly.

We can write the code as the format of plugin, but not separate out.

Codes refer to step2.

New directory structure:

  1. example-app
  2. ├── app
  3. └── router.js
  4. ├── config
  5. └── plugin.js
  6. ├── lib
  7. └── plugin
  8. └── egg-ua
  9. ├── app
  10. └── extend
  11. └── context.js
  12. └── package.json
  13. ├── test
  14. └── index.test.js
  15. └── package.json

Core code:

  • app/extend/context.js move to lib/plugin/egg-ua/app/extend/context.js.

  • lib/plugin/egg-ua/package.json declares plugin.

  1. {
  2. "eggPlugin": {
  3. "name": "ua"
  4. }
  5. }
  • config/plugin.js uses path to mount the plugin.
  1. // config/plugin.js
  2. const path = require('path');
  3. exports.ua = {
  4. enable: true,
  5. path: path.join(__dirname, '../lib/plugin/egg-ua'),
  6. };

Extract to Independent Plugin

The functions of module become better after a period of developing so we could extract it out as an independent plugin.

We extract an egg-ua plugin and have a quick review as below. Details refer to Plugin Development.

Directory structure:

  1. egg-ua
  2. ├── app
  3. └── extend
  4. └── context.js
  5. ├── test
  6. ├── fixtures
  7. └── test-app
  8. ├── app
  9. └── router.js
  10. └── package.json
  11. └── ua.test.js
  12. └── package.json

Codes refer to step3/egg-ua.

Then modify the application, details refer to step3/example-app.

  • Remove directory lib/plugin/egg-ua.
  • declare dependencies egg-ua in package.json.
  • change type to package in config/plugin.js.
  1. // config/plugin.js
  2. exports.ua = {
  3. enable: true,
  4. package: 'egg-ua',
  5. };

Note:We can use npm link for local test before releasing the plugin. Details refer to npm-link.

  1. $ cd example-app
  2. $ npm link ../egg-ua
  3. $ npm i
  4. $ npm test

Finally: A Framework

After repeating the process above, we accumulate a few plugins and configurations, and might find that most of our team projects are using them.

At that time, you can consider abstracting them as a framework which is suitable for business scenarios.

Firstly, abstract the example-framework as below. Let’s have a quick review, details refer to Framework.

Directory structure:

  1. example-framework
  2. ├── config
  3. ├── config.default.js
  4. └── plugin.js
  5. ├── lib
  6. ├── agent.js
  7. └── application.js
  8. ├── test
  9. ├── fixtures
  10. └── test-app
  11. └── framework.test.j.
  12. ├── README.md
  13. ├── index.js
  14. └── package.json
  • Codes refer to example-framework.
  • Remove the dependencies of plugins such as egg-ua and remove it from example-app, then configure them into the package.json and config/plugin.js of the framework.

Then modify the application, details refer to step4/example-app.

  • Remove egg-ua in config/plugin.js.
  • Remove egg-ua in package.json.
  • declare example-framework in package.json and configure the egg.framework.
  1. {
  2. "name": "progressive",
  3. "version": "1.0.0",
  4. "private": true,
  5. "egg": {
  6. "framework": "example-framework"
  7. },
  8. "dependencies": {
  9. "example-framework": "*"
  10. }
  11. }

Note:We can use npm link for local test before releasing the framework npm-link.

  1. $ cd example-app
  2. $ npm link ../egg-framework
  3. $ npm i
  4. $ npm test

Write in the end

In conclusion, we can see how to make the framework evolution step by step which benefits from Egg’s powerful plugin mechanism, code co-build, reusability and modularity.

  • in general, put codes into lib/plugin if they can be reused in the application.
  • separate it into a node module when plugin becomes stable.
  • application with relatively reusable codes will work as a separate plugin.
  • abstract it as framework to release after application become certain solutions of specified business scenario.
  • it would be a great improvement in the efficiency of teamwork after plugins were extracted, modularized and finally became a framework, because other projects could reuse codes by just using npm install.

  • Note:Whether it’s the application/plugin/framework, unittest is necessary and try to reach 100% coverage