Skip to content

Commit

Permalink
Use @wooorm/starry-night for syntax highlighting (#143)
Browse files Browse the repository at this point in the history
Use the high-quality syntax highlighting provided by
@wooorm/starry-night. Now it matches GitHub perfectly.

## Before

<img width="661" alt=""
src="https://github.com/stevenpetryk/mafs/assets/1724000/e8d8fdca-180f-407f-bb0e-9bc5cf32a937">

## After

<img width="660" alt=""
src="https://github.com/stevenpetryk/mafs/assets/1724000/e27cb4e6-f3e6-496b-ae51-f0b20f256875">
  • Loading branch information
stevenpetryk authored Feb 27, 2024
1 parent 2725a0a commit c9ed792
Show file tree
Hide file tree
Showing 8 changed files with 514 additions and 59 deletions.
2 changes: 2 additions & 0 deletions docs/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { AnalyticsWrapper } from "components/analytics"
import Link from "next/link"
import { MafsLogo } from "../components/MafsLogo"
import "./globals.css"
import "../node_modules/@wooorm/starry-night/style/core.css"
import "../node_modules/@wooorm/starry-night/style/dark.css"
import { Metadata } from "next"

export const metadata: Metadata = {
Expand Down
1 change: 1 addition & 0 deletions docs/components/Code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default Code

export function HighlightedCode({ source, language }: { language: string; source: string }) {
const tree = refractor.highlight(source, language)
// @ts-expect-error - idk
const node = toH(React.createElement, tree)

return node
Expand Down
39 changes: 9 additions & 30 deletions docs/components/CodeAndExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import * as React from "react"
import { MinusIcon, PlusIcon } from "@radix-ui/react-icons"
import { HighlightedCode } from "./Code"
import { StackBlitzIcon } from "./icons"

import sdk from "@stackblitz/sdk"
Expand All @@ -11,41 +10,23 @@ import endent from "endent"
interface Props {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
example: any
clean?: boolean
collapsible?: boolean
}

function CodeAndExample({ collapsible: collapsibleProp = true, example, clean = true }: Props) {
const typedExample = example as { $component: React.ComponentType; $source: string }
function CodeAndExample({ collapsible: collapsibleProp = true, example }: Props) {
const typedExample = example as {
$component: React.ComponentType
$source: string
$highlightedSource: React.ReactNode
}
const Component = typedExample.$component
let source = typedExample.$source
const source = typedExample.$source
const highlightedSource = typedExample.$highlightedSource

if (typeof Component !== "function") {
throw new Error(`CodeAndExample: expected example to be a component ${source}`)
}

if (clean) {
const remove = [
/\s+height=\{[^}]*\}/g,
/\s+width=\{.*\}\s*/g,
/.*prettier-ignore.*\n/gm,
/^import React.* from "react"/gm,
/^export default [A-z]+$\n/gm,
/^export default /m,
/"use client"/m,
]

remove.forEach((regex) => {
source = source.replace(regex, "")
})

source = source.replaceAll(/import \{(.|\n)*?\} from "mafs"/gm, (match) => {
return match.replaceAll(/\s+/g, " ").replace(", }", " }")
})

source = source.trim()
}

const collapsible = collapsibleProp && source.split("\n").length > 10
const [expandedState, setExpanded] = React.useState(false)
const expanded = collapsible ? expandedState : true
Expand Down Expand Up @@ -74,9 +55,7 @@ function CodeAndExample({ collapsible: collapsibleProp = true, example, clean =
`}
>
<pre className={`transition ${expanded ? "" : "opacity-40"}`}>
<code className="language-tsx">
<HighlightedCode source={source} language="tsx" />
</code>
<code className="language language-tsx">{highlightedSource}</code>
</pre>
</div>

Expand Down
12 changes: 0 additions & 12 deletions docs/guide-example-loader.js

This file was deleted.

66 changes: 66 additions & 0 deletions docs/guide-example-loader.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// @ts-check
/* eslint-disable no-undef, @typescript-eslint/no-var-requires */

import { createStarryNight, all } from "@wooorm/starry-night"

const starryNightPromise = createStarryNight(all)

/**
* This loader is what powers Mafs' guide examples. It takes the default export
* of all the code in components/guide-examples and makes sure that the source
* code is also exported, and also takes care of syntax highlighting
* at build time.
*
* Since all the guide-examples are maintained in this repo, I didn't bother
* bringing in a proper parser. I just find and replace the default export.
*
* @type {import('webpack').LoaderDefinition}
*/
export default async function guideExampleLoader(source) {
let cleanedSource = source

const remove = [
/\s+height=\{[^}]*\}/g,
/\s+width=\{.*\}\s*/g,
/.*prettier-ignore.*\n/gm,
/^import React.* from "react"/gm,
/^export default [A-z]+$\n/gm,
/^export default /m,
/"use client"/m,
]

remove.forEach((regex) => {
cleanedSource = cleanedSource.replace(regex, "")
})

cleanedSource = cleanedSource.replaceAll(/import \{(.|\n)*?\} from "mafs"/gm, (match) => {
return match.replaceAll(/\s+/g, " ").replace(", }", " }")
})

cleanedSource = cleanedSource.trim()

const starryNight = await starryNightPromise
const scope = starryNight.flagToScope(this.resourcePath)
const tree = starryNight.highlight(cleanedSource, scope ?? "tsx")

const useClientRegex = /'use client'|"use client"/
const usesClient = useClientRegex.test(source)

const transformedSource = source
.replace(/export default/g, "const $default =")
.replace(useClientRegex, "")

return `
${usesClient ? "'use client';" : ""}
import {Fragment, jsx, jsxs} from 'react/jsx-runtime';
import {toJsxRuntime} from 'hast-util-to-jsx-runtime';
${transformedSource};
$default.$source = ${JSON.stringify(cleanedSource)};
$default.$component = $default;
$default.$highlightedSource = toJsxRuntime(${JSON.stringify(tree)}, { Fragment, jsx, jsxs });
export default $default;
`
}
2 changes: 1 addition & 1 deletion docs/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const nextConfig = {
config.module.rules.push({
test: /\.tsx?$/,
include: /guide-examples/,
use: [{ loader: require.resolve("./guide-example-loader") }],
use: require.resolve("./guide-example-loader.mjs"),
})

return config
Expand Down
2 changes: 2 additions & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
},
"devDependencies": {
"@types/lodash": "^4.14.196",
"@wooorm/starry-night": "^3.2.0",
"autoprefixer": "^10.4.14",
"hast-util-to-jsx-runtime": "^2.3.0",
"postcss": "^8.4.27",
"raw-loader": "^4.0.2",
"react-docgen-typescript": "2.2.2",
Expand Down
Loading

0 comments on commit c9ed792

Please sign in to comment.