diff --git a/.changeset/use-getenv-package.md b/.changeset/use-getenv-package.md new file mode 100644 index 0000000..79097d8 --- /dev/null +++ b/.changeset/use-getenv-package.md @@ -0,0 +1,5 @@ +--- +'lino-arguments': patch +--- + +Replace custom getenv implementation with official npm package. Now uses the robust `getenv` package for type casting and validation, enhanced with case-insensitive lookup across multiple naming conventions. Also enforces stricter no-unused-vars linting with catch { } syntax. diff --git a/README.md b/README.md index de45320..f360444 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,8 @@ const config = makeConfig({ Smart environment variable lookup with type preservation and case conversion. +**Built on [getenv](https://www.npmjs.com/package/getenv):** This function uses the official `getenv` npm package internally for robust type casting and validation, enhanced with case-insensitive lookup across multiple naming conventions. + **Parameters:** - `name` (string): Environment variable name (any case format) @@ -125,10 +127,10 @@ getenv('apiKey', ''); // camelCase getenv('api-key', ''); // kebab-case getenv('api_key', ''); // snake_case -// Type preservation: -getenv('PORT', 3000); // Returns number -getenv('DEBUG', false); // Returns boolean -getenv('API_KEY', ''); // Returns string +// Type preservation (powered by getenv package): +getenv('PORT', 3000); // Returns number (uses getenv.int()) +getenv('DEBUG', false); // Returns boolean (uses getenv.boolish()) +getenv('API_KEY', ''); // Returns string (uses getenv.string()) ``` ### Case Conversion Utilities @@ -351,6 +353,7 @@ npm run changeset:status - [links-notation](https://github.com/link-foundation/links-notation) - Links Notation parser - [lino-env](https://github.com/link-foundation/lino-env) - .lenv file operations - [test-anywhere](https://github.com/link-foundation/test-anywhere) - Universal JavaScript testing +- [getenv](https://www.npmjs.com/package/getenv) - Environment variable helper with type casting (used internally) ## License diff --git a/eslint.config.js b/eslint.config.js index 2dfc5df..86fd938 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -30,14 +30,7 @@ export default [ 'prettier/prettier': 'error', // Code quality rules - 'no-unused-vars': [ - 'error', - { - argsIgnorePattern: '^_', - varsIgnorePattern: '^_', - caughtErrorsIgnorePattern: '^_', - }, - ], + 'no-unused-vars': 'error', // No exceptions - use catch { } for ignored errors 'no-console': 'off', // Allow console in this project 'no-debugger': 'error', diff --git a/package-lock.json b/package-lock.json index 3106cd1..5d2c6b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.2.0", "license": "Unlicense", "dependencies": { + "getenv": "^2.0.0", "links-notation": "^0.11.2", "lino-env": "^0.2.6", "yargs": "^17.7.2" @@ -1609,6 +1610,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/getenv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", + "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", diff --git a/package.json b/package.json index 9856724..6f20823 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "node": ">=20.0.0" }, "dependencies": { + "getenv": "^2.0.0", "links-notation": "^0.11.2", "lino-env": "^0.2.6", "yargs": "^17.7.2" diff --git a/scripts/format-release-notes.mjs b/scripts/format-release-notes.mjs index 6ca758b..d06f929 100644 --- a/scripts/format-release-notes.mjs +++ b/scripts/format-release-notes.mjs @@ -79,7 +79,7 @@ try { if (relevantPr) { prNumber = relevantPr.number; } - } catch (_error) { + } catch { console.log('⚠️ Could not find PR for commit', commitHash); } diff --git a/src/index.js b/src/index.js index 9537512..4bb41fb 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,7 @@ import { Parser } from 'links-notation'; import { LinoEnv } from 'lino-env'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; +import baseGetenv from 'getenv'; /** * lino-arguments - A unified configuration library @@ -108,7 +109,8 @@ export function toPascalCase(str) { /** * Get environment variable with default value and case conversion - * Tries multiple case formats to find the variable + * Uses the official getenv npm package internally, with enhanced case-insensitive lookup. + * Tries multiple case formats to find the variable. * * @param {string} key - Variable name (any case format) * @param {string|number|boolean} [defaultValue=''] - Default value if not found @@ -130,29 +132,30 @@ export function getenv(key, defaultValue = '') { toPascalCase(key), // PascalCase ]; + // Try to find the variable using any case variant for (const variant of variants) { if (process.env[variant] !== undefined) { - const value = process.env[variant]; - - // If default is a number, try to parse the env value as a number - if (typeof defaultValue === 'number') { - const parsed = Number(value); - return isNaN(parsed) ? defaultValue : parsed; - } - - // If default is a boolean, try to parse the env value as a boolean - if (typeof defaultValue === 'boolean') { - if (value.toLowerCase() === 'true') { - return true; + // Use the official getenv package based on the type of defaultValue + try { + if (typeof defaultValue === 'number') { + // Use getenv.int or getenv.float + const isFloat = !Number.isInteger(defaultValue); + return isFloat + ? baseGetenv.float(variant, defaultValue) + : baseGetenv.int(variant, defaultValue); } - if (value.toLowerCase() === 'false') { - return false; + + if (typeof defaultValue === 'boolean') { + // Use getenv.boolish for flexible boolean parsing + return baseGetenv.boolish(variant, defaultValue); } + + // Otherwise use getenv.string + return baseGetenv.string(variant, defaultValue); + } catch { + // If getenv throws, return the default value return defaultValue; } - - // Otherwise return as string - return value; } } @@ -174,7 +177,7 @@ function loadLinoEnv(filePath = '.lenv') { const env = new LinoEnv(filePath); env.read(); return env; - } catch (_error) { + } catch { return null; } } @@ -247,7 +250,7 @@ async function loadDotenvx(options = {}) { try { const dotenvx = await import('@dotenvx/dotenvx'); return dotenvx.config({ ...options, quiet: true }); - } catch (_error) { + } catch { if (!quiet) { console.error('⚠️ dotenvx not installed, skipping .env loading'); } @@ -344,7 +347,7 @@ export function makeConfig(config = {}) { let initialParsed; try { initialParsed = initialYargs.parseSync(); - } catch (_error) { + } catch { initialParsed = {}; } @@ -419,7 +422,7 @@ export function parseLinoArguments(linoString) { return args.filter( (arg) => arg && arg.trim() && !arg.trim().startsWith('#') ); - } catch (_error) { + } catch { return linoString .split('\n') .map((line) => line.trim())