From c0501fc4e069d860f093bba9ad18f7b50fd81c41 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 5 Aug 2023 18:26:12 +0100 Subject: [PATCH] Server: Removed obsolete node-env-file dependency --- packages/server/package.json | 1 - packages/server/src/app.ts | 11 +- .../server/src/utils/testing/testRouters.ts | 1 - packages/utils/env.ts | 133 ++++++++++++++++++ packages/utils/package.json | 1 + yarn.lock | 8 -- 6 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 packages/utils/env.ts diff --git a/packages/server/package.json b/packages/server/package.json index f0ddec36f..2c184f96a 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -43,7 +43,6 @@ "mustache": "4.2.0", "nanoid": "2.1.11", "node-cron": "3.0.2", - "node-env-file": "0.1.8", "nodemailer": "6.9.3", "nodemon": "2.0.22", "pg": "8.11.1", diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 5e1bc2c65..85db9c390 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -22,6 +22,7 @@ import newModelFactory from './models/factory'; import setupCommands from './utils/setupCommands'; import { RouteResponseFormat, routeResponseFormat } from './utils/routeUtils'; import { parseEnv } from './env'; +import { parseEnvFile } from '@joplin/utils/env'; import storageConnectionCheck from './utils/storageConnectionCheck'; import { setLocale } from '@joplin/lib/locale'; import initLib from '@joplin/lib/initLib'; @@ -35,7 +36,6 @@ interface Argv { const nodeSqlite = require('sqlite3'); const cors = require('@koa/cors'); -const nodeEnvFile = require('node-env-file'); const { shimInit } = require('@joplin/lib/shim-init-node.js'); shimInit({ nodeSqlite }); @@ -99,11 +99,16 @@ async function main() { const envFilePath = await getEnvFilePath(env, argv); - if (envFilePath) nodeEnvFile(envFilePath); + const envFromFile = envFilePath ? parseEnvFile(envFilePath) : {}; + + const fullEnv = { + ...envFromFile, + ...process.env, + }; if (!defaultEnvVariables[env]) throw new Error(`Invalid env: ${env}`); - const envVariables = parseEnv(process.env, defaultEnvVariables[env]); + const envVariables = parseEnv(fullEnv, defaultEnvVariables[env]); const app = new Koa(); diff --git a/packages/server/src/utils/testing/testRouters.ts b/packages/server/src/utils/testing/testRouters.ts index 8cee2841d..38029550c 100644 --- a/packages/server/src/utils/testing/testRouters.ts +++ b/packages/server/src/utils/testing/testRouters.ts @@ -125,7 +125,6 @@ async function main() { serverProcess = spawn('node', serverCommandParams, { detached: true, stdio: 'inherit', - // env: { ...process.env, NODE_ENV: 'testing' }, }); const cleanUp = () => { diff --git a/packages/utils/env.ts b/packages/utils/env.ts new file mode 100644 index 000000000..5a45d79cb --- /dev/null +++ b/packages/utils/env.ts @@ -0,0 +1,133 @@ +// Based on https://github.com/grimen/node-env-file with various improvements to +// remove global state and conversion to TypeScript. + +import { existsSync, readFileSync } from 'fs-extra'; +import { resolve } from 'path'; + +export interface Options { + verbose?: boolean; + overwrite?: boolean; + raise?: boolean; + logger?: typeof console; +} + +export const parseEnvFile = (env_file: string, options: Options = {}) => { + options = { + logger: console, + overwrite: false, + raise: true, + verbose: false, + ...options, + }; + + if (typeof env_file !== 'string') { + if (options.raise) { + throw new TypeError(`Environment file argument is not a valid \`String\`: ${env_file}`); + } else { + if (options.verbose && options.logger) { + options.logger.error('[ENV]: ERROR Environment file argument is not a valid `String`:', env_file); + } + return {}; + } + } + + try { + env_file = resolve(env_file); + } catch (error) { + if (options.raise) { + throw new TypeError(`Environment file path could not be resolved: ${error}`); + } else { + if (options.verbose && options.logger) { + options.logger.error('[ENV]: ERROR Environment file path could not be resolved:', env_file); + } + return {}; + } + } + + const data: Record> = {}; + data[env_file] = {}; + + if (options.verbose && options.logger) { + options.logger.info('[ENV]: Loading environment:', env_file); + } + + if (existsSync(env_file)) { + let lines: string[] = []; + + try { + lines = (readFileSync(env_file, 'utf8') || '') + .split(/\r?\n|\r/) + .filter((line) => { + return /\s*=\s*/i.test(line); + }) + .map((line) => { + return line.replace('exports ', ''); + }); + + } catch (error) { + if (options.raise) { + (error as any).message = `Environment file could not be read: ${env_file}: ${(error as any).message}`; + throw error; + } else { + if (options.verbose && options.logger) { + options.logger.error('[ENV]: ERROR Environment file could not be read:', env_file); + } + return {}; + } + } + + const lineComments: string[] = []; + const lineVariables: string[] = []; + + let is_comment = false; + + for (const line of lines) { + is_comment = /^\s*#/i.test(line); // ignore comment lines (starting with #). + + if (is_comment) { + lineComments.push(line); + + if (options.verbose && options.logger) { + options.logger.info('[ENV]: Ignored line:', line); + } + + } else { + lineVariables.push(line); + + const key_value = line.match(/^([^=]+)\s*=\s*(.*)$/) as any; + + const env_key = key_value[1]; + + // remove ' and " characters if right side of = is quoted + const env_value = key_value[2].match(/^(['"]?)([^\n]*)\1$/m)[2]; + + if (options.overwrite) { + data[env_file][env_key] = env_value; + + if (options.verbose && options.logger && data[env_file][env_key] !== env_value) { + options.logger.info('[ENV]: Overwritten ', data[env_file][env_key], ' => ', env_value); + } + + } else { + data[env_file][env_key] = process.env[env_key] || env_value; + } + + if (options.verbose && options.logger) { + options.logger.info('[ENV]:', data[env_file]); + } + } + } + } else { + if (options.raise) { + throw new TypeError(`Environment file doesn't exist: ${env_file}`); + + } else { + if (options.verbose && options.logger) { + options.logger.error('[ENV]: ERROR Environment file path could not be resolved:', env_file); + } + return {}; + } + } + + return data[env_file]; +}; diff --git a/packages/utils/package.json b/packages/utils/package.json index 97b1abec3..c33531319 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -7,6 +7,7 @@ ".": "./dist/index.js", "./net": "./dist/net.js", "./fs": "./dist/fs.js", + "./env": "./dist/env.js", "./Logger": "./dist/Logger.js" }, "publishConfig": { diff --git a/yarn.lock b/yarn.lock index 20c30abb6..ef7da566b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4915,7 +4915,6 @@ __metadata: mustache: 4.2.0 nanoid: 2.1.11 node-cron: 3.0.2 - node-env-file: 0.1.8 node-mocks-http: 1.12.2 nodemailer: 6.9.3 nodemon: 2.0.22 @@ -24585,13 +24584,6 @@ __metadata: languageName: node linkType: hard -"node-env-file@npm:0.1.8": - version: 0.1.8 - resolution: "node-env-file@npm:0.1.8" - checksum: 5346ea202075a3f9cda508d5abba450b60996625d53c40890a4a6d87919cca6ef47230af8d71899dbad0ae1c024a7a65f4ceacf2a300f70eb1d37f92366176e2 - languageName: node - linkType: hard - "node-fetch-npm@npm:^2.0.2": version: 2.0.4 resolution: "node-fetch-npm@npm:2.0.4"