Просмотр исходного кода

fix: better error message when config has invalid references (#1874)

Aiden Cline 6 месяцев назад
Родитель
Сommit
ed5f76d849
2 измененных файлов с 22 добавлено и 8 удалено
  1. 1 1
      packages/opencode/src/cli/error.ts
  2. 21 7
      packages/opencode/src/config/config.ts

+ 1 - 1
packages/opencode/src/cli/error.ts

@@ -12,7 +12,7 @@ export function FormatError(input: unknown) {
   }
   if (Config.InvalidError.isInstance(input))
     return [
-      `Config file at ${input.data.path} is invalid`,
+      `Config file at ${input.data.path} is invalid` + (input.data.message ? `: ${input.data.message}` : ""),
       ...(input.data.issues?.map((issue) => "↳ " + issue.message + " " + issue.path.join(".")) ?? []),
     ].join("\n")
 

+ 21 - 7
packages/opencode/src/config/config.ts

@@ -429,14 +429,14 @@ export namespace Config {
     return load(text, filepath)
   }
 
-  async function load(text: string, filepath: string) {
+  async function load(text: string, configFilepath: string) {
     text = text.replace(/\{env:([^}]+)\}/g, (_, varName) => {
       return process.env[varName] || ""
     })
 
     const fileMatches = text.match(/\{file:[^}]+\}/g)
     if (fileMatches) {
-      const configDir = path.dirname(filepath)
+      const configDir = path.dirname(configFilepath)
       const lines = text.split("\n")
 
       for (const match of fileMatches) {
@@ -449,7 +449,20 @@ export namespace Config {
           filePath = path.join(os.homedir(), filePath.slice(2))
         }
         const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(configDir, filePath)
-        const fileContent = (await Bun.file(resolvedPath).text()).trim()
+        const fileContent = (
+          await Bun.file(resolvedPath)
+            .text()
+            .catch((error) => {
+              const errMsg = `bad file reference: "${match}"`
+              if (error.code === "ENOENT") {
+                throw new InvalidError(
+                  { path: configFilepath, message: errMsg + ` ${resolvedPath} does not exist` },
+                  { cause: error },
+                )
+              }
+              throw new InvalidError({ path: configFilepath, message: errMsg }, { cause: error })
+            })
+        ).trim()
         // escape newlines/quotes, strip outer quotes
         text = text.replace(match, JSON.stringify(fileContent).slice(1, -1))
       }
@@ -474,7 +487,7 @@ export namespace Config {
         .join("\n")
 
       throw new JsonError({
-        path: filepath,
+        path: configFilepath,
         message: `\n--- JSONC Input ---\n${text}\n--- Errors ---\n${errorDetails}\n--- End ---`,
       })
     }
@@ -483,21 +496,21 @@ export namespace Config {
     if (parsed.success) {
       if (!parsed.data.$schema) {
         parsed.data.$schema = "https://opencode.ai/config.json"
-        await Bun.write(filepath, JSON.stringify(parsed.data, null, 2))
+        await Bun.write(configFilepath, JSON.stringify(parsed.data, null, 2))
       }
       const data = parsed.data
       if (data.plugin) {
         for (let i = 0; i < data.plugin?.length; i++) {
           const plugin = data.plugin[i]
           try {
-            data.plugin[i] = import.meta.resolve(plugin, filepath)
+            data.plugin[i] = import.meta.resolve(plugin, configFilepath)
           } catch (err) {}
         }
       }
       return data
     }
 
-    throw new InvalidError({ path: filepath, issues: parsed.error.issues })
+    throw new InvalidError({ path: configFilepath, issues: parsed.error.issues })
   }
   export const JsonError = NamedError.create(
     "ConfigJsonError",
@@ -512,6 +525,7 @@ export namespace Config {
     z.object({
       path: z.string(),
       issues: z.custom<z.ZodIssue[]>().optional(),
+      message: z.string().optional(),
     }),
   )