Blacklisting react-native module doesnt works - reactjs

I have a project folder which contains both Web and Mobile application developed on React and React-native, There is a dependency for web which adds SymLink in the postinstall script, The problem I am facing is React-native Packager server also picks up that symlink and App doesnt work correctly.
At first I followed this- how to make react native packager ignore certain directories but getBlacklistRE silently ignores whatever Regex I am passing.
EDIT
This is the actual issue I am facing and I have tried implemented all the solutions mentioned there but no success yet. - https://github.com/facebook/react-native/issues/19763
EDIT2
Now seems to me blacklisting not working and it could also happen due to clashes between the babel version I am using in web and that of React Native below is my package.json

You mention trying blacklist with no luck. I don't have a web and react native project side by side but I do have a web project nested inside my react native project so this may be of some help. The blacklist does work for me and this is how it is implemented.
In rn-cli.config.js
const blacklist = require('metro/src/blacklist');
module.exports = {
getTransformModulePath() {
return require.resolve('./customTransformer'); // More on this below
},
getBlacklistRE() {
return blacklist([
/apps\/.*/,
]);
},
};
I have a directory called apps/ in the root of my project. Inside apps/ I have the equivalent of React Native's UI Explorer (a nested React Native app) and web documentation (React web app). By blacklisting this directory the react native packager at the root of my project won't load those node modules, otherwise there is unwanted collision.
The customTransformer may not be of direct help here but I include it incase it lends a hand to your situation. I use it to replace local mock data files with an empty object so that my final builds have mocked data stripped. There may be a way this helps your project out if you're not aware of it.
// Note transformer was renamed to reactNativeTransformer effective in RN 0.56.x
const upstreamTransformer = require('metro/src/reactNativeTransformer');
module.exports.transform = function (input) {
const { filename, options, src } = input;
let newSrc = src;
if (filename.indexOf('mocks') > -1 && !options.dev) {
newSrc = 'module.exports = {}';
}
return upstreamTransformer.transform({ src: newSrc, filename, options });
};

Related

How do I generate static HTML for my homepage from Create React App?

