Importing node modules to web worker in React/Webpack app - reactjs

I have a React app bundled with Webpack. I would like to use a web worker for one of my components, which exports data to Pdf. The generation of the pdf can take a while and lock the browser, so I want to do this work in a web worker to do it in a separate thread. The problem I am having is importing the JsPDF library into my web worker script so i can use it.
This is my worker script:
import * as JsPDF from "jspdf";
export default () => {
self.addEventListener("message", event => {
const canvases = event.data;
const pdf = new JsPDF({
orientation: "l",
unit: "in",
});
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < canvases.length; i++) {
if (i > 0) pdf.addPage();
pdf.addImage(canvases[i].toDataURL("image/png"), "PNG", 0.25, 0, 11, 8);
}
pdf.save("report.pdf");
self.postMessage("done", "");
});
};
This gives me this error at runtime:
Uncaught ReferenceError: jspdf__WEBPACK_IMPORTED_MODULE_0__ is not defined
at blob:http://localhost:11449/ea75c456-15ee-45e9-b82b-902c518dc635:4
I did also try using the importScript() function, like so:
export default () => {
importScripts("jspdf");
self.addEventListener("message", event => {
const canvases = event.data;
const pdf = new JsPDF({
orientation: "l",
unit: "in",
});
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < canvases.length; i++) {
if (i > 0) pdf.addPage();
pdf.addImage(canvases[i].toDataURL("image/png"), "PNG", 0.25, 0, 11, 8);
}
pdf.save("report.pdf");
self.postMessage("done", "");
});
};
and I get this error:
Uncaught DOMException: Failed to execute 'importScripts' on 'WorkerGlobalScope': The URL 'jspdf' is invalid.
or I try:
importScripts("../../../../node_modules/jspdf/dist/jspdf.min.js");
and I get the same invalid URL error.
I also tried using require outside of the export:
const JsPDF = require("jspdf");
but i still get an error that JsPDF is not defined. What else can I try?
I should mention that the web worker is instantiated using a custom class, as in https://medium.com/prolanceer/optimizing-react-app-performance-using-web-workers-79266afd4a7, for getting the correct URL with webpack:
export default class WebWorker {
constructor(worker: any) {
const code = worker.toString();
const blob = new Blob([`(${code})()`]);
return new Worker(URL.createObjectURL(blob));
}
}

I ended up using worker-loader for webpack. This allows me to import modules in worker scripts like in any other script.

Related

I am using React Native Expo And need to do live background location tracking

