create-react-app
create-react-app copied to clipboard
Tree shaking only works with process.env.NODE_ENV?
I'm finding that tree shaking only works with process.env.NODE_ENV
and won't work with process.env.REACT_APP_[my variable name]
For example, if I do the following myModule
is not showing up in my production build.
import {myFunction} from './myModule'
if(process.env.NODE_ENV === 'development') {
myFunction()
}
But, if I want to use a different environment variable myModule
is always included.
import {myFunction} from './myModule'
if(process.env.REACT_APP_ENABLE_MY_FUNCTION === 'true') {
myFunction()
}
Is it known that tree shaking only works with process.env.NODE_ENV
? Is there any way to do tree shaking with a custom environment variable?
Now I see that REACT_APP_
environment variables are available at runtime, whereas NODE_ENV
is not. So this makes sense that using REACT_APP_
environment variables wouldn't enable tree shaking.
I guess that means my question is, is there a way to add custom environment variables that behave like NODE_ENV
, where process.env.NODE_ENV
is replaced with a string at build time? It seems the answer is no?
I see here (https://github.com/facebook/create-react-app/issues/8626) someone suggested using a dynamic import, which ultimately works if there is no other solution. However, then I'll need to write a post-build script to delete these bundles if I want to make sure they aren't on my server. And I need to update my logic to handle asynchronously loading the module, which isn't ideal.
Here it is....
If you want to do this:
import {myFunction} from './myModule'
if(process.env.REACT_APP_ENABLE_MY_FUNCTION === 'true') {
myFunction()
}
You must be sure to define REACT_APP_ENABLE_MY_FUNCTION
. If you do not define it then the if(process.env.REACT_APP_ENABLE_MY_FUNCTION === 'true')
doesn't get turned into if(false)
at build time.
But it still evaluates to false at runtime.
So when using this command (REACT_APP_ENABLE_MY_FUNCTION=false npm run build
), myModule
is not included in the build. But if I just run npm run build
without explicitly setting REACT_APP_ENABLE_MY_FUNCTION
it is included.
I don't see this documented anywhere, but it probably should be. Although I realize this behavior comes from something create-react-app depends on and not create-react-app itself. The only way I figured this out was by taking the time to go through the generated bundle.
This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 5 days if no further activity occurs.
I also faced this issue. Thanks for your investigation on this. I helped a lot! Would be great to have this documented. Do you think you can contribute this to the documentation?
I also faced this issue. Thanks for your investigation on this. I helped a lot! Would be great to have this documented. Do you think you can contribute this to the documentation?
I'm not completely sure this is the best practice for achieving what I'm trying to do, so it would be nice to get some feedback from someone more knowledgable first.
For more clarity, my specific use case here is that I have two distinct builds I want to do for my app created with create-react-app. I have one build that is my standard web build, hosted on Netlify, and another build that is for packaging with Cordova. In my standard web build I want to strip out all Cordova references at build time. Using the method I described above is working for this use case, however, since it was so tricky to figure out I do wonder if there is a more appropriate approach to solving this problem?
Hi @NAllred91,
I am having a problem tree shaking some code for production while experimenting with MirageJs for development following this and got here while looking for solutions.
The thing is I am not even being able to tree shake my code with process.env.NODE_ENV
.
I have tried the following
import { makeServer } from "./server";
if (process.env.NODE_ENV === "development") {
makeServer();
}
or
import { makeServer } from "./server";
if (false) {
makeServer();
}
And for any of the cases, the code is removed. It is removed if I do
import { makeServer } from "./server";
if (false) {
// makeServer();
}
I am using the yarn build
command, which uses react-scripts
. The version I am using of react-script is 4.0.3. It is a brand new project created with create-react-app with no major changes. Which version are you using?
I am using the typescript template of create-react-app.
Do you have any ideas of what it could be?
Thanks and sorry for bothering you with not related things.
@joaoantunesTD I'm on 3.4.1 for react-scripts
, I doubt that matters much but maybe give it a try on that version and see if you have the same issue?
It is odd that if(false)
doesn't work, but commenting out the call to makeServer
does. All I can think is to try 3.4.1 and see if you get the same results?
I was also using typescript when I created this issue. I have never tried with javascript.
Thanks @NAllred91 for answering so fast :) !
Just to let you know what I have found. I do not know really why, but if I move the code inside theif
I have the code removed on production build. I moved the miragejs
import to index.tsx
and I have now something like:
./server.ts
import { AnyFactories, AnyModels } from "miragejs/-types";
import { ServerConfig } from "miragejs/server";
export const BASE_CONFIGS: ServerConfig<AnyModels, AnyFactories> = {
// configs removed here, just to show
};
export function generateConfigsForEnv(
environment = "test"
): ServerConfig<AnyModels, AnyFactories> {
return {
environment,
...BASE_CONFIGS,
};
}
And then on index.tsx
:
import React from "react";
import ReactDOM from "react-dom";
import { createServer } from "miragejs";
import "./index.css";
import App from "./App";
import { generateConfigsForEnv } from "./server";
if (process.env.NODE_ENV === "development") {
createServer(generateConfigsForEnv("development"));
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
I also face this problem. follow above comments,i found import did not work but require did work,and "react-scripts": "4.0.3"。when I use import i found axios-mock-adapter in my bundle.js
import:
require:
//mock/index.ts
my english is not good,sorry about this.
Hi @wangi4myself ,
Thanks! Interesting, I have not tried with require
. I ended up using an asynchronous import, something like this:
if (process.env.NODE_ENV === 'development') {
(async function loadApiMockServer() {
const apiMock = await import('my-lib');
apiMock.initiateApiMock({ environment: 'development' });
})();
}
PS: Your English is fine :)
Having the same problem, and here is my solution:
if (process.env.NODE_ENV !== 'production') {
import('./server').then(({ makeServer }) => {
makeServer({ environment: 'development' });
});
}
Async imports only split code to another chunk but will not prevent it from bundling it to final dist.
My solution is to always define a value with webpack define plugin:
new webpack.DefinePlugin({
'process.env.MOCK': JSON.stringify(process.env.MOCK === 'true')
}),
In you app code, don't do any comparison check, so it will be replaced at build time and resolves to if (false) {...}
then the whole import will be eliminated.
import startMock from './mock'
// don't do any comparison here.
if (process.env.MOCK) {
startMock()
}
@zoubingwu Thank you, your solution is the best here and it works fine for me.
I ended up a little improve in this way, it avoids conflict env error.
new webpack.DefinePlugin({
...(!process.env.REACT_APP_ENABLE_MSW && {
'process.env.REACT_APP_ENABLE_MSW': false,
}),
}),