issues while running node.js project on windows - node.js

I got node.js project which is developed to run on linux. Now the requirement is to run it on windows. I am facing some issues in that.
On linux, project runs with script which in turn runs below commands
export ABC_ENV=manufacturing; export NODE_ENV=development; npm run dev
package.json
"scripts": {
"dev": "ts-node-dev --respawn --transpile-only --inspect -- src/app.ts",
"build": "./node_modules/typescript/bin/tsc"
},
src/config/app.config.ts
import {readFileSync} from 'fs';
import {join} from 'path';
const secureEnv = require('secure-env');
/**
* Decrypt the .env.enc file, bind parsed environment variables to
* global.env, and expose them centrally as aliased constants
* for consumption throughout the app.
*/
try {
const basePath = join(__dirname, '../../', `.env-${process.env.ABC_ENV}`)
const secret = readFileSync(`${basePath}.key`, {encoding: 'utf8'}).trim();
global.env = secureEnv({secret, path: `${basePath}.enc`});
} catch (e) {
console.error(`Problem reading encrypted .env variables:\n${e}`);
}
if (!global.env) {
throw new Error('Environment variables were not properly bound to `global` by secure-env.');
}
export const operationalMode = global.env.OP_MODE || 'development';
export const port = global.env.PORT;
export const databaseHost = global.env.DB_HOST || 'localhost';
export const database = global.env.DB_NAME;
export const databaseUser = global.env.DB_USER;
export const databasePass = global.env.DB_PASS;
export const twilioAccountSid = global.env.TWILIO_SID;
export const twilioApiKey = global.env.TWILIO_API_KEY;
export const twilioApiSecret = global.env.TWILIO_API_SECRET;
export const awsAccessKey = global.env.AWS_ACCESS_KEY;
export const awsSecret = global.env.AWS_SECRET;
export const awsTopBucket = global.env.AWS_TOP_BUCKET;
export const expressSessionSecret = global.env.EXPRESS_SESSION_SECRET;
export const jwtAccessSecret = global.env.JWT_ACCESS_TOKEN_SECRET;
export const sysadminEmail = global.env.SYSADMIN_EMAIL;
export const loggingEmail = global.env.LOGGING_EMAIL;
To run project in Windows, first I installed packages with npm i. With secure-env, I have created .env.enc file from .env file. Then I excuted below commands
set ABC_ENV=manufacturing;
set NODE_ENV=development;
npm run dev
But by running above commands, it gives error like which is from app.config.ts :
Environment variables were not properly bound to `global` by secure-env.
I tried with nodemon app.js . But facing same error.
I don't know about node.js and so not able to understand what's happening and don't know what will require to run project on Windows.
Any suggestions to trace what's missing or what should I check will be helpful.
Please help and guide to resolve this. Thanks

Related

NodeJS + WebPack setting client static data

