Analyzing a Gatsby Bundle File for Slow Load - reactjs

I have a large SPA which takes a second or two to load completely. What can I use to figure out what is causing my SPA to have a slow first load?
This is a video of the issue.
I have removed all calls to APIs. I've tried tools such as gatsby-plugin-webpack-bundle-analyser-v2, but I don't believe it's accurate. On gatsby build, it shows a bundle size of 10mb where the actual bundle .js file is only 2.3mb.
I can't figure out how to export a webpack-stats file to use https://chrisbateman.github.io/webpack-visualizer/.

If you want to export a stat file with gatsby-plugin-webpack-bundle-analyser-v2, you need to pass this option :
gatsby-config.js
{
resolve: 'gatsby-plugin-webpack-bundle-analyser-v2',
options: {
generateStatsFile: true,
},
},
It will generate JSON file named stats.json in your /public dir.
Hope this helps.

Related

How to have Nx environment variables in React app per environment?

Context
I have a three projects inside my Nx workspace, two applications which are react apps (both of them have shared logic, however they are intended for different platforms web and microsoft teams and must be separated) and a library which contains logic for an api client, which both of the applications use.
The api client project requires the variable base URL which is environment specific.
I can define environment variables and introduce file replacements in the build process for .ts files (e.g. environment.ts is replaced with environment.production.ts, when configuration is production), however I do not want to reference this file (environment.ts) in the api client project so as not to introduce two way dependencies.
What have I tried
From the api project I was not able to extract the logic which depends on the URL variable as this is tied to some code generation which is changeable.
I succeeded in providing this variable by using .env file in the root of the application project, the variable is in the format NX_MY_URL, and could be accessed with process.env.NX_MY_URL.
However I was not able to change this variable when changing the build configuration (e.g. development, test, production). I have tried adding a fileReplacements task such as
"configurations": {
"development": {
"fileReplacements": [
{
"replace": "apps/ra-web/src/environments/environment.ts",
"with": "apps/ra-web/src/environments/environment.development.ts" //<----- This works fine
},
{
"replace": "apps/ra-web/.env",
"with": "apps/ra-web/.development.env" //<----- This does not work, .env values remain
}
],
Question
How can .env files be replaced based on Nx target's configuration?
Why the mentioned approach does not work?
The reason of why the fileReplacements approach is not working is because those replacements are meant for the building process, which is the bundler the one in charge (webpack or vite, etc). That file content replacement is actually working, yet it doesn't happen at file system level but at memory level (for the bundling process). As your application code does not "import" that .env file (and you should not directly depend on it), then those file replacements make no difference in the build output.
On the other side, Nx is in charge of reading the .env file (at file-system level) and loading the data as environment variables so that when the bundling process starts, those are available. This a completely separate process than the fileReplacements done by the bundler.
How to achieve what you are looking for?
If you think about projects in libs as shareable/re-usable code, then you can imagine those are external dependencies added to your apps (or other libs). As those dependencies are external, they should rely on their implementor to get the data needed for them to work.
Having the above in mind, your lib public API (the main index.ts file) should be parametrized to receive that base URL that will depend on each application. With that you can keep working with the environment.ts file replacements, get the value in the app and pass it down to the lib.
Example:
// app's main.tsx
import { environment } from './environments/environment';
import { apiClient } from '#myorg/api-client';
apiClient.init({ baseUrl: environment.baseUrl });
// api-client's index.ts
export { apiClient } from './lib/api-client';
// api-client's api-client.ts
export const apiClient = {
init: ({ baseUrl }) => { ... }
};
If you still need to work with the .env file (not trying to replace it), just the the env vars in the environment.ts as work with it as mentioned above.

Invalid plugin options for "gatsby-source-contentful"

I'm encountering the following error when attempting to open a project I forked via GitHub.
success open and validate gatsby-configs - 0.492s
ERROR #11331 PLUGIN
Invalid plugin options for "gatsby-source-contentful":
- "accessToken" is required
not finished load plugins - 6.220s
I've made several edits but am unable to work on the project as I'm unable to open it at the moment. I do have a contentful account, but am fairly new to Gatsby and am unaware of how to set a new value for the accessToken.
Would I need to do this via process.env, or am I missing the process entirely?
Thank you, any help is appreciated.
Would I need to do this via process.env, or am I missing the process
entirely?
Absolutely, you need to provide to Gatsby and Contentful your access tokens. Gatsby, by default, takes the .env.development and .env.production when running gatsby develop and gatsby build respectively, so you will need to add the credentials to the environment files.
First of all, add the following snippet in your gatsby-node.js, above the module exportation:
require("dotenv").config({
path: `.env.${process.env.NODE_ENV}`,
})
This will tell Gatsby which file needs to be taken in each running command.
The next step is to fill the environment files, in both of them add:
CONTENTFUL_ACCESS_TOKEN=123456
CONTENTFUL_SPACE_ID=78910
So, finally your gatsby-config.js should look like:
// In your gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
},
},
],
}

