Skip to content

Commit

Permalink
refactor: extract plugin code
Browse files Browse the repository at this point in the history
re #20
  • Loading branch information
Val-istar-Guo committed Mar 29, 2023
1 parent 2bf0b4e commit e56b1f2
Show file tree
Hide file tree
Showing 18 changed files with 112 additions and 58 deletions.
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
{
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"eslint.validate": [
"typescript",
"typescriptreact"
],
"cSpell.words": [
"mdast",
"rehype",
"unist"
]
}
14 changes: 0 additions & 14 deletions src/checker.ts

This file was deleted.

4 changes: 4 additions & 0 deletions src/constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { RehypePrismPlugin } from './interface/rehype-prism-plugin'


export const internalPlugins: RehypePrismPlugin[] = ['line-numbers']
4 changes: 0 additions & 4 deletions src/get-line-number.ts

This file was deleted.

6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import unifiedTypes from 'unified'
import { Element } from 'hast'
import { visit } from 'unist-util-visit'
import { Test } from 'unist-util-is'
import { RehypePrismOptions } from './interface/rehype-prism-options.js'
import { preElementSelector } from './pre-element-selector.js'
import { RehypePrismOptions } from './rehype-prism-options.js'
import { parseCodeVisitor } from './parse-code-vistor.js'
import { internalPlugins } from './constant.js'


const rehypePrism: unifiedTypes.Plugin<[RehypePrismOptions?], Element> = (options?: RehypePrismOptions) => {
if (options && options.plugins) {
const plugins = options.plugins.filter(plugin => plugin !== 'line-numbers')
const plugins = options.plugins
.filter(plugin => !internalPlugins.includes(plugin))

for (const plugin of plugins) {
import(`prismjs/plugins/${plugin}/prism-${plugin}.js`)
Expand Down
Empty file added src/interface/index.ts
Empty file.
10 changes: 10 additions & 0 deletions src/interface/plugin-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Element } from 'hast'


export interface PluginOptions {
preElement: Element
codeElement: Element

raw: string
lang: string
}
3 changes: 3 additions & 0 deletions src/interface/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { PluginOptions } from './plugin-options'

export type Plugin = (options: PluginOptions) => void
6 changes: 6 additions & 0 deletions src/interface/rehype-prism-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { RehypePrismPlugin } from './rehype-prism-plugin'


export interface RehypePrismOptions {
plugins?: RehypePrismPlugin[]
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,3 @@ export type RehypePrismPlugin = (
'show-invisibles' | 'show-language' | 'toolbar' | 'treeview' |
'unescaped-markup' | 'wpd'
)

export interface RehypePrismOptions {
plugins?: RehypePrismPlugin[]
}
57 changes: 25 additions & 32 deletions src/parse-code-vistor.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,50 @@
import Prism from 'prismjs'
import rehypeParse from 'rehype-parse'
import { Element, Parent, ElementContent } from 'hast'
import { Text } from 'mdast'
import { Visitor } from 'unist-util-visit/complex-types'
import { unified } from 'unified'
import { h } from 'hastscript'
import { select } from 'unist-util-select'
import { getLang } from './get-lang.js'
import { Checker } from './checker.js'
import { appendClassName } from './append-class-name.js'
import { getLineNumber } from './get-line-number.js'
import { RehypePrismOptions } from './rehype-prism-options.js'
import { getLang } from './utils/get-lang.js'
import { appendClassName } from './utils/append-class-name.js'
import { RehypePrismOptions } from './interface/rehype-prism-options.js'

import { isElementNode } from './utils/is-element-node.js'
import { isTextNode } from './utils/is-text-node.js'
import { applyPlugin } from './plugins/apply-plugin.js'


const parser = unified()
.use(rehypeParse, { fragment: true })

export function parseCodeVisitor(options?: RehypePrismOptions): Visitor<ElementContent, Parent> {
return node => {
if (!Checker.isElement(node) || node.tagName !== 'pre') return
const preNode = node
if (!isElementNode(node) || node.tagName !== 'pre') return
const preElement = node

const codeNode = select('[tagName=code]', preNode)
if (!Checker.isElement(codeNode)) return
const codeElement = select('[tagName=code]', preElement)
if (!isElementNode(codeElement)) return

const lang = getLang(codeNode)
const lang = getLang(codeElement)
if (!lang || !Prism.languages[lang]) return

const textNode = select('text', codeNode) as (Text | null)
if (!Checker.isText(textNode)) return

const codeText = textNode.value
const textNode = select('text', codeElement)
if (!isTextNode(textNode)) return
const raw = textNode.value

const html = Prism.highlight(codeText, Prism.languages[lang], lang)
const html = Prism.highlight(raw, Prism.languages[lang], lang)
const tree = parser.parse(html) as unknown as Element

appendClassName(preNode, `language-${lang}`)

codeNode.children = [...tree.children]
appendClassName(preElement, `language-${lang}`)

if (options?.plugins?.includes('line-numbers')) {
appendClassName(preNode, 'line-numbers')
codeElement.children = [...tree.children]

const lineNumber = getLineNumber(codeText)
const lineNumberColumn = h(
'span',
{
'aria-hidden': 'true',
className: ['line-numbers-rows'],
},
new Array(lineNumber).fill(h('span')),
)
codeNode.children.push(lineNumberColumn)
if (options?.plugins) {
applyPlugin(options.plugins, {
preElement,
codeElement,
raw,
lang,
})
}
}
}
10 changes: 10 additions & 0 deletions src/plugins/apply-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { PluginOptions } from '@/interface/plugin-options'
import { RehypePrismPlugin } from '@/interface/rehype-prism-plugin'
import { applyLineNumberPlugin } from './line-numbers.js'


export function applyPlugin(plugins: RehypePrismPlugin[], options: PluginOptions): void {
for (const plugin of plugins) {
if (plugin === 'line-numbers') applyLineNumberPlugin(options)
}
}
26 changes: 26 additions & 0 deletions src/plugins/line-numbers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { h } from 'hastscript'
import { appendClassName } from '@/utils/append-class-name.js'
import { Plugin } from '@/interface/plugin.js'


function getLineNumber(str: string): number {
const match = str.match(/\n(?!$)/g)
return match ? match.length + 1 : 1
}

export const applyLineNumberPlugin: Plugin = options => {
const { preElement, codeElement, raw } = options

appendClassName(preElement, 'line-numbers')

const lineNumber = getLineNumber(raw)
const lineNumberColumn = h(
'span',
{
'aria-hidden': 'true',
className: ['line-numbers-rows'],
},
new Array(lineNumber).fill(h('span')),
)
codeElement.children.push(lineNumberColumn)
}
4 changes: 2 additions & 2 deletions src/pre-element-selector.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Test } from 'unist-util-is'
import { Checker } from './checker.js'
import { isElementNode } from './utils/is-element-node.js'


export function preElementSelector(): Test {
return node => Checker.isElement(node) && node.tagName === 'pre'
return node => isElementNode(node) && node.tagName === 'pre'
}
File renamed without changes.
File renamed without changes.
7 changes: 7 additions & 0 deletions src/utils/is-element-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Node } from 'unist'
import { Element } from 'hast'


export function isElementNode(node?: Node | null): node is Element {
return !!node && node.type === 'element' && 'tagName' in node
}
7 changes: 7 additions & 0 deletions src/utils/is-text-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Node } from 'unist'
import { Text } from 'mdast'


export function isTextNode(node: Node | null): node is Text {
return !!node && node.type === 'text'
}

0 comments on commit e56b1f2

Please sign in to comment.