From a6172455f32b220c0d9642aecb97a5c92518f0b6 Mon Sep 17 00:00:00 2001 From: mrboxs Date: Tue, 10 Nov 2026 07:28:44 +0700 Subject: [PATCH 1/2] fix(web): restore dynamic imports for Shiki themes - Apply text styling when each custom theme is imported - Split editor and Shiki language chunks and tune chunk warnings - Load API auth statically while keeping the database dynamic --- apps/web/src/utils/shiki.bundle.ts | 35 ++++++++++++++---------------- apps/web/vite.config.ts | 32 +++++++++++++++++++++++++++ packages/api/src/context.ts | 7 +++--- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/apps/web/src/utils/shiki.bundle.ts b/apps/web/src/utils/shiki.bundle.ts index 05e2f514..b7da5f56 100644 --- a/apps/web/src/utils/shiki.bundle.ts +++ b/apps/web/src/utils/shiki.bundle.ts @@ -3,8 +3,6 @@ import { createSingletonShorthands, } from '@shikijs/core' import { createJavaScriptRegexEngine } from '@shikijs/engine-javascript' -import oneDarkProTheme from '@shikijs/themes/one-dark-pro' -import oneLightTheme from '@shikijs/themes/one-light' /* Generate by @shikijs/codegen */ import type { DynamicImportLanguageRegistration, @@ -229,15 +227,6 @@ function withCodeBlockBoldTokens(theme: ThemeRegistration, name: string) { }) satisfies ThemeRegistration } -const codeBlockShikiTheme = withCodeBlockBoldTokens( - oneDarkProTheme, - CODE_BLOCK_SHIKI_THEME -) -const codeBlockShikiLightTheme = withCodeBlockBoldTokens( - oneLightTheme, - CODE_BLOCK_SHIKI_LIGHT_THEME -) - const bundledLanguages = { 'angular-html': () => import('@shikijs/langs/angular-html'), 'angular-ts': () => import('@shikijs/langs/angular-ts'), @@ -325,14 +314,22 @@ const bundledThemes = { 'one-light': () => import('@shikijs/themes/one-light'), 'github-light': () => import('@shikijs/themes/github-light'), 'github-dark': () => import('@shikijs/themes/github-dark'), - [CODE_BLOCK_SHIKI_THEME]: () => - Promise.resolve({ - default: codeBlockShikiTheme, - }), - [CODE_BLOCK_SHIKI_LIGHT_THEME]: () => - Promise.resolve({ - default: codeBlockShikiLightTheme, - }), + [CODE_BLOCK_SHIKI_THEME]: async () => { + return { + default: withCodeBlockBoldTokens( + (await import('@shikijs/themes/one-dark-pro')).default, + CODE_BLOCK_SHIKI_THEME + ), + } + }, + [CODE_BLOCK_SHIKI_LIGHT_THEME]: async () => { + return { + default: withCodeBlockBoldTokens( + (await import('@shikijs/themes/one-light')).default, + CODE_BLOCK_SHIKI_LIGHT_THEME + ), + } + }, } as Record function getFontStyleHtmlStyle(token: ThemedToken) { diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index b9ec7836..51d4e07d 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -22,6 +22,38 @@ const config = defineConfig({ resolve: { tsconfigPaths: true }, server: { port: 3010 }, ssr: { external: ['@vercel/og', 'sharp'] }, + environments: { + client: { + build: { + // Shiki's C++ grammar alone minifies to ~501 kB and cannot be split. + chunkSizeWarningLimit: 512, + rolldownOptions: { + output: { + codeSplitting: { + groups: [ + { + name: 'react', + test: /node_modules[\\/](?:react|react-dom|scheduler)[\\/]/, + }, + { + name: 'editor', + test: /node_modules[\\/](?:@blocknote|@tiptap|prosemirror-[^\\/]+)[\\/]/, + entriesAware: true, + maxSize: 500_000, + }, + { + name: 'shiki-languages', + test: /node_modules[\\/]@shikijs[\\/]langs[\\/]/, + entriesAware: true, + maxSize: 500_000, + }, + ], + }, + }, + }, + }, + }, + }, optimizeDeps: { include: ['@tanstack/react-form-start'], }, diff --git a/packages/api/src/context.ts b/packages/api/src/context.ts index 8f0a1ee3..82becc03 100644 --- a/packages/api/src/context.ts +++ b/packages/api/src/context.ts @@ -1,10 +1,9 @@ import type { LoggerContext } from '@orpc/evlog' +import { auth } from '@altstack/auth/server' + export async function createORPCContext({ headers }: { headers: Headers }) { - const [{ auth }, { db }] = await Promise.all([ - import('@altstack/auth/server'), - import('@altstack/db'), - ]) + const { db } = await import('@altstack/db') const session = await auth.api.getSession({ headers }) From f6b20322e348e60f6a955a61c6bd06454eb1a0ac Mon Sep 17 00:00:00 2001 From: mrboxs Date: Tue, 10 Nov 2026 07:38:08 +0700 Subject: [PATCH 2/2] fix(web): avoid loading C++ with Ruby syntax highlighting - Use a Ruby grammar without its embedded C++ dependency - Keep C++ code blocks readable as plain text --- apps/web/package.json | 1 + apps/web/src/utils/shiki-ruby.ts | 47 +++++++++++++++++++ apps/web/src/utils/shiki.bundle.ts | 9 +--- apps/web/tests/public-content-prepare.test.ts | 45 ++++++++++++++++++ apps/web/vite.config.ts | 2 - bun.lock | 3 ++ 6 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 apps/web/src/utils/shiki-ruby.ts diff --git a/apps/web/package.json b/apps/web/package.json index 79e16eb8..72258d4a 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -69,6 +69,7 @@ "remark-gfm": "4.0.1", "remark-parse": "^11.0.0", "sharp": "0.35.5", + "tm-grammars": "1.32.22", "unified": "^11.0.5", "unist-util-visit": "^5.2.0", "zod": "catalog:" diff --git a/apps/web/src/utils/shiki-ruby.ts b/apps/web/src/utils/shiki-ruby.ts new file mode 100644 index 00000000..11f67f1b --- /dev/null +++ b/apps/web/src/utils/shiki-ruby.ts @@ -0,0 +1,47 @@ +import c from '@shikijs/langs/c' +import css from '@shikijs/langs/css' +import graphql from '@shikijs/langs/graphql' +import haml from '@shikijs/langs/haml' +import html from '@shikijs/langs/html' +import javascript from '@shikijs/langs/javascript' +import lua from '@shikijs/langs/lua' +import shellscript from '@shikijs/langs/shellscript' +import sql from '@shikijs/langs/sql' +import xml from '@shikijs/langs/xml' +import yaml from '@shikijs/langs/yaml' +import type { LanguageRegistration } from '@shikijs/types' +import ruby from 'tm-grammars/grammars/ruby.json?raw' + +// Shiki's Ruby entry also bundles C++ for heredocs. Load the grammar without it. +const language: LanguageRegistration = { + ...(JSON.parse(ruby) as LanguageRegistration), + aliases: ['rb'], + embeddedLangs: [ + 'html', + 'haml', + 'xml', + 'sql', + 'graphql', + 'css', + 'c', + 'javascript', + 'shellscript', + 'lua', + 'yaml', + ], +} + +export default [ + ...html, + ...haml, + ...xml, + ...sql, + ...graphql, + ...css, + ...c, + ...javascript, + ...shellscript, + ...lua, + ...yaml, + language, +] diff --git a/apps/web/src/utils/shiki.bundle.ts b/apps/web/src/utils/shiki.bundle.ts index b7da5f56..360a66ab 100644 --- a/apps/web/src/utils/shiki.bundle.ts +++ b/apps/web/src/utils/shiki.bundle.ts @@ -20,8 +20,6 @@ type BundledLanguage = | 'angular-ts' | 'astro' | 'c' - | 'cpp' - | 'c++' | 'csharp' | 'c#' | 'cs' @@ -134,7 +132,6 @@ export const SHIKI_BUNDLED_LANGUAGE_GROUPS = [ { id: 'angular-ts', name: 'Angular TypeScript' }, { id: 'astro', name: 'Astro' }, { id: 'c', name: 'C' }, - { id: 'cpp', name: 'C++', aliases: ['c++'] }, { id: 'csharp', name: 'C#', aliases: ['c#', 'cs'] }, { id: 'css', name: 'CSS' }, { id: 'csv', name: 'CSV' }, @@ -232,8 +229,6 @@ const bundledLanguages = { 'angular-ts': () => import('@shikijs/langs/angular-ts'), astro: () => import('@shikijs/langs/astro'), c: () => import('@shikijs/langs/c'), - cpp: () => import('@shikijs/langs/cpp'), - 'c++': () => import('@shikijs/langs/cpp'), csharp: () => import('@shikijs/langs/csharp'), 'c#': () => import('@shikijs/langs/csharp'), cs: () => import('@shikijs/langs/csharp'), @@ -281,8 +276,8 @@ const bundledLanguages = { prisma: () => import('@shikijs/langs/prisma'), python: () => import('@shikijs/langs/python'), py: () => import('@shikijs/langs/python'), - ruby: () => import('@shikijs/langs/ruby'), - rb: () => import('@shikijs/langs/ruby'), + ruby: () => import('#/utils/shiki-ruby'), + rb: () => import('#/utils/shiki-ruby'), rust: () => import('@shikijs/langs/rust'), rs: () => import('@shikijs/langs/rust'), sass: () => import('@shikijs/langs/sass'), diff --git a/apps/web/tests/public-content-prepare.test.ts b/apps/web/tests/public-content-prepare.test.ts index eb153f02..4cbf33ef 100644 --- a/apps/web/tests/public-content-prepare.test.ts +++ b/apps/web/tests/public-content-prepare.test.ts @@ -174,6 +174,51 @@ describe('public Markdown preparation', () => { expect(result.projectsBySlug).toEqual({}) }) + it.each(['cpp', 'c++'])( + 'keeps %s code readable without syntax highlighting', + async (language) => { + const code = 'std::cout << "Hello";' + const result = await prepareContent({ + slug: 'source', + content: `\`\`\`${language}\n${code}\n\`\`\``, + }) + expect(result.codeBlocks[0]).toEqual({ code, language }) + } + ) + + it.each(['ruby', 'rb'] as const)( + 'highlights %s and embedded HTML without loading C++', + async (language) => { + const highlighter = await shiki.createHighlighter({ + themes: [shiki.CODE_BLOCK_SHIKI_THEME], + langs: [language], + }) + try { + const code = 'def greet\n puts <<~HTML\n Hello\n HTML\nend' + const { tokens } = highlighter.codeToTokens(code, { + lang: language, + theme: shiki.CODE_BLOCK_SHIKI_THEME, + tokenizeTimeLimit: 0, + }) + expect( + tokens + .map((line) => line.map((token) => token.content).join('')) + .join('\n') + ).toBe(code) + expect( + new Set(tokens[0].map((token) => token.color)).size + ).toBeGreaterThan(1) + expect( + new Set(tokens[2].map((token) => token.color)).size + ).toBeGreaterThan(1) + expect(highlighter.getLoadedLanguages()).not.toContain('cpp') + expect(highlighter.getLoadedLanguages()).not.toContain('c++') + } finally { + highlighter.dispose() + } + } + ) + it.each(['powershell', 'ps1'] as const)( 'loads %s on demand for editor and public highlighting', async (language) => { diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 51d4e07d..4e307e08 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -25,8 +25,6 @@ const config = defineConfig({ environments: { client: { build: { - // Shiki's C++ grammar alone minifies to ~501 kB and cannot be split. - chunkSizeWarningLimit: 512, rolldownOptions: { output: { codeSplitting: { diff --git a/bun.lock b/bun.lock index 0b46c7da..a8feb5d7 100644 --- a/bun.lock +++ b/bun.lock @@ -87,6 +87,7 @@ "remark-gfm": "4.0.1", "remark-parse": "^11.0.0", "sharp": "0.35.5", + "tm-grammars": "1.32.22", "unified": "^11.0.5", "unist-util-visit": "^5.2.0", "zod": "catalog:", @@ -2884,6 +2885,8 @@ "tldts-core": ["tldts-core@7.4.10", "https://registry-bridge.viteplus.dev/tldts-core/-/tldts-core-7.4.10.tgz", {}, "sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw=="], + "tm-grammars": ["tm-grammars@1.32.22", "", {}, "sha512-VKWL+UiqlGqbGHonRbrBc0kkqDIHUUiVO/A2rBTBz5LBXKA8Fe2f34O1YiBI7ED37tTUlT3MGmLf9+0F/H4/Ww=="], + "to-regex-range": ["to-regex-range@5.0.1", "https://registry-bridge.viteplus.dev/to-regex-range/-/to-regex-range-5.0.1.tgz", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "toad-cache": ["toad-cache@3.7.4", "", {}, "sha512-m1TdR/rvT7kgGJZhspNtXdsdYk0fddFpJJFlG5s+UkPFo6lkLoZ3YLOaovPYjq1R75NP5JfeTlSHaOsE09peCg=="],