Skip to content

Commit

Permalink
Allow Code component to run in Vercel (withastro#5409)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
matthewp committed Nov 16, 2022
1 parent 6c0f966 commit 9f80a40
Show file tree
Hide file tree
Showing 7 changed files with 1,744 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/cuddly-eyes-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fix Code component usage in Vercel
5 changes: 4 additions & 1 deletion packages/astro/components/Code.astro
Original file line number Diff line number Diff line change
Expand Up @@ -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);
---

Expand Down
52 changes: 46 additions & 6 deletions packages/astro/components/Shiki.js
Original file line number Diff line number Diff line change
@@ -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());
Expand All @@ -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);
async function resolveHighlighter(opts) {
const resolvedThemes = [];
if(opts.theme && (opts.theme in themes)) {
resolvedThemes.push(await themes[opts.theme]());
}

// Highlighter has already been requested, reuse the same instance
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
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)',
Expand All @@ -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;
Expand Down
Loading

0 comments on commit 9f80a40

Please sign in to comment.