How to code split packages (fontawesome) from Webpack 4 React project - reactjs

I have a large bundle that needs to be split into several chunks to get below the 2MB PWA restriction.
From the BundleAnalyzerPlugin I can see that I would benefit from trying to split out lodash, fontawsome and moment into separate chunks/bundle files.
I have tried to use the import() splitting technique described here but can't see how to make it work for Fontawesome.
The below example does not work, since it still leaves fontawesome in the bundle and only loads icons when interacted with.
import { faBell, faEyeSlash, faEye} from '#fortawesome/free-solid-svg-icons';
import { faBell as regularBell} from '#fortawesome/free-regular-svg-icons';
import('#fortawesome/fontawesome-svg-core').then(fontawesome => {
fontawesome.library.add(
faBell, faEye, faEyeSlash, regularBell
)
})
What is the correct technique for separating packages such as lodash, fontawesome and moment into separate bundles?
Kind regards /K

For tree-shaking to work with the font-awesome react packages in webpack, I needed to import the icons from the subfolder directly like this:
import { faBell } from '#fortawesome/free-solid-svg-icons/faBell';
import { faEyeSlash } from '#fortawesome/free-solid-svg-icons/faEyeSlash';
import { faEye } from '#fortawesome/free-solid-svg-icons/faEye';
import { faBell as regularBell } from '#fortawesome/free-regular-svg-icons/faBell';
I am on webpack v4 and fontawesome v5.

I don't know what exactly are you trying to come up with but i'm assuming you want a lower bundle size then I did a research for you:
If you really want to push your bundle size to the lowest then look for gzip compression
https://webpack.js.org/plugins/compression-webpack-plugin/
A little bit research would lead you to this:
https://webpack.js.org/guides/code-splitting/
What is the correct technique for separating packages such as lodash,
fontawesome and moment into separate bundles?
For lodash:
// You should be using:
import isEmpty from 'lodash/isEmpty'
// instead of:
import _ from 'lodash';
import { isEmpty } from 'lodash';
read: https://www.blazemeter.com/blog/the-correct-way-to-import-lodash-libraries-a-benchmark/
For moment:
https://medium.com/#Memija/less-is-more-with-moment-and-moment-timezone-d7afbab34df3
For fontawesome:
I don't know exactly is this?
The below example does not work, since it still leaves fontawesome in
the bundle and only loads icons when interacted with.
Yes, it'll be included to the bundle of course depending on what you've stated in your config? What you did is from the docs itself:
https://fontawesome.com/how-to-use/with-the-api/other/tree-shaking

Related

import React from 'react'

When we write code in index.js file in src folder of an React app first of all we write this line:
import React from 'react';
I know react is a package
But I want to know what is React basically
an object, a method or something else.
The React variable that you import is an object, and it contains most of the methods that are used by React when generating a web-page.
The reason this import has been historically required is because the JSX that you write (e.g. return <p>text</p>) gets converted into a function call, calling the React.createElement function.
Note that in newer versions of React you no longer need to import react when using JSX. See https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html for more information.
Credit to Olivier Boissé for an answer in the comments.
In order to find out what React is, you just need to write:
console.log(react);
Then you will see that it is an object with many properties and methods.
String:
import React from "react";
We are writing in order to import this object into a file where we will use some of its method. If you don't use anything from React in the file, then you don't need to import it.
For example, in react 18, it is no longer necessary to import the react object into a file index.js
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
In any case, at this stage of your study of react, it may not be entirely clear to you what is used in it and for what, but in the future you will become more aware of all the possibilities of react. There is a time for everything

Import a file as a string (or source asset) in Gatsby / React

