-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmdast-util-mdat-expand.ts
119 lines (102 loc) · 3.31 KB
/
mdast-util-mdat-expand.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import type { Html, Root } from 'mdast'
import { remark } from 'remark'
import remarkGfm from 'remark-gfm'
import { CONTINUE, visit } from 'unist-util-visit'
import { type VFile } from 'vfile'
import { saveLog } from '../mdat/mdat-log'
import { type CommentMarkerNode, parseCommentNode } from '../mdat/parse'
import { getRuleContent, normalizeRules, type Rules, validateRules } from '../mdat/rules'
export type Options = {
addMetaComment: boolean
closingPrefix: string
keywordPrefix: string
metaCommentIdentifier: string
rules: Rules
}
type ValidCommentMarker = {
type: 'close' | 'open'
} & CommentMarkerNode
/*
* Mdast utility plugin to collapse mdat comments and strip generated meta
* comments, effectively resetting the document to its original state.
*/
export async function mdatExpand(tree: Root, file: VFile, options: Options) {
const {
addMetaComment,
closingPrefix,
keywordPrefix,
metaCommentIdentifier,
rules: rawRules,
} = options
// Make the rules easier to deal with by normalizing to consistent structure
validateRules(rawRules)
const rules = normalizeRules(rawRules)
// Get all valid comment markers from the tree
const commentMarkers: ValidCommentMarker[] = []
visit(tree, 'html', (node, index, parent) => {
if (parent === undefined || index === undefined) return CONTINUE
// Find all <!-- mdat --> comments
const commentMarker = parseCommentNode(node, parent, {
closingPrefix,
keywordPrefix,
metaCommentIdentifier,
})
// Save the marker if it meets all criteria
if (
commentMarker !== undefined &&
commentMarker.type === 'open' &&
rules[commentMarker.keyword] !== undefined
)
commentMarkers.push(commentMarker)
})
// Sort by application order
commentMarkers.sort(
(a, b) => rules[a.keyword].applicationOrder - rules[b.keyword].applicationOrder,
)
// Expand the rules
for (const comment of commentMarkers) {
const { closingPrefix, html, keyword, keywordPrefix, node, options, parent } = comment
const rule = rules[keyword]
let newMarkdownString = ''
try {
// Handle compound rules
newMarkdownString = await getRuleContent(rule, options, tree)
// TODO just let check get this?
if (newMarkdownString.trim() === '') {
saveLog(file, 'error', 'expand', `Got empty content when expanding ${html}`, node)
}
} catch (error) {
if (error instanceof Error) {
saveLog(
file,
'error',
'expand',
`Caught error expanding ${html}, Error message: "${error.message}"`,
node,
)
}
continue
}
// String to Markdown Nodes
// TODO Consider exposing this for more complex use cases?
const newNodes = remark().use(remarkGfm).parse(newMarkdownString).children
// Add closing tag
const closingNode: Html = {
type: 'html',
value: `<!-- ${closingPrefix}${keywordPrefix}${keyword} -->`,
}
const openingCommentIndex = parent.children.indexOf(node)
parent.children.splice(openingCommentIndex + 1, 0, ...newNodes, closingNode)
saveLog(file, 'info', 'expand', `Expanded: ${html}`, node)
}
// Add meta comment
if (addMetaComment) {
const message =
'Warning: Content inside HTML comment blocks was generated by mdat and may be overwritten.'
const metaComment: Html = {
type: 'html',
value: `<!--${metaCommentIdentifier} ${message} ${metaCommentIdentifier}-->`,
}
tree.children.unshift(metaComment)
}
}