I'm trying to use an EJS loader and the Html Webpack Plugin to compile and output an HTML file but it keeps rendering a file like this:
module.exports = "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n </head>\n\n <body>\n <div id=\"root\"></div>\n </body>\n</html>\n"
Here's my webpack config:
module.exports = {
target: 'node',
entry: './src/server.tsx',
output: {
filename: 'server.js'
},
module: {
rules: [
{
test: /\.ejs$/,
use: {
loader: 'ejs-webpack-loader',
options: {
data: { example: 'test' }
}
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
filename: 'output.html',
inject: false,
template: path.join(rootDir, '../compiler/templates/index.ejs'),
{ example: 'test' },
minify: false
})
]
}
Here's my 'index.ejs' file:
<!DOCTYPE html>
<html lang="en">
<head>
<%- include ./test.ejs %>
</head>
<body>
<div id="root"></div>
</body>
</html>
And here's my 'test.ejs' file:
<meta charset="UTF-8">
I expect this to output an output.html file that looks like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="root"></div>
</body>
</html>
But instead I get this:
module.exports = "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n </head>\n\n <body>\n <div id=\"root\"></div>\n </body>\n</html>\n"
It looks like the ejs-webpack-loader is working and properly injects the partial, but then the HtmlWebpackPlugin is rendering JS instead of HTML.
How do I get the HtmlWebpackPlugin to render the HTML file properly?
You don't need a special loader (ejs-webpack-loader) in your config, since HtmlWebpackPlugin comes with ejs support out of the box.
Just try to remove it. :]
Related
If I run yarn run build then the HTMLWebpackPlugin will generate the index.html from a template file, but all my code runs twice because the script tag are added two times.
My index.html template file:
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= htmlWebpackPlugin.options.title %></title>
<%= htmlWebpackPlugin.tags.headTags %>
</head>
<body>
<div id="app"></div>
<%= htmlWebpackPlugin.tags.bodyTags %>
</body>
</html>
My index.html that is generated from HTMLWebpackPlugin:
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Website</title>
</head>
<body>
<div id="app"></div>
<script defer src="ecaecb1a919bc0a6e577.main.js"></script>
<script defer src="ecaecb1a919bc0a6e577.main.js"></script>
</body>
</html>
and my webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: '[fullhash].main.js',
path: path.resolve(__dirname, 'dist'),
},
mode: "development",
devServer: {
contentBase: './dist',
port: 9000,
hot: true
},
plugins: [
new HtmlWebpackPlugin({
title: "My Website",
template: path.join(__dirname, "src/webpack_template.html"),
inject: "body",
hash: false
}),
new CleanWebpackPlugin()
],
devtool: 'inline-source-map',
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ['#babel/preset-env']
}
}
}
]
}
};
So, my target is that one script tag will be at the end of the body.
Thank you very much for your help.
If you add tags to HTML, you must disable automatic injection and inject must be false in this case not "body".
Please check documentation
https://github.com/jantimon/html-webpack-plugin#options
true || 'head' || 'body' || false Inject all assets into the given template or templateContent. When passing 'body' all javascript resources will be placed at the bottom of the body element. 'head' will place the scripts in the head element. Passing true will add it to the head/body depending on the scriptLoading option. Passing false will disable automatic injections. - see the
I am a newcomer to webpack, In HTML, I want to compress and reference link CSS files
I have the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
<link rel="stylesheet" href="../src/common.css">
</head>
<body>
<div class="common-red">
hello;
</div>
<script src="main.js"></script>
</body>
</html>
js file:
import "common.css"
This is my profile:
webpack.config.js:
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: "./src/index.js",
output:{
filename: 'bundle.js',
path: path.resolve(__dirname,'dist')
},
module:{
rules:[
{
test:/\.css$/,
use:[
MiniCssExtractPlugin.loader,
"style-loader",
'css-loader'
]
},
{
test:/\.(png|svg|jpg|gif)$/,
use:[
'file-loader'
]
},
{
test:/\.(html)$/,
use:{
loader: "html-loader",
options:{
attrs:['img:src']
}
}
}
]
},
plugins:[
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
}
}
}
}
I want to compress the link CSS code on HTML, but I tried to manipulate it, I can't package the link css file on html, but I can package css in the js file.
what should I do?
Expectations:
Package the linked css file in HTML and reference it
Can you help me?
thank you
You need to import your css into your js file so that MiniCssExtractPlugin can work
Something like this
app.js
import "css/Admin/admin.css";
import "jquery/dist/jquery.min.js";
import "bootstrap/dist/js/bootstrap.min.js";
I am having difficulty configuring webpack4 to properly bundle shared dependencies.
I have two pages in my application (Page1 and Page2). Both require bootstrap, jquery as well as a custom JavaScript app called core.
Page 2 requires the same but also a custom JavaScript application called my-app and also lodash.
Since my core app will be included in all pages, I want to have jquery and bootstrap in the same bundle.
Since lodash is only required for pages running my-app, I want to include that dependency in the my-app bundle.
So I setup my app like this:
webpack.config.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
'core': './src/core/index.js',
'my-app': './src/my-app/index.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
resolve: {
alias: {
jquery: 'jquery/src/jquery',
}
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
}),
],
mode: 'development',
}
page1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page1</title>
<script src="dist/core.bundle.js"></script>
</head>
<body>
<h1>Page1</h1>
<span id="test"></span>
</body>
<script>
$(document).ready(function() {
$('#test').text('jQuery Works!');
});
</script>
</html>
page2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page1</title>
<script src="dist/core.bundle.js"></script>
<script src="dist/my-app.bundle.js"></script>
</head>
<body>
<h1>Page2</h1>
<span id="test"></span>
</body>
<script>
$(document).ready(function() {
$('#test').text('jQuery Works!');
});
</script>
</html>
(Full project: https://github.com/LondonAppDev/webpack-split-example)
When I run npx webpack, it creates core.bundle.js and my-app.bundle.js, however both of these include jquery.
Is it possible to put all "global" dependencies in core.bundle.js?
Just one thing to remember here, with webpack 4 you don't add vendor scripts as an entry to your webpack.config, just real entry scripts to your application.
WP will create an optimized bundle output for your app using the default settings.
You have to add vendor cache group to your config, in order to extract jQuery, Lodash, Bootstrap,Popper into a separate bundle:
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /node_modules/,
name: "vendor",
chunks: "all",
enforce: true
}
}
}
},
I created template in index.html to generate html-code with js, code below. My Webpack configuration also below. When I run it with webpack-dev-server, I get error: title is not defined. Somehow webpack tries to resolve 'title' by self, instead of delegate it to 'lodash/template'. Please help me fix code, I'm in despair(.
import path from 'path';
import glob from 'glob';
import webpack from 'webpack';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
const inProduction = process.env.mode === 'production';
export default {
entry: {
app: [
'./src/scripts/main.js',
],
},
output: {
path: path.join(__dirname, 'build'),
filename: '[name].[chunkhash].js',
},
module: {
rules: [
{
test: /\.s[ac]ss$/,
use: ExtractTextPlugin.extract({
use: ['css-loader', 'sass-loader'],
fallback: 'style-loader',
}),
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: '/node_modules',
},
],
},
plugins: [
new ExtractTextPlugin('[name].[chunkhash].css'),
new webpack.LoaderOptionsPlugin({
minimize: inProduction,
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, './src/index.html'),
}),
],
};
import temp from 'lodash/template'
import data from './data';
const titlePicDiscHalf = temp(document.getElementById('titlePicDiscHalf').innerHTML);
document.write(titlePicDiscHalf({ title: 'Hello World!' }));
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<script type="text/template" id="titlePicDiscHalf">
<div class="titlePicDiscHalf">
<div class="picture"></div>
<div class="title"><%=title%></div>
<div class="discription"></div>
<div class="buttons"></div>
</div>
</script>
</body>
</html>
The problem is that html-webpack-plugin uses the same template tags <%= %> to insert bundle information into template.
You have two options.
1. Change lodash.template delimiters
You could change delimiters used by client-side lodash/template to something else, so Webpack would ignore it. For example:
_.templateSettings.interpolate = /<\$=([\s\S]+?)\$>/g;
Check out this demo.
_.templateSettings.interpolate = /<\$=([\s\S]+?)\$>/g;
const temp = _.template
const titlePicDiscHalf = temp(document.getElementById('titlePicDiscHalf').innerHTML);
document.write(titlePicDiscHalf({ title: 'Hello World!' }));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
<script type="text/template" id="titlePicDiscHalf">
<div class="titlePicDiscHalf">
<div class="picture"></div>
<div class="title">
<$=title$>
</div>
<div class="discription"></div>
<div class="buttons"></div>
</div>
</script>
2. Change html-webpack-plugin delimiters
Install ejs-loader separately and configure html-webpack-plugin to use it to load your template. There you can change delimiters to yours. It could look something like this:
plugins: [
new HtmlWebpackPlugin({
template: './index.html.ejs',
})
],
module: {
rules: [
{ test: /\.ejs$/, loader: 'ejs-loader', query: {
interpolate: /<\$=([\s\S]+?)\$>/g,
evaluate: /<\$([\s\S]+?)\$>/g,
}},
]
},
Now, you can configure your template with two different set of delimiters, one for client bundle lodash template and another for html-webpack-plugin:
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title><$= htmlWebpackPlugin.options.title $></title>
</head>
<body>
<script type="text/template" id="titlePicDiscHalf">
<div class="titlePicDiscHalf">
<div class="picture"></div>
<div class="title"><%= title %></div>
<div class="discription"></div>
<div class="buttons"></div>
</div>
</script>
</body>
</html>
Note, <title><$= htmlWebpackPlugin.options.title $></title> is used by webpack, and <div class="title"><%= title %></div> is by client-side lodash.
Just in case someone else falls in the same trap where one simply wants to use a static html file with some <script> inside and subsequently encountering an error like Referenceerror: variable is not defined.
Make sure you are not using JS template strings!
E.g. instead of doing something like
function addLoginStatus(user) {
document.querySelector('.login-status').textContent = `You logged in as user.firstName`;
}
use something like this:
function addLoginStatus(user) {
document.querySelector('.login-status').textContent = 'You logged in as ' + user.firstName;
}
I found that with template strings, the webpack-html-plugin template will naturally try to interpret them, which was not intended in my case.
I know basically nothing about javascript/webpack/npm/whatever but I am trying a simple Vue.js app.
It looks like this:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vue Test</title>
</head>
<body>
<div class="vue-app">
{{ message }}
</div>
<script src="/static/testvue.bundle.js"></script>
</body>
</html>
main.js
import Vue from 'vue';
new Vue({
data: {
message: 'Testing Vue'
},
el: '.vue-app'
});
webpack.config.js
var path = require('path');
module.exports = {
entry: './main.js',
output: {
filename: 'testvue.bundle.js',
path: path.join(__dirname, '')
},
devServer: {
inline: true,
port: 8080
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015']
}
}
]
}
};
When I go to the webpage, it is blank and I see this in the "elements" in the console:
<!--function (e,n,r,o){return Ce(t,e,n,r,o,!0)}-->
Any idea what's going on and how to make it work? It's like it's trying to do something, but something doesn't line up. I've tried changing a few things that I have seen differently like using #vue-app instead of .vue-app or changing it to be under 'body' and then putting the {{message}} directly into the body but that doesn't help and I dont know what the difference is.
So, looks like the issue is that webpack wants the "ESM" version of Vue.
You can add some stuff to the webpack.config.js as per: https://v2.vuejs.org/v2/guide/installation.html
And then it seems to start working.
Code snippet
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}
I am not sure what the difference is or why this matters though.
You need to return message in data.
new Vue({
data() {
return {
message: 'Testing Vue'
}
},
el: '.vue-app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.9/vue.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vue Test</title>
</head>
<body>
<div class="vue-app">
{{ message }}
</div>
<script src="/static/testvue.bundle.js"></script>
</body>
</html>