Workbox service worker + manifest.json setup webpack 4 - javascript

I’m trying to create a webpack builder with workbox, as i can see Vue CLI 3 is using.
In my package.json file, i’ve made a “build” to build and compile the files and a “dev” to run it.
"build": "webpack --mode development",
"dev": "nodemon --inspect --watch webpack.config.js main.js"
I use InjectManifest from workbox-webpack-plugin to handle the build in my webpack.config.js file. I've tryed different options to see what they did.
webpack.config.js
const path = require('path')
const webpack = require('webpack')
const HTMLWebpackPlugin = require('html-webpack-plugin')
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin')
const {GenerateSW, InjectManifest} = require('workbox-webpack-plugin')
module.exports = {
mode: 'production',
entry: {
app: [
'#babel/polyfill',
'./src/main.js'
]
},
output: {
path: path.resolve(__dirname, 'dist/'),
filename: '[name]-[hash].js',
publicPath: '/'
},
devServer: {
inline: false,
contentBase: "./dist",
overlay: true,
hot: true,
stats: {
colors: true
}
},
module: {
rules: [
{
test: /\.html$/,
use: {
loader: 'html-loader'
}
},
{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['#babel/preset-env']
}
},
{
test: /\.css$/,
loader: [
'style-loader',
'css-loader'
]
},
{
test: /\.scss$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader", options: {
sourceMap: true
}
},
{
loader: "sass-loader", options: {
sourceMap: true
}
}
]
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HTMLWebpackPlugin({
template: './public/index.html'
}),
// new GenerateSW({
// include: [/\.html$/, /\.js$/, /\.css$/],
// exclude: '/node_modules'
// }),
new InjectManifest({
swSrc: './src/js/registerServiceWorker.js',
swDest: '/serviceworker.js',
exclude: '/node_modules'
})
// service worker caching
// new SWPrecacheWebpackPlugin(
// {
// cacheId: 'webpack-project',
// filename: 'service-worker.js',
// staticFileGlobs: [
// 'dist/**/*.{js,css}',
// '/'
// ],
// minify: true,
// stripPrefix: 'dist/',
// dontCacheBustUrlsMatching: /\.\w{6}\./
// }
// )
]
}
When i build the project, i creates the following files:
app-e8dd91....js
index.html
precache-manifest.d92js8....js
serviceworker.js
index.html file only adds the app-e8d91....js file as a script leaving the precached manifest file and service worker not being used, which results in that there's no service worker to my localhost.
I have a registerServiceWorker.js file which is being referenced in the injectManifest plugin.
registerServiceWorker.js
import { register } from "register-service-worker";
const path = require('path')
register(path.resolve(__dirname, 'service-worker.js'), {
ready() {
console.log(
"App is being served from cache by a service worker."
);
},
cached() {
console.log("Content has been cached for offline use.");
},
updated() {
console.log("New content is available; please refresh.");
},
offline() {
console.log(
"No internet connection found. App is running in offline mode."
);
},
error(error) {
console.error("Error during service worker registration:", error);
}
});
How do i make my build create and add the service worker + manifest.json file, so it works on localhost and also works offline?
File structure:
webpack.config.js
server.js (handle server + webpackHotMiddleware + express)
dist (builded folder)
src (folder)
main.js
js (folder)
registerServiceWorker.js
public (folder)
index.html
manifest.json
favicon.ico

Related

How to simply load images with src attribute with webpack 4?

