Splitting HTML code into multiple files using webpack - html

I'm trying desperately to split my HTML into multiple files using Webpack. I've tried the "<%= require() %>" method but doesn't work. Only work with my images. See my webpack config below. Basically what I wanna do is this:
file1.html
<h1>This is my file number 1</h1>
file 2.html
<h1>This is my file number 2</h1>
<%= require("file1.html") %>
And then webpack when rendering the index.html file, will bundle those two files together.
This is my webpack.config.js:
const path = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const webpack = require("webpack");
module.exports = {
entry: "./src/assets/js/index.js",
mode: "development",
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, "dist"),
clean: {
keep: /images\//, // Keep these assets under 'ignored/dir'.
},
},
devtool: 'inline-source-map',
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "./"
}
},
{
loader: 'css-loader',
options: {importLoaders: 2, url: false},
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
config: path.resolve(__dirname, 'postcss.config.js'),
},
},
},
],
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: 'file-loader',
options: {
filename: "[name].[hash:6].[ext]",
outputPath: 'images/',
emitFile: true,
esModule: false
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
title: 'Caching',
template: "./src/template.html"
}),
new MiniCssExtractPlugin({
filename: "[name].[contenthash].css",
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}),
],
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
},
},
},
},
}
Any help is more than welcome.
Thanks in advance!

It is not really possible out-of-box with either Webpack or html-webpack-plugin. However, you have multiple possible options to achieve this.
Option 1
Read the HTML files that you intend to inject into your main HTML and then use template context using templateParameters to pass your HTML content. For example:
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const files = {
file1: fs.readFileSync('file1.html', { encoding: 'utf-8' }),
file2: fs.readFileSync('file1.html', { encoding: 'utf-8' })
};
module.exports = {
// ...other configuration
plugins: [
new HtmlWebpackPlugin({
title: 'Caching',
template: './src/template.html',
templateParameters: {
files
}
})
]
};
Your template.html file would be:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div><%= files.file1 %></div>
<div><%= files.file2 %></div>
</body>
</html>
The limitation here is that you can have includes only inside the template.html file. You cannot have recursive include like file2.html from file1.html. But this will work only if you intend to generate single HTML file.
Option 2
You can use the plugins for html-webpack-plugin. This plugin provides a nice support for partials. And, then there are few more plugins like this and this that can be used. Again these plugins will help you augment your generated html template and not support recursive include inside the html files.
Option 3
Using html-loader in combination extract-loader. This approach is flexible and can be used in multiple ways including using posthtml but it involves lots of configuration. You will have to write your own plugin in combination with other template loader that supports partials like handlebar-loader and then emit the HTML file.
Updates on comments
There are the two options you can try. First do not try to require the images inside the html partial. Instead add image using root relative paths like:
<-- partial.html --!>
<img src="/my/root/relative/image.png" />
The use Webpack copy plugin to copy the images into your dist folder.
I have not fully tried it but the second option is to disable the loader of html-webpack-plugin. Instead use html-loader with the plugin like below. By introducing the html-loader, the HTML file will be processed, the images would be picked up by the file-loader before the final HTML is seen by the html plugin.
const config = {
module: {
rules: [
{
test: /\.html$/,
loader: 'html-loader'
}],
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
})
]
};

Related

Html-loader + file-loader not bundling the correct image source

I'm planning to use Webpack for a project and I'm setting up my workflow with Html-loader + file-loader to get a production html file with dynamic src for the images, as Colt Steele teaches in this video. Here are my src/ files:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Popular on Letterboxd</title>
</head>
<body>
<img src="./assets/chira.jpg" />
</body>
</html>
index.js:
import img from './assets/chira.jpg';
import "./main.css";
And main.css
body {
background-color: darkblue;
}
These are my config files (I have an individual for dev and production and a common for both):
webpack.common.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
devtool: "none",
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
],
},
{
test: /\.html$/,
use: ["html-loader"]
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[hash].[ext]',
publicPath: 'assets',
outputPath: 'assets/img'
}
}
]
}
],
},
};
webpack.dev.js:
const path = require('path');
const common = require("./webpack.common");
const merge = require('webpack-merge');
module.exports = merge(common, {
mode: "development",
devServer: {
contentBase: path.join(__dirname, 'src'),
},
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist")
},
});
And webpack.prod.js:
const path = require('path');
const common = require("./webpack.common");
const merge = require('webpack-merge');
module.exports = merge(common, {
mode: "production",
output: {
filename: "main.[contentHash].js",
path: path.resolve(__dirname, "dist")
},
});
However, when I run npm run build, which executes this command:
"build": "webpack --config webpack.prod.js"
And I get the expected dist folder with assets/img/[name].[hash].[ext], but in my index.html I do not get the expected src tag:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Popular on Letterboxd</title>
</head>
<body>
<img src="[object Module]" />
<script type="text/javascript" src="main.e55bd4ff82bf2f5cec90.js"></script></body>
</html>
I've been trying to fix the problem for a while now, but I can't seem to get a proper answer anywhere, and nothing that I've tried have worked so far. I would appreciate if anyone who has encountered this problem can address how they fixed it, or if someone has any clue of what the problem might be and what can I do. Thanks in advance!
If you are using webpack 5 or later, the file-loader is depricated. File loader documentation
You could use the module asset/resource instead. Remove your file-loader rule and add the following rule to your module in webpack.common.js
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset/resource'
}
]
},
then add assetModuleFilename in the output in your webpack.prod.js
output: {
assetModuleFilename: "assets/img/[name].[hash][ext]"
}
and keep your html-loader rule in the webpack.common.js as it is.
References Asset Modules
If file-loader verions is 5.0 then. Adding another option on file-loader as "esModule: false" will solve this problem.
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[hash].[ext]',
publicPath: 'assets',
outputPath: 'assets/img',
esModule: false
}
}
]
}

