diff --git a/bun.lock b/bun.lock index 1f03dbfdcb..26ce835016 100644 --- a/bun.lock +++ b/bun.lock @@ -156,8 +156,8 @@ "@openauthjs/openauth": "catalog:", "@opencode-ai/plugin": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentui/core": "0.0.0-20251001-886e38c1", - "@opentui/solid": "0.0.0-20251001-886e38c1", + "@opentui/core": "0.0.0-20251001-d57654da", + "@opentui/solid": "0.0.0-20251001-d57654da", "@standard-schema/spec": "1.0.0", "@zip.js/zip.js": "2.7.62", "ai": "catalog:", @@ -183,7 +183,7 @@ "turndown": "7.2.0", "ulid": "catalog:", "vscode-jsonrpc": "8.2.1", - "web-tree-sitter": "0.22.6", + "web-tree-sitter": "0.26.0", "xdg-basedir": "5.1.0", "yargs": "18.0.0", "zod": "catalog:", @@ -835,21 +835,21 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - "@opentui/core": ["@opentui/core@0.0.0-20251001-886e38c1", "", { "dependencies": { "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.0.0-20251001-886e38c1", "@opentui/core-darwin-x64": "0.0.0-20251001-886e38c1", "@opentui/core-linux-arm64": "0.0.0-20251001-886e38c1", "@opentui/core-linux-x64": "0.0.0-20251001-886e38c1", "@opentui/core-win32-arm64": "0.0.0-20251001-886e38c1", "@opentui/core-win32-x64": "0.0.0-20251001-886e38c1", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": ">=0.25.0" } }, "sha512-mbElYuirjjWynIpc4pkNTtIYxK9zmEmEjgaV5Z1wyrSy/e5Ld5nKs39wBLKPFzx/u9XcIIz31YRLNW3hHXlxEQ=="], + "@opentui/core": ["@opentui/core@0.0.0-20251001-d57654da", "", { "dependencies": { "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.0.0-20251001-d57654da", "@opentui/core-darwin-x64": "0.0.0-20251001-d57654da", "@opentui/core-linux-arm64": "0.0.0-20251001-d57654da", "@opentui/core-linux-x64": "0.0.0-20251001-d57654da", "@opentui/core-win32-arm64": "0.0.0-20251001-d57654da", "@opentui/core-win32-x64": "0.0.0-20251001-d57654da", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": ">=0.26.0" } }, "sha512-xDERJfK0+xn3atLPPyjliCPTZOGMZ3Is5NOKuCMZnpICm3zNirJMa02/8FGmxz0fbPL+ll1MTO1qdpl2DZBb6g=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.0.0-20251001-886e38c1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-b5+wxaIaEnT0qdCxeOXgW5q0DP/y1r1dTVEVwT0JUK/NXkTa+MPIkHfPg8Ns82v/Bpv2pdvgZkcgBb8f8F0Zvg=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.0.0-20251001-d57654da", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1Q2F9VYRQO/nZioOQmbJf3gpouBUDYE8fC5oeiw9p0fnOmcxPSN0LiS7YJbQcD+x5cK5N5Sv7jxe9ar44Wjivg=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.0.0-20251001-886e38c1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ct2HbxYp9RTXbESilA+ypLG/1mZXn8fAnMUzrxJ77T10yEIzQx5AQPkMJkIwx1hMDmVVgK5M+Eryuk9/IWagAQ=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.0.0-20251001-d57654da", "", { "os": "darwin", "cpu": "x64" }, "sha512-d1iXRP2QdQe2k4Dwwq9QGDay1b2//YIua7N8DOjWztVHDGDKO/doBWfM6f4+HYEEiXegmZX+1hX/VXYEEKTxWA=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.0.0-20251001-886e38c1", "", { "os": "linux", "cpu": "arm64" }, "sha512-DVimZeRZNmMXdLmziiKGlT04J9NBk1VhkjUcWNz1xKNJz3e0VAJHGE9+B4aUCT7iT06bj6bnmfZuZDlCtmLEyg=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.0.0-20251001-d57654da", "", { "os": "linux", "cpu": "arm64" }, "sha512-dc2C1yZlE5o0NfNzV7rqYCqqRBp5TRIGje3u2QEY2xDY4qlti4dS8cSOrLTWrYWrIoCmuN7W+eGrOH4NuUF+fw=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.0.0-20251001-886e38c1", "", { "os": "linux", "cpu": "x64" }, "sha512-obw90FMU9FXnSgL+4DYBaiEPW6PNz7TIFu7Vc39yXRFfLU9eM6Jrj+KRgmEzbsLAqWB246rmAAHnAX8y0T3qpQ=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.0.0-20251001-d57654da", "", { "os": "linux", "cpu": "x64" }, "sha512-sjzj1Jos7VRIl0I7JjZcOu7T4jmMdJgoRTV+U1q9wBMSVQpV9Prb50N/egGeX8D0bEF0V1T6bWkph9xgCMNKsg=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.0.0-20251001-886e38c1", "", { "os": "win32", "cpu": "arm64" }, "sha512-me60jLhjwmGrKS18ny0quBk3eatCR5uKTTzkxnqxgqdHXaM/tbC2S9SoIOkOLWwySLjj69n8xLzuZrZSOojssQ=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.0.0-20251001-d57654da", "", { "os": "win32", "cpu": "arm64" }, "sha512-F3ySKR284VLS1UUDAtcya7RJRuknghlANwlrYxiJkVOjOa7swAaxGZNYdMXmRRvOmMraeOgHWHVK3y0qdPFV9A=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.0.0-20251001-886e38c1", "", { "os": "win32", "cpu": "x64" }, "sha512-WeoojsODqLH5nX9P9UF7QjFAKDq9Z0SLBDzvcRo7NyU3Wd9PhcQf1v9Cjc42lYgF/EOX24UGv2pDLqT0mhIANA=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.0.0-20251001-d57654da", "", { "os": "win32", "cpu": "x64" }, "sha512-aVpSsEqke8ryVh2oChQrwcz4+zYPh77radM+lks5A2bo926r3dcFWNuIBFHwGTJ0tll5iAXw2MdfHo+2r8srxQ=="], - "@opentui/solid": ["@opentui/solid@0.0.0-20251001-886e38c1", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.0.0-20251001-886e38c1", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-mEydaU1JrFL7sSz0lAVIS80IprmhTpqUFabOyLJSReSQ3SSA3tLssdJCWNdqbLXAMaP5NgpmQu6iOwvVxZdJFA=="], + "@opentui/solid": ["@opentui/solid@0.0.0-20251001-d57654da", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.0.0-20251001-d57654da", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-8rJDFe8b2icFs7kTNT4ypDCIKU0RTgE2bION197RtYh9Mu6ViIagqrfGZloRdIRLcRYroYqXdl21IVh5ji2BsQ=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], @@ -3107,7 +3107,7 @@ "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], - "web-tree-sitter": ["web-tree-sitter@0.22.6", "", {}, "sha512-hS87TH71Zd6mGAmYCvlgxeGDjqd9GTeqXNqTT+u0Gs51uIozNIaaq/kUAbV/Zf56jb2ZOyG8BxZs2GG9wbLi6Q=="], + "web-tree-sitter": ["web-tree-sitter@0.26.0", "", {}, "sha512-wGGAMnJEMF8wy33iEGxSvnyEOfVLzSaa3x6g66aEHsL/hsgFb6IVPrpacIordAMz198pE9qReCEqFUuM0pnfwg=="], "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 7819a51f05..d0388c7992 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -38,8 +38,8 @@ "@openauthjs/openauth": "catalog:", "@opencode-ai/plugin": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentui/core": "0.0.0-20251001-886e38c1", - "@opentui/solid": "0.0.0-20251001-886e38c1", + "@opentui/core": "0.0.0-20251001-d57654da", + "@opentui/solid": "0.0.0-20251001-d57654da", "@standard-schema/spec": "1.0.0", "@zip.js/zip.js": "2.7.62", "ai": "catalog:", @@ -65,7 +65,7 @@ "turndown": "7.2.0", "ulid": "catalog:", "vscode-jsonrpc": "8.2.1", - "web-tree-sitter": "0.22.6", + "web-tree-sitter": "0.26.0", "xdg-basedir": "5.1.0", "yargs": "18.0.0", "zod": "catalog:" diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index 40f982ba70..4175d40dd3 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -40,6 +40,7 @@ for (const [os, arch] of targets) { await $`mkdir -p ../../node_modules/${opentui}` await $`npm pack npm pack ${opentui}`.cwd(path.join(dir, "../../node_modules")).quiet() await $`tar -xf ../../node_modules/${opentui.replace("@opentui/", "opentui-")}-*.tgz -C ../../node_modules/${opentui} --strip-components=1` + await Bun.build({ conditions: ["browser"], tsconfig: "./tsconfig.json", @@ -50,12 +51,14 @@ for (const [os, arch] of targets) { execArgv: [`--user-agent=opencode/${version}`, `--env-file=""`, `--`], windows: {}, }, - entrypoints: ["./src/index.ts", "./src/cli/cmd/tui/worker.ts"], + entrypoints: ["./src/index.ts", path.resolve(dir, "../../node_modules/@opentui/core/parser.worker.js")], define: { OPENCODE_VERSION: `'${version}'`, OPENCODE_TUI_PATH: `'../../../dist/${name}/bin/tui'`, + OTUI_TREE_SITTER_WORKER_PATH: "/$bunfs/root/../../node_modules/@opentui/core/parser.worker.js", }, }) + await $`rm -rf ./dist/${name}/bin/tui` await Bun.file(`dist/${name}/package.json`).write( JSON.stringify( diff --git a/packages/opencode/src/cli/cmd/tui/context/theme.tsx b/packages/opencode/src/cli/cmd/tui/context/theme.tsx index 896c37ffad..20a133482d 100644 --- a/packages/opencode/src/cli/cmd/tui/context/theme.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/theme.tsx @@ -1,3 +1,5 @@ +import { SyntaxStyle } from "@opentui/core" + const OPENCODE_THEME = { primary: { dark: "#fab283", @@ -243,18 +245,191 @@ type Theme = { markdownImage: string markdownImageText: string markdownCodeBlock: string - syntaxComment: string - syntaxKeyword: string - syntaxFunction: string - syntaxVariable: string - syntaxString: string - syntaxNumber: string - syntaxType: string - syntaxOperator: string - syntaxPunctuation: string } export const Theme = Object.entries(OPENCODE_THEME).reduce((acc, [key, value]) => { acc[key as keyof Theme] = value.dark return acc -}, {} as Theme) \ No newline at end of file +}, {} as Theme) + +const syntaxThemeDark = [ + { + scope: ["prompt"], + style: { + foreground: "#56b6c2", + }, + }, + { + scope: ["comment"], + style: { + foreground: "#808080", + italic: true, + }, + }, + { + scope: ["comment.documentation"], + style: { + foreground: "#808080", + italic: true, + }, + }, + { + scope: ["string", "symbol"], + style: { + foreground: "#7fd88f", + }, + }, + { + scope: ["number", "boolean"], + style: { + foreground: "#f5a742", + }, + }, + { + scope: ["character.special"], + style: { + foreground: "#7fd88f", + }, + }, + { + scope: ["keyword.return", "keyword.conditional", "keyword.repeat", "keyword.coroutine"], + style: { + foreground: "#9d7cd8", + italic: true, + }, + }, + { + scope: ["keyword.type"], + style: { + foreground: "#e5c07b", + bold: true, + italic: true, + }, + }, + { + scope: ["keyword.function", "function.method"], + style: { + foreground: "#fab283", + }, + }, + { + scope: ["keyword"], + style: { + foreground: "#9d7cd8", + italic: true, + }, + }, + { + scope: ["keyword.import"], + style: { + foreground: "#9d7cd8", + }, + }, + { + scope: ["operator", "keyword.operator", "punctuation.delimiter"], + style: { + foreground: "#56b6c2", + }, + }, + { + scope: ["keyword.conditional.ternary"], + style: { + foreground: "#56b6c2", + }, + }, + { + scope: ["variable", "variable.parameter", "function.method.call", "function.call"], + style: { + foreground: "#e06c75", + }, + }, + { + scope: ["variable.member", "function", "constructor"], + style: { + foreground: "#fab283", + }, + }, + { + scope: ["type", "module"], + style: { + foreground: "#e5c07b", + }, + }, + { + scope: ["constant"], + style: { + foreground: "#e06c75", + }, + }, + { + scope: ["property"], + style: { + foreground: "#e06c75", + }, + }, + { + scope: ["class"], + style: { + foreground: "#e5c07b", + }, + }, + { + scope: ["parameter"], + style: { + foreground: "#eeeeee", + }, + }, + { + scope: ["punctuation", "punctuation.bracket"], + style: { + foreground: "#eeeeee", + }, + }, + { + scope: ["variable.builtin", "type.builtin", "function.builtin", "module.builtin", "constant.builtin"], + style: { + foreground: "#7fd88f", + }, + }, + { + scope: ["variable.super"], + style: { + foreground: "#e06c75", + }, + }, + { + scope: ["string.escape", "string.regexp"], + style: { + foreground: "#7fd88f", + }, + }, + { + scope: ["keyword.directive"], + style: { + foreground: "#9d7cd8", + italic: true, + }, + }, + { + scope: ["punctuation.special"], + style: { + foreground: "#56b6c2", + }, + }, + { + scope: ["keyword.modifier"], + style: { + foreground: "#9d7cd8", + italic: true, + }, + }, + { + scope: ["keyword.exception"], + style: { + foreground: "#9d7cd8", + italic: true, + }, + }, +] + +export const syntaxTheme = SyntaxStyle.fromTheme(syntaxThemeDark) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 870f3efd8f..daaf574cc8 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -4,8 +4,8 @@ import path from "path" import { useRouteData } from "@tui/context/route" import { useSync } from "@tui/context/sync" import { SplitBorder } from "@tui/component/border" -import { Theme } from "@tui/context/theme" -import { BoxRenderable, ScrollBoxRenderable } from "@opentui/core" +import { syntaxTheme, Theme } from "@tui/context/theme" +import { BoxRenderable, pathToFiletype, ScrollBoxRenderable } from "@opentui/core" import { Prompt, type PromptRef } from "@tui/component/prompt" import type { AssistantMessage, Part, ToolPart, UserMessage, TextPart, ReasoningPart } from "@opencode-ai/sdk" import { useLocal } from "@tui/context/local" @@ -648,22 +648,6 @@ ToolRegistry.register({ }, }) -/* -const syntax = new SyntaxStyle({ - keyword: { fg: RGBA.fromHex(Theme.syntaxKeyword), bold: true }, - string: { fg: RGBA.fromHex(Theme.syntaxString) }, - comment: { fg: RGBA.fromHex(Theme.syntaxComment), italic: true }, - number: { fg: RGBA.fromHex(Theme.syntaxNumber) }, - function: { fg: RGBA.fromHex(Theme.syntaxFunction) }, - type: { fg: RGBA.fromHex(Theme.syntaxType) }, - operator: { fg: RGBA.fromHex(Theme.syntaxOperator) }, - variable: { fg: RGBA.fromHex(Theme.syntaxVariable) }, - bracket: { fg: RGBA.fromHex(Theme.syntaxPunctuation) }, - punctuation: { fg: RGBA.fromHex(Theme.syntaxPunctuation) }, - default: { fg: RGBA.fromHex(Theme.syntaxVariable) }, -}) -*/ - ToolRegistry.register({ name: "read", container: "inline", @@ -708,7 +692,7 @@ ToolRegistry.register({ {(value) => {value}} - {code()} + @@ -819,13 +803,21 @@ ToolRegistry.register({ - {code()} + - {props.input.oldString} - {props.input.newString} + + diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index ddf8227e9e..3492a7ed48 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -26,8 +26,10 @@ const parser = lazy(async () => { p.setLanguage(Bash.language as any) return p } catch (e) { - const { default: Parser } = await import("web-tree-sitter") - const { default: treeWasm } = await import("web-tree-sitter/tree-sitter.wasm" as string, { with: { type: "wasm" } }) + const { Parser, Language } = await import("web-tree-sitter") + const { default: treeWasm } = await import("web-tree-sitter/web-tree-sitter.wasm" as string, { + with: { type: "wasm" }, + }) await Parser.init({ locateFile() { return treeWasm @@ -36,7 +38,7 @@ const parser = lazy(async () => { const { default: bashWasm } = await import("tree-sitter-bash/tree-sitter-bash.wasm" as string, { with: { type: "wasm" }, }) - const bashLanguage = await Parser.Language.load(bashWasm) + const bashLanguage = await Language.load(bashWasm) const p = new Parser() p.setLanguage(bashLanguage) return p @@ -57,6 +59,9 @@ export const BashTool = Tool.define("bash", { async execute(params, ctx) { const timeout = Math.min(params.timeout ?? DEFAULT_TIMEOUT, MAX_TIMEOUT) const tree = await parser().then((p) => p.parse(params.command)) + if (!tree) { + throw new Error("Failed to parse command") + } const permissions = await Agent.get(ctx.agent).then((x) => x.permission.bash) const askPatterns = new Set() diff --git a/packages/opencode/src/tool/test.ts b/packages/opencode/src/tool/test.ts index 138d92fbcb..14427c73c8 100644 --- a/packages/opencode/src/tool/test.ts +++ b/packages/opencode/src/tool/test.ts @@ -6,8 +6,10 @@ const parser = async () => { p.setLanguage(Bash.language as any) return p } catch (e) { - const { default: Parser } = await import("web-tree-sitter") - const { default: treeWasm } = await import("web-tree-sitter/tree-sitter.wasm" as string, { with: { type: "wasm" } }) + const { Parser, Language } = await import("web-tree-sitter") + const { default: treeWasm } = await import("web-tree-sitter/web-tree-sitter.wasm" as string, { + with: { type: "wasm" }, + }) await Parser.init({ locateFile() { return treeWasm @@ -16,7 +18,7 @@ const parser = async () => { const { default: bashWasm } = await import("tree-sitter-bash/tree-sitter-bash.wasm" as string, { with: { type: "wasm" }, }) - const bashLanguage = await Parser.Language.load(bashWasm) + const bashLanguage = await Language.load(bashWasm) const p = new Parser() p.setLanguage(bashLanguage) return p @@ -62,6 +64,9 @@ function extractCommands(node: any): Array<{ command: string; args: string[] }> // Extract and display commands console.log("Source code: " + sourceCode) +if (!tree) { + throw new Error("Failed to parse command") +} const commands = extractCommands(tree.rootNode) console.log("Extracted commands:") commands.forEach((cmd, index) => { diff --git a/packages/opencode/src/treesitter/treesitter.ts b/packages/opencode/src/treesitter/treesitter.ts deleted file mode 100644 index a41c4a42ae..0000000000 --- a/packages/opencode/src/treesitter/treesitter.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { lazy } from "../util/lazy" - -export namespace TreeSitter { - const Parser = lazy(async () => { - try { - return NativeParser() - } catch (e) { - return WasmParser() - } - }) - - const NativeParser = lazy(async () => { - const { default: Parser } = await import("tree-sitter") - return Parser - }) - - const WasmParser = lazy(async () => { - const { default: Parser } = await import("web-tree-sitter") - const { default: treeWasm } = await import("web-tree-sitter/tree-sitter.wasm" as string, { - with: { type: "wasm" }, - }) - await Parser.init({ - locateFile() { - return treeWasm - }, - }) - return Parser - }) - - export async function parser() { - const p = await Parser() - const result = new p() - return result - } - - const Languages: Record any; wasm: () => any }> = { - bash: { - native: () => import("tree-sitter-bash"), - wasm: () => - import("tree-sitter-bash/tree-sitter-bash.wasm" as string, { - with: { type: "wasm" }, - }), - }, - typescript: { - native: () => import("tree-sitter-typescript"), - wasm: () => - import("tree-sitter-typescript/tree-sitter-typescript.wasm" as string, { - with: { type: "wasm" }, - }), - }, - } - - const Extensions = { - ".ts": "typescript", - ".tsx": "typescript", - ".js": "typescript", - ".jsx": "typescript", - ".sh": "bash", - } - - export async function language(extension: keyof typeof Extensions) { - const language = Extensions[extension] - if (!language) return undefined - const { native, wasm } = Languages[language] - try { - const { language } = await native() - return language - } catch (e) { - const { default: mod } = await wasm() - const language = await WasmParser().then((p) => p.Language.load(mod)) - return language - } - } -}