فهرست منبع

fix(core): text files missclassified as binary

Adam 1 ماه پیش
والد
کامیت
8ebdbe0ea2
2فایلهای تغییر یافته به همراه130 افزوده شده و 3 حذف شده
  1. 70 3
      packages/opencode/src/file/index.ts
  2. 60 0
      packages/opencode/test/file/index.test.ts

+ 70 - 3
packages/opencode/src/file/index.ts

@@ -166,7 +166,6 @@ export namespace File {
     "efi",
     "rom",
     "com",
-    "bat",
     "cmd",
     "ps1",
     "sh",
@@ -203,11 +202,77 @@ export namespace File {
     "x3f",
   ])
 
+  const textExtensions = new Set([
+    "ts",
+    "tsx",
+    "mts",
+    "cts",
+    "mtsx",
+    "ctsx",
+    "js",
+    "jsx",
+    "mjs",
+    "cjs",
+    "sh",
+    "bash",
+    "zsh",
+    "fish",
+    "ps1",
+    "psm1",
+    "cmd",
+    "bat",
+    "json",
+    "jsonc",
+    "json5",
+    "yaml",
+    "yml",
+    "toml",
+    "md",
+    "mdx",
+    "txt",
+    "xml",
+    "html",
+    "htm",
+    "css",
+    "scss",
+    "sass",
+    "less",
+    "graphql",
+    "gql",
+    "sql",
+    "ini",
+    "cfg",
+    "conf",
+    "env",
+  ])
+
+  const textNames = new Set([
+    "dockerfile",
+    "makefile",
+    ".gitignore",
+    ".gitattributes",
+    ".editorconfig",
+    ".npmrc",
+    ".nvmrc",
+    ".prettierrc",
+    ".eslintrc",
+  ])
+
   function isImageByExtension(filepath: string): boolean {
     const ext = path.extname(filepath).toLowerCase().slice(1)
     return imageExtensions.has(ext)
   }
 
+  function isTextByExtension(filepath: string): boolean {
+    const ext = path.extname(filepath).toLowerCase().slice(1)
+    return textExtensions.has(ext)
+  }
+
+  function isTextByName(filepath: string): boolean {
+    const name = path.basename(filepath).toLowerCase()
+    return textNames.has(name)
+  }
+
   function getImageMimeType(filepath: string): string {
     const ext = path.extname(filepath).toLowerCase().slice(1)
     const mimeTypes: Record<string, string> = {
@@ -445,7 +510,9 @@ export namespace File {
       return { type: "text", content: "" }
     }
 
-    if (isBinaryByExtension(file)) {
+    const text = isTextByExtension(file) || isTextByName(file)
+
+    if (isBinaryByExtension(file) && !text) {
       return { type: "binary", content: "" }
     }
 
@@ -454,7 +521,7 @@ export namespace File {
     }
 
     const mimeType = Filesystem.mimeType(full)
-    const encode = await shouldEncode(mimeType)
+    const encode = text ? false : await shouldEncode(mimeType)
 
     if (encode && !isImage(mimeType)) {
       return { type: "binary", content: "", mimeType }

+ 60 - 0
packages/opencode/test/file/index.test.ts

@@ -283,6 +283,66 @@ describe("file/index Bun.file patterns", () => {
   })
 
   describe("shouldEncode() logic", () => {
+    test("treats .ts files as text", async () => {
+      await using tmp = await tmpdir()
+      const filepath = path.join(tmp.path, "test.ts")
+      await fs.writeFile(filepath, "export const value = 1", "utf-8")
+
+      await Instance.provide({
+        directory: tmp.path,
+        fn: async () => {
+          const result = await File.read("test.ts")
+          expect(result.type).toBe("text")
+          expect(result.content).toBe("export const value = 1")
+        },
+      })
+    })
+
+    test("treats .mts files as text", async () => {
+      await using tmp = await tmpdir()
+      const filepath = path.join(tmp.path, "test.mts")
+      await fs.writeFile(filepath, "export const value = 1", "utf-8")
+
+      await Instance.provide({
+        directory: tmp.path,
+        fn: async () => {
+          const result = await File.read("test.mts")
+          expect(result.type).toBe("text")
+          expect(result.content).toBe("export const value = 1")
+        },
+      })
+    })
+
+    test("treats .sh files as text", async () => {
+      await using tmp = await tmpdir()
+      const filepath = path.join(tmp.path, "test.sh")
+      await fs.writeFile(filepath, "#!/usr/bin/env bash\necho hello", "utf-8")
+
+      await Instance.provide({
+        directory: tmp.path,
+        fn: async () => {
+          const result = await File.read("test.sh")
+          expect(result.type).toBe("text")
+          expect(result.content).toBe("#!/usr/bin/env bash\necho hello")
+        },
+      })
+    })
+
+    test("treats Dockerfile as text", async () => {
+      await using tmp = await tmpdir()
+      const filepath = path.join(tmp.path, "Dockerfile")
+      await fs.writeFile(filepath, "FROM alpine:3.20", "utf-8")
+
+      await Instance.provide({
+        directory: tmp.path,
+        fn: async () => {
+          const result = await File.read("Dockerfile")
+          expect(result.type).toBe("text")
+          expect(result.content).toBe("FROM alpine:3.20")
+        },
+      })
+    })
+
     test("returns encoding info for text files", async () => {
       await using tmp = await tmpdir()
       const filepath = path.join(tmp.path, "test.txt")