'Is there a way to dynamically pass variables to webpack static react website?

Issue

I am trying to make a static React application with one dynamic configuration value (API_URL). I would like the container to be dynamic so that I can just switch a single file or option and change the URL. What are my options?

I found this StackOverflow article regarding webpack and dotenv.

webpack.prod.ts

import path from 'path'
import { Configuration, DefinePlugin, NormalModuleReplacementPlugin } from 'webpack'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'
import ESLintPlugin from 'eslint-webpack-plugin'
import { CleanWebpackPlugin } from 'clean-webpack-plugin'
import tailwindcss from 'tailwindcss'
import autoprefixer from 'autoprefixer'
import * as dotenv from 'dotenv'

const CopyPlugin = require('copy-webpack-plugin');

const config: Configuration = {
  mode: 'production',
  entry: {
    main: './src/index.tsx',
    'pdf.worker': path.join(__dirname, '../node_modules/pdfjs-dist/build/pdf.worker.js'),
  },
  output: {
    publicPath: '/',
    path: path.join(__dirname, 'dist'),
    filename: '[name].bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.(ts|js)x?$/i,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env',
              '@babel/preset-react',
              '@babel/preset-typescript',
            ],
          },
        },
      },
      {
        test: /\.(sa|sc|c)ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true,
              sassOptions: {
                // * https://github.com/sass/node-sass#outputstyle
                outputStyle: 'compressed',
              },
            },
          },
          {
            loader: 'postcss-loader', // postcss loader needed for tailwindcss
            options: {
              postcssOptions: {
                ident: 'postcss',
                plugins: [tailwindcss, autoprefixer],
              },
            },
          },
        ],
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        loader: 'file-loader',
        options: {
          outputPath: '../fonts',
        },
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
    }),
    new NormalModuleReplacementPlugin(
      /^pdfjs-dist$/,
      (resource) => {
        // eslint-disable-next-line no-param-reassign
        resource.request = path.join(__dirname, '../node_modules/pdfjs-dist/webpack.js')
      },
    ),
    new CopyPlugin({
      patterns: [
        { from: 'public/images', to: 'images' },
      ],
    }),
    // Environment Variable for Build Number - Done in NPM scripts
    // Super helpful article
    // * https://stackoverflow.com/questions/55185601/webpack-process-env-undefined-using-defineplugin-and-dotenv
    new DefinePlugin({
      ...Object.entries(dotenv.config({ path: '/env/.env' }).parsed || []).reduce((acc, curr) => ({ ...acc, [`${curr[0]}`]: JSON.stringify(curr[1]) }), {}),
      VERSION_NUMBER: JSON.stringify(process.env.VERSION_NUMBER),
    }),
    new ForkTsCheckerWebpackPlugin({
      async: false,
    }),
    new ESLintPlugin({
      extensions: ['js', 'jsx', 'ts', 'tsx'],
    }),

    // The CleanWebpackPlugin plugin will clear out the build folder.
    new CleanWebpackPlugin(),
  ],
  performance: {
    hints: false,
    maxEntrypointSize: 512000,
    maxAssetSize: 512000,
  },
  optimization: {
    minimize: true,
    minimizer: [
      // For webpack@5 you can use the `...` syntax to extend existing minimizers
      '...',
    ],
  },
};

export default config

Dockerfile

# Multi-stage Docker file
#  1) Build the node project with the latest version of node
#  2) NGNIX to serve the production front end packed via webpack

# Stage 1: Building the application
FROM node:latest AS build

ARG VERSION_NUMBER
ENV VERSION_NUMBER ${VERSION_NUMBER}

WORKDIR /app

# Copy the app to the working directory on the image
COPY . /app

# Install node modules and build application
RUN yarn install && yarn run build

# Stage 2: Serve content using NGINX Web Server
FROM nginx:latest AS click-n-file

ENV NODE_ENV production

WORKDIR /usr/share/nginx/html
# Remove default nginx static assets
RUN rm -rf ./*
# Copy static assets from builder stage
COPY --from=build /app/build .

# Expose port 80 out of container
EXPOSE 80

# Containers run nginx with global directives and daemon off
ENTRYPOINT ["nginx", "-g", "daemon off;"]

Additional Notes:

  • I am using Azure
  • I am using Pipelines to build this container but have many customers with different URLs
  • I am able to define docker environment variables

Things I have tried

  • Added dotenv but not sure how to put one (a .env file) in a production container
  • Looked at putting a json configuration in the public folder (then switching it out in prod) - but importing it in code is difficult
  • Tried exposing process.env in webpack (didn't get it to work)

I have not changed the docker file or looked at docker-compose

If there is any additional information I can provide let me know. Thanks!



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source