markdown-magic 2.6.1 → 3.0.1

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.
Files changed (58) hide show
  1. package/README.md +47 -37
  2. package/cli.js +5 -82
  3. package/lib/block-parser-js.test.js +171 -0
  4. package/lib/block-parser.js +382 -0
  5. package/lib/block-parser.test.js +479 -0
  6. package/lib/cli.js +245 -0
  7. package/lib/cli.test.js +409 -0
  8. package/lib/defaults.js +12 -0
  9. package/lib/globals.d.ts +66 -0
  10. package/lib/index.js +353 -184
  11. package/lib/index.test.js +11 -0
  12. package/lib/process-contents.js +371 -0
  13. package/lib/process-file.js +37 -0
  14. package/lib/transforms/code.js +67 -28
  15. package/lib/transforms/file.js +17 -17
  16. package/lib/transforms/index.js +0 -114
  17. package/lib/transforms/remote.js +8 -6
  18. package/lib/transforms/sectionToc.js +18 -0
  19. package/lib/transforms/toc.js +12 -265
  20. package/lib/transforms/wordCount.js +5 -0
  21. package/lib/types.js +11 -0
  22. package/lib/utils/fs.js +342 -0
  23. package/lib/utils/fs.test.js +267 -0
  24. package/lib/utils/index.js +19 -0
  25. package/{cli-utils.js → lib/utils/load-config.js} +2 -6
  26. package/lib/utils/logs.js +94 -0
  27. package/lib/utils/md/filters.js +20 -0
  28. package/lib/utils/md/find-code-blocks.js +88 -0
  29. package/lib/utils/md/find-date.js +32 -0
  30. package/lib/utils/md/find-frontmatter.js +92 -0
  31. package/lib/utils/md/find-frontmatter.test.js +17 -0
  32. package/lib/utils/md/find-html-tags.js +105 -0
  33. package/lib/utils/md/find-images-md.js +27 -0
  34. package/lib/utils/md/find-images.js +107 -0
  35. package/lib/utils/md/find-links.js +220 -0
  36. package/lib/utils/md/find-unmatched-html-tags.js +32 -0
  37. package/lib/utils/md/fixtures/2022-01-22-date-in-filename.md +14 -0
  38. package/lib/utils/md/fixtures/file-with-frontmatter.md +32 -0
  39. package/lib/utils/md/fixtures/file-with-links.md +153 -0
  40. package/lib/utils/md/md.test.js +105 -0
  41. package/lib/utils/md/parse.js +146 -0
  42. package/lib/utils/md/utils.js +19 -0
  43. package/lib/utils/regex-timeout.js +84 -0
  44. package/lib/utils/regex.js +40 -6
  45. package/lib/utils/remoteRequest.js +55 -0
  46. package/lib/utils/syntax.js +82 -0
  47. package/lib/utils/text.js +328 -0
  48. package/lib/utils/text.test.js +305 -0
  49. package/lib/utils/toc.js +315 -0
  50. package/package.json +30 -26
  51. package/index.js +0 -46
  52. package/lib/processFile.js +0 -154
  53. package/lib/updateContents.js +0 -125
  54. package/lib/utils/_md.test.js +0 -63
  55. package/lib/utils/new-parser.js +0 -412
  56. package/lib/utils/new-parser.test.js +0 -324
  57. package/lib/utils/weird-parse.js +0 -230
  58. package/lib/utils/weird-parse.test.js +0 -217