Im stuck and can't get to work my webpack configuration for loading images with src attribute from HTML. I cloned a repo with full setup of webpack, but I know there is a way to simply customize and load images directly from HTML.
Webpack.config.js file:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
entry: {
main: "./src/index.js"
},
output: {
path: path.join(__dirname, "../build"),
filename: "[name].bundle.js"
},
mode: "development",
devServer: {
contentBase: path.join(__dirname, "../build"),
compress: true,
port: 3000,
overlay: true
},
devtool: "cheap-module-eval-source-map",
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader" // transpiling our JavaScript files using Babel and webpack
}
},
{
test: /\.(sa|sc|c)ss$/,
use: [
"style-loader", // creates style nodes from JS strings
"css-loader", // translates CSS into CommonJS
"postcss-loader", // Loader for webpack to process CSS with PostCSS
"sass-loader" // compiles Sass to CSS, using Node Sass by default
]
},
{
test: /\.(png|svg|jpe?g|gif)$/,
use: [
{
loader: "file-loader", // This will resolves import/require() on a file into a url
and emits the file into the output directory.
options: {
name: "[name].[ext]",
outputPath: "assets",
}
},
]
},
{
test: /\.html$/,
use: {
loader: "html-loader",
options: {
attrs: ["img:src", ":data-src"],
minimize: true
}
}
}
]
},
plugins: [
// CleanWebpackPlugin will do some clean up/remove folder before build
// In this case, this plugin will remove 'dist' and 'build' folder before re-build again
new CleanWebpackPlugin(),
// The plugin will generate an HTML5 file for you that includes all your webpack bundles
in
the body using script tags
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "index.html"
}),
Before this project I was able to make that images would simply load from HTML but now ironicly im stuck and can't get this working.
Any help will be very appriciated.
When loading a image directly form HTML, I get the following error:
Error: Child compilation failed:
Module not found: Error: Can't resolve '
./src/assets/images/portret.jpg' in '/home/viktoras/www/sites/painter-new/src':
You can do this:
<img src="<%=require('./src/assets/logo.png')%>">
Plugin Conf
$new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html'
}),

Webpack4 how to load images and custom javascript directly from HTML file?

I want to load images directly from HTML with Webpack 4 and add custom Javascript files to my HTML file but both files inspected at console show Not found 404.
How to properly load images and Javascipt files with Webpack 4?
My Webpack 4 config file:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
entry: {
main: "./src/index.js"
},
output: {
path: path.join(__dirname, "../build"),
filename: "[name].bundle.js"
},
mode: "development",
devServer: {
contentBase: path.join(__dirname, "../build"),
compress: true,
port: 3000,
overlay: true
},
devtool: "cheap-module-eval-source-map",
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader" // transpiling our JavaScript files using Babel and webpack
}
},
{
test: /\.(sa|sc|c)ss$/,
use: [
"style-loader", // creates style nodes from JS strings
"css-loader", // translates CSS into CommonJS
"postcss-loader", // Loader for webpack to process CSS with PostCSS
"sass-loader" // compiles Sass to CSS, using Node Sass by default
]
},
{
test: /\.(png|svg|jpe?g|gif)$/,
use: [
{
loader: "file-loader", // This will resolves import/require() on a file into a url and emits the file into the output directory.
options: {
name: "[name].[ext]",
outputPath: "assets",
}
},
]
},
{
test: /\.html$/,
use: {
loader: "html-loader",
options: {
attrs: ["img:src", ":data-src"],
minimize: true
}
}
}
]
},
plugins: [
// CleanWebpackPlugin will do some clean up/remove folder before build
// In this case, this plugin will remove 'dist' and 'build' folder before re-build again
new CleanWebpackPlugin(),
// The plugin will generate an HTML5 file for you that includes all your webpack bundles in the body using script tags
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "index.html"
}),
]
My webpack.prod.js file:
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const TerserJSPlugin = require("terser-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-
plugin");
const BrotliPlugin = require("brotli-webpack-plugin");
const PurgecssPlugin = require('purgecss-webpack-plugin');
const glob = require("glob");
module.exports = {
entry: {
main: "./src/index.js"
},
output: {
path: path.join(__dirname, "../build"),
filename: "[name].[chunkhash:8].bundle.js",
chunkFilename: "[name].[chunkhash:8].chunk.js"
},
mode: "production",
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader" // transpiling our JavaScript files using
Babel and webpack
}
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader", // translates CSS into CommonJS
"postcss-loader", // Loader for webpack to process CSS with
PostCSS
"sass-loader" // compiles Sass to CSS, using Node Sass by
default
]
},
{
test: /\.(png|svg|jpe?g|gif)$/,
use: [
{
loader: "file-loader", // This will resolves import/require()
on a file into a url and emits the file into the output directory.
options: {
name: "[name].[ext]",
outputPath: "assets/"
}
},
]
},
{
test: /\.html$/,
use: {
loader: "html-loader",
options: {
attrs: ["img:src", ":data-src"],
minimize: true
}
}
}
]
},
optimization: {
minimizer: [new TerserJSPlugin(), new OptimizeCSSAssetsPlugin()],
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all"
}
},
chunks: "all"
},
runtimeChunk: {
name: "runtime"
}
},
plugins: [
// CleanWebpackPlugin will do some clean up/remove folder before build
// In this case, this plugin will remove 'dist' and 'build' folder
before re-build again
new CleanWebpackPlugin(),
// PurgecssPlugin will remove unused CSS
new PurgecssPlugin({
paths: glob.sync(path.resolve(__dirname, '../src/**/*'), { nodir:
true })
}),
// This plugin will extract all css to one file
new MiniCssExtractPlugin({
filename: "[name].[chunkhash:8].bundle.css",
chunkFilename: "[name].[chunkhash:8].chunk.css",
}),
// The plugin will generate an HTML5 file for you that includes all
your webpack bundles in the body using script tags
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "index.html"
}),
My project nesting:
--Build
--src
----html
----js
----styles
----assets
------images
I want that files would load simply:
<img src="assets/images/myimage.jpg">
<srcipt src="js/custom.js"></script>
Any help would be appreciated.
Ironically this is my second project with Webpack 4 and this time I can't fix this issue, the first time there was no problem.
This is how img work in Webpack:
<img src=require("./assets/images/myimage.jpg")>
if you use "require" with file-loader, you need to add default.
<img src=require("./assets/images/myimage.jpg").default>
For script tag, src should be your output bundle.js file; because Webpack writes all the code into the bundle.js with the help of Babel. So when browser request html, browser will have only one Javascript file which is the bundle to download. That is the whole point of bundle, smaller bundle.js is better for performance.
// According to your Webpack config, it is main.bundle.js
<srcipt src="main.bundle.js"></script>
but in order to browser to use this main.bundle.js, it has to be publicly available. So you need express to define the public folder, when browser looks for main.bundle.js, you app will be looking into the public folder and if that file exists, it will ship it to the browser.
const express = require("express");
const server = express();
const staticMiddleware = express.static("build");
server.use(staticMiddleware);

