Don P
Don P

Reputation: 63768

Use HTMLWebpackPlugin with an EJS file

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")
  ],
};

Upvotes: 11

Views: 18143

Answers (5)

bert bruynooghe
bert bruynooghe

Reputation: 3103

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 %>",

Upvotes: 0

Moltas
Moltas

Reputation: 69

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.

Upvotes: -1

red-X
red-X

Reputation: 5138

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: '?'});

Upvotes: 1

Fernando
Fernando

Reputation: 21

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) + '"'
  }
})
...

Upvotes: 0

Don P
Don P

Reputation: 63768

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.

Upvotes: 6

Related Questions