goatandsheep / react-native-dotenv

Load react native environment variables using import statements for multiple env files.
https://www.npmjs.com/package/react-native-dotenv
MIT License
798 stars 48 forks source link

.env variables are cached #422

Open AbdulBsit opened 1 year ago

AbdulBsit commented 1 year ago

Describe the bug I tried upgrading, dev dependencies releated to babel, but no luck, it cached the variable, until we change something in to the file we imported the "@env", when we undo the change, it goes back to cached value

Expected behavior Change in .env should reflect in files, after a restart

Additional context

"@babel/core": "^7.21.3",
"@babel/runtime": "^7.21.0",
"react-native-dotenv": "^3.4.7",
github-actions[bot] commented 1 year ago

Hey, thank you for opening this issue! 🙂 To boost priority on this issue and support open source please tip the team at https://issuehunt.io/r/goatandsheep/react-native-dotenv/issues/422

goatandsheep commented 1 year ago

Hi @AbdulBsit those Babel package versions are only really if you're using something overwrites the versions such as expo. Are you using expo? Also are the babel packages installed as dependencies or resolutions? Please tell me what else you tried that didn't work in the cacheing section of the README as I'm unable to reproduce this.

AbdulBsit commented 1 year ago

Hey @goatandsheep These babel packages are installed as devDependencies, and I am not using expo

Also things i have tried which not solves the issue

package.json

{
  "name": "app",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "postinstall": "patch-package",
    "ios": "react-native run-ios",
    "start": "react-native start",
    "test": "jest",
    "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
  },
  "dependencies": {
    "@bandwidth/webrtc-browser": "^0.13.1",
    "@gorhom/bottom-sheet": "^4.4.5",
    "@notifee/react-native": "^7.3.0",
    "@react-native-async-storage/async-storage": "~1.15.0",
    "@react-native-clipboard/clipboard": "^1.11.1",
    "@react-native-community/datetimepicker": "^6.7.1",
    "@react-native-community/netinfo": "^9.3.7",
    "@react-native-firebase/app": "^16.5.0",
    "@react-native-firebase/messaging": "^16.5.0",
    "@react-native-menu/menu": "^0.7.3",
    "@react-navigation/bottom-tabs": "^6.0.9",
    "@react-navigation/drawer": "^6.5.0",
    "@react-navigation/material-top-tabs": "^6.0.6",
    "@react-navigation/native": "^6.0.6",
    "@react-navigation/stack": "^6.0.11",
    "@sentry/react-native": "^5.0.0-alpha.10",
    "appcenter": "4.4.5",
    "appcenter-analytics": "4.4.5",
    "appcenter-crashes": "4.4.5",
    "axios": "^0.21.1",
    "events": "^3.3.0",
    "jwt-decode": "^3.1.2",
    "moment": "^2.29.1",
    "newrelic-react-native-agent": "0.0.9",
    "react": "18.1.0",
    "react-native": "^0.70.7",
    "react-native-action-sheet": "^2.2.0",
    "react-native-autolink": "^3.0.0",
    "react-native-background-timer": "^2.4.1",
    "react-native-callkeep": "^4.3.6",
    "react-native-clean-project": "^4.0.1",
    "react-native-code-push": "^7.1.0",
    "react-native-device-info": "^8.7.1",
    "react-native-document-picker": "^8.1.3",
    "react-native-elements": "^3.4.2",
    "react-native-gesture-handler": "^2.6.2",
    "react-native-get-random-values": "~1.7.0",
    "react-native-image-picker": "^4.7.3",
    "react-native-image-viewing": "^0.2.1",
    "react-native-incall-manager": "^3.3.0",
    "react-native-logs": "^5.0.1",
    "react-native-permissions": "^3.1.0",
    "react-native-popup-menu": "^0.15.10",
    "react-native-reanimated": "^2.14.4",
    "react-native-safe-area-context": "3.2.0",
    "react-native-screens": "~3.4.0",
    "react-native-simple-toast": "^1.1.4",
    "react-native-snackbar": "^2.4.0",
    "react-native-sound": "^0.11.2",
    "react-native-splash-screen": "^3.3.0",
    "react-native-svg": "^12.3.0",
    "react-native-tab-view": "^2.15.2",
    "react-native-vector-icons": "^9.2.0",
    "react-native-voip-push-notification": "^3.3.0",
    "react-native-webrtc": "^106.0.5",
    "react-native-webview": "^11.26.1",
    "react-style-object-to-css": "^1.1.2",
    "socket.io-client": "^2.4.0",
    "sp-react-native-in-app-updates": "^1.2.0",
    "uuid": "^8.3.2",
    "validator": "^13.5.2"
  },
  "devDependencies": {
    "@babel/core": "^7.21.3",
    "@babel/runtime": "^7.21.0",
    "@react-native-community/eslint-config": "^2.0.0",
    "@tsconfig/react-native": "^2.0.2",
    "@types/jest": "^26.0.23",
    "@types/react": "^18.0.21",
    "@types/react-native": "^0.70.6",
    "@types/react-test-renderer": "^18.0.0",
    "@typescript-eslint/eslint-plugin": "^5.37.0",
    "@typescript-eslint/parser": "^5.37.0",
    "babel-jest": "^26.6.3",
    "babel-plugin-inline-import": "^3.0.0",
    "eslint": "^7.32.0",
    "jest": "^26.6.3",
    "metro-react-native-babel-preset": "0.72.3",
    "patch-package": "^6.5.1",
    "react-native-dotenv": "^3.4.7",
    "react-native-svg-transformer": "^1.0.0",
    "react-test-renderer": "18.1.0",
    "typescript": "^4.8.3"
  },
  "jest": {
    "preset": "react-native",
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js",
      "jsx",
      "json",
      "node"
    ]
  }
}

