diff --git a/lib/watch/index.js b/lib/watch/index.js index 81de0b2f..9a9e8bb9 100644 --- a/lib/watch/index.js +++ b/lib/watch/index.js @@ -3,6 +3,7 @@ const path = require('path') const cp = require('child_process') const chalk = require('chalk') +const { readFileSync, existsSync } = require('fs') const { arrayToRegExp, logWatchVerbose } = require('./utils') const { GRACEFUL_SHUT } = require('./constants.js') @@ -36,10 +37,34 @@ const watch = function (args, ignoreWatch, verboseWatch) { }) let readyEmitted = false + const envVarsToProtect = []; const run = (event) => { const childEvent = { childEvent: event } - const env = Object.assign({}, process.env, childEvent, require('dotenv').config().parsed) + + // in order to not override existing env vars, we have to load and parse + // dotenv without modifying process.env + let dotenvParsed = {}; + const dotenvPath = path.resolve(process.cwd(), '.env'); + if (existsSync(dotenvPath)) { + dotenvParsed = require('dotenv').parse(readFileSync(dotenvPath, { encoding: 'utf-8' })); + if (event == 'start') { + // analyse which env-vars should not be overwritten + Object.keys(dotenvParsed).forEach(function (key) { + if (Object.prototype.hasOwnProperty.call(process.env, key)) { + // if a env var already exists with a different value + // than in parsed dotenv, it has to be protected + if (process.env[key] != dotenvParsed[key]) { + envVarsToProtect.push(key); + } + } + }); + } + for (const envVarToProtect of envVarsToProtect) { + delete dotenvParsed[envVarToProtect]; + } + } + const env = Object.assign({}, process.env, dotenvParsed, childEvent); const _child = cp.fork(forkPath, args, { env, diff --git a/test/start.test.js b/test/start.test.js index 59299776..c8489f41 100644 --- a/test/start.test.js +++ b/test/start.test.js @@ -677,6 +677,38 @@ test('should read env variables from .env file', async (t) => { await fastify.close() }) +test('should read env variables from .env file and not override existing env vars in watch mode', async (t) => { + process.env.GREETING = 'planet' + const testdir = t.testdir({ + '.env': 'GREETING=world', + 'plugin.js': await readFile(path.join(__dirname, '../examples/plugin-with-env.js')) + }) + + const cwd = process.cwd() + + process.chdir(testdir) + + const port = getPort() + const argv = ['-p', port, '-w', path.join(testdir, 'plugin.js')] + const fastifyEmitter = await requireUncached('../start').start(argv) + + t.teardown(() => { + process.chdir(cwd) + }) + + await once(fastifyEmitter, 'ready') + + const r1 = await sget({ + method: 'GET', + url: `http://localhost:${port}` + }) + + t.equal(r1.response.statusCode, 200) + t.same(JSON.parse(r1.body), { hello: 'planet' }) + + await fastifyEmitter.stop() +}) + test('crash on unhandled rejection', t => { t.plan(1)