docs: function generateWebpack(options) {
    let base = {
      entry: injectRegistry(options.entries),
      output: {
        path: resolve(__dirname, 'build'),
        filename: (options.name || (pkg.name + (options.bundle ? '_bundle' : ''))) + (options.min && !options.nosuffix ? '.min' : '') + '.js',
        chunkFilename: '[chunkhash].js',
        publicPath: '' // no public path = relative
      },
      resolve: {
        // add `.ts` and `.tsx` as a resolvable extension.
        extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js'],
        alias: Object.assign({}, options.libs || {}),
        // fallback to the directory above if they are siblings just in the workspace context
        modules: isWorkspaceContext ? [
          resolve(__dirname, '../'),
          'node_modules'
        ] : ['node_modules']
      },
      plugins: [
        // define magic constants that are replaced
        new webpack.DefinePlugin({
          'process.env.NODE_ENV': JSON.stringify(options.isProduction ? 'production' : 'development'),
          __VERSION__: JSON.stringify(pkg.version),
          __LICENSE__: JSON.stringify(pkg.license),
          __BUILD_ID__: buildId,
          __DEBUG__: options.isDev || options.isTest,
          __TEST__: options.isTest,
          __PRODUCTION__: options.isProduction,
          __APP_CONTEXT__: JSON.stringify('/')
        })
        // rest depends on type
      ],
      externals: [],
      module: {
        loaders: webpackloaders.slice()
      },
      devServer: {
        proxy: {
          '/api/*': {
            target: 'http://localhost:9000',
            secure: false,
            ws: true
          },
          '/login': {
            target: 'http://localhost:9000',
            secure: false
          },
          '/logout': {
            target: 'http://localhost:9000',
            secure: false
          },
          '/loggedinas': {
            target: 'http://localhost:9000',
            secure: false
          }
        },
        contentBase: resolve(__dirname, 'build'),
        watchOptions: {
          aggregateTimeout: 500,
          ignored: /node_modules/
        }
      },
      watchOptions: {
        aggregateTimeout: 500,
        ignored: /node_modules/
      }
    };
  
    if (options.isProduction) {
      base.plugins.unshift(new webpack.BannerPlugin({
        banner: banner,
        raw: true
      }));
      base.plugins.push(new webpack.optimize.MinChunkSizePlugin({
        minChunkSize: 10000 // at least 10.000 characters
      }));
      base.plugins.push(new webpack.optimize.AggressiveMergingPlugin());
    } else if (options.isDev) {
      // use dev version of tsconfig
      const {TsConfigPathsPlugin} = require('awesome-typescript-loader');
      base.plugins.push(new TsConfigPathsPlugin({configFileName: './tsconfig_dev.json'}));
    }
  
    if (options.library) {
      let libName = /phovea_.*/.test(pkg.name) ? ['phovea', pkg.name.slice(7)] : pkg.name;
      // generate a library, i.e. output the last entry element
      // create library name
      if (options.moduleBundle) {
        libName = 'phovea';
      }
      base.output.library = libName;
      base.output.libraryTarget = 'umd';
      base.output.umdNamedDefine = false; // anonymous require module
    }
  
    if (!options.bundle) {
      // if we don't bundle don't include external libraries and other phovea modules
      base.externals.push(...(options.externals || Object.keys(options.libs || {})));
  
      // ignore all phovea modules
      if (options.modules) {
        base.externals.push(testPhoveaModules(options.modules));
      }
  
      // ignore extra modules
      (options.ignore || []).forEach(function (d) {
        base.module.loaders.push({test: new RegExp(d), loader: 'null-loader'}); // use null loader
      });
      // ingore phovea module registry calls
      (options.modules || []).forEach(function (m) {
        base.module.loaders.push({
          test: new RegExp('.*[\\\\/]' + m + '[\\\\/]phovea_registry.js'),
          loader: 'null-loader'
        }); // use null loader
      });
    }
    if (!options.bundle || options.isApp) {
      // extract the included css file to own file
      let p = new ExtractTextPlugin({
        filename: (options.isApp || options.moduleBundle ? 'style' : pkg.name) + (options.min && !options.nosuffix ? '.min' : '') + '.css',
        allChunks: true // there seems to be a bug in dynamically loaded chunk styles are not loaded, workaround: extract all styles from all chunks
      });
      base.plugins.push(p);
      base.module.loaders[0] = {
        test: /\.scss$/,
        loader: p.extract(['css-loader', 'sass-loader'])
      };
      base.module.loaders[1] = {
        test: /\.css$/,
        loader: p.extract(['css-loader'])
      };
    }
    if (options.isApp) {
      // create manifest
      // base.plugins.push(new webpack.optimize.AppCachePlugin());
    }
    if (options.commons) {
      // build a commons plugin
      base.plugins.push(new webpack.optimize.CommonsChunkPlugin({
        // The order of this array matters
        names: ['common'],
        minChunks: 2
      }));
    }
    if (options.min) {
      // use a minifier
      base.plugins.push(
        new webpack.LoaderOptionsPlugin({
          minimize: true,
          debug: false
        }),
        new webpack.optimize.UglifyJsPlugin());
    } else {
      // generate source maps
      base.devtool = 'source-map';
    }
    return base;
  }