babel.config.js

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: ['module:react-native-dotenv', 'react-native-reanimated/plugin'], // react-native-reanimated/plugin must be last
};

metro.config.js

const {getDefaultConfig} = require('metro-config');

module.exports = (async () => {
  const {
    resolver: {sourceExts, assetExts},
  } = await getDefaultConfig();
  return {
    transformer: {
      babelTransformerPath: require.resolve('react-native-svg-transformer'),
      getTransformOptions: async () => ({
        transform: {
          experimentalImportSupport: false,
          inlineRequires: false,
        },
      }),
    },
    resolver: {
      assetExts: assetExts.filter(ext => ext !== 'svg'),
      sourceExts: [...sourceExts, 'svg'],
    },
  };
})();
MedulHasan commented 1 year ago

I experience the same issue. Is there any update or solution?

Jamal-ReachFirst commented 1 year ago

same here

Amurmurmur commented 1 year ago

api.cache(true) thats what causes the caching, it has nothing to do with this lib. Its the fact that babel is caching it, so you can reset a cache on every start when you update your envs. other libs like inline-env also have the same behaviour. Hope it helps

prageeth commented 1 year ago

Having the same issue. Setting api.cache(false) doesn't solve it either.

illusion77 commented 1 year ago

I come to my react native project root directory, run these command

rm -rf ~/Library/Developer/Xcode/DerivedData
xcrun simctl erase all
rm -rf node_modules
npm install
npx react-native start --reset-cache

it does work

prateekuttreja2020 commented 1 year ago

I am having the same issue. Is there any update or solution?

MedulHasan commented 1 year ago

I am having the same issue. Is there any update or solution?

In my case, using a single quote instead of the double quote when importing the 'env' variable does not cause this problem. ex: import {BASE_API_URL} from '@env';

winston-lee commented 1 year ago

I am having the same issue. Is there any update or solution?

In my case, using a single quote instead of the double quote when importing the 'env' variable does not cause this problem. ex: import {BASE_API_URL} from '@env';

I don't think it does. Try changing the value in the variable and see if it picks it up.

winston-lee commented 1 year ago

This is the one that worked for me https://github.com/goatandsheep/react-native-dotenv/issues/75#issuecomment-728055969

In my package.json

"scripts": {
    "start:devmobile": "APP_ENV=devmobile expo start --clear",
}

And I ran

npm run start:devmobile

(P.S. i'm just starting out front end development so don't laugh at what i just did ;))

philipostli commented 1 year ago

