Allow Code component to run in Vercel (#5409)
* Allow Code component to run in Vercel * Adding a changeset * Pass theme through to `codeToHtml` * Use script to generate languages * another bundling approach * fix lint warnings
This commit is contained in:
parent
6c0f966ef8
commit
9f80a4046f
7 changed files with 1745 additions and 8 deletions
5
.changeset/cuddly-eyes-change.md
Normal file
5
.changeset/cuddly-eyes-change.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fix Code component usage in Vercel
|
|
@ -56,7 +56,10 @@ const highlighter = await getHighlighter({
|
|||
// Load custom lang if passed an object, otherwise load the default
|
||||
langs: typeof lang !== 'string' ? [lang] : undefined,
|
||||
});
|
||||
const _html = highlighter.codeToHtml(code, { lang: typeof lang === 'string' ? lang : lang.id });
|
||||
const _html = highlighter.codeToHtml(code, {
|
||||
lang: typeof lang === 'string' ? lang : lang.id,
|
||||
theme
|
||||
});
|
||||
const html = repairShikiTheme(_html);
|
||||
---
|
||||
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import { getHighlighter as getShikiHighlighter } from 'shiki';
|
||||
import { themes } from './shiki-themes.js';
|
||||
import { languages } from './shiki-languages.js';
|
||||
|
||||
// Caches Promise<Highligher> 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());
|
||||
|
@ -12,16 +17,34 @@ function stringify(opts) {
|
|||
* @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);
|
||||
async function resolveHighlighter(opts) {
|
||||
const resolvedThemes = [];
|
||||
if(opts.theme && (opts.theme in themes)) {
|
||||
resolvedThemes.push(await themes[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'];
|
||||
|
||||
// Start the async getHighlighter call and cache the Promise
|
||||
const highlighter = getShikiHighlighter(opts).then((hl) => {
|
||||
const highlighter = getShikiHighlighter(highlighterOptions).then((hl) => {
|
||||
hl.setColorReplacements({
|
||||
'#000001': 'var(--astro-code-color-text)',
|
||||
'#000002': 'var(--astro-code-color-background)',
|
||||
|
@ -37,6 +60,23 @@ export function getHighlighter(opts) {
|
|||
});
|
||||
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;
|
||||
|
|
1588
packages/astro/components/shiki-languages.js
Normal file
1588
packages/astro/components/shiki-languages.js
Normal file
File diff suppressed because it is too large
Load diff
31
packages/astro/components/shiki-themes.js
Normal file
31
packages/astro/components/shiki-themes.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
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-darker': () => import('shiki/themes/material-darker.json').then(mod => mod.default),
|
||||
'material-default': () => import('shiki/themes/material-default.json').then(mod => mod.default),
|
||||
'material-lighter': () => import('shiki/themes/material-lighter.json').then(mod => mod.default),
|
||||
'material-ocean': () => import('shiki/themes/material-ocean.json').then(mod => mod.default),
|
||||
'material-palenight': () => import('shiki/themes/material-palenight.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)
|
||||
};
|
41
packages/astro/scripts/shiki-gen-languages.mjs
Normal file
41
packages/astro/scripts/shiki-gen-languages.mjs
Normal file
|
@ -0,0 +1,41 @@
|
|||
import fs from '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 => mod.default).then(grammar => {
|
||||
const lang = BUNDLED_LANGUAGES.find(l => l.id === '${key}');
|
||||
if(lang) {
|
||||
return {
|
||||
...lang,
|
||||
grammar
|
||||
};
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
})`
|
||||
];
|
||||
});
|
||||
let code = `import { BUNDLED_LANGUAGES } from 'shiki';
|
||||
|
||||
export const languages = {`;
|
||||
let i = 0;
|
||||
for(const [key, imp] of langImports) {
|
||||
if(i > 0) {
|
||||
code += ',';
|
||||
}
|
||||
code += `\n\t'${key}': () => ${imp}`;
|
||||
i++;
|
||||
}
|
||||
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
|
||||
*/
|
29
packages/astro/scripts/shiki-gen-themes.mjs
Normal file
29
packages/astro/scripts/shiki-gen-themes.mjs
Normal file
|
@ -0,0 +1,29 @@
|
|||
import fs from 'fs';
|
||||
|
||||
const dir = await fs.promises.readdir('packages/astro/node_modules/shiki/themes/');
|
||||
|
||||
const themeImports = dir.map(f => {
|
||||
return [
|
||||
f.slice(0, f.indexOf('.json')),
|
||||
`import('shiki/themes/${f}').then(mod => mod.default)`
|
||||
];
|
||||
});
|
||||
|
||||
let code = `export const themes = {`;
|
||||
let i = 0;
|
||||
for(const [key, imp] of themeImports) {
|
||||
if(i > 0) {
|
||||
code += ',';
|
||||
}
|
||||
code += `\n\t'${key}': () => ${imp}`;
|
||||
i++;
|
||||
}
|
||||
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
|
||||
*/
|
Loading…
Reference in a new issue