Using Htmlwebpackplugin with Webpack-Dev-Middleware

Short Summary
Is combining htmlwebpackplugin functionality with webpack-dev-middleware impossible because of dev-middleware's reliance on files in memory? Screenshots of script outputs at bottom of this post. Because I've chosen to implement cache-hashed filenames in my production config, I can't seem to use dev-middleware anymore.
My Setup
I have 2 main configurations for my webpack instance. One for development (with hot reload) and one for production. I utilize webpack-merge to create a common.config that I'll eventually extract commonalities between the two configurations (for now it's fairly blank). In terms of app setup I have an API run separately in Python.
The problem
On my production config I'm using splitchunks for vendor/bundle splitting as well as some minimizations. It works perfectly fine. However, when I'm trying to run my development environment, although it's creating the the appropriate bundles for development [i.e. without the hashing] according to the terminal, the index.html file is unable to be found (likely because webpack-dev-middleware looks for things in memory). As a result, I can't see my development environment and I can't see any of the hot reload changes? Previously I would generate all my bundle files with npm run build:production and then use NPM start. I imagine dev-middleware would overlay it's in-memory version of the bundle.js changes over my file on disk, but now that I'm using hashed filenames on prod I can't really do that anymore?
Package.json scripts
"scripts": {
"clean": "rimraf dist/*.{js,css,eot,woff,woff2,svg,ttf,jpg,map,json}",
"build":
"webpack -p --progress --verbose --colors --display-error-details --config webpack/common.config.js",
"build:production": "npm run clean && npm run build",
"flow": "flow",
"lint": "eslint src",
"start": "nodemon bin/server.js",
The relevant parts of server.js
(function initWebpack() {
const webpack = require('webpack');
const webpackConfig = require('./webpack/common.config');
const compiler = webpack(webpackConfig);
app.use(
require('webpack-dev-middleware')(compiler, {
noInfo: true,
publicPath: webpackConfig.output.publicPath,
}),
);
app.use(
require('webpack-hot-middleware')(compiler, {
log: console.log,
path: '/__webpack_hmr',
heartbeat: 10 * 1000,
}),
);
app.use(express.static(path.join(__dirname, '/')));
})();
app.get(/.*/, (req, res) => {
res.sendFile(path.join(__dirname, '/dist/index.html'));
});
common.config.js
const path = require('path');
const merge = require('webpack-merge');
// const HtmlWebpackPlugin = require('html-webpack-plugin');
const development = require('./dev.config');
const production = require('./prod.config');
const TARGET = process.env.npm_lifecycle_event;
const PATHS = {
app: path.join(__dirname, '../src'),
build: path.join(__dirname, '../dist'),
nodeModulesDir: path.join(__dirname, 'node_modules'),
indexFile: path.join(__dirname, './src/index'),
};
process.env.BABEL_ENV = TARGET;
const common = {
entry: [PATHS.app],
output: {
path: PATHS.build,
},
};
if (TARGET === 'start' || !TARGET) {
module.exports = merge(development, common);
}
if (TARGET === 'build' || !TARGET) {
module.exports = merge(production, common);
}
dev.config.js
const webpack = require('webpack');
const path = require('path');
const fs = require('fs');
require('babel-polyfill').default;
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PATHS = {
app: path.join(__dirname, '../src'),
};
module.exports = {
devtool: 'cheap-module-eval-source-map',
entry: ['webpack-hot-middleware/client', './src/index'],
mode: 'development',
output: {
publicPath: '/dist/',
},
resolve: {
extensions: ['.jsx', '.js', '.json', '.scss', '.less'],
modules: ['node_modules', PATHS.app],
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
'postcss-loader',
],
},
{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
},
},
'postcss-loader',
'sass-loader',
],
},
{
test: /\.less$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
},
},
'postcss-loader',
{
loader: 'less-loader',
options: {
//addlater
},
},
],
},
{
test: /bootstrap-sass\/assets\/javascripts\//,
use: [
{
loader: 'imports-loader',
options: {
jQuery: 'jquery',
},
},
],
},
{
test: require.resolve('jquery'),
use: [
{
loader: 'expose-loader',
options: '$',
},
{
loader: 'expose-loader',
options: 'jQuery',
},
],
},
{
test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 50000,
mimetype: 'application/font-woff',
},
},
],
},
],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"',
},
__DEVELOPMENT__: true,
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.ProvidePlugin({
jQuery: 'jquery',
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index_template.html',
}),
],
};
Here's an example of what my index.html file looks like after using npm run build:production. As you can see, the in-memory version of index.html probably can't work with this anymore with the hashed filenames?
<link href="/dist/vendor.a85f.css" rel="stylesheet"><link href="/dist/main.4d1e.css" rel="stylesheet"></head>
<body>
<div id="root"></div>
<script type="text/javascript" src="/dist/manifest.81a7.js"></script><script type="text/javascript" src="/dist/vendor.99aa.js"></script><script type="text/javascript" src="/dist/main.6eb4.js"></script></body>
Other notes:
On latest version of webpack 4.
My production version works fine
Any help much appreciated.
UPDATE:
I've swapped out rimraf dist for clean webpack plugin and moved it to my common.config. That way on each build it's doing the clean before generating index.html. However, I've noticed that when I use npm start, while the output in terminal is showing that files are emitted....I can't find them anywhere? After investigated webpack-dev-middleware, it seems they store things in memory. This is probably the core problem. How can I tie htmlwebpack plugin together with something like dev-middleware if it's in memory or perhaps I need to maintain a separate index.html file? I'm guessing the reason why this flow worked previously was because I had static names for bundle.js for both prod and dev so the in-memory version had no problem. now that the names are hashed from the prod version...it doesn't know what to do?