I am having the same issue. Is there any update or solution?

In my case, using a single quote instead of the double quote when importing the 'env' variable does not cause this problem. ex: import {BASE_API_URL} from '@env';

This solved it for me. Says more about my experience with node than anything. Thank you for pointing this out!

david-a commented 1 year ago

The problem in my case was Metro cache. My solution was to add this line to the metro.config.js file:

module.exports = {
  ...
  cacheVersion: process.env.APP_ENV,
  ...
};

my package.json scripts are (always clearing cache for production runs) -

    "start": "react-native start",
    "start:staging": "APP_ENV=staging react-native start",
    "start:prod": "APP_ENV=prod react-native start --reset-cache",

according to the docs it's "an arbitrary string appended to all cache keys in the project before they are hashed". Subsequent runs of the same env keep the cache, while switching between them seems to invalidate it.

Hope it helps

rob-johansen commented 1 year ago

This is what worked for me in the metro.config.js file:

resetCache: true
jfbaraky commented 1 year ago

This is what worked for me in the metro.config.js file:

resetCache: true

Adding the resetCache: true to my metro.config.js worked. The final file is:

const { getDefaultConfig } = require('expo/metro-config');

module.exports = (() => {
  const config = getDefaultConfig(__dirname);

  const { transformer, resolver } = config;

  config.transformer = {
    ...transformer,
    babelTransformerPath: require.resolve('react-native-svg-transformer'),
  };
  config.resolver = {
    ...resolver,
    assetExts: resolver.assetExts.filter((ext) => ext !== 'svg'),
    sourceExts: [...resolver.sourceExts, 'svg'],
  };

  config.resetCache = true; // ADDED IT HERE <<<

  return config;
})();

But it was a nightmare until I found out about the caching. Probably this shouldn't be the default behaviour.

o-alexandrov commented 1 year ago

resetCache is a bad option to solve the caching issue, as you disable caching for everything.

Using cacheVersion described in detail in the prior comment is the only rational solution.

jfbaraky commented 1 year ago

The cacheVersion will only work if you manually set a new version every time you update your environment variables.

Having it only separated for each environment will make you face the same issues with the cache.

o-alexandrov commented 1 year ago

You should concatenate all environment variables, so the cacheVersion changes together with any environment variable.

Example metro.config.js:

module.exports = {
  cacheVersion: [process.env.VAR1, process.env.VAR2, …].join(‘’)
}
jfbaraky commented 1 year ago

Shouldn't this be the default behavior?

At least for the development variables. Should they be cached at all?

sanjarcode commented 11 months ago

I can confirm this happens with React Native CLI too (i.e. react-native command)

pauloguilherm commented 11 months ago

npm start -- --reset-cache it worked for me

LouisJS commented 10 months ago

Setting cacheVersion in metro.config.js doesn't reset cache when switch from one .env to another (or just any changes that happens in .env basically).

We want to repeat : resetCache is a bad option to solve the caching issue, as you disable caching for everything.

You might wanna change your .env settings easily without manual deleting of cache. But still want to keep your cache when your working on the same env

SulthanYS commented 10 months ago

The problem in my case was Metro cache. My solution was to add this line to the metro.config.js file:

module.exports = {
  ...
  cacheVersion: process.env.APP_ENV,
  ...
};

my package.json scripts are (always clearing cache for production runs) -

    "start": "react-native start",
    "start:staging": "APP_ENV=staging react-native start",
    "start:prod": "APP_ENV=prod react-native start --reset-cache",

according to the docs it's "an arbitrary string appended to all cache keys in the project before they are hashed". Subsequent runs of the same env keep the cache, while switching between them seems to invalidate it.

Hope it helps

when I trying with this I got the following error only. even after I change in the metro.config.js

'APP_ENV' is not recognized as an internal or external command, operable program or batch file.

mofolo commented 8 months ago

I'm only seemingly having this issue with my "Production" file. Staging and Dev works, but the moment I set it to APP_ENV=Production it only reads from my .env.staging file. However when I set the release to Debug (dev), it reads from my .env.development file without any issue.

goatandsheep commented 7 months ago

