I have this environment variable file that exports variable config depending on the NODE_END. Currently production and development variable are residing inside a one file, code below. How can I separate the file like development.js and production.js:
if(process.env.NODE_ENV === 'production') {
module.exports = {
API_URL: "https://test.co/api"
}
}
else {
module.exports = {
API_URL: "http://testbeta.co/api"
}
}
You can have a separate file to bridge them and export the intended module. For example, make three separate files index.js, development.js, and production.js on the same folder api.
// production.js
module.exports = {
API_URL: "https://e27.co/api"
}
// development.js
module.exports = {
API_URL: "http://e27beta.co/api"
}
// index.js
let exp
if (process.env.NODE_ENV === 'production') {
exp = require('./production.js')
} else {
exp = require('./development.js')
}
module.exports = exp
Then you can require it elsewhere like
// elsewhere.js
const api = require('path_to_api_folder')
If you using Webpack you can leverage the DefinePlugin plugin for exactly this purpose:
https://webpack.js.org/plugins/define-plugin/#use-case-service-urls
if(isProd) {
config.plugins.push(new webpack.DefinePlugin({
'SERVICE_URL': JSON.stringify("http://prod.example.com")
}));
} else {
config.plugins.push(new webpack.DefinePlugin({
'SERVICE_URL': JSON.stringify("http://dev.example.com")
}));
}
You could have 2 separate webpack configs, each with the appropriate SERVICE_URL'S.
// webpack.config.dev.js
plugins.push(new webpack.DefinePlugin({
'SERVICE_URL': JSON.stringify("http://dev-url.com")
}));
// webpack.config.prod.js
plugins.push(new webpack.DefinePlugin({
'SERVICE_URL': JSON.stringify("http://prod-url.com")
}));
To build, just pass webpack the appropriate config:
webpack --config webpack.config.prod.js
webpack --config webpack.config.dev.js
Related
I am working on a React app bootstraped from create-react-app few years ago.
The app has a .env.dev file with many variables.
The start script is "start": "env-cmd -f .env.dev --use-shell \"react-scripts start\"",
React script version: "react-scripts": "^4.0.1",
When I console.log("gggg", process.env); I get all the variables.
When I:
describe('Login via API', () => {
it('Test login', () => {
console.log('teeest', process.env)
cy.login()
})
})
instead I get an empty object.
I tried to read the question How to use process.env variables in browser running by Cypress
however this question does not answer my question on how to make the process.env variables available to Cypress test files.
Also this question says to install dotenv. Dotenv comes with react-scripts, so no need to install it if the app was create by create-react-app.
I also tried this:
in cypress.config.js I added:
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
config.env = process.env
return config
}
}
})
And in the spec I try to get the variable defined in .env.dev file:
it('Test login', () => {
console.log('new', Cypress.env('REACT_APP_USERNAME'))
cy.login()
})
Still getting undefined.
Can anyone please help me to understand what's wrong? How can I make it work?
Edit:
According to an answer here I tried to install dotenv:
npm install dotenv --save
imported in the test:
import 'dotenv/config'
describe('Login via API', () => {
it('Test login', () => {
console.log('newwwww', Cypress.env('REACT_APP_USERNAME'))
console.log('teeest', process.env)
cy.login()
})
})
Npm start
npm run cypress:open
Result:
newwwww undefined
login-test.cy.js:7 teeest {}
Thanks
When you use "start": "env-cmd -f .env.dev --use-shell \"react-scripts start\"", the env-cmd command is specific to the process for the react app.
You would need the same to run before cypress opens it's process
package.json
{
...
"dependencies": {
...
},
"scripts": {
"cy:open": "env-cmd -f .env.dev cypress open",
...
}
}
Avoiding conflict with other env setting
I also recommend using the spread operator as shown below, otherwise you would lose any env var added in other ways, e.g command line additions.
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
config.env = {
...process.env, // add all process env var here
...config.env // plus any command line overrides
}
return config // return the altered config
},
},
env: {
login_url: '/login', // define some specific env var here
products_url: '/products'
}
});
Avoiding pollution of Cypress settings
If you take a look at Settings/Project Settings in the Cypress runner, you'll see a huge number of unnecessary settings which come from the general machine env var.
To pick just those with prefix REACT_,
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
const reactEnv = Object.keys(process.env).reduce((obj, key) => {
if (key.startsWith('REACT_')) {
obj[key] = process.env[key];
}
return obj;
}, {});
config.env = {
...reactEnv, // add REACT_ process env var here
...config.env // plus any command line overrides
}
return config // return the altered config
},
},
env: {
login_url: '/login', // define some specific env var here
products_url: '/products'
}
});
You're right about dotenv being included with react-scripts, but to access it in your test files you'll have to explicitly import it.
npm install dotenv
then at the top of your cypress code
import 'dotenv/config'
See usage instructions here
https://www.npmjs.com/package/dotenv
I am using dotenv to create an environment variable to insert a base url into my API call using react, dotenv, and webpack.
When I call the variables in the code, I don't get any error other than them printing as undefined. What am I doing wrong?
Here is my webpack file
const webpack = require("webpack");
const Dotenv = require("dotenv");
dotenv.config();
module.exports = (env) => {
return {
plugins: [
new Dotenv(),
new webpack.ProvidePlugin({
process: "process/browser",
}),
new webpack.DefinePlugin({
'process.env': JSON.stringify(process.env)
})
],
};
};
Here are what the variables look like in the .env file in the root of my project
API_URL=http://localhost:8000
BASE_URL=https://my-url.com
And here is how I am accessing it in the code (with axios for auth)
axiosAuth: axios.create({
baseURL: `${process.env.BASE_URL}`,
timeout: 5000,
})
Like Shah already explain, the only thing that you need is add the prefix "REACT_APP" to your current variables..
Change this:
API_URL=http://localhost:8000
BASE_URL=https://my-url.com
To this:
REACT_APP_API_URL=http://localhost:8000
REACT_APP_BASE_URL=https://my-url.com
Set the values explicitly to be defined instead of the whole process.env. Try:
new webpack.DefinePlugin({
'process.env': {
API_URL: JSON.stringify(process.env.API_URL),
BASE_URL: JSON.stringify(process.env.BASE_URL),
}
})
And if you're using create-react-app, prefixing REACT_APP_ will also work and you could remove the define plugin and Dotenv.
example:
API_URL -> REACT_APP_API_URL // .env
usage: `process.env.REACT_APP_API_URL
I've built a react app which uses the following structure;
node_modules
src/
app/
index.ts
index.html
...
server/
index.ts
...
node_modules/ // using the Alle pattern here
#<custom_packages>
api
components
Now I need to add a new app. Which runs on a different domain, but should be able to use as much shared code as it can, inc our custom packages. My first attempt was to do the following;
node_modules
src/
app1/
index.ts
index.html
...
app2/
index.ts
index.html
...
server/
index.ts // Uses ENV_VAR to serve a different bundle
...
node_modules/
#<custom_packages>
api
components
The problem I'm now running into, is that both apps generate their own assets etc. But i would like to share them between apps so the client can cache them. I could decide not to use webpack to build the assets and just put them in a static folder, but then I lose the support of the offline-plugin in webpack.
Also we decided to use a mono-repo in this case. This is making CI significantly harder, but managing shared code a lot easier. I'm kind of wondering if there are any seasoned developers that have faced this situation more often.
Basically, how would you structure 2 react apps that should share as much code as possible?
I needed a similar setup but for Angular. Here is the Webpack config that I have (I left only relevant parts):
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const apps = [
{
name: 'app1',
baseUrl: '/app1'
},
{
name: 'app2',
baseUrl: '/app2'
}
];
module.exports = function (args = {}) {
const isDev = !args.PROD;
const distPath = 'dist';
var config = {};
config.entry = {};
apps.forEach(function (app) {
config.entry[getAppBundleName(app)] = './src/apps/' + app.name + '/main.ts';
});
config.output = {
path: root(distPath),
filename: '[name].js',
chunkFilename: '[name].[chunkhash].js',
publicPath: '/dist/'
};
config.resolve = {
extensions: ['.ts', '.js', '.json'],
modules: [root('src'), root('node_modules')]
};
config.module = {
rules: [
// Add your loaders here
]
};
config.plugins = [
// Add your plugins here
// This enables tree shaking of the vendor modules
new CommonsChunkPlugin({
name: 'vendor',
chunks: ['admin'].concat(apps.map(getAppBundleName)),
minChunks: module => /node_modules/.test(module.resource)
}),
new CommonsChunkPlugin({
name: 'shared',
chunks: ['admin'].concat(apps.map(getAppBundleName)),
minChunks: module => /src(\\|\/)shared/.test(module.resource)
})
];
apps.forEach(function (app) {
var otherApps = apps.slice();
var appItemIndex = otherApps.indexOf(app);
if (appItemIndex > -1) {
otherApps.splice(appItemIndex, 1);
}
config.plugins.push(new HtmlWebpackPlugin({
template: 'index_template.html',
title: app.name,
filename: getAppDevServerHtmlFileName(app),
excludeChunks: otherApps.map(getAppBundleName),
chunksSortMode: 'manual',
chunks: ['vendor', 'shared', getAppBundleName(app)],
inject: 'head',
metadata: {
baseUrl: app.baseUrl
}
}));
});
config.devServer = {
port: 4200,
stats: stats,
historyApiFallback: {
rewrites: apps.map(function (app) {
return {
from: new RegExp('^' + app.baseUrl),
to: '/dist/' + getAppDevServerHtmlFileName(app)
}
}),
},
};
return config;
}
function getAppBundleName(app) {
return app.name;
}
function getAppDevServerHtmlFileName(app) {
return app.name + '_index.html';
}
function root(args) {
args = Array.prototype.slice.call(arguments, 0);
return path.join.apply(path, [__dirname].concat(args));
}`
In my case, I have the following folder structure, which is similar to yours:
node_modules
src/
apps/
app1/
...
main.ts
app2/
...
main.ts
shared/
shared-module1/
...
shared-module2/
...
index.ts
...
webpack.config.js
And here is the output after compilation in the dist folder:
dist/
app1.js
app1_index.html
app2.js
app2_index.html
vender.js
shared.js
As you can see, vendor.js and shared.js contain the shared code between the apps.
I'd like to use a config file in a react/redux app.
In this thread
How to store Configuration file and read it using React
i found a nice solution but i receive the following error when using require in my redux action file.
Module not found: Error: Cannot resolve module 'Config'
I managed to solve it with the following webpack config:
plugins:[
new webpack.DefinePlugin({
'process.env':{
'NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}
}),
],
externals: {
'Config': JSON.stringify(process.env.NODE_ENV === 'production' ? {
api: "http://www.vitto.mt.it/eventi/api/public/"
} : {
api: "/react/eventi/api/public/"
})
},
and calling it:
var Config = require('Config');
But i still have to set manually the NODE_ENV value. How can i set it using an external file?
Update current problem :
it seems that the webpack hot loader goes wrong,because when i run the following cmd:webpack,it can be built as usual.but when i run ""dev": "webpack-dev-server --color --hot --progress && node ./server.js"".webpack cannot generate built files for me .
my webpack.config is as follows:
module.exports = {
entry: getEntries(),
.....
function getEntries(){
var routeDir = path.join(SRC_DIR,"javascripts","routes");
var routeNames = routeDir?fs.readdirSync(routeDir):[];
var nameMaps = {};
routeNames.forEach(function(routeName){
var filename = routeName.match(/(.+)\.js$/)[1];
console.log("filename in entry ",filename);
if(filename){
var devEntryPath = [
'webpack-dev-server/client?http://127.0.0.1:3001', // WebpackDevServer host and port
'webpack/hot/only-dev-server',
path.join(routeDir,filename)
];
nameMaps[filename] = devEntryPath;
}
});
return nameMaps;
}
server.js
var server = new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
hot: true,
historyApiFallback: true
}).listen(3001,'localhost',function(err,result){
if(err) console.log(err);
console.log("webpack listening at port 3001");
});
var app = express();
app.get("/monitor/index",function(req,res){
res.sendFile(__dirname+"/src/views/"+"page1.html");
});
app.get("/monitor/category/*",function(req,res){
res.sendFile(__dirname+"/src/views/"+"page2.html");
});
app.use(express.static(__dirname))
.listen(9090, 'localhost', function (err, result) {
if (err) console.log(err);
console.log('Listening at localhost:9090');
});
finally,i found where the problem is,and know the relationship between webpack-dev-server and my express server.
when using hot-loader with webpack-dev-server:
step1:the webpack build the input file to the publicPath (which was designated in "output" of webpack.config.js).
step2,the node server will send html to the front,and search for the related assets(such as js,img etc),but where? we can change the script(related with html) path to the webpack-dev-server.(just generated by step1),so node-server will ask webpack-dev-server for help.
to sum up ,i modified 3 places:
publicPath of webpackDevServer
webpack output(publicPath),equal to above
script path in html.
that's all.and now,my project can run as expected.