Fix css variables theme
This commit is contained in:
parent
6e497d87b1
commit
ef5a72ecae
5 changed files with 100 additions and 13 deletions
|
@ -7,7 +7,8 @@ import type {
|
|||
ThemeRegistration,
|
||||
ThemeRegistrationRaw,
|
||||
} from 'shikiji';
|
||||
import { getCachedHighlighter } from './shiki.js';
|
||||
import { visit } from 'unist-util-visit';
|
||||
import { getCachedHighlighter, replaceCssVariables } from './shiki.js';
|
||||
|
||||
interface Props {
|
||||
/** The code to highlight. Required. */
|
||||
|
@ -100,6 +101,18 @@ const html = highlighter.codeToHtml(code, {
|
|||
return node.children[0] as typeof node;
|
||||
}
|
||||
},
|
||||
root(node) {
|
||||
// theme.id for shiki -> shikiji compat
|
||||
const themeName = typeof theme === 'string' ? theme : (theme as any).id || 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
---
|
||||
|
|
|
@ -2,9 +2,34 @@ 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());
|
||||
|
|
|
@ -15,7 +15,7 @@ describe('<Code>', () => {
|
|||
const $ = cheerio.load(html);
|
||||
expect($('pre')).to.have.lengthOf(1);
|
||||
expect($('pre').attr('style')).to.equal(
|
||||
'-background-color:#24292e;color:#e1e4e8; overflow-x: auto;',
|
||||
'background-color:#24292e;color:#e1e4e8; overflow-x: auto;',
|
||||
'applies default and overflow'
|
||||
);
|
||||
expect($('pre > code')).to.have.lengthOf(1);
|
||||
|
@ -60,14 +60,16 @@ describe('<Code>', () => {
|
|||
const $ = cheerio.load(html);
|
||||
expect($('pre')).to.have.lengthOf(1);
|
||||
// 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');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('pre')).to.have.lengthOf(1);
|
||||
// test: applies wrap overflow
|
||||
expect($('pre').attr('style')).to.equal('background-color: #24292e');
|
||||
expect($('pre').attr('style')).to.equal('background-color:#24292e;color:#e1e4e8');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -76,18 +78,17 @@ describe('<Code>', () => {
|
|||
const $ = cheerio.load(html);
|
||||
expect($('pre')).to.have.lengthOf(1);
|
||||
expect($('pre').attr('class')).to.equal('astro-code css-variables');
|
||||
// TODO: We can't specify CSS vars with shikiji
|
||||
expect(
|
||||
$('pre, pre span')
|
||||
.map((i, f) => (f.attribs ? f.attribs.style : 'no style found'))
|
||||
.toArray()
|
||||
).to.deep.equal([
|
||||
'background-color: var(--astro-code-color-background); overflow-x: auto;',
|
||||
'color: var(--astro-code-token-constant)',
|
||||
'color: var(--astro-code-token-function)',
|
||||
'color: var(--astro-code-color-text)',
|
||||
'color: var(--astro-code-token-string-expression)',
|
||||
'color: var(--astro-code-color-text)',
|
||||
'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-function)',
|
||||
'color:var(--astro-code-color-text)',
|
||||
'color:var(--astro-code-token-string-expression)',
|
||||
'color:var(--astro-code-color-text)',
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { unescapeHTML } from 'astro/runtime/server/index.js';
|
|||
import { bundledLanguages, getHighlighter, type Highlighter } from 'shikiji';
|
||||
import type { AstroMarkdocConfig } from '../config.js';
|
||||
|
||||
const ASTRO_COLOR_REPLACEMENTS = {
|
||||
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)',
|
||||
|
@ -16,7 +16,11 @@ const ASTRO_COLOR_REPLACEMENTS = {
|
|||
'#000010': 'var(--astro-code-token-string-expression)',
|
||||
'#000011': 'var(--astro-code-token-punctuation)',
|
||||
'#000012': 'var(--astro-code-token-link)',
|
||||
} as const;
|
||||
};
|
||||
const COLOR_REPLACEMENT_REGEX = new RegExp(
|
||||
`(${Object.keys(ASTRO_COLOR_REPLACEMENTS).join('|')})`,
|
||||
'g'
|
||||
);
|
||||
|
||||
const PRE_SELECTOR = /<pre class="(.*?)shiki(.*?)"/;
|
||||
const LINE_SELECTOR = /<span class="line"><span style="(.*?)">([\+|\-])/g;
|
||||
|
@ -92,6 +96,12 @@ export default async function shiki({
|
|||
);
|
||||
}
|
||||
|
||||
// theme.id for shiki -> shikiji compat
|
||||
const themeName = typeof theme === 'string' ? theme : (theme as any).id || theme.name;
|
||||
if (themeName === 'css-variables') {
|
||||
html = html.replace(/style="(.*?)"/g, (m) => replaceCssVariables(m));
|
||||
}
|
||||
|
||||
// Use `unescapeHTML` to return `HTMLString` for Astro renderer to inline as HTML
|
||||
return unescapeHTML(html) as any;
|
||||
},
|
||||
|
@ -99,3 +109,10 @@ export default async function shiki({
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,24 @@ import { bundledLanguages, getHighlighter, type Highlighter } from 'shikiji';
|
|||
import { visit } from 'unist-util-visit';
|
||||
import type { RemarkPlugin, ShikiConfig } from './types.js';
|
||||
|
||||
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'
|
||||
);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -74,9 +92,22 @@ export function remarkShiki({
|
|||
);
|
||||
}
|
||||
|
||||
// theme.id for shiki -> shikiji compat
|
||||
const themeName = typeof theme === 'string' ? theme : (theme as any).id || theme.name;
|
||||
if (themeName === 'css-variables') {
|
||||
html = html.replace(/style="(.*?)"/g, (m) => replaceCssVariables(m));
|
||||
}
|
||||
|
||||
node.type = 'html';
|
||||
node.value = html;
|
||||
node.children = [];
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue