( filePath: string | object | Function | undefined | null, context?: 'assertion' | 'general' | 'vars', )
| 73 | * @throws {Error} If the specified file does not exist. |
| 74 | */ |
| 75 | export function maybeLoadFromExternalFile( |
| 76 | filePath: string | object | Function | undefined | null, |
| 77 | context?: 'assertion' | 'general' | 'vars', |
| 78 | ) { |
| 79 | if (Array.isArray(filePath)) { |
| 80 | return filePath.map((path) => { |
| 81 | const content: any = maybeLoadFromExternalFile(path, context); |
| 82 | return content; |
| 83 | }); |
| 84 | } |
| 85 | |
| 86 | if (typeof filePath !== 'string') { |
| 87 | return filePath; |
| 88 | } |
| 89 | if (!filePath.startsWith('file://')) { |
| 90 | return filePath; |
| 91 | } |
| 92 | |
| 93 | // Render the file path using Nunjucks |
| 94 | const renderedFilePath = getNunjucksEngineForFilePath().renderString(filePath, {}); |
| 95 | |
| 96 | // Parse the file URL to extract file path and function name using existing utility |
| 97 | // This handles colon splitting correctly, including Windows drive letters (C:\path) |
| 98 | const { filePath: cleanPath, functionName } = parseFileUrl(renderedFilePath); |
| 99 | |
| 100 | // In assertion contexts, always preserve Python/JS file references |
| 101 | // This prevents premature dereferencing of assertion files that should be |
| 102 | // handled by the assertion system, not the generic config loader |
| 103 | if (context === 'assertion' && (cleanPath.endsWith('.py') || isJavascriptFile(cleanPath))) { |
| 104 | logger.debug(`Preserving Python/JS file reference in assertion context: ${renderedFilePath}`); |
| 105 | return renderedFilePath; |
| 106 | } |
| 107 | |
| 108 | // In vars contexts, preserve all file:// references for test case expansion |
| 109 | // This prevents premature file loading - JS/Python files should be executed at runtime |
| 110 | // by renderPrompt in evaluatorHelpers.ts, and glob patterns should be expanded by |
| 111 | // generateVarCombinations in evaluator.ts |
| 112 | if (context === 'vars') { |
| 113 | logger.debug(`Preserving file reference in vars context: ${renderedFilePath}`); |
| 114 | return renderedFilePath; |
| 115 | } |
| 116 | |
| 117 | // For Python/JS files with function names, return the original string unchanged |
| 118 | // to allow the assertion system to handle function loading at execution time. |
| 119 | // This prevents premature file existence checks that would fail for function references. |
| 120 | if (functionName && (cleanPath.endsWith('.py') || isJavascriptFile(cleanPath))) { |
| 121 | return renderedFilePath; |
| 122 | } |
| 123 | |
| 124 | // For non-Python/JS files, use the original path (ignore potential function name) |
| 125 | const pathToUse = |
| 126 | functionName && !(cleanPath.endsWith('.py') || isJavascriptFile(cleanPath)) |
| 127 | ? renderedFilePath.slice('file://'.length) // Use original path for non-script files |
| 128 | : cleanPath; |
| 129 | |
| 130 | const resolvedPath = path.resolve(cliState.basePath || '', pathToUse); |
| 131 | |
| 132 | // Check if the path contains glob patterns |
no test coverage detected
searching dependent graphs…