summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Emanuele Stoppa <my.burning@gmail.com> 2024-10-09 10:51:38 +0100
committerGravatar GitHub <noreply@github.com> 2024-10-09 10:51:38 +0100
commit710a1a11f488ff6ed3da6d3e0723b2322ccfe27b (patch)
tree0e928b9b1e9744739e49d8322b6f72d747ad45ef
parent3ac2263ff6070136bec9cffb863c38bcc31ccdfe (diff)
downloadastro-710a1a11f488ff6ed3da6d3e0723b2322ccfe27b.tar.gz
astro-710a1a11f488ff6ed3da6d3e0723b2322ccfe27b.tar.zst
astro-710a1a11f488ff6ed3da6d3e0723b2322ccfe27b.zip
feat(markdown): add support for `shiki` option `langAlias` (#12039)
* feat(shiki): add support for `langAlias` * chore: apply feedback * Update packages/markdown/remark/src/types.ts Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com> * fix build * Fix bug * Apply suggestions from code review Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Update .changeset/dirty-socks-sip.md --------- Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com> Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
-rw-r--r--.changeset/dirty-socks-sip.md34
-rw-r--r--packages/astro/src/core/config/schema.ts4
-rw-r--r--packages/markdown/remark/src/index.ts1
-rw-r--r--packages/markdown/remark/src/shiki.ts13
-rw-r--r--packages/markdown/remark/src/types.ts2
-rw-r--r--packages/markdown/remark/test/shiki.test.js28
6 files changed, 78 insertions, 4 deletions
diff --git a/.changeset/dirty-socks-sip.md b/.changeset/dirty-socks-sip.md
new file mode 100644
index 000000000..1784e2031
--- /dev/null
+++ b/.changeset/dirty-socks-sip.md
@@ -0,0 +1,34 @@
+---
+'@astrojs/markdown-remark': minor
+'astro': minor
+---
+
+Adds a `markdown.shikiConfig.langAlias` option that allows [aliasing a non-supported code language to a known language](https://shiki.style/guide/load-lang#custom-language-aliases). This is useful when the language of your code samples is not [a built-in Shiki language](https://shiki.style/languages), but you want your Markdown source to contain an accurate language while also displaying syntax highlighting.
+
+The following example configures Shiki to highlight `cjs` code blocks using the `javascript` syntax highlighter:
+
+```js
+import { defineConfig } from 'astro/config';
+
+export default defineConfig({
+ markdown: {
+ shikiConfig: {
+ langAlias: {
+ cjs: 'javascript',
+ },
+ },
+ },
+});
+```
+
+Then in your Markdown, you can use the alias as the language for a code block for syntax highlighting:
+
+````md
+```cjs
+'use strict';
+
+function commonJs() {
+ return 'I am a commonjs file';
+}
+```
+````
diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts
index da70ead1a..27f703a10 100644
--- a/packages/astro/src/core/config/schema.ts
+++ b/packages/astro/src/core/config/schema.ts
@@ -313,6 +313,10 @@ export const AstroConfigSchema = z.object({
return langs;
})
.default([]),
+ langAlias: z
+ .record(z.string(), z.string())
+ .optional()
+ .default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.langAlias!),
theme: z
.enum(Object.keys(bundledThemes) as [BuiltinTheme, ...BuiltinTheme[]])
.or(z.custom<ShikiTheme>())
diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts
index 5849f5799..a9ae7ed59 100644
--- a/packages/markdown/remark/src/index.ts
+++ b/packages/markdown/remark/src/index.ts
@@ -37,6 +37,7 @@ export const markdownConfigDefaults: Required<AstroMarkdownOptions> = {
themes: {},
wrap: false,
transformers: [],
+ langAlias: {},
},
remarkPlugins: [],
rehypePlugins: [],
diff --git a/packages/markdown/remark/src/shiki.ts b/packages/markdown/remark/src/shiki.ts
index 28f51c5e8..2f06ef9a9 100644
--- a/packages/markdown/remark/src/shiki.ts
+++ b/packages/markdown/remark/src/shiki.ts
@@ -45,24 +45,29 @@ export async function createShikiHighlighter({
defaultColor,
wrap = false,
transformers = [],
+ langAlias = {},
}: ShikiConfig = {}): Promise<ShikiHighlighter> {
theme = theme === 'css-variables' ? cssVariablesTheme() : theme;
const highlighter = await getHighlighter({
langs: ['plaintext', ...langs],
+ langAlias,
themes: Object.values(themes).length ? Object.values(themes) : [theme],
});
return {
async highlight(code, lang = 'plaintext', options) {
+ const resolvedLang = langAlias[lang] ?? lang;
const loadedLanguages = highlighter.getLoadedLanguages();
- if (!isSpecialLang(lang) && !loadedLanguages.includes(lang)) {
+ if (!isSpecialLang(lang) && !loadedLanguages.includes(resolvedLang)) {
try {
- await highlighter.loadLanguage(lang as BundledLanguage);
+ await highlighter.loadLanguage(resolvedLang as BundledLanguage);
} catch (_err) {
+ const langStr =
+ lang === resolvedLang ? `"${lang}"` : `"${lang}" (aliased to "${resolvedLang}")`;
console.warn(
- `[Shiki] The language "${lang}" doesn't exist, falling back to "plaintext".`,
+ `[Shiki] The language ${langStr} doesn't exist, falling back to "plaintext".`,
);
lang = 'plaintext';
}
@@ -120,7 +125,7 @@ export async function createShikiHighlighter({
// Add "user-select: none;" for "+"/"-" diff symbols.
// Transform `<span class="line"><span style="...">+ something</span></span>
// into `<span class="line"><span style="..."><span style="user-select: none;">+</span> something</span></span>`
- if (lang === 'diff') {
+ if (resolvedLang === 'diff') {
const innerSpanNode = node.children[0];
const innerSpanTextNode =
innerSpanNode?.type === 'element' && innerSpanNode.children?.[0];
diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts
index aa7b62c9a..d95676b55 100644
--- a/packages/markdown/remark/src/types.ts
+++ b/packages/markdown/remark/src/types.ts
@@ -3,6 +3,7 @@ import type * as mdast from 'mdast';
import type { Options as RemarkRehypeOptions } from 'remark-rehype';
import type {
BuiltinTheme,
+ HighlighterCoreOptions,
LanguageRegistration,
ShikiTransformer,
ThemeRegistration,
@@ -37,6 +38,7 @@ export type ThemePresets = BuiltinTheme | 'css-variables';
export interface ShikiConfig {
langs?: LanguageRegistration[];
+ langAlias?: HighlighterCoreOptions['langAlias'];
theme?: ThemePresets | ThemeRegistration | ThemeRegistrationRaw;
themes?: Record<string, ThemePresets | ThemeRegistration | ThemeRegistrationRaw>;
defaultColor?: 'light' | 'dark' | string | false;
diff --git a/packages/markdown/remark/test/shiki.test.js b/packages/markdown/remark/test/shiki.test.js
index c3cb81370..ca17ab1d8 100644
--- a/packages/markdown/remark/test/shiki.test.js
+++ b/packages/markdown/remark/test/shiki.test.js
@@ -101,4 +101,32 @@ describe('shiki syntax highlighting', () => {
// Doesn't have `color` or `background-color` properties.
assert.doesNotMatch(code, /color:/);
});
+
+ it('the highlighter supports lang alias', async () => {
+ const highlighter = await createShikiHighlighter({
+ langAlias: {
+ cjs: 'javascript',
+ },
+ });
+
+ const html = await highlighter.highlight(`let test = "some string"`, 'cjs', {
+ attributes: { 'data-foo': 'bar', autofocus: true },
+ });
+
+ assert.match(html, /data-language="cjs"/);
+ });
+
+ it('the markdown processsor support lang alias', async () => {
+ const processor = await createMarkdownProcessor({
+ shikiConfig: {
+ langAlias: {
+ cjs: 'javascript',
+ },
+ },
+ });
+
+ const { code } = await processor.render('```cjs\nlet foo = "bar"\n```');
+
+ assert.match(code, /data-language="cjs"/);
+ });
});