Body elements disappearing as I run webpack with html-webpack-plugin dependency - javascript

I have tried looking for this problem on the internet and couldn't find anything related. I am trying to build a small project where the body of my HTML will only have one div with id="content" and I have to append the rest of the elements one-by-one to this div. Now I created an element h1 and appended it to the aforementioned div and then hit the npm run build command in the terminal. As soon as webpack emits a bundle, the div id="content" disappears from the body. I am attaching the screenshot of the issue. This is how the index.html look like before I run webpack:
index.js stays the same after running webpack:
This is how index.html looks like after I run the command npm run build or npm run watch:
Here is my webpack configuration:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports ={
entry: {
index:{
import: './src/index.js'
}
},
mode: "development",
devtool: "inline-source-map",
plugins : [
new HtmlWebpackPlugin({
title: 'Resturant Page',
})
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.png|jpg|jpeg$/,
type: 'asset/resource',
},
{
test: /\.svg$/,
type: 'asset/resource',
},
{
test: /\.woff|wof$/,
type: 'asset/resource',
}
]
}
}

Try replacing the HtmlWebpackPlugin lines with following:
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html',
inject: false, // prevents adding script tag to html so you may do it manually
})
]
I suggest keeping index.html in src folder, Webpack will copy the output to dist for you.
Also, instead of inject: false it is much better to let it be added automatically for you.

Related

Webpack-dev-server HMR not working with multiple entry points