REACT: Add multiple entry points in config-overrides.js file for multiple html files

I am new to React CRA (it is rewired as per doc in ant-design description for project setup) and facing issues in adding multiple entry points in webpack-config file.
I have 2 html files in public folder, index.html & stack.html.
-public
-index.html //runs on localhost:3000
-stack.html // runs on localhost:3000/stack.html
-src
-index.tsx
-stack.tsx
-config-overrides.ts
Default html index.html and index.tsx is used to boot and load react components.
I created stack.html file and accordingly i have created stack.tsx file as entry point to boot and load react components. I am unable to wire things up.
What configuration should be made to wire this up.
It is possible to do this, but you will need to eject from CRA. After that:
Add entry to the other html file in paths.js.
Update entry inside webpack.config.js and add the second html file entry (to be similar to the original entry).
Change the output file name inside webpack.config.js. Change static/j/bundle.js to static/js/[name].bundle.js.
Upadte webpack plugins to generate second file with injected JS scripts (also inside webpack.config.js).
Update the ManifestPlugin configuration to include the new entry point (also inside webpack.config.js).
Finally, there are two different steps for development and production.
For DEV, rewrite paths using the following in webpackDevServer.config.js (if you want to redirect all /admin to admin.html file):
verbose: true,
rewrites: [
{ from: /^/admin/, to: '/admin.html' },
],
For Production, this step is different for each provider. For Heroku, it is very easy, just create a static.json file with the following content:
{
"root": "build/",
"routes": {
"/admin**": "admin.html",
"/**": "index.html"
}
}
For full details and file diffs, see this post.
AFAIK, there are no good ways of doing this.
One way is to just use react-scripts and build multiple apps by copying and replacing index.html and index.js for each build. Something like
https://gist.github.com/jkarttunen/741fd48eb441137404a168883238ddc1
Also for CRA v3, there is an open PR for fixing this: https://github.com/facebook/create-react-app/pull/8249

How to slowly integrate webpack by including in the bundle non imported (nor exported) files?

I'm working in a project where we want to integrate Webpack into our workflow. The problem is, we have over 1000 AngularJS files and adding import/export to all of them in one go is not an option for us. We'd like to bundle all of them and slowly incorporate the import/exports as we work on each file over time.
How would you approach that problem? Any specific best practices when doing this?
We literally had the same problem. Essentially you want to create "entry point files" that perform requires for all your files, since this is how webpack works (it follows the dependency tree). Then point webpack at these "entry point files".
The example at the link above uses TypeScript, but you can easily use ES5 like this:
# ./entry-points/feature1.js
importAll = function(r) {
r.keys().forEach(r);
};
importAll(require.context('./app/feature1', true, /module\.js$/));
importAll(require.context('./app/feature1', true, /(^(?!.*(spec|module)\.js).*\.js)$/));
You can grab a polyfill for Object.keys here, and Array.forEach` here.
Then point to this file from your webpack config like this:
entry: {
'feature1': './entry-points/feature1.js'
}
You can read more details here

How do you handle cache busting SVG with webpack and server side rendered React?

I have a need for both inline SVG (currently handled with a sprite using <svg><use xlinkHref="icons.svg#info" /></svg>) and SVG used as a background in CSS (background-image: url(/assets/svg/info.svg);).
I want to implement hashing of filenames to help with cache busting, which works fine in the case of CSS, using the following:
{
test: /\.svg$/i,
loader: 'file-loader',
query: {
name: 'svg/[name]-[sha512:hash:base64:7].[ext]',
publicPath: PATHS.public
}
}
However, I want to also be able to refer to the individual SVG icons inline, whilst ensuring that the source files have a hashed filename.
Does anybody have a foolproof approach to getting the best of both worlds?
Ultimately I want to have a source folder of SVG files which during a build are:
Individually have their filenames hashed
All compiled to a sprite which has a hashed filename and is then available for use with an <svg> tag?
Thanks,
Dan
It turns out here that the problem was fairly simple, I wasn't running the sass loader over both client and server javascript.
While I've been working this problem out I've been keeping a separate demo repository to build out a good starting place for server and client rendered React with Webpack and a good production ready set of processing for assets.
https://github.com/danielrosewarne/webpack-demo
Hope that helps!

Resources