Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
46 changes: 45 additions & 1 deletion e2e/fixtures/code-block-runtime/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { expect, test } from '@playwright/test';
import { getPort, killProcess, runDevCommand } from '../../utils/runCommands';
import {
getPort,
killProcess,
runBuildCommand,
runDevCommand,
runPreviewCommand,
} from '../../utils/runCommands';

test.describe('<CodeBlockRuntime />', async () => {
let appPort: number;
Expand Down Expand Up @@ -62,3 +68,41 @@ test.describe('<CodeBlockRuntime />', async () => {
}
});
});

test.describe('<CodeBlockRuntime /> SSG', async () => {
let appPort: number;
let app: Awaited<ReturnType<typeof runPreviewCommand>>;
test.beforeAll(async () => {
const appDir = __dirname;
appPort = await getPort();
await runBuildCommand(appDir);
app = await runPreviewCommand(appDir, appPort);
});

test.afterAll(async () => {
if (app) {
await killProcess(app);
}
});

test('should render code from static HTML when JavaScript is disabled', async ({
browser,
}) => {
const context = await browser.newContext({ javaScriptEnabled: false });
const page = await context.newPage();
try {
await page.goto(`http://localhost:${appPort}`, {
waitUntil: 'domcontentloaded',
});

const containers = page.locator('div.language-js');
await expect(containers).toHaveCount(2);
const codeElement = containers
.locator('.rp-codeblock__content pre code')
.first();
await expect(codeElement).toHaveText("console.log('Hello CodeBlock!');");
} finally {
await context.close();
}
});
});
12 changes: 6 additions & 6 deletions packages/core/src/theme/components/CodeBlock/index.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.rp-codeblock {
position: relative;
margin: 16px 0;
margin: 1rem 0;
border: var(--rp-code-block-border);
box-shadow: var(--rp-code-block-shadow);

Expand All @@ -9,14 +9,14 @@
border-radius: var(--rp-radius);

@media (max-width: 640px) {
margin: 24px 0;
margin: 1.5rem 0;
contain: content;
}
}

.rp-codeblock__title {
font-family: var(--rp-font-family-mono);
padding: 12px 16px;
padding: 0.75rem 1rem;
font-size: var(--rp-code-font-size);
background-color: var(--rp-code-title-bg);
border-bottom: var(--rp-code-block-border);
Expand Down Expand Up @@ -59,7 +59,7 @@
font-size: var(--rp-code-font-size);
font-family: var(--rp-font-family-mono);
display: inline-block;
padding: 16px 0;
padding: 1rem 0;
width: fit-content;
min-width: 100%;
line-height: 1.7;
Expand All @@ -76,8 +76,8 @@
& .line::before {
content: counter(step);
counter-increment: step;
width: 15px;
margin-right: 15px;
width: 1rem;
margin-right: 1rem;
display: inline-block;
text-align: right;
color: rgba(115, 138, 148, 0.4);
Expand Down
64 changes: 53 additions & 11 deletions packages/core/src/theme/components/CodeBlockRuntime/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import type { CodeBlockProps } from '@theme';
import { getCustomMDXComponent } from '@theme';
import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
import { Fragment, type ReactNode, useEffect, useRef, useState } from 'react';
import {
Fragment,
type ReactNode,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { jsx, jsxs } from 'react/jsx-runtime';
import {
type BundledLanguage,
Expand Down Expand Up @@ -47,9 +54,39 @@ export function CodeBlockRuntime({
children: _,
containerElementClassName,
onRendered,
...otherProps
wrapCode,
lineNumbers,
}: CodeBlockRuntimeProps) {
const [child, setChild] = useState<ReactNode | null>(null);
// getCustomMDXComponent is stable for theme rendering
const mdxComponents = useMemo(() => getCustomMDXComponent(), []);
const { pre: ShikiPre, code: Code, ...otherMdxComponents } = mdxComponents;
const fallback = useMemo(
() => (
<ShikiPre
title={title}
lang={lang}
wrapCode={wrapCode}
lineNumbers={lineNumbers}
containerElementClassName={containerElementClassName}
codeButtonGroupProps={codeButtonGroupProps}
className="shiki css-variables"
>
<Code style={{ padding: '1rem 1.25rem' }}>{code}</Code>
</ShikiPre>
),
[
ShikiPre,
Code,
title,
lang,
wrapCode,
lineNumbers,
containerElementClassName,
codeButtonGroupProps,
code,
],
);
const [child, setChild] = useState<ReactNode | null>(fallback);
const codeRef = useLatest(code);

useEffect(() => {
Expand All @@ -66,12 +103,6 @@ export function CodeBlockRuntime({
return;
}

const {
pre: ShikiPre,
code: Code,
...otherMdxComponents
} = getCustomMDXComponent();

const reactNode = toJsxRuntime(hast, {
jsx,
jsxs,
Expand All @@ -84,8 +115,9 @@ export function CodeBlockRuntime({
lang={lang}
containerElementClassName={containerElementClassName}
codeButtonGroupProps={codeButtonGroupProps}
wrapCode={wrapCode}
lineNumbers={lineNumbers}
{...props}
{...otherProps}
/>
),
code: props => <Code {...props} />,
Expand All @@ -96,7 +128,17 @@ export function CodeBlockRuntime({
setChild(reactNode);
};
void highlightCode();
}, [lang, code, shikiOptions]);
}, [
code,
codeButtonGroupProps,
containerElementClassName,
fallback,
lang,
lineNumbers,
shikiOptions,
title,
wrapCode,
]);

useEffect(() => {
if (child) {
Expand Down
1 change: 1 addition & 0 deletions scripts/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ deepmerge
directorytest
docgen
docsite
domcontentloaded
envinfo
estree
facti
Expand Down
Loading