Save files in cache with sw-precache and React - reactjs

Does anyone know how I can cache my APP_SHELL files using sw-precache plugin and React?
The application was created with the cra command and I have managed to read my own service-worker. For the build and to take my files I have the following configuration:
module.exports = {
staticFileGlobs: [
'build/static/css/**.css',
'build/static/js/**.js',
'index.html',
'/'
],
swFilePath: './build/service-worker.js',
stripPrefix: 'build/',
importScripts: (['./service-worker-custom.js']),
handleFetch: false,
minify: true,
}
But I do not know in which part to indicate that I need to cache the APP_SHELL of my PWA.
I have tried to do it from my custom service worker but I can not register the files correctly, I have tried it in this way:
const cacheUrls = [
'/',
'build/static/css/**.css',
'build/static/js/**.js',
'build/media/**.jpg'
];
const cacheUrls = [
'/',
'/index.html',
'/static/css/',
'/static/js/',
'/static/media/',
];
const cacheUrls = [
'/',
'build/static/css/*.css',
'build/static/css/**/*.css',
'build/static/js/*.js',
'build/static/js/**/*.js',
'build/media/**/*.jpg',
'build/media/*.jpg'
];
But in no way registers the files in cache, only the index.html and the others when reviewing their conditional from the chrome console have the same content as index.html.
Thanks!

Related

webpack-dev-server - Remove URI fragment from URL when live reloading

