Skip to content

Commit 6956647

Browse files
committed
feat: add core library
1 parent 696d82e commit 6956647

File tree

8 files changed

+2981
-0
lines changed

8 files changed

+2981
-0
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist/

.eslintrc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"extends": ["eslint:recommended", "prettier"],
3+
"parserOptions": {
4+
"ecmaVersion": 2020,
5+
"sourceType": "module",
6+
"ecmaFeatures": {
7+
"jsx": true
8+
}
9+
},
10+
"env": {
11+
"node": true,
12+
"es6": true,
13+
"jest": true
14+
},
15+
"plugins": ["node"],
16+
"rules": {
17+
"no-unused-vars": ["warn"],
18+
"node/no-missing-require": ["error"]
19+
}
20+
}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
*.log
3+
.DS_Store
4+
coverage
5+
dist/

index.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { visit } from 'unist-util-visit'
2+
import toString from 'hast-util-to-string'
3+
import { refractor } from 'refractor'
4+
import rangeParser from 'parse-numeric-range'
5+
6+
const getLanguage = (node) => {
7+
const className = node.properties.className || []
8+
9+
for (const classListItem of className) {
10+
if (classListItem.slice(0, 9) === 'language-') {
11+
return classListItem.slice(9).toLowerCase()
12+
}
13+
}
14+
return null
15+
}
16+
17+
// Create a closure that determines if we have to highlight the given index
18+
const calculateLinesToHighlight = (meta) => {
19+
const RE = /{([\d,-]+)}/
20+
if (RE.test(meta)) {
21+
const strlineNumbers = RE.exec(meta)[1]
22+
const lineNumbers = rangeParser(strlineNumbers)
23+
return (index) => lineNumbers.includes(index + 1)
24+
} else {
25+
return () => false
26+
}
27+
}
28+
29+
const splitLine = (text) => {
30+
// Xdm Markdown parser every code line with \n
31+
const textArray = text.split(/\n/)
32+
33+
// Remove last line \n which results in empty array
34+
if (textArray[textArray.length - 1].trim() === '') {
35+
textArray.pop()
36+
}
37+
38+
return textArray.map((line) => {
39+
return {
40+
type: 'element',
41+
tagName: 'div',
42+
properties: { className: ['code-line'] },
43+
children: [{ type: 'text', value: line }],
44+
}
45+
})
46+
}
47+
48+
const rehypePrism = (options) => {
49+
options = options || {}
50+
51+
return (tree) => {
52+
visit(tree, 'element', visitor)
53+
}
54+
55+
function visitor(node, index, parent) {
56+
if (!parent || parent.tagName !== 'pre' || node.tagName !== 'code') {
57+
return
58+
}
59+
60+
const lang = getLanguage(node)
61+
const meta = node.data && node.data.meta ? node.data.meta : ''
62+
const shouldHighlightLine = calculateLinesToHighlight(meta)
63+
64+
if (lang) {
65+
parent.properties.className = (parent.properties.className || []).concat('language-' + lang)
66+
}
67+
68+
const codeLineArray = splitLine(toString(node))
69+
70+
for (const [i, line] of codeLineArray.entries()) {
71+
// Code lines
72+
if (meta.includes('showLineNumbers') || options.showLineNumbers) {
73+
line.properties.line = [(i + 1).toString()]
74+
line.properties.className = [`${line.properties.className} line-number`]
75+
}
76+
77+
// Line highlight
78+
if (shouldHighlightLine(i)) {
79+
line.properties.className = [`${line.properties.className} highlight-line`]
80+
}
81+
82+
// Syntax highlight
83+
if (lang) {
84+
try {
85+
line.children = refractor.highlight(line.children[0].value, lang).children
86+
} catch (err) {
87+
// eslint-disable-next-line no-empty
88+
if (options.ignoreMissing && /Unknown language/.test(err.message)) {
89+
} else {
90+
throw err
91+
}
92+
}
93+
}
94+
}
95+
96+
node.children = codeLineArray
97+
}
98+
}
99+
100+
export default rehypePrism

0 commit comments

Comments
 (0)