today I've noticed a strange bug (or I am to dumb?) with my webpack-dev-server.
I've got a Spring Boot App with thymleaf templates. Some pages may only load one others may have more than one js-file:
// main.js
import "../style.scss";
single.html:
<body>
<script th:src="#{/myapp/js/main.js}"></script>
</body>
multiple.html
<body>
<script th:src="#{/myapp/js/main.js}"></script>
<script th:src="#{/myapp/js/other.js}"></script>
</body>
I've splitted my config into a dev, production and common part:
webpack.common.js:
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: {
main: path.resolve(__dirname + "/src/main/js/main.js"),
other: path.resolve(__dirname + "/src/main/js/other.js"),
},
output: {
path: path.resolve(__dirname, "./src/main/resources/static/myapp"),
filename: "js/[name].js",
},
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"sass-loader",
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
// prettier-ignore
["#babel/preset-env", {
corejs: "3.6.4",
// debug: true,
useBuiltIns: "usage"
}
],
],
},
},
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "css/[name].css",
chunkFilename: "css/[name].css",
}),
],
};
webpack.dev.js
const common = require("./webpack.common");
const { merge } = require("webpack-merge");
module.exports = merge(common, {
mode: "development",
devtool: "inline-source-map",
devServer: {
proxy: {
"/": "http://localhost:8081",
},
port: 8083,
devMiddleware: {
publicPath: "/myapp",
},
},
});
The strange behaviour: If I'm editing files for the page which only a single script has been loaded (single.html), changes are applied immediately. For example changing the background color in the css file is displayed without pagereload. If I'm editing a page where multiple scripts (entry points) are used this is not working anymore. My dev-console logs the following:
[HMR] Update failed: Loading hot update chunk global failed.
(missing: http://localhost:8083/myapp/main.618757b0411fc5552e94.hot-update.js)
The first entry point / chunk (main.js) cannot be loaded, caused by the hash? I need to manually refresh the whole page, to apply changes. I've already searched for solutions and tried to apply this tip
optimization: {
runtimeChunk: {
name: 'single',
},
}
However my dev console does not log any HMR output anymore and nothing happens. It seems like HMR has stopped working in my browser. Webpack is running and bundling it correctly!
Any ideas? Thanks so far and apologizes for this wall of text.
The optimization.runtimeChunk option should be true or 'single' and not an object with name:
optimization: {
runtimeChunk: 'single',
},
As the documentation explains this is an alias for:
optimization: {
runtimeChunk: {
name: 'runtime',
},
},
Also, you'll want to include the runtime.js file on the page, however you need to do that for your set up. It should only be served in the dev environment (so for example, if NODE_ENV is "development").
In my case I have production, server-side script files alongside webpack-dev-server, and needed this option enabled so it would correctly serve runtime.js from the manifest.json:
devServer: {
devMiddleware: {
writeToDisk: true,
},
}

Get list of webpack output files and use in another module for PWA

I'm trying to build a progressive web app, with support for offline usage.
According to MDN, the way to make PWAs work offline is to add the required resources to a cache in the service worker. This requires that the service worker code knows each of the output files. Ideally, this shouldn't be harcoded, and should be generated by webpack, since it knows what files it generates.
I'm struggling to actually generate this list. From my search, there are two plugins that can generate a json file containing a list of the files - webpack-assets-manifest and webpack-manifest-plugin. I can use these in combination with separate targets to generate a manifest with the page files. But I can't import the manifest, since webpack doesn't actually write the manifest until everything is done.
How can I import a list of files that one entry point generates and use them in another entry point/module?
webpack.config.js:
const path = require('path');
const WebpackAssetsManifest = require('webpack-assets-manifest');
const frontend = {
mode: "development",
entry: {
page:"./src/page/page.tsx",
},
devtool: 'inline-source-map',
output: {
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.html?$|\.png$/,
type: "asset/resource",
generator: {
filename: "[name][ext]",
},
},
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/,
},
{
test: /\.json$/,
type: "asset/resource",
exclude: /node_modules/,
}
],
},
resolve: {
extensions: [".html", ".tsx", ".ts", ".js"],
},
plugins: [
new WebpackAssetsManifest({
output: "page-files.json",
writeToDisk: true,
}),
]
};
const serviceworker = {
mode: "development",
entry: {
serviceworker: "./src/serviceworker/serviceworker.ts",
},
devtool: 'inline-source-map',
output: {
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.html?$|\.png$/,
type: "asset/resource",
generator: {
filename: "[name][ext]",
},
},
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/,
},
{
test: /\.json$/,
resourceQuery: /link/,
type: "asset/resource",
exclude: /node_modules/,
},
{
test: /\.json$/,
resourceQuery: /str/,
type: "asset/source",
exclude: /node_modules/,
}
],
},
resolve: {
extensions: [".html", ".tsx", ".ts", ".js"],
},
};
module.exports = [frontend, serviceworker];
serviceworker.ts:
import files from "../../dist/page-files.json?str";
console.log(files);
Error is:
Module not found: Error: Can't resolve '../../dist/page-files.json?str' in '<REDACTED>/src/serviceworker'
(When I build it again, it will find the file from the previous build)
Rather than relying on pre-existing webpack plugins to generate assets, I think you're going to need to write your own plugin for this use case. And if you want that plugin to write the manifest to an entry that itself needs to be bundled/compiled, creating a child compilation in that plugin would be the way to do it.
This is unfortunately not a straightforward task, but you can refer to the source code for the workbox-webpack-plugin's InjectManifest plugin, which more or does what you describe, as inspiration.
Alternatively... you can just use InjectManifest directly, if that meets your use case. While it's part of the Workbox family of libraries, InjectManifest will only actually do two things: process the entry file you pass in as swSrc via a child compilation, and replace the symbol self.__WB_MANIFEST anywhere in that swSrc file with an array of {url: '...', revision: '...'} entries generated based on the assets in the main configuration, filtered by any include/exclude parameters.
So if you don't plan on using Workbox, you can just make use of that self.__WB_MANIFEST value from your own code.
// service-worker.ts
const manifest = self.__WB_MANIFEST || [];
self.addEventListener('install', (event) => {
// Your code to cache the contents of manifest goes here.
});
// webpack.config.js
const {InjectManifest} = require('workbox-webpack-plugin');
module.exports = {
// ...other webpack config...
plugins: [
new InjectManifest({
swSrc: 'src/service-worker.ts',
swDest: 'service-worker.js',
// ...exclude/include config here...
}),
],
};
The reason behind this behavior is that webpack runs these 2 configurations parallelly. By forcing webpack to run sequentially we can fix the problem.
To do serial processing, add module.exports.parallelism = 1; at end of your webpack config.
module.exports = [frontend, serviceworker];
module.exports.parallelism = 1;
Here is the documentation from webpack, https://webpack.js.org/configuration/configuration-types/#parallelism

How to bundle js inline into .html file

