koa(selectedMiddlewares = ALL_MIDDLEWARES) {
        if (!Array.isArray(selectedMiddlewares)) {
            selectedMiddlewares = [selectedMiddlewares];
        }

        const composeKoa = require('koa-compose');
        const c2k = require('koa-connect');
        const mount = require('koa-mount');
        const koaStatic = require('koa-static');
        const send = require('koa-send');

        let {router: {base}, build: {ssr, publicPath}, serviceWorker, errorHandler} = this.config;
        base = removeTrailingSlash(base || '/');

        if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.COMPRESSION)) {
            // gzip compression
            this.add(compression());
        }

        if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.FAVICON)) {
            // serve favicon
            let faviconPath = posix.join(this.cwd, ASSETS_DIRNAME_IN_DIST, 'img/icons/favicon.ico');
            if (existsSync(faviconPath)) {
                this.add(favicon(faviconPath));
            }
        }

        // transform express/connect style middleware to koa style
        this.internalMiddlewares = this.internalMiddlewares.map(c2k);

        // koa defaults to 404 when it sees that status is unset
        this.add(async (ctx, next) => {
            ctx.status = 200;
            await next();
        }, true);

        // handle errors
        if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.ERROR)) {
            this.add(koaErrorFactory(errorHandler), true);
        }

        // Redirect without trailing slash.
        if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.TRAILING_SLASH)) {
            this.add(async (ctx, next) => {
                if (base === ctx.path) {
                    ctx.redirect(`${ctx.path}/${ctx.search}`);
                }
                else {
                    await next();
                }
            });
        }

        if (ssr) {
            /**
             * Add static files middleware only in prod mode,
             * because we already have webpack-dev-middleware in dev mode.
             * Don't need this middleware when CDN being used to serve static files.
             */
            if (this.isProd && !isFromCDN(publicPath)) {

                if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.STATIC)) {
                    // serve /static
                    this.add(mount(
                        posix.join(publicPath, ASSETS_DIRNAME_IN_DIST),
                        koaStatic(join(this.cwd, ASSETS_DIRNAME_IN_DIST))
                    ));
                }

                // serve sw-register.js & sw.js
                if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.SERVICE_WORKER)
                    && serviceWorker && serviceWorker.swDest) {
                    let swFiles = [
                        basename(serviceWorker.swDest),
                        'sw-register.js'
                    ].map(f => posix.join(publicPath, f));
                    this.add(async (ctx, next) => {
                        let done = false;
                        if (swFiles.includes(ctx.path) || /^\/precache\-manifest.+\.js$/.test(ctx.path)) {
                            // Don't cache service-worker.js & sw-register.js & precache-manifest.js.
                            ctx.set('Cache-Control', 'private, no-cache, no-store');
                            done = await send(ctx, ctx.path.substring(publicPath.length), {
                                root: this.cwd
                            });
                        }
                        if (!done) {
                            await next();
                        }
                    });
                }
            }

            if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.SSR)) {
                this.add(c2k(ssrFactory(this.core)));
            }
        }

        return composeKoa(this.internalMiddlewares);
    }
compose middlewares into a chain.
NOTE: MUST be called in Node >= 7.6.0 because of "async" syntax.

@param {Array<Function>|Function} selectedMiddlewares middlewares selected by user
@return {Function} koaMiddleware