Is there a way to route to html files with Vue router?

Hello I am using VueJS and the webpack template. I have a bunch of components I can easily display with Vue Router. However, my organization uses Robot Framework for testing and we generate an HTML page using the command:
python -m robot.testdoc /tests/directory /destination.html
This is basically how I am using the router:
import Vue from 'vue'
import Router from 'vue-router'
import Main from '#/components/Main.vue'
import Component1 from '#/components/Component1.vue'
import Component2 from '#/components/Component2.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
mode: history,
name: 'Main',
component: Main
},
{
path: '/component1',
mode: history,
name: 'Component1',
component: Component1
},
{
path: '/component2',
mode: history,
name: 'Component2',
component: Component2
}
]
})
Is there a way to route to an HTML file using Vue Router?
First you'll need html-loader:
yarn add html-loader | npm install html-loader
Then you need to update your webpack.config.js file and add an entry to your rules to handle .html extensions:
{
test: /\.(html)$/,
exclude: /(node_modules)/,
use: {
loader: "html-loader"
}
}
Then you can import your .html files like you would components:
import Destination from '/path/to/destination.html'
Now treat component as an Object and leverage the template property to serve static HTML files:
{
path: '/destination',
mode: history,
name: 'destination',
component: { template: Destination }
}
1.install html-loader
npm install --save-dev html-loader
2.use below code vue.config.js or Webpack.config.js
For webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.html$/i,
loader: 'html-loader',
},
],
},
};
For Vue cli users vue.config.js
module.exports = {
chainWebpack: config => {
config.module
.rule('html')
.test(/\.html$/)
.use('html-loader')
.loader('html-loader')
}
}
just add router in your
{
path: '/print',
name: 'print',
component: () => import('../pages/print.html'),
},
more about vue
https://cli.vuejs.org/guide/webpack.html#replacing-loaders-of-a-rule

Refusing to apply styles because MIME type

I am working on a React application that is running on the webpack-dev-server from npm. Upon running the server, I notice that I get the following message in the browser console:
"Refused to apply style from 'http://localhost:8080/css/index.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled."
I am able to fetch all of the following resources except the custom css file titled style.css. When I run application directly from the containing folder(without running on the local server), the style.css file loads without a problem.
Do I need utilize a css loader with webpack?
I already have reviewed the following post and have tried all the suggestions, but to no avail:
Stylesheet not loaded because of MIME-type
In index.html I use a link tag with the following format:
<link rel="stylesheet" type="text/css" href="css/style.css"
Here is my webpack.config.js file:
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
}
]
},
plugins: [
new HTMLWebpackPlugin({
template: './src/index.html', //source
filename: 'index.html' //destination
})
]
}
Here is my project directory structure:
src
components
css
style.css
index.html
index.js
Any help would be appreciated
So it turns out that I needed to utilize the style-loader and css-loader. I suspect that the issue was entirely with webpack-dev-server and how it was referencing the stylesheet. I am utilizing webpack 4 in case it helps anyone in the future.
My webpack.config.js now looks like this:
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
//will automatically inject bundle js into ./dist/index.html
new HTMLWebpackPlugin({
template: './src/index.html', //source
filename: 'index.html' //destination
})
]
}
Pretty simple one.
It's enough that you just need to add
< base href="/" >
on your layout section

How to import font-awesome scss correctly

