markdown-magic 2.5.2 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -393,3 +393,8 @@ This section was generated by the cli config markdown.config.js file
393
393
 
394
394
  - [Project using markdown-magic](https://github.com/search?o=desc&q=filename%3Apackage.json+%22markdown-magic%22&s=indexed&type=Code)
395
395
  - [Examples in md](https://github.com/search?l=Markdown&o=desc&q=AUTO-GENERATED-CONTENT&s=indexed&type=Code)
396
+
397
+
398
+ ## Misc Markdown helpers
399
+
400
+ - https://github.com/azu/markdown-function
package/cli.js CHANGED
File without changes
package/lib/index.js ADDED
@@ -0,0 +1,219 @@
1
+ const path = require('path')
2
+ const fs = require('fs').promises
3
+ const { getTextBetween, parseBlocks } = require('./utils/new-parser')
4
+
5
+ async function processContents({
6
+ filePath,
7
+ syntax,
8
+ open,
9
+ close,
10
+ transforms,
11
+ beforeMiddelwares,
12
+ afterMiddelwares,
13
+ DEBUG = false
14
+ }) {
15
+ const syntaxType = syntax || path.extname(filePath).replace(/^\./, '')
16
+ const originalContents = await fs.readFile(filePath, 'utf8')
17
+
18
+ const foundBlocks = parseBlocks(originalContents, {
19
+ syntax: syntaxType,
20
+ open,
21
+ close,
22
+ })
23
+
24
+ if (DEBUG) {
25
+ console.log('foundBlocks')
26
+ console.log(foundBlocks)
27
+ }
28
+
29
+ const transformsToRun = sortPlugins(foundBlocks.transforms)
30
+ // console.log('transformsToRun', transformsToRun)
31
+
32
+ // if (!transformsToRun.length) {
33
+ // process.exit(1)
34
+ // }
35
+
36
+ let missingTransforms = []
37
+ const updatedContents = await transformsToRun.reduce(async (updatedContent, ogmatch) => {
38
+ const md = await updatedContent
39
+ /* Apply Before middleware to all transforms */
40
+ const match = await applyMiddleware(ogmatch, md, beforeMiddelwares)
41
+ const { block, raw, transform, args } = match
42
+ const { openTag, closeTag, content, contentStart, contentEnd, start, end } = block
43
+
44
+ /* Run transform plugins */
45
+ let tempContent = content
46
+ if (transforms[transform]) {
47
+ tempContent = await transforms[transform](match, md)
48
+ }
49
+
50
+ /* Apply After middleware to all transforms */
51
+ const afterContent = await applyMiddleware({
52
+ ...match,
53
+ ...{
54
+ block: {
55
+ ...match.block,
56
+ content: tempContent
57
+ }
58
+ }
59
+ }, md, afterMiddelwares)
60
+ if (DEBUG) {
61
+ console.log('afterContent', afterContent)
62
+ }
63
+
64
+ if (!transforms[transform]) {
65
+ missingTransforms.push(afterContent)
66
+ console.log(`Missing "${transform}" transform`)
67
+ }
68
+
69
+ const newContent = afterContent.block.content
70
+ const formattedNewContent = (args.noTrim) ? newContent : smartTrim(newContent)
71
+ /* Remove any conflicting imported comments */
72
+ // const fix = removeConflictingComments(formattedNewContent, foundBlocks.commentOpen, foundBlocks.commentClose)
73
+ const fix = stripAllComments(formattedNewContent, foundBlocks.commentOpen, foundBlocks.commentClose)
74
+ // console.log('foundBlocks.commentClose', foundBlocks.commentClose)
75
+ // console.log('formattedNewContent', formattedNewContent)
76
+ // console.log('fix', fix)
77
+ const preserveIndent = (true || args.preserveIndent) ? block.indentation.length + block.contentIndent : block.indentation.length
78
+ const indent = indentString(fix, preserveIndent)
79
+ const newCont = `${openTag}${indent}${closeTag}`
80
+ /* Replace original contents */
81
+ const newContents = md.replace(raw.block, newCont)
82
+ return Promise.resolve(newContents)
83
+ }, Promise.resolve(originalContents))
84
+
85
+ if (DEBUG) {
86
+ console.log('Output Markdown')
87
+ console.log(updatedContents)
88
+ }
89
+
90
+ /*
91
+ if (missingTransforms.length) {
92
+ console.log('missingTransforms', missingTransforms)
93
+ let matchOne = missingTransforms[1]
94
+ matchOne = missingTransforms[missingTransforms.length - 1]
95
+ // console.log('matchOne', matchOne)
96
+ const { block, transform, args } = matchOne
97
+ const { openTag, closeTag, content, contentStart, contentEnd, start, end} = block
98
+
99
+ // console.log('contentStart', contentStart)
100
+ // console.log('contentEnd', contentEnd)
101
+ // console.log('original text between', `"${getTextBetween(md, contentStart, contentEnd)}"`)
102
+ // console.log('original block between', `"${getTextBetween(md, start, end)}"`)
103
+ }
104
+ /** */
105
+
106
+ return {
107
+ filePath,
108
+ transforms: transformsToRun,
109
+ missingTransforms,
110
+ originalContents,
111
+ updatedContents
112
+ }
113
+ }
114
+
115
+ function applyMiddleware(data, md, middlewares) {
116
+ return middlewares.reduce(async (acc, curr) => {
117
+ const realAcc = await acc
118
+ // console.log(`Running "${curr.name}" Middleware on "${realAcc.transform}" block`)
119
+ const updatedContent = await curr.transform(realAcc, md)
120
+ // realAcc.block.content = updatedContent
121
+ return Promise.resolve({
122
+ ...realAcc,
123
+ ...{
124
+ block: {
125
+ ...realAcc.block,
126
+ content: updatedContent
127
+ }
128
+ }
129
+ })
130
+ }, Promise.resolve(data))
131
+ }
132
+
133
+ /**
134
+ * Trim leading & trailing spaces/line breaks in code and keeps the indentation of the first non-empty line
135
+ * @param {string} str
136
+ * @returns string
137
+ */
138
+ function smartTrim(str) {
139
+ return str.replace(/^(?:[\t ]*(?:\r?\n|\r))+|\s+$/g, '')
140
+ }
141
+
142
+ function stripAllComments(block) {
143
+ const pattern = new RegExp(`([^\\s]*)?([ \\t]*)?(<!-+\\s?([\\s\\S]*?)?-+>)([^\\s<]*)?(\n{1,2})?`, 'gi')
144
+
145
+ // console.log('closeTagRegex', closeTagRegex)
146
+ let matches
147
+ let remove = []
148
+ while ((matches = pattern.exec(block)) !== null) {
149
+ if (matches.index === pattern.lastIndex) {
150
+ pattern.lastIndex++ // avoid infinite loops with zero-width matches
151
+ }
152
+ const [ match, leadingText, leadingSpace, comment, insideComment, trailingText, trailingNewLine ] = matches
153
+ /*
154
+ console.log('match', match)
155
+ console.log('leadingText', leadingText)
156
+ console.log('leadingSpace', leadingSpace)
157
+ console.log('comment', comment)
158
+ console.log('insideComment', insideComment)
159
+ console.log('trailingText', trailingText)
160
+ console.log('trailingNewLine', trailingNewLine)
161
+ /** */
162
+ const newLineCount = (trailingNewLine || '').length
163
+ const trailing = (!trailingText && newLineCount > 1) ? `${trailingNewLine || ''}` : ''
164
+ const leading = (leadingSpace) ? leadingSpace.slice(1) : ''
165
+ remove.push(`${leading}${comment}${trailing}`)
166
+ }
167
+ return remove.reduce((acc, curr) => {
168
+ return acc.replaceAll(curr, '')
169
+ }, block)
170
+ }
171
+
172
+ /**
173
+ * Remove conflicting comments that might have been inserted from transforms
174
+ * @param {*} content
175
+ * @param {*} openPattern
176
+ * @param {*} closePattern
177
+ * @returns
178
+ */
179
+ function removeConflictingComments(content, openPattern, closePattern) {
180
+ const removeOpen = content.replace(openPattern, '')
181
+ // TODO this probably needs to be a loop for larger blocks
182
+ closePattern.lastIndex = 0; // reset regex
183
+ const hasClose = closePattern.exec(content)
184
+ // console.log('closePattern', closePattern)
185
+ // console.log('has', content)
186
+ // console.log('hasClose', hasClose)
187
+ if (!hasClose) {
188
+ return removeOpen
189
+ }
190
+ const closeTag = `${hasClose[2]}${hasClose[3] || ''}`
191
+ // console.log('closeTag', closeTag)
192
+ return removeOpen
193
+ .replaceAll(closeTag, '')
194
+ /* Trailing new line */
195
+ .replace(/\n$/, '')
196
+ }
197
+
198
+ function sortPlugins(data) {
199
+ return data.sort((a, b) => {
200
+ // put table of contents (TOC) at end of tranforms
201
+ if (a.transform === 'TOC') return 1
202
+ if (b.transform === 'TOC') return -1
203
+ return 0
204
+ })
205
+ }
206
+
207
+ function indentString(string, count = 1, options = {}) {
208
+ const {
209
+ indent = ' ',
210
+ includeEmptyLines = false
211
+ } = options;
212
+ if (count === 0) return string
213
+ const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm
214
+ return string.replace(regex, indent.repeat(count))
215
+ }
216
+
217
+ module.exports = {
218
+ processContents
219
+ }
@@ -38,7 +38,7 @@ module.exports = async function processFile(filePath, config) {
38
38
  matchWord: 'AUTO-GENERATED-CONTENT',
39
39
  /**
40
40
  * - `DEBUG` - *Boolean* - (optional) set debug flag to `true` to inspect the process
41
- * @type {string}
41
+ * @type {boolean}
42
42
  */
43
43
  DEBUG: false,
44
44
  }
@@ -55,6 +55,7 @@ module.exports = async function processFile(filePath, config) {
55
55
  mergedConfig.outputContent = content
56
56
 
57
57
  const regex = regexUtils.matchCommentBlock(mergedConfig.matchWord)
58
+ // console.log(regex)
58
59
  const match = content.match(regex)
59
60
  const transformsFound = []
60
61
 
@@ -69,11 +69,11 @@ module.exports = async function TOC(content, options, config) {
69
69
  const matchTextEscaped = escapeStringRegexp(matchText)
70
70
  // console.log('matchTextEscaped', matchTextEscaped)
71
71
  // /^#{1}\s+(.*)/
72
- const FIRST_H1_REGEX = new RegExp(`^#\\s*${matchTextEscaped}`, 'gim')
72
+ const FIRST_H1_REGEX = new RegExp(`^#\\s*\\[?${matchTextEscaped}\\]?(?:.*)?`, 'gim')
73
73
  // /^<h1\b[^>]*>([\s\S]*?)<\/h1>/
74
- const FIRST_HTML_H1_REGEX = new RegExp(`^<h1\\b[^>]*>[\\s]*?(${matchTextEscaped})[\\s]*?<\\/h1>`, 'gim')
74
+ const FIRST_HTML_H1_REGEX = new RegExp(`^<h1\\b[^>]*>[\\s]*?(${matchTextEscaped})[\\s]*?<\\/h1>`, 'gim')
75
75
  // /^(.*)\n={3,}/
76
- const FIRST_UNDERSCORE_H1 = new RegExp(`^(${matchTextEscaped})\n={3,}`, 'gim')
76
+ const FIRST_UNDERSCORE_H1 = new RegExp(`^(${matchTextEscaped})\n={3,}`, 'gim')
77
77
 
78
78
  let docHasHeading = false
79
79
  if (contents.match(FIRST_H1_REGEX)) {
@@ -262,3 +262,5 @@ function removeUndefined(output) {
262
262
  // remove undefined from new line and start of string if first H1 is missing
263
263
  return output.replace(/\nundefined/g, '\n-').replace(/^undefined/g, '-')
264
264
  }
265
+
266
+ // Alt https://github.com/thlorenz/doctoc
@@ -0,0 +1,63 @@
1
+ const { test } = require('uvu')
2
+ const assert = require('uvu/assert')
3
+ const { parseBlocks, replaceContent } = require('./new-parser')
4
+
5
+ const defaultOpts = {
6
+ syntax: 'md',
7
+ open: 'DOCS:START',
8
+ close: 'DOCS:END',
9
+ }
10
+
11
+ test('Returns empty array', () => {
12
+ assert.equal(parseBlocks('', defaultOpts).transforms, [])
13
+ assert.equal(parseBlocks(' ', defaultOpts).transforms, [])
14
+ assert.equal(parseBlocks(`
15
+
16
+
17
+ `, defaultOpts).transforms, [])
18
+ assert.equal(parseBlocks(`
19
+ # No block in here
20
+
21
+ nope
22
+ `, defaultOpts).transforms, [])
23
+ })
24
+
25
+ const md = `
26
+ Very nice
27
+
28
+ <!-- DOCS:START(TOC) foo={{ rad: 'orange' }} ------>
29
+ ok
30
+ <!-- DOCS:END -->`
31
+
32
+ test('Parse md blocks', () => {
33
+ const parsedValue = parseBlocks(md, defaultOpts)
34
+ console.log('parsedValue', parsedValue)
35
+ assert.equal(parsedValue.transforms, [
36
+ {
37
+ transform: 'TOC',
38
+ args: { foo: { rad: 'orange' } },
39
+ block: {
40
+ indentation: '',
41
+ start: 12,
42
+ end: 85,
43
+ contentStart: 64,
44
+ contentEnd: 68,
45
+ contentIndent: 0,
46
+ openTag: "<!-- DOCS:START(TOC) foo={{ rad: 'orange' }} ------>\n",
47
+ content: 'ok',
48
+ closeTag: '\n<!-- DOCS:END -->'
49
+ },
50
+ raw: {
51
+ transform: '(TOC)',
52
+ args: "foo={{ rad: 'orange' }} ----",
53
+ content: '\nok\n',
54
+ block: "<!-- DOCS:START(TOC) foo={{ rad: 'orange' }} ------>\n" +
55
+ 'ok\n' +
56
+ '<!-- DOCS:END -->'
57
+ },
58
+ meta: { isMultiline: true }
59
+ }
60
+ ], '')
61
+ })
62
+
63
+ test.run()