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
|
// Load custom lang if passed an object, otherwise load the default
|
||||||
langs: typeof lang !== 'string' ? [lang] : undefined,
|
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);
|
const html = repairShikiTheme(_html);
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
import { getHighlighter as getShikiHighlighter } from 'shiki';
|
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
|
// Caches Promise<Highligher> for reuse when the same theme and langs are provided
|
||||||
const _resolvedHighlighters = new Map();
|
const _resolvedHighlighters = new Map();
|
||||||
|
|
||||||
|
/** @type {Promise<any>} */
|
||||||
|
let _allLanguages;
|
||||||
|
|
||||||
function stringify(opts) {
|
function stringify(opts) {
|
||||||
// Always sort keys before stringifying to make sure objects match regardless of parameter ordering
|
// Always sort keys before stringifying to make sure objects match regardless of parameter ordering
|
||||||
return JSON.stringify(opts, Object.keys(opts).sort());
|
return JSON.stringify(opts, Object.keys(opts).sort());
|
||||||
|
@ -12,16 +17,34 @@ function stringify(opts) {
|
||||||
* @param {import('shiki').HighlighterOptions} opts
|
* @param {import('shiki').HighlighterOptions} opts
|
||||||
* @returns {Promise<import('shiki').Highlighter>}
|
* @returns {Promise<import('shiki').Highlighter>}
|
||||||
*/
|
*/
|
||||||
export function getHighlighter(opts) {
|
async function resolveHighlighter(opts) {
|
||||||
const key = stringify(opts);
|
const resolvedThemes = [];
|
||||||
|
if(opts.theme && (opts.theme in themes)) {
|
||||||
// Highlighter has already been requested, reuse the same instance
|
resolvedThemes.push(await themes[opts.theme]());
|
||||||
if (_resolvedHighlighters.has(key)) {
|
|
||||||
return _resolvedHighlighters.get(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// Start the async getHighlighter call and cache the Promise
|
||||||
const highlighter = getShikiHighlighter(opts).then((hl) => {
|
const highlighter = getShikiHighlighter(highlighterOptions).then((hl) => {
|
||||||
hl.setColorReplacements({
|
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)',
|
||||||
|
@ -37,6 +60,23 @@ export function getHighlighter(opts) {
|
||||||
});
|
});
|
||||||
return hl;
|
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);
|
_resolvedHighlighters.set(key, highlighter);
|
||||||
|
|
||||||
return 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…
Add table
Reference in a new issue