From 9e1145df813a49bcf603f5e1830018eac8c4864d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 2 Feb 2024 15:05:18 +0900
Subject: [PATCH] =?UTF-8?q?enhance(frontend):=20shiki=20v1=E3=81=AB?=
 =?UTF-8?q?=E7=A7=BB=E8=A1=8C=20(#13138)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): shiki v1に移行

* optimize chunks, エラーを握りつぶす

* wasmを分離

* バンドルサイズの警告の最小値を650kBに引き上げ

* optimize
---
 packages/frontend/package.json                |  2 +-
 .../frontend/src/components/MkCode.core.vue   | 21 ++++++-------
 .../frontend/src/scripts/code-highlighter.ts  | 28 +++++++++--------
 pnpm-lock.yaml                                | 30 +++++++------------
 scripts/build-assets.mjs                      |  8 -----
 5 files changed, 37 insertions(+), 52 deletions(-)

diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index e7193a27a..d614f7588 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -60,7 +60,7 @@
 		"rollup": "4.9.6",
 		"sanitize-html": "2.11.0",
 		"sass": "1.70.0",
-		"shiki": "0.14.7",
+		"shiki": "1.0.0-beta.3",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
 		"three": "0.160.1",
diff --git a/packages/frontend/src/components/MkCode.core.vue b/packages/frontend/src/components/MkCode.core.vue
index 8fca3bb15..c655ff416 100644
--- a/packages/frontend/src/components/MkCode.core.vue
+++ b/packages/frontend/src/components/MkCode.core.vue
@@ -5,13 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <!-- eslint-disable vue/no-v-html -->
 <template>
-<div :class="['codeBlockRoot', { 'codeEditor': codeEditor }]" v-html="html"></div>
+<div :class="[$style.codeBlockRoot, { [$style.codeEditor]: codeEditor }]" v-html="html"></div>
 </template>
 
 <script lang="ts" setup>
 import { ref, computed, watch } from 'vue';
-import { BUNDLED_LANGUAGES } from 'shiki';
-import type { Lang as ShikiLang } from 'shiki';
+import { bundledLanguagesInfo } from 'shiki';
+import type { BuiltinLanguage } from 'shiki';
 import { getHighlighter } from '@/scripts/code-highlighter.js';
 
 const props = defineProps<{
@@ -22,24 +22,25 @@ const props = defineProps<{
 
 const highlighter = await getHighlighter();
 
-const codeLang = ref<ShikiLang | 'aiscript'>('js');
+const codeLang = ref<BuiltinLanguage | 'aiscript'>('js');
 const html = computed(() => highlighter.codeToHtml(props.code, {
 	lang: codeLang.value,
 	theme: 'dark-plus',
 }));
 
 async function fetchLanguage(to: string): Promise<void> {
-	const language = to as ShikiLang;
+	const language = to as BuiltinLanguage;
 
 	// Check for the loaded languages, and load the language if it's not loaded yet.
 	if (!highlighter.getLoadedLanguages().includes(language)) {
 		// Check if the language is supported by Shiki
-		const bundles = BUNDLED_LANGUAGES.filter((bundle) => {
+		const bundles = bundledLanguagesInfo.filter((bundle) => {
 			// Languages are specified by their id, they can also have aliases (i. e. "js" and "javascript")
 			return bundle.id === language || bundle.aliases?.includes(language);
 		});
 		if (bundles.length > 0) {
-			await highlighter.loadLanguage(language);
+			console.log(`Loading language: ${language}`);
+			await highlighter.loadLanguage(bundles[0].import);
 			codeLang.value = language;
 		} else {
 			codeLang.value = 'js';
@@ -57,8 +58,8 @@ watch(() => props.lang, (to) => {
 }, { immediate: true });
 </script>
 
-<style scoped lang="scss">
-.codeBlockRoot :deep(.shiki) {
+<style module lang="scss">
+.codeBlockRoot :global(.shiki) {
 	padding: 1em;
 	margin: .5em 0;
 	overflow: auto;
@@ -74,7 +75,7 @@ watch(() => props.lang, (to) => {
 	min-width: 100%;
 	height: 100%;
 
-	& :deep(.shiki) {
+	& :global(.shiki) {
 		padding: 12px;
 		margin: 0;
 		border-radius: 6px;
diff --git a/packages/frontend/src/scripts/code-highlighter.ts b/packages/frontend/src/scripts/code-highlighter.ts
index 957669122..bc05ec94d 100644
--- a/packages/frontend/src/scripts/code-highlighter.ts
+++ b/packages/frontend/src/scripts/code-highlighter.ts
@@ -1,7 +1,6 @@
-import { setWasm, setCDN, Highlighter, getHighlighter as _getHighlighter } from 'shiki';
-
-setWasm('/assets/shiki/dist/onig.wasm');
-setCDN('/assets/shiki/');
+import { getHighlighterCore, loadWasm } from 'shiki/core';
+import darkPlus from 'shiki/themes/dark-plus.mjs';
+import type { Highlighter, LanguageRegistration } from 'shiki';
 
 let _highlighter: Highlighter | null = null;
 
@@ -13,16 +12,19 @@ export async function getHighlighter(): Promise<Highlighter> {
 }
 
 export async function initHighlighter() {
-	const highlighter = await _getHighlighter({
-		theme: 'dark-plus',
-		langs: ['js'],
-	});
+	const aiScriptGrammar = await import('aiscript-vscode/aiscript/syntaxes/aiscript.tmLanguage.json');
 
-	await highlighter.loadLanguage({
-		path: 'languages/aiscript.tmLanguage.json',
-		id: 'aiscript',
-		scopeName: 'source.aiscript',
-		aliases: ['is', 'ais'],
+	await loadWasm(import('shiki/onig.wasm?init'));
+
+	const highlighter = await getHighlighterCore({
+		themes: [darkPlus],
+		langs: [
+			import('shiki/langs/javascript.mjs'),
+			{
+				aliases: ['is', 'ais'],
+				...aiScriptGrammar.default,
+			} as unknown as LanguageRegistration,
+		],
 	});
 
 	_highlighter = highlighter;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f2cbad24f..0561e8b01 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -797,8 +797,8 @@ importers:
         specifier: 1.70.0
         version: 1.70.0
       shiki:
-        specifier: 0.14.7
-        version: 0.14.7
+        specifier: 1.0.0-beta.3
+        version: 1.0.0-beta.3
       strict-event-emitter-types:
         specifier: 2.0.0
         version: 2.0.0
@@ -5951,6 +5951,10 @@ packages:
       string-argv: 0.3.1
     dev: true
 
+  /@shikijs/core@1.0.0-beta.3:
+    resolution: {integrity: sha512-SCwPom2Wn8XxNlEeqdzycU93SKgzYeVsedjqDsgZaz4XiiPpZUzlHt2NAEQTwTnPcHNZapZ6vbkwJ8P11ggL3Q==}
+    dev: false
+
   /@sideway/address@4.1.4:
     resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
     dependencies:
@@ -9297,10 +9301,6 @@ packages:
     resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
     engines: {node: '>=12'}
 
-  /ansi-sequence-parser@1.1.1:
-    resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==}
-    dev: false
-
   /ansi-styles@3.2.1:
     resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
     engines: {node: '>=4'}
@@ -14693,6 +14693,7 @@ packages:
 
   /jsonc-parser@3.2.0:
     resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
+    dev: true
 
   /jsonfile@4.0.0:
     resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
@@ -18247,13 +18248,10 @@ packages:
     resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
     engines: {node: '>=8'}
 
-  /shiki@0.14.7:
-    resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==}
+  /shiki@1.0.0-beta.3:
+    resolution: {integrity: sha512-z7cHTNSSvwGx2DfeLwjSNLo+HcVxifgNIzLm6Ye52eXcIwNHXT0wHbhy7FDOKSKveuEHBwt9opfj3Hoc8LE1Yg==}
     dependencies:
-      ansi-sequence-parser: 1.1.1
-      jsonc-parser: 3.2.0
-      vscode-oniguruma: 1.7.0
-      vscode-textmate: 8.0.0
+      '@shikijs/core': 1.0.0-beta.3
     dev: false
 
   /side-channel@1.0.4:
@@ -20019,14 +20017,6 @@ packages:
     resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
     engines: {node: '>=0.10.0'}
 
-  /vscode-oniguruma@1.7.0:
-    resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==}
-    dev: false
-
-  /vscode-textmate@8.0.0:
-    resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
-    dev: false
-
   /vue-component-type-helpers@1.8.27:
     resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==}
     dev: true
diff --git a/scripts/build-assets.mjs b/scripts/build-assets.mjs
index 2e1268130..d2dabe853 100644
--- a/scripts/build-assets.mjs
+++ b/scripts/build-assets.mjs
@@ -35,13 +35,6 @@ async function copyFrontendLocales() {
   }
 }
 
-async function copyFrontendShikiAssets() {
-  await fs.cp('./packages/frontend/node_modules/shiki/dist', './built/_frontend_dist_/shiki/dist', { dereference: true, recursive: true });
-  await fs.cp('./packages/frontend/node_modules/shiki/languages', './built/_frontend_dist_/shiki/languages', { dereference: true, recursive: true });
-  await fs.cp('./packages/frontend/node_modules/aiscript-vscode/aiscript/syntaxes', './built/_frontend_dist_/shiki/languages', { dereference: true, recursive: true });
-  await fs.cp('./packages/frontend/node_modules/shiki/themes', './built/_frontend_dist_/shiki/themes', { dereference: true, recursive: true });
-}
-
 async function copyBackendViews() {
   await fs.cp('./packages/backend/src/server/web/views', './packages/backend/built/server/web/views', { recursive: true });
 }
@@ -81,7 +74,6 @@ async function build() {
     copyFrontendFonts(),
     copyFrontendTablerIcons(),
     copyFrontendLocales(),
-    copyFrontendShikiAssets(),
     copyBackendViews(),
     buildBackendScript(),
     buildBackendStyle(),