How to isolate environment variables from imported modules in Node.js applications?

I’m working on a Node.js project where I have multiple JavaScript files that serve dual purposes. Each file can run independently as a test suite and also exports objects for use in other files.

I want to pass configuration parameters when executing files like this:
npm run test-suite --config=banner-test

This allows me to either run all tests with npm run test-suite or target specific tests using npm run test-suite --config=specific-test.

My main file looks like this:

// testRunner.js
const dashboardTests = require('./dashboard/dashboardTests.js');
const userTests = require('./users/userTests.js');
const testFiles = {...dashboardTests.testFiles, ...userTests.testFiles};
let testSpecs = Object.values(testFiles);

if (process.env.npm_config_config) {
    const selectedTest = testFiles[process.env.npm_config_config];
    if (selectedTest) {
        testSpecs = selectedTest;
    }
    else {
        console.error(`Configuration "${process.env.npm_config_config}" not found.`);
        process.exit(1);
    }
}
// execute tests

And my sub-modules have similar logic:

// dashboardTests.js
const testFiles = {
   "banner-test":"./bannerTest.spec.js",
   "sidebar-test":"./sidebarTest.spec.js",
};
let testSpecs = Object.values(testFiles);

if (process.env.npm_config_config) {
    const selectedTest = testFiles[process.env.npm_config_config];
    if (selectedTest) {
        testSpecs = selectedTest;
    }
    else {
        console.error(`Configuration "${process.env.npm_config_config}" not found.`);
        process.exit(1);
    }
}
// run tests
module.exports = {testFiles};

The problem is when I run npm run test-suite --config=banner-test, the environment variable gets passed to all required files, including userTests.js. Since “banner-test” doesn’t exist in userTests.js, it hits the error condition and exits.

How can I import just the exported objects from these files without executing their internal logic that depends on environment variables?

You could also wrap your env logic in an IIFE that only runs when needed. Skip executing the config code at module load - use lazy evaluation so it only runs when called. if (require.main === module) { /* your env logic here */ } works, but I’d rather keep exports clean and move execution into separate functions that run conditionally.

You need to split your modules - separate the executable logic from what you’re exporting. The problem is your modules are running configuration-dependent code at import time, which happens whether you’re running the file directly or importing it. Move all the environment variable processing and test execution logic into a separate function that only runs when the file is executed directly. Use require.main === module to detect this. Here’s how I’d fix your dashboardTests.js:

const testFiles = {
   "banner-test":"./bannerTest.spec.js",
   "sidebar-test":"./sidebarTest.spec.js",
};

function runTests() {
    let testSpecs = Object.values(testFiles);
    
    if (process.env.npm_config_config) {
        const selectedTest = testFiles[process.env.npm_config_config];
        if (selectedTest) {
            testSpecs = selectedTest;
        } else {
            console.error(`Configuration "${process.env.npm_config_config}" not found.`);
            process.exit(1);
        }
    }
    // run tests here
}

if (require.main === module) {
    runTests();
}

module.exports = {testFiles};

Now when you import the module, you only get the testFiles object without any side effects from environment variable processing.

You can also restructure your module exports to avoid running any logic during import. Don’t process environment variables at the module level - create a factory function that handles configuration only when you call it explicitly. Here’s how your dashboardTests.js would look:

const testFiles = {
   "banner-test":"./bannerTest.spec.js",
   "sidebar-test":"./sidebarTest.spec.js",
};

function createTestRunner(config) {
    let testSpecs = Object.values(testFiles);
    
    if (config) {
        const selectedTest = testFiles[config];
        if (selectedTest) {
            testSpecs = selectedTest;
        } else {
            throw new Error(`Configuration "${config}" not found.`);
        }
    }
    return testSpecs;
}

module.exports = { testFiles, createTestRunner };

Now your main testRunner.js just calls createTestRunner(process.env.npm_config_config) for modules that need specific configuration. The imported modules stay completely passive until you explicitly invoke their functionality.