Skip to content

Commit

Permalink
Merge pull request #35 from malinskibeniamin/add-optional-flag
Browse files Browse the repository at this point in the history
Allow optional environment variables to be defined
  • Loading branch information
benawad authored Mar 11, 2022
2 parents a5709e2 + 67bd04b commit bdc3806
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 8 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
node_modules
node_modules
test/.env.example
env.d.ts
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ npx gen-env-types path/to/.env
-h, --help Show usage information
-o, --types-output Output name/path for types file | defaults to `env.d.ts`
-e, --example-env-path Path to save .env.example file
-O, --optional [vars] Make some of the environment variables optional.
Accepts a list of environment variables to be made optional.
-r, --rename-example-env Custom name for .env example output file | defaults to `env.example` if omitted
-k, --keep-comments Keep comments/blank lines in .env example output file | defaults to false if omitted.
Not accepting the value. When specified, it will be true.
Expand Down
39 changes: 32 additions & 7 deletions gen-env-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const printHelp = (exitCode) => {
-h, --help Show usage information
-o, --types-output Output name/path for types file | defaults to \`env.d.ts\`
-e, --example-env-path Path to save .env.example file
-O, --optional [vars] Make some of the environment variables optional.
Accepts a list of environment variables to be made optional.
-r, --rename-example-env Custom name for .env example output file | defaults to \`env.example\` if omitted
-k, --keep-comments Keep comments/blank lines in .env example output file | defaults to false if omitted.
Not accepting the value. When specified, it will be true.
Expand All @@ -45,6 +47,7 @@ const parseArgs = (args) => {
typesOutput: "env.d.ts",
exampleEnvOutput: ".env.example",
keepComments: false,
listOfOptionalVariables: []
};

while (args.length > 0) {
Expand All @@ -61,6 +64,16 @@ const parseArgs = (args) => {
case "--version":
cliConfig.version = true;
break;
case "-O":
case "--optional":
const listOfOptionalVariables = args.shift();
if (!listOfOptionalVariables) {
showError(
"Expected a list of optional variables, bad input: " + listOfOptionalVariables
);
}
cliConfig.listOfOptionalVariables = listOfOptionalVariables;
break;
case "-o":
case "--types-output":
const typesOutput = args.shift();
Expand Down Expand Up @@ -117,10 +130,10 @@ if (!cliConfig.envPath) {
printHelp(1);
}
if (cliConfig.help) {
return printHelp(0);
printHelp(0);
}
if (cliConfig.version) {
return printVersion();
printVersion();
}

const envString = readFileSync(cliConfig.envPath, {
Expand All @@ -141,16 +154,17 @@ function writeEnvTypes(path) {
interface ProcessEnv {
${filteredEnvString
.map(({key}, i) => {
const isKeyOptional = cliConfig.listOfOptionalVariables.length > 0 && cliConfig.listOfOptionalVariables.includes(key);
if (!existingModuleDeclaration) {
return `${i ? " " : ""}${key}: string;`;
return `${i ? " " : ""}${key}${isKeyOptional ? '?' : ''}: string;`;
}
const existingPropertySignature = existingModuleDeclaration
.split("\n")
.find((line) => line.includes(`${key}:`));
.find((line) => line.includes(`${key}:`) || line.includes(`${key}?:`));
if (!existingPropertySignature) {
return `${i ? " " : ""}${key}: string;`;
return `${i ? " " : ""}${key}${isKeyOptional ? '?' : ''}: string;`;
}
return `${i ? " " : ""}${existingPropertySignature.trim()}`;
Expand All @@ -166,10 +180,12 @@ export {}
writeFileSync(path, moduleDeclaration);

console.log("Wrote env types to: ", path);

return moduleDeclaration;
}

function writeExampleEnv(parsedExistingEnvString, path, isNew) {
const out = (cliConfig.keepComments? parsedEnvString: filteredEnvString)
const out = (cliConfig.keepComments ? parsedEnvString: filteredEnvString)
.map(({key, isEnvVar,value}) => {
if(isEnvVar) return `${key}=`;
// Comment or blank value
Expand Down Expand Up @@ -199,8 +215,17 @@ if (cliConfig.exampleEnvPath) {
readFileSync(outputExampleEnvPath, { encoding: "utf-8" })
);

return writeExampleEnv(parsedExistingEnvString, outputExampleEnvPath);
writeExampleEnv(parsedExistingEnvString, outputExampleEnvPath);
}

writeExampleEnv(filteredEnvString, outputExampleEnvPath, true);
}

module.exports = {
/**
* @description Writes environment types to a file
* @param {string} path
* @returns the content of the created file
*/
writeEnvTypes
}
20 changes: 20 additions & 0 deletions gen-env-types.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const { writeEnvTypes } = require('./gen-env-types');

describe('Environment variable types generator', function () {
it('should accept a list of optional variables', function () {
const result = writeEnvTypes('env.d.ts');
expect(result).toMatchInlineSnapshot(`
"declare global {
namespace NodeJS {
interface ProcessEnv {
OPTIONAL_SECRET?: string;
REQUIRED_SECRET: string;
}
}
}
export {}
"
`);
});
});
3 changes: 3 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
setupFiles: ['./testSetup'],
};
2 changes: 2 additions & 0 deletions test/.env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
OPTIONAL_SECRET=test_optional
REQUIRED_SECRET=test_required
1 change: 1 addition & 0 deletions testSetup.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit bdc3806

Please sign in to comment.