Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { highlightToHtml } from '@node-core/rehype-shiki';
import { highlightToHtml } from '@node-core/rehype-shiki/minimal';
import AlertBox from '@node-core/ui-components/Common/AlertBox';
import Skeleton from '@node-core/ui-components/Common/Skeleton';
import { useTranslations } from 'next-intl';
Expand Down
2 changes: 1 addition & 1 deletion apps/site/next.mdx.plugins.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

import rehypeShikiji from '@node-core/rehype-shiki';
import rehypeShikiji from '@node-core/rehype-shiki/plugin';
import remarkHeadings from '@vcarl/remark-headings';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import rehypeSlug from 'rehype-slug';
Expand Down
6 changes: 4 additions & 2 deletions packages/rehype-shiki/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
{
"name": "@node-core/rehype-shiki",
"type": "module",
"main": "./src/index.mjs",
"module": "./src/index.mjs",
"exports": {
".": "./src/index.mjs",
"./*": "./src/*.mjs"
},
"scripts": {
"lint:js": "eslint \"**/*.mjs\"",
"test": "node --test"
Expand Down
97 changes: 58 additions & 39 deletions packages/rehype-shiki/src/highlighter.mjs
Original file line number Diff line number Diff line change
@@ -1,47 +1,66 @@
import { createHighlighterCoreSync } from '@shikijs/core';
import { createJavaScriptRegexEngine } from '@shikijs/engine-javascript';
import shikiNordTheme from 'shiki/themes/nord.mjs';

import { LANGUAGES, DEFAULT_THEME } from './languages.mjs';

let _shiki;

/**
* Lazy-load and memoize the minimal Shikiji Syntax Highlighter
* @returns {import('@shikijs/core').HighlighterCore}
*/
export const getShiki = () => {
if (!_shiki) {
_shiki = createHighlighterCoreSync({
themes: [DEFAULT_THEME],
langs: LANGUAGES,
// Let's use Shiki's new Experimental JavaScript-based regex engine!
engine: createJavaScriptRegexEngine(),
});
}
return _shiki;
const DEFAULT_THEME = {
// We updating this color because the background color and comment text color
Comment thread
avivkeller marked this conversation as resolved.
Outdated
// in the Codebox component do not comply with accessibility standards
// @see https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html
colorReplacements: { '#616e88': '#707e99' },
...shikiNordTheme,
};

/**
* Highlights code and returns the inner HTML inside the <code> tag
*
* @param {string} code - The code to highlight
* @param {string} language - The programming language to use for highlighting
* @returns {string} The inner HTML of the highlighted code
* Creates a syntax highlighter with utility functions
* @param {import('@shikijs/core').HighlighterCoreOptions} options - Configuration options for the highlighter
*/
export const highlightToHtml = (code, language) =>
getShiki()
.codeToHtml(code, { lang: language, theme: DEFAULT_THEME })
// Shiki will always return the Highlighted code encapsulated in a <pre> and <code> tag
// since our own CodeBox component handles the <code> tag, we just want to extract
// the inner highlighted code to the CodeBox
.match(/<code>(.+?)<\/code>/s)[1];
export const createHighlighter = options => {
const shiki = createHighlighterCoreSync({
themes: [DEFAULT_THEME],
engine: createJavaScriptRegexEngine(),
...options,
});
const theme = options.themes?.[0] ?? DEFAULT_THEME;
Comment thread
avivkeller marked this conversation as resolved.
const langs = options.langs ?? [];

/**
* Highlights code and returns a HAST tree
*
* @param {string} code - The code to highlight
* @param {string} language - The programming language to use for highlighting
* @returns {import('hast').Element} The HAST representation of the highlighted code
*/
export const highlightToHast = (code, language) =>
getShiki().codeToHast(code, { lang: language, theme: DEFAULT_THEME });
const getLanguageDisplayName = language => {
const languageByIdOrAlias = langs.find(
({ name, aliases }) =>
name.toLowerCase() === language.toLowerCase() ||
(aliases !== undefined && aliases.includes(language.toLowerCase()))
);

return languageByIdOrAlias?.displayName ?? language;
};

/**
* Highlights code and returns the inner HTML inside the <code> tag
*
* @param {string} code - The code to highlight
* @param {string} language - The programming language to use for highlighting
* @returns {string} The inner HTML of the highlighted code
*/
const highlightToHtml = (code, language) =>
shiki
.codeToHtml(code, { lang: language, theme })
// Shiki will always return the Highlighted code encapsulated in a <pre> and <code> tag
// since our own CodeBox component handles the <code> tag, we just want to extract
// the inner highlighted code to the CodeBox
.match(/<code>(.+?)<\/code>/s)[1];

/**
* Highlights code and returns a HAST tree
*
* @param {string} code - The code to highlight
* @param {string} language - The programming language to use for highlighting
*/
const highlightToHast = (code, language) =>
shiki.codeToHast(code, { lang: language, theme });

return {
shiki,
getLanguageDisplayName,
highlightToHtml,
highlightToHast,
};
};
46 changes: 42 additions & 4 deletions packages/rehype-shiki/src/index.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
import { rehypeShikiji } from './plugin.mjs';
export * from './highlighter.mjs';
export * from './languages.mjs';
import cLanguage from 'shiki/langs/c.mjs';
import coffeeScriptLanguage from 'shiki/langs/coffeescript.mjs';
import cPlusPlusLanguage from 'shiki/langs/cpp.mjs';
import diffLanguage from 'shiki/langs/diff.mjs';
import dockerLanguage from 'shiki/langs/docker.mjs';
import httpLanguage from 'shiki/langs/http.mjs';
import iniLanguage from 'shiki/langs/ini.mjs';
import javaScriptLanguage from 'shiki/langs/javascript.mjs';
import jsonLanguage from 'shiki/langs/json.mjs';
import powershellLanguage from 'shiki/langs/powershell.mjs';
import shellScriptLanguage from 'shiki/langs/shellscript.mjs';
import shellSessionLanguage from 'shiki/langs/shellsession.mjs';
import typeScriptLanguage from 'shiki/langs/typescript.mjs';
import yamlLanguage from 'shiki/langs/yaml.mjs';

export default rehypeShikiji;
import { createHighlighter } from './highlighter.mjs';

const { shiki, getLanguageDisplayName, highlightToHast, highlightToHtml } =
createHighlighter({
langs: [
...cLanguage,
...coffeeScriptLanguage,
...cPlusPlusLanguage,
...diffLanguage,
...dockerLanguage,
...httpLanguage,
...iniLanguage,
{
...javaScriptLanguage[0],
// We patch the JavaScript language to include the CommonJS and ES Module aliases
// that are commonly used (non-standard aliases) within our API docs and Blog posts
aliases: javaScriptLanguage[0].aliases.concat('cjs', 'mjs'),
},
...jsonLanguage,
...powershellLanguage,
...shellScriptLanguage,
...shellSessionLanguage,
...typeScriptLanguage,
...yamlLanguage,
],
});

export { shiki, getLanguageDisplayName, highlightToHast, highlightToHtml };
60 changes: 0 additions & 60 deletions packages/rehype-shiki/src/languages.mjs

This file was deleted.

11 changes: 11 additions & 0 deletions packages/rehype-shiki/src/minimal.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import powershellLanguage from 'shiki/langs/powershell.mjs';
import shellScriptLanguage from 'shiki/langs/shellscript.mjs';

import { createHighlighter } from './highlighter.mjs';

const { shiki, getLanguageDisplayName, highlightToHast, highlightToHtml } =
createHighlighter({
langs: [...powershellLanguage, ...shellScriptLanguage],
});

export { shiki, getLanguageDisplayName, highlightToHast, highlightToHtml };
4 changes: 2 additions & 2 deletions packages/rehype-shiki/src/plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import classNames from 'classnames';
import { toString } from 'hast-util-to-string';
import { SKIP, visit } from 'unist-util-visit';

import { highlightToHast } from './highlighter.mjs';
import { highlightToHast } from '.';

// This is what Remark will use as prefix within a <pre> className
// to attribute the current language of the <pre> element
Expand Down Expand Up @@ -53,7 +53,7 @@ function isCodeBlock(node) {
);
}

export function rehypeShikiji() {
export default function rehypeShikiji() {
return function (tree) {
visit(tree, 'element', (_, index, parent) => {
const languages = [];
Expand Down
Loading