Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(frontend): shikiの言語・テーマの定義ファイルをCDN(esm.sh)から取るようにする #13598

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/459)
- Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/528)
- Fix: コードブロックのシンタックスハイライトで使用される定義ファイルをCDNから取得するように #13177
- CDNから取得せずMisskey本体にバンドルする場合は`pacakges/frontend/vite.config.ts`を修正してください。

### Server
- Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"rollup": "4.12.0",
"sanitize-html": "2.12.1",
"sass": "1.71.1",
"shiki": "1.1.7",
"shiki": "1.2.0",
"strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0",
"three": "0.162.0",
Expand Down
10 changes: 5 additions & 5 deletions packages/frontend/src/components/MkCode.core.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>

<script lang="ts" setup>
import { ref, computed, watch } from 'vue';
import { bundledLanguagesInfo } from 'shiki';
import type { BuiltinLanguage } from 'shiki';
import { computed, ref, watch } from 'vue';
import { bundledLanguagesInfo } from 'shiki/langs';
import type { BundledLanguage } from 'shiki/langs';
import { getHighlighter, getTheme } from '@/scripts/code-highlighter.js';
import { defaultStore } from '@/store.js';

Expand All @@ -23,7 +23,7 @@ const props = defineProps<{

const highlighter = await getHighlighter();
const darkMode = defaultStore.reactiveState.darkMode;
const codeLang = ref<BuiltinLanguage | 'aiscript'>('js');
const codeLang = ref<BundledLanguage | 'aiscript'>('js');

const [lightThemeName, darkThemeName] = await Promise.all([
getTheme('light', true),
Expand All @@ -42,7 +42,7 @@ const html = computed(() => highlighter.codeToHtml(props.code, {
}));

async function fetchLanguage(to: string): Promise<void> {
const language = to as BuiltinLanguage;
const language = to as BundledLanguage;

// Check for the loaded languages, and load the language if it's not loaded yet.
if (!highlighter.getLoadedLanguages().includes(language)) {
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
http-equiv="Content-Security-Policy"
content="default-src 'self' https://newassets.hcaptcha.com/ https://challenges.cloudflare.com/ http://localhost:7493/;
worker-src 'self';
script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com;
script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com https://esm.sh;
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
Expand Down
18 changes: 9 additions & 9 deletions packages/frontend/src/scripts/code-highlighter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { bundledThemesInfo } from 'shiki';
import { getHighlighterCore, loadWasm } from 'shiki/core';
import darkPlus from 'shiki/themes/dark-plus.mjs';
import { bundledThemesInfo } from 'shiki/themes';
import { bundledLanguagesInfo } from 'shiki/langs';
import { unique } from './array.js';
import { deepClone } from './clone.js';
import { deepMerge } from './merge.js';
import type { Highlighter, LanguageRegistration, ThemeRegistration, ThemeRegistrationRaw } from 'shiki';
import type { HighlighterCore, LanguageRegistration, ThemeRegistration, ThemeRegistrationRaw } from 'shiki/core';
import { ColdDeviceStorage } from '@/store.js';
import lightTheme from '@/themes/_light.json5';
import darkTheme from '@/themes/_dark.json5';

let _highlighter: Highlighter | null = null;
let _highlighter: HighlighterCore | null = null;

export async function getTheme(mode: 'light' | 'dark', getName: true): Promise<string>;
export async function getTheme(mode: 'light' | 'dark', getName?: false): Promise<ThemeRegistration | ThemeRegistrationRaw>;
Expand Down Expand Up @@ -51,16 +52,14 @@ export async function getTheme(mode: 'light' | 'dark', getName = false): Promise
return darkPlus;
}

export async function getHighlighter(): Promise<Highlighter> {
export async function getHighlighter(): Promise<HighlighterCore> {
if (!_highlighter) {
return await initHighlighter();
}
return _highlighter;
}

export async function initHighlighter() {
const aiScriptGrammar = await import('aiscript-vscode/aiscript/syntaxes/aiscript.tmLanguage.json');

async function initHighlighter() {
await loadWasm(import('shiki/onig.wasm?init'));

// テーマの重複を消す
Expand All @@ -69,11 +68,12 @@ export async function initHighlighter() {
...(await Promise.all([getTheme('light'), getTheme('dark')])),
]);

const jsLangInfo = bundledLanguagesInfo.find(t => t.id === 'javascript');
const highlighter = await getHighlighterCore({
themes,
langs: [
import('shiki/langs/javascript.mjs'),
aiScriptGrammar.default as unknown as LanguageRegistration,
...(jsLangInfo ? [async () => await jsLangInfo.import()] : []),
async () => (await import('aiscript-vscode/aiscript/syntaxes/aiscript.tmLanguage.json')).default as unknown as LanguageRegistration,
],
});

Expand Down
4 changes: 2 additions & 2 deletions packages/frontend/src/scripts/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { ref } from 'vue';
import tinycolor from 'tinycolor2';
import { deepClone } from './clone.js';
import type { BuiltinTheme } from 'shiki';
import type { BundledTheme } from 'shiki/themes';
import { globalEvents } from '@/events.js';
import lightTheme from '@/themes/_light.json5';
import darkTheme from '@/themes/_dark.json5';
Expand All @@ -20,7 +20,7 @@ export type Theme = {
base?: 'dark' | 'light';
props: Record<string, string>;
codeHighlighter?: {
base: BuiltinTheme;
base: BundledTheme;
overrides?: Record<string, any>;
} | {
base: '_none_';
Expand Down
1 change: 0 additions & 1 deletion packages/frontend/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { markRaw, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { miLocalStorage } from './local-storage.js';
import type { SoundType } from '@/scripts/sound.js';
import type { BuiltinTheme as ShikiBuiltinTheme } from 'shiki';
import { Storage } from '@/pizzax.js';
import { hemisphere } from '@/scripts/intl-const.js';

Expand Down
29 changes: 29 additions & 0 deletions packages/frontend/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,30 @@ import { type UserConfig, defineConfig } from 'vite';

import locales from '../../locales/index.js';
import meta from '../../package.json';
import packageInfo from './package.json' assert { type: 'json' };
import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js';
import pluginJson5 from './vite.json5.js';

const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue'];

/**
* Misskeyのフロントエンドにバンドルせず、CDNなどから別途読み込むリソースを記述する。
* CDNを使わずにバンドルしたい場合、以下の配列から該当要素を削除orコメントアウトすればOK
*/
const externalPackages = [
// shiki(コードブロックのシンタックスハイライトで使用中)はテーマ・言語の定義の容量が大きいため、それらはCDNから読み込む
{
name: 'shiki',
match: /^shiki\/(?<subPkg>(langs|themes))$/,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
match: /^shiki\/(?<subPkg>(langs|themes))$/,
test: /^shiki\/(?<subPkg>langs|themes)$/,

path(id: string, pattern: RegExp): string {
const match = pattern.exec(id)?.groups;
return match
? `https://esm.sh/shiki@${packageInfo.dependencies.shiki}/${match['subPkg']}`
: id;
},
Comment on lines +23 to +28
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
path(id: string, pattern: RegExp): string {
const match = pattern.exec(id)?.groups;
return match
? `https://esm.sh/shiki@${packageInfo.dependencies.shiki}/${match['subPkg']}`
: id;
},
path(id: string, match: RegExpMatchArray): string | undefined {
return `https://esm.sh/shiki@${packageInfo.dependencies.shiki}/${match.subPkg}`;
},

},
];

const hash = (str: string, seed = 0): number => {
let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed;
Expand Down Expand Up @@ -112,13 +131,23 @@ export function getConfig(): UserConfig {
input: {
app: './src/_boot_.ts',
},
external: externalPackages.map(p => p.match),
output: {
manualChunks: {
vue: ['vue'],
photoswipe: ['photoswipe', 'photoswipe/lightbox', 'photoswipe/style.css'],
},
chunkFileNames: process.env.NODE_ENV === 'production' ? '[hash:8].js' : '[name]-[hash:8].js',
assetFileNames: process.env.NODE_ENV === 'production' ? '[hash:8][extname]' : '[name]-[hash:8][extname]',
paths(id) {
for (const p of externalPackages) {
if (p.match.test(id)) {
return p.path(id, p.match);
}
Comment on lines +144 to +146
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (p.match.test(id)) {
return p.path(id, p.match);
}
const match = id.match(p.test);
if (match) {
const id = p.path(id, p.match);
if (id) {
return id;
}
}

}

return id;
},
},
},
cssCodeSplit: true,
Expand Down
20 changes: 12 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading