make bundle for a site
function readyBundle(app, zlSite, theme, resolve = ()=>{}, cb) {
  var { apps, themes, cache, getInheritTheme, defaultTheme, config } = app.locals;

  var contentPath, bundlTheme;

  if (zlSite) {
    // when bundling a site (front-end)
    contentPath = `${path.join(process.cwd(), config.contentPath)}/site/${zlSite.zsId}`;
    bundlTheme = zlSite.zsTheme;
    app.debug('getting site:%s ready to bundle', zlSite.zsId);
  }
  else if (theme) {
    // when bundling a theme (server-side)
    contentPath = `${path.join(process.cwd(), config.contentPath)}/build/${theme}`;
    bundlTheme = theme;
    app.debug('getting theme:%s ready to bundle', theme);
  }

  var appBundled = 0, sassPath = [], wpEntry = { app: []};
  var wpConfig = {
    resolve: {
      alias: {
        zlSite: contentPath,
        'zealder-cms': app.locals.zlConfig.path
      }
    },
    module: { rules: [] }
  };

  // prevent from bundling 2 copies of these libraries by aliasing copy in cms
  _.forEach(app.locals.dependencies, function(ver, lib) {
    if ('production' === app.get('env')) {
      wpConfig.resolve.alias[lib] = path.join(process.cwd(), `node_modules/${lib}`)
    }
    else {
      wpConfig.resolve.alias[lib] = `${app.locals.zlConfig.path}/node_modules/${lib}`;
    }
  });

  // dictionary of components
  let compDict = 'module.exports = function() { return{';
  //
  let modIncl = [new RegExp(`zealder-cms/(?!.*node_modules)`)];

  // keep list of packages/version starting with zealder-cms
  let packagesVer = {"zealder-cms": app.locals.zlConfig.version};

  function bundleIt(type, key, pack) {
    var bdlData = readyBundleConf(app, wpConfig, modIncl, compDict, sassPath, appBundled, wpEntry, type, key, pack, packagesVer);
    if (bdlData) {
      compDict = bdlData.compDict;
      appBundled = bdlData.appBundled;
    }
  }

  // ready all themes in tree
  if (bundlTheme) {
    var themeTree = getInheritTheme(bundlTheme);
    _.forEach(themeTree, function(v, key) {
      if (appBundled || key === defaultTheme) {
        // index rendered must be last in bundle chain
        // and we dont bundle defaultTheme yet
        return false;
      }
      bundleIt('theme', key, themes[key]);
    });
  }

  // ready all site's apps
  if (!zlSite) {
    _.forEach(apps, function(v, key) {
      bundleIt('app', key, apps[key]);
    });
  }
  else if (zlSite.zsApps) {
    // we convert to json manually since we fetched directly from db
    let zsApps = JSON.parse(zlSite.zsApps);
    _.forEach(zsApps, function(key) {
      if (apps[key]) {
        bundleIt('app', key, apps[key]);
      }
    });
  }

  // lastly, bundle default theme
  if (!appBundled) {
    bundleIt('theme', defaultTheme, themes[defaultTheme]);
  }

  // add entry
  if (zlSite) wpConfig.entry = wpEntry;

  // save disctionary of components
  compDict += '}}';
  // now wrapup config
  wrapup(sassPath, wpConfig, modIncl);

  var dictFile = contentPath+'/compDict.js';
  var versFile = contentPath+'/packagesVer.js';

  // check if existing bundle was made
  if ('build-hot' !== process.env.WORK && fs.existsSync(dictFile) && fs.existsSync(versFile)) {
    let prevPackagesVer = fs.readFileSync(versFile);
    if (_.isEqual(JSON.parse(prevPackagesVer), packagesVer)) {
      // since no package in bundle has changed, keep previous one
      app.debug('skipping %s because existing packages bundle have not changed', contentPath);
      resolve();
      return cb();
    }
    else {
      // removed this file since it's outdated
      fs.unlinkSync(versFile);
    }
  }

  mkdirp.sync(contentPath);
  fs.writeFileSync(dictFile, compDict);

  // callback with wpConfig as param
  cb(wpConfig, contentPath, appBundled, () => {
    // publish it
    if ('build' === process.env.WORK) {
      publish(app, zlSite, contentPath, versFile, packagesVer, resolve);
    }
  });
}