Skip to content

Commit 6473952

Browse files
authored
feat: extract rehypeConfluence (#3)
1 parent c199229 commit 6473952

5 files changed

Lines changed: 105 additions & 101 deletions

File tree

.changeset/cold-spies-fold.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"cf-cleaner": patch
3+
---
4+
5+
feat: extract rehypeConfluence

.changeset/wet-boxes-talk.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'cf-cleaner': patch
3+
---
4+
5+
feat: first blood, should just work

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
"main": "./lib/index.cjs",
1616
"module": "./lib/index.js",
1717
"exports": {
18-
"import": "./lib/index.js",
19-
"require": "./lib/index.cjs"
18+
".": {
19+
"import": "./lib/index.js",
20+
"require": "./lib/index.cjs"
21+
},
22+
"./lib/rehype-confluence": "./lib/rehype-confluence.js"
2023
},
2124
"types": "lib",
2225
"files": [

src/index.ts

Lines changed: 1 addition & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,12 @@
1-
import { URL } from 'url'
2-
3-
import type { Element, Root } from 'hast'
41
import rehypeFormat from 'rehype-format'
52
import rehypeParse from 'rehype-parse'
63
import rehypePresetMinify from 'rehype-preset-minify'
74
import rehypeStringify from 'rehype-stringify'
85
import { unified } from 'unified'
96
import type { MinimalDuplex } from 'unified-stream'
107
import { stream } from 'unified-stream'
11-
import { remove } from 'unist-util-remove'
12-
import { visit } from 'unist-util-visit'
13-
14-
const CLASSNAME_MAPPER = {
15-
'toc-macro': 'toc',
16-
'hide-toolbar': 'expandable',
17-
'confluence-information-macro': 'alert',
18-
code: 'code',
19-
panel: 'panel',
20-
panelHeader: 'panel-header',
21-
panelContent: 'panel-content',
22-
confluenceTd: 'border-td',
23-
confluenceTh: 'border-th',
24-
}
25-
26-
// eslint-disable-next-line sonarjs/cognitive-complexity
27-
export const rehypeConfluence = () => (root: Root) => {
28-
remove(
29-
root,
30-
node =>
31-
node.type === 'element' &&
32-
(['style', 'script'].includes(node.tagName) ||
33-
(node.properties?.className as string[] | undefined)?.some(className =>
34-
['aui-icon', 'hide-border-bottom', 'hidden'].includes(className),
35-
)),
36-
)
378

38-
remove(root, node => {
39-
if (node.type === 'element' && node.tagName === 'p') {
40-
return node.children.every(item => {
41-
if (item.type === 'text') {
42-
return !item.value.trim()
43-
}
44-
45-
return false
46-
})
47-
}
48-
})
49-
50-
visit(
51-
root,
52-
node => node.type === 'element' && !!(node as Element).properties,
53-
_el => {
54-
const properties = (_el as Element).properties!
55-
for (const key of Object.keys(properties)) {
56-
switch (key) {
57-
case 'className': {
58-
const className = properties.className as string[]
59-
properties.className = className
60-
// eslint-disable-next-line array-callback-return
61-
.map(name => {
62-
if (name in CLASSNAME_MAPPER) {
63-
return CLASSNAME_MAPPER[name as keyof typeof CLASSNAME_MAPPER]
64-
}
65-
66-
if (name.startsWith('confluence-information-macro-')) {
67-
return name.replace('confluence-information-macro-', 'alert-')
68-
}
69-
})
70-
.filter(Boolean) as string[]
71-
if (properties.className.length === 0) {
72-
delete properties.className
73-
}
74-
if (properties.style) {
75-
delete properties.style
76-
}
77-
break
78-
}
79-
case 'dataSyntaxhighlighterParams': {
80-
const params = properties[key] as string
81-
const matched = /brush:\s*([^;]+);/.exec(params)
82-
const lang = matched?.[1]
83-
if (lang) {
84-
properties.className = ['language-' + lang]
85-
}
86-
delete properties[key]
87-
break
88-
}
89-
case 'href': {
90-
const href = properties.href as string
91-
const url = new URL(href)
92-
const matched = /\?pageId=(\d+)/.exec(url.search)
93-
if (matched) {
94-
properties.href = '/knowledge/' + matched[1] + url.hash
95-
}
96-
break
97-
}
98-
default: {
99-
if (key.startsWith('data')) {
100-
delete properties[key]
101-
}
102-
}
103-
}
104-
}
105-
},
106-
)
107-
}
9+
import { rehypeConfluence } from './rehype-confluence'
10810

10911
const getProcessor = (minify?: boolean | undefined) => {
11012
let processor = unified()

src/rehype-confluence.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import type { Element, Root } from 'hast'
2+
import { remove } from 'unist-util-remove'
3+
import { visit } from 'unist-util-visit'
4+
5+
const CLASSNAME_MAPPER = {
6+
'toc-macro': 'toc',
7+
'hide-toolbar': 'expandable',
8+
'confluence-information-macro': 'alert',
9+
code: 'code',
10+
panel: 'panel',
11+
panelHeader: 'panel-header',
12+
panelContent: 'panel-content',
13+
confluenceTd: 'border-td',
14+
confluenceTh: 'border-th',
15+
}
16+
17+
// eslint-disable-next-line sonarjs/cognitive-complexity
18+
export const rehypeConfluence = () => (root: Root) => {
19+
remove(
20+
root,
21+
node =>
22+
node.type === 'element' &&
23+
(['style', 'script'].includes(node.tagName) ||
24+
(node.properties?.className as string[] | undefined)?.some(className =>
25+
['aui-icon', 'hide-border-bottom', 'hidden'].includes(className),
26+
)),
27+
)
28+
29+
remove(root, node => {
30+
if (node.type === 'element' && node.tagName === 'p') {
31+
return node.children.every(item => {
32+
if (item.type === 'text') {
33+
return !item.value.trim()
34+
}
35+
36+
return item.type === 'element' && item.tagName === 'br'
37+
})
38+
}
39+
})
40+
41+
visit(
42+
root,
43+
node => node.type === 'element' && !!(node as Element).properties,
44+
_el => {
45+
const properties = (_el as Element).properties!
46+
for (const key of Object.keys(properties)) {
47+
switch (key) {
48+
case 'className': {
49+
const className = properties.className as string[]
50+
properties.className = className
51+
// eslint-disable-next-line array-callback-return
52+
.map(name => {
53+
if (name in CLASSNAME_MAPPER) {
54+
return CLASSNAME_MAPPER[name as keyof typeof CLASSNAME_MAPPER]
55+
}
56+
57+
if (name.startsWith('confluence-information-macro-')) {
58+
return name.replace('confluence-information-macro-', 'alert-')
59+
}
60+
})
61+
.filter(Boolean) as string[]
62+
if (properties.className.length === 0) {
63+
delete properties.className
64+
}
65+
if (properties.style) {
66+
delete properties.style
67+
}
68+
break
69+
}
70+
case 'dataSyntaxhighlighterParams': {
71+
const params = properties[key] as string
72+
const matched = /brush:\s*([^;]+);/.exec(params)
73+
const lang = matched?.[1]
74+
if (lang) {
75+
properties.className = ['language-' + lang]
76+
}
77+
delete properties[key]
78+
break
79+
}
80+
default: {
81+
if (key.startsWith('data')) {
82+
delete properties[key]
83+
}
84+
}
85+
}
86+
}
87+
},
88+
)
89+
}

0 commit comments

Comments
 (0)