I am trying to use an icon from font-awesome with webpack 4 via scss.
The webconfig file looks as following:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require("path");
const autoprefixer = require("autoprefixer");
module.exports = {
entry: ["./src/index.js"],
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.[hash].js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env", "#babel/preset-react"]
}
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: {
minimize: true
}
}
]
},
{
test: /\.(sass|scss)$/,
use: [
{
loader: "file-loader",
options: {
name: "style.css"
}
},
{ loader: "extract-loader" },
{
loader: "css-loader"
},
{
loader: "postcss-loader",
options: {
plugins: () => [autoprefixer({ grid: false })]
}
},
{
loader: "sass-loader",
options: {
includePaths: ["./node_modules"]
}
}
]
},
{
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: "file-loader",
options: {
name: "[name][hash].[ext]",
outputPath: "fonts/"
}
}
]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./public/index.html",
filename: "./index.html"
}),
new CopyWebpackPlugin([
{
from: "public"
}
])
]
};
I imported the scss as following:
#import "#fortawesome/fontawesome-free/scss/fontawesome.scss";
and use:
<a class="mdc-list-item mdc-list-item--selected demo-drawer-list-item" href="#">
<i class="fas fa-inbox mdc-list-item__graphic"></i>Inbox
</a>
It shows:
a rectangle instead an icon. What am I doing wrong?
The fully example is on github.
I hit the same issue importing the font-awesome scss into my project, this is what worked for me.
#import "~#fortawesome/fontawesome-free/scss/fontawesome";
For anyone else running into an issue with the fonts not loading (e.g. showing the empty square glyph) despite Webpack (4) compiling and file-loader copying the fonts properly, I found an option that makes everything load as expected.
First the recommended SCSS imports with ~ node_modules path as seen in other posts:
$fa-font-path: '~#fortawesome/fontawesome-pro/webfonts';
#import '~#fortawesome/fontawesome-pro/scss/fontawesome';
#import '~#fortawesome/fontawesome-pro/scss/light';
Then you'll have the file-loader config targeting font files (above). To the file-loader options you want to add the esModule: false option. Note that this could mess with other fonts you may be importing outside of SCSS.
Basically what I discovered is the fonts would get copied, but the font file urls in the compiled CSS would show as src: url([object Module]);. By default file-loader turns the imported file into a module for convenience, but of course we don't need that for fonts here, so adding the option above fixes that.
to get font-awesome for Laravel project the next worked for me:
first i had searched 'fa-fw' in public/css. assured it is absent.
npm install --save #fortawesome/fontawesome-free - that downloaded and created files to node_modules.
add to resources/sass/app.scss
#import '~#fortawesome/fontawesome-free/scss/fontawesome';
#import '~#fortawesome/fontawesome-free/scss/regular';
#import '~#fortawesome/fontawesome-free/scss/brands';
#import '~#fortawesome/fontawesome-free/scss/solid';
add to resources/sass/_variables.scss (not sure for this)
$fa-font-path: "../webfonts";
build. npm run dev
fa now in you app.css. enjoy!

Webpack is not copying images to dist folder

I'm starting with webpack, but I'm really new on this and I'm stuck right now.
My project copies my fonts correctly but not images. Now the only way I am able to make it work is by copying my images manually to the dist/img folder.
This is my config:
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var webpack = require('webpack');
var path = require("path");
module.exports = {
entry: './src/app.js',
output: {
path: path.resolve(__dirname + '/dist'),
filename: 'app.bundle.js'
// publicPath: '/dist',
},
module: {
rules:[
{
test:/\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader?sourceMap","resolve-url-loader","sass-loader?sourceMap"],
// publicPath: '/dist'
})
},
{
test: /\.(woff2?|ttf|otf|eot|svg)$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}]
// loader: 'file-loader?name=/fonts/[name].[ext]'
},
{
test: /\.(jpg|png|gif)$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'img/',
publicPath:'img/'
}
}]
}
]
},
devServer: {
contentBase: path.join(__dirname, "/dist"),
compress: true,
port: 8000,
stats: "errors-only",
open: true
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
new ExtractTextPlugin("styles.css"),
new HtmlWebpackPlugin({
title: 'Project',
hash:true,
template: './src/index.html'
})
]
}
I've tried several configurations but no solution. I also searched here for any solution but without success.
If your images are only referenced in HTML files as <img> tags, webpack by default won't pick them up because it doesn't parse HTML. You have at least 2 choices:
Use CopyWebpackPlugin to copy the files to wherever you want, this at least removes the "manual" part you mention
Move your images references to styles, where webpack can pick them up via the scss loader you are using. For example
background-image: url("img/foo.png");
There is also option import image trough JavaScript.
import '../img/image.png';
I had this problem. I didn't know that the file-loader only copies the images if you run a build, and doesn't do anything while using webpack-dev-server. My solution was just:
$ npx webpack