I am using webpack version 5.46 and trying to bundle inline js code into my .html file. All I found is plugins, that are not working anymore or have no compatibility with the current webpack version. At the current moment my output is:
<head><script defer="defer" src="ui.js"></script></head><div id="react-page"></div>
But here is what I want:
<div id="react-page"></div><script >console.log('here is my code directly in the .html file!')</script>
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')
module.exports = (env, argv) => ({
mode: argv.mode === 'production' ? 'production' : 'development',
// This is necessary because Figma's 'eval' works differently than normal eval
devtool: argv.mode === 'production' ? false : 'inline-source-map',
entry: {
ui: './src/ui.tsx', // The entry point for your UI code
code: './src/code.ts', // The entry point for your plugin code
},
module: {
rules: [
// Converts TypeScript code to JavaScript
{ test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ },
// Enables including CSS by doing "import './file.css'" in your TypeScript code
{ test: /\.css$/, use: ['style-loader', { loader: 'css-loader' }] },
// Allows you to use "<%= require('./file.svg') %>" in your HTML code to get a data URI
{ test: /\.(png|jpg|gif|webp|svg)$/, loader: 'url-loader' },
],
},
output: {
clean: true,
filename: '[name].js',
path: path.resolve(__dirname, 'dist'), // Compile into a folder called "dist"
},
// Tells Webpack to generate "ui.html" and to inline "ui.ts" into it
plugins: [
new HtmlWebpackPlugin({
template: './src/ui.html',
filename: 'ui.html',
inlineSource: '.(js)$',
chunks: ['ui'],
})
]
})
Any plugins for webpack supported for this purposes?
P.S. I am creating a figma plugin and here they used an outdated library, but figma requires all the content to be in the one file directly

Minify all images assets using webpack (regardless whether they were imported)

I am going through webpack tutorials and it teaches how it is possible to minify and output images that have been imported in main index.js file.
However I would like to minify all image assets, regardless whether they were imported in the index.js or not. Something that was easily done in gulp by having a watch set up on the folder. Does webpack follow same format?
This is my webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules : [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
},
{
loader: 'image-webpack-loader',
}
]
}
]
}
};
No, webpack does not follow the same "logic" as gulp. Webpack """""watches""""" for changes in files that are linked throughout the entire dependency tree. This means that the file you wan't to touch HAS TO BE imported somewhere.

webpack-dev-server watches and compiles files correctly, but browser can't access them

EDIT: Link to github repo where this example is hosted is here in case someone wants to run it
I'm getting the near exact same problem as another user (you can find the question here), in that running the webpack-dev-server does actually compile and watch files correctly (seeing the console output in the terminal), but the browser still can't view my site correctly. This is my webpack.config.js file:
var webpack = require('webpack'),
path = require('path'),
// webpack plugins
CopyWebpackPlugin = require('copy-webpack-plugin');
var config = {
context: path.join(__dirname,'app'),
entry: './index.js',
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js',
publicPath: path.join(__dirname, 'public')
},
devServer: {
// contentBase: './public/'
},
plugins: [
// copies html to public directory
new CopyWebpackPlugin([
{ from: path.join(__dirname, 'app', 'index.html'),
to: path.join(__dirname, 'public')}
]),
// required bugfix for current webpack version
new webpack.OldWatchingPlugin()
],
module: {
loaders: [
// uses babel-loader which allows usage of ECMAScript 6 (requires installing babel-preset-es2015)
{test: /\.js$/, loader: 'babel', exclude: /node_modules/, query: { presets: ['es2015']}},
// uses the css-loader (loads css content) and style-loader (inserts css from css-loader into html)
{test: /\.css$/, loader: 'style!css', exclude: /node_modules/}
]
}
};
module.exports = config;
And this is my directory structure:
+--- webpack/
+--- app/
+--- index.html
+--- index.js
+--- styles.css
+--- package.json
+--- webpack.config.js
Currently, running webpack-dev-server outputs the following in the browser (note the lack of the public/ directory which is where webpack normally outputs my html and javascript bundle):
EDIT: Adding the devServer.contentBase property and setting it to public gets the browser to return a 403 error not found as shown here:
Okay, so I was able to reproduce the issue that you have on my project. To fix the issue I changed some things.
Here is what I have set up. I'm defining a bit less in the output and using jsx instead of js, but the results should be the same. You can replace my src with wherever your source code is.
const config = {
entry: './src/App.jsx',
output: {
filename: 'app.js'
},
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react', 'stage-0'],
plugins: ['add-module-exports']
}
},
{
include: /\.json$/, loaders: ['json-loader']
},
{
test: /\.scss$/,
loaders: ['style', 'css?modules', 'sass']
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
loader: 'file?name=fonts/[name].[ext]'
}
]
},
plugins: [
new webpack.ProvidePlugin({
'Promise': 'exports?module.exports.Promise!es6-promise',
'fetch': 'imports?self=>global!exports?global.fetch!isomorphic-fetch'
}),
new webpack.IgnorePlugin(/^\.\/locale$/, [/moment$/]),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
],
resolve: {
root: path.resolve('./src')
},
devServer: {
contentBase: 'src'
}
};
So basically you would want this output in terminal:
webpack result is served from / - tells us that whatever we build from will be at the root
content is served from src - tells us that it's building from that directory
Hope this helps

Categories

Resources