(
configPath: string,
source: "user" | "project",
)
| 94 | } |
| 95 | |
| 96 | function loadConfigFileDetailed( |
| 97 | configPath: string, |
| 98 | source: "user" | "project", |
| 99 | ): LoadedConfigFileDetailed | null { |
| 100 | if (!existsSync(configPath)) { |
| 101 | return null; |
| 102 | } |
| 103 | |
| 104 | let rawText: string; |
| 105 | try { |
| 106 | rawText = readFileSync(configPath, "utf-8"); |
| 107 | } catch (error) { |
| 108 | return { |
| 109 | config: {}, |
| 110 | warnings: [ |
| 111 | `${configPath}: failed to read config: ${error instanceof Error ? error.message : String(error)}`, |
| 112 | ], |
| 113 | outcome: "project-file-io-error", |
| 114 | source, |
| 115 | }; |
| 116 | } |
| 117 | |
| 118 | try { |
| 119 | const substituted = substituteConfigVariables({ |
| 120 | text: rawText, |
| 121 | configPath, |
| 122 | isProjectConfig: source === "project", |
| 123 | }); |
| 124 | return { |
| 125 | config: parseJsonc<Record<string, unknown>>(substituted.text), |
| 126 | warnings: substituted.warnings.map((w) => `${configPath}: ${w}`), |
| 127 | outcome: substituted.warnings.length > 0 ? "substitution-failure" : "ok", |
| 128 | source, |
| 129 | }; |
| 130 | } catch (error) { |
| 131 | return { |
| 132 | config: {}, |
| 133 | warnings: [ |
| 134 | `${configPath}: failed to load config: ${error instanceof Error ? error.message : String(error)}`, |
| 135 | ], |
| 136 | outcome: "project-file-parse-error", |
| 137 | source, |
| 138 | }; |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * Deep-merge two raw JSON objects. Both inputs must come from BEFORE Zod |
no test coverage detected