initial (real) commit
This commit is contained in:
		| @@ -1,5 +1,5 @@ | ||||
| { | ||||
|   "name": "ts-module-default", | ||||
|   "name": "@itsericwoodward/utils", | ||||
|   "version": "0.1.0", | ||||
|   "description": "", | ||||
|   "main": "./lib/cjs/index.js", | ||||
| @@ -24,12 +24,12 @@ | ||||
|   "license": "MIT", | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "https://git.itsericwoodward.com/eric/ts-npm-module-default.git" | ||||
|     "url": "https://git.itsericwoodward.com/eric/utils.git" | ||||
|   }, | ||||
|   "bugs": { | ||||
|     "url": "https://git.itsericwoodward.com/eric/ts-npm-module-default/issues" | ||||
|     "url": "https://git.itsericwoodward.com/eric/utils/issues" | ||||
|   }, | ||||
|   "homepage": "https://git.itsericwoodward.com/eric/ts-npm-module-default#readme", | ||||
|   "homepage": "https://git.itsericwoodward.com/eric/utils#readme", | ||||
|   "keywords": [], | ||||
|   "devDependencies": { | ||||
|     "@tsconfig/node20": "^20.1.2", | ||||
|   | ||||
							
								
								
									
										2
									
								
								src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| export * from "./utils.js"; | ||||
| export { default as loadConfig } from "./loadConfig.js"; | ||||
							
								
								
									
										50
									
								
								src/loadConfig.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/loadConfig.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| import path from "path"; | ||||
|  | ||||
| import { | ||||
| 	convertCamelToUpperSnakeCase, | ||||
| 	getDirname, | ||||
| 	readJsonIfExists, | ||||
| } from "./utils"; | ||||
|  | ||||
| export default <T>( | ||||
| 	opts: Partial<T>, | ||||
| 	envKey: string, | ||||
| 	localDefaultFilePath = "" | ||||
| ) => { | ||||
| 	const { env } = process, | ||||
| 		{ __dirname } = getDirname(), | ||||
| 		def = readJsonIfExists( | ||||
| 			path.join(__dirname, localDefaultFilePath || "./defaults.json5") | ||||
| 		), | ||||
| 		// gets value from ENV || options || defaults (in that order) | ||||
| 		getVal = (envName: keyof T) => { | ||||
| 			const snakeEnvName = `${envKey}_${convertCamelToUpperSnakeCase( | ||||
| 				envName as string | ||||
| 			)}`; | ||||
| 			if (env[snakeEnvName]) return env[snakeEnvName]; | ||||
| 			if (opts[envName]) return opts[envName]; | ||||
| 			return def[envName]; | ||||
| 		}, | ||||
| 		// gets array from ENV || options || defaults (in that order) | ||||
| 		getArray = (envName: keyof T, optName: keyof T | "" = "") => { | ||||
| 			let newEnvName; | ||||
| 			if (optName === "") { | ||||
| 				optName = envName; | ||||
| 				newEnvName = convertCamelToUpperSnakeCase(envName as string); | ||||
| 			} | ||||
| 			newEnvName = `${envKey}_${String(envName)}`; | ||||
| 			if (env[newEnvName]) | ||||
| 				return env?.[newEnvName as keyof typeof env]?.split(path.delimiter); | ||||
| 			if (Array.isArray(opts[optName]) && (opts[optName] as []).length) | ||||
| 				return opts[optName]; | ||||
| 			return def[optName]; | ||||
| 		}; | ||||
|  | ||||
| 	return { | ||||
| 		...Object.keys(def).reduce((acc: Partial<typeof def>, curr) => { | ||||
| 			if (Array.isArray(def[curr])) acc[curr] = getArray(curr as keyof T); | ||||
| 			else acc[curr] = getVal(curr as keyof T); | ||||
| 			return acc; | ||||
| 		}, {}), | ||||
| 	}; | ||||
| }; | ||||
							
								
								
									
										67
									
								
								src/utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/utils.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| import chalk from "chalk"; | ||||
| import fs from "fs"; | ||||
| import json5 from "json5"; | ||||
|  | ||||
| export interface ErrorWithCode { | ||||
| 	code: string; | ||||
| } | ||||
|  | ||||
| export const convertCamelToUpperSnakeCase = (str: string) => | ||||
| 	str.replace(/[A-Z]/g, (letter) => `_${letter}`).toUpperCase(); | ||||
|  | ||||
| /** | ||||
|  * From: https://www.webmanajemen.com/2022/05/use-import-meta-cjs.html | ||||
|  */ | ||||
| export const getDirname = () => { | ||||
| 	// get the stack | ||||
| 	const { stack } = new Error(); | ||||
| 	// get the third line (the original invoker) | ||||
| 	const invokeFileLine = (stack || "").split(`\n`)[2]; | ||||
| 	// match the file url from file://(.+.(ts|js)) and get the first capturing group | ||||
| 	const __filename = (invokeFileLine.match(/file:\/\/(.+.(ts|js))/) || | ||||
| 		[])[1].slice(1); | ||||
| 	// match the file URL from file://(.+)/ and get the first capturing group | ||||
| 	//     the (.+) is a greedy quantifier and will make the RegExp expand to the largest match | ||||
| 	const __dirname = (invokeFileLine.match(/file:\/\/(.+)\//) || [])[1].slice(1); | ||||
| 	return { __dirname, __filename }; | ||||
| }; | ||||
|  | ||||
| export const getTime = () => { | ||||
| 	const now = new Date(), | ||||
| 		tzo = -now.getTimezoneOffset(), | ||||
| 		dif = tzo >= 0 ? "+" : "-", | ||||
| 		pad = (num: number) => { | ||||
| 			const norm = Math.floor(Math.abs(num)); | ||||
| 			return `${norm < 10 ? "0" : ""}${norm}`; | ||||
| 		}; | ||||
| 	return [ | ||||
| 		now.getFullYear(), | ||||
| 		"-", | ||||
| 		pad(now.getMonth() + 1), | ||||
| 		"-", | ||||
| 		pad(now.getDate()), | ||||
| 		"T", | ||||
| 		pad(now.getHours()), | ||||
| 		":", | ||||
| 		pad(now.getMinutes()), | ||||
| 		":", | ||||
| 		pad(now.getSeconds()), | ||||
| 		dif, | ||||
| 		pad(tzo / 60), | ||||
| 		":", | ||||
| 		pad(tzo % 60), | ||||
| 	].join(""); | ||||
| }; | ||||
|  | ||||
| export const log = (...msg: unknown[]) => | ||||
| 	console.log(`${chalk.grey(`${getTime()}:`)} ${msg}`); | ||||
|  | ||||
| export const readJsonIfExists = (filePath: string | URL) => { | ||||
| 	try { | ||||
| 		return json5.parse(fs.readFileSync(filePath, { encoding: "utf8" })); | ||||
| 	} catch (err) { | ||||
| 		// if no file, return empty object | ||||
| 		if ((err as ErrorWithCode)?.code === "ENOENT") return {}; | ||||
| 		throw err; | ||||
| 	} | ||||
| }; | ||||
| @@ -2,7 +2,7 @@ | ||||
|     "extends": "./tsconfig.json", | ||||
|     "compilerOptions": { | ||||
|       "module": "CommonJS", | ||||
|       "moduleResolution": "Classic", | ||||
|       "moduleResolution": "Node10", | ||||
|       "outDir": "./lib/cjs" | ||||
|     }, | ||||
|   } | ||||
| @@ -1,5 +1,5 @@ | ||||
| { | ||||
|   "extends": "@tsconfig/node18/tsconfig.json", | ||||
|   "extends": "@tsconfig/node20/tsconfig.json", | ||||
|   "compilerOptions": { | ||||
|     /* Visit https://aka.ms/tsconfig to read more about this file */ | ||||
|  | ||||
| @@ -9,7 +9,7 @@ | ||||
|  | ||||
|     // values from https://github.com/tsconfig/bases#centralized-recommendations-for-tsconfig-bases | ||||
|     "module": "Node16", | ||||
|     "moduleResolution": "node16", | ||||
|     "moduleResolution": "Node16", | ||||
|  | ||||
|     "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ | ||||
|     "declarationMap": true,                           /* Create sourcemaps for d.ts files. */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user