@mofolo this is a whole other issue unfortunately. If you're using production, you don't need APP_ENV, since NODE_ENV should already do it. Also, APP_ENV doesn't really work with production and development. Basically APP_ENV was introduced very late for people who were struggling to override NODE_ENV, but it has it's quirks so try to avoid using it where possible. Feel free to open a separate ticket on this if I'm wrong

goatandsheep commented 7 months ago

@mofolo actually it occurred to me, what if you try in the config setting the name for APP_ENV to something like REACT_APP_ENV

{
  "plugins": [
    ["module:react-native-dotenv", {
     "envName": "REACT_APP_ENV"
    }]
  ]
}
goatandsheep commented 7 months ago

@SulthanYS are you using Windows Powershell? to set environment variables in CLI, you need to do it differently

Set APP_ENV=test&& react-native start
goatandsheep commented 7 months ago

@jfbaraky you're right about the cache. it should be working and it isn't working. I just tested it now even with the package version resolutions. I worked with the babel team to try fix this. so i'm not sure why it's not working. Anyways, the solution is just to have to do api.cache(false) or --reset-cache every time you change your env type, or env values

jovanialferez commented 5 months ago

this works for me rm -rf node_modules/react-native-config/ios/ReactNativeConfig/GeneratedDotEnv.m

Aixufey commented 4 months ago

Hi,

the module import with version 3.4.11 is breaking but I tried lower the version to 3.4.9 and only process.env. seems to work. Could not resolve @env on launch.

athrvk commented 4 months ago

After scratching my head a lot the workaround i did is explained in this article

updating cacheVersion in metro.config.js did not work for me so i used resetCache

and to update that i am calculating hash of env files and updating as required in a prestart script

since i am working on windows my solution is mainly for that but i think it can be used on other platforms

hope this helps!

prestart.mjs

import inquirer from 'inquirer';
import fs from 'fs';
import crypto from 'crypto'; // To calculate file hashes

const questions = [
    {
        type: 'list',
        name: 'environment',
        message: 'Which environment to run in?',
        choices: ['development', 'production'],
        default: 'development'
    }
];

inquirer.prompt(questions).then(async (answers) => {
    const env = answers.environment;
    const envFile = `.env.${env}`;

    try {
        // Check if the environment file exists
        if (!fs.existsSync(envFile)) {
            console.error(`Environment file ${envFile} not found.`);
            return;
        }

        // Calculate the hash of the environment file
        const fileHash = calculateFileHash(envFile);

        // Store previous state (if available)
        const prevStateFile = '.prestart.state.json';
        let prevState = {};
        if (fs.existsSync(prevStateFile)) {
            prevState = JSON.parse(fs.readFileSync(prevStateFile, 'utf8'));
        }

        // Check for changes
        const envChanged = prevState.env !== env;
        const fileChanged = prevState.fileHash !== fileHash;

        // Update .env file and set resetCache
        const shouldResetCache = envChanged || fileChanged;
        fs.writeFileSync('.env', `APP_ENV=${env}\nresetCache=${shouldResetCache}`);

        // Update the state file
        fs.writeFileSync(prevStateFile, JSON.stringify({ env, fileHash }));

        console.log(`Running in ${env} mode...`);

    } catch (error) {
        console.error('Error during setting environment:', error);
    }
});

function calculateFileHash(filename) {
    const fileBuffer = fs.readFileSync(filename);
    const hash = crypto.createHash('sha256');
    hash.update(fileBuffer);
    return hash.digest('hex');
}

metro.config.js

const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
require('dotenv').config();

/**
 * Metro configuration
 * https://reactnative.dev/docs/metro
 *
 * @type {import('metro-config').MetroConfig}
 */
const config = {
    resetCache: process.env.resetCache === 'true',
};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);

package.json

{
  ...,
  "scripts": {
    ...,
    "prestart": "node prestart.mjs",
    ...
  },
  ...
}
pke commented 3 months ago

I wonder, when the files change, it should already be picked up by the plugin, since it checks the modified time of each file?

pke commented 3 months ago

even using cacheVersion in metro.config does not work reliably. Because the babel plugin will only pick up changes in files and not reprocess them, even if the metro cache ins invalidated.