From 6018439b5595d280ee9b25fadd18784dccc8fcb6 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 15 Dec 2023 03:30:52 +0100 Subject: [PATCH] feat: new `shikiji-core` package, improve bundling (#42) --- .github/workflows/ci.yml | 6 + .github/workflows/release.yml | 3 - .gitmodules | 3 + eslint.config.ts | 1 + package.json | 4 +- packages/shikiji-core/README.md | 5 + packages/shikiji-core/package.json | 71 ++ packages/shikiji-core/rollup.config.mjs | 94 ++ .../src}/bundle-factory.ts | 2 +- .../core => shikiji-core/src}/highlighter.ts | 2 +- .../src/core => shikiji-core/src}/index.ts | 3 +- .../src/core => shikiji-core/src}/internal.ts | 23 +- .../core => shikiji-core/src}/normalize.ts | 4 +- .../src/oniguruma/LICENSE | 0 .../src/oniguruma/index.ts | 0 .../src/oniguruma/onig.d.ts | 0 .../src/oniguruma/onig.js | 0 .../src/oniguruma/types.ts | 0 .../src/core => shikiji-core/src}/registry.ts | 6 +- .../src}/renderer-hast.ts | 4 +- .../src}/renderer-html-themes.ts | 2 +- .../src}/renderer-html.ts | 2 +- .../src/core => shikiji-core/src}/resolver.ts | 13 +- .../src/stack-element-metadata.ts} | 38 +- packages/shikiji-core/src/textmate.ts | 13 + .../src}/tokenizer-ansi.ts | 4 +- .../core => shikiji-core/src}/tokenizer.ts | 9 +- packages/shikiji-core/src/types.ts | 552 ++++++++++++ .../src/core => shikiji-core/src}/utils.ts | 2 +- packages/shikiji-core/src/wasm.ts | 11 + packages/shikiji-core/tsconfig.json | 17 + packages/shikiji-core/vendor/vscode-textmate | 1 + packages/shikiji/package.json | 5 +- packages/shikiji/rollup.config.mjs | 28 +- packages/shikiji/src/core.ts | 3 +- packages/shikiji/src/index.ts | 1 + packages/shikiji/src/types.ts | 548 +----------- packages/shikiji/src/wasm.ts | 11 +- packages/shikiji/test/core.test.ts | 4 +- packages/shikiji/test/themes.test.ts | 2 +- pnpm-lock.yaml | 823 +++++++++++++++++- pnpm-workspace.yaml | 1 + tsconfig.json | 13 +- vitest.config.ts | 13 +- 44 files changed, 1675 insertions(+), 672 deletions(-) create mode 100644 .gitmodules create mode 100644 packages/shikiji-core/README.md create mode 100644 packages/shikiji-core/package.json create mode 100644 packages/shikiji-core/rollup.config.mjs rename packages/{shikiji/src/core => shikiji-core/src}/bundle-factory.ts (99%) rename packages/{shikiji/src/core => shikiji-core/src}/highlighter.ts (99%) rename packages/{shikiji/src/core => shikiji-core/src}/index.ts (87%) rename packages/{shikiji/src/core => shikiji-core/src}/internal.ts (90%) rename packages/{shikiji/src/core => shikiji-core/src}/normalize.ts (95%) rename packages/{shikiji => shikiji-core}/src/oniguruma/LICENSE (100%) rename packages/{shikiji => shikiji-core}/src/oniguruma/index.ts (100%) rename packages/{shikiji => shikiji-core}/src/oniguruma/onig.d.ts (100%) rename packages/{shikiji => shikiji-core}/src/oniguruma/onig.js (100%) rename packages/{shikiji => shikiji-core}/src/oniguruma/types.ts (100%) rename packages/{shikiji/src/core => shikiji-core/src}/registry.ts (96%) rename packages/{shikiji/src/core => shikiji-core/src}/renderer-hast.ts (99%) rename packages/{shikiji/src/core => shikiji-core/src}/renderer-html-themes.ts (99%) rename packages/{shikiji/src/core => shikiji-core/src}/renderer-html.ts (96%) rename packages/{shikiji/src/core => shikiji-core/src}/resolver.ts (71%) rename packages/{shikiji/src/core/stackElementMetadata.ts => shikiji-core/src/stack-element-metadata.ts} (86%) create mode 100644 packages/shikiji-core/src/textmate.ts rename packages/{shikiji/src/core => shikiji-core/src}/tokenizer-ansi.ts (95%) rename packages/{shikiji/src/core => shikiji-core/src}/tokenizer.ts (94%) create mode 100644 packages/shikiji-core/src/types.ts rename packages/{shikiji/src/core => shikiji-core/src}/utils.ts (95%) create mode 100644 packages/shikiji-core/src/wasm.ts create mode 100644 packages/shikiji-core/tsconfig.json create mode 160000 packages/shikiji-core/vendor/vscode-textmate diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf843ab9c..1c56598b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + submodules: recursive - name: Install pnpm uses: pnpm/action-setup@v2 @@ -36,6 +38,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + submodules: recursive - name: Install pnpm uses: pnpm/action-setup@v2 @@ -73,6 +77,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: recursive - name: Install pnpm uses: pnpm/action-setup@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2d7e827be..8fd1be1e1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,9 +16,6 @@ jobs: with: fetch-depth: 0 - - name: Install pnpm - uses: pnpm/action-setup@v2 - - name: Set node uses: actions/setup-node@v3 with: diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..edab82627 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "packages/shikiji-core/vendor/vscode-textmate"] + path = packages/shikiji-core/vendor/vscode-textmate + url = https://github.com/microsoft/vscode-textmate diff --git a/eslint.config.ts b/eslint.config.ts index 853c1d1b5..226d2fc66 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -13,6 +13,7 @@ export default antfu( '**/test/out/**', 'docs/languages.md', 'docs/themes.md', + '**/vendor/**', ], }, { diff --git a/package.json b/package.json index 9a49cae84..427650b66 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@antfu/eslint-config": "^2.4.5", "@antfu/ni": "^0.21.12", "@antfu/utils": "^0.7.7", + "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", @@ -42,6 +43,7 @@ "rollup-plugin-copy": "^3.5.0", "rollup-plugin-dts": "^6.1.0", "rollup-plugin-esbuild": "^6.1.0", + "rollup-plugin-typescript2": "^0.36.0", "shiki": "^0.14.6", "shikiji": "workspace:*", "simple-git-hooks": "^2.9.0", @@ -50,8 +52,6 @@ "unbuild": "^2.0.0", "vite": "^5.0.9", "vitest": "^1.0.4", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^9.0.0", "wrangler": "^3.20.0" }, "resolutions": { diff --git a/packages/shikiji-core/README.md b/packages/shikiji-core/README.md new file mode 100644 index 000000000..c3aa883a0 --- /dev/null +++ b/packages/shikiji-core/README.md @@ -0,0 +1,5 @@ +# shikiji-core + +The core functionality of [Shikiji](https://github.com/antfu/shikiji), without any grammar of themes bundled. + +It's the same as importing `shikiji/core`. diff --git a/packages/shikiji-core/package.json b/packages/shikiji-core/package.json new file mode 100644 index 000000000..ea940f732 --- /dev/null +++ b/packages/shikiji-core/package.json @@ -0,0 +1,71 @@ +{ + "name": "shikiji-core", + "type": "module", + "version": "0.9.3-alpha.0", + "description": "Core of Shikiji", + "author": "Pine Wu ; Anthony Fu ", + "license": "MIT", + "homepage": "https://github.com/antfu/shikiji#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/antfu/shikiji.git", + "directory": "packages/shikiji-core" + }, + "bugs": "https://github.com/antfu/shikiji/issues", + "keywords": [ + "shiki" + ], + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "./wasm": { + "types": "./dist/wasm.d.mts", + "default": "./dist/wasm.mjs" + }, + "./textmate": { + "types": "./dist/textmate.d.mts", + "default": "./dist/textmate.mjs" + }, + "./types": { + "types": "./dist/types.d.mts" + }, + "./dist/*": "./dist/*", + "./*": "./dist/*" + }, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "typesVersions": { + "*": { + "wasm": [ + "./dist/wasm.d.mts" + ], + "types": [ + "./dist/types.d.mts" + ], + "textmate": [ + "./dist/textmate.d.mts" + ], + "*": [ + "./dist/*", + "./*" + ] + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "rimraf dist && rollup -c", + "dev": "rollup -cw", + "prepublishOnly": "nr build", + "test": "vitest" + }, + "devDependencies": { + "hast-util-to-html": "^9.0.0", + "vscode-oniguruma": "^1.7.0" + } +} diff --git a/packages/shikiji-core/rollup.config.mjs b/packages/shikiji-core/rollup.config.mjs new file mode 100644 index 000000000..b82dcf47d --- /dev/null +++ b/packages/shikiji-core/rollup.config.mjs @@ -0,0 +1,94 @@ +// @ts-check +import { defineConfig } from 'rollup' +import { nodeResolve } from '@rollup/plugin-node-resolve' +import commonjs from '@rollup/plugin-commonjs' +import replace from '@rollup/plugin-replace' +import dts from 'rollup-plugin-dts' +import json from '@rollup/plugin-json' +import fs from 'fs-extra' +import ts from 'rollup-plugin-typescript2' + +const entries = [ + 'src/index.ts', + 'src/types.ts', + 'src/textmate.ts', + 'src/wasm.ts', +] + +const plugins = [ + ts({ + check: false, + }), + replace({ + 'DebugFlags.InDebugMode': 'false', + 'preventAssignment': true, + }), + nodeResolve(), + commonjs(), + json({ + namedExports: false, + preferConst: true, + compact: true, + }), + wasmPlugin(), +] + +export default defineConfig([ + { + input: entries, + output: { + dir: 'dist', + format: 'esm', + entryFileNames: '[name].mjs', + chunkFileNames: (f) => { + if (f.name === 'onig') + return 'onig.mjs' + return 'chunks-[name].mjs' + }, + }, + plugins: [ + ...plugins, + ], + }, + { + input: entries, + output: { + dir: 'dist', + format: 'esm', + chunkFileNames: '[name].d.mts', + entryFileNames: f => `${f.name.replace(/src[\\\/]/, '')}.d.mts`, + }, + plugins: [ + dts({ + respectExternal: true, + }), + { + name: 'post', + async buildEnd() { + await fs.writeFile('dist/onig.d.mts', 'declare const binary: ArrayBuffer; export default binary;', 'utf-8') + }, + }, + ], + onwarn: (warning, warn) => { + if (!/Circular|an empty chunk/.test(warning.message)) + warn(warning) + }, + external: [], + }, +]) + +/** + * @returns {import('rollup').Plugin} Plugin + */ +export function wasmPlugin() { + return { + name: 'wasm', + async load(id) { + if (!id.endsWith('.wasm')) + return + const binary = await fs.readFile(id) + const base64 = binary.toString('base64') + return `export default Uint8Array.from(atob(${JSON.stringify(base64)}), c => c.charCodeAt(0))` + }, + } +} diff --git a/packages/shikiji/src/core/bundle-factory.ts b/packages/shikiji-core/src/bundle-factory.ts similarity index 99% rename from packages/shikiji/src/core/bundle-factory.ts rename to packages/shikiji-core/src/bundle-factory.ts index ec34722d8..6323f43e8 100644 --- a/packages/shikiji/src/core/bundle-factory.ts +++ b/packages/shikiji-core/src/bundle-factory.ts @@ -1,4 +1,4 @@ -import type { BundledHighlighterOptions, CodeToHastOptions, CodeToThemedTokensOptions, CodeToTokensWithThemesOptions, HighlighterCoreOptions, HighlighterGeneric, LanguageInput, MaybeArray, RequireKeys, SpecialLanguage, ThemeInput, ThemeRegistration, ThemeRegistrationRaw } from '../types' +import type { BundledHighlighterOptions, CodeToHastOptions, CodeToThemedTokensOptions, CodeToTokensWithThemesOptions, HighlighterCoreOptions, HighlighterGeneric, LanguageInput, MaybeArray, RequireKeys, SpecialLanguage, ThemeInput, ThemeRegistration, ThemeRegistrationRaw } from './types' import { isSpecialLang, toArray } from './utils' import { getHighlighterCore } from './highlighter' diff --git a/packages/shikiji/src/core/highlighter.ts b/packages/shikiji-core/src/highlighter.ts similarity index 99% rename from packages/shikiji/src/core/highlighter.ts rename to packages/shikiji-core/src/highlighter.ts index 6f16581a9..0bb7e0e3a 100644 --- a/packages/shikiji/src/core/highlighter.ts +++ b/packages/shikiji-core/src/highlighter.ts @@ -1,4 +1,4 @@ -import type { HighlighterCoreOptions, HighlighterGeneric } from '../types' +import type { HighlighterCoreOptions, HighlighterGeneric } from './types' import { codeToHtml } from './renderer-html' import { codeToTokensWithThemes } from './renderer-html-themes' import { codeToThemedTokens } from './tokenizer' diff --git a/packages/shikiji/src/core/index.ts b/packages/shikiji-core/src/index.ts similarity index 87% rename from packages/shikiji/src/core/index.ts rename to packages/shikiji-core/src/index.ts index add88fb49..e7a46a7a3 100644 --- a/packages/shikiji/src/core/index.ts +++ b/packages/shikiji-core/src/index.ts @@ -1,8 +1,9 @@ -export { loadWasm } from '../oniguruma' +export { loadWasm } from './oniguruma' export * from './highlighter' export * from './bundle-factory' export * from './utils' +export * from './types' export { getShikiInternal, getShikiContext } from './internal' export { codeToThemedTokens } from './tokenizer' diff --git a/packages/shikiji/src/core/internal.ts b/packages/shikiji-core/src/internal.ts similarity index 90% rename from packages/shikiji/src/core/internal.ts rename to packages/shikiji-core/src/internal.ts index 3705fe51e..be22b4e5c 100644 --- a/packages/shikiji/src/core/internal.ts +++ b/packages/shikiji-core/src/internal.ts @@ -1,5 +1,5 @@ -import type { HighlighterCoreOptions, LanguageInput, MaybeGetter, ShikiInternal, ThemeInput, ThemeRegistration } from '../types' -import { createOnigScanner, createOnigString, loadWasm } from '../oniguruma' +import type { HighlighterCoreOptions, LanguageInput, MaybeGetter, ShikiInternal, ThemeInput, ThemeRegistration } from './types' +import { createOnigScanner, createOnigString, loadWasm } from './oniguruma' import { Registry } from './registry' import { Resolver } from './resolver' @@ -30,14 +30,17 @@ export async function getShikiInternal(options: HighlighterCoreOptions = {}): Pr : undefined, ] as const) - const resolver = new Resolver(Promise.resolve({ - createOnigScanner(patterns) { - return createOnigScanner(patterns) - }, - createOnigString(s) { - return createOnigString(s) - }, - }), 'vscode-oniguruma', langs) + const resolver = new Resolver( + Promise.resolve({ + createOnigScanner(patterns) { + return createOnigScanner(patterns) + }, + createOnigString(s) { + return createOnigString(s) + }, + }), + langs, + ) const _registry = new Registry(resolver, themes, langs) Object.assign(_registry.alias, options.langAlias) diff --git a/packages/shikiji/src/core/normalize.ts b/packages/shikiji-core/src/normalize.ts similarity index 95% rename from packages/shikiji/src/core/normalize.ts rename to packages/shikiji-core/src/normalize.ts index a0741ba6e..38cf9f244 100644 --- a/packages/shikiji/src/core/normalize.ts +++ b/packages/shikiji-core/src/normalize.ts @@ -1,5 +1,5 @@ -import type { IRawTheme } from 'vscode-textmate' -import type { ThemeRegistration, ThemeRegistrationRaw } from '../types' +import type { IRawTheme } from './textmate' +import type { ThemeRegistration, ThemeRegistrationRaw } from './types' export function toShikiTheme(rawTheme: ThemeRegistrationRaw | ThemeRegistration): ThemeRegistration { const type = (rawTheme).type || 'dark' diff --git a/packages/shikiji/src/oniguruma/LICENSE b/packages/shikiji-core/src/oniguruma/LICENSE similarity index 100% rename from packages/shikiji/src/oniguruma/LICENSE rename to packages/shikiji-core/src/oniguruma/LICENSE diff --git a/packages/shikiji/src/oniguruma/index.ts b/packages/shikiji-core/src/oniguruma/index.ts similarity index 100% rename from packages/shikiji/src/oniguruma/index.ts rename to packages/shikiji-core/src/oniguruma/index.ts diff --git a/packages/shikiji/src/oniguruma/onig.d.ts b/packages/shikiji-core/src/oniguruma/onig.d.ts similarity index 100% rename from packages/shikiji/src/oniguruma/onig.d.ts rename to packages/shikiji-core/src/oniguruma/onig.d.ts diff --git a/packages/shikiji/src/oniguruma/onig.js b/packages/shikiji-core/src/oniguruma/onig.js similarity index 100% rename from packages/shikiji/src/oniguruma/onig.js rename to packages/shikiji-core/src/oniguruma/onig.js diff --git a/packages/shikiji/src/oniguruma/types.ts b/packages/shikiji-core/src/oniguruma/types.ts similarity index 100% rename from packages/shikiji/src/oniguruma/types.ts rename to packages/shikiji-core/src/oniguruma/types.ts diff --git a/packages/shikiji/src/core/registry.ts b/packages/shikiji-core/src/registry.ts similarity index 96% rename from packages/shikiji/src/core/registry.ts rename to packages/shikiji-core/src/registry.ts index b5e933df4..31a9cd746 100644 --- a/packages/shikiji/src/core/registry.ts +++ b/packages/shikiji-core/src/registry.ts @@ -1,6 +1,6 @@ -import type { IGrammar, IGrammarConfiguration } from 'vscode-textmate' -import { Registry as TextMateRegistry } from 'vscode-textmate' -import type { LanguageRegistration, ThemeRegistration, ThemeRegistrationRaw } from '../types' +import type { IGrammar, IGrammarConfiguration } from './textmate' +import { Registry as TextMateRegistry } from './textmate' +import type { LanguageRegistration, ThemeRegistration, ThemeRegistrationRaw } from './types' import type { Resolver } from './resolver' import { toShikiTheme } from './normalize' diff --git a/packages/shikiji/src/core/renderer-hast.ts b/packages/shikiji-core/src/renderer-hast.ts similarity index 99% rename from packages/shikiji/src/core/renderer-hast.ts rename to packages/shikiji-core/src/renderer-hast.ts index 84c32a993..bcdfa10ae 100644 --- a/packages/shikiji/src/core/renderer-hast.ts +++ b/packages/shikiji-core/src/renderer-hast.ts @@ -8,9 +8,9 @@ import type { ThemedToken, ThemedTokenWithVariants, TokenStyles, -} from '../types' +} from './types' import { codeToThemedTokens } from './tokenizer' -import { FontStyle } from './stackElementMetadata' +import { FontStyle } from './types' import { codeToTokensWithThemes } from './renderer-html-themes' export function codeToHast( diff --git a/packages/shikiji/src/core/renderer-html-themes.ts b/packages/shikiji-core/src/renderer-html-themes.ts similarity index 99% rename from packages/shikiji/src/core/renderer-html-themes.ts rename to packages/shikiji-core/src/renderer-html-themes.ts index ed2c0b31c..d576accc2 100644 --- a/packages/shikiji/src/core/renderer-html-themes.ts +++ b/packages/shikiji-core/src/renderer-html-themes.ts @@ -3,7 +3,7 @@ import type { ShikiInternal, ThemedToken, ThemedTokenWithVariants, -} from '../types' +} from './types' import { codeToThemedTokens } from './tokenizer' /** diff --git a/packages/shikiji/src/core/renderer-html.ts b/packages/shikiji-core/src/renderer-html.ts similarity index 96% rename from packages/shikiji/src/core/renderer-html.ts rename to packages/shikiji-core/src/renderer-html.ts index 248a24268..6d177fb49 100644 --- a/packages/shikiji/src/core/renderer-html.ts +++ b/packages/shikiji-core/src/renderer-html.ts @@ -1,5 +1,5 @@ import { toHtml as hastToHtml } from 'hast-util-to-html' -import type { CodeToHastOptions, ShikiInternal, ShikijiTransformerContextCommon } from '../types' +import type { CodeToHastOptions, ShikiInternal, ShikijiTransformerContextCommon } from './types' import { codeToHast } from './renderer-hast' /** diff --git a/packages/shikiji/src/core/resolver.ts b/packages/shikiji-core/src/resolver.ts similarity index 71% rename from packages/shikiji/src/core/resolver.ts rename to packages/shikiji-core/src/resolver.ts index ab9444392..93c6cd6ce 100644 --- a/packages/shikiji/src/core/resolver.ts +++ b/packages/shikiji-core/src/resolver.ts @@ -1,17 +1,14 @@ -import type { IOnigLib, RegistryOptions } from 'vscode-textmate' - -import type { LanguageRegistration } from '../types' +import type { IOnigLib, RegistryOptions } from './textmate' +import type { LanguageRegistration } from './types' export class Resolver implements RegistryOptions { private readonly languageMap: { [langIdOrAlias: string]: LanguageRegistration } = {} private readonly scopeToLangMap: { [scope: string]: LanguageRegistration } = {} private readonly _onigLibPromise: Promise - private readonly _onigLibName: string - constructor(onigLibPromise: Promise, onigLibName: string, langs: LanguageRegistration[]) { + constructor(onigLibPromise: Promise, langs: LanguageRegistration[]) { this._onigLibPromise = onigLibPromise - this._onigLibName = onigLibName langs.forEach(i => this.addLanguage(i)) } @@ -20,10 +17,6 @@ export class Resolver implements RegistryOptions { return this._onigLibPromise } - public getOnigLibName(): string { - return this._onigLibName - } - public getLangRegistration(langIdOrAlias: string): LanguageRegistration { return this.languageMap[langIdOrAlias] } diff --git a/packages/shikiji/src/core/stackElementMetadata.ts b/packages/shikiji-core/src/stack-element-metadata.ts similarity index 86% rename from packages/shikiji/src/core/stackElementMetadata.ts rename to packages/shikiji-core/src/stack-element-metadata.ts index 6d75a5b3c..9b9bf9ddf 100644 --- a/packages/shikiji/src/core/stackElementMetadata.ts +++ b/packages/shikiji-core/src/stack-element-metadata.ts @@ -1,3 +1,5 @@ +import { FontStyle } from './types' + /** * Helpers to manage the "collapsed" metadata of an entire StackElement stack. * The following assumptions have been made: @@ -18,20 +20,20 @@ * - f = foreground color (9 bits) * - b = background color (9 bits) */ -export const enum MetadataConsts { - LANGUAGEID_MASK = 0b00000000000000000000000011111111, - TOKEN_TYPE_MASK = 0b00000000000000000000001100000000, - BALANCED_BRACKETS_MASK = 0b00000000000000000000010000000000, - FONT_STYLE_MASK = 0b00000000000000000011100000000000, - FOREGROUND_MASK = 0b00000000011111111100000000000000, - BACKGROUND_MASK = 0b11111111100000000000000000000000, - - LANGUAGEID_OFFSET = 0, - TOKEN_TYPE_OFFSET = 8, - BALANCED_BRACKETS_OFFSET = 10, - FONT_STYLE_OFFSET = 11, - FOREGROUND_OFFSET = 15, - BACKGROUND_OFFSET = 24, +const MetadataConsts = { + LANGUAGEID_MASK: 0b00000000000000000000000011111111, + TOKEN_TYPE_MASK: 0b00000000000000000000001100000000, + BALANCED_BRACKETS_MASK: 0b00000000000000000000010000000000, + FONT_STYLE_MASK: 0b00000000000000000011100000000000, + FOREGROUND_MASK: 0b00000000011111111100000000000000, + BACKGROUND_MASK: 0b11111111100000000000000000000000, + + LANGUAGEID_OFFSET: 0, + TOKEN_TYPE_OFFSET: 8, + BALANCED_BRACKETS_OFFSET: 10, + FONT_STYLE_OFFSET: 11, + FOREGROUND_OFFSET: 15, + BACKGROUND_OFFSET: 24, } export const enum TemporaryStandardTokenType { @@ -42,14 +44,6 @@ export const enum TemporaryStandardTokenType { MetaEmbedded = 8, } -export enum FontStyle { - NotSet = -1, - None = 0, - Italic = 1, - Bold = 2, - Underline = 4, -} - export const enum StandardTokenType { Other = 0, Comment = 1, diff --git a/packages/shikiji-core/src/textmate.ts b/packages/shikiji-core/src/textmate.ts new file mode 100644 index 000000000..b967dc633 --- /dev/null +++ b/packages/shikiji-core/src/textmate.ts @@ -0,0 +1,13 @@ +// We re-bundled vscode-textmate from source to have ESM support. +// This file re-exports some runtime values we need. +export { Registry, INITIAL, StateStack } from '../vendor/vscode-textmate/src/main' +export type { + IRawTheme, + IRawGrammar, + IGrammar, + IGrammarConfiguration, + IOnigLib, + RegistryOptions, +} from '../vendor/vscode-textmate/src/main' +export type { IRawThemeSetting } from '../vendor/vscode-textmate/src/theme' +export * from './stack-element-metadata' diff --git a/packages/shikiji/src/core/tokenizer-ansi.ts b/packages/shikiji-core/src/tokenizer-ansi.ts similarity index 95% rename from packages/shikiji/src/core/tokenizer-ansi.ts rename to packages/shikiji-core/src/tokenizer-ansi.ts index 0ec2cac66..fb3758aad 100644 --- a/packages/shikiji/src/core/tokenizer-ansi.ts +++ b/packages/shikiji-core/src/tokenizer-ansi.ts @@ -1,6 +1,6 @@ import { createAnsiSequenceParser, createColorPalette, namedColors } from 'ansi-sequence-parser' -import type { ThemeRegistration, ThemedToken } from '../types' -import { FontStyle } from './stackElementMetadata' +import type { ThemeRegistration, ThemedToken } from './types' +import { FontStyle } from './types' export function tokenizeAnsiWithTheme(theme: ThemeRegistration, fileContents: string): ThemedToken[][] { const lines = fileContents.split(/\r?\n/) diff --git a/packages/shikiji/src/core/tokenizer.ts b/packages/shikiji-core/src/tokenizer.ts similarity index 94% rename from packages/shikiji/src/core/tokenizer.ts rename to packages/shikiji-core/src/tokenizer.ts index 47a034c04..2767f659d 100644 --- a/packages/shikiji/src/core/tokenizer.ts +++ b/packages/shikiji-core/src/tokenizer.ts @@ -1,11 +1,10 @@ /* --------------------------------------------------------- * Copyright (C) Microsoft Corporation. All rights reserved. *-------------------------------------------------------- */ -import type { IGrammar, IRawTheme } from 'vscode-textmate' -import { INITIAL } from 'vscode-textmate' -import type { CodeToThemedTokensOptions, ShikiInternal, ThemedToken, ThemedTokenScopeExplanation } from '../types' -import type { FontStyle } from './stackElementMetadata' -import { StackElementMetadata } from './stackElementMetadata' +import type { IGrammar, IRawTheme } from './textmate' +import { INITIAL } from './textmate' +import type { CodeToThemedTokensOptions, FontStyle, ShikiInternal, ThemedToken, ThemedTokenScopeExplanation } from './types' +import { StackElementMetadata } from './stack-element-metadata' import { isPlaintext } from './utils' import { tokenizeAnsiWithTheme } from './tokenizer-ansi' diff --git a/packages/shikiji-core/src/types.ts b/packages/shikiji-core/src/types.ts new file mode 100644 index 000000000..89f771986 --- /dev/null +++ b/packages/shikiji-core/src/types.ts @@ -0,0 +1,552 @@ +import type { Element, Root } from 'hast' +import type { + IGrammar as Grammar, + IRawThemeSetting, + IRawGrammar as RawGrammar, + IRawTheme as RawTheme, +} from './textmate' +import type { OnigurumaLoadOptions } from './oniguruma' + +export { + Grammar, + RawGrammar, + // TODO: remove these in the next major version + Grammar as IGrammar, + RawGrammar as IRawGrammar, +} + +export enum FontStyle { + NotSet = -1, + None = 0, + Italic = 1, + Bold = 2, + Underline = 4, +} + +export type PlainTextLanguage = 'text' | 'plaintext' | 'txt' +export type AnsiLanguage = 'ansi' +export type SpecialLanguage = PlainTextLanguage | AnsiLanguage + +export type Awaitable = T | Promise +export type MaybeGetter = Awaitable> | (() => Awaitable>) +export type MaybeModule = T | { default: T } +export type MaybeArray = T | T[] +export type RequireKeys = Omit & Required> + +export type ThemeInput = MaybeGetter +export type LanguageInput = MaybeGetter> + +interface Nothing {} + +/** + * type StringLiteralUnion<'foo'> = 'foo' | string + * This has auto completion whereas `'foo' | string` doesn't + * Adapted from https://github.com/microsoft/TypeScript/issues/29729 + */ +export type StringLiteralUnion = T | (U & Nothing) + +export type ResolveBundleKey = never extends T ? string : T + +export interface ShikiInternal { + setTheme(name: string | ThemeRegistration | ThemeRegistrationRaw): { + theme: ThemeRegistration + colorMap: string[] + } + + getTheme(name: string | ThemeRegistration | ThemeRegistrationRaw): ThemeRegistration + getLangGrammar(name: string): Grammar + + getLoadedThemes(): string[] + getLoadedLanguages(): string[] + loadLanguage(...langs: LanguageInput[]): Promise + loadTheme(...themes: ThemeInput[]): Promise + + getAlias(): Record + updateAlias(alias: Record): void +} + +export interface HighlighterGeneric { + /** + * Get highlighted code in HTML string + */ + codeToHtml( + code: string, + options: CodeToHastOptions, ResolveBundleKey> + ): string + /** + * Get highlighted code in HAST. + * @see https://github.com/syntax-tree/hast + */ + codeToHast( + code: string, + options: CodeToHastOptions, ResolveBundleKey> + ): Root + /** + * Get highlighted code in tokens. + * @returns A 2D array of tokens, first dimension is lines, second dimension is tokens in a line. + */ + codeToThemedTokens( + code: string, + options: CodeToThemedTokensOptions, ResolveBundleKey> + ): ThemedToken[][] + /** + * Get highlighted code in tokens with multiple themes. + * + * Different from `codeToThemedTokens`, each token will have a `variants` property consisting of an object of color name to token styles. + * + * @returns A 2D array of tokens, first dimension is lines, second dimension is tokens in a line. + */ + codeToTokensWithThemes( + code: string, + options: CodeToTokensWithThemesOptions, ResolveBundleKey> + ): ThemedTokenWithVariants[][] + + /** + * Load a theme to the highlighter, so later it can be used synchronously. + */ + loadTheme(...themes: (ThemeInput | BundledThemeKeys)[]): Promise + /** + * Load a language to the highlighter, so later it can be used synchronously. + */ + loadLanguage(...langs: (LanguageInput | BundledLangKeys | SpecialLanguage)[]): Promise + + /** + * Get the theme registration object + */ + getTheme(name: string | ThemeRegistration | ThemeRegistrationRaw): ThemeRegistration + + /** + * Get the names of loaded languages + * + * Special-handled languages like `text`, `plain` and `ansi` are not included. + */ + getLoadedLanguages(): string[] + /** + * Get the names of loaded themes + */ + getLoadedThemes(): string[] + + /** + * Get internal context object + * @internal + * @deprecated + */ + getInternalContext(): ShikiInternal +} + +export interface HighlighterCoreOptions { + /** + * Theme names, or theme registration objects to be loaded upfront. + */ + themes?: ThemeInput[] + /** + * Language names, or language registration objects to be loaded upfront. + */ + langs?: LanguageInput[] + /** + * Alias of languages + * @example { 'my-lang': 'javascript' } + */ + langAlias?: Record + /** + * Load wasm file from a custom path or using a custom function. + */ + loadWasm?: OnigurumaLoadOptions | (() => Promise) +} + +export interface BundledHighlighterOptions { + /** + * Theme registation + * + * @default [] + */ + themes?: (ThemeInput | StringLiteralUnion)[] + /** + * Language registation + * + * @default Object.keys(bundledThemes) + */ + langs?: (LanguageInput | StringLiteralUnion | SpecialLanguage)[] + /** + * Alias of languages + * @example { 'my-lang': 'javascript' } + */ + langAlias?: Record> +} + +export interface LanguageRegistration extends RawGrammar { + name: string + scopeName: string + displayName?: string + aliases?: string[] + /** + * A list of languages the current language embeds. + * If manually specifying languages to load, make sure to load the embedded + * languages for each parent language. + */ + embeddedLangs?: string[] + balancedBracketSelectors?: string[] + unbalancedBracketSelectors?: string[] +} + +export interface CodeToThemedTokensOptions { + lang?: Languages | SpecialLanguage + theme?: Themes | ThemeRegistration | ThemeRegistrationRaw + /** + * Include explanation of why a token is given a color. + * + * @default true + */ + includeExplanation?: boolean +} + +export interface CodeToHastOptionsCommon extends TransformerOptions { + lang: StringLiteralUnion + + /** + * Merge token with only whitespace to the next token, + * Saving a few extra `` + * + * @default true + */ + mergeWhitespaces?: boolean +} + +export interface CodeToTokensWithThemesOptions { + lang?: Languages | SpecialLanguage + + /** + * A map of color names to themes. + * + * `light` and `dark` are required, and arbitrary color names can be added. + * + * @example + * ```ts + * themes: { + * light: 'vitesse-light', + * dark: 'vitesse-dark', + * soft: 'nord', + * // custom colors + * } + * ``` + */ + themes: Partial> +} + +export interface CodeOptionsSingleTheme { + theme: ThemeRegistration | ThemeRegistrationRaw | StringLiteralUnion +} + +export interface CodeOptionsMultipleThemes { + /** + * A map of color names to themes. + * This allows you to specify multiple themes for the generated code. + * + * ```ts + * shiki.codeToHtml(code, { + * lang: 'js', + * themes: { + * light: 'vitesse-light', + * dark: 'vitesse-dark', + * } + * }) + * ``` + * + * Will generate: + * + * ```html + * code + * ``` + * + * @see https://github.com/antfu/shikiji#lightdark-dual-themes + */ + themes: Partial>> + + /** + * The default theme applied to the code (via inline `color` style). + * The rest of the themes are applied via CSS variables, and toggled by CSS overrides. + * + * For example, if `defaultColor` is `light`, then `light` theme is applied to the code, + * and the `dark` theme and other custom themes are applied via CSS variables: + * + * ```html + * code + * ``` + * + * When set to `false`, no default styles will be applied, and totally up to users to apply the styles: + * + * ```html + * code + * ``` + * + * + * @default 'light' + */ + defaultColor?: StringLiteralUnion<'light' | 'dark'> | false + + /** + * Prefix of CSS variables used to store the color of the other theme. + * + * @default '--shiki-' + */ + cssVariablePrefix?: string +} + +export type CodeOptionsThemes = + | CodeOptionsSingleTheme + | CodeOptionsMultipleThemes + +export interface CodeOptionsMeta { + /** + * Meta data passed to Shikiji, usually used by plugin integrations to pass the code block header. + * + * Key values in meta will be serialized to the attributes of the root `
` element.
+   *
+   * Keys starting with `_` will be ignored.
+   *
+   * A special key `__raw` key will be used to pass the raw code block header (if the integration supports it).
+   */
+  meta?: {
+    /**
+     * Raw string of the code block header.
+     */
+    __raw?: string
+    [key: string]: any
+  }
+}
+
+export interface TransformerOptions {
+  /**
+   * Transform the generated HAST tree.
+   */
+  transformers?: ShikijiTransformer[]
+
+  /**
+   * @deprecated use `transformers` instead
+   */
+  transforms?: ShikijiTransformer
+}
+
+export type CodeToHastOptions =
+  & CodeToHastOptionsCommon
+  & CodeOptionsThemes
+  & CodeOptionsMeta
+
+export interface ThemeRegistrationRaw extends RawTheme {}
+
+export interface ThemeRegistration extends ThemeRegistrationRaw {
+  /**
+   * @description theme name
+   */
+  name: string
+
+  /**
+   * @description light/dark theme
+   */
+  type: 'light' | 'dark' | 'css'
+
+  /**
+   * @description tokenColors of the theme file
+   */
+  settings: IRawThemeSetting[]
+
+  /**
+   * @description text default foreground color
+   */
+  fg: string
+
+  /**
+   * @description text default background color
+   */
+  bg: string
+
+  /**
+   * @description relative path of included theme
+   */
+  include?: string
+
+  /**
+   *
+   * @description color map of the theme file
+   */
+  colors?: Record
+}
+
+export interface ShikijiTransformerContextMeta {}
+
+export interface ShikijiTransformerContextCommon {
+  meta: ShikijiTransformerContextMeta
+  codeToHast: (code: string, options: CodeToHastOptions) => Root
+}
+
+export interface ShikijiTransformerContext extends ShikijiTransformerContextCommon {
+  readonly tokens: ThemedToken[][]
+  readonly options: CodeToHastOptions
+  readonly root: Root
+  readonly pre: Element
+  readonly code: Element
+  readonly lines: Element[]
+}
+
+export interface ShikijiTransformer {
+  /**
+   * Name of the transformer
+   */
+  name?: string
+  /**
+   * Transform the entire generated HAST tree. Return a new Node will replace the original one.
+   */
+  root?(this: ShikijiTransformerContext, hast: Root): Root | void
+  /**
+   * Transform the `
` element. Return a new Node will replace the original one.
+   */
+  pre?(this: ShikijiTransformerContext, hast: Element): Element | void
+  /**
+   * Transform the `` element. Return a new Node will replace the original one.
+   */
+  code?(this: ShikijiTransformerContext, hast: Element): Element | void
+  /**
+   * Transform each line `` element.
+   *
+   * @param hast
+   * @param line 1-based line number
+   */
+  line?(this: ShikijiTransformerContext, hast: Element, line: number): Element | void
+  /**
+   * Transform each token `` element.
+   */
+  token?(this: ShikijiTransformerContext, hast: Element, line: number, col: number, lineElement: Element): Element | void
+
+  /**
+   * Transform the raw input code before passing to the highlighter.
+   * This hook will only be called with `codeToHtml`.
+   */
+  preprocess?(this: ShikijiTransformerContextCommon, code: string, options: CodeToHastOptions): string | undefined
+
+  /**
+   * Transform the generated HTML string before returning.
+   * This hook will only be called with `codeToHtml`.
+   */
+  postprocess?(this: ShikijiTransformerContextCommon, code: string, options: CodeToHastOptions): string | undefined
+}
+
+export interface HtmlRendererOptionsCommon extends TransformerOptions {
+  lang?: string
+  langId?: string
+  fg?: string
+  bg?: string
+
+  themeName?: string
+
+  /**
+   * Custom style string to be applied to the root `
` element.
+   * When specified, `fg` and `bg` will be ignored.
+   */
+  rootStyle?: string
+}
+
+export type HtmlRendererOptions = HtmlRendererOptionsCommon & CodeToHastOptions
+
+export interface ThemedTokenScopeExplanation {
+  scopeName: string
+  themeMatches: any[]
+}
+
+export interface ThemedTokenExplanation {
+  content: string
+  scopes: ThemedTokenScopeExplanation[]
+}
+
+/**
+ * A single token with color, and optionally with explanation.
+ *
+ * For example:
+ *
+ * {
+ *   "content": "shiki",
+ *   "color": "#D8DEE9",
+ *   "explanation": [
+ *     {
+ *       "content": "shiki",
+ *       "scopes": [
+ *         {
+ *           "scopeName": "source.js",
+ *           "themeMatches": []
+ *         },
+ *         {
+ *           "scopeName": "meta.objectliteral.js",
+ *           "themeMatches": []
+ *         },
+ *         {
+ *           "scopeName": "meta.object.member.js",
+ *           "themeMatches": []
+ *         },
+ *         {
+ *           "scopeName": "meta.array.literal.js",
+ *           "themeMatches": []
+ *         },
+ *         {
+ *           "scopeName": "variable.other.object.js",
+ *           "themeMatches": [
+ *             {
+ *               "name": "Variable",
+ *               "scope": "variable.other",
+ *               "settings": {
+ *                 "foreground": "#D8DEE9"
+ *               }
+ *             },
+ *             {
+ *               "name": "[JavaScript] Variable Other Object",
+ *               "scope": "source.js variable.other.object",
+ *               "settings": {
+ *                 "foreground": "#D8DEE9"
+ *               }
+ *             }
+ *           ]
+ *         }
+ *       ]
+ *     }
+ *   ]
+ * }
+ *
+ */
+export interface ThemedToken extends TokenStyles, TokenBase {}
+
+export interface TokenBase {
+  /**
+   * The content of the token
+   */
+  content: string
+  /**
+   * Explanation of
+   *
+   * - token text's matching scopes
+   * - reason that token text is given a color (one matching scope matches a rule (scope -> color) in the theme)
+   */
+  explanation?: ThemedTokenExplanation[]
+}
+
+export interface TokenStyles {
+  /**
+   * 6 or 8 digit hex code representation of the token's color
+   */
+  color?: string
+  /**
+   * Font style of token. Can be None/Italic/Bold/Underline
+   */
+  fontStyle?: FontStyle
+  /**
+   * Override with custom inline style for HTML renderer.
+   * When specified, `color` and `fontStyle` will be ignored.
+   */
+  htmlStyle?: string
+}
+
+export interface ThemedTokenWithVariants extends TokenBase {
+  /**
+   * An object of color name to token styles
+   */
+  variants: Record
+}
+
+export {}
diff --git a/packages/shikiji/src/core/utils.ts b/packages/shikiji-core/src/utils.ts
similarity index 95%
rename from packages/shikiji/src/core/utils.ts
rename to packages/shikiji-core/src/utils.ts
index 1fbbc2337..76264873b 100644
--- a/packages/shikiji/src/core/utils.ts
+++ b/packages/shikiji-core/src/utils.ts
@@ -1,5 +1,5 @@
 import type { Element } from 'hast'
-import type { MaybeArray } from '../types'
+import type { MaybeArray } from './types'
 
 export function isPlaintext(lang: string | null | undefined) {
   return !lang || ['plaintext', 'txt', 'text', 'plain'].includes(lang)
diff --git a/packages/shikiji-core/src/wasm.ts b/packages/shikiji-core/src/wasm.ts
new file mode 100644
index 000000000..c758356a5
--- /dev/null
+++ b/packages/shikiji-core/src/wasm.ts
@@ -0,0 +1,11 @@
+let _onigurumaPromise: Promise<{ data: ArrayBuffer }> | null = null
+export async function getWasmInlined(): Promise<{ data: ArrayBuffer }> {
+  if (!_onigurumaPromise) {
+    // @ts-expect-error anyway
+    _onigurumaPromise = import('vscode-oniguruma/release/onig.wasm')
+      .then(r => ({ data: r.default as ArrayBuffer }))
+  }
+  return _onigurumaPromise
+}
+
+export default getWasmInlined
diff --git a/packages/shikiji-core/tsconfig.json b/packages/shikiji-core/tsconfig.json
new file mode 100644
index 000000000..b2ac9cc56
--- /dev/null
+++ b/packages/shikiji-core/tsconfig.json
@@ -0,0 +1,17 @@
+{
+  "compilerOptions": {
+    "target": "esnext",
+    "lib": ["esnext", "DOM"],
+    "rootDir": ".",
+    "module": "esnext",
+    "moduleResolution": "Bundler",
+    "resolveJsonModule": true,
+    "allowJs": true,
+    "strict": true,
+    "strictNullChecks": true,
+    "preserveValueImports": false,
+    "esModuleInterop": true,
+    "skipDefaultLibCheck": true,
+    "skipLibCheck": true
+  }
+}
diff --git a/packages/shikiji-core/vendor/vscode-textmate b/packages/shikiji-core/vendor/vscode-textmate
new file mode 160000
index 000000000..8b07a3c2b
--- /dev/null
+++ b/packages/shikiji-core/vendor/vscode-textmate
@@ -0,0 +1 @@
+Subproject commit 8b07a3c2be6fe4674f9ce6bba6d5c962a7f50df5
diff --git a/packages/shikiji/package.json b/packages/shikiji/package.json
index 6c7cc6724..c7901a51f 100644
--- a/packages/shikiji/package.json
+++ b/packages/shikiji/package.json
@@ -75,11 +75,10 @@
     "test:cf": "wrangler dev test/cf.ts --port 60001"
   },
   "dependencies": {
-    "hast-util-to-html": "^9.0.0"
+    "shikiji-core": "workspace:*"
   },
   "devDependencies": {
     "shiki": "^0.14.6",
-    "vscode-oniguruma": "^1.7.0",
-    "vscode-textmate": "^9.0.0"
+    "vscode-oniguruma": "^1.7.0"
   }
 }
diff --git a/packages/shikiji/rollup.config.mjs b/packages/shikiji/rollup.config.mjs
index 8f245c00b..ff7e92195 100644
--- a/packages/shikiji/rollup.config.mjs
+++ b/packages/shikiji/rollup.config.mjs
@@ -19,6 +19,12 @@ const entries = [
   'src/wasm.ts',
 ]
 
+const external = [
+  'shikiji-core',
+  'shikiji-core/wasm',
+  'shikiji-core/types',
+]
+
 const plugins = [
   esbuild(),
   nodeResolve(),
@@ -28,7 +34,6 @@ const plugins = [
     preferConst: true,
     compact: true,
   }),
-  wasmPlugin(),
 ]
 
 export default defineConfig([
@@ -43,14 +48,13 @@ export default defineConfig([
           return `langs/${f.name.replace('.tmLanguage', '')}.mjs`
         else if (f.moduleIds.some(i => i.match(/[\\\/]themes[\\\/]/)))
           return 'themes/[name].mjs'
-        else if (f.name === 'onig')
-          return 'onig.mjs'
         return 'chunks/[name].mjs'
       },
     },
     plugins: [
       ...plugins,
     ],
+    external,
   },
   {
     input: entries,
@@ -70,7 +74,7 @@ export default defineConfig([
       {
         name: 'post',
         async buildEnd() {
-          await fs.writeFile('dist/onig.d.ts', 'declare const binary: ArrayBuffer; export default binary;', 'utf-8')
+          await fs.writeFile('dist/onig.d.mts', 'declare const binary: ArrayBuffer; export default binary;', 'utf-8')
           const langs = await fg('dist/langs/*.mjs', { absolute: true })
           await Promise.all(
             langs.map(file => fs.writeFile(
@@ -90,22 +94,10 @@ export default defineConfig([
         },
       },
     ],
+    external,
     onwarn: (warning, warn) => {
-      if (!/Circular/.test(warning.message))
+      if (!/Circular|an empty chunk/.test(warning.message))
         warn(warning)
     },
   },
 ])
-
-export function wasmPlugin() {
-  return {
-    name: 'wasm',
-    async load(id) {
-      if (!id.endsWith('.wasm'))
-        return
-      const binary = await fs.readFile(id)
-      const base64 = binary.toString('base64')
-      return `export default Uint8Array.from(atob(${JSON.stringify(base64)}), c => c.charCodeAt(0))`
-    },
-  }
-}
diff --git a/packages/shikiji/src/core.ts b/packages/shikiji/src/core.ts
index bd9c2483a..97fa86019 100644
--- a/packages/shikiji/src/core.ts
+++ b/packages/shikiji/src/core.ts
@@ -1,2 +1 @@
-export * from './core/index'
-export * from './types'
+export * from 'shikiji-core'
diff --git a/packages/shikiji/src/index.ts b/packages/shikiji/src/index.ts
index 4e026969f..657f6be00 100644
--- a/packages/shikiji/src/index.ts
+++ b/packages/shikiji/src/index.ts
@@ -8,6 +8,7 @@ export * from './core'
 export * from './themes'
 export * from './langs'
 export * from './wasm'
+export * from './types'
 
 export type Highlighter = HighlighterGeneric
 
diff --git a/packages/shikiji/src/types.ts b/packages/shikiji/src/types.ts
index 5ea0febad..1d7e5be18 100644
--- a/packages/shikiji/src/types.ts
+++ b/packages/shikiji/src/types.ts
@@ -1,551 +1,7 @@
-import type {
-  IGrammar as Grammar,
-  IRawGrammar as RawGrammar,
-  IRawTheme as RawTheme,
-} from 'vscode-textmate'
-import type { IRawThemeSetting } from 'vscode-textmate/release/theme'
-import type { Element, Root } from 'hast'
-import type { bundledThemes } from './themes'
 import type { bundledLanguages } from './assets/langs'
-import type { FontStyle } from './core/stackElementMetadata'
-import type { OnigurumaLoadOptions } from './oniguruma'
-
-export {
-  Grammar,
-  RawGrammar,
-  FontStyle,
-  // TODO: move these in the next major version
-  Grammar as IGrammar,
-  RawGrammar as IRawGrammar,
-}
+import type { bundledThemes } from './themes'
 
 export type BuiltinLanguage = keyof typeof bundledLanguages
 export type BuiltinTheme = keyof typeof bundledThemes
 
-export type PlainTextLanguage = 'text' | 'plaintext' | 'txt'
-export type AnsiLanguage = 'ansi'
-export type SpecialLanguage = PlainTextLanguage | AnsiLanguage
-
-export type Awaitable = T | Promise
-export type MaybeGetter = Awaitable> | (() => Awaitable>)
-export type MaybeModule = T | { default: T }
-export type MaybeArray = T | T[]
-export type RequireKeys = Omit & Required>
-
-export type ThemeInput = MaybeGetter
-export type LanguageInput = MaybeGetter>
-
-interface Nothing {}
-
-/**
- * type StringLiteralUnion<'foo'> = 'foo' | string
- * This has auto completion whereas `'foo' | string` doesn't
- * Adapted from https://github.com/microsoft/TypeScript/issues/29729
- */
-export type StringLiteralUnion = T | (U & Nothing)
-
-export type ResolveBundleKey = never extends T ? string : T
-
-export interface ShikiInternal {
-  setTheme(name: string | ThemeRegistration | ThemeRegistrationRaw): {
-    theme: ThemeRegistration
-    colorMap: string[]
-  }
-
-  getTheme(name: string | ThemeRegistration | ThemeRegistrationRaw): ThemeRegistration
-  getLangGrammar(name: string): Grammar
-
-  getLoadedThemes(): string[]
-  getLoadedLanguages(): string[]
-  loadLanguage(...langs: LanguageInput[]): Promise
-  loadTheme(...themes: ThemeInput[]): Promise
-
-  getAlias(): Record
-  updateAlias(alias: Record): void
-}
-
-export interface HighlighterGeneric {
-  /**
-   * Get highlighted code in HTML string
-   */
-  codeToHtml(
-    code: string,
-    options: CodeToHastOptions, ResolveBundleKey>
-  ): string
-  /**
-   * Get highlighted code in HAST.
-   * @see https://github.com/syntax-tree/hast
-   */
-  codeToHast(
-    code: string,
-    options: CodeToHastOptions, ResolveBundleKey>
-  ): Root
-  /**
-   * Get highlighted code in tokens.
-   * @returns A 2D array of tokens, first dimension is lines, second dimension is tokens in a line.
-   */
-  codeToThemedTokens(
-    code: string,
-    options: CodeToThemedTokensOptions, ResolveBundleKey>
-  ): ThemedToken[][]
-  /**
-   * Get highlighted code in tokens with multiple themes.
-   *
-   * Different from `codeToThemedTokens`, each token will have a `variants` property consisting of an object of color name to token styles.
-   *
-   * @returns A 2D array of tokens, first dimension is lines, second dimension is tokens in a line.
-   */
-  codeToTokensWithThemes(
-    code: string,
-    options: CodeToTokensWithThemesOptions, ResolveBundleKey>
-  ): ThemedTokenWithVariants[][]
-
-  /**
-   * Load a theme to the highlighter, so later it can be used synchronously.
-   */
-  loadTheme(...themes: (ThemeInput | BundledThemeKeys)[]): Promise
-  /**
-   * Load a language to the highlighter, so later it can be used synchronously.
-   */
-  loadLanguage(...langs: (LanguageInput | BundledLangKeys | SpecialLanguage)[]): Promise
-
-  /**
-   * Get the theme registration object
-   */
-  getTheme(name: string | ThemeRegistration | ThemeRegistrationRaw): ThemeRegistration
-
-  /**
-   * Get the names of loaded languages
-   *
-   * Special-handled languages like `text`, `plain` and `ansi` are not included.
-   */
-  getLoadedLanguages(): string[]
-  /**
-   * Get the names of loaded themes
-   */
-  getLoadedThemes(): string[]
-
-  /**
-   * Get internal context object
-   * @internal
-   * @deprecated
-   */
-  getInternalContext(): ShikiInternal
-}
-
-export interface HighlighterCoreOptions {
-  /**
-   * Theme names, or theme registration objects to be loaded upfront.
-   */
-  themes?: ThemeInput[]
-  /**
-   * Language names, or language registration objects to be loaded upfront.
-   */
-  langs?: LanguageInput[]
-  /**
-   * Alias of languages
-   * @example { 'my-lang': 'javascript' }
-   */
-  langAlias?: Record
-  /**
-   * Load wasm file from a custom path or using a custom function.
-   */
-  loadWasm?: OnigurumaLoadOptions | (() => Promise)
-}
-
-export interface BundledHighlighterOptions {
-  /**
-   * Theme registation
-   *
-   * @default []
-   */
-  themes?: (ThemeInput | StringLiteralUnion)[]
-  /**
-   * Language registation
-   *
-   * @default Object.keys(bundledThemes)
-   */
-  langs?: (LanguageInput | StringLiteralUnion | SpecialLanguage)[]
-  /**
-   * Alias of languages
-   * @example { 'my-lang': 'javascript' }
-   */
-  langAlias?: Record>
-}
-
-export interface LanguageRegistration extends RawGrammar {
-  name: string
-  scopeName: string
-  displayName?: string
-  aliases?: string[]
-  /**
-   * A list of languages the current language embeds.
-   * If manually specifying languages to load, make sure to load the embedded
-   * languages for each parent language.
-   */
-  embeddedLangs?: string[]
-  balancedBracketSelectors?: string[]
-  unbalancedBracketSelectors?: string[]
-}
-
-export interface CodeToThemedTokensOptions {
-  lang?: Languages | SpecialLanguage
-  theme?: Themes | ThemeRegistration | ThemeRegistrationRaw
-  /**
-   * Include explanation of why a token is given a color.
-   *
-   * @default true
-   */
-  includeExplanation?: boolean
-}
-
-export interface CodeToHastOptionsCommon extends TransformerOptions {
-  lang: StringLiteralUnion
-
-  /**
-   * Merge token with only whitespace to the next token,
-   * Saving a few extra ``
-   *
-   * @default true
-   */
-  mergeWhitespaces?: boolean
-}
-
-export interface CodeToTokensWithThemesOptions {
-  lang?: Languages | SpecialLanguage
-
-  /**
-   * A map of color names to themes.
-   *
-   * `light` and `dark` are required, and arbitrary color names can be added.
-   *
-   * @example
-   * ```ts
-   * themes: {
-   *   light: 'vitesse-light',
-   *   dark: 'vitesse-dark',
-   *   soft: 'nord',
-   *   // custom colors
-   * }
-   * ```
-   */
-  themes: Partial>
-}
-
-export interface CodeOptionsSingleTheme {
-  theme: ThemeRegistration | ThemeRegistrationRaw | StringLiteralUnion
-}
-
-export interface CodeOptionsMultipleThemes {
-  /**
-   * A map of color names to themes.
-   * This allows you to specify multiple themes for the generated code.
-   *
-   * ```ts
-   * shiki.codeToHtml(code, {
-   *  lang: 'js',
-   *  themes: {
-   *    light: 'vitesse-light',
-   *    dark: 'vitesse-dark',
-   *  }
-   * })
-   * ```
-   *
-   * Will generate:
-   *
-   * ```html
-   * code
-   * ```
-   *
-   * @see https://github.com/antfu/shikiji#lightdark-dual-themes
-   */
-  themes: Partial>>
-
-  /**
-   * The default theme applied to the code (via inline `color` style).
-   * The rest of the themes are applied via CSS variables, and toggled by CSS overrides.
-   *
-   * For example, if `defaultColor` is `light`, then `light` theme is applied to the code,
-   * and the `dark` theme and other custom themes are applied via CSS variables:
-   *
-   * ```html
-   * code
-   * ```
-   *
-   * When set to `false`, no default styles will be applied, and totally up to users to apply the styles:
-   *
-   * ```html
-   * code
-   * ```
-   *
-   *
-   * @default 'light'
-   */
-  defaultColor?: StringLiteralUnion<'light' | 'dark'> | false
-
-  /**
-   * Prefix of CSS variables used to store the color of the other theme.
-   *
-   * @default '--shiki-'
-   */
-  cssVariablePrefix?: string
-}
-
-export type CodeOptionsThemes =
-  | CodeOptionsSingleTheme
-  | CodeOptionsMultipleThemes
-
-export interface CodeOptionsMeta {
-  /**
-   * Meta data passed to Shikiji, usually used by plugin integrations to pass the code block header.
-   *
-   * Key values in meta will be serialized to the attributes of the root `
` element.
-   *
-   * Keys starting with `_` will be ignored.
-   *
-   * A special key `__raw` key will be used to pass the raw code block header (if the integration supports it).
-   */
-  meta?: {
-    /**
-     * Raw string of the code block header.
-     */
-    __raw?: string
-    [key: string]: any
-  }
-}
-
-export interface TransformerOptions {
-  /**
-   * Transform the generated HAST tree.
-   */
-  transformers?: ShikijiTransformer[]
-
-  /**
-   * @deprecated use `transformers` instead
-   */
-  transforms?: ShikijiTransformer
-}
-
-export type CodeToHastOptions =
-  & CodeToHastOptionsCommon
-  & CodeOptionsThemes
-  & CodeOptionsMeta
-
-export interface ThemeRegistrationRaw extends RawTheme {}
-
-export interface ThemeRegistration extends ThemeRegistrationRaw {
-  /**
-   * @description theme name
-   */
-  name: string
-
-  /**
-   * @description light/dark theme
-   */
-  type: 'light' | 'dark' | 'css'
-
-  /**
-   * @description tokenColors of the theme file
-   */
-  settings: IRawThemeSetting[]
-
-  /**
-   * @description text default foreground color
-   */
-  fg: string
-
-  /**
-   * @description text default background color
-   */
-  bg: string
-
-  /**
-   * @description relative path of included theme
-   */
-  include?: string
-
-  /**
-   *
-   * @description color map of the theme file
-   */
-  colors?: Record
-}
-
-export interface ShikijiTransformerContextMeta {}
-
-export interface ShikijiTransformerContextCommon {
-  meta: ShikijiTransformerContextMeta
-  codeToHast: (code: string, options: CodeToHastOptions) => Root
-}
-
-export interface ShikijiTransformerContext extends ShikijiTransformerContextCommon {
-  readonly tokens: ThemedToken[][]
-  readonly options: CodeToHastOptions
-  readonly root: Root
-  readonly pre: Element
-  readonly code: Element
-  readonly lines: Element[]
-}
-
-export interface ShikijiTransformer {
-  /**
-   * Name of the transformer
-   */
-  name?: string
-  /**
-   * Transform the entire generated HAST tree. Return a new Node will replace the original one.
-   */
-  root?(this: ShikijiTransformerContext, hast: Root): Root | void
-  /**
-   * Transform the `
` element. Return a new Node will replace the original one.
-   */
-  pre?(this: ShikijiTransformerContext, hast: Element): Element | void
-  /**
-   * Transform the `` element. Return a new Node will replace the original one.
-   */
-  code?(this: ShikijiTransformerContext, hast: Element): Element | void
-  /**
-   * Transform each line `` element.
-   *
-   * @param hast
-   * @param line 1-based line number
-   */
-  line?(this: ShikijiTransformerContext, hast: Element, line: number): Element | void
-  /**
-   * Transform each token `` element.
-   */
-  token?(this: ShikijiTransformerContext, hast: Element, line: number, col: number, lineElement: Element): Element | void
-
-  /**
-   * Transform the raw input code before passing to the highlighter.
-   * This hook will only be called with `codeToHtml`.
-   */
-  preprocess?(this: ShikijiTransformerContextCommon, code: string, options: CodeToHastOptions): string | undefined
-
-  /**
-   * Transform the generated HTML string before returning.
-   * This hook will only be called with `codeToHtml`.
-   */
-  postprocess?(this: ShikijiTransformerContextCommon, code: string, options: CodeToHastOptions): string | undefined
-}
-
-export interface HtmlRendererOptionsCommon extends TransformerOptions {
-  lang?: string
-  langId?: string
-  fg?: string
-  bg?: string
-
-  themeName?: string
-
-  /**
-   * Custom style string to be applied to the root `
` element.
-   * When specified, `fg` and `bg` will be ignored.
-   */
-  rootStyle?: string
-}
-
-export type HtmlRendererOptions = HtmlRendererOptionsCommon & CodeToHastOptions
-
-export interface ThemedTokenScopeExplanation {
-  scopeName: string
-  themeMatches: any[]
-}
-
-export interface ThemedTokenExplanation {
-  content: string
-  scopes: ThemedTokenScopeExplanation[]
-}
-
-/**
- * A single token with color, and optionally with explanation.
- *
- * For example:
- *
- * {
- *   "content": "shiki",
- *   "color": "#D8DEE9",
- *   "explanation": [
- *     {
- *       "content": "shiki",
- *       "scopes": [
- *         {
- *           "scopeName": "source.js",
- *           "themeMatches": []
- *         },
- *         {
- *           "scopeName": "meta.objectliteral.js",
- *           "themeMatches": []
- *         },
- *         {
- *           "scopeName": "meta.object.member.js",
- *           "themeMatches": []
- *         },
- *         {
- *           "scopeName": "meta.array.literal.js",
- *           "themeMatches": []
- *         },
- *         {
- *           "scopeName": "variable.other.object.js",
- *           "themeMatches": [
- *             {
- *               "name": "Variable",
- *               "scope": "variable.other",
- *               "settings": {
- *                 "foreground": "#D8DEE9"
- *               }
- *             },
- *             {
- *               "name": "[JavaScript] Variable Other Object",
- *               "scope": "source.js variable.other.object",
- *               "settings": {
- *                 "foreground": "#D8DEE9"
- *               }
- *             }
- *           ]
- *         }
- *       ]
- *     }
- *   ]
- * }
- *
- */
-export interface ThemedToken extends TokenStyles, TokenBase {}
-
-export interface TokenBase {
-  /**
-   * The content of the token
-   */
-  content: string
-  /**
-   * Explanation of
-   *
-   * - token text's matching scopes
-   * - reason that token text is given a color (one matching scope matches a rule (scope -> color) in the theme)
-   */
-  explanation?: ThemedTokenExplanation[]
-}
-
-export interface TokenStyles {
-  /**
-   * 6 or 8 digit hex code representation of the token's color
-   */
-  color?: string
-  /**
-   * Font style of token. Can be None/Italic/Bold/Underline
-   */
-  fontStyle?: FontStyle
-  /**
-   * Override with custom inline style for HTML renderer.
-   * When specified, `color` and `fontStyle` will be ignored.
-   */
-  htmlStyle?: string
-}
-
-export interface ThemedTokenWithVariants extends TokenBase {
-  /**
-   * An object of color name to token styles
-   */
-  variants: Record
-}
-
-export {}
+export type * from 'shikiji-core/types'
diff --git a/packages/shikiji/src/wasm.ts b/packages/shikiji/src/wasm.ts
index 8b480b0d8..2bbdfcd5b 100644
--- a/packages/shikiji/src/wasm.ts
+++ b/packages/shikiji/src/wasm.ts
@@ -1,9 +1,2 @@
-let _onigurumaPromise: Promise<{ data: ArrayBuffer }> | null = null
-export async function getWasmInlined(): Promise<{ data: ArrayBuffer }> {
-  if (!_onigurumaPromise) {
-    // @ts-expect-error anyway
-    _onigurumaPromise = import('vscode-oniguruma/release/onig.wasm')
-      .then(r => ({ data: r.default as ArrayBuffer }))
-  }
-  return _onigurumaPromise
-}
+export { default } from 'shikiji-core/wasm'
+export * from 'shikiji-core/wasm'
diff --git a/packages/shikiji/test/core.test.ts b/packages/shikiji/test/core.test.ts
index 462d2da6d..048e2a69e 100644
--- a/packages/shikiji/test/core.test.ts
+++ b/packages/shikiji/test/core.test.ts
@@ -5,9 +5,7 @@ import js from '../src/assets/langs/javascript'
 import ts from '../src/assets/langs/typescript'
 import nord from '../dist/themes/nord.mjs'
 import mtp from '../dist/themes/material-theme-palenight.mjs'
-
-// @ts-expect-error no-types
-import onig from '../dist/onig.mjs'
+import onig from '../../shikiji-core/dist/onig.mjs'
 
 describe('should', () => {
   it('works', async () => {
diff --git a/packages/shikiji/test/themes.test.ts b/packages/shikiji/test/themes.test.ts
index a7bc36114..636f5ea4d 100644
--- a/packages/shikiji/test/themes.test.ts
+++ b/packages/shikiji/test/themes.test.ts
@@ -1,7 +1,7 @@
 import { describe, expect, it } from 'vitest'
 import type { ThemedToken } from '../src'
 import { codeToHtml, codeToThemedTokens, codeToTokensWithThemes } from '../src'
-import { syncThemesTokenization } from '../src/core/renderer-html-themes'
+import { syncThemesTokenization } from '../../shikiji-core/src/renderer-html-themes'
 
 describe('syncThemesTokenization', () => {
   function stringifyTokens(tokens: ThemedToken[][]) {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5f9ab8c22..f1a96c296 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -24,6 +24,9 @@ importers:
       '@antfu/utils':
         specifier: ^0.7.7
         version: 0.7.7
+      '@rollup/plugin-alias':
+        specifier: ^5.1.0
+        version: 5.1.0(rollup@4.9.0)
       '@rollup/plugin-commonjs':
         specifier: ^25.0.7
         version: 25.0.7(rollup@4.9.0)
@@ -108,6 +111,9 @@ importers:
       rollup-plugin-esbuild:
         specifier: ^6.1.0
         version: 6.1.0(esbuild@0.19.8)(rollup@4.9.0)
+      rollup-plugin-typescript2:
+        specifier: ^0.36.0
+        version: 0.36.0(rollup@4.9.0)(typescript@5.3.3)
       shiki:
         specifier: ^0.14.6
         version: 0.14.6
@@ -132,12 +138,6 @@ importers:
       vitest:
         specifier: ^1.0.4
         version: 1.0.4(@types/node@20.10.4)
-      vscode-oniguruma:
-        specifier: ^1.7.0
-        version: 1.7.0
-      vscode-textmate:
-        specifier: ^9.0.0
-        version: 9.0.0
       wrangler:
         specifier: ^3.20.0
         version: 3.20.0
@@ -185,9 +185,9 @@ importers:
 
   packages/shikiji:
     dependencies:
-      hast-util-to-html:
-        specifier: ^9.0.0
-        version: 9.0.0
+      shikiji-core:
+        specifier: workspace:*
+        version: link:../shikiji-core
     devDependencies:
       shiki:
         specifier: ^0.14.6
@@ -195,9 +195,6 @@ importers:
       vscode-oniguruma:
         specifier: ^1.7.0
         version: 1.7.0
-      vscode-textmate:
-        specifier: ^9.0.0
-        version: 9.0.0
 
   packages/shikiji-compat:
     dependencies:
@@ -208,6 +205,42 @@ importers:
         specifier: workspace:*
         version: link:../shikiji-transformers
 
+  packages/shikiji-core:
+    devDependencies:
+      hast-util-to-html:
+        specifier: ^9.0.0
+        version: 9.0.0
+      vscode-oniguruma:
+        specifier: ^1.7.0
+        version: 1.7.0
+
+  packages/shikiji-core/vendor/vscode-textmate:
+    devDependencies:
+      '@types/mocha':
+        specifier: ^9.1.0
+        version: 9.1.1
+      '@types/node':
+        specifier: ^16.6.1
+        version: 16.18.68
+      copy-webpack-plugin:
+        specifier: ^9.1.0
+        version: 9.1.0(webpack@5.89.0)
+      mocha:
+        specifier: ^9.2.2
+        version: 9.2.2
+      typescript:
+        specifier: ^4.3.5
+        version: 4.9.5
+      vscode-oniguruma:
+        specifier: ^1.7.0
+        version: 1.7.0
+      webpack:
+        specifier: ^5.50.0
+        version: 5.89.0(esbuild@0.19.8)(webpack-cli@4.10.0)
+      webpack-cli:
+        specifier: ^4.8.0
+        version: 4.10.0(webpack@5.89.0)
+
   packages/shikiji-transformers:
     dependencies:
       shikiji:
@@ -604,6 +637,11 @@ packages:
     dev: true
     optional: true
 
+  /@discoveryjs/json-ext@0.5.7:
+    resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
+    engines: {node: '>=10.0.0'}
+    dev: true
+
   /@dprint/formatter@0.2.0:
     resolution: {integrity: sha512-E1q1JaOPeEUBhG//IUayqJQvNpqprZ0OCF8B/bIhUqSsMIp0Y74PgF8JPvDCrOdq43qiGUYAavVO8iCBHOoU/A==}
     dev: true
@@ -1560,6 +1598,19 @@ packages:
       slash: 4.0.0
     dev: true
 
+  /@rollup/plugin-alias@5.1.0(rollup@4.9.0):
+    resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+    peerDependenciesMeta:
+      rollup:
+        optional: true
+    dependencies:
+      rollup: 4.9.0
+      slash: 4.0.0
+    dev: true
+
   /@rollup/plugin-commonjs@25.0.7(rollup@3.29.4):
     resolution: {integrity: sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==}
     engines: {node: '>=14.0.0'}
@@ -1701,6 +1752,14 @@ packages:
       terser: 5.26.0
     dev: true
 
+  /@rollup/pluginutils@4.2.1:
+    resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
+    engines: {node: '>= 8.0.0'}
+    dependencies:
+      estree-walker: 2.0.2
+      picomatch: 2.3.1
+    dev: true
+
   /@rollup/pluginutils@5.1.0(rollup@3.29.4):
     resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
     engines: {node: '>=14.0.0'}
@@ -1962,6 +2021,20 @@ packages:
       '@types/ms': 0.7.31
     dev: true
 
+  /@types/eslint-scope@3.7.7:
+    resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
+    dependencies:
+      '@types/eslint': 8.44.9
+      '@types/estree': 1.0.5
+    dev: true
+
+  /@types/eslint@8.44.9:
+    resolution: {integrity: sha512-6yBxcvwnnYoYT1Uk2d+jvIfsuP4mb2EdIxFnrPABj5a/838qe5bGkNLFOiipX4ULQ7XVQvTxOh7jO+BTAiqsEw==}
+    dependencies:
+      '@types/estree': 1.0.5
+      '@types/json-schema': 7.0.15
+    dev: true
+
   /@types/estree@1.0.5:
     resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
     dev: true
@@ -2027,6 +2100,7 @@ packages:
     resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==}
     dependencies:
       '@types/unist': 3.0.2
+    dev: true
 
   /@types/mdurl@1.0.2:
     resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==}
@@ -2036,6 +2110,10 @@ packages:
     resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
     dev: true
 
+  /@types/mocha@9.1.1:
+    resolution: {integrity: sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==}
+    dev: true
+
   /@types/ms@0.7.31:
     resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
     dev: true
@@ -2046,6 +2124,10 @@ packages:
       '@types/node': 20.10.4
     dev: true
 
+  /@types/node@16.18.68:
+    resolution: {integrity: sha512-sG3hPIQwJLoewrN7cr0dwEy+yF5nD4D/4FxtQpFciRD/xwUzgD+G05uxZHv5mhfXo4F9Jkp13jjn0CC2q325sg==}
+    dev: true
+
   /@types/node@20.10.4:
     resolution: {integrity: sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==}
     dependencies:
@@ -2246,8 +2328,13 @@ packages:
       - supports-color
     dev: false
 
+  /@ungap/promise-all-settled@1.1.2:
+    resolution: {integrity: sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==}
+    dev: true
+
   /@ungap/structured-clone@1.2.0:
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+    dev: true
 
   /@vitest/coverage-v8@1.0.4(vitest@1.0.4):
     resolution: {integrity: sha512-xD6Yuql6RW0Ir/JJIs6rVrmnG2/KOWJF+IRX1oJQk5wGKGxbtdrYPbl+WTUn/4ICCQ2G20zbE1e8/nPNyAG5Vg==}
@@ -2362,11 +2449,164 @@ packages:
     resolution: {integrity: sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==}
     dev: true
 
+  /@webassemblyjs/ast@1.11.6:
+    resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==}
+    dependencies:
+      '@webassemblyjs/helper-numbers': 1.11.6
+      '@webassemblyjs/helper-wasm-bytecode': 1.11.6
+    dev: true
+
+  /@webassemblyjs/floating-point-hex-parser@1.11.6:
+    resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==}
+    dev: true
+
+  /@webassemblyjs/helper-api-error@1.11.6:
+    resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==}
+    dev: true
+
+  /@webassemblyjs/helper-buffer@1.11.6:
+    resolution: {integrity: sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==}
+    dev: true
+
+  /@webassemblyjs/helper-numbers@1.11.6:
+    resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==}
+    dependencies:
+      '@webassemblyjs/floating-point-hex-parser': 1.11.6
+      '@webassemblyjs/helper-api-error': 1.11.6
+      '@xtuc/long': 4.2.2
+    dev: true
+
+  /@webassemblyjs/helper-wasm-bytecode@1.11.6:
+    resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==}
+    dev: true
+
+  /@webassemblyjs/helper-wasm-section@1.11.6:
+    resolution: {integrity: sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==}
+    dependencies:
+      '@webassemblyjs/ast': 1.11.6
+      '@webassemblyjs/helper-buffer': 1.11.6
+      '@webassemblyjs/helper-wasm-bytecode': 1.11.6
+      '@webassemblyjs/wasm-gen': 1.11.6
+    dev: true
+
+  /@webassemblyjs/ieee754@1.11.6:
+    resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==}
+    dependencies:
+      '@xtuc/ieee754': 1.2.0
+    dev: true
+
+  /@webassemblyjs/leb128@1.11.6:
+    resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==}
+    dependencies:
+      '@xtuc/long': 4.2.2
+    dev: true
+
+  /@webassemblyjs/utf8@1.11.6:
+    resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==}
+    dev: true
+
+  /@webassemblyjs/wasm-edit@1.11.6:
+    resolution: {integrity: sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==}
+    dependencies:
+      '@webassemblyjs/ast': 1.11.6
+      '@webassemblyjs/helper-buffer': 1.11.6
+      '@webassemblyjs/helper-wasm-bytecode': 1.11.6
+      '@webassemblyjs/helper-wasm-section': 1.11.6
+      '@webassemblyjs/wasm-gen': 1.11.6
+      '@webassemblyjs/wasm-opt': 1.11.6
+      '@webassemblyjs/wasm-parser': 1.11.6
+      '@webassemblyjs/wast-printer': 1.11.6
+    dev: true
+
+  /@webassemblyjs/wasm-gen@1.11.6:
+    resolution: {integrity: sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==}
+    dependencies:
+      '@webassemblyjs/ast': 1.11.6
+      '@webassemblyjs/helper-wasm-bytecode': 1.11.6
+      '@webassemblyjs/ieee754': 1.11.6
+      '@webassemblyjs/leb128': 1.11.6
+      '@webassemblyjs/utf8': 1.11.6
+    dev: true
+
+  /@webassemblyjs/wasm-opt@1.11.6:
+    resolution: {integrity: sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==}
+    dependencies:
+      '@webassemblyjs/ast': 1.11.6
+      '@webassemblyjs/helper-buffer': 1.11.6
+      '@webassemblyjs/wasm-gen': 1.11.6
+      '@webassemblyjs/wasm-parser': 1.11.6
+    dev: true
+
+  /@webassemblyjs/wasm-parser@1.11.6:
+    resolution: {integrity: sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==}
+    dependencies:
+      '@webassemblyjs/ast': 1.11.6
+      '@webassemblyjs/helper-api-error': 1.11.6
+      '@webassemblyjs/helper-wasm-bytecode': 1.11.6
+      '@webassemblyjs/ieee754': 1.11.6
+      '@webassemblyjs/leb128': 1.11.6
+      '@webassemblyjs/utf8': 1.11.6
+    dev: true
+
+  /@webassemblyjs/wast-printer@1.11.6:
+    resolution: {integrity: sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==}
+    dependencies:
+      '@webassemblyjs/ast': 1.11.6
+      '@xtuc/long': 4.2.2
+    dev: true
+
+  /@webpack-cli/configtest@1.2.0(webpack-cli@4.10.0)(webpack@5.89.0):
+    resolution: {integrity: sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==}
+    peerDependencies:
+      webpack: 4.x.x || 5.x.x
+      webpack-cli: 4.x.x
+    dependencies:
+      webpack: 5.89.0(esbuild@0.19.8)(webpack-cli@4.10.0)
+      webpack-cli: 4.10.0(webpack@5.89.0)
+    dev: true
+
+  /@webpack-cli/info@1.5.0(webpack-cli@4.10.0):
+    resolution: {integrity: sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==}
+    peerDependencies:
+      webpack-cli: 4.x.x
+    dependencies:
+      envinfo: 7.11.0
+      webpack-cli: 4.10.0(webpack@5.89.0)
+    dev: true
+
+  /@webpack-cli/serve@1.7.0(webpack-cli@4.10.0):
+    resolution: {integrity: sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==}
+    peerDependencies:
+      webpack-cli: 4.x.x
+      webpack-dev-server: '*'
+    peerDependenciesMeta:
+      webpack-dev-server:
+        optional: true
+    dependencies:
+      webpack-cli: 4.10.0(webpack@5.89.0)
+    dev: true
+
+  /@xtuc/ieee754@1.2.0:
+    resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
+    dev: true
+
+  /@xtuc/long@4.2.2:
+    resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
+    dev: true
+
   /abbrev@2.0.0:
     resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dev: true
 
+  /acorn-import-assertions@1.9.0(acorn@8.11.2):
+    resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
+    peerDependencies:
+      acorn: ^8
+    dependencies:
+      acorn: 8.11.2
+    dev: true
+
   /acorn-jsx@5.3.2(acorn@8.11.2):
     resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
     peerDependencies:
@@ -2403,6 +2643,14 @@ packages:
       indent-string: 4.0.0
     dev: true
 
+  /ajv-keywords@3.5.2(ajv@6.12.6):
+    resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
+    peerDependencies:
+      ajv: ^6.9.1
+    dependencies:
+      ajv: 6.12.6
+    dev: true
+
   /ajv@6.12.6:
     resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
     dependencies:
@@ -2412,6 +2660,11 @@ packages:
       uri-js: 4.4.1
     dev: true
 
+  /ansi-colors@4.1.1:
+    resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==}
+    engines: {node: '>=6'}
+    dev: true
+
   /ansi-escapes@6.2.0:
     resolution: {integrity: sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==}
     engines: {node: '>=14.16'}
@@ -2557,6 +2810,10 @@ packages:
       fill-range: 7.0.1
     dev: true
 
+  /browser-stdout@1.3.1:
+    resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==}
+    dev: true
+
   /browserslist@4.22.2:
     resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==}
     engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -2655,6 +2912,11 @@ packages:
     engines: {node: '>=6'}
     dev: true
 
+  /camelcase@6.3.0:
+    resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+    engines: {node: '>=10'}
+    dev: true
+
   /caniuse-api@3.0.0:
     resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
     dependencies:
@@ -2679,6 +2941,7 @@ packages:
 
   /ccount@2.0.1:
     resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+    dev: true
 
   /chai@4.3.10:
     resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==}
@@ -2718,6 +2981,7 @@ packages:
 
   /character-entities-html4@2.1.0:
     resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
+    dev: true
 
   /character-entities-legacy@1.1.4:
     resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==}
@@ -2725,6 +2989,7 @@ packages:
 
   /character-entities-legacy@3.0.0:
     resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
+    dev: true
 
   /character-entities@1.2.4:
     resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==}
@@ -2764,6 +3029,11 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
+  /chrome-trace-event@1.0.3:
+    resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
+    engines: {node: '>=6.0'}
+    dev: true
+
   /ci-info@3.9.0:
     resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
     engines: {node: '>=8'}
@@ -2814,6 +3084,14 @@ packages:
       string-width: 7.0.0
     dev: true
 
+  /cliui@7.0.4:
+    resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 7.0.0
+    dev: true
+
   /cliui@8.0.1:
     resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
     engines: {node: '>=12'}
@@ -2823,6 +3101,15 @@ packages:
       wrap-ansi: 7.0.0
     dev: true
 
+  /clone-deep@4.0.1:
+    resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==}
+    engines: {node: '>=6'}
+    dependencies:
+      is-plain-object: 2.0.4
+      kind-of: 6.0.3
+      shallow-clone: 3.0.1
+    dev: true
+
   /color-convert@1.9.3:
     resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
     requiresBuild: true
@@ -2860,6 +3147,7 @@ packages:
 
   /comma-separated-tokens@2.0.3:
     resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
+    dev: true
 
   /commander@11.1.0:
     resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
@@ -2902,6 +3190,21 @@ packages:
     engines: {node: '>= 0.6'}
     dev: true
 
+  /copy-webpack-plugin@9.1.0(webpack@5.89.0):
+    resolution: {integrity: sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA==}
+    engines: {node: '>= 12.13.0'}
+    peerDependencies:
+      webpack: ^5.1.0
+    dependencies:
+      fast-glob: 3.3.2
+      glob-parent: 6.0.2
+      globby: 11.1.0
+      normalize-path: 3.0.0
+      schema-utils: 3.3.0
+      serialize-javascript: 6.0.1
+      webpack: 5.89.0(esbuild@0.19.8)(webpack-cli@4.10.0)
+    dev: true
+
   /cross-spawn@7.0.3:
     resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
     engines: {node: '>= 8'}
@@ -3037,6 +3340,19 @@ packages:
       ms: 2.1.3
     dev: true
 
+  /debug@4.3.3(supports-color@8.1.1):
+    resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.1.2
+      supports-color: 8.1.1
+    dev: true
+
   /debug@4.3.4:
     resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
     engines: {node: '>=6.0'}
@@ -3048,6 +3364,11 @@ packages:
     dependencies:
       ms: 2.1.2
 
+  /decamelize@4.0.0:
+    resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==}
+    engines: {node: '>=10'}
+    dev: true
+
   /decode-named-character-reference@1.0.2:
     resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
     dependencies:
@@ -3120,6 +3441,11 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dev: true
 
+  /diff@5.0.0:
+    resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==}
+    engines: {node: '>=0.3.1'}
+    dev: true
+
   /dir-glob@3.0.1:
     resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
     engines: {node: '>=8'}
@@ -3201,6 +3527,14 @@ packages:
     dev: true
     optional: true
 
+  /enhanced-resolve@5.15.0:
+    resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==}
+    engines: {node: '>=10.13.0'}
+    dependencies:
+      graceful-fs: 4.2.11
+      tapable: 2.2.1
+    dev: true
+
   /entities@4.5.0:
     resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
     engines: {node: '>=0.12'}
@@ -3210,6 +3544,12 @@ packages:
     engines: {node: '>=6'}
     dev: true
 
+  /envinfo@7.11.0:
+    resolution: {integrity: sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: true
+
   /err-code@2.0.3:
     resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==}
     dev: true
@@ -3694,6 +4034,14 @@ packages:
     engines: {node: '>=4.0.0'}
     dev: true
 
+  /eslint-scope@5.1.1:
+    resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
+    engines: {node: '>=8.0.0'}
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 4.3.0
+    dev: true
+
   /eslint-scope@7.2.2:
     resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -3795,6 +4143,11 @@ packages:
       estraverse: 5.3.0
     dev: true
 
+  /estraverse@4.3.0:
+    resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
+    engines: {node: '>=4.0'}
+    dev: true
+
   /estraverse@5.3.0:
     resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
     engines: {node: '>=4.0'}
@@ -3817,6 +4170,11 @@ packages:
     resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
     dev: true
 
+  /events@3.3.0:
+    resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+    engines: {node: '>=0.8.x'}
+    dev: true
+
   /execa@5.1.1:
     resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
     engines: {node: '>=10'}
@@ -3901,6 +4259,11 @@ packages:
     resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
     dev: true
 
+  /fastest-levenshtein@1.0.16:
+    resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==}
+    engines: {node: '>= 4.9.1'}
+    dev: true
+
   /fastq@1.15.0:
     resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
     dependencies:
@@ -3926,6 +4289,15 @@ packages:
       to-regex-range: 5.0.1
     dev: true
 
+  /find-cache-dir@3.3.2:
+    resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
+    engines: {node: '>=8'}
+    dependencies:
+      commondir: 1.0.1
+      make-dir: 3.1.0
+      pkg-dir: 4.2.0
+    dev: true
+
   /find-up@4.1.0:
     resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
     engines: {node: '>=8'}
@@ -3972,6 +4344,15 @@ packages:
     resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
     dev: true
 
+  /fs-extra@10.1.0:
+    resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      graceful-fs: 4.2.11
+      jsonfile: 6.1.0
+      universalify: 2.0.1
+    dev: true
+
   /fs-extra@11.2.0:
     resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
     engines: {node: '>=14.14'}
@@ -4107,6 +4488,17 @@ packages:
       path-scurry: 1.10.1
     dev: true
 
+  /glob@7.2.0:
+    resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==}
+    dependencies:
+      fs.realpath: 1.0.0
+      inflight: 1.0.6
+      inherits: 2.0.4
+      minimatch: 3.1.2
+      once: 1.4.0
+      path-is-absolute: 1.0.1
+    dev: true
+
   /glob@7.2.3:
     resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
     dependencies:
@@ -4186,6 +4578,11 @@ packages:
     resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
     dev: true
 
+  /growl@1.10.5:
+    resolution: {integrity: sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==}
+    engines: {node: '>=4.x'}
+    dev: true
+
   /has-flag@3.0.0:
     resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
     engines: {node: '>=4'}
@@ -4226,11 +4623,13 @@ packages:
       vfile: 6.0.1
       vfile-location: 5.0.1
       web-namespaces: 2.0.1
+    dev: true
 
   /hast-util-parse-selector@4.0.0:
     resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
     dependencies:
       '@types/hast': 3.0.3
+    dev: true
 
   /hast-util-raw@9.0.0:
     resolution: {integrity: sha512-HRjOZeaRAOM66qU/zmJ1fI/dKQovAFi9Fpq0bBW/6P/ZDdyQnFyu/ZMPk+CXmUEgE0AaqP1HNgTdg+N5JyvNeA==}
@@ -4247,6 +4646,7 @@ packages:
       vfile: 6.0.1
       web-namespaces: 2.0.1
       zwitch: 2.0.4
+    dev: true
 
   /hast-util-to-html@9.0.0:
     resolution: {integrity: sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw==}
@@ -4263,6 +4663,7 @@ packages:
       space-separated-tokens: 2.0.2
       stringify-entities: 4.0.3
       zwitch: 2.0.4
+    dev: true
 
   /hast-util-to-parse5@8.0.0:
     resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==}
@@ -4274,6 +4675,7 @@ packages:
       space-separated-tokens: 2.0.2
       web-namespaces: 2.0.1
       zwitch: 2.0.4
+    dev: true
 
   /hast-util-to-string@3.0.0:
     resolution: {integrity: sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA==}
@@ -4285,6 +4687,7 @@ packages:
     resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
     dependencies:
       '@types/hast': 3.0.3
+    dev: true
 
   /hastscript@8.0.0:
     resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==}
@@ -4294,6 +4697,12 @@ packages:
       hast-util-parse-selector: 4.0.0
       property-information: 6.2.0
       space-separated-tokens: 2.0.2
+    dev: true
+
+  /he@1.2.0:
+    resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+    hasBin: true
+    dev: true
 
   /hookable@5.5.3:
     resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
@@ -4316,6 +4725,7 @@ packages:
 
   /html-void-elements@3.0.0:
     resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
+    dev: true
 
   /http-cache-semantics@4.1.1:
     resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
@@ -4385,6 +4795,15 @@ packages:
       resolve-from: 4.0.0
     dev: true
 
+  /import-local@3.1.0:
+    resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==}
+    engines: {node: '>=8'}
+    hasBin: true
+    dependencies:
+      pkg-dir: 4.2.0
+      resolve-cwd: 3.0.0
+    dev: true
+
   /imurmurhash@0.1.4:
     resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
     engines: {node: '>=0.8.19'}
@@ -4411,6 +4830,11 @@ packages:
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dev: true
 
+  /interpret@2.2.0:
+    resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==}
+    engines: {node: '>= 0.10'}
+    dev: true
+
   /ip@2.0.0:
     resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==}
     dev: true
@@ -4525,10 +4949,22 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /is-plain-obj@2.1.0:
+    resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
+    engines: {node: '>=8'}
+    dev: true
+
   /is-plain-obj@4.1.0:
     resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
     engines: {node: '>=12'}
 
+  /is-plain-object@2.0.4:
+    resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      isobject: 3.0.1
+    dev: true
+
   /is-plain-object@3.0.1:
     resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==}
     engines: {node: '>=0.10.0'}
@@ -4550,6 +4986,11 @@ packages:
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
     dev: true
 
+  /is-unicode-supported@0.1.0:
+    resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
+    engines: {node: '>=10'}
+    dev: true
+
   /is-wsl@2.2.0:
     resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
     engines: {node: '>=8'}
@@ -4566,6 +5007,11 @@ packages:
     engines: {node: '>=16'}
     dev: true
 
+  /isobject@3.0.1:
+    resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
   /istanbul-lib-coverage@3.2.2:
     resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
     engines: {node: '>=8'}
@@ -4608,6 +5054,15 @@ packages:
       '@pkgjs/parseargs': 0.11.0
     dev: true
 
+  /jest-worker@27.5.1:
+    resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
+    engines: {node: '>= 10.13.0'}
+    dependencies:
+      '@types/node': 20.10.4
+      merge-stream: 2.0.0
+      supports-color: 8.1.1
+    dev: true
+
   /jiti@1.21.0:
     resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==}
     hasBin: true
@@ -4713,6 +5168,11 @@ packages:
       json-buffer: 3.0.1
     dev: true
 
+  /kind-of@6.0.3:
+    resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
   /kleur@3.0.3:
     resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
     engines: {node: '>=6'}
@@ -4776,6 +5236,11 @@ packages:
       wrap-ansi: 9.0.0
     dev: true
 
+  /loader-runner@4.3.0:
+    resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
+    engines: {node: '>=6.11.5'}
+    dev: true
+
   /local-pkg@0.5.0:
     resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
     engines: {node: '>=14'}
@@ -4814,6 +5279,14 @@ packages:
     resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
     dev: true
 
+  /log-symbols@4.1.0:
+    resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
+    engines: {node: '>=10'}
+    dependencies:
+      chalk: 4.1.2
+      is-unicode-supported: 0.1.0
+    dev: true
+
   /log-update@6.0.0:
     resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==}
     engines: {node: '>=18'}
@@ -4874,6 +5347,13 @@ packages:
       source-map-js: 1.0.2
     dev: true
 
+  /make-dir@3.1.0:
+    resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
+    engines: {node: '>=8'}
+    dependencies:
+      semver: 6.3.1
+    dev: true
+
   /make-dir@4.0.0:
     resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
     engines: {node: '>=10'}
@@ -4953,6 +5433,7 @@ packages:
       trim-lines: 3.0.1
       unist-util-position: 5.0.0
       unist-util-visit: 5.0.0
+    dev: true
 
   /mdast-util-to-string@2.0.0:
     resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==}
@@ -5052,6 +5533,7 @@ packages:
     dependencies:
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
+    dev: true
 
   /micromark-util-chunked@2.0.0:
     resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==}
@@ -5091,6 +5573,7 @@ packages:
 
   /micromark-util-encode@2.0.0:
     resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
+    dev: true
 
   /micromark-util-html-tag-name@2.0.0:
     resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==}
@@ -5114,6 +5597,7 @@ packages:
       micromark-util-character: 2.0.1
       micromark-util-encode: 2.0.0
       micromark-util-symbol: 2.0.0
+    dev: true
 
   /micromark-util-subtokenize@2.0.0:
     resolution: {integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==}
@@ -5126,9 +5610,11 @@ packages:
 
   /micromark-util-symbol@2.0.0:
     resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
+    dev: true
 
   /micromark-util-types@2.0.0:
     resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
+    dev: true
 
   /micromark@2.11.4:
     resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==}
@@ -5171,6 +5657,18 @@ packages:
       picomatch: 2.3.1
     dev: true
 
+  /mime-db@1.52.0:
+    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /mime-types@2.1.35:
+    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+    engines: {node: '>= 0.6'}
+    dependencies:
+      mime-db: 1.52.0
+    dev: true
+
   /mime@3.0.0:
     resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
     engines: {node: '>=10.0.0'}
@@ -5221,6 +5719,13 @@ packages:
       brace-expansion: 1.1.11
     dev: true
 
+  /minimatch@4.2.1:
+    resolution: {integrity: sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==}
+    engines: {node: '>=10'}
+    dependencies:
+      brace-expansion: 1.1.11
+    dev: true
+
   /minimatch@5.1.6:
     resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
     engines: {node: '>=10'}
@@ -5349,6 +5854,37 @@ packages:
       ufo: 1.3.2
     dev: true
 
+  /mocha@9.2.2:
+    resolution: {integrity: sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==}
+    engines: {node: '>= 12.0.0'}
+    hasBin: true
+    dependencies:
+      '@ungap/promise-all-settled': 1.1.2
+      ansi-colors: 4.1.1
+      browser-stdout: 1.3.1
+      chokidar: 3.5.3
+      debug: 4.3.3(supports-color@8.1.1)
+      diff: 5.0.0
+      escape-string-regexp: 4.0.0
+      find-up: 5.0.0
+      glob: 7.2.0
+      growl: 1.10.5
+      he: 1.2.0
+      js-yaml: 4.1.0
+      log-symbols: 4.1.0
+      minimatch: 4.2.1
+      ms: 2.1.3
+      nanoid: 3.3.1
+      serialize-javascript: 6.0.0
+      strip-json-comments: 3.1.1
+      supports-color: 8.1.1
+      which: 2.0.2
+      workerpool: 6.2.0
+      yargs: 16.2.0
+      yargs-parser: 20.2.4
+      yargs-unparser: 2.0.0
+    dev: true
+
   /mri@1.2.0:
     resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
     engines: {node: '>=4'}
@@ -5366,6 +5902,12 @@ packages:
     hasBin: true
     dev: true
 
+  /nanoid@3.3.1:
+    resolution: {integrity: sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+    dev: true
+
   /nanoid@3.3.7:
     resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -5385,6 +5927,10 @@ packages:
     engines: {node: '>= 0.6'}
     dev: true
 
+  /neo-async@2.6.2:
+    resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
+    dev: true
+
   /node-fetch-native@1.4.1:
     resolution: {integrity: sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w==}
     dev: true
@@ -5693,6 +6239,7 @@ packages:
     resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
     dependencies:
       entities: 4.5.0
+    dev: true
 
   /path-exists@4.0.0:
     resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
@@ -5762,6 +6309,13 @@ packages:
     hasBin: true
     dev: true
 
+  /pkg-dir@4.2.0:
+    resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      find-up: 4.1.0
+    dev: true
+
   /pkg-types@1.0.3:
     resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
     dependencies:
@@ -6162,6 +6716,7 @@ packages:
 
   /property-information@6.2.0:
     resolution: {integrity: sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==}
+    dev: true
 
   /punycode.js@2.3.1:
     resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
@@ -6238,6 +6793,13 @@ packages:
       picomatch: 2.3.1
     dev: true
 
+  /rechoir@0.7.1:
+    resolution: {integrity: sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==}
+    engines: {node: '>= 0.10'}
+    dependencies:
+      resolve: 1.22.8
+    dev: true
+
   /regexp-tree@0.1.27:
     resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
     hasBin: true
@@ -6284,11 +6846,23 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /resolve-cwd@3.0.0:
+    resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      resolve-from: 5.0.0
+    dev: true
+
   /resolve-from@4.0.0:
     resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
     engines: {node: '>=4'}
     dev: true
 
+  /resolve-from@5.0.0:
+    resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+    engines: {node: '>=8'}
+    dev: true
+
   /resolve-pkg-maps@1.0.0:
     resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
     dev: true
@@ -6415,6 +6989,21 @@ packages:
       rollup-plugin-inject: 3.0.2
     dev: true
 
+  /rollup-plugin-typescript2@0.36.0(rollup@4.9.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-NB2CSQDxSe9+Oe2ahZbf+B4bh7pHwjV5L+RSYpCu7Q5ROuN94F9b6ioWwKfz3ueL3KTtmX4o2MUH2cgHDIEUsw==}
+    peerDependencies:
+      rollup: '>=1.26.3'
+      typescript: '>=2.4.0'
+    dependencies:
+      '@rollup/pluginutils': 4.2.1
+      find-cache-dir: 3.3.2
+      fs-extra: 10.1.0
+      rollup: 4.9.0
+      semver: 7.5.4
+      tslib: 2.6.2
+      typescript: 5.3.3
+    dev: true
+
   /rollup-pluginutils@2.8.2:
     resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==}
     dependencies:
@@ -6473,6 +7062,15 @@ packages:
     dev: true
     optional: true
 
+  /schema-utils@3.3.0:
+    resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==}
+    engines: {node: '>= 10.13.0'}
+    dependencies:
+      '@types/json-schema': 7.0.15
+      ajv: 6.12.6
+      ajv-keywords: 3.5.2(ajv@6.12.6)
+    dev: true
+
   /scule@1.1.1:
     resolution: {integrity: sha512-sHtm/SsIK9BUBI3EFT/Gnp9VoKfY6QLvlkvAE6YK7454IF8FSgJEAnJpVdSC7K5/pjI5NfxhzBLW2JAfYA/shQ==}
     dev: true
@@ -6503,12 +7101,25 @@ packages:
       lru-cache: 6.0.0
     dev: true
 
+  /serialize-javascript@6.0.0:
+    resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==}
+    dependencies:
+      randombytes: 2.1.0
+    dev: true
+
   /serialize-javascript@6.0.1:
     resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==}
     dependencies:
       randombytes: 2.1.0
     dev: true
 
+  /shallow-clone@3.0.1:
+    resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==}
+    engines: {node: '>=8'}
+    dependencies:
+      kind-of: 6.0.3
+    dev: true
+
   /shebang-command@2.0.0:
     resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
     engines: {node: '>=8'}
@@ -6665,6 +7276,7 @@ packages:
 
   /space-separated-tokens@2.0.2:
     resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+    dev: true
 
   /spdx-correct@3.2.0:
     resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
@@ -6752,6 +7364,7 @@ packages:
     dependencies:
       character-entities-html4: 2.1.0
       character-entities-legacy: 3.0.0
+    dev: true
 
   /strip-ansi@6.0.1:
     resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
@@ -6821,6 +7434,13 @@ packages:
       has-flag: 4.0.0
     dev: true
 
+  /supports-color@8.1.1:
+    resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+    engines: {node: '>=10'}
+    dependencies:
+      has-flag: 4.0.0
+    dev: true
+
   /supports-preserve-symlinks-flag@1.0.0:
     resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
     engines: {node: '>= 0.4'}
@@ -6848,6 +7468,11 @@ packages:
       tslib: 2.6.2
     dev: true
 
+  /tapable@2.2.1:
+    resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+    engines: {node: '>=6'}
+    dev: true
+
   /tar@6.2.0:
     resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==}
     engines: {node: '>=10'}
@@ -6881,6 +7506,31 @@ packages:
       - supports-color
     dev: true
 
+  /terser-webpack-plugin@5.3.9(esbuild@0.19.8)(webpack@5.89.0):
+    resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==}
+    engines: {node: '>= 10.13.0'}
+    peerDependencies:
+      '@swc/core': '*'
+      esbuild: '*'
+      uglify-js: '*'
+      webpack: ^5.1.0
+    peerDependenciesMeta:
+      '@swc/core':
+        optional: true
+      esbuild:
+        optional: true
+      uglify-js:
+        optional: true
+    dependencies:
+      '@jridgewell/trace-mapping': 0.3.20
+      esbuild: 0.19.8
+      jest-worker: 27.5.1
+      schema-utils: 3.3.0
+      serialize-javascript: 6.0.1
+      terser: 5.26.0
+      webpack: 5.89.0(esbuild@0.19.8)(webpack-cli@4.10.0)
+    dev: true
+
   /terser@5.26.0:
     resolution: {integrity: sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==}
     engines: {node: '>=10'}
@@ -6945,6 +7595,7 @@ packages:
 
   /trim-lines@3.0.1:
     resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+    dev: true
 
   /trough@2.1.0:
     resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==}
@@ -7016,6 +7667,12 @@ packages:
     engines: {node: '>=14.16'}
     dev: true
 
+  /typescript@4.9.5:
+    resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
+    engines: {node: '>=4.2.0'}
+    hasBin: true
+    dev: true
+
   /typescript@5.3.3:
     resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
     engines: {node: '>=14.17'}
@@ -7121,6 +7778,7 @@ packages:
     resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
     dependencies:
       '@types/unist': 3.0.2
+    dev: true
 
   /unist-util-stringify-position@2.0.3:
     resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==}
@@ -7225,6 +7883,7 @@ packages:
     dependencies:
       '@types/unist': 3.0.2
       vfile: 6.0.1
+    dev: true
 
   /vfile-message@4.0.2:
     resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
@@ -7365,10 +8024,6 @@ packages:
     resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
     dev: true
 
-  /vscode-textmate@9.0.0:
-    resolution: {integrity: sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg==}
-    dev: true
-
   /vue-eslint-parser@9.3.2(eslint-ts-patch@8.55.0-1):
     resolution: {integrity: sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==}
     engines: {node: ^14.17.0 || >=16.0.0}
@@ -7391,8 +8046,107 @@ packages:
     resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==}
     dev: true
 
+  /watchpack@2.4.0:
+    resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
+    engines: {node: '>=10.13.0'}
+    dependencies:
+      glob-to-regexp: 0.4.1
+      graceful-fs: 4.2.11
+    dev: true
+
   /web-namespaces@2.0.1:
     resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
+    dev: true
+
+  /webpack-cli@4.10.0(webpack@5.89.0):
+    resolution: {integrity: sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+    peerDependencies:
+      '@webpack-cli/generators': '*'
+      '@webpack-cli/migrate': '*'
+      webpack: 4.x.x || 5.x.x
+      webpack-bundle-analyzer: '*'
+      webpack-dev-server: '*'
+    peerDependenciesMeta:
+      '@webpack-cli/generators':
+        optional: true
+      '@webpack-cli/migrate':
+        optional: true
+      webpack-bundle-analyzer:
+        optional: true
+      webpack-dev-server:
+        optional: true
+    dependencies:
+      '@discoveryjs/json-ext': 0.5.7
+      '@webpack-cli/configtest': 1.2.0(webpack-cli@4.10.0)(webpack@5.89.0)
+      '@webpack-cli/info': 1.5.0(webpack-cli@4.10.0)
+      '@webpack-cli/serve': 1.7.0(webpack-cli@4.10.0)
+      colorette: 2.0.20
+      commander: 7.2.0
+      cross-spawn: 7.0.3
+      fastest-levenshtein: 1.0.16
+      import-local: 3.1.0
+      interpret: 2.2.0
+      rechoir: 0.7.1
+      webpack: 5.89.0(esbuild@0.19.8)(webpack-cli@4.10.0)
+      webpack-merge: 5.10.0
+    dev: true
+
+  /webpack-merge@5.10.0:
+    resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==}
+    engines: {node: '>=10.0.0'}
+    dependencies:
+      clone-deep: 4.0.1
+      flat: 5.0.2
+      wildcard: 2.0.1
+    dev: true
+
+  /webpack-sources@3.2.3:
+    resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
+    engines: {node: '>=10.13.0'}
+    dev: true
+
+  /webpack@5.89.0(esbuild@0.19.8)(webpack-cli@4.10.0):
+    resolution: {integrity: sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+    peerDependencies:
+      webpack-cli: '*'
+    peerDependenciesMeta:
+      webpack-cli:
+        optional: true
+    dependencies:
+      '@types/eslint-scope': 3.7.7
+      '@types/estree': 1.0.5
+      '@webassemblyjs/ast': 1.11.6
+      '@webassemblyjs/wasm-edit': 1.11.6
+      '@webassemblyjs/wasm-parser': 1.11.6
+      acorn: 8.11.2
+      acorn-import-assertions: 1.9.0(acorn@8.11.2)
+      browserslist: 4.22.2
+      chrome-trace-event: 1.0.3
+      enhanced-resolve: 5.15.0
+      es-module-lexer: 1.4.1
+      eslint-scope: 5.1.1
+      events: 3.3.0
+      glob-to-regexp: 0.4.1
+      graceful-fs: 4.2.11
+      json-parse-even-better-errors: 2.3.1
+      loader-runner: 4.3.0
+      mime-types: 2.1.35
+      neo-async: 2.6.2
+      schema-utils: 3.3.0
+      tapable: 2.2.1
+      terser-webpack-plugin: 5.3.9(esbuild@0.19.8)(webpack@5.89.0)
+      watchpack: 2.4.0
+      webpack-cli: 4.10.0(webpack@5.89.0)
+      webpack-sources: 3.2.3
+    transitivePeerDependencies:
+      - '@swc/core'
+      - esbuild
+      - uglify-js
+    dev: true
 
   /which@2.0.2:
     resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
@@ -7419,6 +8173,10 @@ packages:
       stackback: 0.0.2
     dev: true
 
+  /wildcard@2.0.1:
+    resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==}
+    dev: true
+
   /workerd@1.20231030.0:
     resolution: {integrity: sha512-+FSW+d31f8RrjHanFf/R9A+Z0csf3OtsvzdPmAKuwuZm/5HrBv83cvG9fFeTxl7/nI6irUUXIRF9xcj/NomQzQ==}
     engines: {node: '>=16'}
@@ -7432,6 +8190,10 @@ packages:
       '@cloudflare/workerd-windows-64': 1.20231030.0
     dev: true
 
+  /workerpool@6.2.0:
+    resolution: {integrity: sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==}
+    dev: true
+
   /wrangler@3.20.0:
     resolution: {integrity: sha512-7mg25zJByhBmrfG+CbImSid7JNd5lxGovLA167ndtE8Yrqd3TUukrGWL8o0RCQIm0FUcgl2nCzWArJDShlZVKA==}
     engines: {node: '>=16.17.0'}
@@ -7539,11 +8301,39 @@ packages:
     engines: {node: '>= 14'}
     dev: true
 
+  /yargs-parser@20.2.4:
+    resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==}
+    engines: {node: '>=10'}
+    dev: true
+
   /yargs-parser@21.1.1:
     resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
     engines: {node: '>=12'}
     dev: true
 
+  /yargs-unparser@2.0.0:
+    resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==}
+    engines: {node: '>=10'}
+    dependencies:
+      camelcase: 6.3.0
+      decamelize: 4.0.0
+      flat: 5.0.2
+      is-plain-obj: 2.1.0
+    dev: true
+
+  /yargs@16.2.0:
+    resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
+    engines: {node: '>=10'}
+    dependencies:
+      cliui: 7.0.4
+      escalade: 3.1.1
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      string-width: 4.2.3
+      y18n: 5.0.8
+      yargs-parser: 20.2.4
+    dev: true
+
   /yargs@17.7.2:
     resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
     engines: {node: '>=12'}
@@ -7581,3 +8371,4 @@ packages:
 
   /zwitch@2.0.4:
     resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+    dev: true
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index bfb86b426..11cdfdd8c 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -2,3 +2,4 @@ packages:
   - packages/*
   - playground
   - examples/*
+  - packages/shikiji-core/vendor/*
diff --git a/tsconfig.json b/tsconfig.json
index 5f633377d..7a8ac05ca 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,23 +6,30 @@
     "module": "esnext",
     "moduleResolution": "Bundler",
     "paths": {
-      "shikiji": ["./packages/shikiji/src/index.ts"],
-      "shikiji/core": ["./packages/shikiji/src/core/index.ts"],
+      "shikiji-core/types": ["./packages/shikiji-core/src/types.ts"],
+      "shikiji-core/wasm": ["./packages/shikiji-core/src/wasm.ts"],
+      "shikiji-core": ["./packages/shikiji-core/src/index.ts"],
       "shikiji-transformers": ["./packages/shikiji-transformers/src/index.ts"],
       "shikiji-twoslash": ["./packages/shikiji-twoslash/src/index.ts"],
+      "shikiji/core": ["./packages/shikiji/src/core.ts"],
+      "shikiji": ["./packages/shikiji/src/index.ts"],
       "markdown-it-shikiji": ["./packages/markdown-it-shikiji/src/index.ts"]
     },
     "resolveJsonModule": true,
+    "allowJs": true,
     "strict": true,
     "strictNullChecks": true,
+    "preserveValueImports": false,
     "esModuleInterop": true,
     "skipDefaultLibCheck": true,
     "skipLibCheck": true
   },
   "include": [
-    "**/*.ts"
+    "**/*.ts",
+    "**/*.mjs"
   ],
   "exclude": [
+    "**/vendor/**",
     "**/node_modules/**",
     "**/dist/**",
     "**/fixtures/**"
diff --git a/vitest.config.ts b/vitest.config.ts
index a4da8091a..af44db2ec 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -1,8 +1,6 @@
 import { fileURLToPath } from 'node:url'
 import { defineConfig } from 'vitest/config'
-
-// @ts-expect-error - no types
-import { wasmPlugin } from './packages/shikiji/rollup.config.mjs'
+import { wasmPlugin } from './packages/shikiji-core/rollup.config.mjs'
 
 export default defineConfig({
   plugins: [
@@ -10,15 +8,20 @@ export default defineConfig({
   ],
   resolve: {
     alias: {
-      'shikiji/core': fileURLToPath(new URL('./packages/shikiji/src/core/index.ts', import.meta.url)),
+      'shikiji-core/wasm': fileURLToPath(new URL('./packages/shikiji-core/src/wasm.ts', import.meta.url)),
+      'shikiji-core/types': fileURLToPath(new URL('./packages/shikiji-core/src/types.ts', import.meta.url)),
+      'shikiji-core': fileURLToPath(new URL('./packages/shikiji-core/src/index.ts', import.meta.url)),
       'shikiji-transformers': fileURLToPath(new URL('./packages/shikiji-transformers/src/index.ts', import.meta.url)),
       'shikiji-compat': fileURLToPath(new URL('./packages/shikiji-compat/src/index.ts', import.meta.url)),
       'shikiji-twoslash': fileURLToPath(new URL('./packages/shikiji-twoslash/src/index.ts', import.meta.url)),
       'markdown-it-shikiji': fileURLToPath(new URL('./packages/markdown-it-shikiji/src/index.ts', import.meta.url)),
+      'shikiji/wasm': fileURLToPath(new URL('./packages/shikiji/src/wasm.ts', import.meta.url)),
+      'shikiji/core': fileURLToPath(new URL('./packages/shikiji/src/core.ts', import.meta.url)),
       'shikiji': fileURLToPath(new URL('./packages/shikiji/src/index.ts', import.meta.url)),
     },
   },
   test: {
+    exclude: ['**/vendor/**', '**/node_modules/**'],
     server: {
       deps: {
         inline: [
@@ -36,6 +39,8 @@ export default defineConfig({
         '**/src/assets/**',
         '**/node_modules/**',
         '**/dist/**',
+        '**/scripts/**',
+        '**/vendor/**',
         '**/stackElementMetadata.ts',
       ],
     },