fix(editor): avoid bundling unused Shiki languages
This commit is contained in:
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(
|
||||
|
||||
@@ -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],
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user