typescript-scaffolder
Version:
 
132 lines (131 loc) • 5.19 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateEnvLoader = generateEnvLoader;
const ts_morph_1 = require("ts-morph");
const fs = __importStar(require("fs"));
const dotenv_1 = __importDefault(require("dotenv"));
const file_system_1 = require("../utils/file-system");
const object_helpers_1 = require("../utils/object-helpers");
const logger_1 = require("../utils/logger");
const path_1 = __importDefault(require("path"));
dotenv_1.default.config();
/**
* Takes in an env file and produces a typescript-safe interface of a class and enum
*
* @param envFile
* @param outputDir
* @param outputFile
* @param envClassName
* @param envEnumName
*/
function generateEnvLoader(envFile, outputDir, outputFile, envClassName = 'EnvConfig', envEnumName = 'EnvKeys') {
const funcName = "generateEnvLoader";
logger_1.Logger.debug(funcName, 'Generating env accessor');
// need to make sure the new codegen root exists
(0, file_system_1.ensureDir)(outputDir);
const baseEnvFile = path_1.default.basename(envFile);
if (!/^\.env(\..+)?$/.test(baseEnvFile) && baseEnvFile !== '.env') {
const error = `Expected an .env* file but received: ${envFile}`;
logger_1.Logger.error(funcName, error);
throw new Error(funcName);
}
if (!fs.existsSync(envFile)) {
const error = `ENV file does not exist at path: ${envFile}`;
logger_1.Logger.error(funcName, error);
throw new Error(funcName);
}
const envLines = fs.readFileSync(envFile, 'utf-8')
.split('\n')
.map(line => line.trim())
.filter(line => !!line && !line.startsWith('#') && line.includes('='));
const envVars = envLines.map(line => {
const [key, rawVal] = line.split('=');
const cleanKey = key.trim();
const value = rawVal?.trim() ?? '';
const inferred = (0, object_helpers_1.inferPrimitiveType)(value);
// test proper key naming
const keyPattern = /^[A-Z_][A-Z0-9_]*$/;
if (!keyPattern.test(cleanKey)) {
const error = `Invalid env key format: ${cleanKey}`;
logger_1.Logger.error(funcName, error);
throw new Error(funcName);
}
// warn on empty values
if (value === '') {
logger_1.Logger.warn(funcName, `Empty value detected for env key: ${cleanKey}`);
}
return { key: cleanKey, value, inferred };
});
// Test min var count
if (envVars.length < 2) {
logger_1.Logger.warn('generateEnvLoader', `Only ${envVars.length} environment variables found.`);
}
// test duplicate keys
const seenKeys = new Set();
for (const { key } of envVars) {
if (seenKeys.has(key)) {
logger_1.Logger.error(funcName, `Duplicate env key detected: ${key}`);
throw new Error(funcName);
}
seenKeys.add(key);
}
const project = new ts_morph_1.Project();
const sourceFile = project.createSourceFile(`${outputDir}/${outputFile}`, '', { overwrite: true });
sourceFile.addClass({
name: envClassName,
isExported: true,
properties: envVars.map(({ key, value, inferred }) => ({
name: key,
isStatic: true,
isReadonly: true,
initializer: `process.env.${key} ?? ${inferred === 'string' ? `'${value}'` : value}`,
type: inferred
})),
});
// Optionally generate an enum of the keys
sourceFile.addEnum({
name: envEnumName,
isExported: true,
members: envVars.map(({ key }) => ({
name: key,
value: key
}))
});
sourceFile.saveSync();
}