diff --git a/README.md b/README.md index 6eb8efb..96f5c7a 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,9 @@ pnpm build # Filter by partial match (case-insensitive) ./dist/cli.js --filter API +./dist/cli.js -f API ./dist/cli.js --filter database +./dist/cli.js -f database # Display help ./dist/cli.js --help diff --git a/src/cli/config.ts b/src/cli/config.ts index b1a95b1..4f43ea2 100644 --- a/src/cli/config.ts +++ b/src/cli/config.ts @@ -30,7 +30,7 @@ function formatHelpText(): string { 'Options:', ' -h, --help display help for command', ' -v, --version display version number', - ' --filter TEXT filter variables containing TEXT (case-insensitive)', + ' -f, --filter TEXT filter variables containing TEXT (case-insensitive)', '', CLI_MESSAGES.HELP_EXAMPLES, CLI_MESSAGES.HELP_DESCRIPTION, diff --git a/src/constants/cli.ts b/src/constants/cli.ts index 22d7463..c7965f3 100644 --- a/src/constants/cli.ts +++ b/src/constants/cli.ts @@ -5,6 +5,7 @@ export const OPTION_ALIASES: Record = { h: 'help', v: 'version', V: 'version', + f: 'filter', }; /** @@ -18,7 +19,7 @@ export const EXIT_CODES = { SUCCESS: 0, SYSTEM_ERROR: 1, DATA_NOT_FOUND: 2, - INVALID_ARGUMENT: 2, // UNIX convention: invalid argument uses exit code 2 + INVALID_ARGUMENT: 2, } as const; export type ExitCode = (typeof EXIT_CODES)[keyof typeof EXIT_CODES]; diff --git a/src/core/filter.ts b/src/core/filter.ts index 9f2dd23..6c7ca73 100644 --- a/src/core/filter.ts +++ b/src/core/filter.ts @@ -1,30 +1,44 @@ import type { EnvironmentData, FilterConfig, FilterResult } from '../types/environment.js'; import { ERROR_MESSAGES, CLI_MESSAGES } from '../constants/index.js'; +/** + * Creates a filter function that matches environment variables by type and search text + * @param type - The type of filter ('prefix' or 'partial') + * @param searchText - The text to match (case-insensitive) + * @returns A filter function that checks if a variable matches the criteria + */ +export function createFilter( + type: 'prefix' | 'partial', + searchText: string, +): (env: EnvironmentData) => boolean { + const lowerSearchText = searchText.toLowerCase(); + + return (env: EnvironmentData): boolean => { + const lowerKey = env.key.toLowerCase(); + return type === 'prefix' + ? lowerKey.startsWith(lowerSearchText) + : lowerKey.includes(lowerSearchText); + }; +} + /** * Creates a filter function that matches environment variables by prefix * @param prefix - The prefix to match (case-insensitive) * @returns A filter function that checks if a variable starts with the prefix + * @deprecated Use createFilter('prefix', prefix) instead */ export function createPrefixFilter(prefix: string): (env: EnvironmentData) => boolean { - const lowerPrefix = prefix.toLowerCase(); - - return (env: EnvironmentData): boolean => { - return env.key.toLowerCase().startsWith(lowerPrefix); - }; + return createFilter('prefix', prefix); } /** * Creates a filter function that matches environment variables by partial match * @param searchText - The text to search for (case-insensitive) * @returns A filter function that checks if a variable contains the search text + * @deprecated Use createFilter('partial', searchText) instead */ export function createPartialMatchFilter(searchText: string): (env: EnvironmentData) => boolean { - const lowerSearchText = searchText.toLowerCase(); - - return (env: EnvironmentData): boolean => { - return env.key.toLowerCase().includes(lowerSearchText); - }; + return createFilter('partial', searchText); } /** @@ -50,14 +64,7 @@ export function filterEnvironmentVariables( } // Apply appropriate filter based on type - let filter: (env: EnvironmentData) => boolean; - - if (config.type === 'prefix') { - filter = createPrefixFilter(config.value); - } else { - // config.type === 'partial' - filter = createPartialMatchFilter(config.value); - } + const filter = createFilter(config.type, config.value); const filtered = data.filter(filter); diff --git a/tests/unit/cli/parser.test.ts b/tests/unit/cli/parser.test.ts index 82ebd0f..77d5bc9 100644 --- a/tests/unit/cli/parser.test.ts +++ b/tests/unit/cli/parser.test.ts @@ -241,6 +241,37 @@ describe('CLI Parser', () => { expect(result.data.arguments).toEqual(['PREFIX']); } }); + + it('should parse -f option with value', () => { + const result = parseArgs(['node', 'cli.js', '-f', 'API'], mockConfig); + + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.command).toBe('main'); + expect(result.data.flags.has('filter')).toBe(true); + expect(result.data.filterValue).toBe('API'); + expect(result.data.arguments).toEqual([]); + } + }); + + it('should handle -f without value', () => { + const result = parseArgs(['node', 'cli.js', '-f'], mockConfig); + + expect(result.success).toBe(false); + if (!result.success) { + expect(result.error.type).toBe('filter_requires_value'); + expect(result.error.message).toBe('--filter option requires a search text'); + } + }); + + it('should trim whitespace from -f value', () => { + const result = parseArgs(['node', 'cli.js', '-f', ' API '], mockConfig); + + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.filterValue).toBe('API'); + } + }); }); describe('isValidOption', () => {