I'm dealing with an Angular 1 application that uses URI fragments for routing purposes.
For example:
http://localhost:3000/#/home
http://localhost:3000/#/menu
http://localhost:3000/#/list
Rather than:
http://localhost:3000/home
http://localhost:3000/menu
http://localhost:3000/list
When I'm using Webpack Dev Server to launch a local server with hot reloading, every time I apply some changes, Webpack reloads the URL but keeping the "/#/route" part.
Unfortunately, due to some code restrictions, I need to always reload the page from "/" because some redirections are applied internally when the app launches the first time and the app will crash if you try to start directly from any URI fragment.
I tried to use the "proxy" and "historyApiFallback" configurations in webpack.config.js like:
devServer: {
proxy: {
'/#': {
target: 'http://localhost:3000',
pathRewrite: { '^/#': '' }
}
}
And:
devServer: {
historyApiFallback: {
verbose: true,
rewrites: [{ from: /^\/#/, to: '/' }]
}
}
But so far, I didn't have any luck. The URL keeps the hash when reloading.
Any suggestions or ideas to configure Webpack Dev Server to always start from "/" and remove the hash URI?

After deploy projects configured with Vercel or Nextjs rewriting can't find assets path

I have the following structure to use one domain to multiple projects:
A repo that contains a single vercel.json file to configure path to others projects
A repo that contains a home app example
A repo that contains a checkout app example
The vercel.json works pretty great and is deployed in nizen.com.br, here is how is it:
// vercel.json
{
"rewrites": [
{
"source": "/checkout",
"destination": "https://checkout.nizen.com.br/checkout"
},
{
"source": "/",
"destination": "https://home.nizen.com.br"
}
]
}
So if we go to nizen.com.br we get the home app example without styles and a bunch of errors in console trying to get the assets of home.nizen.com.br in nizen.com.br and the same happens to checkout app.
Here is one of the similar errors that appear in console on Home app:
And here is the checkout app:
About the app next.config.js files, only Home app stays untouched and Checkout app has a basePath added with value "/checkout" as follows:
// Checkout app next.config.js
module.exports = {
reactStrictMode: true,
basePath: "/checkout",
};
I have tried to use the Next rewrites in hope that will solve the problem but it didn't work.
// example of rewrite that I did
module.exports = {
async rewrites() {
return [
{
source: "/",
destination: "https://home.nizen.com.br",
},
];
},
};
I tried to pass :path* and :path in source and destination, as well use fallback and even change the destination to checkout app to see if something change and none of that worked.
If I got to https://home.nizen.com.br the app runs perfect and same to checkout. What am I doing wrong?
I have solved this question changing the strategy. Instead of using a repo with vercel.json to route the micro-frontends, I'm using the home app as the project hosted in nizen.com.br and it's next.config.js file is the one who makes the magic happen!
//Home App next.config.js file
module.exports = {
reactStrictMode: true,
async rewrites() {
return {
fallback: [
{
source: "/checkout",
destination: "https://checkout.nizen.com.br/checkout",
},
{
source: "/checkout/:path*",
destination: "https://checkout.nizen.com.br/checkout/:path*",
},
],
};
},
};
Both the source and destination must point to the same sub-path (/checkout) and both fallback blocks are necessary. I'm using Next version 12 by the time I'm writing this.

Webpack Dev Server - favicon not showing on localhost but works on external URL

I have a strange problem, I was wondering if anyone has experienced this. I have webpack in my app to bundle, serve and everything in between. I have noticed that when I compile and run webpack-devserver from my gulp file everything goes as expected and in my terminal I get the following:
webpack: Compiled successfully.
2017-11-30T11:46:05.288Z - error:
[Browsersync] Proxying: http://localhost:3001
[Browsersync] Access URLs:
--------------------------------------
Local: http://localhost:3002
External: http://10.101.51.117:3002
--------------------------------------
UI: http://localhost:3003
UI External: http://10.101.51.117:3003
--------------------------------------
Now when I access my app via 'localhost' no Favicon is shown in the browser tab for that page, there is no 404 in the console and there is no request for the favicon in the Developer Tools Network Tab? Now should I use the external URL and type http://10.101.51.117:3002 into the browser the Favicon is there in the page tab however there is no request for the favicon in the Developer Tools Network Tab.
Now when I make a direct call in the browser to the favicon to http://localhost:3002/assets/favicon.ico the favicon is served in the browser window so it seems like server is bundling the Favicon?
In my HTML I have the tag <link rel="shortcut icon" href="assets/favicon.ico"> nothing strange there and in my webpack.common.js file I have the following (I have removed some items here for simplicity)
module.exports = {
// lots of things here..
module: {
rules: [
// stuff here has been removed
{
test: /\.html$/,
use: 'html-loader'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot)$/,
use: 'file-loader?name=assets/[name].[hash].[ext]'
},
{
test: /\.(ico)$/,
use: 'file-loader?name=assets/[name].[ext]'
},
I can't think what or why the favicon isn't being shown in the tab when I use the localhost URL? If anyone has experienced this and resolved the issue and advice would be appreciated.
Please note that the Favicon is not cached from before as I have changed the favicon name, location and I cleared the browser cache.
If you are using HtmlWebpackPlugin then you can include favicon: './src/assets/favicon.ico', property there.
plugins: [
new HtmlWebpackPlugin({
template: './src/template.html',
favicon: './src/assets/favicon.ico',
filename: './index.html',
}),
],
BTW - starting from Chrome 80 you can use favicon.svg favicons as well.
Apparently your favicon should work but doesn't. Two things you may want to try:
Favicon caching is VERY nasty. I suggest you to test your favicon with a browser you normally don't use (so it didn't cache your icon).
Run the favicon checker. As your site is hosted locally, you will have to use ngrok to make is accessible from outside.
Disclosure: I'm the author of RealFaviconGenerator
I would use the file loader like this:
Install the package
npm i --save-dev file-loader
Then add the following to your webpack config (default webpack.config.js)
{
test: /\.(png|svg|jpg|gif|ico)$/,
use: ['file-loader?name=[name].[ext]']
}
This should move the files to your dist folder.
How about copying favicon.ico by using copy-webpack-plugin.
// File: webpack.config.js
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
...
plugins: [
new CopyPlugin({
patterns: [
{ from: "favicon.ico" },
],
}),
]
}
Don't forget to reload browser page ignoring caches.

Matching root route in webpack-dev-server's historyApiFallback`

Sample repo demonstrating the issue is here.
I'm trying to set up webpack dev server so that:
Requests to / are served by public/landing.html (a static landing page)
Requests to anything else are served by my React app
Using create-react-app, and based on webpack-dev-server's options, I've set up my webpackDevServer.config.js as follows:
historyApiFallback: {
// Paths with dots should still use the history fallback.
// See https://github.com/facebookincubator/create-react-app/issues/387.
disableDotRule: true,
rewrites: [
// shows views/landing.html as the landing page
{ from: /^\/$/, to: 'landing.html' },
// shows views/subpage.html for all routes starting with /subpage
{ from: /^\/subpage/, to: 'subpage.html' },
// shows views/404.html on all other pages
{ from: /./, to: '404.html' },
],
},
And when I start webpack here's what I see:
Requests to /subpage are routed correctly to subpage.html
Requests to /foo are routed correctly to 404.html. Eventually, these would be handled by my React app.
Requests to / are routed incorrectly to my React app.
How can I get landing.html to respond to requests at /?
I ended up opening a bug request with webpack-dev-middleware and discovered it was not a bug but a failure of configuration.
Specifically, the issue is using HtmlWebpackPlugin alongside historyApiFallback. I believe plugins are processed before the regex matching, and HtmlWebpackPlugin's default file output is index.html; this means that out of the box, / will always be routed to the HtmlWebpackPlugin output file.
The solution to this is to set a custom filename in HtmlWebpackPlugin, which allows you to control the matching again. Here's a sample repo demonstrating the fix and here's the webpack config:
module.exports = {
context: __dirname,
entry: [
'./app.js',
],
output: {
path: __dirname + "/dist",
filename: "bundle.js"
},
devServer: {
publicPath: "/",
// contentBase: "./public",
// hot: true,
historyApiFallback: {
// disableDotRule: true,
rewrites: [
{ from: /^\/$/, to: '/public/index.html' },
{ from: /^\/foo/, to: '/public/foo.html' },
{ from: /(.*)/, to: '/test.html' },
],
}
},
plugins: [
new HtmlWebpackPlugin({
title: "Html Webpack html",
hash: true,
filename: 'test.html',
template: 'public/plugin.html',
}),
],
};
Two thoughts come to mind:
What you want to do is not possible with Webpack Dev Server (as far as I'm aware)
And, once npm run build is run and deployed the deployed app would not follow the same rules as configured in Webpack Dev Server
Even if I'm mistaken on #1, #2 seems like a bigger issue if you ever plan to deploy the app. This leads me to recommend an alternate setup which will work on dev and production (or wherever it's deployed).
A few options:
setup the app as a single-page app (SPA) and use React Router to serve the static routes (/ and /subpage) and wildcards (everything else)
setup node (or another server) to serve the static routes (/ and /subpage) and wildcards (everything else)
An Attempt
In an attempt to setup the routes I was able to achieve this setup:
Display landing.html when / is requested
Display subpage.html when /subpage is requested
Display the React App at a specific path, like app.html
To do this make the following changes:
Move /public/index.html to /public/app.html
mv ./public/index.html ./public/app.html
In /config/webpackDevServer.config.js, update historyApiFallback to:
historyApiFallback: {
// Paths with dots should still use the history fallback.
// See https://github.com/facebookincubator/create-react-app/issues/387.
disableDotRule: true,
rewrites: [
// shows views/landing.html as the landing page
{ from: /^\/$/, to: "landing.html" },
// shows views/subpage.html for all routes starting with /subpage
{ from: /^\/subpage/, to: "subpage.html" },
// shows views/app.html on all other pages
// but doesn't work for routes other than app.html
{ from: /./, to: "app.html" }
]
}
In /config/paths.js update appHTML to:
appHtml: resolveApp("public/app.html")
In /config/webpack.config.dev.js, updateplugins` to include:
new HtmlWebpackPlugin({
filename: "app.html",
inject: true,
template: paths.appHtml
})
Requesting a random URL, like localhost:3000/foo will return a blank page but contains the HTML from the app.html page without the bundled <script> tags injected. So maybe you can find a solution to this final hurdle.

React Hot Loader not updating as expected

I am using react-hot-loader and webpack. I also use webpack-dev-server together with an express backend.
This is my relevant webpack config for development:
var frontendConfig = config({
entry: [
'./src/client/app.js',
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/dev-server'
],
output: {
path: targetDir,
publicPath: PROD ? '/build/assets/' : 'http://localhost:3000/build/assets/' ,
filename: 'app.js'
},
module: {
loaders: [
{test: /\.js$/,
exclude: /node_modules/,
loaders: PROD ? [babelLoader] : ['react-hot', babelLoader] }
]
},
plugins: [
new webpack.HotModuleReplacementPlugin({ quiet: true })
]
});
with this config I start webpack and webpack-dev-server
gulp.task('frontend-watch', function() {
new WebpackDevServer(webpack(frontendConfig), {
publicPath: frontendConfig.output.publicPath,
hot: true,
stats: { colors: true }
}).listen(3000, 'localhost', function (err, result) {
if(err) {
console.log(err);
}
else {
console.log('webpack dev server listening at localhost:3000');
}
});
});
so webpack-dev-server is running at localhost:3000 and receives app.js from webpack watcher (which now is not anymore written to file system).
my express server serves as a backend/api and has the following config:
var express = require('express');
// proxy for react-hot-loader in dev mode
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({
changeOrigin: true,
ws: true
});
var isProduction = process.env.NODE_ENV === 'production';
// It is important to catch any errors from the proxy or the
// server will crash. An example of this is connecting to the
// server when webpack is bundling
proxy.on('error', function(e) {
console.log('Could not connect to proxy, please try again...');
});
module.exports = function (app) {
// We only want to run the workflow when not in production
if (!isProduction) {
console.log('setting up proxy for webpack-dev-server..');
// Any requests to localhost:4200/build is proxied
// to webpack-dev-server
app.all('assets/app.js', function (req, res) {
proxy.web(req, res, {
target: 'http://localhost:3000'
});
console.log('request proxied to webpack-dev!');
});
}
var server = require('http').createServer(app);
app.use(express.static(homeDirectory + '/build'));
app.use(express.static(homeDirectory + '/files'));
server.listen(4200);
};
That's all good so far, the proxying work for app.js and I see successfull hot update messages in the browser console:
Now, while it looks fine it does not work as I expected:
when I change a component's render() method it updates as supposed, but when I change a helper method (that is used in render()) then I don't get any hot update. is that normal?
Another thing that bugs me, if I work like this, and do a 'hard' browser reload at some point, all changes I made are reverted to the point where I started my webpack-dev-server - all the hot updates in between have not been persisted somehow. is that normal as well? I would expect that I loose my state but not any changes I made to the code in the meantime. That has probably something to with my app.js not being written to the file system.
For your question #2, that's not normal, I have a template repo that has HMR working available here and it works just fine https://github.com/briandipalma/wp-r-template
For question #1, usually render methods display or format data, not grab it from somewhere. But if you need to format data, use a function outside of the component
Parent component would call the following once you retrieve the price
<ChildComponent price={this.state.price}
ChildComponent's render function would use props (or better yet a parameter of the function). Remember: the whole point of React is composition and data flow
return (
<div>{this.props.price}</div>
);

Resources