I am developing a project in React and I want to exclude some specific pdf files from being saved in the cache. I am using typical code for service worker provided in create-react-app
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
checkValidServiceWorker(swUrl);
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker'
);
});
} else {
registerValidSW(swUrl);
}
});
}
I have no idea where should I do it and how.
I think I found a solution. Unfortunately, it requires to use eject on the project. After that, we need to find a file called webpack.config.prod.js in the config folder. Then search for SWPrecacheWebpackPlugin. It has a parameter called staticFileGlobsIgnorePatterns. There we can add whatever we want. Default value is [/\.map$/, /asset-manifest\.json$/].
Related
I need to fetch data from an API that is pretty slow and seldom changes, so I thought I'd use an in-memory cache. I first tried a very simple approach by just saving it to a variable outside the scope of the loader function in my route:
let cache;
export const loader = async () => {
if (!cache) {
// we always end up here
cache = await (await fetch("...)).json()
}
}
but that didn't work. I then tried a proper caching library (lru-cache), but that cache was also always empty. I then realized that the entired file got reloaded on each request which I guess is a dev mode thing, so I tried moving the creation of the cache to a separate file cache.server.ts and importing it from there.
import LRU from "lru-cache";
console.log("Creating cache"); // this is logged on each request
const cache = new LRU({ max: 200 });
export default cache;
But that file also seems to be reloaded on each request.
If I build a production version and run that everything works great, but it would be nice to have some way of getting it to work in dev mode as well.
Remix purges the require cache on every request in development to support <LiveReload/>. To make sure your cache survives these purges, you need to assign it to the global object.
Here's an example from the Jokes Tutorial
import { PrismaClient } from "#prisma/client";
let db: PrismaClient;
declare global {
var __db: PrismaClient | undefined;
}
// this is needed because in development we don't want to restart
// the server with every change, but we want to make sure we don't
// create a new connection to the DB with every change either.
if (process.env.NODE_ENV === "production") {
db = new PrismaClient();
} else {
if (!global.__db) {
global.__db = new PrismaClient();
}
db = global.__db;
}
export { db };
https://remix.run/docs/en/v1/tutorials/jokes#connect-to-the-database
As a follow up on Kilimans answer, here's how I did it:
/*
* #see https://www.npmjs.com/package/node-cache
*/
import NodeCache from "node-cache";
let cache: NodeCache;
declare global {
var __cache: NodeCache | undefined;
}
if (process.env.NODE_ENV === "production") {
cache = new NodeCache();
} else {
if (!global.__cache) {
global.__cache = new NodeCache();
}
cache = global.__cache;
}
export { cache };
And I used it in the loader:
import { getGitHubRepos } from "~/models/github.server";
import { cache } from "~/utils/cache";
export async function loader(args: LoaderArgs) {
if (cache.has("GitHubRepos")) {
return json(cache.get("GitHubRepos"));
}
const repos = await getGitHubRepos();
cache.set("GitHubRepos", repos, 60 * 60 * 24);
return json(repos);
}
I'm pretty new to react and I'm trying to implement a service worker at the moment.
Actually I always get an error 'Uncaught SyntaxError: Unexpected token 'export'' in my "serviceworker.js" class.
Here's my main.tsx file.
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import App from './app/app';
import * as registerServiceWorker from './serviceworker/serviceworker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker.register();
And thats my "serviceworker.js" file.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
)
export function register(config) {
if ('serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
// const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
// if (publicUrl.origin !== window.location.origin) {
// // Our service worker won't work if PUBLIC_URL is on a different origin
// // from what our page is served on. This might happen if a CDN is used to
//
// return;
// }
window.addEventListener('load', () => {
const swUrl = `/serviceworker/serviceworker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, '
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See /CRA-PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType === null)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
Any idea what I did wrong here?
I already added Babel as suggested in this thread react export Unexpected token but the error didn't disappear.
I already tried to export it via modules.export, but no sucess either.
Thanks in advance!
EDIT:
Thats what my ".babelrc" looks like:
{
"presets": ["#babel/preset-react"],
"plugins": [
"babel-plugin-transform-export-extensions",
"transform-es2015-modules-commonjs"
]
}
Thats what my ".babelrc" looks like:
{
"presets": ["#babel/preset-react"],
"plugins": [
"babel-plugin-transform-export-extensions",
"transform-es2015-modules-commonjs"
]
}
This problem occurs because you are trying to use the same file as the service worker you are registering it with. Because of this, the browser cannot figure out which service worker features you need.
Use for example this content for service-worker.js in your public folder:
self.addEventListener('push', (event) => {
const data = event.data.json();
console.log('New notification', data);
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.description,
icon: data.icon,
})
);
});
Trying to use some installed npm packages like fs-extra into below js files given by Truffle.But it says "can't find module "fs-extra".
1) Tried importing local js files using require() method but that fails too.
2) Tried running separate js files using node and it works just fine.
3) Issue comes when I try to use require("fs-extra") inside a function declared in APP object.
App = {
web3Provider: null,
contracts: {},
init: async function () {
return await App.initWeb3();
},
initWeb3: async function () {
// Modern dapp browsers...
if (window.ethereum) {
App.web3Provider = window.ethereum;
try {
// Request account access
await window.ethereum.enable();
} catch (error) {
// User denied account access...
console.error("User denied account access")
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = window.web3.currentProvider;
}
// If no injected web3 instance is detected, fall back to Ganache
else {
App.web3Provider = new Web3.providers.HttpProvider('http://0.0.0.0:9283');
}
web3 = new Web3(App.web3Provider);
return App.initContract();
},
initContract: function () {
$.getJSON('UserCreation.json', function (data) { //<VK>Satish to add his contract file here
// Get the necessary contract artifact file and instantiate it with truffle-contract
var CMArtifact = data;
App.contracts.UserCreation = TruffleContract(CMArtifact);
App.contracts.UserCreation.setProvider(App.web3Provider);
});
return App.bindEvents();
},
createUser: function (event) {
event.preventDefault();
var username = $("#sign-up-username").val();
var title = $("#sign-up-title").val();
var intro = $("#sign-up-intro").val();
const utility=require('fs-extra'); // Failing to find module
}
}
$(function () {
console.log("initiaing farmer")
$(window).load(function () {
App.init();
});
});
Expected: Should be able to call methods from fs-extra package
Actual : can't find module "fs-extra"
npm ls fs-extra to check if you've installed it correctly. Then try npm install fs-extra.
require('fs-extra') will only work in server side javascript (nodejs) .
If your code runs on a browser require will not work
I use default React code for registering service worker. I have reports in bugsnag that some users are getting TypeError: undefined is not a function on the line .then(registration => { inside registerValidSW.
For me it is working but for some probably not. I did not find where could be a problem.
Could you help me with this?
export function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
.......
} else {
registerValidSW(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
...
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
Serviceworker and its methods including register are supported on all latest browsers excluding IE.
It has been noticed that even when serviceWorker is accessible in navigator, property register is not present which should always return a promise.
Chromium bug for the same.
For now, you could avoid this issue by adding the following check:
'serviceWorker' in navigator && 'register' in navigator.serviceWorker
This is happening because you are using normal react app template which does not have service worker functions like register()
to use pwa react app create your react app by
npx create-react-app my-app --template cra-template-pwa
TypeScript equivalent
npx create-react-app my-app --template cra-template-pwa-typescript
Goal: To support dynamic loading of Javascript modules contingent on some security or defined user role requirement such that even if the name of the module is identified in dev tools, it cannot be successfully imported via the console.
A JavaScript module can be easily uploaded to a cloud storage service like Firebase (#AskFirebase) and the code can be conditionally retrieved using a Firebase Cloud Function firebase.functions().httpsCallable("ghost"); based on the presence of a custom claim or similar test.
export const ghost = functions.https.onCall(async (data, context) => {
if (! context.auth.token.restrictedAccess === true) {
throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
}
const storage = new Storage();
const bucketName = 'bucket-name.appspot.com';
const srcFilename = 'RestrictedChunk.chunk.js';
// Downloads the file
const response = await storage
.bucket(bucketName)
.file(srcFilename).download();
const code = String.fromCharCode.apply(String, response[0])
return {source: code};
})
In the end, what I want to do...
...is take a webpack'ed React component, put it in the cloud, conditionally download it to the client after a server-side security check, and import() it into the user's client environment and render it.
Storing the Javascript in the cloud and conditionally downloading to the client are easy. Once I have the webpack'ed code in the client, I can use Function(downloadedRestrictedComponent) to add it to the user's environment much as one would use import('./RestrictedComponent') but what I can't figure out is how to get the default export from the component so I can actually render the thing.
import(pathToComponent) returns the loaded module, and as far as I know there is no option to pass import() a string or a stream, just a path to the module. And Function(downloadedComponent) will add the downloaded code into the client environment but I don't know how to access the module's export(s) to render the dynamically loaded React components.
Is there any way to dynamically import a Javascript module from a downloaded stream?
Edit to add: Thanks for the reply. Not familiar with the nuances of Blobs and URL.createObjectURL. Any idea why this would be not found?
const ghost = firebase.functions().httpsCallable("ghost");
const LoadableRestricted = Loadable({
// loader: () => import(/* webpackChunkName: "Restricted" */ "./Restricted"),
loader: async () => {
const ghostContents = await ghost();
console.log(ghostContents);
const myBlob = new Blob([ghostContents.data.source], {
type: "application/javascript"
});
console.log(myBlob);
const myURL = URL.createObjectURL(myBlob);
console.log(myURL);
return import(myURL);
},
render(loaded, props) {
console.log(loaded);
let Component = loaded.Restricted;
return <Component {...props} />;
},
loading: Loading,
delay: 2000
});
Read the contents of the module file/stream into a BLOB. The use URL.createObjectURL() to create your dynamic URL to the BLOB. Now use import as you suggested above:
import(myBlobURL).then(module=>{/*doSomethingWithModule*/});
You can try using React.lazy:
import React, {lazy, Suspense} from 'react';
const Example = () => {
const [userAuthenticated, setUserAuthenticated] = useState(true);
if (userAthenticated) {
const RestrictedComponent = lazy(() => import('./RestrictedComponent'));
return (
<div>
<Suspense fallback={<div><p>Loading...</p></div>}>
<RestrictedComponent />
</Suspense>
</div>
)
}
return (
<div>
<h1>404</h1>
<p>Restricted</p>
</div>
);
}
export default Example;