Compare commits
11 commits
Author | SHA1 | Date | |
---|---|---|---|
|
b491e6bc5c | ||
|
8006f98550 | ||
|
1a8a3179da | ||
|
2fd5d7ce43 | ||
|
e6ad081950 | ||
|
e0ae877384 | ||
|
0387b8518a | ||
|
ef5a72ecae | ||
|
6e497d87b1 | ||
|
704a75f473 | ||
|
d5d2d3414a |
23 changed files with 464 additions and 585 deletions
33
.changeset/few-peas-hunt.md
Normal file
33
.changeset/few-peas-hunt.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
'@astrojs/markdoc': minor
|
||||||
|
'@astrojs/markdown-remark': minor
|
||||||
|
'astro': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Updates the internal `shiki` syntax highlighter to `shikiji`, an ESM-focused alternative that simplifies bundling and maintenance.
|
||||||
|
|
||||||
|
There are no new options and no changes to how you author code blocks and syntax highlighting.
|
||||||
|
|
||||||
|
**Potentially breaking change:** While this refactor should be transparent for most projects, the transition to `shikiji` now produces a smaller HTML markup by attaching a fallback `color` style to the `pre` or `code` element, instead of to the line `span` directly. For example:
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<code class="astro-code" style="background-color: #24292e">
|
||||||
|
<pre>
|
||||||
|
<span class="line" style="color: #e1e4e8">my code</span>
|
||||||
|
</pre>
|
||||||
|
</code>
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<code class="astro-code" style="background-color: #24292e; color: #e1e4e8">
|
||||||
|
<pre>
|
||||||
|
<span class="line">my code<span>
|
||||||
|
</pre>
|
||||||
|
</code>
|
||||||
|
```
|
||||||
|
|
||||||
|
This does not affect the colors as the `span` will inherit the `color` from the parent, but if you're relying on a specific HTML markup, please check your site carefully after upgrading to verify the styles.
|
|
@ -1,7 +1,14 @@
|
||||||
---
|
---
|
||||||
import type * as shiki from 'shiki';
|
import type {
|
||||||
import { renderToHtml } from 'shiki';
|
BuiltinLanguage,
|
||||||
import { getHighlighter } from './Shiki.js';
|
BuiltinTheme,
|
||||||
|
LanguageRegistration,
|
||||||
|
SpecialLanguage,
|
||||||
|
ThemeRegistration,
|
||||||
|
ThemeRegistrationRaw,
|
||||||
|
} from 'shikiji';
|
||||||
|
import { visit } from 'unist-util-visit';
|
||||||
|
import { getCachedHighlighter, replaceCssVariables } from './shiki.js';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/** The code to highlight. Required. */
|
/** The code to highlight. Required. */
|
||||||
|
@ -13,7 +20,7 @@ interface Props {
|
||||||
*
|
*
|
||||||
* @default "plaintext"
|
* @default "plaintext"
|
||||||
*/
|
*/
|
||||||
lang?: shiki.Lang | shiki.ILanguageRegistration;
|
lang?: BuiltinLanguage | SpecialLanguage | LanguageRegistration;
|
||||||
/**
|
/**
|
||||||
* The styling theme.
|
* The styling theme.
|
||||||
* Supports all themes listed here: https://github.com/shikijs/shiki/blob/main/docs/themes.md#all-themes
|
* Supports all themes listed here: https://github.com/shikijs/shiki/blob/main/docs/themes.md#all-themes
|
||||||
|
@ -21,7 +28,7 @@ interface Props {
|
||||||
*
|
*
|
||||||
* @default "github-dark"
|
* @default "github-dark"
|
||||||
*/
|
*/
|
||||||
theme?: shiki.IThemeRegistration;
|
theme?: BuiltinTheme | ThemeRegistration | ThemeRegistrationRaw;
|
||||||
/**
|
/**
|
||||||
* Enable word wrapping.
|
* Enable word wrapping.
|
||||||
* - true: enabled.
|
* - true: enabled.
|
||||||
|
@ -47,41 +54,65 @@ const {
|
||||||
inline = false,
|
inline = false,
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
|
|
||||||
// 1. Get the shiki syntax highlighter
|
// shiki -> shikiji compat
|
||||||
const highlighter = await getHighlighter({
|
if (typeof lang === 'object') {
|
||||||
theme,
|
// `id` renamed to `name
|
||||||
// Load custom lang if passed an object, otherwise load the default
|
if ((lang as any).id && !lang.name) {
|
||||||
langs: typeof lang !== 'string' ? [lang] : undefined,
|
lang.name = (lang as any).id;
|
||||||
|
}
|
||||||
|
// `grammar` flattened to lang itself
|
||||||
|
if ((lang as any).grammar) {
|
||||||
|
Object.assign(lang, (lang as any).grammar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const highlighter = await getCachedHighlighter({
|
||||||
|
langs: [lang],
|
||||||
|
themes: [theme],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Turn code into shiki theme tokens
|
const html = highlighter.codeToHtml(code, {
|
||||||
const tokens = highlighter.codeToThemedTokens(code, typeof lang === 'string' ? lang : lang.id);
|
lang: typeof lang === 'string' ? lang : lang.name,
|
||||||
|
theme,
|
||||||
// 3. Get shiki theme object
|
transforms: {
|
||||||
const _theme = highlighter.getTheme();
|
pre(node) {
|
||||||
|
|
||||||
// 4. Render the theme tokens as html
|
|
||||||
const html = renderToHtml(tokens, {
|
|
||||||
themeName: _theme.name,
|
|
||||||
fg: _theme.fg,
|
|
||||||
bg: _theme.bg,
|
|
||||||
elements: {
|
|
||||||
pre({ className, style, children }) {
|
|
||||||
// Swap to `code` tag if inline
|
// Swap to `code` tag if inline
|
||||||
const tag = inline ? 'code' : 'pre';
|
if (inline) {
|
||||||
|
node.tagName = 'code';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cast to string as shikiji will always pass them as strings instead of any other types
|
||||||
|
const classValue = (node.properties.class as string) ?? '';
|
||||||
|
const styleValue = (node.properties.style as string) ?? '';
|
||||||
|
|
||||||
// Replace "shiki" class naming with "astro-code"
|
// Replace "shiki" class naming with "astro-code"
|
||||||
className = className.replace(/shiki/g, 'astro-code');
|
node.properties.class = classValue.replace(/shiki/g, 'astro-code');
|
||||||
|
|
||||||
// Handle code wrapping
|
// Handle code wrapping
|
||||||
// if wrap=null, do nothing.
|
// if wrap=null, do nothing.
|
||||||
if (wrap === false) {
|
if (wrap === false) {
|
||||||
style += '; overflow-x: auto;';
|
node.properties.style = styleValue + '; overflow-x: auto;';
|
||||||
} else if (wrap === true) {
|
} else if (wrap === true) {
|
||||||
style += '; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;';
|
node.properties.style =
|
||||||
|
styleValue + '; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;';
|
||||||
}
|
}
|
||||||
return `<${tag} class="${className}" style="${style}" tabindex="0">${children}</${tag}>`;
|
|
||||||
},
|
},
|
||||||
code({ children }) {
|
code(node) {
|
||||||
return inline ? children : `<code>${children}</code>`;
|
if (inline) {
|
||||||
|
return node.children[0] as typeof node;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
root(node) {
|
||||||
|
// theme.id for shiki -> shikiji compat
|
||||||
|
const themeName = typeof theme === 'string' ? theme : theme.name;
|
||||||
|
if (themeName === 'css-variables') {
|
||||||
|
// Replace special color tokens to CSS variables
|
||||||
|
visit(node as any, 'element', (child) => {
|
||||||
|
if (child.properties?.style) {
|
||||||
|
child.properties.style = replaceCssVariables(child.properties.style);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
import { getHighlighter as getShikiHighlighter } from 'shiki';
|
|
||||||
import { themes } from './shiki-themes.js';
|
|
||||||
import { languages } from './shiki-languages.js';
|
|
||||||
|
|
||||||
// Caches Promise<Highlighter> for reuse when the same theme and langs are provided
|
|
||||||
const _resolvedHighlighters = new Map();
|
|
||||||
|
|
||||||
/** @type {Promise<any>} */
|
|
||||||
let _allLanguages;
|
|
||||||
|
|
||||||
function stringify(opts) {
|
|
||||||
// Always sort keys before stringifying to make sure objects match regardless of parameter ordering
|
|
||||||
return JSON.stringify(opts, Object.keys(opts).sort());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import('shiki').HighlighterOptions} opts
|
|
||||||
* @returns {Promise<import('shiki').HighlighterOptions>}
|
|
||||||
*/
|
|
||||||
export async function resolveHighlighterOptions(opts) {
|
|
||||||
const resolvedThemes = [];
|
|
||||||
if (opts.theme && opts.theme in themes) {
|
|
||||||
resolvedThemes.push(await themes[opts.theme]());
|
|
||||||
} else if (Object.keys(opts.theme).length) {
|
|
||||||
resolvedThemes.push(opts.theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
let resolvedLanguages;
|
|
||||||
if (opts.langs) {
|
|
||||||
resolvedLanguages = opts.langs;
|
|
||||||
} else {
|
|
||||||
if (!_allLanguages) {
|
|
||||||
_allLanguages = (await Promise.all(Object.values(languages).map((fn) => fn()))).filter(
|
|
||||||
Boolean
|
|
||||||
);
|
|
||||||
}
|
|
||||||
resolvedLanguages = await _allLanguages;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @type {import('shiki').HighlighterOptions} */
|
|
||||||
const highlighterOptions = {
|
|
||||||
...opts,
|
|
||||||
themes: resolvedThemes,
|
|
||||||
langs: resolvedLanguages,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Do not pass through the theme as that will attempt to load it, even if it's included in themes
|
|
||||||
delete highlighterOptions['theme'];
|
|
||||||
|
|
||||||
return highlighterOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import('shiki').HighlighterOptions} opts
|
|
||||||
* @returns {Promise<import('shiki').Highlighter>}
|
|
||||||
*/
|
|
||||||
async function resolveHighlighter(opts) {
|
|
||||||
const highlighterOptions = await resolveHighlighterOptions(opts);
|
|
||||||
|
|
||||||
// Start the async getHighlighter call and cache the Promise
|
|
||||||
const highlighter = getShikiHighlighter(highlighterOptions).then((hl) => {
|
|
||||||
hl.setColorReplacements({
|
|
||||||
'#000001': 'var(--astro-code-color-text)',
|
|
||||||
'#000002': 'var(--astro-code-color-background)',
|
|
||||||
'#000004': 'var(--astro-code-token-constant)',
|
|
||||||
'#000005': 'var(--astro-code-token-string)',
|
|
||||||
'#000006': 'var(--astro-code-token-comment)',
|
|
||||||
'#000007': 'var(--astro-code-token-keyword)',
|
|
||||||
'#000008': 'var(--astro-code-token-parameter)',
|
|
||||||
'#000009': 'var(--astro-code-token-function)',
|
|
||||||
'#000010': 'var(--astro-code-token-string-expression)',
|
|
||||||
'#000011': 'var(--astro-code-token-punctuation)',
|
|
||||||
'#000012': 'var(--astro-code-token-link)',
|
|
||||||
});
|
|
||||||
return hl;
|
|
||||||
});
|
|
||||||
|
|
||||||
return highlighter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import('shiki').HighlighterOptions} opts
|
|
||||||
* @returns {Promise<import('shiki').Highlighter>}
|
|
||||||
*/
|
|
||||||
export function getHighlighter(opts) {
|
|
||||||
const key = stringify(opts);
|
|
||||||
|
|
||||||
// Highlighter has already been requested, reuse the same instance
|
|
||||||
if (_resolvedHighlighters.has(key)) {
|
|
||||||
return _resolvedHighlighters.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
const highlighter = resolveHighlighter(opts);
|
|
||||||
_resolvedHighlighters.set(key, highlighter);
|
|
||||||
|
|
||||||
return highlighter;
|
|
||||||
}
|
|
|
@ -1,176 +0,0 @@
|
||||||
/**
|
|
||||||
* This file is prebuilt from packages/astro/scripts/shiki-gen-languages.mjs
|
|
||||||
* Do not edit this directly, but instead edit that file and rerun it to generate this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { BUNDLED_LANGUAGES } from 'shiki';
|
|
||||||
|
|
||||||
function handleLang(grammar, language) {
|
|
||||||
const lang = BUNDLED_LANGUAGES.find((l) => l.id === language);
|
|
||||||
if (lang) {
|
|
||||||
return {
|
|
||||||
...lang,
|
|
||||||
grammar,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
export const languages = {
|
|
||||||
'abap': () => import('shiki/languages/abap.tmLanguage.json').then((mod) => handleLang(mod.default, 'abap')),
|
|
||||||
'actionscript-3': () => import('shiki/languages/actionscript-3.tmLanguage.json').then((mod) => handleLang(mod.default, 'actionscript-3')),
|
|
||||||
'ada': () => import('shiki/languages/ada.tmLanguage.json').then((mod) => handleLang(mod.default, 'ada')),
|
|
||||||
'apache': () => import('shiki/languages/apache.tmLanguage.json').then((mod) => handleLang(mod.default, 'apache')),
|
|
||||||
'apex': () => import('shiki/languages/apex.tmLanguage.json').then((mod) => handleLang(mod.default, 'apex')),
|
|
||||||
'apl': () => import('shiki/languages/apl.tmLanguage.json').then((mod) => handleLang(mod.default, 'apl')),
|
|
||||||
'applescript': () => import('shiki/languages/applescript.tmLanguage.json').then((mod) => handleLang(mod.default, 'applescript')),
|
|
||||||
'ara': () => import('shiki/languages/ara.tmLanguage.json').then((mod) => handleLang(mod.default, 'ara')),
|
|
||||||
'asm': () => import('shiki/languages/asm.tmLanguage.json').then((mod) => handleLang(mod.default, 'asm')),
|
|
||||||
'astro': () => import('shiki/languages/astro.tmLanguage.json').then((mod) => handleLang(mod.default, 'astro')),
|
|
||||||
'awk': () => import('shiki/languages/awk.tmLanguage.json').then((mod) => handleLang(mod.default, 'awk')),
|
|
||||||
'ballerina': () => import('shiki/languages/ballerina.tmLanguage.json').then((mod) => handleLang(mod.default, 'ballerina')),
|
|
||||||
'bat': () => import('shiki/languages/bat.tmLanguage.json').then((mod) => handleLang(mod.default, 'bat')),
|
|
||||||
'berry': () => import('shiki/languages/berry.tmLanguage.json').then((mod) => handleLang(mod.default, 'berry')),
|
|
||||||
'bibtex': () => import('shiki/languages/bibtex.tmLanguage.json').then((mod) => handleLang(mod.default, 'bibtex')),
|
|
||||||
'bicep': () => import('shiki/languages/bicep.tmLanguage.json').then((mod) => handleLang(mod.default, 'bicep')),
|
|
||||||
'blade': () => import('shiki/languages/blade.tmLanguage.json').then((mod) => handleLang(mod.default, 'blade')),
|
|
||||||
'c': () => import('shiki/languages/c.tmLanguage.json').then((mod) => handleLang(mod.default, 'c')),
|
|
||||||
'cadence': () => import('shiki/languages/cadence.tmLanguage.json').then((mod) => handleLang(mod.default, 'cadence')),
|
|
||||||
'clarity': () => import('shiki/languages/clarity.tmLanguage.json').then((mod) => handleLang(mod.default, 'clarity')),
|
|
||||||
'clojure': () => import('shiki/languages/clojure.tmLanguage.json').then((mod) => handleLang(mod.default, 'clojure')),
|
|
||||||
'cmake': () => import('shiki/languages/cmake.tmLanguage.json').then((mod) => handleLang(mod.default, 'cmake')),
|
|
||||||
'cobol': () => import('shiki/languages/cobol.tmLanguage.json').then((mod) => handleLang(mod.default, 'cobol')),
|
|
||||||
'codeql': () => import('shiki/languages/codeql.tmLanguage.json').then((mod) => handleLang(mod.default, 'codeql')),
|
|
||||||
'coffee': () => import('shiki/languages/coffee.tmLanguage.json').then((mod) => handleLang(mod.default, 'coffee')),
|
|
||||||
'cpp-macro': () => import('shiki/languages/cpp-macro.tmLanguage.json').then((mod) => handleLang(mod.default, 'cpp-macro')),
|
|
||||||
'cpp': () => import('shiki/languages/cpp.tmLanguage.json').then((mod) => handleLang(mod.default, 'cpp')),
|
|
||||||
'crystal': () => import('shiki/languages/crystal.tmLanguage.json').then((mod) => handleLang(mod.default, 'crystal')),
|
|
||||||
'csharp': () => import('shiki/languages/csharp.tmLanguage.json').then((mod) => handleLang(mod.default, 'csharp')),
|
|
||||||
'css': () => import('shiki/languages/css.tmLanguage.json').then((mod) => handleLang(mod.default, 'css')),
|
|
||||||
'cue': () => import('shiki/languages/cue.tmLanguage.json').then((mod) => handleLang(mod.default, 'cue')),
|
|
||||||
'd': () => import('shiki/languages/d.tmLanguage.json').then((mod) => handleLang(mod.default, 'd')),
|
|
||||||
'dart': () => import('shiki/languages/dart.tmLanguage.json').then((mod) => handleLang(mod.default, 'dart')),
|
|
||||||
'dax': () => import('shiki/languages/dax.tmLanguage.json').then((mod) => handleLang(mod.default, 'dax')),
|
|
||||||
'diff': () => import('shiki/languages/diff.tmLanguage.json').then((mod) => handleLang(mod.default, 'diff')),
|
|
||||||
'docker': () => import('shiki/languages/docker.tmLanguage.json').then((mod) => handleLang(mod.default, 'docker')),
|
|
||||||
'dream-maker': () => import('shiki/languages/dream-maker.tmLanguage.json').then((mod) => handleLang(mod.default, 'dream-maker')),
|
|
||||||
'elixir': () => import('shiki/languages/elixir.tmLanguage.json').then((mod) => handleLang(mod.default, 'elixir')),
|
|
||||||
'elm': () => import('shiki/languages/elm.tmLanguage.json').then((mod) => handleLang(mod.default, 'elm')),
|
|
||||||
'erb': () => import('shiki/languages/erb.tmLanguage.json').then((mod) => handleLang(mod.default, 'erb')),
|
|
||||||
'erlang': () => import('shiki/languages/erlang.tmLanguage.json').then((mod) => handleLang(mod.default, 'erlang')),
|
|
||||||
'fish': () => import('shiki/languages/fish.tmLanguage.json').then((mod) => handleLang(mod.default, 'fish')),
|
|
||||||
'fsharp': () => import('shiki/languages/fsharp.tmLanguage.json').then((mod) => handleLang(mod.default, 'fsharp')),
|
|
||||||
'gherkin': () => import('shiki/languages/gherkin.tmLanguage.json').then((mod) => handleLang(mod.default, 'gherkin')),
|
|
||||||
'git-commit': () => import('shiki/languages/git-commit.tmLanguage.json').then((mod) => handleLang(mod.default, 'git-commit')),
|
|
||||||
'git-rebase': () => import('shiki/languages/git-rebase.tmLanguage.json').then((mod) => handleLang(mod.default, 'git-rebase')),
|
|
||||||
'glsl': () => import('shiki/languages/glsl.tmLanguage.json').then((mod) => handleLang(mod.default, 'glsl')),
|
|
||||||
'gnuplot': () => import('shiki/languages/gnuplot.tmLanguage.json').then((mod) => handleLang(mod.default, 'gnuplot')),
|
|
||||||
'go': () => import('shiki/languages/go.tmLanguage.json').then((mod) => handleLang(mod.default, 'go')),
|
|
||||||
'graphql': () => import('shiki/languages/graphql.tmLanguage.json').then((mod) => handleLang(mod.default, 'graphql')),
|
|
||||||
'groovy': () => import('shiki/languages/groovy.tmLanguage.json').then((mod) => handleLang(mod.default, 'groovy')),
|
|
||||||
'hack': () => import('shiki/languages/hack.tmLanguage.json').then((mod) => handleLang(mod.default, 'hack')),
|
|
||||||
'haml': () => import('shiki/languages/haml.tmLanguage.json').then((mod) => handleLang(mod.default, 'haml')),
|
|
||||||
'handlebars': () => import('shiki/languages/handlebars.tmLanguage.json').then((mod) => handleLang(mod.default, 'handlebars')),
|
|
||||||
'haskell': () => import('shiki/languages/haskell.tmLanguage.json').then((mod) => handleLang(mod.default, 'haskell')),
|
|
||||||
'hcl': () => import('shiki/languages/hcl.tmLanguage.json').then((mod) => handleLang(mod.default, 'hcl')),
|
|
||||||
'hlsl': () => import('shiki/languages/hlsl.tmLanguage.json').then((mod) => handleLang(mod.default, 'hlsl')),
|
|
||||||
'html': () => import('shiki/languages/html.tmLanguage.json').then((mod) => handleLang(mod.default, 'html')),
|
|
||||||
'http': () => import('shiki/languages/http.tmLanguage.json').then((mod) => handleLang(mod.default, 'http')),
|
|
||||||
'imba': () => import('shiki/languages/imba.tmLanguage.json').then((mod) => handleLang(mod.default, 'imba')),
|
|
||||||
'ini': () => import('shiki/languages/ini.tmLanguage.json').then((mod) => handleLang(mod.default, 'ini')),
|
|
||||||
'java': () => import('shiki/languages/java.tmLanguage.json').then((mod) => handleLang(mod.default, 'java')),
|
|
||||||
'javascript': () => import('shiki/languages/javascript.tmLanguage.json').then((mod) => handleLang(mod.default, 'javascript')),
|
|
||||||
'jinja-html': () => import('shiki/languages/jinja-html.tmLanguage.json').then((mod) => handleLang(mod.default, 'jinja-html')),
|
|
||||||
'jinja': () => import('shiki/languages/jinja.tmLanguage.json').then((mod) => handleLang(mod.default, 'jinja')),
|
|
||||||
'jison': () => import('shiki/languages/jison.tmLanguage.json').then((mod) => handleLang(mod.default, 'jison')),
|
|
||||||
'json': () => import('shiki/languages/json.tmLanguage.json').then((mod) => handleLang(mod.default, 'json')),
|
|
||||||
'json5': () => import('shiki/languages/json5.tmLanguage.json').then((mod) => handleLang(mod.default, 'json5')),
|
|
||||||
'jsonc': () => import('shiki/languages/jsonc.tmLanguage.json').then((mod) => handleLang(mod.default, 'jsonc')),
|
|
||||||
'jsonnet': () => import('shiki/languages/jsonnet.tmLanguage.json').then((mod) => handleLang(mod.default, 'jsonnet')),
|
|
||||||
'jssm': () => import('shiki/languages/jssm.tmLanguage.json').then((mod) => handleLang(mod.default, 'jssm')),
|
|
||||||
'jsx': () => import('shiki/languages/jsx.tmLanguage.json').then((mod) => handleLang(mod.default, 'jsx')),
|
|
||||||
'julia': () => import('shiki/languages/julia.tmLanguage.json').then((mod) => handleLang(mod.default, 'julia')),
|
|
||||||
'kotlin': () => import('shiki/languages/kotlin.tmLanguage.json').then((mod) => handleLang(mod.default, 'kotlin')),
|
|
||||||
'latex': () => import('shiki/languages/latex.tmLanguage.json').then((mod) => handleLang(mod.default, 'latex')),
|
|
||||||
'less': () => import('shiki/languages/less.tmLanguage.json').then((mod) => handleLang(mod.default, 'less')),
|
|
||||||
'liquid': () => import('shiki/languages/liquid.tmLanguage.json').then((mod) => handleLang(mod.default, 'liquid')),
|
|
||||||
'lisp': () => import('shiki/languages/lisp.tmLanguage.json').then((mod) => handleLang(mod.default, 'lisp')),
|
|
||||||
'logo': () => import('shiki/languages/logo.tmLanguage.json').then((mod) => handleLang(mod.default, 'logo')),
|
|
||||||
'lua': () => import('shiki/languages/lua.tmLanguage.json').then((mod) => handleLang(mod.default, 'lua')),
|
|
||||||
'make': () => import('shiki/languages/make.tmLanguage.json').then((mod) => handleLang(mod.default, 'make')),
|
|
||||||
'markdown': () => import('shiki/languages/markdown.tmLanguage.json').then((mod) => handleLang(mod.default, 'markdown')),
|
|
||||||
'marko': () => import('shiki/languages/marko.tmLanguage.json').then((mod) => handleLang(mod.default, 'marko')),
|
|
||||||
'matlab': () => import('shiki/languages/matlab.tmLanguage.json').then((mod) => handleLang(mod.default, 'matlab')),
|
|
||||||
'mdx': () => import('shiki/languages/mdx.tmLanguage.json').then((mod) => handleLang(mod.default, 'mdx')),
|
|
||||||
'mermaid': () => import('shiki/languages/mermaid.tmLanguage.json').then((mod) => handleLang(mod.default, 'mermaid')),
|
|
||||||
'nginx': () => import('shiki/languages/nginx.tmLanguage.json').then((mod) => handleLang(mod.default, 'nginx')),
|
|
||||||
'nim': () => import('shiki/languages/nim.tmLanguage.json').then((mod) => handleLang(mod.default, 'nim')),
|
|
||||||
'nix': () => import('shiki/languages/nix.tmLanguage.json').then((mod) => handleLang(mod.default, 'nix')),
|
|
||||||
'objective-c': () => import('shiki/languages/objective-c.tmLanguage.json').then((mod) => handleLang(mod.default, 'objective-c')),
|
|
||||||
'objective-cpp': () => import('shiki/languages/objective-cpp.tmLanguage.json').then((mod) => handleLang(mod.default, 'objective-cpp')),
|
|
||||||
'ocaml': () => import('shiki/languages/ocaml.tmLanguage.json').then((mod) => handleLang(mod.default, 'ocaml')),
|
|
||||||
'pascal': () => import('shiki/languages/pascal.tmLanguage.json').then((mod) => handleLang(mod.default, 'pascal')),
|
|
||||||
'perl': () => import('shiki/languages/perl.tmLanguage.json').then((mod) => handleLang(mod.default, 'perl')),
|
|
||||||
'php-html': () => import('shiki/languages/php-html.tmLanguage.json').then((mod) => handleLang(mod.default, 'php-html')),
|
|
||||||
'php': () => import('shiki/languages/php.tmLanguage.json').then((mod) => handleLang(mod.default, 'php')),
|
|
||||||
'plsql': () => import('shiki/languages/plsql.tmLanguage.json').then((mod) => handleLang(mod.default, 'plsql')),
|
|
||||||
'postcss': () => import('shiki/languages/postcss.tmLanguage.json').then((mod) => handleLang(mod.default, 'postcss')),
|
|
||||||
'powerquery': () => import('shiki/languages/powerquery.tmLanguage.json').then((mod) => handleLang(mod.default, 'powerquery')),
|
|
||||||
'powershell': () => import('shiki/languages/powershell.tmLanguage.json').then((mod) => handleLang(mod.default, 'powershell')),
|
|
||||||
'prisma': () => import('shiki/languages/prisma.tmLanguage.json').then((mod) => handleLang(mod.default, 'prisma')),
|
|
||||||
'prolog': () => import('shiki/languages/prolog.tmLanguage.json').then((mod) => handleLang(mod.default, 'prolog')),
|
|
||||||
'proto': () => import('shiki/languages/proto.tmLanguage.json').then((mod) => handleLang(mod.default, 'proto')),
|
|
||||||
'pug': () => import('shiki/languages/pug.tmLanguage.json').then((mod) => handleLang(mod.default, 'pug')),
|
|
||||||
'puppet': () => import('shiki/languages/puppet.tmLanguage.json').then((mod) => handleLang(mod.default, 'puppet')),
|
|
||||||
'purescript': () => import('shiki/languages/purescript.tmLanguage.json').then((mod) => handleLang(mod.default, 'purescript')),
|
|
||||||
'python': () => import('shiki/languages/python.tmLanguage.json').then((mod) => handleLang(mod.default, 'python')),
|
|
||||||
'r': () => import('shiki/languages/r.tmLanguage.json').then((mod) => handleLang(mod.default, 'r')),
|
|
||||||
'raku': () => import('shiki/languages/raku.tmLanguage.json').then((mod) => handleLang(mod.default, 'raku')),
|
|
||||||
'razor': () => import('shiki/languages/razor.tmLanguage.json').then((mod) => handleLang(mod.default, 'razor')),
|
|
||||||
'rel': () => import('shiki/languages/rel.tmLanguage.json').then((mod) => handleLang(mod.default, 'rel')),
|
|
||||||
'riscv': () => import('shiki/languages/riscv.tmLanguage.json').then((mod) => handleLang(mod.default, 'riscv')),
|
|
||||||
'rst': () => import('shiki/languages/rst.tmLanguage.json').then((mod) => handleLang(mod.default, 'rst')),
|
|
||||||
'ruby': () => import('shiki/languages/ruby.tmLanguage.json').then((mod) => handleLang(mod.default, 'ruby')),
|
|
||||||
'rust': () => import('shiki/languages/rust.tmLanguage.json').then((mod) => handleLang(mod.default, 'rust')),
|
|
||||||
'sas': () => import('shiki/languages/sas.tmLanguage.json').then((mod) => handleLang(mod.default, 'sas')),
|
|
||||||
'sass': () => import('shiki/languages/sass.tmLanguage.json').then((mod) => handleLang(mod.default, 'sass')),
|
|
||||||
'scala': () => import('shiki/languages/scala.tmLanguage.json').then((mod) => handleLang(mod.default, 'scala')),
|
|
||||||
'scheme': () => import('shiki/languages/scheme.tmLanguage.json').then((mod) => handleLang(mod.default, 'scheme')),
|
|
||||||
'scss': () => import('shiki/languages/scss.tmLanguage.json').then((mod) => handleLang(mod.default, 'scss')),
|
|
||||||
'shaderlab': () => import('shiki/languages/shaderlab.tmLanguage.json').then((mod) => handleLang(mod.default, 'shaderlab')),
|
|
||||||
'shellscript': () => import('shiki/languages/shellscript.tmLanguage.json').then((mod) => handleLang(mod.default, 'shellscript')),
|
|
||||||
'smalltalk': () => import('shiki/languages/smalltalk.tmLanguage.json').then((mod) => handleLang(mod.default, 'smalltalk')),
|
|
||||||
'solidity': () => import('shiki/languages/solidity.tmLanguage.json').then((mod) => handleLang(mod.default, 'solidity')),
|
|
||||||
'sparql': () => import('shiki/languages/sparql.tmLanguage.json').then((mod) => handleLang(mod.default, 'sparql')),
|
|
||||||
'sql': () => import('shiki/languages/sql.tmLanguage.json').then((mod) => handleLang(mod.default, 'sql')),
|
|
||||||
'ssh-config': () => import('shiki/languages/ssh-config.tmLanguage.json').then((mod) => handleLang(mod.default, 'ssh-config')),
|
|
||||||
'stata': () => import('shiki/languages/stata.tmLanguage.json').then((mod) => handleLang(mod.default, 'stata')),
|
|
||||||
'stylus': () => import('shiki/languages/stylus.tmLanguage.json').then((mod) => handleLang(mod.default, 'stylus')),
|
|
||||||
'svelte': () => import('shiki/languages/svelte.tmLanguage.json').then((mod) => handleLang(mod.default, 'svelte')),
|
|
||||||
'swift': () => import('shiki/languages/swift.tmLanguage.json').then((mod) => handleLang(mod.default, 'swift')),
|
|
||||||
'system-verilog': () => import('shiki/languages/system-verilog.tmLanguage.json').then((mod) => handleLang(mod.default, 'system-verilog')),
|
|
||||||
'tasl': () => import('shiki/languages/tasl.tmLanguage.json').then((mod) => handleLang(mod.default, 'tasl')),
|
|
||||||
'tcl': () => import('shiki/languages/tcl.tmLanguage.json').then((mod) => handleLang(mod.default, 'tcl')),
|
|
||||||
'tex': () => import('shiki/languages/tex.tmLanguage.json').then((mod) => handleLang(mod.default, 'tex')),
|
|
||||||
'toml': () => import('shiki/languages/toml.tmLanguage.json').then((mod) => handleLang(mod.default, 'toml')),
|
|
||||||
'tsx': () => import('shiki/languages/tsx.tmLanguage.json').then((mod) => handleLang(mod.default, 'tsx')),
|
|
||||||
'turtle': () => import('shiki/languages/turtle.tmLanguage.json').then((mod) => handleLang(mod.default, 'turtle')),
|
|
||||||
'twig': () => import('shiki/languages/twig.tmLanguage.json').then((mod) => handleLang(mod.default, 'twig')),
|
|
||||||
'typescript': () => import('shiki/languages/typescript.tmLanguage.json').then((mod) => handleLang(mod.default, 'typescript')),
|
|
||||||
'v': () => import('shiki/languages/v.tmLanguage.json').then((mod) => handleLang(mod.default, 'v')),
|
|
||||||
'vb': () => import('shiki/languages/vb.tmLanguage.json').then((mod) => handleLang(mod.default, 'vb')),
|
|
||||||
'verilog': () => import('shiki/languages/verilog.tmLanguage.json').then((mod) => handleLang(mod.default, 'verilog')),
|
|
||||||
'vhdl': () => import('shiki/languages/vhdl.tmLanguage.json').then((mod) => handleLang(mod.default, 'vhdl')),
|
|
||||||
'viml': () => import('shiki/languages/viml.tmLanguage.json').then((mod) => handleLang(mod.default, 'viml')),
|
|
||||||
'vue-html': () => import('shiki/languages/vue-html.tmLanguage.json').then((mod) => handleLang(mod.default, 'vue-html')),
|
|
||||||
'vue': () => import('shiki/languages/vue.tmLanguage.json').then((mod) => handleLang(mod.default, 'vue')),
|
|
||||||
'wasm': () => import('shiki/languages/wasm.tmLanguage.json').then((mod) => handleLang(mod.default, 'wasm')),
|
|
||||||
'wenyan': () => import('shiki/languages/wenyan.tmLanguage.json').then((mod) => handleLang(mod.default, 'wenyan')),
|
|
||||||
'wgsl': () => import('shiki/languages/wgsl.tmLanguage.json').then((mod) => handleLang(mod.default, 'wgsl')),
|
|
||||||
'xml': () => import('shiki/languages/xml.tmLanguage.json').then((mod) => handleLang(mod.default, 'xml')),
|
|
||||||
'xsl': () => import('shiki/languages/xsl.tmLanguage.json').then((mod) => handleLang(mod.default, 'xsl')),
|
|
||||||
'yaml': () => import('shiki/languages/yaml.tmLanguage.json').then((mod) => handleLang(mod.default, 'yaml')),
|
|
||||||
'zenscript': () => import('shiki/languages/zenscript.tmLanguage.json').then((mod) => handleLang(mod.default, 'zenscript')),
|
|
||||||
};
|
|
|
@ -1,37 +0,0 @@
|
||||||
/**
|
|
||||||
* This file is prebuilt from packages/astro/scripts/shiki-gen-themes.mjs
|
|
||||||
* Do not edit this directly, but instead edit that file and rerun it to generate this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
export const themes = {
|
|
||||||
'css-variables': () => import('shiki/themes/css-variables.json').then(mod => mod.default),
|
|
||||||
'dark-plus': () => import('shiki/themes/dark-plus.json').then(mod => mod.default),
|
|
||||||
'dracula-soft': () => import('shiki/themes/dracula-soft.json').then(mod => mod.default),
|
|
||||||
'dracula': () => import('shiki/themes/dracula.json').then(mod => mod.default),
|
|
||||||
'github-dark-dimmed': () => import('shiki/themes/github-dark-dimmed.json').then(mod => mod.default),
|
|
||||||
'github-dark': () => import('shiki/themes/github-dark.json').then(mod => mod.default),
|
|
||||||
'github-light': () => import('shiki/themes/github-light.json').then(mod => mod.default),
|
|
||||||
'hc_light': () => import('shiki/themes/hc_light.json').then(mod => mod.default),
|
|
||||||
'light-plus': () => import('shiki/themes/light-plus.json').then(mod => mod.default),
|
|
||||||
'material-theme-darker': () => import('shiki/themes/material-theme-darker.json').then(mod => mod.default),
|
|
||||||
'material-theme-lighter': () => import('shiki/themes/material-theme-lighter.json').then(mod => mod.default),
|
|
||||||
'material-theme-ocean': () => import('shiki/themes/material-theme-ocean.json').then(mod => mod.default),
|
|
||||||
'material-theme-palenight': () => import('shiki/themes/material-theme-palenight.json').then(mod => mod.default),
|
|
||||||
'material-theme': () => import('shiki/themes/material-theme.json').then(mod => mod.default),
|
|
||||||
'min-dark': () => import('shiki/themes/min-dark.json').then(mod => mod.default),
|
|
||||||
'min-light': () => import('shiki/themes/min-light.json').then(mod => mod.default),
|
|
||||||
'monokai': () => import('shiki/themes/monokai.json').then(mod => mod.default),
|
|
||||||
'nord': () => import('shiki/themes/nord.json').then(mod => mod.default),
|
|
||||||
'one-dark-pro': () => import('shiki/themes/one-dark-pro.json').then(mod => mod.default),
|
|
||||||
'poimandres': () => import('shiki/themes/poimandres.json').then(mod => mod.default),
|
|
||||||
'rose-pine-dawn': () => import('shiki/themes/rose-pine-dawn.json').then(mod => mod.default),
|
|
||||||
'rose-pine-moon': () => import('shiki/themes/rose-pine-moon.json').then(mod => mod.default),
|
|
||||||
'rose-pine': () => import('shiki/themes/rose-pine.json').then(mod => mod.default),
|
|
||||||
'slack-dark': () => import('shiki/themes/slack-dark.json').then(mod => mod.default),
|
|
||||||
'slack-ochin': () => import('shiki/themes/slack-ochin.json').then(mod => mod.default),
|
|
||||||
'solarized-dark': () => import('shiki/themes/solarized-dark.json').then(mod => mod.default),
|
|
||||||
'solarized-light': () => import('shiki/themes/solarized-light.json').then(mod => mod.default),
|
|
||||||
'vitesse-dark': () => import('shiki/themes/vitesse-dark.json').then(mod => mod.default),
|
|
||||||
'vitesse-light': () => import('shiki/themes/vitesse-light.json').then(mod => mod.default),
|
|
||||||
};
|
|
46
packages/astro/components/shiki.ts
Normal file
46
packages/astro/components/shiki.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { type Highlighter, getHighlighter } from 'shikiji';
|
||||||
|
|
||||||
|
type HighlighterOptions = NonNullable<Parameters<typeof getHighlighter>[0]>;
|
||||||
|
|
||||||
|
const ASTRO_COLOR_REPLACEMENTS: Record<string, string> = {
|
||||||
|
'#000001': 'var(--astro-code-color-text)',
|
||||||
|
'#000002': 'var(--astro-code-color-background)',
|
||||||
|
'#000004': 'var(--astro-code-token-constant)',
|
||||||
|
'#000005': 'var(--astro-code-token-string)',
|
||||||
|
'#000006': 'var(--astro-code-token-comment)',
|
||||||
|
'#000007': 'var(--astro-code-token-keyword)',
|
||||||
|
'#000008': 'var(--astro-code-token-parameter)',
|
||||||
|
'#000009': 'var(--astro-code-token-function)',
|
||||||
|
'#000010': 'var(--astro-code-token-string-expression)',
|
||||||
|
'#000011': 'var(--astro-code-token-punctuation)',
|
||||||
|
'#000012': 'var(--astro-code-token-link)',
|
||||||
|
};
|
||||||
|
const COLOR_REPLACEMENT_REGEX = new RegExp(
|
||||||
|
`(${Object.keys(ASTRO_COLOR_REPLACEMENTS).join('|')})`,
|
||||||
|
'g'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Caches Promise<Highlighter> for reuse when the same theme and langs are provided
|
||||||
|
const cachedHighlighters = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shiki -> shikiji compat as we need to manually replace it
|
||||||
|
*/
|
||||||
|
export function replaceCssVariables(str: string) {
|
||||||
|
return str.replace(COLOR_REPLACEMENT_REGEX, (match) => ASTRO_COLOR_REPLACEMENTS[match] || match);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCachedHighlighter(opts: HighlighterOptions): Promise<Highlighter> {
|
||||||
|
// Always sort keys before stringifying to make sure objects match regardless of parameter ordering
|
||||||
|
const key = JSON.stringify(opts, Object.keys(opts).sort());
|
||||||
|
|
||||||
|
// Highlighter has already been requested, reuse the same instance
|
||||||
|
if (cachedHighlighters.has(key)) {
|
||||||
|
return cachedHighlighters.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const highlighter = getHighlighter(opts);
|
||||||
|
cachedHighlighters.set(key, highlighter);
|
||||||
|
|
||||||
|
return highlighter;
|
||||||
|
}
|
|
@ -164,7 +164,7 @@
|
||||||
"resolve": "^1.22.4",
|
"resolve": "^1.22.4",
|
||||||
"semver": "^7.5.4",
|
"semver": "^7.5.4",
|
||||||
"server-destroy": "^1.0.1",
|
"server-destroy": "^1.0.1",
|
||||||
"shiki": "^0.14.3",
|
"shikiji": "^0.6.8",
|
||||||
"string-width": "^6.1.0",
|
"string-width": "^6.1.0",
|
||||||
"strip-ansi": "^7.1.0",
|
"strip-ansi": "^7.1.0",
|
||||||
"tsconfig-resolver": "^3.0.1",
|
"tsconfig-resolver": "^3.0.1",
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
import fs from 'node:fs';
|
|
||||||
|
|
||||||
const dir = await fs.promises.readdir('packages/astro/node_modules/shiki/languages/');
|
|
||||||
|
|
||||||
const langImports = dir.map((f) => {
|
|
||||||
const key = f.slice(0, f.indexOf('.tmLanguage.json'));
|
|
||||||
return [key, `import('shiki/languages/${f}').then((mod) => handleLang(mod.default, '${key}'))`];
|
|
||||||
});
|
|
||||||
|
|
||||||
let code = `\
|
|
||||||
/**
|
|
||||||
* This file is prebuilt from packages/astro/scripts/shiki-gen-languages.mjs
|
|
||||||
* Do not edit this directly, but instead edit that file and rerun it to generate this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { BUNDLED_LANGUAGES } from 'shiki';
|
|
||||||
|
|
||||||
function handleLang(grammar, language) {
|
|
||||||
const lang = BUNDLED_LANGUAGES.find((l) => l.id === language);
|
|
||||||
if (lang) {
|
|
||||||
return {
|
|
||||||
...lang,
|
|
||||||
grammar,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
export const languages = {`;
|
|
||||||
|
|
||||||
for (const [key, imp] of langImports) {
|
|
||||||
code += `\n\t'${key}': () => ${imp},`;
|
|
||||||
}
|
|
||||||
code += '\n};';
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(code);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run this script and pipe it into the output file, for ex.
|
|
||||||
* node packages/astro/scripts/shiki-gen-languages.mjs > packages/astro/components/shiki-languages.js
|
|
||||||
*/
|
|
|
@ -1,31 +0,0 @@
|
||||||
import fs from 'node:fs';
|
|
||||||
|
|
||||||
const dir = await fs.promises.readdir('packages/astro/node_modules/shiki/themes/');
|
|
||||||
|
|
||||||
const toThemeImport = (theme) => `import('shiki/themes/${theme}').then(mod => mod.default)`;
|
|
||||||
|
|
||||||
const themeImports = dir.map((f) => {
|
|
||||||
return [f.slice(0, f.indexOf('.json')), toThemeImport(f)];
|
|
||||||
});
|
|
||||||
|
|
||||||
let code = `\
|
|
||||||
/**
|
|
||||||
* This file is prebuilt from packages/astro/scripts/shiki-gen-themes.mjs
|
|
||||||
* Do not edit this directly, but instead edit that file and rerun it to generate this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
export const themes = {`;
|
|
||||||
|
|
||||||
for (const [key, imp] of themeImports) {
|
|
||||||
code += `\n\t'${key}': () => ${imp},`;
|
|
||||||
}
|
|
||||||
code += '\n};';
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(code);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run this script and pipe it into the output file, for ex.
|
|
||||||
* node packages/astro/scripts/shiki-gen-themes.mjs > packages/astro/components/shiki-themes.js
|
|
||||||
*/
|
|
|
@ -16,7 +16,6 @@ export function vitePluginInternals(input: Set<string>, internals: BuildInternal
|
||||||
// Except for these packages as they're not bundle-friendly. Users with strict package installations
|
// Except for these packages as they're not bundle-friendly. Users with strict package installations
|
||||||
// need to manually install these themselves if they use the related features.
|
// need to manually install these themselves if they use the related features.
|
||||||
external: [
|
external: [
|
||||||
'shiki', // For syntax highlighting
|
|
||||||
'sharp', // For sharp image service
|
'sharp', // For sharp image service
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
import type { RehypePlugin, RemarkPlugin, RemarkRehype } from '@astrojs/markdown-remark';
|
import type {
|
||||||
|
RehypePlugin,
|
||||||
|
RemarkPlugin,
|
||||||
|
RemarkRehype,
|
||||||
|
ShikiConfig,
|
||||||
|
} from '@astrojs/markdown-remark';
|
||||||
import { markdownConfigDefaults } from '@astrojs/markdown-remark';
|
import { markdownConfigDefaults } from '@astrojs/markdown-remark';
|
||||||
import type { ILanguageRegistration, IShikiTheme, Theme } from 'shiki';
|
import { bundledThemes, type BuiltinTheme } from 'shikiji';
|
||||||
import type { AstroUserConfig, ViteUserConfig } from '../../@types/astro.js';
|
import type { AstroUserConfig, ViteUserConfig } from '../../@types/astro.js';
|
||||||
|
|
||||||
import type { OutgoingHttpHeaders } from 'node:http';
|
import type { OutgoingHttpHeaders } from 'node:http';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { pathToFileURL } from 'node:url';
|
import { pathToFileURL } from 'node:url';
|
||||||
import { BUNDLED_THEMES } from 'shiki';
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { appendForwardSlash, prependForwardSlash, removeTrailingForwardSlash } from '../path.js';
|
import { appendForwardSlash, prependForwardSlash, removeTrailingForwardSlash } from '../path.js';
|
||||||
|
|
||||||
|
type ShikiLangs = NonNullable<ShikiConfig['langs']>;
|
||||||
|
type ShikiTheme = NonNullable<ShikiConfig['theme']>;
|
||||||
|
|
||||||
const ASTRO_CONFIG_DEFAULTS = {
|
const ASTRO_CONFIG_DEFAULTS = {
|
||||||
root: '.',
|
root: '.',
|
||||||
srcDir: './src',
|
srcDir: './src',
|
||||||
|
@ -228,11 +235,30 @@ export const AstroConfigSchema = z.object({
|
||||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.syntaxHighlight),
|
.default(ASTRO_CONFIG_DEFAULTS.markdown.syntaxHighlight),
|
||||||
shikiConfig: z
|
shikiConfig: z
|
||||||
.object({
|
.object({
|
||||||
langs: z.custom<ILanguageRegistration>().array().default([]),
|
langs: z
|
||||||
|
.custom<ShikiLangs[number]>()
|
||||||
|
.array()
|
||||||
|
.transform((langs) => {
|
||||||
|
for (const lang of langs) {
|
||||||
|
// shiki -> shikiji compat
|
||||||
|
if (typeof lang === 'object') {
|
||||||
|
// `id` renamed to `name
|
||||||
|
if ((lang as any).id && !lang.name) {
|
||||||
|
lang.name = (lang as any).id;
|
||||||
|
}
|
||||||
|
// `grammar` flattened to lang itself
|
||||||
|
if ((lang as any).grammar) {
|
||||||
|
Object.assign(lang, (lang as any).grammar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return langs;
|
||||||
|
})
|
||||||
|
.default([]),
|
||||||
theme: z
|
theme: z
|
||||||
.enum(BUNDLED_THEMES as [Theme, ...Theme[]])
|
.enum(Object.keys(bundledThemes) as [BuiltinTheme, ...BuiltinTheme[]])
|
||||||
.or(z.custom<IShikiTheme>())
|
.or(z.custom<ShikiTheme>())
|
||||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.theme! as Theme),
|
.default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.theme as BuiltinTheme),
|
||||||
wrap: z.boolean().or(z.null()).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.wrap!),
|
wrap: z.boolean().or(z.null()).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.wrap!),
|
||||||
})
|
})
|
||||||
.default({}),
|
.default({}),
|
||||||
|
|
|
@ -55,8 +55,6 @@ const ALWAYS_NOEXTERNAL = [
|
||||||
// pipeline, which Vite doesn't support in development time. This hardcoded list temporarily
|
// pipeline, which Vite doesn't support in development time. This hardcoded list temporarily
|
||||||
// fixes things until Vite can properly handle them, or when they support ESM.
|
// fixes things until Vite can properly handle them, or when they support ESM.
|
||||||
const ONLY_DEV_EXTERNAL = [
|
const ONLY_DEV_EXTERNAL = [
|
||||||
// Imported by `<Code/>` which is processed by Vite
|
|
||||||
'shiki',
|
|
||||||
// Imported by `@astrojs/prism` which exposes `<Prism/>` that is processed by Vite
|
// Imported by `@astrojs/prism` which exposes `<Prism/>` that is processed by Vite
|
||||||
'prismjs/components/index.js',
|
'prismjs/components/index.js',
|
||||||
// Imported by `astro/assets` -> `packages/astro/src/core/logger/core.ts`
|
// Imported by `astro/assets` -> `packages/astro/src/core/logger/core.ts`
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as fs from 'node:fs';
|
import * as fs from 'node:fs';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { getHighlighter } from 'shiki';
|
import { codeToHtml } from 'shikiji';
|
||||||
import type { ErrorPayload } from 'vite';
|
import type { ErrorPayload } from 'vite';
|
||||||
import type { ModuleLoader } from '../../module-loader/index.js';
|
import type { ModuleLoader } from '../../module-loader/index.js';
|
||||||
import { FailedToLoadModuleSSR, InvalidGlob, MdxIntegrationMissingError } from '../errors-data.js';
|
import { FailedToLoadModuleSSR, InvalidGlob, MdxIntegrationMissingError } from '../errors-data.js';
|
||||||
|
@ -139,7 +139,6 @@ export async function getViteErrorPayload(err: ErrorWithMetadata): Promise<Astro
|
||||||
|
|
||||||
const docslink = getDocsForError(err);
|
const docslink = getDocsForError(err);
|
||||||
|
|
||||||
const highlighter = await getHighlighter({ theme: 'css-variables' });
|
|
||||||
let highlighterLang = err.loc?.file?.split('.').pop();
|
let highlighterLang = err.loc?.file?.split('.').pop();
|
||||||
if (ALTERNATIVE_JS_EXTS.includes(highlighterLang ?? '')) {
|
if (ALTERNATIVE_JS_EXTS.includes(highlighterLang ?? '')) {
|
||||||
highlighterLang = 'js';
|
highlighterLang = 'js';
|
||||||
|
@ -148,8 +147,10 @@ export async function getViteErrorPayload(err: ErrorWithMetadata): Promise<Astro
|
||||||
highlighterLang = 'md';
|
highlighterLang = 'md';
|
||||||
}
|
}
|
||||||
const highlightedCode = err.fullCode
|
const highlightedCode = err.fullCode
|
||||||
? highlighter.codeToHtml(err.fullCode, {
|
? await codeToHtml(err.fullCode, {
|
||||||
|
// @ts-expect-error always assume that shiki can accept the lang string
|
||||||
lang: highlighterLang,
|
lang: highlighterLang,
|
||||||
|
theme: 'css-variables',
|
||||||
lineOptions: err.loc?.line ? [{ line: err.loc.line, classes: ['error-line'] }] : undefined,
|
lineOptions: err.loc?.line ? [{ line: err.loc.line, classes: ['error-line'] }] : undefined,
|
||||||
})
|
})
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
|
@ -15,7 +15,7 @@ describe('<Code>', () => {
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
expect($('pre')).to.have.lengthOf(1);
|
expect($('pre')).to.have.lengthOf(1);
|
||||||
expect($('pre').attr('style')).to.equal(
|
expect($('pre').attr('style')).to.equal(
|
||||||
'background-color: #24292e; overflow-x: auto;',
|
'background-color:#24292e;color:#e1e4e8; overflow-x: auto;',
|
||||||
'applies default and overflow'
|
'applies default and overflow'
|
||||||
);
|
);
|
||||||
expect($('pre > code')).to.have.lengthOf(1);
|
expect($('pre > code')).to.have.lengthOf(1);
|
||||||
|
@ -40,7 +40,7 @@ describe('<Code>', () => {
|
||||||
expect($('pre')).to.have.lengthOf(1);
|
expect($('pre')).to.have.lengthOf(1);
|
||||||
expect($('pre').attr('class')).to.equal('astro-code nord');
|
expect($('pre').attr('class')).to.equal('astro-code nord');
|
||||||
expect($('pre').attr('style')).to.equal(
|
expect($('pre').attr('style')).to.equal(
|
||||||
'background-color: #2e3440ff; overflow-x: auto;',
|
'background-color:#2e3440ff;color:#d8dee9ff; overflow-x: auto;',
|
||||||
'applies custom theme'
|
'applies custom theme'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -52,7 +52,7 @@ describe('<Code>', () => {
|
||||||
expect($('pre')).to.have.lengthOf(1);
|
expect($('pre')).to.have.lengthOf(1);
|
||||||
// test: applies wrap overflow
|
// test: applies wrap overflow
|
||||||
expect($('pre').attr('style')).to.equal(
|
expect($('pre').attr('style')).to.equal(
|
||||||
'background-color: #24292e; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;'
|
'background-color:#24292e;color:#e1e4e8; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -60,14 +60,16 @@ describe('<Code>', () => {
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
expect($('pre')).to.have.lengthOf(1);
|
expect($('pre')).to.have.lengthOf(1);
|
||||||
// test: applies wrap overflow
|
// test: applies wrap overflow
|
||||||
expect($('pre').attr('style')).to.equal('background-color: #24292e; overflow-x: auto;');
|
expect($('pre').attr('style')).to.equal(
|
||||||
|
'background-color:#24292e;color:#e1e4e8; overflow-x: auto;'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let html = await fixture.readFile('/wrap-null/index.html');
|
let html = await fixture.readFile('/wrap-null/index.html');
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
expect($('pre')).to.have.lengthOf(1);
|
expect($('pre')).to.have.lengthOf(1);
|
||||||
// test: applies wrap overflow
|
// test: applies wrap overflow
|
||||||
expect($('pre').attr('style')).to.equal('background-color: #24292e');
|
expect($('pre').attr('style')).to.equal('background-color:#24292e;color:#e1e4e8');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -81,12 +83,12 @@ describe('<Code>', () => {
|
||||||
.map((i, f) => (f.attribs ? f.attribs.style : 'no style found'))
|
.map((i, f) => (f.attribs ? f.attribs.style : 'no style found'))
|
||||||
.toArray()
|
.toArray()
|
||||||
).to.deep.equal([
|
).to.deep.equal([
|
||||||
'background-color: var(--astro-code-color-background); overflow-x: auto;',
|
'background-color:var(--astro-code-color-background);color:var(--astro-code-color-text); overflow-x: auto;',
|
||||||
'color: var(--astro-code-token-constant)',
|
'color:var(--astro-code-token-constant)',
|
||||||
'color: var(--astro-code-token-function)',
|
'color:var(--astro-code-token-function)',
|
||||||
'color: var(--astro-code-color-text)',
|
'color:var(--astro-code-color-text)',
|
||||||
'color: var(--astro-code-token-string-expression)',
|
'color:var(--astro-code-token-string-expression)',
|
||||||
'color: var(--astro-code-color-text)',
|
'color:var(--astro-code-color-text)',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,9 @@ describe('Astro Markdown Shiki', () => {
|
||||||
|
|
||||||
expect($('pre')).to.have.lengthOf(2);
|
expect($('pre')).to.have.lengthOf(2);
|
||||||
expect($('pre').hasClass('astro-code')).to.equal(true);
|
expect($('pre').hasClass('astro-code')).to.equal(true);
|
||||||
expect($('pre').attr().style).to.equal('background-color: #24292e; overflow-x: auto;');
|
expect($('pre').attr().style).to.equal(
|
||||||
|
'background-color:#24292e;color:#e1e4e8; overflow-x: auto;'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Can render diff syntax with "user-select: none"', async () => {
|
it('Can render diff syntax with "user-select: none"', async () => {
|
||||||
|
@ -47,7 +49,9 @@ describe('Astro Markdown Shiki', () => {
|
||||||
|
|
||||||
expect($('pre')).to.have.lengthOf(1);
|
expect($('pre')).to.have.lengthOf(1);
|
||||||
expect($('pre').hasClass('astro-code')).to.equal(true);
|
expect($('pre').hasClass('astro-code')).to.equal(true);
|
||||||
expect($('pre').attr().style).to.equal('background-color: #fff; overflow-x: auto;');
|
expect($('pre').attr().style).to.equal(
|
||||||
|
'background-color:#fff;color:#24292e; overflow-x: auto;'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -65,7 +69,9 @@ describe('Astro Markdown Shiki', () => {
|
||||||
|
|
||||||
expect($('pre')).to.have.lengthOf(1);
|
expect($('pre')).to.have.lengthOf(1);
|
||||||
expect($('pre').hasClass('astro-code')).to.equal(true);
|
expect($('pre').hasClass('astro-code')).to.equal(true);
|
||||||
expect($('pre').attr().style).to.equal('background-color: #FDFDFE; overflow-x: auto;');
|
expect($('pre').attr().style).to.equal(
|
||||||
|
'background-color:#FDFDFE;color:#4E5377; overflow-x: auto;'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -83,22 +89,19 @@ describe('Astro Markdown Shiki', () => {
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
const segments = $('.line').get(6).children;
|
const segments = $('.line').get(6).children;
|
||||||
expect(segments).to.have.lengthOf(3);
|
expect(segments).to.have.lengthOf(2);
|
||||||
expect(segments[0].attribs.style).to.be.equal('color: #E1E4E8');
|
expect(segments[0].attribs.style).to.be.equal('color:#79B8FF');
|
||||||
expect(segments[1].attribs.style).to.be.equal('color: #79B8FF');
|
expect(segments[1].attribs.style).to.be.equal('color:#E1E4E8');
|
||||||
expect(segments[2].attribs.style).to.be.equal('color: #E1E4E8');
|
|
||||||
|
|
||||||
const unknownLang = $('.line').last().html();
|
const unknownLang = $('.astro-code').last();
|
||||||
expect(unknownLang).to.be.equal(
|
expect(unknownLang.attr('style')).to.contain('background-color:#24292e;color:#e1e4e8;');
|
||||||
'<span style="color: #e1e4e8">This language does not exist</span>'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Wrap', () => {
|
describe('Wrap', () => {
|
||||||
describe('wrap = true', () => {
|
describe('wrap = true', () => {
|
||||||
const style =
|
const style =
|
||||||
'background-color: #24292e; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;';
|
'background-color:#24292e;color:#e1e4e8; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;';
|
||||||
let fixture;
|
let fixture;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
|
@ -117,7 +120,7 @@ describe('Astro Markdown Shiki', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('wrap = false', () => {
|
describe('wrap = false', () => {
|
||||||
const style = 'background-color: #24292e; overflow-x: auto;';
|
const style = 'background-color:#24292e;color:#e1e4e8; overflow-x: auto;';
|
||||||
let fixture;
|
let fixture;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
|
@ -135,7 +138,7 @@ describe('Astro Markdown Shiki', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('wrap = null', () => {
|
describe('wrap = null', () => {
|
||||||
const style = 'background-color: #24292e';
|
const style = 'background-color:#24292e;color:#e1e4e8';
|
||||||
let fixture;
|
let fixture;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
import { expect } from 'chai';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
import { createContainer } from '../../../dist/core/dev/index.js';
|
|
||||||
import { createViteLoader } from '../../../dist/core/module-loader/index.js';
|
|
||||||
import { createBasicSettings, defaultLogger } from '../test-utils.js';
|
|
||||||
|
|
||||||
const root = new URL('../../fixtures/alias/', import.meta.url);
|
|
||||||
|
|
||||||
describe('<Code />', () => {
|
|
||||||
describe('Shiki - getHighlighterOptions', () => {
|
|
||||||
let container;
|
|
||||||
let mod;
|
|
||||||
before(async () => {
|
|
||||||
const settings = await createBasicSettings({ root: fileURLToPath(root) });
|
|
||||||
container = await createContainer({ settings, logger: defaultLogger });
|
|
||||||
const loader = createViteLoader(container.viteServer);
|
|
||||||
mod = await loader.import('astro/components/Shiki.js');
|
|
||||||
});
|
|
||||||
|
|
||||||
after(async () => {
|
|
||||||
await container.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses the bundles themes for built-in themes', async () => {
|
|
||||||
const { resolveHighlighterOptions } = mod;
|
|
||||||
// NOTE: pass empty `langs` to prevent Shiki from loading all langs by default, which slows down the test
|
|
||||||
const opts = await resolveHighlighterOptions({ theme: 'css-variables', langs: [] });
|
|
||||||
const themes = opts.themes;
|
|
||||||
|
|
||||||
expect(themes).to.have.a.lengthOf(1);
|
|
||||||
expect(themes[0]).to.be.an('object');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses the string theme name for custom themes', async () => {
|
|
||||||
const { resolveHighlighterOptions } = mod;
|
|
||||||
// NOTE: pass empty `langs` to prevent Shiki from loading all langs by default, which slows down the test
|
|
||||||
const opts = await resolveHighlighterOptions({ theme: 'some-custom-theme', langs: [] });
|
|
||||||
const themes = opts.themes;
|
|
||||||
|
|
||||||
expect(themes).to.have.a.lengthOf(1);
|
|
||||||
expect(themes[0]).to.be.an('string');
|
|
||||||
expect(themes[0]).to.equal('some-custom-theme');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -71,7 +71,7 @@
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"htmlparser2": "^9.0.0",
|
"htmlparser2": "^9.0.0",
|
||||||
"kleur": "^4.1.5",
|
"kleur": "^4.1.5",
|
||||||
"shiki": "^0.14.3",
|
"shikiji": "^0.6.8",
|
||||||
"zod": "3.21.1"
|
"zod": "3.21.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import Markdoc from '@markdoc/markdoc';
|
import Markdoc from '@markdoc/markdoc';
|
||||||
import type { ShikiConfig } from 'astro';
|
import type { ShikiConfig } from 'astro';
|
||||||
import { unescapeHTML } from 'astro/runtime/server/index.js';
|
import { unescapeHTML } from 'astro/runtime/server/index.js';
|
||||||
import type * as shikiTypes from 'shiki';
|
import { bundledLanguages, getHighlighter, type Highlighter } from 'shikiji';
|
||||||
import { getHighlighter } from 'shiki';
|
|
||||||
import type { AstroMarkdocConfig } from '../config.js';
|
import type { AstroMarkdocConfig } from '../config.js';
|
||||||
|
|
||||||
const ASTRO_COLOR_REPLACEMENTS = {
|
const ASTRO_COLOR_REPLACEMENTS: Record<string, string> = {
|
||||||
'#000001': 'var(--astro-code-color-text)',
|
'#000001': 'var(--astro-code-color-text)',
|
||||||
'#000002': 'var(--astro-code-color-background)',
|
'#000002': 'var(--astro-code-color-background)',
|
||||||
'#000004': 'var(--astro-code-token-constant)',
|
'#000004': 'var(--astro-code-token-constant)',
|
||||||
|
@ -18,37 +17,37 @@ const ASTRO_COLOR_REPLACEMENTS = {
|
||||||
'#000011': 'var(--astro-code-token-punctuation)',
|
'#000011': 'var(--astro-code-token-punctuation)',
|
||||||
'#000012': 'var(--astro-code-token-link)',
|
'#000012': 'var(--astro-code-token-link)',
|
||||||
};
|
};
|
||||||
|
const COLOR_REPLACEMENT_REGEX = new RegExp(
|
||||||
|
`(${Object.keys(ASTRO_COLOR_REPLACEMENTS).join('|')})`,
|
||||||
|
'g'
|
||||||
|
);
|
||||||
|
|
||||||
const PRE_SELECTOR = /<pre class="(.*?)shiki(.*?)"/;
|
const PRE_SELECTOR = /<pre class="(.*?)shiki(.*?)"/;
|
||||||
const LINE_SELECTOR = /<span class="line"><span style="(.*?)">([\+|\-])/g;
|
const LINE_SELECTOR = /<span class="line"><span style="(.*?)">([\+|\-])/g;
|
||||||
const INLINE_STYLE_SELECTOR = /style="(.*?)"/;
|
const INLINE_STYLE_SELECTOR = /style="(.*?)"/;
|
||||||
|
const INLINE_STYLE_SELECTOR_GLOBAL = /style="(.*?)"/g;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: cache only needed for dev server reloads, internal test suites, and manual calls to `Markdoc.transform` by the user.
|
* Note: cache only needed for dev server reloads, internal test suites, and manual calls to `Markdoc.transform` by the user.
|
||||||
* Otherwise, `shiki()` is only called once per build, NOT once per page, so a cache isn't needed!
|
* Otherwise, `shiki()` is only called once per build, NOT once per page, so a cache isn't needed!
|
||||||
*/
|
*/
|
||||||
const highlighterCache = new Map<string, shikiTypes.Highlighter>();
|
const highlighterCache = new Map<string, Highlighter>();
|
||||||
|
|
||||||
export default async function shiki({
|
export default async function shiki({
|
||||||
langs = [],
|
langs = [],
|
||||||
theme = 'github-dark',
|
theme = 'github-dark',
|
||||||
wrap = false,
|
wrap = false,
|
||||||
}: ShikiConfig = {}): Promise<AstroMarkdocConfig> {
|
}: ShikiConfig = {}): Promise<AstroMarkdocConfig> {
|
||||||
const cacheID: string = typeof theme === 'string' ? theme : theme.name;
|
const cacheId = typeof theme === 'string' ? theme : theme.name || '';
|
||||||
if (!highlighterCache.has(cacheID)) {
|
let highlighter = highlighterCache.get(cacheId)!;
|
||||||
highlighterCache.set(
|
if (!highlighter) {
|
||||||
cacheID,
|
highlighter = await getHighlighter({
|
||||||
await getHighlighter({ theme }).then((hl) => {
|
langs: langs.length ? langs : Object.keys(bundledLanguages),
|
||||||
hl.setColorReplacements(ASTRO_COLOR_REPLACEMENTS);
|
themes: [theme],
|
||||||
return hl;
|
});
|
||||||
})
|
highlighterCache.set(cacheId, highlighter);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const highlighter = highlighterCache.get(cacheID)!;
|
|
||||||
|
|
||||||
for (const lang of langs) {
|
|
||||||
await highlighter.loadLanguage(lang);
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
nodes: {
|
nodes: {
|
||||||
fence: {
|
fence: {
|
||||||
|
@ -72,7 +71,7 @@ export default async function shiki({
|
||||||
lang = 'plaintext';
|
lang = 'plaintext';
|
||||||
}
|
}
|
||||||
|
|
||||||
let html = highlighter.codeToHtml(attributes.content, { lang });
|
let html = highlighter.codeToHtml(attributes.content, { lang, theme });
|
||||||
|
|
||||||
// Q: Could these regexes match on a user's inputted code blocks?
|
// Q: Could these regexes match on a user's inputted code blocks?
|
||||||
// A: Nope! All rendered HTML is properly escaped.
|
// A: Nope! All rendered HTML is properly escaped.
|
||||||
|
@ -98,6 +97,12 @@ export default async function shiki({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// theme.id for shiki -> shikiji compat
|
||||||
|
const themeName = typeof theme === 'string' ? theme : theme.name;
|
||||||
|
if (themeName === 'css-variables') {
|
||||||
|
html = html.replace(INLINE_STYLE_SELECTOR_GLOBAL, (m) => replaceCssVariables(m));
|
||||||
|
}
|
||||||
|
|
||||||
// Use `unescapeHTML` to return `HTMLString` for Astro renderer to inline as HTML
|
// Use `unescapeHTML` to return `HTMLString` for Astro renderer to inline as HTML
|
||||||
return unescapeHTML(html) as any;
|
return unescapeHTML(html) as any;
|
||||||
},
|
},
|
||||||
|
@ -105,3 +110,10 @@ export default async function shiki({
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shiki -> shikiji compat as we need to manually replace it
|
||||||
|
*/
|
||||||
|
function replaceCssVariables(str: string) {
|
||||||
|
return str.replace(COLOR_REPLACEMENT_REGEX, (match) => ASTRO_COLOR_REPLACEMENTS[match] || match);
|
||||||
|
}
|
||||||
|
|
|
@ -5,8 +5,5 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/markdoc": "workspace:*",
|
"@astrojs/markdoc": "workspace:*",
|
||||||
"astro": "workspace:*"
|
"astro": "workspace:*"
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"shiki": "^0.14.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
"remark-parse": "^10.0.2",
|
"remark-parse": "^10.0.2",
|
||||||
"remark-rehype": "^10.1.0",
|
"remark-rehype": "^10.1.0",
|
||||||
"remark-smartypants": "^2.0.0",
|
"remark-smartypants": "^2.0.0",
|
||||||
"shiki": "^0.14.3",
|
"shikiji": "^0.6.8",
|
||||||
"unified": "^10.1.2",
|
"unified": "^10.1.2",
|
||||||
"unist-util-visit": "^4.1.2",
|
"unist-util-visit": "^4.1.2",
|
||||||
"vfile": "^5.3.7"
|
"vfile": "^5.3.7"
|
||||||
|
|
|
@ -1,25 +1,8 @@
|
||||||
import type * as shiki from 'shiki';
|
import { bundledLanguages, getHighlighter, type Highlighter } from 'shikiji';
|
||||||
import { getHighlighter } from 'shiki';
|
|
||||||
import { visit } from 'unist-util-visit';
|
import { visit } from 'unist-util-visit';
|
||||||
import type { RemarkPlugin, ShikiConfig } from './types.js';
|
import type { RemarkPlugin, ShikiConfig } from './types.js';
|
||||||
|
|
||||||
/**
|
const ASTRO_COLOR_REPLACEMENTS: Record<string, string> = {
|
||||||
* getHighlighter() is the most expensive step of Shiki. Instead of calling it on every page,
|
|
||||||
* cache it here as much as possible. Make sure that your highlighters can be cached, state-free.
|
|
||||||
* We make this async, so that multiple calls to parse markdown still share the same highlighter.
|
|
||||||
*/
|
|
||||||
const highlighterCacheAsync = new Map<string, Promise<shiki.Highlighter>>();
|
|
||||||
|
|
||||||
export function remarkShiki({
|
|
||||||
langs = [],
|
|
||||||
theme = 'github-dark',
|
|
||||||
wrap = false,
|
|
||||||
}: ShikiConfig = {}): ReturnType<RemarkPlugin> {
|
|
||||||
const cacheID: string = typeof theme === 'string' ? theme : theme.name;
|
|
||||||
let highlighterAsync = highlighterCacheAsync.get(cacheID);
|
|
||||||
if (!highlighterAsync) {
|
|
||||||
highlighterAsync = getHighlighter({ theme }).then((hl) => {
|
|
||||||
hl.setColorReplacements({
|
|
||||||
'#000001': 'var(--astro-code-color-text)',
|
'#000001': 'var(--astro-code-color-text)',
|
||||||
'#000002': 'var(--astro-code-color-background)',
|
'#000002': 'var(--astro-code-color-background)',
|
||||||
'#000004': 'var(--astro-code-token-constant)',
|
'#000004': 'var(--astro-code-token-constant)',
|
||||||
|
@ -31,26 +14,39 @@ export function remarkShiki({
|
||||||
'#000010': 'var(--astro-code-token-string-expression)',
|
'#000010': 'var(--astro-code-token-string-expression)',
|
||||||
'#000011': 'var(--astro-code-token-punctuation)',
|
'#000011': 'var(--astro-code-token-punctuation)',
|
||||||
'#000012': 'var(--astro-code-token-link)',
|
'#000012': 'var(--astro-code-token-link)',
|
||||||
});
|
};
|
||||||
return hl;
|
const COLOR_REPLACEMENT_REGEX = new RegExp(
|
||||||
});
|
`(${Object.keys(ASTRO_COLOR_REPLACEMENTS).join('|')})`,
|
||||||
highlighterCacheAsync.set(cacheID, highlighterAsync);
|
'g'
|
||||||
}
|
);
|
||||||
|
|
||||||
let highlighter: shiki.Highlighter;
|
/**
|
||||||
|
* getHighlighter() is the most expensive step of Shiki. Instead of calling it on every page,
|
||||||
|
* cache it here as much as possible. Make sure that your highlighters can be cached, state-free.
|
||||||
|
* We make this async, so that multiple calls to parse markdown still share the same highlighter.
|
||||||
|
*/
|
||||||
|
const highlighterCacheAsync = new Map<string, Promise<Highlighter>>();
|
||||||
|
|
||||||
|
export function remarkShiki({
|
||||||
|
langs = [],
|
||||||
|
theme = 'github-dark',
|
||||||
|
wrap = false,
|
||||||
|
}: ShikiConfig = {}): ReturnType<RemarkPlugin> {
|
||||||
|
const cacheId =
|
||||||
|
(typeof theme === 'string' ? theme : theme.name ?? '') +
|
||||||
|
langs.map((l) => l.name ?? (l as any).id).join(',');
|
||||||
|
|
||||||
|
let highlighterAsync = highlighterCacheAsync.get(cacheId);
|
||||||
|
if (!highlighterAsync) {
|
||||||
|
highlighterAsync = getHighlighter({
|
||||||
|
langs: langs.length ? langs : Object.keys(bundledLanguages),
|
||||||
|
themes: [theme],
|
||||||
|
});
|
||||||
|
highlighterCacheAsync.set(cacheId, highlighterAsync);
|
||||||
|
}
|
||||||
|
|
||||||
return async (tree: any) => {
|
return async (tree: any) => {
|
||||||
// Lazily assign the highlighter as async can only happen within this function,
|
const highlighter = await highlighterAsync!;
|
||||||
// and not on `remarkShiki` directly.
|
|
||||||
if (!highlighter) {
|
|
||||||
highlighter = await highlighterAsync!;
|
|
||||||
|
|
||||||
// NOTE: There may be a performance issue here for large sites that use `lang`.
|
|
||||||
// Since this will be called on every page load. Unclear how to fix this.
|
|
||||||
for (const lang of langs) {
|
|
||||||
await highlighter.loadLanguage(lang);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(tree, 'code', (node) => {
|
visit(tree, 'code', (node) => {
|
||||||
let lang: string;
|
let lang: string;
|
||||||
|
@ -68,7 +64,7 @@ export function remarkShiki({
|
||||||
lang = 'plaintext';
|
lang = 'plaintext';
|
||||||
}
|
}
|
||||||
|
|
||||||
let html = highlighter.codeToHtml(node.value, { lang });
|
let html = highlighter.codeToHtml(node.value, { lang, theme });
|
||||||
|
|
||||||
// Q: Couldn't these regexes match on a user's inputted code blocks?
|
// Q: Couldn't these regexes match on a user's inputted code blocks?
|
||||||
// A: Nope! All rendered HTML is properly escaped.
|
// A: Nope! All rendered HTML is properly escaped.
|
||||||
|
@ -96,9 +92,22 @@ export function remarkShiki({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// theme.id for shiki -> shikiji compat
|
||||||
|
const themeName = typeof theme === 'string' ? theme : theme.name;
|
||||||
|
if (themeName === 'css-variables') {
|
||||||
|
html = html.replace(/style="(.*?)"/g, (m) => replaceCssVariables(m));
|
||||||
|
}
|
||||||
|
|
||||||
node.type = 'html';
|
node.type = 'html';
|
||||||
node.value = html;
|
node.value = html;
|
||||||
node.children = [];
|
node.children = [];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shiki -> shikiji compat as we need to manually replace it
|
||||||
|
*/
|
||||||
|
function replaceCssVariables(str: string) {
|
||||||
|
return str.replace(COLOR_REPLACEMENT_REGEX, (match) => ASTRO_COLOR_REPLACEMENTS[match] || match);
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,12 @@ import type {
|
||||||
all as Handlers,
|
all as Handlers,
|
||||||
Options as RemarkRehypeOptions,
|
Options as RemarkRehypeOptions,
|
||||||
} from 'remark-rehype';
|
} from 'remark-rehype';
|
||||||
import type { ILanguageRegistration, IThemeRegistration, Theme } from 'shiki';
|
import type {
|
||||||
|
BuiltinTheme,
|
||||||
|
LanguageRegistration,
|
||||||
|
ThemeRegistration,
|
||||||
|
ThemeRegistrationRaw,
|
||||||
|
} from 'shikiji';
|
||||||
import type * as unified from 'unified';
|
import type * as unified from 'unified';
|
||||||
import type { VFile } from 'vfile';
|
import type { VFile } from 'vfile';
|
||||||
|
|
||||||
|
@ -35,8 +40,8 @@ export type RemarkRehype = Omit<RemarkRehypeOptions, 'handlers' | 'unknownHandle
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ShikiConfig {
|
export interface ShikiConfig {
|
||||||
langs?: ILanguageRegistration[];
|
langs?: LanguageRegistration[];
|
||||||
theme?: Theme | IThemeRegistration;
|
theme?: BuiltinTheme | ThemeRegistration | ThemeRegistrationRaw;
|
||||||
wrap?: boolean | null;
|
wrap?: boolean | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
197
pnpm-lock.yaml
197
pnpm-lock.yaml
|
@ -616,9 +616,9 @@ importers:
|
||||||
server-destroy:
|
server-destroy:
|
||||||
specifier: ^1.0.1
|
specifier: ^1.0.1
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
shiki:
|
shikiji:
|
||||||
specifier: ^0.14.3
|
specifier: ^0.6.8
|
||||||
version: 0.14.3
|
version: 0.6.8
|
||||||
string-width:
|
string-width:
|
||||||
specifier: ^6.1.0
|
specifier: ^6.1.0
|
||||||
version: 6.1.0
|
version: 6.1.0
|
||||||
|
@ -3878,9 +3878,9 @@ importers:
|
||||||
kleur:
|
kleur:
|
||||||
specifier: ^4.1.5
|
specifier: ^4.1.5
|
||||||
version: 4.1.5
|
version: 4.1.5
|
||||||
shiki:
|
shikiji:
|
||||||
specifier: ^0.14.3
|
specifier: ^0.6.8
|
||||||
version: 0.14.3
|
version: 0.6.8
|
||||||
zod:
|
zod:
|
||||||
specifier: 3.21.1
|
specifier: 3.21.1
|
||||||
version: 3.21.1
|
version: 3.21.1
|
||||||
|
@ -4005,10 +4005,6 @@ importers:
|
||||||
astro:
|
astro:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../../../../astro
|
version: link:../../../../../astro
|
||||||
devDependencies:
|
|
||||||
shiki:
|
|
||||||
specifier: ^0.14.3
|
|
||||||
version: 0.14.3
|
|
||||||
|
|
||||||
packages/integrations/markdoc/test/fixtures/render-with-config:
|
packages/integrations/markdoc/test/fixtures/render-with-config:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4944,9 +4940,9 @@ importers:
|
||||||
remark-smartypants:
|
remark-smartypants:
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
shiki:
|
shikiji:
|
||||||
specifier: ^0.14.3
|
specifier: ^0.6.8
|
||||||
version: 0.14.3
|
version: 0.6.8
|
||||||
unified:
|
unified:
|
||||||
specifier: ^10.1.2
|
specifier: ^10.1.2
|
||||||
version: 10.1.2
|
version: 10.1.2
|
||||||
|
@ -9199,6 +9195,10 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@ungap/structured-clone@1.2.0:
|
||||||
|
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@vercel/analytics@1.0.2:
|
/@vercel/analytics@1.0.2:
|
||||||
resolution: {integrity: sha512-BZFxVrv24VbNNl5xMxqUojQIegEeXMI6rX3rg1uVLYUEXsuKNBSAEQf4BWEcjQDp/8aYJOj6m8V4PUA3x/cxgg==}
|
resolution: {integrity: sha512-BZFxVrv24VbNNl5xMxqUojQIegEeXMI6rX3rg1uVLYUEXsuKNBSAEQf4BWEcjQDp/8aYJOj6m8V4PUA3x/cxgg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -9627,9 +9627,6 @@ packages:
|
||||||
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
|
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
/ansi-sequence-parser@1.1.0:
|
|
||||||
resolution: {integrity: sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==}
|
|
||||||
|
|
||||||
/ansi-styles@3.2.1:
|
/ansi-styles@3.2.1:
|
||||||
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
|
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
@ -12216,6 +12213,19 @@ packages:
|
||||||
web-namespaces: 2.0.1
|
web-namespaces: 2.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/hast-util-from-parse5@8.0.1:
|
||||||
|
resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==}
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.0
|
||||||
|
'@types/unist': 3.0.0
|
||||||
|
devlop: 1.1.0
|
||||||
|
hastscript: 8.0.0
|
||||||
|
property-information: 6.2.0
|
||||||
|
vfile: 6.0.1
|
||||||
|
vfile-location: 5.0.2
|
||||||
|
web-namespaces: 2.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/hast-util-has-property@2.0.1:
|
/hast-util-has-property@2.0.1:
|
||||||
resolution: {integrity: sha512-X2+RwZIMTMKpXUzlotatPzWj8bspCymtXH3cfG3iQKV+wPF53Vgaqxi/eLqGck0wKq1kS9nvoB1wchbCPEL8sg==}
|
resolution: {integrity: sha512-X2+RwZIMTMKpXUzlotatPzWj8bspCymtXH3cfG3iQKV+wPF53Vgaqxi/eLqGck0wKq1kS9nvoB1wchbCPEL8sg==}
|
||||||
|
|
||||||
|
@ -12241,6 +12251,12 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 2.3.5
|
'@types/hast': 2.3.5
|
||||||
|
|
||||||
|
/hast-util-parse-selector@4.0.0:
|
||||||
|
resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/hast-util-raw@7.2.3:
|
/hast-util-raw@7.2.3:
|
||||||
resolution: {integrity: sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==}
|
resolution: {integrity: sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -12257,6 +12273,24 @@ packages:
|
||||||
zwitch: 2.0.4
|
zwitch: 2.0.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/hast-util-raw@9.0.1:
|
||||||
|
resolution: {integrity: sha512-5m1gmba658Q+lO5uqL5YNGQWeh1MYWZbZmWrM5lncdcuiXuo5E2HT/CIOp0rLF8ksfSwiCVJ3twlgVRyTGThGA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.0
|
||||||
|
'@types/unist': 3.0.0
|
||||||
|
'@ungap/structured-clone': 1.2.0
|
||||||
|
hast-util-from-parse5: 8.0.1
|
||||||
|
hast-util-to-parse5: 8.0.0
|
||||||
|
html-void-elements: 3.0.0
|
||||||
|
mdast-util-to-hast: 13.0.2
|
||||||
|
parse5: 7.1.2
|
||||||
|
unist-util-position: 5.0.0
|
||||||
|
unist-util-visit: 5.0.0
|
||||||
|
vfile: 6.0.1
|
||||||
|
web-namespaces: 2.0.1
|
||||||
|
zwitch: 2.0.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/hast-util-select@5.0.5:
|
/hast-util-select@5.0.5:
|
||||||
resolution: {integrity: sha512-QQhWMhgTFRhCaQdgTKzZ5g31GLQ9qRb1hZtDPMqQaOhpLBziWcshUS0uCR5IJ0U1jrK/mxg35fmcq+Dp/Cy2Aw==}
|
resolution: {integrity: sha512-QQhWMhgTFRhCaQdgTKzZ5g31GLQ9qRb1hZtDPMqQaOhpLBziWcshUS0uCR5IJ0U1jrK/mxg35fmcq+Dp/Cy2Aw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -12336,6 +12370,23 @@ packages:
|
||||||
zwitch: 2.0.4
|
zwitch: 2.0.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/hast-util-to-html@9.0.0:
|
||||||
|
resolution: {integrity: sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.0
|
||||||
|
'@types/unist': 3.0.0
|
||||||
|
ccount: 2.0.1
|
||||||
|
comma-separated-tokens: 2.0.3
|
||||||
|
hast-util-raw: 9.0.1
|
||||||
|
hast-util-whitespace: 3.0.0
|
||||||
|
html-void-elements: 3.0.0
|
||||||
|
mdast-util-to-hast: 13.0.2
|
||||||
|
property-information: 6.2.0
|
||||||
|
space-separated-tokens: 2.0.2
|
||||||
|
stringify-entities: 4.0.3
|
||||||
|
zwitch: 2.0.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/hast-util-to-parse5@7.1.0:
|
/hast-util-to-parse5@7.1.0:
|
||||||
resolution: {integrity: sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==}
|
resolution: {integrity: sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -12347,6 +12398,18 @@ packages:
|
||||||
zwitch: 2.0.4
|
zwitch: 2.0.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/hast-util-to-parse5@8.0.0:
|
||||||
|
resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.0
|
||||||
|
comma-separated-tokens: 2.0.3
|
||||||
|
devlop: 1.1.0
|
||||||
|
property-information: 6.2.0
|
||||||
|
space-separated-tokens: 2.0.2
|
||||||
|
web-namespaces: 2.0.1
|
||||||
|
zwitch: 2.0.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/hast-util-to-string@2.0.0:
|
/hast-util-to-string@2.0.0:
|
||||||
resolution: {integrity: sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==}
|
resolution: {integrity: sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -12380,6 +12443,16 @@ packages:
|
||||||
property-information: 6.2.0
|
property-information: 6.2.0
|
||||||
space-separated-tokens: 2.0.2
|
space-separated-tokens: 2.0.2
|
||||||
|
|
||||||
|
/hastscript@8.0.0:
|
||||||
|
resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.0
|
||||||
|
comma-separated-tokens: 2.0.3
|
||||||
|
hast-util-parse-selector: 4.0.0
|
||||||
|
property-information: 6.2.0
|
||||||
|
space-separated-tokens: 2.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/hdr-histogram-js@3.0.0:
|
/hdr-histogram-js@3.0.0:
|
||||||
resolution: {integrity: sha512-/EpvQI2/Z98mNFYEnlqJ8Ogful8OpArLG/6Tf2bPnkutBVLIeMVNHjk1ZDfshF2BUweipzbk+dB1hgSB7SIakw==}
|
resolution: {integrity: sha512-/EpvQI2/Z98mNFYEnlqJ8Ogful8OpArLG/6Tf2bPnkutBVLIeMVNHjk1ZDfshF2BUweipzbk+dB1hgSB7SIakw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
@ -12438,6 +12511,10 @@ packages:
|
||||||
resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==}
|
resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/html-void-elements@3.0.0:
|
||||||
|
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/htmlparser2@8.0.2:
|
/htmlparser2@8.0.2:
|
||||||
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
|
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -13591,6 +13668,19 @@ packages:
|
||||||
unist-util-position: 4.0.4
|
unist-util-position: 4.0.4
|
||||||
unist-util-visit: 4.1.2
|
unist-util-visit: 4.1.2
|
||||||
|
|
||||||
|
/mdast-util-to-hast@13.0.2:
|
||||||
|
resolution: {integrity: sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og==}
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.0
|
||||||
|
'@types/mdast': 4.0.0
|
||||||
|
'@ungap/structured-clone': 1.2.0
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-util-sanitize-uri: 2.0.0
|
||||||
|
trim-lines: 3.0.1
|
||||||
|
unist-util-position: 5.0.0
|
||||||
|
unist-util-visit: 5.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/mdast-util-to-markdown@1.5.0:
|
/mdast-util-to-markdown@1.5.0:
|
||||||
resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==}
|
resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -13919,6 +14009,13 @@ packages:
|
||||||
micromark-util-symbol: 1.1.0
|
micromark-util-symbol: 1.1.0
|
||||||
micromark-util-types: 1.1.0
|
micromark-util-types: 1.1.0
|
||||||
|
|
||||||
|
/micromark-util-character@2.0.1:
|
||||||
|
resolution: {integrity: sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==}
|
||||||
|
dependencies:
|
||||||
|
micromark-util-symbol: 2.0.0
|
||||||
|
micromark-util-types: 2.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/micromark-util-chunked@1.0.0:
|
/micromark-util-chunked@1.0.0:
|
||||||
resolution: {integrity: sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==}
|
resolution: {integrity: sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -13953,6 +14050,10 @@ packages:
|
||||||
/micromark-util-encode@1.0.1:
|
/micromark-util-encode@1.0.1:
|
||||||
resolution: {integrity: sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==}
|
resolution: {integrity: sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==}
|
||||||
|
|
||||||
|
/micromark-util-encode@2.0.0:
|
||||||
|
resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/micromark-util-events-to-acorn@1.2.3:
|
/micromark-util-events-to-acorn@1.2.3:
|
||||||
resolution: {integrity: sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==}
|
resolution: {integrity: sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -13986,6 +14087,14 @@ packages:
|
||||||
micromark-util-encode: 1.0.1
|
micromark-util-encode: 1.0.1
|
||||||
micromark-util-symbol: 1.1.0
|
micromark-util-symbol: 1.1.0
|
||||||
|
|
||||||
|
/micromark-util-sanitize-uri@2.0.0:
|
||||||
|
resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
|
||||||
|
dependencies:
|
||||||
|
micromark-util-character: 2.0.1
|
||||||
|
micromark-util-encode: 2.0.0
|
||||||
|
micromark-util-symbol: 2.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/micromark-util-subtokenize@1.0.2:
|
/micromark-util-subtokenize@1.0.2:
|
||||||
resolution: {integrity: sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==}
|
resolution: {integrity: sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -13997,12 +14106,20 @@ packages:
|
||||||
/micromark-util-symbol@1.1.0:
|
/micromark-util-symbol@1.1.0:
|
||||||
resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==}
|
resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==}
|
||||||
|
|
||||||
|
/micromark-util-symbol@2.0.0:
|
||||||
|
resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/micromark-util-types@1.0.2:
|
/micromark-util-types@1.0.2:
|
||||||
resolution: {integrity: sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==}
|
resolution: {integrity: sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==}
|
||||||
|
|
||||||
/micromark-util-types@1.1.0:
|
/micromark-util-types@1.1.0:
|
||||||
resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==}
|
resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==}
|
||||||
|
|
||||||
|
/micromark-util-types@2.0.0:
|
||||||
|
resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/micromark@3.2.0:
|
/micromark@3.2.0:
|
||||||
resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==}
|
resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -16229,13 +16346,11 @@ packages:
|
||||||
vscode-textmate: 5.2.0
|
vscode-textmate: 5.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/shiki@0.14.3:
|
/shikiji@0.6.8:
|
||||||
resolution: {integrity: sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g==}
|
resolution: {integrity: sha512-K0axxNAdB9KvLUflU7QoLC7p6i2p1R2MFG0eP+iclbjtuEZqng99jHcg3VJL0GWRO67yozTICnykjo1HjOzdkg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-sequence-parser: 1.1.0
|
hast-util-to-html: 9.0.0
|
||||||
jsonc-parser: 3.2.0
|
dev: false
|
||||||
vscode-oniguruma: 1.7.0
|
|
||||||
vscode-textmate: 8.0.0
|
|
||||||
|
|
||||||
/side-channel@1.0.4:
|
/side-channel@1.0.4:
|
||||||
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
|
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
|
||||||
|
@ -17368,6 +17483,12 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 2.0.7
|
'@types/unist': 2.0.7
|
||||||
|
|
||||||
|
/unist-util-position@5.0.0:
|
||||||
|
resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/unist-util-remove-position@4.0.2:
|
/unist-util-remove-position@4.0.2:
|
||||||
resolution: {integrity: sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==}
|
resolution: {integrity: sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -17388,6 +17509,12 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 2.0.7
|
'@types/unist': 2.0.7
|
||||||
|
|
||||||
|
/unist-util-stringify-position@4.0.0:
|
||||||
|
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/unist-util-visit-children@2.0.2:
|
/unist-util-visit-children@2.0.2:
|
||||||
resolution: {integrity: sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q==}
|
resolution: {integrity: sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -17574,12 +17701,26 @@ packages:
|
||||||
vfile: 5.3.7
|
vfile: 5.3.7
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/vfile-location@5.0.2:
|
||||||
|
resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==}
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.0
|
||||||
|
vfile: 6.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/vfile-message@3.1.4:
|
/vfile-message@3.1.4:
|
||||||
resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==}
|
resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 2.0.7
|
'@types/unist': 2.0.7
|
||||||
unist-util-stringify-position: 3.0.3
|
unist-util-stringify-position: 3.0.3
|
||||||
|
|
||||||
|
/vfile-message@4.0.2:
|
||||||
|
resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.0
|
||||||
|
unist-util-stringify-position: 4.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/vfile@5.3.7:
|
/vfile@5.3.7:
|
||||||
resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==}
|
resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -17588,6 +17729,14 @@ packages:
|
||||||
unist-util-stringify-position: 3.0.3
|
unist-util-stringify-position: 3.0.3
|
||||||
vfile-message: 3.1.4
|
vfile-message: 3.1.4
|
||||||
|
|
||||||
|
/vfile@6.0.1:
|
||||||
|
resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.0
|
||||||
|
unist-util-stringify-position: 4.0.0
|
||||||
|
vfile-message: 4.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/vite-node@0.34.2(@types/node@20.5.3):
|
/vite-node@0.34.2(@types/node@20.5.3):
|
||||||
resolution: {integrity: sha512-JtW249Zm3FB+F7pQfH56uWSdlltCo1IOkZW5oHBzeQo0iX4jtC7o1t9aILMGd9kVekXBP2lfJBEQt9rBh07ebA==}
|
resolution: {integrity: sha512-JtW249Zm3FB+F7pQfH56uWSdlltCo1IOkZW5oHBzeQo0iX4jtC7o1t9aILMGd9kVekXBP2lfJBEQt9rBh07ebA==}
|
||||||
engines: {node: '>=v14.18.0'}
|
engines: {node: '>=v14.18.0'}
|
||||||
|
@ -17940,14 +18089,12 @@ packages:
|
||||||
|
|
||||||
/vscode-oniguruma@1.7.0:
|
/vscode-oniguruma@1.7.0:
|
||||||
resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==}
|
resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/vscode-textmate@5.2.0:
|
/vscode-textmate@5.2.0:
|
||||||
resolution: {integrity: sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==}
|
resolution: {integrity: sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vscode-textmate@8.0.0:
|
|
||||||
resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
|
|
||||||
|
|
||||||
/vscode-uri@2.1.2:
|
/vscode-uri@2.1.2:
|
||||||
resolution: {integrity: sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==}
|
resolution: {integrity: sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
Loading…
Reference in a new issue