I have a CRA and want to have the first page generated statically to improve load time and SEO. The idea is to run a NodeJS script that renders the App document inside index.html.
Here is my code:
const { renderToString } = require("react-dom/server");
const App = require('./App');
const content = `<html><body>${renderToString(App)}</body></html>`
const fs = require('fs');
fs.writeFileSync('../public/index.html', content);
However, I have problems running it with NodeJS:
import React from 'react';
^^^^^^
SyntaxError: Cannot use import statement outside a module
Apparently, I have to build the source, but I don't know how to do it. I'm using CRA to build my React files (actually, I'm using react-app-rewired, so I could customize the build process if I only knew how to do it).
What should I do to make it work?
the error is from node: https://stackoverflow.com/a/59399717/13216797
I know it's not your question... but what about using Nextjs?
Even without a node environment you can use the "next export" command to make a bundle that will work as it would static files while still being react...
I ended up using puppeteer and just saving the generated client-side code. Before that I spent hours stubbing out first window, then window.location, then window.localStorage, window.document, window.document.createElement, it never ended. With puppeteer, it was pretty easy.

Create react app - how to copy pdf.worker.js file from pdfjs-dist/build to your project's output folder?

Since I can't use browser's pdf viewer in the network where the app is going to be used, I am testing a react-pdf package for loading PDF's with React.
I have made a component where I am sending a url of my PDF that I get from backend:
import React, { useState } from 'react';
import { Document, Page } from 'react-pdf';
const PDFViewer = ({url}) => {
const [numPages, setNumPages] = useState(null);
const [pageNumber, setPageNumber] = useState(1);
function onDocumentLoadSuccess({ numPages }) {
setNumPages(numPages);
}
function onLoadError(error) {
console.log(error);
}
function onSourceError(error) {
console.log(error);
}
return (
<div>
<Document
file={window.location.origin + url}
onLoadSuccess={onDocumentLoadSuccess}
onLoadError={onLoadError}
onSourceError={onSourceError}
>
{[...Array(numPages).keys()].map((p) => (
<Page pageNumber={p + 1} />
))}
</Document>
</div>
);
};
export default PDFViewer;
But, on opening the PDFViewer I get an error
Error: Setting up fake worker failed: "Cannot read property 'WorkerMessageHandler' of undefined"
In documentation it says that you should set up service worker and that the recommended way is to do that with CDN:
import { pdfjs } from 'react-pdf';
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;
But, I can't use CDN links for my project, and in the documentation it also says:
Create React App uses Webpack under the hood, but instructions for Webpack will not work. Standard instructions apply.
Standard (Browserify and others)
If you use Browserify or other bundling tools, you will have to make sure on your own that pdf.worker.js file from pdfjs-dist/build is copied to your project's output folder.
There are no instructions on how to do that with create-react-app. How can I set this up locally then?
Install pdfjs-dist
import { Document, Page, pdfjs } from "react-pdf";
import pdfjsWorker from "pdfjs-dist/build/pdf.worker.entry";
pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;
Reference: https://github.com/mozilla/pdf.js/issues/8305
found a more efficient way of including the worker
by including the library from the dependencies of react-pdf itself, this way you will never get a version mismatch like this The API version "2.3.45" does not match the Worker version "2.1.266"
if you install pdfjs-dist manually you will have to check react pdf dependency version on every build
import { Document, Page, pdfjs } from "react-pdf";
import pdfjsWorker from "react-pdf/node_modules/pdfjs-dist/build/pdf.worker.entry";
pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;
see similar error on pdfjs library : https://github.com/mozilla/pdf.js/issues/10997
hope it helps people
You can install worker loader module for webpack:
npm install worker-loader --save-dev
Then use it inline where you are going to work with a worker:
import SomeWorker from 'worker-loader?inline=true!../workers/some.worker'
const someWorker: Worker = new SomeWorker()
someWorker.postMessage(...)
I haven't tried this solution with react-pdf, but it might help.
You may need to add types for TypeScript if you are using it:
declare module 'worker-loader*' {
class SomeWorker extends Worker {
constructor()
}
export default SomeWorker
}
Just to add that in some .d.ts file in your project.
Install pdfjs-dist then use the webpack module:
import { pdfjs } from 'react-pdf'
import worker from 'pdfjs-dist/webpack'
pdfjs.GlobalWorkerOptions.workerSrc = worker
If your build process uses cli commands, (i.e. AWS buildspec), you can use this:
mkdir -p build && cp ./node_modules/pdfjs-dist/build/pdf.worker.js build
If you are in a corporate codebase environment and have little to no experience configuring WebPack, I wanted to share a little more info if (like me) you struggled with this for quite a long time.
My environment has several complicated WebPack config files (base, production, and development), and the resolution ended up being pretty simple but it escaped me for quite a while because I was unfamiliar with the complicated build process.
1) The Implementation
Quite simple, just as the docs recommend (I went with the minified file). Our React environment required me to use React-PDF#4.2.0, but there aren't any differences here.
import {Document, Page, pdfjs} from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = 'pdf.worker.min.js'
Note: a previous solution recommended grabbing the source from the react-pdf node_modules folder, however, my codebase is setup to install dependencies separately somehow because when I npm install react-pdf, pdfjs-dist is also installed separately. Regardless, this method did not work for my codebase (importing the worker as a variable) due to the way the project is built. The import command acted like it couldn't find the proper named export inside a node_modules folder. It was top-level or nothing.
2) WebPack Config
Since I do not know WebPack at all, but found pretty easily that what I needed to do was take advantage of CopyWebpackPlugin, I searched through those existing dev and prod webpack config files, and found existing copy commands for JQuery and polyfill and added a new plugin to that array:
new CopyWebpackPlugin({from: 'node_modules/pdfjs-dist/build/pdf.worker.min.js})
I had to do this in multiples places in both config files as this large project has several entry point server files for the different services of the website.
3) Inserting Script Tag to HTML Head
This was the crucial part I was missing. There was a "ComponentFactory" file whose job it was to insert chunks of html in the <head> and tail of the html file. I wasn't used to something like this on small projects. So there, I simply copied what was already done for the jquery and polyfill, which included a string literal of the location of the assets folder the webpack was building out to. In my case, that was something like "assets/v1/". So the tag looked like this:
<script src=`${STATIC_ASSETS_URL}/pdf.worker.min.js` defer></script>
It works perfectly, however I am still getting the "Setting Up a Fake Worker" but immediately after that, it loaded it successfully in console and checking the dev tools, it was using the proper file. It's probably just a timing thing of the src set not running high enough in the code, but it was not effecting the outcome, so I let it go.
(Sidebar, if you also get the "TT unknown function" (paraphrasing) error, that can be ignored. It's just a font issue with whatever PDF you are loading and is just a warning, not an error.)
I was facing this issue once I had to use "react-pdf" from within a package.
It was solved by importing the worker conditionally into the code:
Conditional import:
export const getWorker = () => {
try {
return require('react-pdf/node_modules/pdfjs-dist/legacy/build/pdf.worker.entry.js')
} catch () {
return require('pdfjs-dist/legacy/build/pdf.worker.entry.js')
}
}
usage:
import { Document, Page, pdfjs } from 'react-pdf/dist/umd/entry.webpack'
pdfjs.GlobalWorkerOptions.workerSrc = getWorker()

Where should I put general app config in a Gatsby project?

I'm new to Gatsby, but familiar with CRA. I'm using the default Gatsby setup generated by running gatsby new <project_name> from Gatsby CLI.
I have some general config that I want run in my whole project, regardless of current page - for example:
import { enableMapSet, enableES5 } from "immer";
enableMapSet();
enableES5();
In CRA projects I put this stuff inside App.tsx, but can't figure out what the right place is in a Gatsby project.
In Gatsby, you don't have a single configuration file per se. There are several APIs exposed by Gatsby that allows you to configure one specific part of your site.
To run a set of functions regardless of the page you can use a gatsby-browser.js (placed in the root of the project).
Basically, Gatsby exposes a bunch of APIs that lets you respond to actions within the browser, and wrap your site in additional components. The Gatsby Browser API gives you many options for interacting with the client-side of Gatsby. One of them, onClientEntry fits your requirements. From its documentation:
onClientEntry
(_: emptyArg, pluginOptions: pluginOptions) => undefined
Called when the Gatsby browser runtime first starts.
Applied to your code, your gatsby-browser.js should look like:
import { enableMapSet, enableES5 } from "immer";
import React from 'react';
export const onClientEntry = () =>{
enableMapSet();
enableES5();
};
The snippet above will trigger enableMapSet() and enableES5() on every page.

requestAnimationFrame is not defined it Next.js with React Native Web (Animated module)

I'm working on Next.js and React-Native-Web. I managed to run them together following the official Next.js example but when I'm trying to use the Animated package from the react-native it fails with Error that the requestAnimationFrame isn't defined. Basically this functionality does the node_modules package but I set the alias in webpack to translate all react-native requires to the react-native-web so even the node_modules package should use the react-native-web.
Any suggestions on how to solve it?
ReferenceError: requestAnimationFrame is not defined
at start (...node_modules\react-native-web\
dist\cjs\vendor\react-native\Animated\animations\TimingAnimation.js:104:11)
enter code here
Thanks for any help!
The problem is in the missed RequestAnimationFrame functionality at the server. This error happens when Next.js tries to render the component during SSR.
Unfortunately, there is no polyfill, etc. for such purpose so I just decided to use the Next.js dynamic imports for a Component that has animation functionality.
Next.js Official documentation
My own case оust to show how code looks:
import dynamic from 'next/dynamic';
const AutocompleteDropdown = dynamic(
() => import(
'myAwesomeLib/components/dropdown/autocomplete/AutocompleteDropdown'
),
{
ssr: false,
}
);
Now you can use the AutocompleteDropdown as the standard JSX component
I'm coding an App with React Native Web and NextJS 12, and in 2021 I encounter this problem and I fixed it, but now I know my fix was only for Next Dev, because it returned for Next Production Build.
Solution details:
No Dynamic import (which is useful too, but can be annoying when having lot of components using it)
Using RAF polyfill and Webpack ProvidePlugin.
Main thing to have in mind is that next.config.js with webpack 5 is going to check the codes first before even reach next entry points _documents.js and _app.js. It means that, you can put polyfill in those entry point files, it will still raise error of RAF undefined. You have to make requestAnimationFrame ready for config check.
DEV approach that will work on Next DEV only. Install RAF package https://www.npmjs.com/package/raf and In next.config.js add codes:
const raf = require('raf');
raf.polyfill();
This will add requestAnimationFrame and cancelAnimationFrame function to global and window object if they don't have it. In our case, it would add it in global for NodeJS.
But this solution won't work when executing npm run dev. I don't know why, if anyone knows why Next or Webpack 5 act differently from DEV to PRODUCTION, let me know.
Complete Solution:
Use ProvidePlugin config of webpack 5 https://webpack.js.org/plugins/provide-plugin/ . Create a file to use as modules, let's say: raf.js in root project or anywhere you want:
const raf = require('raf');
const polys = {};
raf.polyfill(polys);
module.exports = polys.requestAnimationFrame;
And in next.config.js use it inside webpack: () = {} like:
webpack: (config, options) => {
// console.log('fallback', config.resolve.fallback);
if (options.isServer) {
// provide plugin
config.plugins.push(
new options.webpack.ProvidePlugin({
requestAnimationFrame: path.resolve(__dirname, './raf.js'),
}),
);
}
And now, it's up to you to adapt to your existing config logic. By doing this, in Production Build, NextJS is injecting the requestAnimationFrame function in Server Side everywhere a module is using it.

How to webpack a node_module for use in both React and ReactNative

I'm building out a library that will be used by both a React Native project and a React project as a node_module hosted on my github.
This is proving quite daunting- and based on how ugly my code has become, I feel I must be doing something wrong.
Here's the presenting problem, the most recent issue:
Failed to compile.
./node_modules/plenti-api/src/services/AccountService.ts 11:10
Module parse failed: Unexpected token (11:10)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|
| export default class AccountService {
> private client: IClient
| constructor(client: IClient) {
| this.client = client
This is occurring when trying to build the React project via npm start. The project successfully builds and runs on the React Native side of things.
what do I need to put in my .tsconfig / .webpack.config.js / somethingelse in order to allow for these typescript structures to exist in my library and be imported correctly into my web project?
Thanks to #Mike in the comments, figured out it's because node needs to be written in vanilla javascript and therefore node_modules need to be built so that can be imported by pure javascript.
This article https://www.twilio.com/blog/2017/06/writing-a-node-module-in-typescript.html was very helpful in achieving that task.

Resources