'dynamic <base> tag based on window object

I need to set a base tag based on document.location I'm using webpack5 and react.

this is my webpack config

const webpack = require("webpack");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const packageDirName = process.cwd();

const paths = {
  src: path.resolve(packageDirName, "src"),
  dist: path.resolve(packageDirName, "../../dist"),
};

module.exports = {
  entry: paths.src,
  output: {
    filename: "main.js",
    path: paths.dist,
    clean: true,
    // publicPath: '/', //need to use it only on dev.
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            rootMode: "upward",
          },
        },
      },
      {
        test: /\.scss$/i,
        use: ["style-loader", "css-loader", "sass-loader"],
      },
      {test: /\.(jpg|jpeg|png|woff|woff2|eot|ttf|svg)$/,loader: 'url-loader'}
    ],
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.DefinePlugin(getEnvVars("npm_package_version", "API_BASE_URL")),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "public/index.html"),
      favicon: path.resolve(__dirname, "public/favicon.ico"),
      inject: true,
    }),
  ],
  devServer: {
    historyApiFallback: true,
    hot: true,
    open: true,
    // ...
  },
};

and on the index.html I added the following code it tweak the base tag (inside )

<script type="text/javascript">
  const baseURL = document.location.href.includes('/me') ? `${document.location.href.split('/me')[0]}/me/` : '/';
  console.log('baseURL', baseURL);
  const baseDirEl = document.createElement('base');

  baseDirEl.setAttribute('href', baseURL);
  document.getElementsByTagName('head')[0].appendChild(baseDirEl);
</script>

webpack is injecting this in index.html <script defer src="main.js"></script>

the main.js is fetched twice maybe it was triggered before the tag.

  1. for this link: http://example.com/me
  1. http://example.com/en/me
  1. http://example.com/en/me/x/y/z

is there a way to avoid the first bundle fetch ? until my scripts finishes to generate the base tag ? or if there is a better way to tackle it ?

thanks.



Solution 1:[1]

Assuming you have a server that renders your html file.

You can simplify the solution using webpack's publicPath attribute.

My trick to inject a ejs variable, and change it's value when I render the HTML.

// webpack.config.js

const isDev = process.env.NODE_ENV === 'development';

module.exports = {
  entry: paths.src,
  output: {
    filename: "main.js",
    path: paths.dist,
    clean: true,
    publicPath: isDev ? '/': '/<%= lng %>me/',
    // -----------------------^ this is the trick 
  },
  ...
}

Then when you render the html on you server, pass lng param which the value will be en/ or nothing if there is no language.

// server.js

var express = require('express');
var app = express();

// set the view engine to ejs
app.set('view engine', 'ejs'); // define ejs

// index page
app.get('/', function (req, res) {
  const lng = req.lng ? `${req.lng}/` : '';

  res.render('pages/index', {
    lng,
  });
});

Sources

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

Source: Stack Overflow

Solution Source
Solution 1 felixmosh