Use HTMLWebpackPlugin with an EJS file
Asked Answered
K

5

11

I want to use the HTMLWebpackPlugin to take my index.ejs template file, insert my bundled assets, and output a final index.ejs file.

This example has a EJS variable <%= API_URL %>, but webpack is interpreting it.

How can I stop webpack from substituting the variable?

Starting "template":

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Monitor</title>
    <script>
      window.config = {
        API_URL: "<%= API_URL %>"
      }
    </script>
  </head>
  <body>
    <div class="container"></div>
  </body>
</html>

When you try to run webpack:

ERROR in Template execution failed: ReferenceError: API_URL is not defined

Desired result index.ejs: (has bundled assets and EJS var)

Monitor window.config = { API_URL: "<%= API_URL %>" }

webpack.config.js

var webpack = require('webpack');
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  entry: {
    bundle: './src/index.js'
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
  },
  module: {
    rules: [
    {
      test: /\.js$/,
      use: 'babel-loader',
      exclude: /node_modules/
    },
    {
      // CSS is imported in app.js.
      test: /\.scss$/,
      use: ExtractTextPlugin.extract({
        fallbackLoader: 'style-loader',
        loader: ["css-loader", "sass-loader"]
      })
    }]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify(process.env.NODE_ENV),
        'API_URL': JSON.stringify(process.env.API_URL)
      }
    }),
    new HtmlWebpackPlugin({
      template: 'src/index.ejs',
      inject: true
    }),
    new ExtractTextPlugin("styles.css")
  ],
};
Katekatee answered 5/5, 2017 at 23:24 Comment(0)
K
6

Here is a really bad hacky solution, and I hope someone else has a real answer / understanding of how to do this.

In your template file (index.ejs), do this:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Monitor</title>
    <script>
      window.config = {
        API_URL: "<%= htmlWebpackPlugin.options.API_URL_TEMPLATE_VAR %>"
      }
    </script>
  </head>
  <body>
    <div class="container"></div>
  </body>
</html>

In your webpack config, do this (the relevant part is the new HtmlWebpackPlugin where I define a variable.:

plugins: [
    // Define environment variables that are accessible inside of app javascript.
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
      }
    }),
    // Adds bundled file links to the index.html
    new HtmlWebpackPlugin({
      // The input file name
      template: 'src/index.prod.ejs',
      // Injects scripts into the <body>
      inject: true,
      // This is so hacky. I inject a string so the built .ejs file has this template var. Lets us set api_url when server is started instead of at bundle time.
      API_URL_TEMPLATE_VAR: '<%= process.env.API_URL %>',
      // The output file name
      filename: 'index.ejs'
    }),
    new ExtractTextPlugin("styles.css")
  ],

Because I defined API_URL_TEMPLATE_VAR, when html-webpack-plugin evaluates it, it will print out <%= process.env.API_URL %> into the final template.

Hacky, but works. Not accepting my own answer / waiting for a better answer.

Katekatee answered 7/5, 2017 at 23:3 Comment(3)
Upvoted because, although hacky, seems like the only solution which answers this question and solves a problem. I searched a lot and this is the only answer I could findMaurey
This will work, but what about if in the ejs? You will have to put all the login in the webpack.config.js...Dishearten
For me, all I needed was filename: 'index.ejs' and then I loaded it using raw-loader, which causes Webpack to load the file as-is.Unni
C
0

I ran into the same issue and adding a quote (") to the definition of the variable solved it:

In your case:

new webpack.DefinePlugin({
  'process.env': {
    'NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    'API_URL': '"' + JSON.stringify(process.env.API_URL) + '"'
  }
})
...
Coppersmith answered 4/11, 2017 at 15:31 Comment(0)
I
0

The HTMLWebpackPlugin uses lodash.template for this and it should be possible though not very easy to change the default delimiter for it to something like <? so you can use the <% delimiter for the frontend. However if you're using the ejs package for the frontend it would be a lot easier to change the delimiter there and keep the normal one for webpack.

ejs:

<div>
  <%=SOME_WEBPACK_VAR%>
  <br />
  <?=SOME_EJS_VAR%>
</div>

javascript:

ejs.render(yourHtml, {SOME_EJS_VAR: 'foo'}, {delimiter: '?'});
Indiction answered 2/7, 2018 at 15:14 Comment(0)
B
0

As far as I understand EJS, your problem would be solved by using <%%= as explained in the EJS docs, so replacing your code by: API_URL: "<%%= API_URL %>",

Bermuda answered 10/12, 2021 at 9:6 Comment(0)
A
-1

If you want Webpack to ignore your file from the build you can prepend raw-loader in front of the file path like this:

new HtmlWebpackPlugin({
    inject: true,
    filename: "index.ejs",
    template: "!!raw-loader!" + 'src/index.ejs',
}),

And in case you want to inject variables into your template directly from Webpack you can use a dependency called react-dev-utils, which has a very handy plugin for this:

new InterpolateHtmlPlugin(HtmlWebpackPlugin, {
   PUBLIC_URL: publicUrl,
  // You can pass any key-value pairs, this was just an example.
  // WHATEVER: 42 will replace %WHATEVER% with 42 in index.html.
}),

Haven't tried with an ejs template myself but I see no reason that it shouldn't work.

Alligator answered 19/3, 2021 at 15:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.