skip to Main Content

I have a React application with an index.html file in the public folder:

<!DOCTYPE html>
<html lang="ru-RU">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Test</title>
   
    <link rel="dx-theme" data-theme="generic.dark" href="/css/some.css" data-active="false">
    <base href="/" />
</head>

<body class="dx-viewport">
    <div id="root"></div>
    <script src="/scripts/some-script.js"></script>
</body>

</html>

HtmlWebpackPlugin settings:

new HtmlWebpackPlugin({
    favicon: 'public/faviconDOCs.ico',
    template: 'public/index.html',
    scriptLoading: 'blocking',
    minify: {
        removeComments: true,
    },
}),

As a result, Webpack generates the following index.html in the dist folder:

<!DOCTYPE html>
<html lang="ru-RU">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
     <title>Test</title>
   
 <link rel="dx-theme" data-theme="generic.dark" href="/css/some.css" data-active="false">
    <base href="/">
    <link rel="icon" href="/faviconDOCs.ico">
</head>

<body class="dx-viewport">
<div id="root"></div>
 <script src="/scripts/some-script.js"></script>
 <script src="/main.bundle.0cec3d5e983e6fa0c00b.js"></script>
</body>

</html>

But I need the to be included after all the bundled scripts. How can I achieve this?

I tried to add templateParameters with additionalScript

new HtmlWebpackPlugin({
      favicon: 'public/faviconDOCs.ico',
      template: 'public/index.html',
      scriptLoading: 'blocking',
      inject: 'body', // Scripts are added at the end of the body
      minify: {
        removeComments: true,
      },
      templateParameters: {
        additionalScript: '<script src="/tflex-viewer/tflex-viewer3d-widget.js"></script>'
      }
    }),

and html…

<!DOCTYPE html>
<html lang="ru-RU">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Test</title>
   
    <link rel="dx-theme" data-theme="generic.dark" href="/css/some.css" data-active="false">
    <base href="/" />
</head>

<body class="dx-viewport">
    <div id="root"></div>
    <%= additionalScript %> 
</body>

</html>

But the output is the same

<body class="dx-viewport">
<div id="root"></div>
<script src="/scripts/some-script.js"></script>
<script src="/main.bundle.0cec3d5e983e6fa0c00b.js"></script>
</body>

How can I make the script be included at the end of all scripts in index.html?

Thank you in advance.

2

Answers


  1. Chosen as BEST ANSWER

    By specifying the order of scripts in the HTML template, we can achieve the desired result.

    <!DOCTYPE html>
    <html lang="ru-RU">
    
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Test</title>
    
        <link rel="dx-theme" data-theme="generic.dark" href="/css/some.css" data-active="false">
        <base href="/" />
    </head>
    
    <body class="dx-viewport">
        <div id="root"></div>
        <!-- Insert all Webpack scripts -->
        <%= htmlWebpackPlugin.tags.bodyTags %>
        <!-- Add the additional script after all bundled scripts -->
        <%= htmlWebpackPlugin.options.templateParameters.additionalScript %>
    </body>
    
    </html>
    

    And for the plugin, we set inject: false to disable automatic script insertion:

    new HtmlWebpackPlugin({
        favicon: 'public/faviconDOCs.ico',
        template: 'public/index.html',
        inject: false,
        scriptLoading: 'blocking',
        minify: {
            removeComments: true,
        },
        templateParameters: {
            additionalScript: '<script src="/tflex-viewer/tflex-viewer3d-widget.js"></script>'
        }
    }),
    

    Now, the output will be as expected - Webpack will generate the following index.html:

    <!DOCTYPE html>
    <html lang="ru-RU">
    
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title>T-FLEX DOCs</title>
       <link rel="dx-theme" data-theme="generic.dark" href="/css/some.css" data-active="false">
    </head>
    
    <body class="dx-viewport">
        <div id="root"></div>
    
        <script src="/main.bundle.950a4151bb8f16528cf1.js"></script>
    
        <script src="/script/script-test.js"></script>
    </body>
    
    </html>
    

    As a result, the required script (script-test.js) is included at the end (As required by the task conditions)


  2. The HtmlWebpackPlugin adds <script> at bottom of head or body tag. But you can’t exactly define the place in HTML.

    Try to use modern html-bundler-webpack-plugin.

    Using this plugin you can specify all source files directly in HTML wherever you want. The plugin resolves source files of assets in templates and replaces them with correct output URLs in the generated HTML. The defined <script> tag stay exactly on the same place.

    For example, there is the HTML template:

    <!DOCTYPE html>
    <html lang="ru-RU">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Test</title>
        <!-- relative path to favicon source file -->
        <link href="../images/favicon.ico" rel="icon" />
        <!-- relative path to SCSS source file -->
        <link href="../scss/style.scss" rel="dx-theme" data-theme="generic.dark" data-active="false">
        
        <!-- relative path to JS source file -->
        <script src="../vendor/main.js" defer="defer"></script>
    </head>
    
    <body class="dx-viewport">
        <div id="root"></div>
        <!-- relative path to JS source file -->
        <script src="../vendor/tflex-viewer/tflex-viewer3d-widget.js"></script>
        <!-- relative path to JS source file -->
        <script src="../scripts/some-script.js"></script>
    </body>
    </html>
    

    Minimal Webpack config:

    const path = require('path');
    const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
    
    module.exports = {
      output: {
        path: path.resolve(__dirname, 'dist'),
      },
    
      plugins: [
        new HtmlBundlerPlugin({
          entry: {
            // template file relative to the Webpack config file
            index: 'public/index.html', // save generated HTML into dist/index.html
          },
          js: {
            filename: 'js/[name].[contenthash:8].js', // JS output filename
          },
          css: {
            filename: 'css/[name].[contenthash:8].css', // CSS output filename
          },
        }),
      ],
      
      module: {
          rules: [
            {
              test: /.s?css$/,
              use: ['css-loader', 'sass-loader'],
            },
            {
              test: /.(ico|png|jp?g|svg)/,
              type: 'asset/resource',
              generator: {
                filename: 'img/[name].[hash:8][ext][query]',
              },
            },
          ],
        },
    };
    

    The generated HTML contains hashed output URL of assets.
    All <script> tags will be exactly on the same place:

    <!DOCTYPE html>
    <html lang="ru-RU">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Test</title>
        <link href="img/favicon.1a2b3c4d.ico" rel="icon" />
        <link href="css/style.1234abcd.css" rel="dx-theme" data-theme="generic.dark" data-active="false">
        
        <!-- compiled source file: vendor/main.js -->
        <script src="js/main.adcb4321.js" defer="defer"></script>
    </head>
    
    <body class="dx-viewport">
        <div id="root"></div>
        <!-- compiled source file: vendor/tflex-viewer/tflex-viewer3d-widget.js -->
        <script src="js/tflex-viewer3d-widget.adcb4321.js"></script>
        <!-- compiled source file: scripts/some-script.js -->
        <script src="js/some-script.adcb4321.js"></script>
    </body>
    </html>
    

    Using this plugin you don’t need define source JS scrips in Webpack entry option. You specify source JS files (and other assets) directly in HTML.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search