markdown-magic 2.4.0 → 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.
@@ -0,0 +1,412 @@
1
+
2
+ const weirdParse = require('./weird-parse')
3
+
4
+ const html = {
5
+ tags: ['<!--', '-->'],
6
+ pattern: ['<!--+', '-->'],
7
+ }
8
+
9
+ const jsx = {
10
+ tags: ['{/*', '*/}'],
11
+ pattern: [
12
+ '\{\/\\*+',
13
+ '\\*+/\}'
14
+ ]
15
+ }
16
+
17
+ const yaml = {
18
+ tags: ['##', '##'],
19
+ pattern: [
20
+ '##+',
21
+ '##+'
22
+ ],
23
+ converter: (str) => {
24
+ return str.split('\n').map((line) => {
25
+ return line[0] === '#' ? line : `#${line}`
26
+ }).join()
27
+ }
28
+ }
29
+
30
+ const syntaxMap = {
31
+ // <!-- x -->
32
+ md: html,
33
+ // <!-- x -->
34
+ html: html,
35
+ // /* x */
36
+ js: {
37
+ tags: ['/*', '*/'],
38
+ pattern: [
39
+ '\/\\*+', // '\/\*[\*\n\s\t]+', //
40
+ '\\*+/'
41
+ ],
42
+ },
43
+ // {/* x */}
44
+ jsx: jsx,
45
+ mdx: jsx,
46
+ // ## x ##
47
+ yaml: yaml,
48
+ yml: yaml
49
+ }
50
+
51
+ function convertCommentSyntax({
52
+ str,
53
+ from,
54
+ to
55
+ }) {
56
+ const [ openPattern, closePattern ] = syntaxMap[from].pattern
57
+ const [ openTag, closeTag ] = syntaxMap[to].tags
58
+ const match = ` *?\\${openPattern}([\\s\\S]*?)?${closePattern}\\n\\n*?`
59
+ // const match = `${openPattern}(.*|\\r?|\\n?|\\s*)*${closePattern}`
60
+ const regexToUse = new RegExp(match, 'g')
61
+ // console.log('regexToUse', regexToUse)
62
+ const found = str.match(regexToUse)
63
+ if (!found) {
64
+ return str
65
+ }
66
+ const newComment = found[0].replace(regexToUse, `${openTag}$1${closeTag}`)
67
+ const converter = syntaxMap[to].converter
68
+ const newText = (converter) ? converter(newComment) : newComment
69
+ return str.replace(regexToUse, newText)
70
+ }
71
+
72
+ const defaultOptions = {
73
+ syntax: 'md',
74
+ open: `DOCS:START`,
75
+ close: `DOCS:END`,
76
+ }
77
+
78
+ function parseBlocks(contents, options = defaultOptions) {
79
+ const { syntax } = options
80
+ const transformsToRun = []
81
+ // const blockRegex = new RegExp(
82
+ // `(.*)(?:\\<\\!--(?:.*|\r?|\n?|\\s*)${matchWord}:START(?:(?:.|\\r?\\n)*?(?:\\()(.*)\\))?(.|\\r?\\n)*?)?<\!--(?:.*|\r?|\n?|\\s*)${matchWord}:END(?:.|\\r?\\n)*?--\\>`,
83
+ // 'gm'
84
+ // )
85
+ // `(?:.*)(?:\\<\\!--(?:.*|\\r?|\\n?|\\s*)${matchWord}:START\\s*([(\\[\\{]*[A-Za-z0-9_$-]*[)\\]\\}]*)\\s*)((?:.|\\r?\\n)*?)<\!--(?:.*|\\r?|\\n?|\\s*)${matchWord}:END(?:.|\\r?\\n)*?--\\>`,
86
+ const START = options.open
87
+ const END = options.close
88
+
89
+ const [ commentOpen, commentClose ] = syntaxMap[syntax].pattern
90
+ // const regexToUse = new RegExp(
91
+ // `([ \\t]*)(?:\\<\\!--(?:.*|\\r?|\\n?|\\s*)${matchWord}:START\\s*([(\\[\\{]*[A-Za-z0-9_$-]*[)\\]\\}]*)\\s*)((?:.|\\r?\\n)*?)<\!--(?:.*|\\r?|\\n?|\\s*)${matchWord}:END(?:.|\\r?\\n)*?--\\>`,
92
+ // 'gmi'
93
+ // )
94
+ const regexToUse = new RegExp(
95
+ `([ \\t]*)(?:${commentOpen}(?:.*|\\r?|\\n?|\\s*)${START}\\s*([(\\[\\{]*[A-Za-z0-9_$-]*[)\\]\\}]*)\\s*)((?:.|\\r?\\n)*?)${commentOpen}(?:.*|\\r?|\\n?|\\s*)${END}(?:.|\\r?\\n)*?${commentClose}`,
96
+ 'gmi'
97
+ )
98
+ const paramsRegex = new RegExp(`([\\s\\S]*?)${commentClose}`, 'gm')
99
+ const trimRegex = new RegExp(`${commentClose}$`)
100
+
101
+ // console.log('paramsRegex', paramsRegex)
102
+ // ([ \t]*)(?:\/\*(?:.*|\r?|\n?|\s*)XYZ:START\s*([(\[\{]*[A-Za-z0-9_$-]*[)\]\}]*)\s*)((?:.|\r?\n)*?)\/\*(?:.*|\r?|\n?|\s*)XYZ:END(?:.|\r?\n)*?\*\/
103
+ // console.log('regexToUse', regexToUse)
104
+
105
+ let openTagRegex = matchOpeningCommentTag(START, commentOpen, commentClose)
106
+ let closeTagRegex = matchClosingCommentTag(END, commentOpen, commentClose)
107
+ while ((commentMatches = regexToUse.exec(contents)) !== null) {
108
+ let props = {}
109
+ let meta = {}
110
+ const [ block, spaces, action, params ] = commentMatches
111
+ const indentation = spaces || ''
112
+ /*
113
+ console.log('index', commentMatches.index)
114
+ console.log('block', block)
115
+ console.log('action', action)
116
+ console.log('params', params)
117
+ console.log('spaces', `"${spaces}"`)
118
+ /** */
119
+ // This is necessary to avoid infinite loops
120
+ if (commentMatches.index === regexToUse.lastIndex) {
121
+ regexToUse.lastIndex++
122
+ }
123
+
124
+ openTagRegex = matchOpeningCommentTag(START, commentOpen, commentClose)
125
+ const openingTag = getOpeningTags(block, {
126
+ pattern: openTagRegex,
127
+ open: commentOpen,
128
+ close: commentClose
129
+ })
130
+ closeTagRegex = matchClosingCommentTag(END, commentOpen, commentClose)
131
+ const closingTag = getClosingTags(block, {
132
+ pattern: closeTagRegex
133
+ })
134
+ /*
135
+ console.log('openingTag', openingTag)
136
+ console.log('closingTag', closingTag)
137
+ /** */
138
+ const openingTagLength = openingTag.length //+ indentation.length
139
+ const contentEndPosition = block.indexOf(closingTag.tag, openingTagLength)
140
+ const content = getTextBetween(block, openingTagLength, contentEndPosition)
141
+ // console.log('contentcontent', content)
142
+ let originalContent = content
143
+ const contentEndsWithNewLine = getLastCharacter(originalContent) === '\n'
144
+ const openEndsWithNewLine = getLastCharacter(openingTag.tag) === '\n'
145
+ const isMultiline = block.indexOf('\n') > -1
146
+ meta.isMultiline = isMultiline
147
+ const closeTag = (contentEndsWithNewLine) ? `\n${closingTag.tag}` : closingTag.tag
148
+
149
+ // Move new line to beginning of closing tag
150
+ // if (originalContent.match(/\n$/)) {
151
+ if (contentEndsWithNewLine) {
152
+ // originalContent = originalContent.replace(/\n$/, '')
153
+ originalContent = originalContent.slice(0, -1)
154
+ }
155
+
156
+ // Strip indentation
157
+ originalContent = stripIndent(originalContent, indentation.length)
158
+ // console.log('originalContent')
159
+ // console.log(`"${originalContent}"`)
160
+ // originalContent = originalContent.replace(/^\s+|\s+$/g, '')
161
+
162
+ // (functionName) or [functionName] or {functionName}
163
+ const transform = action.replace(/[(\[\{]*([A-Z-a-z0-9_$-]*)[)\]\}]*/, '$1')
164
+ // if (transform && !transform.match(/^-+/)) {
165
+ if (transform && getFirstCharacter(transform) !== '-') {
166
+ // console.log('params', params)
167
+ // const paramValue = params.match(/([\s\S]*?)-->/gm)
168
+ const paramValue = params.match(paramsRegex)
169
+ let paramString
170
+ if (paramValue) {
171
+ // paramString = paramValue[0].replace(/-*>$/, '').trim()
172
+ paramString = paramValue[0].replace(trimRegex, '').trim()
173
+ // console.log('paramString', paramString)
174
+ if (paramString) {
175
+ // Legacy v1 options parser
176
+ if (getFirstCharacter(paramString) === ':') {
177
+ meta.isLegacy = true
178
+ paramString = paramString.replace(/\s?\)\s?$/, '').substring(1)
179
+ props = legacyParseOptions(paramString)
180
+ } else {
181
+ props = weirdParse(paramString)
182
+ }
183
+ }
184
+ }
185
+ /*
186
+ console.log(regexToUse)
187
+ console.log(`transform "${transform}" at ${regexToUse.lastIndex} using props:`)
188
+ console.log(props)
189
+ console.log('───────────────────────')
190
+ /** */
191
+ const shift = (openEndsWithNewLine) ? 1 : 0
192
+ const contentStart = commentMatches.index + openingTag.tag.length - shift //+ indentation.length
193
+ const contentEnd = contentStart + content.length + indentation.length + shift
194
+ //const addOne = (contentEndsWithNewLine) ? 1 : 0
195
+ transformsToRun.push({
196
+ transform,
197
+ args: props,
198
+ // content: originalContent,
199
+ block: {
200
+ indentation,
201
+ start: commentMatches.index,
202
+ end: regexToUse.lastIndex,
203
+ contentStart,
204
+ contentEnd,
205
+ contentIndent: minIndent(originalContent),
206
+ openTag: openingTag.tag,
207
+ content: originalContent,
208
+ closeTag: closeTag,
209
+ // full: `${openingTag.tag}${indentString(originalContent, indentation.length)}${closeTag}`,
210
+ // full: indentString(`${openingTag.tag}${originalContent}${closeTag}`, indentation.length),
211
+ // full: indentString(`${stripIndent(openingTag.tag, indentation.length)}${originalContent}${stripIndent(closeTag, indentation.length)}`, indentation.length)
212
+ },
213
+ raw: {
214
+ transform: (meta.isLegacy) ? action.replace(/^\s?\(/, '') : action,
215
+ args: paramString,
216
+ content: getTextBetween(contents, contentStart, contentEnd),
217
+ block: block,
218
+ },
219
+ meta,
220
+ })
221
+ }
222
+ }
223
+
224
+ // const newer =
225
+ // /(?:.*)(?:\<\!--(?:.*|\r?|\n?|\s*)DOCS:START\s*([(\[\{][A-Z-a-z_$-]*[)\]\}])\s*)((?:.|\r?\n)*?)<\!--(?:.*|\r?|\n?|\s*)DOCS:END(?:.|\r?\n)*?--\>/gmi
226
+ // // console.log('newera', newer)
227
+ /*
228
+ const comments = content.match(newerString)
229
+ console.log('comments', comments)
230
+ const commentRegexInside = /<\!-*\s*([\s\S]*?) ?-*\>\n*?/g
231
+ if (comments) {
232
+ // console.log('comments', comments)
233
+ // console.log(comments.length)
234
+ comments.forEach((text) => {
235
+ console.log('text', text)
236
+ const inside = commentRegexInside.exec(text)
237
+ console.log('inside', inside)
238
+ if (inside) {
239
+ const config = inside[1].replace(`${matchWord}:START`, '').trim()
240
+ // console.log(formatProps(config))
241
+ }
242
+ })
243
+ }*/
244
+
245
+ return {
246
+ pattern: regexToUse,
247
+ commentOpen: openTagRegex,
248
+ commentClose: closeTagRegex,
249
+ transforms: transformsToRun
250
+ }
251
+ }
252
+
253
+ function replaceTextBetween(origin, startIndex, endIndex, insertion) {
254
+ return origin.substring(0, startIndex) + insertion + origin.substring(endIndex)
255
+ }
256
+
257
+ function replaceContent(origin, insertion, data) {
258
+ return replaceTextBetween(origin, data.block.contentStart, data.block.contentEnd, insertion)
259
+ }
260
+
261
+ function getFirstCharacter(str) {
262
+ return str.charAt(0)
263
+ }
264
+
265
+ function getLastCharacter(str) {
266
+ return str.substr(-1)
267
+ }
268
+
269
+ function getLeadingSpaces(text) {
270
+ return text.match(/^\s/) ? text : ''
271
+ }
272
+
273
+ function getTextBetween(text, start, end) {
274
+ return text.slice(start, end)
275
+ }
276
+
277
+ function stripIndent(string, indentation) {
278
+ const indent = typeof indentation !== 'undefined' ? indentation : minIndent(string);
279
+ if (indent === 0) {
280
+ return string
281
+ }
282
+ const regex = new RegExp(`^[ \\t]{${indent}}`, 'gm')
283
+ return string.replace(regex, '')
284
+ }
285
+
286
+ // https://github.com/jamiebuilds/min-indent/blob/master/index.js
287
+ function minIndent(string) {
288
+ const match = string.match(/^[ \t]*(?=\S)/gm)
289
+ if (!match) return 0
290
+ return match.reduce((r, a) => Math.min(r, a.length), Infinity)
291
+ }
292
+
293
+ function indentString(string, count = 1, options = {}) {
294
+ const {
295
+ indent = ' ',
296
+ includeEmptyLines = false
297
+ } = options;
298
+ if (count === 0) return string
299
+ const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm
300
+ return string.replace(regex, indent.repeat(count))
301
+ }
302
+
303
+ function getOpeningTags(block, {
304
+ pattern,
305
+ open,
306
+ close
307
+ }) {
308
+ // console.log(block.match(/^\/\*+(.*)\*\//))
309
+ // console.log('openTagRegex', pattern)
310
+ let matches
311
+ while ((matches = pattern.exec(block)) !== null) {
312
+ if (matches.index === pattern.lastIndex) {
313
+ pattern.lastIndex++ // avoid infinite loops with zero-width matches
314
+ }
315
+ const [ tag, spaces, tagStart, tagEnd ] = matches
316
+ /*
317
+ console.log('FULL Open Tag >>>>>', tag)
318
+ console.log('openTag Start', "'"+tagStart+"'");
319
+ console.log('openTag End', "'"+tagEnd+"'");
320
+ /**/
321
+ return {
322
+ tag,
323
+ spaces: spaces || '',
324
+ length: tag.length,
325
+ tagStart,
326
+ tagEnd,
327
+ }
328
+ }
329
+ // Fallthrough
330
+ const fallbackRegex = new RegExp(`^([ \\t]*)(${open}([\\s\\S]*?)${close})\\n?`)
331
+ // const xyz = block.match(/^([ \t]*)(\/\*+([\s\S]*?)\*+\/)/)
332
+ const xyz = block.match(fallbackRegex)
333
+ /*
334
+ console.log('fallbackRegex', fallbackRegex)
335
+ console.log('fall through', `"${block}"`)
336
+ console.log('xyz', xyz)
337
+ /** */
338
+ return {
339
+ tag: xyz[0],
340
+ spaces: xyz[1] || '',
341
+ length: xyz[0].length,
342
+ }
343
+ }
344
+
345
+ function getClosingTags(block, {
346
+ pattern,
347
+ // open,
348
+ // close
349
+ }) {
350
+ // console.log('closeTagRegex', closeTagRegex)
351
+ let matches
352
+ while ((matches = pattern.exec(block)) !== null) {
353
+ if (matches.index === pattern.lastIndex) {
354
+ pattern.lastIndex++ // avoid infinite loops with zero-width matches
355
+ }
356
+ const [ _tag, spaces, tagStart, tagEnd] = matches
357
+ /*
358
+ console.log('FULL CLOSE Tag >>>>>', matches[0])
359
+ console.log('closeTag Start', "'"+matches[1]+"'");
360
+ console.log('closeTag End', "'"+matches[2]+"'");
361
+ /**/
362
+ const tag = spaces + tagStart + tagEnd
363
+ return {
364
+ tag: tag,
365
+ length: tag.length,
366
+ spaces: spaces || '',
367
+ tagStart,
368
+ tagEnd
369
+ }
370
+ }
371
+ }
372
+
373
+ function removeComments(str) {
374
+ // /([^\s]*)?([ \\t]*)\<\!-+\s?([\s\S]*?)?-+\>\n*?([^\s]*)?/gi
375
+ const pattern = new RegExp(`([^\\s]*)?([ \\t]*)<!-+\\s?([\\s\\S]*?)?-+>\n*?([^\\s]*)?`, 'gi')
376
+ return str.replace(pattern, '')
377
+ }
378
+
379
+ function matchOpeningCommentTag(word, open, close) {
380
+ // console.log('open', open)
381
+ // return new RegExp(`(\\<\\!--(?:.|\\r?\\n)*?${matchWord}:START)((?:.|\\r?\\n)*?--\\>)`, 'g')
382
+ return new RegExp(`([ \\t]*)(${open}(?:.|\r?|\n?|\\s*)\\b${word}\\b)((?:.|\\r?\\n)*?${close}\n?)`, 'gi')
383
+ // return new RegExp(`([ \\t]*)(\\<\\!--(?:.*|\r?|\n?|\s*)${word}:START)((?:.|\\r?\\n)*?--\\>\n?)`, 'gi')
384
+ }
385
+
386
+ function matchClosingCommentTag(word, open, close) {
387
+ return new RegExp(`${close}(?:.|\\r?\\n)*?([ \t]*)((?:${open}(?:.*|\\r?\\n)(?:.*|\\r?\\n))*?\\b${word}\\b)((?:.|\\r?\\n)*?${close})`, 'gi')
388
+ // return new RegExp(`--\\>(?:.|\\r?\\n)*?([ \t]*)((?:\\<\\!--(?:.*|\\r?\\n)(?:.*|\\r?\\n))*?${word}:END)((?:.|\\r?\\n)*?--\\>)`, 'gi')
389
+ }
390
+
391
+
392
+ function legacyParseOptions(options) {
393
+ const returnOptions = {}
394
+ if (!options) {
395
+ return returnOptions
396
+ }
397
+ options.split('&').map((opt, i) => { // eslint-disable-line
398
+ const getValues = opt.split(/=(.+)/)
399
+ if (getValues[0] && getValues[1]) {
400
+ returnOptions[getValues[0]] = getValues[1]
401
+ }
402
+ })
403
+ return returnOptions
404
+ }
405
+
406
+
407
+ module.exports = {
408
+ getTextBetween,
409
+ replaceTextBetween,
410
+ replaceContent,
411
+ parseBlocks,
412
+ }