From 2b336292d86d7ad739762f8804843c634bcb6b44 Mon Sep 17 00:00:00 2001 From: Low Heok Hong Date: Fri, 8 May 2020 18:30:25 +0800 Subject: [PATCH] Use .d.ts as schema --- dotenv-extended.d.ts | 2 +- package-lock.json | 5 +++++ package.json | 3 ++- src/index.js | 5 ++++- src/utils/load-env-type-declaration.js | 25 +++++++++++++++++++++++++ test/EnvSchema.d.ts | 15 +++++++++++++++ test/test.spec.js | 16 ++++++++++++++++ 7 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 src/utils/load-env-type-declaration.js create mode 100644 test/EnvSchema.d.ts diff --git a/dotenv-extended.d.ts b/dotenv-extended.d.ts index 72d7072..d259840 100644 --- a/dotenv-extended.d.ts +++ b/dotenv-extended.d.ts @@ -4,7 +4,7 @@ * The result of a call to load() or parse() */ export interface IEnvironmentMap { - [name: string]: string; + [name: string]: string | undefined; } /** diff --git a/package-lock.json b/package-lock.json index e64aafd..f5780a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9497,6 +9497,11 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==" + }, "typpy": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/typpy/-/typpy-2.3.11.tgz", diff --git a/package.json b/package.json index 168fb73..5d59f4e 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "auto-parse": "^1.3.0", "camelcase": "^5.3.1", "cross-spawn": "^7.0.1", - "dotenv": "^8.2.0" + "dotenv": "^8.2.0", + "typescript": "^3.8.3" } } diff --git a/src/index.js b/src/index.js index d48bd29..2967ff5 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ import dotenv from 'dotenv'; import getConfigFromEnv from './utils/config-from-env'; import loadEnvironmentFile from './utils/load-environment-file'; +import loadEnvTypeDeclaration from './utils/load-env-type-declaration'; export const parse = dotenv.parse.bind(dotenv); export const config = options => { @@ -35,7 +36,9 @@ export const config = options => { const configKeys = Object.keys(config); if (options.errorOnMissing || options.errorOnExtra || options.errorOnRegex) { - const schema = loadEnvironmentFile(options.schema, options.encoding, options.silent); + const schema = options.schema.endsWith('.d.ts') ? + loadEnvTypeDeclaration(options.schema, options.encoding, options.silent) + : loadEnvironmentFile(options.schema, options.encoding, options.silent); const schemaKeys = Object.keys(schema); let missingKeys = schemaKeys.filter(function (key) { diff --git a/src/utils/load-env-type-declaration.js b/src/utils/load-env-type-declaration.js new file mode 100644 index 0000000..a52c5bd --- /dev/null +++ b/src/utils/load-env-type-declaration.js @@ -0,0 +1,25 @@ +import fs from 'fs'; +import ts from 'typescript'; + +export const loadEnvTypeDeclaration = (path, encoding, silent) => { + try { + const data = fs.readFileSync(path, encoding); + const source = ts.createSourceFile(path, data, ts.ScriptTarget.ES2015, true); + const schema = {}; + source.getChildren().find(c => c.kind === ts.SyntaxKind.SyntaxList).getChildren() + .find(c => c.kind === ts.SyntaxKind.ModuleDeclaration && c.name.text === 'dotenv-extended').body.getChildren() + .find(c => c.kind === ts.SyntaxKind.SyntaxList).getChildren() + .find(c => c.kind === ts.SyntaxKind.InterfaceDeclaration && c.name.escapedText === 'IEnvironmentMap').getChildren() + .find(c => c.kind === ts.SyntaxKind.SyntaxList).getChildren() + // Only accepts members of type "string" + .filter(c => c.type.kind === ts.SyntaxKind.StringKeyword) + .forEach(c => schema[c.name.escapedText] = ''); + return schema; + } catch (err) { + if (!silent) { + console.error(err.message); + } + return {}; + } +}; +export default loadEnvTypeDeclaration; diff --git a/test/EnvSchema.d.ts b/test/EnvSchema.d.ts new file mode 100644 index 0000000..bea1e03 --- /dev/null +++ b/test/EnvSchema.d.ts @@ -0,0 +1,15 @@ +import { IEnvironmentMap } from "dotenv-extended"; + +declare module "dotenv-extended" { + interface IEnvironmentMap { + TEST_ONE: string; + TEST_TWO: string; + TEST_THREE: string; + } +} + +declare global{ + namespace NodeJS { + interface ProcessEnv extends IEnvironmentMap {} + } +} diff --git a/test/test.spec.js b/test/test.spec.js index 6b158ac..dfb5034 100644 --- a/test/test.spec.js +++ b/test/test.spec.js @@ -167,6 +167,22 @@ describe('dotenv-extended tests', () => { dotenvex.load({silent: false}); expect(console.error).to.have.been.calledOnce; }); + + it('Should load .d.ts schema, defaults and env into correct values in process.env and returned object', function () { + const config = dotenvex.load({ + schema: 'EnvSchema.d.ts', + defaults: '.env.defaults.example', + path: '.env.override', + errorOnExtra: true, + errorOnMissing: true + }); + expect(config.TEST_ONE).to.equal('one overridden'); + expect(config.TEST_TWO).to.equal('two'); + expect(config.TEST_THREE).to.equal('three'); + expect(process.env.TEST_ONE).to.equal('one overridden'); + expect(process.env.TEST_TWO).to.equal('two'); + expect(process.env.TEST_THREE).to.equal('three'); + }); }); describe('Supporting libraries tests', () => {