I have a NodeJS/React/WebPack application that I'm trying to take environment variables that are present at build time and export them as variables that are available to the client without having to request them with AJAX.
What is currently setup is a /browser/index.js file with a method that is exported however the variables are not getting expanded when webpack runs.
function applicationSetup()
{
const config = JSON.parse(process.env.CONFIG);
const APPLICATION_ID = process.env.APPLICATION_ID;
.........
}
During the build process we run node node_modules/webpack/bin/webpack.js --mode production with npm.
What do I need to do in order to expand the environment variable to be their actual values when webpack creates the .js file?
Edit 8/23
I've tried adding it in the webpack.DefinePlugin section of the webpack.config.js file however it's still doesn't seem to be available in the client side code. What am I missing?
Edit #2 (webpack.config.js)
const getClientConfig = (env, mode) => {
return {
plugins: [
new webpack.DefinePlugin({
__isBrowser__: 'false',
__Config__: process.env.CONFIG,
__ApplicationID__:process.env.APPLICATION_ID
})]
}
module.exports = (env, options) => {
const configs = [
getClientConfig(options.env, options.mode)
];
return configs;
};

Async .mjs works when calling directly, fails when called from another .mjs

I am currently working with the Ring-Client-API and am running into a small issue at the very end of my development. I succesfully created, tested, and ran my RingListener as an individual file, ie by executing RingListener.mjs. My goal is to now start the listener from another file location, and I am running into some issues trying to do that. I am more familiar with CommonJS so please feel free to point me in the right direction for ES6 stuff I am missing. I am running node 14.15.4
Code RingListener.mjs:
import {RingApi} from 'ring-client-api'
import * as dotenv from "dotenv";
dotenv.config({path: '../.env'});
import {readFile, writeFile} from 'fs'
import {promisify} from 'util'
import App from "../objects/GoogleHomeNotification.js";
export async function start() {
const {env} = process;
console.log("Test 1")
const ringApi = new RingApi({my credentials});
console.log("Test 2")
const allCameras = await ringApi.getCameras();
console.log("Test 3")
console.log("Found " + allCameras.length + " camera(s)")
ringApi.onRefreshTokenUpdated.subscribe(
async ({newRefreshToken, oldRefreshToken}) => {
console.log('Refresh Token Updated: ', newRefreshToken)
}
)
if (allCameras.length) {
console.log('Listening for motion and doorbell presses on your cameras.')
}
}
start();
Output for RingListener.mjs
Test 1
Test 2
Test 3
Found 1 camera(s).
Refresh Token Updated: {my token}
Now writing it to proper .env file
Listening for motion and doorbell presses on your cameras.
When I try to start it from my other file, I only reach Test 2.
Start.mjs
import {start} from './objects/RingListener.mjs'
start();
//await start(); //Returns the same results as just start()
Output for Start.mjs
Test 1
Test 2
When running it from another location it seems to get stuck at the first await statement, and I'm not sure why. Any help would be greatly appreciated. I am quite stumped because I am able to actually execute the function and I get the console log statements, but for some reason it keeps failing at the exact same spot with the await call when executed through another file. Is there something I am missing when calling an async function from another file?
Thank you!
EDIT: Thanks #JoshA for pointing me in the right direction for the filepath for dotenv.
The following code now hangs on the "Test 1 Test 2" when I try to import another js module.
import {start} from './objects/RingListener.mjs'
import {default as Webserver} from './app.js'
await start();
Output
Test 1
Test 2
But when I remove my import to the other class it runs perfectly, IE "Test 1, 2, 3, etc".
import {start} from './objects/RingListener.mjs'
//import {default as Webserver} from './app.js'
await start();
Output
Test 1
Test 2
Test 3
Found 1 camera(s).
Refresh Token Updated:
Now writing it to proper .env file
Listening for motion and doorbell presses on your cameras.
I'm not even using it yet and it still is causing it to hang. Eventually I am going to use Webserver.listen(); but the ./app.js just exports the express app.
EDIT: The app.js contains a bunch of variable initialization and express app configuration. Mapping to the different routes on the server. The goal is to remove the app.listen() in the app.js and move it to the Start.mjs and call it by Webserver.listen() from the import.
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var logger = require('morgan');
require('dotenv').config()
/* Variable def here */
var app = express();
// app config here
/* Exports */
module.exports = app;
app.listen(1337, () => {
console.log("Starting server on 1337");
})
I assume you are using dotenv to load your credentials from your .env file which you then pass on to the new RingApi({my credentials}) constructor.
If that's the case, the most likely reason it's failing is that dotenv uses fs.readFileSync to read the .env file which looks for files relative to the path where node js was executed and not relative to the path of the module. Which is why it stops working if you execute the app from the Start.mjs which is in a different path.
If you really want to keep the dotenv config call inside your RingListener.mjs file you can rewrite it to something like this which resolves the absolute path for the .env file.
import { resolve } from 'path';
dotenv.config({path: resolve(__dirname, '../.env')});
If you get an error __dirname is not defined this is because it's not available in ECMAScript modules as documented here.
As a workaround, you can do something like this.
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
// Initialize __dirname
const __dirname = dirname(fileURLToPath(import.meta.url));
// Then use it to resolve path for .env
dotenv.config({path: resolve(__dirname, '../.env')});

How can I ignore the actual dotenv (.env) file while running in jenkins

In cypress how can I ignore the dotenv (.env) file while running in jenkins, as jenkins run on actual environment variables. Now I am getting No such file or directory open/student-proj/.env
How can I check if it is running in jenkins then use actual env variables else use .env ?
plugins/index.js
const configEnv = require('dotenv').config();
const cucumber = require('cypress-cucumber-preprocessor').default;
module.exports = (on, config) => {
on('file:preprocessor', cucumber());
if (configEnv.error) {
throw configEnv.error;
}
const env = { ...config.env, ...configEnv.parsed };
const envData = { ...config, env };
return envData;
}
why not just use a jenkins env variable specific in your code e.g. JOB_NAME ... if the value returns null... that means its not running on jenkins

How to handle different .env files in Next?

What I want
I have created a new Next project, and I want to manage app behavior according to the NODE_ENV variable. The application must load different variables located in different .env files. eje. if I load NODE_ENV=development, the application should to load the variables located in .env.development file. What is the most efficient and safe way to do it in Next.
What I have
package.json
In the dev script I pass the environment type:
"scripts": {
"dev": "cross-env NODE_ENV=development next",
"build": "next build",
"start": "next start",
},
next.config.js
In the next configuration I load environment variables from correct .env file with dotenv library according to NODE_ENV variable pass in devscript in package.json.
const path = require('path');
const withOffline = require('next-offline');
const webpack = require('webpack');
require('dotenv').config({
path: path.resolve(
__dirname,
`.env.${process.env.NODE_ENV}`,
),
});
module.exports = withOffline({
webpack: (config) => {
// Returns environment variables as an object
const env = Object.keys(process.env).reduce((acc, curr) => {
acc[`process.env.${curr}`] = JSON.stringify(process.env[curr]);
return acc;
}, {});
// Allows you to create global constants which can be configured
// at compile time, which in our case is our environment variables
config.plugins.push(new webpack.DefinePlugin(env));
return config;
},
});
.env.development
TITLE=modo development
pages/index.js
function HomePage() {
return <div>{process.env.TITLE}</div>
}
export default HomePage
With this aproach...
This is the most efficient and safe way to handle diferent .env files in Next?
Nextjs supports env by default without the need to use of webpack.DefinePlugin, just pass it to the env property of next.config.js.
So your code will become:
// next.conf.js
const path = require('path');
const withOffline = require('next-offline');
const webpack = require('webpack');
require('dotenv').config({
path: path.resolve(
__dirname,
`.env.${process.env.NODE_ENV}`,
),
});
module.exports = withOffline({
env: {
VAR_1: process.env.VAR_1
...
// List all the variables that you want to expose to the client
}
});
PAY ATTENTION: these env variables may be exposed to the client side (if you use them in one of your app page).
For example, if your process.env is containing secrets, and by mistake you are using one of them in one of the pages / components that are used by pages, they will be inside js files that are downloaded to the client side.
Since Next.js 9.4, there is a built in .env loading functionality, read about it here https://nextjs.org/docs/basic-features/environment-variables
Agree with the #felixmosh that Nextjs supports env by default without the need to use of webpack.DefinePlugin, but...
It may be seen as limited and confused while loading different configurations on each environment. You can see common problem here
enter link description here
You can solve this problem easily by following these small steps.
You’ll need to create a folder config on the root, with all environment stages you’d like to have.
You can add the initial/common configuration in default.js like this.
API: {
API_URL: process.env.API_URL || '<http://localhost:4000>',
ENDPOINT: '********',
IS_MOCKING_ENABLED: false,
},
}```
Include above config files in the Next.config.js file by publicRuntimeConfig. If you’d like to have it just on the server-side use just serverRuntimeConfig like this.
const APIConfig = config.get('API')
const nextConfig = {
publicRuntimeConfig: {
APIConfig,
},
}
module.exports = nextConfig ```
Usage in any file.
const { publicRuntimeConfig } = getConfig()
const APIConfig = publicRuntimeConfig.APIConfig
[...] ```
Finally, in your package.json. You can inject the environment variables to load the appropriate configuration.
"start:local": "NODE_ENV=development run-p dev"
Reference: enter link description here for detail explanation.

Test process.env with Jest

I have an application that depends on environmental variables like:
const APP_PORT = process.env.APP_PORT || 8080;
And I would like to test that for example:
APP_PORT can be set by a Node.js environment variable.
or that an Express.js application is running on the port set with process.env.APP_PORT
How can I achieve this with Jest? Can I set these process.env variables before each test or should I mock it somehow maybe?
The way I did it can be found in this Stack Overflow question.
It is important to use resetModules before each test and then dynamically import the module inside the test:
describe('environmental variables', () => {
const OLD_ENV = process.env;
beforeEach(() => {
jest.resetModules() // Most important - it clears the cache
process.env = { ...OLD_ENV }; // Make a copy
});
afterAll(() => {
process.env = OLD_ENV; // Restore old environment
});
test('will receive process.env variables', () => {
// Set the variables
process.env.NODE_ENV = 'dev';
process.env.PROXY_PREFIX = '/new-prefix/';
process.env.API_URL = 'https://new-api.com/';
process.env.APP_PORT = '7080';
process.env.USE_PROXY = 'false';
const testedModule = require('../../config/env').default
// ... actual testing
});
});
If you look for a way to load environment values before running the Jest look for the answer below. You should use setupFiles for that.
Jest's setupFiles is the proper way to handle this, and you need not install dotenv, nor use an .env file at all, to make it work.
jest.config.js:
module.exports = {
setupFiles: ["<rootDir>/.jest/setEnvVars.js"]
};
.jest/setEnvVars.js:
process.env.MY_CUSTOM_TEST_ENV_VAR = 'foo'
That's it.
Another option is to add it to the jest.config.js file after the module.exports definition:
process.env = Object.assign(process.env, {
VAR_NAME: 'varValue',
VAR_NAME_2: 'varValue2'
});
This way it's not necessary to define the environment variables in each .spec file and they can be adjusted globally.
In ./package.json:
"jest": {
"setupFiles": [
"<rootDir>/jest/setEnvVars.js"
]
}
In ./jest/setEnvVars.js:
process.env.SOME_VAR = 'value';
You can use the setupFiles feature of the Jest configuration. As the documentation said that,
A list of paths to modules that run some code to configure or set up
the testing environment. Each setupFile will be run once per test
file. Since every test runs in its own environment, these scripts will
be executed in the testing environment immediately before executing
the test code itself.
npm install dotenv dotenv that uses to access environment variable.
Create your .env file to the root directory of your application and add this line into it:
#.env
APP_PORT=8080
Create your custom module file as its name being someModuleForTest.js and add this line into it:
// someModuleForTest.js
require("dotenv").config()
Update your jest.config.js file like this:
module.exports = {
setupFiles: ["./someModuleForTest"]
}
You can access an environment variable within all test blocks.
test("Some test name", () => {
expect(process.env.APP_PORT).toBe("8080")
})
Expanding a bit on Serhan C.'s answer...
According to the blog post How to Setup dotenv with Jest Testing - In-depth Explanation, you can include "dotenv/config" directly in setupFiles, without having to create and reference an external script that calls require("dotenv").config().
I.e., simply do
module.exports = {
setupFiles: ["dotenv/config"]
}
In test file:
const APP_PORT = process.env.APP_PORT || 8080;
In the test script of ./package.json:
"scripts": {
"test": "jest --setupFiles dotenv/config",
}
In ./env:
APP_PORT=8080
In my opinion, it's much cleaner and easier to understand if you extract the retrieval of environment variables into a utility (you probably want to include a check to fail fast if an environment variable is not set anyway), and then you can just mock the utility.
// util.js
exports.getEnv = (key) => {
const value = process.env[key];
if (value === undefined) {
throw new Error(`Missing required environment variable ${key}`);
}
return value;
};
// app.test.js
const util = require('./util');
jest.mock('./util');
util.getEnv.mockImplementation(key => `fake-${key}`);
test('test', () => {...});
Depending on how you can organize your code, another option can be to put the environment variable within a function that's executed at runtime.
In this file, the environment variable is set at import time and requires dynamic requires in order to test different environment variables (as described in this answer):
const env = process.env.MY_ENV_VAR;
const envMessage = () => `MY_ENV_VAR is set to ${env}!`;
export default myModule;
In this file, the environment variable is set at envMessage execution time, and you should be able to mutate process.env directly in your tests:
const envMessage = () => {
const env = process.env.MY_VAR;
return `MY_ENV_VAR is set to ${env}!`;
}
export default myModule;
Jest test:
const vals = [
'ONE',
'TWO',
'THREE',
];
vals.forEach((val) => {
it(`Returns the correct string for each ${val} value`, () => {
process.env.MY_VAR = val;
expect(envMessage()).toEqual(...
you can import this in your jest.config.js
require('dotenv').config()
this work for me
All the above methods work if you're using require("dotenv").config within the jest.config.js file, a NodeJS application without TypeScript such as what Jialx or Henry Tipantuna has suggested.
But if you're using ts-jest and within the jest.config.ts file.
import dotenv from "dotenv"
dotenv.config()
/* config options below */
When using Typescript the following works for me:
in root:
jest.config.js
/* eslint-disable #typescript-eslint/no-var-requires */
const { pathsToModuleNameMapper } = require('ts-jest');
const { compilerOptions } = require('./tsconfig.paths.json');
module.exports = {
// [...]
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' }),
};
process.env = Object.assign(process.env, {
env_name: 'dev',
another_var: 'abc123',
});
To build upon #HenryTipantuña's suggestion is to import dotenv in your jest.config.js and use a .env.test file in the config path
require('dotenv').config({
path: '.env.test'
})
Building on top of #jahller's answer.
I made it responsive so you don't need to keep the files in sync as things change.
Put this at the bottom of your jest.config.js file.
const arr = require('fs')
.readFileSync('.env', 'utf8')
.split('\n')
.reduce((vars, i) => {
const [variable, value] = i.split('=')
vars[variable] = value
return vars
}, {})
process.env = Object.assign(process.env, arr)
It reads the contents of your .env file, splits every new line and reduces it all back down to an object where you then assign it to process.env
OR
just use dotenv in jest.setup.js 🤷‍♂️
i have most simple for implementation env (specialy test.env)
require("dotenv").config({ path: './test.env' });
const { sum } = require('./sum.js');
describe('sum', () => {
beforeEach(() => {
jest.resetModules(); // remove cache
})
test('should success', () => {
expect(sum(1, 3)).toEqual(4);
})
})
I think you could try this too:
const currentEnv = process.env;
process.env = { ENV_NODE: 'whatever' };
// test code...
process.env = currentEnv;
This works for me and you don't need module things

Resources