I want to import .ts, .tsx, .js, and .jsx files into a react component and render them within a PrismJS highlighting block. For example, let's say I have a TypeScript file with functionA in it that I want to highlight in my actual website:
functionA.ts:
export function functionA() {
console.log("I am function A!");
}
I want to include this in a different component. The problem is, when I import it, I am obviously importing the webpack module version of it. My weak attempt at trying to get my function render in a react component looks like this:
MyComponent.tsx:
import * as React from "react"
import { functionA } from "./functionA"
export function MyComponent() {
return (
<>
<h1>Here is your code block:</h1>
<pre>
<code>
{functionA.toString()}
</code>
</pre>
</>
)
}
and what will actually render on the page where the code block is will look something like this:
Here is your code block:
WEBPACK__IMPORT.functionA() {
console.log("I am function A!")
}
I can't exactly remember what the .toString() function output looked like, but the point is it is NOT just the contents of the file how it appears in a code edit for example - it has been modulized by WebPack.
So, in a Gatsby project, how can i get these various code snippets to be imported directly as a string, purely as they are written, without WebPack enacting its import stuff on it? Is there a plugin or some way to tell Webpack to use the imported file as its asset/source module type? I know for MD or MDX files there is the gatsby-remark-embed-snippet, but I am building a component based HTML page and can't use MD or MDX files!
It's very late, and perhaps I just can't see the forest from the trees, I know there must be a way to do this...
You need to require the file using webpack's raw-loader, i.e:
const functionA = require("!!raw-loader!./functionA");
This works for create-react-app, as in the solution discussed here, and this works for Gatsby as well!
After using require on such a file, the file contents can be rendered in the component as:
<pre>{functionA.default.toString()}</pre>
It's then up to you to add syntax highlighting using a tool like prism or similar.
Note this solution will only work as long as Gatsby V3 continues to use WebPack v4, as raw-loader is deprecated in WebPack v5 and will be phased out for asset/source type modules.

How to convert p5.Something() to fit react

In my file sketch.js before shifting to combined p5 and react, I had a command
amp = new p5.Amplitude();
after shifting to react, the 'p5' method is not defined anymore.
p.Amplitude()/song.Amplitude() isn't doing the job and returns
(TypeError: ... .Amplitude is not a constructor)
I really don't know from where or how import p5. I guess its something to do with the web config but not sure what.
I've npm install both p5 and react-p5-wrapper, and except this line and rest of the things that required amp all the code running as expected, and I can play music/adjust background with sliders etc ...
at the begging of the file I'm importing:
import React from 'react';
import 'p5/lib/addons/p5.sound';
import 'p5/lib/addons/p5.dom';
I will really be glad for little help!
You might be able to try:
import * as p5 from './{library-path}/p5.js';
It looks like P5 was not originally set up for easy ES6 imports.
This GitHub issue from 2016 seems to identify a similar problem. https://github.com/processing/p5.js/issues/1734
More recently it looks like it can be used correctly with NPM:
https://medium.com/front-end-weekly/learning-the-p5-canvas-drawing-library-in-es6-and-webpack-bf514a679544
Also check out this other potential answer here.
How to use React with p5.js

Devextreme as external library for sharepoint framework

I am using a very large library in my spfx solution and I would like to not only share it among webparts within this app but share it across any other webparts that might be using this library.
What I know
I know that you can include external libraries such as jquery and some libraries that have typings separate from their npm package.
I know that even if I put a reference to an external library in the externals section it won't actually pull down the library if I don't import it anywhere in my code.
I understand the current examples in the documentation. I managed to successfully use moment-timezone which is great.
Problem I am having
I am trying to use the DevExtreme and devextreme-react libraries. This library is very large so I want to include it once from a cdn when a webpart is requesting it and continue using the same script with each additional webpart instance. The problem is that the library comes with typings included in it. It is in a subfolder bundles/dx.all.d.ts instead of in its own package. It also has multiple files you can include instead of including the entire library. In addition the devextreme-react is depending on the devextreme library so it is also including files from devextreme.
The project either doesn't load the external libraries because I don't include them, or things fail to compile because I am not importing it properly..? maybe?
My question is how do I exclude the library, add it to externals configuration, import it into my project files with typings and still have my project compile?
So I found a potential solution, of course the only problem is I have to stop using devextreme-react and use just plain jquery.
Basically if I put in the config this:
"jquery": {
"path": "https://code.jquery.com/jquery-2.1.1.min.js",
"globalName": "jQuery"
},
"devextreme/bundles/dx.all": {
"path": "https://cdn3.devexpress.com/jslib/18.1.6/js/dx.all.js",
"globalName": "DevExpress",
"globalDependencies": ["jquery"]
}
And then anywhere that requires a devextreme component I would have to create a wrapper and import devextreme like this:
import * as React from 'react';
import * as $ from 'jquery';
import DevExpress from 'devextreme/bundles/dx.all'; DevExpress;
export default class DxButton extends React.Component<DevExpress.ui.dxButtonOptions> {
private el: HTMLElement;
private $el: JQuery<HTMLElement>;
public constructor(props: DevExpress.ui.dxButtonOptions) {
super(props);
}
public componentDidMount() {
this.$el = $(this.el);
(this.$el as any).dxButton({
...this.props as any
} as DevExpress.ui.dxButtonOptions);
}
public componentWillUnmount() {
(this.$el as any).dxButton('dispose');
this.$el.remove();
}
public render(): React.ReactElement<DevExpress.ui.dxButtonOptions> {
return <div ref={el => this.el = el} />;
}
}
The reason I can't use devextreme-react is because it uses the modules of devextreme and forces the package to be bundled no matter what.
Not the ideal solution but seems like the only possible way to do it in the end.
Be aware to not use imports from the devextreme-react as straight forward.
//wrong import
import { TextBox } from 'devextreme-react';
You must specify import for each component you have.
//Correct import
import { TextBox } from 'devextreme-react/text-box';

