mirror of
https://github.com/supabase/supabase.git
synced 2026-10-11 12:25:05 +03:00
revert codeblock
This commit is contained in:
1 parent
e76176cdd4
commit
2d89df2f63
3 files changed
+184
-413
No files matched your search
@@ -582,10 +582,6 @@
|
||||
"import": "./src/admonition.tsx",
|
||||
"types": "./src/admonition.tsx"
|
||||
},
|
||||
"./multiple-code-block": {
|
||||
"import": "./src/multiple-code-block.tsx",
|
||||
"types": "./src/multiple-code-block.tsx"
|
||||
},
|
||||
"./consent": {
|
||||
"import": "./src/consent.tsx",
|
||||
"types": "./src/consent.tsx"
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
import {
|
||||
CodeBlock,
|
||||
CodeBlockLang,
|
||||
TabsContent_Shadcn_,
|
||||
TabsList_Shadcn_,
|
||||
TabsTrigger_Shadcn_,
|
||||
Tabs_Shadcn_,
|
||||
} from 'ui'
|
||||
|
||||
interface MultipleCodeBlockFile {
|
||||
name: string
|
||||
code: string
|
||||
language?: string
|
||||
}
|
||||
|
||||
interface MultipleCodeBlockProps {
|
||||
files: MultipleCodeBlockFile[]
|
||||
value?: string
|
||||
onValueChange?: (value: string) => void
|
||||
}
|
||||
|
||||
const languageAliases: Record<string, CodeBlockLang> = {
|
||||
bash: 'bash',
|
||||
csharp: 'csharp',
|
||||
cs: 'csharp',
|
||||
curl: 'curl',
|
||||
dart: 'dart',
|
||||
go: 'go',
|
||||
html: 'html',
|
||||
http: 'http',
|
||||
javascript: 'js',
|
||||
js: 'js',
|
||||
json: 'json',
|
||||
jsx: 'jsx',
|
||||
kotlin: 'kotlin',
|
||||
pgsql: 'pgsql',
|
||||
php: 'php',
|
||||
py: 'python',
|
||||
python: 'python',
|
||||
sh: 'bash',
|
||||
shell: 'bash',
|
||||
sql: 'sql',
|
||||
swift: 'swift',
|
||||
ts: 'ts',
|
||||
typescript: 'ts',
|
||||
yaml: 'yaml',
|
||||
yml: 'yaml',
|
||||
}
|
||||
|
||||
const extensionLanguageMap: Record<string, CodeBlockLang> = {
|
||||
astro: 'html',
|
||||
bash: 'bash',
|
||||
cjs: 'js',
|
||||
dart: 'dart',
|
||||
go: 'go',
|
||||
htm: 'html',
|
||||
html: 'html',
|
||||
js: 'js',
|
||||
json: 'json',
|
||||
jsx: 'jsx',
|
||||
kt: 'kotlin',
|
||||
mjs: 'js',
|
||||
php: 'php',
|
||||
pgsql: 'pgsql',
|
||||
py: 'python',
|
||||
sh: 'bash',
|
||||
sql: 'sql',
|
||||
swift: 'swift',
|
||||
svelte: 'html',
|
||||
ts: 'ts',
|
||||
vue: 'html',
|
||||
yaml: 'yaml',
|
||||
yml: 'yaml',
|
||||
}
|
||||
|
||||
const inferLanguageFromName = (name: string): CodeBlockLang | undefined => {
|
||||
const lowerName = name.toLowerCase()
|
||||
if (lowerName.startsWith('.env')) {
|
||||
return 'bash'
|
||||
}
|
||||
|
||||
const extension = lowerName.split('.').pop()
|
||||
if (!extension || extension === lowerName) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return extensionLanguageMap[extension]
|
||||
}
|
||||
|
||||
const resolveLanguage = (language: string | undefined, name: string): CodeBlockLang => {
|
||||
if (language) {
|
||||
const normalized = language.toLowerCase()
|
||||
const resolved = languageAliases[normalized]
|
||||
if (resolved) {
|
||||
return resolved
|
||||
}
|
||||
}
|
||||
|
||||
return inferLanguageFromName(name) ?? 'js'
|
||||
}
|
||||
|
||||
export const MultipleCodeBlock = ({ files, value, onValueChange }: MultipleCodeBlockProps) => {
|
||||
if (!files?.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
const defaultValue = files[0]?.name ?? ''
|
||||
|
||||
const trimmedFiles = files.map((file) => ({
|
||||
...file,
|
||||
code: typeof file.code === 'string' ? file.code.trim() : file.code,
|
||||
}))
|
||||
|
||||
return (
|
||||
<Tabs_Shadcn_
|
||||
defaultValue={defaultValue}
|
||||
value={value}
|
||||
onValueChange={onValueChange}
|
||||
className="border rounded-lg gap-0 space-y-0 overflow-hidden"
|
||||
>
|
||||
<TabsList_Shadcn_ className="bg-surface-75 px-5 gap-5 overflow-x-auto border-0 border-b">
|
||||
{files.map((file) => (
|
||||
<TabsTrigger_Shadcn_
|
||||
key={file.name}
|
||||
value={file.name}
|
||||
className="flex items-center gap-1 text-xs px-0 data-[state=active]:bg-transparent py-2.5"
|
||||
>
|
||||
{file.name}
|
||||
</TabsTrigger_Shadcn_>
|
||||
))}
|
||||
</TabsList_Shadcn_>
|
||||
|
||||
{trimmedFiles.map((file) => (
|
||||
<TabsContent_Shadcn_
|
||||
key={file.name}
|
||||
value={file.name}
|
||||
forceMount
|
||||
className="p-0 max-h-72 overflow-scroll data-[state=inactive]:hidden"
|
||||
data-connect-tab-content
|
||||
data-tab-label={file.name}
|
||||
>
|
||||
<CodeBlock
|
||||
value={typeof file.code === 'string' ? file.code.trim() : file.code}
|
||||
language={resolveLanguage(file.language, file.name)}
|
||||
className="min-h-72 !bg-surface-75 rounded-none border-0"
|
||||
/>
|
||||
</TabsContent_Shadcn_>
|
||||
))}
|
||||
</Tabs_Shadcn_>
|
||||
)
|
||||
}
|
||||
@@ -1,275 +1,201 @@
|
||||
'use client'
|
||||
import { Fragment, type PropsWithChildren } from 'react'
|
||||
import { type BundledLanguage, type ThemedToken, bundledLanguages, createHighlighter } from 'shiki'
|
||||
import { type ExtraFiles, type NodeHover, createTwoslasher } from 'twoslash'
|
||||
import { cn } from 'ui'
|
||||
|
||||
import curl from 'highlightjs-curl'
|
||||
import { noop } from 'lodash'
|
||||
import { Check, Copy } from 'lucide-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { Children, ReactNode, useState } from 'react'
|
||||
import { Light as SyntaxHighlighter, SyntaxHighlighterProps } from 'react-syntax-highlighter'
|
||||
import bash from 'react-syntax-highlighter/dist/cjs/languages/hljs/bash'
|
||||
import csharp from 'react-syntax-highlighter/dist/cjs/languages/hljs/csharp'
|
||||
import dart from 'react-syntax-highlighter/dist/cjs/languages/hljs/dart'
|
||||
import go from 'react-syntax-highlighter/dist/cjs/languages/hljs/go'
|
||||
import http from 'react-syntax-highlighter/dist/cjs/languages/hljs/http'
|
||||
import js from 'react-syntax-highlighter/dist/cjs/languages/hljs/javascript'
|
||||
import json from 'react-syntax-highlighter/dist/cjs/languages/hljs/json'
|
||||
import kotlin from 'react-syntax-highlighter/dist/cjs/languages/hljs/kotlin'
|
||||
import pgsql from 'react-syntax-highlighter/dist/cjs/languages/hljs/pgsql'
|
||||
import php from 'react-syntax-highlighter/dist/cjs/languages/hljs/php'
|
||||
import {
|
||||
default as py,
|
||||
default as python,
|
||||
} from 'react-syntax-highlighter/dist/cjs/languages/hljs/python'
|
||||
import sql from 'react-syntax-highlighter/dist/cjs/languages/hljs/sql'
|
||||
import swift from 'react-syntax-highlighter/dist/cjs/languages/hljs/swift'
|
||||
import ts from 'react-syntax-highlighter/dist/cjs/languages/hljs/typescript'
|
||||
import yaml from 'react-syntax-highlighter/dist/cjs/languages/hljs/yaml'
|
||||
import { AnnotatedSpan, CodeBlockControls } from './CodeBlock.client'
|
||||
import { getFontStyle } from './CodeBlock.utils'
|
||||
import theme from './supabase-2.json' with { type: 'json' }
|
||||
import denoTypes from './types/lib.deno.d.ts.include'
|
||||
|
||||
import { copyToClipboard } from '../../lib/utils'
|
||||
import { cn } from '../../lib/utils/cn'
|
||||
import { Button } from '../Button/Button'
|
||||
import { monokaiCustomTheme } from './CodeBlock.utils'
|
||||
const extraFiles: ExtraFiles = { 'deno.d.ts': denoTypes }
|
||||
|
||||
export type CodeBlockLang =
|
||||
| 'js'
|
||||
| 'jsx'
|
||||
| 'sql'
|
||||
| 'py'
|
||||
| 'bash'
|
||||
| 'ts'
|
||||
| 'dart'
|
||||
| 'json'
|
||||
| 'csharp'
|
||||
| 'kotlin'
|
||||
| 'curl'
|
||||
| 'http'
|
||||
| 'php'
|
||||
| 'python'
|
||||
| 'go'
|
||||
| 'pgsql'
|
||||
| 'swift'
|
||||
| 'yaml'
|
||||
| 'toml'
|
||||
| 'html'
|
||||
const twoslasher = createTwoslasher({ extraFiles })
|
||||
const TWOSLASHABLE_LANGS: ReadonlyArray<string> = ['js', 'ts', 'javascript', 'typescript']
|
||||
|
||||
export interface CodeBlockProps {
|
||||
title?: ReactNode
|
||||
language?: CodeBlockLang
|
||||
linesToHighlight?: number[]
|
||||
highlightBorder?: boolean
|
||||
styleConfig?: {
|
||||
lineNumber?: string
|
||||
highlightBackgroundColor?: string
|
||||
highlightBorderColor?: string
|
||||
}
|
||||
hideCopy?: boolean
|
||||
hideLineNumbers?: boolean
|
||||
className?: string
|
||||
wrapperClassName?: string
|
||||
value?: string
|
||||
theme?: any
|
||||
children?: string
|
||||
wrapLines?: boolean
|
||||
focusable?: boolean
|
||||
renderer?: SyntaxHighlighterProps['renderer']
|
||||
handleCopy?: (value?: string) => void
|
||||
onCopyCallback?: () => void
|
||||
}
|
||||
const BUNDLED_LANGUAGES = Object.keys(bundledLanguages)
|
||||
const highlighter = await createHighlighter({
|
||||
themes: [theme],
|
||||
langs: BUNDLED_LANGUAGES,
|
||||
})
|
||||
|
||||
/**
|
||||
* CodeBlock component for displaying syntax-highlighted code.
|
||||
* @param {ReactNode} [props.title] - Optional title for the code block.
|
||||
* @param {string} [props.language] - The programming language of the code.
|
||||
* @param {number[]} [props.linesToHighlight=[]] - Array of line numbers to highlight.
|
||||
* @param {boolean} [props.highlightBorder] - Whether to show a border on highlighted lines.
|
||||
* @param {Object} [props.styleConfig] - Custom style configurations.
|
||||
* @param {string} [props.className] - Additional CSS classes for the code block.
|
||||
* @param {string} [props.wrapperClassName] - CSS classes for the wrapper div.
|
||||
* @param {string} [props.value] - The code content as a string.
|
||||
* @param {any} [props.theme] - Custom theme for syntax highlighting.
|
||||
* @param {string} [props.children] - The code content as children.
|
||||
* @param {boolean} [props.hideCopy=false] - Whether to hide the copy button.
|
||||
* @param {boolean} [props.hideLineNumbers=false] - Whether to hide line numbers.
|
||||
* @param {SyntaxHighlighterProps['renderer']} [props.renderer] - Custom renderer for syntax highlighting.
|
||||
* @param {boolean} [props.focusable=true] - Whether the code block is focusable. When true, users can focus the code block to select text or use ⌘A (Cmd+A) to select all. This is so we don't need to load Monaco Editor.
|
||||
* @param {function} [props.handleCopy] - Optional override behaviour for copying value. For e.g if the code block contains obfuscated values, but the copy behaviour should reveal those values instead.
|
||||
*/
|
||||
export const CodeBlock = ({
|
||||
title,
|
||||
language,
|
||||
linesToHighlight = [],
|
||||
highlightBorder,
|
||||
styleConfig,
|
||||
export async function CodeBlock({
|
||||
className,
|
||||
wrapperClassName,
|
||||
value,
|
||||
theme,
|
||||
lang: langSetting,
|
||||
lineNumbers = true,
|
||||
contents,
|
||||
children,
|
||||
hideCopy = false,
|
||||
hideLineNumbers = false,
|
||||
wrapLines = true,
|
||||
renderer,
|
||||
focusable = true,
|
||||
onCopyCallback = noop,
|
||||
handleCopy,
|
||||
}: CodeBlockProps) => {
|
||||
const { resolvedTheme } = useTheme()
|
||||
const isDarkTheme = resolvedTheme?.includes('dark')!
|
||||
const monokaiTheme = theme ?? monokaiCustomTheme(isDarkTheme)
|
||||
skipTypeGeneration,
|
||||
}: PropsWithChildren<{
|
||||
className?: string
|
||||
lang?: string
|
||||
lineNumbers?: boolean
|
||||
contents?: string
|
||||
skipTypeGeneration?: boolean
|
||||
}>) {
|
||||
let code = (contents || extractCode(children)).trim()
|
||||
const lang = tryToBundledLanguage(langSetting || '') || extractLang(children)
|
||||
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const onSelectCopy = (value?: string) => {
|
||||
if (value) {
|
||||
if (!!handleCopy) {
|
||||
handleCopy(value)
|
||||
} else {
|
||||
copyToClipboard(value)
|
||||
}
|
||||
let twoslashed = null as null | Map<number, Map<number, Array<NodeHover>>>
|
||||
if (!skipTypeGeneration && lang && TWOSLASHABLE_LANGS.includes(lang)) {
|
||||
try {
|
||||
const { code: editedCode, nodes } = twoslasher(code)
|
||||
const hoverNodes: Array<NodeHover> = nodes.filter((node) => node.type === 'hover')
|
||||
twoslashed = annotationsByLine(hoverNodes)
|
||||
code = editedCode
|
||||
} catch (_err) {
|
||||
// Silently ignore, if imports aren't defined type compilation fails
|
||||
// Uncomment lines below to debug in dev
|
||||
// console.log('\n==========CODE==========\n')
|
||||
// console.log(code)
|
||||
// console.error(_err.recommendation)
|
||||
}
|
||||
setCopied(true)
|
||||
onCopyCallback()
|
||||
setTimeout(() => setCopied(false), 1000)
|
||||
}
|
||||
|
||||
// Extract string when `children` has a single string node
|
||||
const childrenArray = Children.toArray(children)
|
||||
const [singleChild] = childrenArray.length === 1 ? childrenArray : []
|
||||
const singleString = typeof singleChild === 'string' ? singleChild : undefined
|
||||
|
||||
let codeValue = value ?? singleString ?? children
|
||||
codeValue = codeValue?.trimEnd?.() ?? codeValue
|
||||
|
||||
// check the length of the string inside the <code> tag
|
||||
// if it's fewer than 70 characters, add a white-space: pre so it doesn't wrap
|
||||
const shortCodeBlockClasses =
|
||||
typeof codeValue === 'string' && codeValue.length < 70 ? 'short-inline-codeblock' : ''
|
||||
|
||||
let lang = language ? language : className ? className.replace('language-', '') : 'js'
|
||||
// force jsx to be js highlighted
|
||||
if (lang === 'jsx') lang = 'js'
|
||||
SyntaxHighlighter.registerLanguage('js', js)
|
||||
SyntaxHighlighter.registerLanguage('ts', ts)
|
||||
SyntaxHighlighter.registerLanguage('py', py)
|
||||
SyntaxHighlighter.registerLanguage('sql', sql)
|
||||
SyntaxHighlighter.registerLanguage('bash', bash)
|
||||
SyntaxHighlighter.registerLanguage('dart', dart)
|
||||
SyntaxHighlighter.registerLanguage('csharp', csharp)
|
||||
SyntaxHighlighter.registerLanguage('json', json)
|
||||
SyntaxHighlighter.registerLanguage('kotlin', kotlin)
|
||||
SyntaxHighlighter.registerLanguage('curl', curl)
|
||||
SyntaxHighlighter.registerLanguage('http', http)
|
||||
SyntaxHighlighter.registerLanguage('php', php)
|
||||
SyntaxHighlighter.registerLanguage('python', python)
|
||||
SyntaxHighlighter.registerLanguage('go', go)
|
||||
SyntaxHighlighter.registerLanguage('pgsql', pgsql)
|
||||
SyntaxHighlighter.registerLanguage('swift', swift)
|
||||
SyntaxHighlighter.registerLanguage('yaml', yaml)
|
||||
|
||||
const large = false
|
||||
// don't show line numbers if bash == lang
|
||||
if (lang === 'bash' || lang === 'sh') hideLineNumbers = true
|
||||
const showLineNumbers = !hideLineNumbers
|
||||
const { tokens } = highlighter.codeToTokens(code, {
|
||||
lang: lang || undefined,
|
||||
theme: 'Supabase Theme',
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
{title && (
|
||||
<div className="text-sm rounded-t-md bg-surface-100 py-2 px-4 border border-b-0 border-default font-sans">
|
||||
{title}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'shiki',
|
||||
'group',
|
||||
'relative',
|
||||
'not-prose',
|
||||
'w-full overflow-x-auto',
|
||||
'border border-default rounded-lg',
|
||||
'bg-200',
|
||||
'text-sm',
|
||||
className
|
||||
)}
|
||||
{className ? (
|
||||
<div
|
||||
className={cn(
|
||||
'group relative max-w-[90vw] md:max-w-none overflow-auto',
|
||||
wrapperClassName
|
||||
)}
|
||||
>
|
||||
{/* @ts-ignore */}
|
||||
<SyntaxHighlighter
|
||||
suppressContentEditableWarning
|
||||
language={lang}
|
||||
wrapLines={wrapLines}
|
||||
style={monokaiTheme}
|
||||
className={cn(
|
||||
'code-block border border-surface p-4 w-full !my-0 !bg-surface-100 outline-none focus:border-foreground-lighter/50',
|
||||
`${!title ? 'rounded-md' : 'rounded-t-none rounded-b-md'}`,
|
||||
`${!showLineNumbers ? 'pl-6' : ''}`,
|
||||
className
|
||||
)}
|
||||
customStyle={{
|
||||
fontSize: large ? 18 : 13,
|
||||
lineHeight: large ? 1.5 : 1.4,
|
||||
}}
|
||||
showLineNumbers={showLineNumbers}
|
||||
lineProps={(lineNumber) => {
|
||||
if (linesToHighlight.includes(lineNumber)) {
|
||||
return {
|
||||
style: {
|
||||
display: 'block',
|
||||
backgroundColor: styleConfig?.highlightBackgroundColor
|
||||
? styleConfig?.highlightBackgroundColor
|
||||
: 'hsl(var(--background-selection))',
|
||||
borderLeft: highlightBorder
|
||||
? `1px solid ${styleConfig?.highlightBorderColor ? styleConfig?.highlightBorderColor : 'hsl(var(--foreground-default)'})`
|
||||
: null,
|
||||
},
|
||||
class: 'hljs-line-highlight',
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}}
|
||||
lineNumberContainerStyle={{
|
||||
paddingTop: '128px',
|
||||
}}
|
||||
lineNumberStyle={{
|
||||
minWidth: '44px',
|
||||
paddingLeft: '4px',
|
||||
paddingRight: '4px',
|
||||
marginRight: '12px',
|
||||
color: styleConfig?.lineNumber ?? '#828282',
|
||||
textAlign: 'center',
|
||||
fontSize: large ? 14 : 12,
|
||||
paddingTop: '4px',
|
||||
paddingBottom: '4px',
|
||||
}}
|
||||
renderer={renderer}
|
||||
contentEditable={focusable}
|
||||
onBeforeInput={(e: any) => {
|
||||
e.preventDefault()
|
||||
return false
|
||||
}}
|
||||
onKeyDown={(e: any) => {
|
||||
if (e.code === 'Backspace') {
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
}}
|
||||
>
|
||||
{codeValue}
|
||||
</SyntaxHighlighter>
|
||||
{!hideCopy && (value || children) && className ? (
|
||||
<div
|
||||
className={[
|
||||
'absolute right-2 top-2',
|
||||
'opacity-0 group-hover:opacity-100 transition',
|
||||
`${isDarkTheme ? 'dark' : ''}`,
|
||||
].join(' ')}
|
||||
>
|
||||
<Button
|
||||
type="default"
|
||||
className="px-1.5"
|
||||
icon={copied ? <Check /> : <Copy />}
|
||||
onClick={() => onSelectCopy(value || children)}
|
||||
>
|
||||
{copied ? 'Copied' : ''}
|
||||
</Button>
|
||||
>
|
||||
<pre>
|
||||
<code className={lineNumbers ? 'grid grid-cols-[auto_1fr]' : ''}>
|
||||
{lineNumbers ? (
|
||||
<>
|
||||
{tokens.map((line, idx) => (
|
||||
<Fragment key={idx}>
|
||||
<div
|
||||
className={cn(
|
||||
'select-none text-right text-muted bg-control px-2 min-h-5 leading-5',
|
||||
idx === 0 && 'pt-6',
|
||||
idx === tokens.length - 1 && 'pb-6'
|
||||
)}
|
||||
>
|
||||
{idx + 1}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'code-content min-h-5 leading-5 pl-6 pr-6',
|
||||
idx === 0 && 'pt-6',
|
||||
idx === tokens.length - 1 && 'pb-6'
|
||||
)}
|
||||
>
|
||||
<CodeLine tokens={line} twoslash={twoslashed?.get(idx)} />
|
||||
</div>
|
||||
</Fragment>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<div className="code-content p-6">
|
||||
{tokens.map((line, idx) => (
|
||||
<CodeLine key={idx} tokens={line} twoslash={twoslashed?.get(idx)} />
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<code className={shortCodeBlockClasses}>{value || children}</code>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</code>
|
||||
</pre>
|
||||
<CodeBlockControls content={code.trim()} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function CodeLine({
|
||||
tokens: rawTokens,
|
||||
twoslash,
|
||||
}: {
|
||||
tokens: Array<ThemedToken>
|
||||
twoslash?: Map<number, Array<NodeHover>>
|
||||
}) {
|
||||
let offset = 0
|
||||
const tokens = rawTokens.map((token) => {
|
||||
const newToken = { ...token, offset }
|
||||
offset += token.content.length
|
||||
return newToken
|
||||
})
|
||||
|
||||
return (
|
||||
<span className="block min-h-5 leading-5">
|
||||
{tokens.map((token) =>
|
||||
twoslash?.has(token.offset) ? (
|
||||
<AnnotatedSpan
|
||||
key={token.offset}
|
||||
token={token}
|
||||
annotations={twoslash.get(token.offset)!}
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
key={token.offset}
|
||||
style={{ color: token.color, ...getFontStyle(token.fontStyle || 0) }}
|
||||
>
|
||||
{token.content}
|
||||
</span>
|
||||
)
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
function extractCode(children: React.ReactNode): string {
|
||||
if (typeof children === 'string') return children
|
||||
const child = Array.isArray(children) ? children[0] : children
|
||||
if (!!child && typeof child === 'object' && 'props' in child) {
|
||||
const props = child.props
|
||||
if (!!props && typeof props === 'object' && 'children' in props) {
|
||||
const code = props.children
|
||||
if (typeof code === 'string') return code
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function extractLang(children: React.ReactNode): BundledLanguage | null {
|
||||
if (typeof children === 'string') return null
|
||||
const child = Array.isArray(children) ? children[0] : children
|
||||
if (!!child && typeof child === 'object' && 'props' in child) {
|
||||
const props = child.props
|
||||
if (!!props && typeof props === 'object' && 'className' in props) {
|
||||
const className = props.className
|
||||
if (typeof className === 'string') {
|
||||
const lang = className.split(' ').find((className) => className.startsWith('language-'))
|
||||
return lang ? tryToBundledLanguage(lang.replace('language-', '')) : null
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function annotationsByLine(nodes: Array<NodeHover>): Map<number, Map<number, Array<NodeHover>>> {
|
||||
const result = new Map()
|
||||
nodes.forEach((node) => {
|
||||
const line = node.line
|
||||
const char = node.character
|
||||
if (!result.has(line)) {
|
||||
result.set(line, new Map())
|
||||
}
|
||||
if (!result.get(line).has(char)) {
|
||||
result.get(line).set(char, [])
|
||||
}
|
||||
result.get(line).get(char).push(node)
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
function tryToBundledLanguage(lang: string): BundledLanguage | null {
|
||||
if (BUNDLED_LANGUAGES.includes(lang)) {
|
||||
return lang as BundledLanguage
|
||||
}
|
||||
return null
|
||||
}
|
||||
Reference in new issue
Block a user