fix(editor): avoid bundling unused Shiki languages

This commit is contained in:
t8y2 committed 2026-08-18 21:33:49 +08:00
1 parent 871bd9d21f
commit 3c1813123d
4 files changed
+76 -25

No files matched your search

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from "vue";
import { ref, computed, watch, onMounted, nextTick } from "vue";
import { useI18n } from "vue-i18n";
import { AlignLeft, Copy, ChevronDown, Undo2, Redo2 } from "@lucide/vue";
import { Button } from "@/components/ui/button";
@@ -8,7 +8,7 @@ import { useTheme } from "@/composables/useTheme";
import { useToast } from "@/composables/useToast";
import { copyToClipboard } from "@/lib/common/clipboard";
import { formatSqlText, type SqlFormatDialect } from "@/lib/sql/sqlFormatter";
import type { Highlighter } from "shiki";
import { createShikiSqlHighlighter, type SqlHighlighter } from "@/lib/sql/sqlHighlighter";
const props = defineProps<{
sql: string;
@@ -34,7 +34,7 @@ const formatting = ref(false);
const highlightedHtml = ref("");
const highlighterReady = ref(false);
let highlighter: Highlighter | null = null;
let highlighter: SqlHighlighter | null = null;
const displaySql = computed(() => {
if (isFormatted.value && formattedSql.value) {
@@ -48,10 +48,9 @@ const hasSql = computed(() => props.sql.trim().length > 0);
async function initHighlighter() {
if (highlighter) return;
try {
const { createHighlighter } = await import("shiki");
highlighter = await createHighlighter({
themes: ["dark-plus", "min-light"],
langs: ["sql"],
highlighter = await createShikiSqlHighlighter({
appearance: () => (isDark.value ? "dark" : "light"),
themePreset: "preview",
});
highlighterReady.value = true;
await highlightSql();
@@ -63,12 +62,7 @@ async function initHighlighter() {
async function highlightSql() {
if (!highlighter || !displaySql.value) return;
try {
const theme = isDark.value ? "dark-plus" : "min-light";
highlightedHtml.value = highlighter.codeToHtml(displaySql.value, {
lang: "sql",
theme,
structure: "inline",
});
highlightedHtml.value = highlighter(displaySql.value);
} catch {
// fallback to plain text
highlightedHtml.value = "";
@@ -134,12 +128,6 @@ onMounted(() => {
void initHighlighter();
});
});
onBeforeUnmount(() => {
highlighter?.dispose();
highlighter = null;
highlighterReady.value = false;
});
</script>
<template>
@@ -8,6 +8,49 @@ describe("escapeHtml", () => {
});
describe("createSafeSqlHighlighter", () => {
it("keeps the default GitHub themes", () => {
const codeToHtml = vi.fn(() => "<span>SELECT 1</span>");
const highlighter = createSafeSqlHighlighter({ codeToHtml }, { appearance: () => "light" });
expect(highlighter("SELECT 1")).toBe("<span>SELECT 1</span>");
expect(highlighter("SELECT 2", "dark")).toBe("<span>SELECT 1</span>");
expect(codeToHtml).toHaveBeenNthCalledWith(1, "SELECT 1", {
lang: "sql",
structure: "inline",
theme: "github-light",
});
expect(codeToHtml).toHaveBeenNthCalledWith(2, "SELECT 2", {
lang: "sql",
structure: "inline",
theme: "github-dark",
});
});
it("uses the existing SQL preview themes", () => {
const codeToHtml = vi.fn(() => "<span>SELECT 1</span>");
const highlighter = createSafeSqlHighlighter(
{ codeToHtml },
{
appearance: () => "light",
themePreset: "preview",
},
);
highlighter("SELECT 1");
highlighter("SELECT 2", "dark");
expect(codeToHtml).toHaveBeenNthCalledWith(1, "SELECT 1", {
lang: "sql",
structure: "inline",
theme: "min-light",
});
expect(codeToHtml).toHaveBeenNthCalledWith(2, "SELECT 2", {
lang: "sql",
structure: "inline",
theme: "dark-plus",
});
});
it("falls back to escaped SQL when Shiki highlighting fails", () => {
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
const highlighter = createSafeSqlHighlighter(
+23 -5
View File
@@ -4,11 +4,20 @@ export type SqlHighlighter = (content: string, appearance?: AppThemeAppearance)
interface ShikiSqlHighlighterOptions {
appearance: () => AppThemeAppearance;
themePreset?: ShikiSqlThemePreset;
}
type ShikiSqlThemePreset = "default" | "preview";
const SHIKI_THEMES = {
dark: "github-dark",
light: "github-light",
default: {
dark: "github-dark",
light: "github-light",
},
preview: {
dark: "dark-plus",
light: "min-light",
},
} as const;
type ShikiHighlighter = Awaited<ReturnType<typeof import("shiki/core").createHighlighterCore>>;
@@ -29,10 +38,11 @@ export async function createShikiSqlHighlighter(options: ShikiSqlHighlighterOpti
export function createSafeSqlHighlighter(highlighter: ShikiCodeHighlighter, options: ShikiSqlHighlighterOptions): SqlHighlighter {
return (content, appearance = options.appearance()) => {
try {
const themes = SHIKI_THEMES[options.themePreset ?? "default"];
return highlighter.codeToHtml(content, {
lang: "sql",
structure: "inline",
theme: SHIKI_THEMES[appearance],
theme: themes[appearance],
});
} catch (error) {
console.warn("[DBX][sqlHighlighter] Failed to highlight SQL:", error);
@@ -64,11 +74,19 @@ function getShikiSqlHighlighter(): Promise<ShikiHighlighter> {
}
async function loadShikiSqlHighlighter(): Promise<ShikiHighlighter> {
const [{ createHighlighterCore }, { createJavaScriptRegexEngine }, githubDark, githubLight, sql] = await Promise.all([import("shiki/core"), import("shiki/engine/javascript"), import("shiki/themes/github-dark.mjs"), import("shiki/themes/github-light.mjs"), import("shiki/langs/sql.mjs")]);
const [{ createHighlighterCore }, { createJavaScriptRegexEngine }, githubDark, githubLight, darkPlus, minLight, sql] = await Promise.all([
import("shiki/core"),
import("shiki/engine/javascript"),
import("shiki/themes/github-dark.mjs"),
import("shiki/themes/github-light.mjs"),
import("shiki/themes/dark-plus.mjs"),
import("shiki/themes/min-light.mjs"),
import("shiki/langs/sql.mjs"),
]);
return createHighlighterCore({
engine: createJavaScriptRegexEngine(),
langs: [sql.default],
themes: [githubDark.default, githubLight.default],
themes: [githubDark.default, githubLight.default, darkPlus.default, minLight.default],
});
}