Webpack - Folder Access Outside Application Folder

I have code base, the folder structure for which looks like this :
|- build\
|- node_modules\
|- apps\
|--- app_no_1\
|----- index.js
|- src\
|--- modules\
|----- form-login\
|------- Form.jsx
|- package.json
|- webpack.config.js
....
The app_no_1\ folder holds the index file for its React app. However, the modules are sat within the src\ folder. When I import the component from the src directory into the app, I get the error:
bundle.js:41448 Uncaught Error: Module parse failed: Unexpected token (15:18)
You may need an appropriate loader to handle this file type.
| // );
Is there some webpack configuration option I am missing which is required for access to files outside the app's folder? My webpack.config.js is this:
const path = require('path');
const merge = require('merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PATHS = {
app: path.join(__dirname, 'apps'),
appAthenaTrader: path.join(__dirname, 'apps/athenaTrader'),
appAthenaFinancier: path.join(__dirname, 'apps/athenaFinancier'),
build: path.join(__dirname, 'build'),
modules: path.join(__dirname, 'src/modules')
};
const common = {
output: {
path: PATHS.build,
publicPath: '/',
filename: 'bundle.js'
},
resolve: {
alias: {
modules: PATHS.modules
},
extensions: ['.js', '.jsx', '.json'],
modules: [PATHS.modules, 'node_modules']
},
module: {
rules: [
{
test: /\.jsx?$/,
include: PATHS.app,
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
]
},
{
test: /\.scss$/,
include: PATHS.app,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader'
}
]
},
{
test: /\.(jpg|png)$/,
include: PATHS.app,
use: {
loader: 'file-loader?name=[name].[ext]'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'Satoshi Ltd - Athena'
})
]
};
const generateEntry = env => {
const entryVariable = {
entry: {
app: ''
}
};
if (env.app === 'athena-trader') {
entryVariable.entry.app = PATHS.appAthenaTrader;
} else if (env.app === 'athena-financier') {
entryVariable.entry.app = PATHS.appAthenaFinancier;
// } else ...
}
return entryVariable;
};
const devServer = {
devServer: {
stats: 'errors-only',
host: process.env.HOST || 'localhost',
port: process.env.PORT || 3000
},
devtool: 'inline-source-map'
};
const generateConfig = env => {
const entry = generateEntry(env);
if (env.profile === 'development') {
return merge(common, entry, devServer);
}
return merge(common, entry);
};
module.exports = generateConfig(process.env);
I should note that when the folder is brought inside the app_no_1, the app functions fine, i.e. it is able to execute the component & display it. However, the above folder structure is not being accepted for the apps.
The issue is in your babel-loader configuration. Webpack is complaining that it doesn't know how to parse your files (I'm assuming it's JSX).
In your configuration, you have:
module: {
rules: [
{
test: /\.jsx?$/,
include: PATHS.app,
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
]
},
// ...
]
include tells webpack to use babel-loader on any files located inside PATHS.app. When it looks at your files located in PATHS.modules, it doesn't use babel-loader. This is when webpack shows that Module parse failed error.
To fix this, you can update your include value to something like this:
include: [PATHS.app, PATHS.modules]
An alternative is to use exclude instead of include.
// assuming you want to only ignore node_modules
exclude: /node_modules/
I also made a barebones example of this on Github.

Uncaught ReferenceError: require is not defined, webpack error

I'm using node modules for a client application in the browser. I get
'Uncaught ReferenceError: require is not defined' when I try to require a constructor function from a neighbouring file into the app.js. app.js and Desktop.js are located in the same folder so the relative link path should be correct.
app.js
console.log('hello')
const Desktop = require('./Desktop')
let newVar = new Desktop()
console.log(newVar.height)
Desktop.js
function Desktop () {
this.height = 3
}
module.exports = Desktop
webpack is also installed with the following config settings:
var path = require('path')
module.exports = {
entry: './src/js/app.js',
output: {
filename: 'build.js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
contentBase: path.join(__dirname, 'src'),
watchContentBase: true,
port: 4000,
public: 'localhost:4000'
},
devtool: 'cheap-eval-source-map',
module: {
rules: [
{
// set up standard-loader as a preloader
enforce: 'pre',
test: /\.jsx?$/,
loader: 'standard-loader',
exclude: /(node_modules)/,
options: {
// Emit errors instead of warnings (default = false)
error: false,
// enable snazzy output (default = true)
snazzy: true
}
},
{
test: /\.css$/,
loader: 'style-loader!css-loader'
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
}
}
Is there any way to solve this?

Categories