text: /*eslint-env node*/
  /*eslint import/no-nodejs-modules:0 */
  const path = require('path');
  const fs = require('fs');
  const webpack = require('webpack');
  const babelConfig = require('./babel.config');
  const ExtractTextPlugin = require('mini-css-extract-plugin');
  const CompressionPlugin = require('compression-webpack-plugin');
  const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
  const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
  
  const {env} = process;
  
  const IS_PRODUCTION = env.NODE_ENV === 'production';
  const IS_TEST = env.NODE_ENV === 'test' || env.TEST_SUITE;
  const IS_STORYBOOK = env.STORYBOOK_BUILD === '1';
  const WEBPACK_DEV_PORT = env.WEBPACK_DEV_PORT;
  const SENTRY_DEVSERVER_PORT = env.SENTRY_DEVSERVER_PORT;
  const USE_HOT_MODULE_RELOAD = !IS_PRODUCTION && WEBPACK_DEV_PORT && SENTRY_DEVSERVER_PORT;
  const WEBPACK_MODE = IS_PRODUCTION ? 'production' : 'development';
  
  // this is set by setup.py sdist
  const staticPrefix = path.join(__dirname, 'src/sentry/static/sentry');
  const distPath = env.SENTRY_STATIC_DIST_PATH || path.join(staticPrefix, 'dist');
  
  /**
   * Locale file extraction build step
   */
  if (env.SENTRY_EXTRACT_TRANSLATIONS === '1') {
    babelConfig.plugins.push([
      'babel-gettext-extractor',
      {
        fileName: 'build/javascript.po',
        baseDirectory: path.join(__dirname, 'src/sentry'),
        functionNames: {
          gettext: ['msgid'],
          ngettext: ['msgid', 'msgid_plural', 'count'],
          gettextComponentTemplate: ['msgid'],
          t: ['msgid'],
          tn: ['msgid', 'msgid_plural', 'count'],
          tct: ['msgid'],
        },
      },
    ]);
  }
  
  /**
   * Locale compilation and optimizations.
   *
   * Locales are code-split from the app and vendor chunk into separate chunks
   * that will be loaded by layout.html depending on the users configured locale.
   *
   * Code splitting happens using the splitChunks plugin, configured under the
   * `optimization` key of the webpack module. We create chunk (cache) groups for
   * each of our supported locales and extract the PO files and moment.js locale
   * files into each chunk.
   *
   * A plugin is used to remove the locale chunks from the app entry's chunk
   * dependency list, so that our compiled bundle does not expect that *all*
   * locale chunks must be loadd
   */
  const localeCatalogPath = path.join(
    __dirname,
    'src',
    'sentry',
    'locale',
    'catalogs.json'
  );
  
  const localeCatalog = JSON.parse(fs.readFileSync(localeCatalogPath, 'utf8'));
  
  // Translates a locale name to a language code.
  //
  // * po files are kept in a directory represented by the locale name [0]
  // * moment.js locales are stored as language code files
  // * Sentry will request the user configured language from locale/{language}.js
  //
  // [0] https://docs.djangoproject.com/en/2.1/topics/i18n/#term-locale-name
  const localeToLanguage = locale => locale.toLowerCase().replace('_', '-');
  
  const supportedLocales = localeCatalog.supported_locales;
  const supportedLanguages = supportedLocales.map(localeToLanguage);
  
  // A mapping of chunk groups used for locale code splitting
  const localeChunkGroups = {};
  
  // No need to split the english locale out as it will be completely empty and
  // is not included in the django layout.html.
  supportedLocales.filter(l => l !== 'en').forEach(locale => {
    const language = localeToLanguage(locale);
    const group = `locale/${language}`;
  
    // List of module path tests to group into locale chunks
    const localeGroupTests = [
      new RegExp(`locale\\/${locale}\\/.*\\.po$`),
      new RegExp(`moment\\/locale\\/${language}\\.js$`),
    ];
  
    // module test taken from [0] and modified to support testing against
    // multiple expressions.
    //
    // [0] https://github.com/webpack/webpack/blob/7a6a71f1e9349f86833de12a673805621f0fc6f6/lib/optimize/SplitChunksPlugin.js#L309-L320
    const groupTest = module =>
      localeGroupTests.some(
        pattern =>
          module.nameForCondition && pattern.test(module.nameForCondition())
            ? true
            : Array.from(module.chunksIterable).some(c => c.name && pattern.test(c.name))
      );
  
    localeChunkGroups[group] = {
      name: group,
      test: groupTest,
      enforce: true,
    };
  });
  
  /**
   * Restirct translation files that are pulled in through app/translations.jsx
   * and through moment/locale/* to only those which we create bundles for via
   * locale/catalogs.json.
   */
  const localeRestrictionPlugins = [
    new webpack.ContextReplacementPlugin(
      /sentry-locale$/,
      path.join(__dirname, 'src', 'sentry', 'locale', path.sep),
      true,
      new RegExp(`(${supportedLocales.join('|')})/.*\\.po$`)
    ),
    new webpack.ContextReplacementPlugin(
      /moment\/locale/,
      new RegExp(`(${supportedLanguages.join('|')})\\.js$`)
    ),
  ];
  
  /**
   * When our locales are codesplit into cache groups, webpack expects that all
   * chunks *must* be loaded before the main entrypoint can be executed. However,
   * since we will only be using one locale at a time we do not want to load all
   * locale chunks, just the one the user has enabled.
   *
   * This plugin removes the locale chunks from the app entrypoint's immediate
   * chunk dependants list, ensuring the the compiled entrypoint will execute
   * *without* all locale chunks loaded.
   */
  const pluginName = 'OptionalLocaleChunkPlugin';
  
  const clearLocaleChunks = chunks =>
    chunks.filter(chunk => chunk.name !== 'app').forEach(chunk => {
      const mainGroup = Array.from(chunk.groupsIterable)[0];
      mainGroup.chunks = mainGroup.chunks.filter(
        c => c.name && !c.name.startsWith('locale')
      );
    });
  
  class OptionalLocaleChunkPlugin {
    apply(compiler) {
      compiler.hooks.compilation.tap(pluginName, compilation =>
        compilation.hooks.afterOptimizeChunks.tap(pluginName, clearLocaleChunks)
      );
    }
  }
  
  /**
   * Explicit codesplitting cache groups
   */
  const cacheGroups = {
    vendors: {
      name: 'vendor',
      test: /[\\/]node_modules[\\/]/,
      priority: -10,
      enforce: true,
      chunks: 'initial',
    },
    ...localeChunkGroups,
  };
  
  /**
   * Main Webpack config for Sentry React SPA.
   */
  const appConfig = {
    mode: WEBPACK_MODE,
    entry: {app: 'app'},
    context: staticPrefix,
    module: {
      rules: [
        {
          test: /\.jsx?$/,
          include: [staticPrefix],
          exclude: /(vendor|node_modules|dist)/,
          use: {
            loader: 'babel-loader',
            options: {...babelConfig, cacheDirectory: true},
          },
        },
        {
          test: /\.po$/,
          use: {
            loader: 'po-catalog-loader',
            options: {
              referenceExtensions: ['.js', '.jsx'],
              domain: 'sentry',
            },
          },
        },
        {
          test: /app\/icons\/.*\.svg$/,
          use: ['svg-sprite-loader', 'svgo-loader'],
        },
        {
          test: /\.css/,
          use: ['style-loader', 'css-loader'],
        },
        {
          test: /\.(woff|woff2|ttf|eot|svg|png|gif|ico|jpg)($|\?)/,
          exclude: /app\/icons\/.*\.svg$/,
          use: [
            {
              loader: 'file-loader',
              options: {
                name: '[name].[ext]',
              },
            },
          ],
        },
      ],
      noParse: [
        // don't parse known, pre-built javascript files (improves webpack perf)
        /dist\/jquery\.js/,
        /jed\/jed\.js/,
        /marked\/lib\/marked\.js/,
      ],
    },
    plugins: [
      /**
       * Used to make our lodash modules even smaller
       */
      new LodashModuleReplacementPlugin({
        collections: true,
        currying: true, // these are enabled to support lodash/fp/ features
        flattening: true, // used by a dependency of react-mentions
        shorthands: true,
      }),
      /**
       * jQuery must be provided in the global scope specifically and only for
       * bootstrap, as it will not import jQuery itself.
       *
       * We discourage the use of global jQuery through eslint rules
       */
      new webpack.ProvidePlugin({jQuery: 'jquery'}),
      /**
       * Extract CSS into separate files.
       */
      new ExtractTextPlugin(),
      /**
       * Defines environemnt specific flags.
       */
      new webpack.DefinePlugin({
        'process.env': {
          NODE_ENV: JSON.stringify(env.NODE_ENV),
          IS_PERCY: JSON.stringify(env.CI && !!env.PERCY_TOKEN && !!env.TRAVIS),
        },
      }),
      /**
       * See above for locale chunks. These plugins help with that
       * funcationality.
       */
      new OptionalLocaleChunkPlugin(),
      ...localeRestrictionPlugins,
    ],
    resolve: {
      alias: {
        app: path.join(staticPrefix, 'app'),
        'app-test': path.join(__dirname, 'tests', 'js'),
        'sentry-locale': path.join(__dirname, 'src', 'sentry', 'locale'),
        'integration-docs-platforms':
          IS_TEST || IS_STORYBOOK
            ? path.join(__dirname, 'tests/fixtures/integration-docs/_platforms.json')
            : path.join(__dirname, 'src/sentry/integration-docs/_platforms.json'),
      },
      modules: ['node_modules'],
      extensions: ['.jsx', '.js', '.json'],
    },
    output: {
      path: distPath,
      filename: '[name].js',
      sourceMapFilename: '[name].js.map',
    },
    optimization: {
      splitChunks: {
        chunks: 'all',
        maxInitialRequests: 5,
        maxAsyncRequests: 7,
        cacheGroups,
      },
    },
    devtool: IS_PRODUCTION ? 'source-map' : 'cheap-module-eval-source-map',
  };
  
  /**
   * Legacy CSS Webpack appConfig for Django-powered views.
   * This generates a single "sentry.css" file that imports ALL component styles
   * for use on Django-powered pages.
   */
  const legacyCssConfig = {
    mode: WEBPACK_MODE,
    entry: {
      sentry: 'less/sentry.less',
  
      // Below is for old plugins that use select2 when creating a new issue for a plugin
      // e.g. Trello, Teamwork
      select2: 'less/select2.less',
    },
    context: staticPrefix,
    output: {
      path: distPath,
    },
    plugins: [new ExtractTextPlugin()],
    resolve: {
      extensions: ['.less', '.js'],
      modules: [staticPrefix, 'node_modules'],
    },
    module: {
      rules: [
        {
          test: /\.less$/,
          include: [staticPrefix],
          use: [ExtractTextPlugin.loader, 'css-loader', 'less-loader'],
        },
        {
          test: /\.(woff|woff2|ttf|eot|svg|png|gif|ico|jpg)($|\?)/,
          use: [
            {
              loader: 'file-loader',
              options: {
                name: '[name].[ext]',
              },
            },
          ],
        },
      ],
    },
  };
  
  // Dev only! Hot module reloading
  if (USE_HOT_MODULE_RELOAD) {
    appConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
    appConfig.devServer = {
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Credentials': 'true',
      },
      // Required for getsentry
      disableHostCheck: true,
      contentBase: './src/sentry/static/sentry',
      hot: true,
      // If below is false, will reload on errors
      hotOnly: true,
      port: WEBPACK_DEV_PORT,
    };
  
    // Required, without this we get this on updates:
    // [HMR] Update failed: SyntaxError: Unexpected token < in JSON at position 12
    appConfig.output.publicPath = `http://localhost:${WEBPACK_DEV_PORT}/`;
  }
  
  const minificationPlugins = [
    // This compression-webpack-plugin generates pre-compressed files
    // ending in .gz, to be picked up and served by our internal static media
    // server as well as nginx when paired with the gzip_static module.
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|map|css|svg|html|txt|ico|eot|ttf)$/,
    }),
    new OptimizeCssAssetsPlugin(),
  
    // NOTE: In production mode webpack will automatically minify javascript
    // using the TerserWebpackPlugin.
  ];
  
  if (IS_PRODUCTION) {
    // NOTE: can't do plugins.push(Array) because webpack/webpack#2217
    minificationPlugins.forEach(function(plugin) {
      appConfig.plugins.push(plugin);
      legacyCssConfig.plugins.push(plugin);
    });
  }
  
  module.exports = [appConfig, legacyCssConfig];
  