@@ -0,0 +1,382 @@
1
+
2
+ const { parse } = require('oparser')
3
+ const { getSyntaxInfo } = require('./utils/syntax')
4
+ const { getTextBetweenChars, findMinIndent } = require('./utils/text')
5
+ const { OPEN_WORD, CLOSE_WORD, SYNTAX } = require('./defaults')
6
+ // Alt parser https://github.com/LesterLyu/fast-formula-parser/blob/master/grammar/lexing.js
7
+
8
+ const defaultOptions = {
9
+ syntax: SYNTAX,
10
+ open: OPEN_WORD,
11
+ close: CLOSE_WORD,
12
+ }
13
+
14
+ function parseBlocks(contents, opts = {}) {
15
+ const options = Object.assign({}, defaultOptions, opts)
16
+ const { syntax, open, close } = options
17
+ if (!open) {
18
+ throw new Error('Missing options.open')
19
+ }
20
+ if (!close) {
21
+ throw new Error('Missing options.close')
22
+ }
23
+ if (!syntax) {
24
+ throw new Error('Missing options.syntax')
25
+ }
26
+ const syntaxInfo = getSyntaxInfo(syntax)
27
+ if (!syntaxInfo.pattern) {
28
+ throw new Error(`Unknown syntax "${syntax}"`)
29
+ }
30
+ const [ openComment, closeComment ] = syntaxInfo.pattern
31
+
32
+ const patterns = getBlockRegex({
33
+ openComment,
34
+ closeComment,
35
+ openText: open,
36
+ closeText: close
37
+ })
38
+
39
+ const newerRegex = patterns.blockPattern
40
+ // console.log('newerRegex', newerRegex)
41
+
42
+ /*
43
+ const regexToUse = getBlockRegex({
44
+ openComment,
45
+ closeComment,
46
+ openText: open,
47
+ closeText: close
48
+ })
49
+ console.log('regexToUse', regexToUse)
50
+ */
51
+
52
+ // let openTagRegex = getOpenCommentRegex(open, openComment, closeComment)
53
+ // let closeTagRegex = getClosingCommentRegex(close, openComment, closeComment)
54
+ // console.log('openTagRegex', openTagRegex)
55
+ // console.log('patterns.openPattern', patterns.openPattern)
56
+ // console.log('closeTagRegex', closeTagRegex)
57
+ // console.log('patterns.closePattern', patterns.closePattern)
58
+
59
+ /* Verify comment blocks aren't broken (redos) */
60
+ const { isBalanced, openCount, closeCount } = verifyTagsBalanced(
61
+ contents,
62
+ patterns.openPattern,
63
+ patterns.closePattern
64
+ )
65
+ // console.log('isBalanced', isBalanced)
66
+ const balanced = (closeCount > openCount) ? true : isBalanced
67
+ if (!balanced) {
68
+ throw new Error(`Blocks are unbalanced.
69
+ ${openCount} "${open}" open tags.
70
+ ${closeCount} "${close}" close tags.
71
+ `)
72
+ }
73
+
74
+ /* New regex works! */
75
+ const newBlocks = []
76
+ let blockIndex = 0
77
+ let newMatches
78
+ while ((newMatches = newerRegex.exec(contents)) !== null) {
79
+ blockIndex++
80
+ let paramString = ''
81
+ let options = {}
82
+ const [ block, spaces, openTag, type, params = '', content, closeTag ] = newMatches
83
+
84
+ let transformType = type
85
+ paramString = params.trim()
86
+
87
+ /* Account for dashes in transform name. E.g. funky-name-here */
88
+ const dashInTransform = params.match(/^(-[^\s]*)/)
89
+ if (dashInTransform && dashInTransform[1]) {
90
+ transformType = type + dashInTransform[1]
91
+ paramString = paramString.replace(dashInTransform[1], '')
92
+ }
93
+ /*
94
+ console.log('index', newMatches.index)
95
+ console.log('block', block)
96
+ console.log('type', type)
97
+ console.log('params', params)
98
+ console.log('spaces', `"${spaces}"`)
99
+ /** */
100
+ const isMultiline = block.indexOf('\n') > -1
101
+ const indentation = spaces || ''
102
+ let context = {
103
+ isMultiline,
104
+ }
105
+ // console.log('newMatches', newMatches)
106
+ // This is necessary to avoid infinite loops
107
+ if (newMatches.index === newerRegex.lastIndex) {
108
+ newerRegex.lastIndex++
109
+ }
110
+ const openValue = indentation + openTag
111
+ const openStart = newMatches.index + indentation.length
112
+ const openEnd = openStart + openTag.length
113
+
114
+
115
+ const closeEnd = newerRegex.lastIndex
116
+ // const finIndentation = (lineOpen === lineClose) ? '' : indentation
117
+
118
+ const lineOpen = contents.substr(0, openStart).split('\n').length
119
+ const lineClose = contents.substr(0, closeEnd).split('\n').length
120
+
121
+ const contentStart = openStart + openTag.length // + indentation.length// - shift //+ indentation.length
122
+ const contentEnd = contentStart + content.length // + finIndentation.length // + shift
123
+ /* If single line comment block, remove indentation */
124
+ const finIndentation = (lineOpen === lineClose) ? '' : indentation
125
+
126
+ /* If old syntax XYZ?foo | XYZ:foo */
127
+ if (paramString.match(/^:|^\?/)) {
128
+ paramString = paramString.split(')')[0]
129
+ paramString = paramString.replace(/^:/, '').replace(/^\?/, '').replace(/\)$/g, '')
130
+ // console.log('paramString', `"${paramString}"`)
131
+ options = legacyParseOptions(paramString)
132
+ context.isLegacy = true
133
+ } else {
134
+ // console.log('paramString', paramString)
135
+ options = parse(paramString)
136
+ }
137
+
138
+ // console.log('open start', openStart)
139
+ // console.log('openEnd', openEnd)
140
+ // console.log('options', options)
141
+
142
+ const blockData = {
143
+ index: blockIndex,
144
+ type: transformType,
145
+ options,
146
+ context,
147
+ /* Open Tag */
148
+ open: {
149
+ value: openValue,
150
+ start: openStart,
151
+ end: openEnd
152
+ },
153
+ /* Inner Content */
154
+ content: {
155
+ value: content,
156
+ start: contentStart,
157
+ end: contentEnd,
158
+ indentation: findMinIndent(content),
159
+ },
160
+ /* Close Tag */
161
+ close: {
162
+ value: closeTag,
163
+ start: contentEnd,
164
+ end: closeEnd
165
+ },
166
+ /* Full Block */
167
+ block: {
168
+ indentation: finIndentation,
169
+ lines: [lineOpen, lineClose],
170
+ start: openStart,
171
+ end: closeEnd,
172
+ // position: [ commentMatches.index, regexToUse.lastIndex ],
173
+ // rawType: (context.isLegacy) ? type.replace(/^\s?\(/, '') : type,
174
+ rawArgs: paramString,
175
+ rawContent: getTextBetweenChars(contents, contentStart, contentEnd),
176
+ value: block,
177
+ },
178
+ }
179
+ // console.log('blockData', blockData)
180
+ newBlocks.push(blockData)
181
+ }
182
+
183
+ return {
184
+ // Close but no single line newPattern: newGetBlockRegex({ openComment, commentClose, start: START, ending: END }),
185
+ // pattern: regexToUse,
186
+ pattern: newerRegex,
187
+ // COMMENT_OPEN_REGEX: openTagRegex,
188
+ // COMMENT_CLOSE_REGEX: closeTagRegex,
189
+ COMMENT_OPEN_REGEX: patterns.openPattern,
190
+ COMMENT_CLOSE_REGEX: patterns.closePattern,
191
+ blocks: newBlocks
192
+ }
193
+ }
194
+
195
+ function verifyTagsBalanced(str, open, close) {
196
+ const openCount = (str.match(open) || []).length
197
+ const closeCount = (str.match(close) || []).length
198
+ return {
199
+ isBalanced: openCount === closeCount,
200
+ openCount,
201
+ closeCount
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Strip brackets from string (functionName) or [functionName] or {functionName}
207
+ * @param {string} str
208
+ * @returns {string}
209
+ */
210
+ function stripBrackets(str) {
211
+ return str.replace(/[(\[\{]*([A-Z-a-z0-9_$-]*)[)\]\}]*/, '$1')
212
+ }
213
+
214
+ function legacyParseOptions(options) {
215
+ const returnOptions = {}
216
+ if (!options) {
217
+ return returnOptions
218
+ }
219
+ options.split('&').map((opt, i) => { // eslint-disable-line
220
+ const getValues = opt.split(/=(.+)/)
221
+ if (getValues[0] && getValues[1]) {
222
+ returnOptions[getValues[0]] = getValues[1]
223
+ }
224
+ })
225
+ return returnOptions
226
+ }
227
+
228
+
229
+ /* TODO someday Named matches
230
+ (?<leading>[ \t]*)(?:<!-{2,}(?:.*|\r?|\n?|\s*)MD-MAGIC-EXAMPLE:START\s*(?<key>[(\[\{]*[A-Za-z0-9_$-]*[)\]\}]*)\s*)([\s\S]*?)-->(?<content>(?:.*?|.*?\r?\n?)*?)<!-{2,}(?:.*|\r?|\n?|\s*)MD-MAGIC-EXAMPLE:END(?:.|\r?\n)*?-{2,}>
231
+ */
232
+
233
+ /**
234
+ * Block matching patterns
235
+ * @typedef {{blockPattern: RegExp, openPattern: RegExp, closePattern: RegExp}} RegexPatterns
236
+ */
237
+
238
+ /**
239
+ * Get Regex pattern to match block
240
+ * @param {object} options
241
+ * @param {string} [options.openEmoji] - emoji
242
+ * @param {string} [options.openComment] - comment syntax open
243
+ * @param {string} [options.closeComment] - comment syntax open
244
+ * @param {string} [options.openText] - comment open text
245
+ * @param {string} [options.closeText] - comment close text
246
+ * @param {boolean} [options.allowMissingTransforms] - Allow for missing transform key
247
+ * @returns {RegexPatterns}
248
+ */
249
+ function getBlockRegex({
250
+ openEmoji,
251
+ openComment,
252
+ closeComment,
253
+ openText = '',
254
+ closeText = '',
255
+ allowMissingTransforms = false
256
+ }) {
257
+ // https://regex101.com/r/SU2g1Q/1
258
+ // https://regex101.com/r/SU2g1Q/2
259
+ // https://regex101.com/r/SU2g1Q/3
260
+ // https://regex101.com/r/SU2g1Q/4
261
+ // https://regex101.com/r/SU2g1Q/5
262
+ // https://regex101.com/r/SU2g1Q/6
263
+ // /([ \t]*)(<!-{2,}(?:.|\r?|\n?|\s*)\bdoc-gen\b)((?:.|\r?\n)*?)-{2,}>([\s\S]*?.*)\n?<!-{2,}(?:.*|\r?|\n?|\s*)end-doc-gen(?:.|\r?\n)*?-{2,}>/gim
264
+ // /([ \t]*)(<!-{2,}(?:\r?|\n?|\s*)\bdoc-gen\b)\s*([(\[\{]*[A-Za-z0-9_$-]*[)\]\}]*)\s*((?:.|\r?\n)*?)-{2,}>([\s\S]*?.*)\n?<!-{2,}(?:.*|\r?|\n?|\s*)end-doc-gen(?:.|\r?\n)*?-{2,}>/
265
+ const emojiPat = (openEmoji) ? `(?:\\s*${openEmoji})?` : '(?:\\s*⛔️)?'
266
+ const boundary = openText.indexOf('/') > -1 ? '' : '\\b'
267
+ const matchWord = `${boundary}${openText}${boundary}`
268
+ const hasOne = (allowMissingTransforms) ? '*' : '+'
269
+ const open = `((?:${openComment}${emojiPat}(?:\\r?|\\n?|\\s*)${matchWord})\\s*[(\\[\\{]*([A-Za-z0-9_$]${hasOne})[)\\]\\}]*\\s*((?:.|\\r?\\n)*?)${closeComment}\\n?)`
270
+ const close = `(\\n?[ \\t]*${openComment}${emojiPat}(?:\\r?|\\n?|\\s*)${closeText}(?:.|\\r?\\n)*?${closeComment})`
271
+ // const close = `(\\n?${openComment}(?:.*|\\r?|\\n?|\\s*)${closeText}(?:.|\\r?\\n)*?${closeComment})`
272
+ const blockPattern = new RegExp(`([ \\t]*)${open}([\\s\\S]*?.*)${close}`, 'gmi')
273
+ const openPattern = new RegExp(open, 'gi')
274
+ const closePattern = new RegExp(close, 'gi')
275
+
276
+ return {
277
+ blockPattern,
278
+ openPattern,
279
+ closePattern
280
+ }
281
+ }
282
+
283
+ // function getOpeningTags(block, {
284
+ // pattern,
285
+ // open,
286
+ // close
287
+ // }) {
288
+ // // console.log(block.match(/^\/\*+(.*)\*\//))
289
+ // // console.log('openTagRegex', pattern)
290
+ // let matches
291
+ // while ((matches = pattern.exec(block)) !== null) {
292
+ // if (matches.index === pattern.lastIndex) {
293
+ // pattern.lastIndex++ // avoid infinite loops with zero-width matches
294
+ // }
295
+ // const [ tag, spaces, tagStart, tagEnd ] = matches
296
+ // /*
297
+ // console.log('FULL Open Tag >>>>>', tag)
298
+ // console.log('openTag Start', "'"+tagStart+"'");
299
+ // console.log('openTag End', "'"+tagEnd+"'");
300
+ // /**/
301
+ // return {
302
+ // tag,
303
+ // spaces: spaces || '',
304
+ // length: tag.length,
305
+ // tagStart,
306
+ // tagEnd,
307
+ // }
308
+ // }
309
+ // }
310
+
311
+ // function getClosingTags(block, {
312
+ // pattern,
313
+ // // open,
314
+ // // close
315
+ // }) {
316
+ // // console.log('closeTagRegex', closeTagRegex)
317
+ // let matches
318
+ // while ((matches = pattern.exec(block)) !== null) {
319
+ // if (matches.index === pattern.lastIndex) {
320
+ // pattern.lastIndex++ // avoid infinite loops with zero-width matches
321
+ // }
322
+ // const [ _tag, spaces, tagStart, tagEnd] = matches
323
+ // /*
324
+ // console.log('FULL CLOSE Tag >>>>>', matches[0])
325
+ // console.log('closeTag Start', "'"+matches[1]+"'");
326
+ // console.log('closeTag End', "'"+matches[2]+"'");
327
+ // /**/
328
+ // const tag = spaces + tagStart + tagEnd
329
+ // return {
330
+ // tag: tag,
331
+ // length: tag.length,
332
+ // spaces: spaces || '',
333
+ // tagStart,
334
+ // tagEnd
335
+ // }
336
+ // }
337
+ // }
338
+
339
+ // /**
340
+ // * Get Regex pattern to match block
341
+ // * @param {object} options
342
+ // * @param {string} [options.openComment] - comment syntax open
343
+ // * @param {string} [options.closeComment] - comment syntax open
344
+ // * @param {string} [options.openText] - comment open text
345
+ // * @param {string} [options.closeText] - comment close text
346
+ // * @returns {RegExp}
347
+ // */
348
+ // function getBlockRegexOld({ openComment, closeComment, openText, closeText }) {
349
+ // // /([ \t]*)(<!-{2,}(?:.|\r?|\n?|\s*)\bdoc-gen\b)((?:.|\r?\n)*?)-{2,}>(.*)<!-{2,}(?:.*|\r?|\n?|\s*)end-doc-gen(?:.|\r?\n)*?-{2,}>/i singleline
350
+ // return new RegExp(
351
+ // `([ \\t]*)(?:${openComment}(?:.*|\\r?|\\n?|\\s*)${openText}\\s*([(\\[\\{]*[A-Za-z0-9_$-]*[)\\]\\}]*)\\s*)((?:.*?|.*?\\r?\\n?)*?)${openComment}(?:.*|\\r?|\\n?|\\s*)${closeText}(?:.|\\r?\\n)*?${closeComment}`,
352
+ // 'gmi'
353
+ // )
354
+ // }
355
+
356
+
357
+ // function newGetBlockRegex({ commentOpen, commentClose, start, ending }) {
358
+ // // https://regex101.com/r/C9WSk8/1 close but breaks on single line blocks. Maybe needs lookahead https://stackoverflow.com/questions/7124778/how-can-i-match-anything-up-until-this-sequence-of-characters-in-a-regular-exp
359
+ // return new RegExp(
360
+ // `([ \\t]*)(?:${commentOpen}(?:.*|\\r?|\\n?|\\s*)${start}\\s*([(\\[\\{]*[A-Za-z0-9_$-]*[)\\]\\}]*)\\s*)([\\s\\S]*?)${commentClose}((?:.*?|.*?\\r?\\n?)*?)${commentOpen}(?:.*|\\r?|\\n?|\\s*)${ending}(?:.|\\r?\\n)*?${commentClose}`,
361
+ // 'gmi'
362
+ // )
363
+ // }
364
+
365
+ // function getOpenCommentRegex(word, open, close) {
366
+ // // console.log('open', open)
367
+ // const boundary = word.indexOf('/') > -1 ? '' : '\\b'
368
+ // // console.log('boundary', boundary)
369
+ // // return new RegExp(`(\\<\\!--(?:.|\\r?\\n)*?${matchWord}:START)((?:.|\\r?\\n)*?--\\>)`, 'g')
370
+ // return new RegExp(`([ \\t]*)(${open}(?:.|\r?|\n?|\\s*)${boundary}${word}${boundary})((?:.|\\r?\\n)*?${close}\n?)`, 'gi')
371
+ // }
372
+
373
+ // function getClosingCommentRegex(word, open, close) {
374
+ // const boundary = word.indexOf('/') > -1 ? '' : '\\b'
375
+ // return new RegExp(`${close}(?:.|\\r?\\n)*?([ \t]*)((?:${open}(?:.*|\\r?\\n)(?:.*|\\r?\\n))*?${boundary}${word}${boundary})((?:.|\\r?\\n)*?${close})`, 'gi')
376
+ // // return new RegExp(`--\\>(?:.|\\r?\\n)*?([ \t]*)((?:\\<\\!--(?:.*|\\r?\\n)(?:.*|\\r?\\n))*?${word}:END)((?:.|\\r?\\n)*?--\\>)`, 'gi')
377
+ // }
378
+
379
+ module.exports = {
380
+ getBlockRegex,
381
+ parseBlocks,
382
+ }