Allow direct import of files within npm module like lodash

Lodash allows import like
import merge from 'lodash/merge';
This reduces the size of the import drastically. I maintain an npm module called react-spinners, and I want to allow the same import.
https://github.com/davidhu2000/react-spinners
For example, the current way to import is
import { BarLoader } from 'react-spinners';
I want to allow
import BarLoader from 'react-spinners/BarLoader';
Based on what I found, to do this, I need to have the following folder structure
main
- index.js <- the file that exports everything
- BarLoader.js
- ... other loaders
This structure is pretty messy because all the js files will be in the root directory.
The current set up I have is
main
- index.js
- dist <- output folder from compiling
- index.js
- BarLoader.js
- src <- uncompiled react code
- index.js
- BarLoader.js
So, currently, in order to import only a single loader is
import BarLoader from 'react-spinners/dist/BarLoader';
I cannot find anything that tells me how I can remove the dist from the above statement.
If you really want to mess with the npm-package You can create a file like
BarLoader.js on the main package folder and only export BarLoader(just like index.js exports every items)
Then you can import it via
import BarLoader from 'react-spinners/BarLoader';
But I would not recommend it as it would be a tedious task to create one file for each import
I guess it is a fair question, What is wrong with import BarLoader from 'react-spinners/dist/BarLoader';? maybe code readability?
I ran into the exact same issue, and did a bit of research on this.
Hackfix
Using Webpack, you can use config.resolve.alias, like so:
const webpackCfg = {
// ...
resolve: {
alias: {
'react-spinners': path.resolve(__dirname, 'node_modules/react-spinners/dist')
}
}
// ...
};
This way all files will be directly looked for in the dist folder.
import reactSpinners from 'react-spinners'; resolves to <root>/node_modules/react-spinners/dist (or node_modules/react-spinners/dist/index.js)
import BarLoader from 'react-spinners/BarLoader' will resolve to <root>/node_modules/react-spinners/dist/BarLoader etc...
How does Lodash do it?
As lodash points out in their README and also in this thread:
The Lodash github repository is not the pure source code, but rather "Lodash exported as a UMD module". They go further to explain:
Generated using lodash-cli:
$ npm run build
$ lodash -o ./dist/lodash.js
$ lodash core -o ./dist/lodash.core.js
However, they also mention here that they want to make this work without having two separate repositories in Lodash v5... Gonna be interesting to see how they end up doing it!
I myself did some experiments, and I could not get it to work quite yet. E.g. I tried to link to the dist folder and copy a modified version of the package.json into it after every build, and that kinda starts working. But sometimes the depending project will be unhappy about folders starting to get messed up. I'm sure, this can work (basically mixing source + dist into the dist folder) but it started to feel like a rabbit hole, so I have not continued pursuing this path (quite yet).

Resources