React Native Expo: Background Location has not been configured. To enable it, add location to UIBackgroundModes in Info.plistfile
I am using expo-location and trying to do background location tracking.
my app.json under IOS includes this
"infoPlist": { "UIBackgroundModes": [ "location", "fetch" ],
I am calling the startLocationUpdatesAsync function as such
` useEffect(() => {
var appLocation = undefined;
const startLocationTracking = async () => {
var opts = {
accuracy: Location.Accuracy.BestForNavigation,
distanceInterval: 1, //meters
};
appLocation = await Location.watchPositionAsync(opts, (location) => {
dispatch(setCurrentLocation(location.coords));
sendLocationToAPI(location);
});
const backgroundLocation = await Location.startLocationUpdatesAsync("background-location")
}`
I end up getting this error
Unhandled promise rejection: Error: Background Location has not been configured. To enable it, add locationtoUIBackgroundModes in Info.plist file.
I Do not know how to get rid of this, but I think I may have to expo eject and switch to cli. If anyone can help so I can avoid this it would be really helpful.

Serve static javascript file from nextjs api

I need a route in website build with nextjs that sends javascript that can be used on different website to do some things.
I created new file in pages/api, let's call it sendTest.ts so it's location is pages/api/sendTest.ts. In the same folder I crated test.ts file that I want to send from sendTest.ts.
sendTest.ts
import type { NextApiRequest, NextApiResponse } from 'next'
import fs from 'fs';
import path from 'path'
const filePath = path.join(__dirname, 'test.js');
const file = fs.readFileSync(filePath);
export default function handler(
req: NextApiRequest,
res: NextApiResponse
) {
res.setHeader('Content-Type', 'text/javascript');
res.status(200).send(file)
}
test.ts
console.log('hello');
After build nextjs serves that file at website.com/api/sendTest but after bundling it ends up as
"use strict";
(() => {
var exports = {};
exports.id = 318;
exports.ids = [318];
exports.modules = {
/***/ 211:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
console.log("hello there");
/***/ })
};
;
// load runtime
var __webpack_require__ = require("../../webpack-api-runtime.js");
__webpack_require__.C(exports);
var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId))
var __webpack_exports__ = (__webpack_exec__(211));
module.exports = __webpack_exports__;
})();
which when used in different page as
<script src="website.com/api/sendTest"></script>
results in error
Uncaught ReferenceError: require is not defined
at sendTest:22:27
at sendTest:28:3
My question is how can I force nextjs to skip loading webpack into that file and just allow typescript to change content into javascript and serve file as is? Or is there better way to do what I want, which is sending javascript from specified nextjs route?
Got it.
I changed
const filePath = path.join(__dirname, 'test.js');
to
const filePath = path.resolve('.', 'script/test.js');
and put my script file into folder called script (name doesn't matter) in the main directory

React-Js - Integrating Xterm.js (V ^5.0.0) causing reference error when deployed on production

I am building a live terminal in react js web app. On running local host , the terminal works fine but in production the code breaks with the below error on console.
ReferenceError: Cannot access 'r' before initialization
at new S (12320.cfcfec944a34227a5e11.bundle.js:1:135248)
at new b (12320.cfcfec944a34227a5e11.bundle.js:1:129539)
at new M (12320.cfcfec944a34227a5e11.bundle.js:1:27353)
at new r.exports.i.Terminal (12320.cfcfec944a34227a5e11.bundle.js:1:264753)
at 42919.8ded40c50ac3709077ea.bundle.js:1:578863
at C (main.503b8d39e2b5052d0fa6.bundle.js:1:1333747)
at Generator._invoke (main.503b8d39e2b5052d0fa6.bundle.js:1:1333500)
at Generator.next (main.503b8d39e2b5052d0fa6.bundle.js:1:1333927)
at r (main.503b8d39e2b5052d0fa6.bundle.js:1:1340419)
at o (main.503b8d39e2b5052d0fa6.bundle.js:1:1340602)
Here's my code.
const Termcomponent = () => {
useEffect(() => {
try {
const initTerm = async () => {
const {
Terminal
} = await import('xterm')
term = new Terminal({
screenKeys: true,
useStyle: true,
cursorBlink: true,
});
term.open(document.getElementById('x_terminal'));
}
initTerm();
} catch (ex) {
console.log(ex)
}
}, [])
return < div id = "x_terminal" > < /div>
}
Can anyone help me out here?
I am using webpack with this project.
Again, this logic works fine locally. but throws error on console in production.

React - Three.js - GLTFLoader "JSON.parse error"

so whenever I want to import a model via GLTFLoader locally I get this error:
JSON.parse: unexpected character at line 1 column 1 of the JSON data
but when I try it via a link it works.
The link I tried with is:
https://s3-us-west-2.amazonaws.com/s.cdpn.io/39255/ladybug.gltf
I tested the models I used in the official three.js-Editor and even exported them from there to get a "clean" gltf model. I also saved the content of the link in a gltf file and it didnt work neither.
This is my code:
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
async function run() {
try {
var loader = new GLTFLoader();
loader.crossOrigin = true;
loader.load(
"./scene.gltf",
//"https://s3-us-west-2.amazonaws.com/s.cdpn.io/39255/ladybug.gltf",
function (data) {
var object = data.scene;
object.position.set(0, 0, 0);
scene.add(object);
}
);
} catch (e) {
console.log(e);
}
}
run();
As Marquizzo said: You have to run a local server for the models. You cannot import them via a path normally, because of security restrictions.

importScripts in Web Workers is undefined inside a React/Webpack environment

I am working on a React app created with create-react-app. I was having trouble creating a web worker in it so I posted a question here on SO: Creating a web worker inside React
I've found a solution, as written in the post above, to load a worker without ejecting the app and messing with the Webpack config. This is the code, from the post above:
// worker.js
const workercode = () => {
self.onmessage = function(e) {
console.log('Message received from main script');
const workerResult = 'Received from main: ' + (e.data);
console.log('Posting message back to main script');
self.postMessage(workerResult);
}
};
let code = workercode.toString();
code = code.substring(code.indexOf("{")+1, code.lastIndexOf("}"));
const blob = new Blob([code], {type: "application/javascript"});
const worker_script = URL.createObjectURL(blob);
module.exports = worker_script;
and in the file that uses the worker:
import worker_script from './worker';
const myWorker = new Worker(worker_script);
myWorker.onmessage = (m) => {
console.log("msg from worker: ", m.data);
};
myWorker.postMessage('im from main');
It works, however, I cannot seem to get importScripts to work. Even if I do this (outside onmessage or inside onmessage):
if (typeof importScripts === 'function') {
importScripts('myscript.js');
}
In that case, the if statement turns out to be true, but then fails on the actual import with the same error message 'importScripts' is not defined as if the if statement is a false positive, which doesn't sound right. I'd say this is a context issue and that the worker probably isn't loading properly (although it seems to work), but it's just a guess.
Any ideas what's happening here?
importScripts in a worker created from Blob works fine, at least in 2021 (react 17.0.2, react-scripts 4.0.3, Chrome 92). The imported script URL must be absolute because worker was created from Blob.
The original issue might have been a bug in webpack or the transpilation might have changed the code in a weird way.
const workercode = () => {
importScripts("https://example.com/extra.js");
console.log(self.extraValue); // 10
self.onmessage = function(e) {
console.log('Message received from main script');
...
}
};
 
// extra.js
self.extraValue = 10;
Looks like this is still broken in 2022 - Seems there is a regression coming down the dev pipeline (at least in Android WebView and possibly some dev/canary chrome verions.)
https://bugs.chromium.org/p/chromium/issues/detail?id=1078821

Resources