markdown-magic 2.6.1 → 3.0.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.
Files changed (61) hide show
  1. package/README.md +6 -10
  2. package/cli.js +5 -82
  3. package/lib/block-parser-js.test.js +179 -0
  4. package/lib/block-parser.js +389 -0
  5. package/lib/{utils/new-parser.test.js → block-parser.test.js} +168 -50
  6. package/lib/cli.js +234 -0
  7. package/lib/cli.test.js +409 -0
  8. package/lib/defaults.js +12 -0
  9. package/lib/index.js +319 -184
  10. package/lib/index.test.js +11 -0
  11. package/lib/options-parser.js +498 -0
  12. package/lib/options-parser.test.js +1237 -0
  13. package/lib/process-contents.js +330 -0
  14. package/lib/process-file.js +34 -0
  15. package/lib/transforms/code.js +67 -22
  16. package/lib/transforms/file.js +13 -10
  17. package/lib/transforms/remote.js +9 -6
  18. package/lib/transforms/toc.js +136 -64
  19. package/lib/transforms/wordCount.js +5 -0
  20. package/lib/utils/fs.js +340 -0
  21. package/lib/utils/fs.test.js +268 -0
  22. package/lib/utils/html-to-json/compat.js +42 -0
  23. package/lib/utils/html-to-json/format.js +64 -0
  24. package/lib/utils/html-to-json/index.js +37 -0
  25. package/lib/utils/html-to-json/lexer.js +345 -0
  26. package/lib/utils/html-to-json/parser.js +146 -0
  27. package/lib/utils/html-to-json/stringify.js +37 -0
  28. package/lib/utils/html-to-json/tags.js +171 -0
  29. package/lib/utils/index.js +19 -0
  30. package/{cli-utils.js → lib/utils/load-config.js} +2 -6
  31. package/lib/utils/logs.js +89 -0
  32. package/lib/utils/md/filters.js +20 -0
  33. package/lib/utils/md/find-code-blocks.js +80 -0
  34. package/lib/utils/md/find-date.js +32 -0
  35. package/lib/utils/md/find-frontmatter.js +94 -0
  36. package/lib/utils/md/find-frontmatter.test.js +17 -0
  37. package/lib/utils/md/find-html-tags.js +105 -0
  38. package/lib/utils/md/find-images.js +102 -0
  39. package/lib/utils/md/find-links.js +202 -0
  40. package/lib/utils/md/find-unmatched-html-tags.js +33 -0
  41. package/lib/utils/md/fixtures/2022-01-22-date-in-filename.md +14 -0
  42. package/lib/utils/md/fixtures/file-with-frontmatter.md +32 -0
  43. package/lib/utils/md/fixtures/file-with-links.md +143 -0
  44. package/lib/utils/md/md.test.js +37 -0
  45. package/lib/utils/md/parse.js +122 -0
  46. package/lib/utils/md/utils.js +19 -0
  47. package/lib/utils/regex-timeout.js +83 -0
  48. package/lib/utils/regex.js +38 -5
  49. package/lib/utils/remoteRequest.js +54 -0
  50. package/lib/utils/syntax.js +79 -0
  51. package/lib/utils/text.js +260 -0
  52. package/lib/utils/text.test.js +311 -0
  53. package/package.json +25 -25
  54. package/index.js +0 -46
  55. package/lib/processFile.js +0 -154
  56. package/lib/transforms/index.js +0 -114
  57. package/lib/updateContents.js +0 -125
  58. package/lib/utils/_md.test.js +0 -63
  59. package/lib/utils/new-parser.js +0 -412
  60. package/lib/utils/weird-parse.js +0 -230
  61. package/lib/utils/weird-parse.test.js +0 -217
@@ -0,0 +1,389 @@
1
+
2
+ const { optionsParse } = require('./options-parser')
3
+ const { getSyntaxInfo } = require('./utils/syntax')
4
+ const {
5
+ getFirstCharacter,
6
+ getLastCharacter,
7
+ getTextBetweenChars,
8
+ stripIndent,
9
+ findMinIndent
10
+ } = require('./utils/text')
11
+ const { OPEN_WORD, CLOSE_WORD, SYNTAX } = require('./defaults')
12
+ // Alt parser https://github.com/LesterLyu/fast-formula-parser/blob/master/grammar/lexing.js
13
+
14
+ const defaultOptions = {
15
+ syntax: SYNTAX,
16
+ open: OPEN_WORD,
17
+ close: CLOSE_WORD,
18
+ }
19
+
20
+ function parseBlocks(contents, opts = {}) {
21
+ const blocks = []
22
+ const options = Object.assign({}, defaultOptions, opts)
23
+ const { syntax, open, close } = options
24
+ if (!open) {
25
+ throw new Error('Missing options.open')
26
+ }
27
+ if (!close) {
28
+ throw new Error('Missing options.close')
29
+ }
30
+ if (!syntax) {
31
+ throw new Error('Missing options.syntax')
32
+ }
33
+ const syntaxInfo = getSyntaxInfo(syntax)
34
+ if (!syntaxInfo.pattern) {
35
+ throw new Error(`Unknown syntax "${syntax}"`)
36
+ }
37
+ const [ openComment, closeComment ] = syntaxInfo.pattern
38
+ const regexToUse = getBlockRegex({
39
+ openComment,
40
+ closeComment,
41
+ openText: open,
42
+ closeText: close
43
+ })
44
+ // console.log('regexToUse', regexToUse)
45
+ const paramsRegex = new RegExp(`([\\s\\S]*?)${closeComment}`, 'gm')
46
+ //console.log('paramsRegex', paramsRegex)
47
+ const trimRegex = new RegExp(`${closeComment}$`)
48
+ // ([ \t]*)(?:\/\*(?:.*|\r?|\n?|\s*)XYZ:START\s*([(\[\{]*[A-Za-z0-9_$-]*[)\]\}]*)\s*)((?:.|\r?\n)*?)\/\*(?:.*|\r?|\n?|\s*)XYZ:END(?:.|\r?\n)*?\*\/
49
+
50
+ let openTagRegex = getOpenCommentRegex(open, openComment, closeComment)
51
+ let closeTagRegex = getClosingCommentRegex(close, openComment, closeComment)
52
+ // console.log('openTagRegex', openTagRegex)
53
+ // console.log('closeTagRegex', closeTagRegex)
54
+
55
+ /* Verify comment blocks aren't broken (redos) */
56
+ const { isBalanced, openCount, closeCount } = verifyTagsBalanced(contents, openTagRegex, closeTagRegex)
57
+ const balanced = (closeCount > openCount) ? true : isBalanced
58
+ if (!balanced) {
59
+ throw new Error(`Blocks are unbalanced.
60
+ ${openCount} "${open}" open tags.
61
+ ${closeCount} "${close}" close tags.
62
+ `)
63
+ }
64
+ let index = 0
65
+ while ((commentMatches = regexToUse.exec(contents)) !== null) {
66
+ index++
67
+ let props = {}
68
+ let paramString = ''
69
+ const [ block, spaces, __type, params ] = commentMatches
70
+ const isMultiline = block.indexOf('\n') > -1
71
+ let context = {
72
+ isMultiline,
73
+ }
74
+ // console.log('commentMatches', commentMatches)
75
+ const indentation = spaces || ''
76
+ /* Remove trailing -- if no params */
77
+ const type = __type.replace(/-*$/, '')
78
+ /*
79
+ console.log('index', commentMatches.index)
80
+ console.log('block', block)
81
+ console.log('type', type)
82
+ console.log('params', params)
83
+ console.log('spaces', `"${spaces}"`)
84
+ /** */
85
+ // This is necessary to avoid infinite loops
86
+ if (commentMatches.index === regexToUse.lastIndex) {
87
+ regexToUse.lastIndex++
88
+ }
89
+
90
+ openTagRegex = getOpenCommentRegex(open, openComment, closeComment)
91
+ // console.log('openTagRegex', openTagRegex)
92
+ const openingTag = getOpeningTags(block, {
93
+ pattern: openTagRegex,
94
+ open: openComment,
95
+ close: closeComment
96
+ })
97
+ closeTagRegex = getClosingCommentRegex(close, openComment, closeComment)
98
+ // console.log('closeTagRegex', closeTagRegex)
99
+ const closingTag = getClosingTags(block, {
100
+ pattern: closeTagRegex
101
+ })
102
+ /*
103
+ console.log('openingTag', openingTag)
104
+ console.log('closingTag', closingTag)
105
+ /** */
106
+ if (!openingTag || !closingTag) {
107
+ continue;
108
+ }
109
+
110
+ const openingTagLength = openingTag.length //+ indentation.length
111
+ const contentEndPosition = block.indexOf(closingTag.tag, openingTagLength)
112
+ const content = getTextBetweenChars(block, openingTagLength, contentEndPosition)
113
+ // console.log('content', content)
114
+ let originalContent = content
115
+ const contentEndsWithNewLine = getLastCharacter(originalContent) === '\n'
116
+ const openEndsWithNewLine = getLastCharacter(openingTag.tag) === '\n'
117
+
118
+ const closeTag = (contentEndsWithNewLine) ? `\n${closingTag.tag}` : closingTag.tag
119
+
120
+ // Move new line to beginning of closing tag
121
+ // if (originalContent.match(/\n$/)) {
122
+ if (contentEndsWithNewLine) {
123
+ // originalContent = originalContent.replace(/\n$/, '')
124
+ originalContent = originalContent.slice(0, -1)
125
+ }
126
+ /* Strip indentation */
127
+ originalContent = stripIndent(originalContent, indentation.length)
128
+ // originalContent = originalContent.replace(/^\s+|\s+$/g, '')
129
+ /*
130
+ console.log('originalContent')
131
+ console.log(`"${originalContent}"`)
132
+ /** */
133
+
134
+ /* strip brackets (functionName) or [functionName] or {functionName} */
135
+ const cleanType = stripBrackets(type)
136
+ const shift = (openEndsWithNewLine) ? 1 : 0
137
+ const lineOpen = contents.substr(0, commentMatches.index).split('\n').length
138
+ const lineClose = contents.substr(0, regexToUse.lastIndex).split('\n').length
139
+ const contentStart = commentMatches.index + openingTag.tag.length - shift //+ indentation.length
140
+ /* If single line comment block, remove indentation */
141
+ const finIndentation = (lineOpen === lineClose) ? '' : indentation
142
+ const contentEnd = contentStart + content.length + finIndentation.length + shift
143
+
144
+ // if (cleanType && !cleanType.match(/^-+/)) {
145
+ if (cleanType && getFirstCharacter(cleanType) !== '-') {
146
+ // console.log('params', params)
147
+ // const paramValue = params.match(/([\s\S]*?)-->/gm)
148
+ const paramValue = params.match(paramsRegex)
149
+ // console.log('paramValue', paramValue)
150
+ if (paramValue) {
151
+ // paramString = paramValue[0].replace(/-*>$/, '').trim()
152
+ paramString = paramValue[0].replace(trimRegex, '').trim()
153
+ // console.log('clean', `${cleanType}`)
154
+ // console.log('param', `${paramString}`)
155
+ // console.log('type ', `${__type}`)
156
+ // console.log('──────────────────────')
157
+ // console.log(`${cleanType} "${paramString}" "${__type}"`)
158
+ if (paramString) {
159
+ // console.log('paramString', `"${paramString}"`)
160
+ // Legacy v1 options parser
161
+ if (getFirstCharacter(paramString) === ':' || getFirstCharacter(paramString) === '?') {
162
+ context.isLegacy = true
163
+ // paramString = paramString.replace(/\s?\)\s*$/, '').substring(1)
164
+ paramString = paramString.split(')')[0].substring(1)
165
+ // console.log('fixed paramString', paramString)
166
+ props = legacyParseOptions(paramString)
167
+ } else {
168
+ if (type.startsWith('(') && paramString.endsWith(')')) {
169
+ paramString = paramString.replace(/\)$/, '')
170
+ }
171
+ props = optionsParse(paramString)
172
+
173
+ }
174
+ } else if (!paramString && __type.match(/^\(.*\)$/)) {
175
+ context.isLegacy = true
176
+ }
177
+ }
178
+ /*
179
+ console.log(regexToUse)
180
+ console.log(`cleanType "${cleanType}" at ${regexToUse.lastIndex} using props:`)
181
+ console.log(props)
182
+ console.log('───────────────────────')
183
+ /** */
184
+ }
185
+
186
+ /* Add found block */
187
+ blocks.push({
188
+ index: index,
189
+ type: cleanType,
190
+ options: props,
191
+ context,
192
+ /* Open Tag */
193
+ open: {
194
+ value: openingTag.tag,
195
+ start: commentMatches.index,
196
+ end: contentStart
197
+ },
198
+ /* Inner Content */
199
+ content: {
200
+ value: originalContent,
201
+ start: contentStart,
202
+ end: contentEnd,
203
+ indentation: findMinIndent(originalContent),
204
+ },
205
+ /* Close Tag */
206
+ close: {
207
+ value: closeTag,
208
+ start: contentEnd,
209
+ end: regexToUse.lastIndex
210
+ },
211
+ /* Full Block */
212
+ block: {
213
+ indentation: finIndentation,
214
+ lines: [lineOpen, lineClose],
215
+ start: commentMatches.index,
216
+ end: regexToUse.lastIndex,
217
+ // position: [ commentMatches.index, regexToUse.lastIndex ],
218
+ rawType: (context.isLegacy) ? type.replace(/^\s?\(/, '') : type,
219
+ rawArgs: paramString,
220
+ rawContent: getTextBetweenChars(contents, contentStart, contentEnd),
221
+ value: block,
222
+ },
223
+ })
224
+ }
225
+ return {
226
+ // Close but no single line newPattern: newGetBlockRegex({ openComment, commentClose, start: START, ending: END }),
227
+ pattern: regexToUse,
228
+ COMMENT_OPEN_REGEX: openTagRegex,
229
+ COMMENT_CLOSE_REGEX: closeTagRegex,
230
+ blocks
231
+ }
232
+ }
233
+
234
+ function verifyTagsBalanced(str, open, close) {
235
+ const openCount = (str.match(open) || []).length
236
+ const closeCount = (str.match(close) || []).length
237
+ return {
238
+ isBalanced: openCount === closeCount,
239
+ openCount,
240
+ closeCount
241
+ }
242
+ }
243
+
244
+ function getOpeningTags(block, {
245
+ pattern,
246
+ open,
247
+ close
248
+ }) {
249
+ // console.log(block.match(/^\/\*+(.*)\*\//))
250
+ // console.log('openTagRegex', pattern)
251
+ let matches
252
+ while ((matches = pattern.exec(block)) !== null) {
253
+ if (matches.index === pattern.lastIndex) {
254
+ pattern.lastIndex++ // avoid infinite loops with zero-width matches
255
+ }
256
+ const [ tag, spaces, tagStart, tagEnd ] = matches
257
+ /*
258
+ console.log('FULL Open Tag >>>>>', tag)
259
+ console.log('openTag Start', "'"+tagStart+"'");
260
+ console.log('openTag End', "'"+tagEnd+"'");
261
+ /**/
262
+ return {
263
+ tag,
264
+ spaces: spaces || '',
265
+ length: tag.length,
266
+ tagStart,
267
+ tagEnd,
268
+ }
269
+ }
270
+ // // Fallthrough
271
+ // // const fallbackRegex = new RegExp(`^([ \\t]*)(${open}([\\s\\S]*?)${close})\\n?`)
272
+ // const fallbackRegex = new RegExp(`^([ \\t]*)(\\b${open}\\b([\\s\\S]*?)${close})\\n?`)
273
+ // // const xyz = block.match(/^([ \t]*)(\/\*+([\s\S]*?)\*+\/)/)
274
+ // const fallbackMatch= block.match(fallbackRegex)
275
+ // if (fallbackMatch) {
276
+ // /*
277
+ // console.log('fallbackRegex', fallbackRegex)
278
+ // console.log('fall through', `"${block}"`)
279
+ // console.log('xyz', xyz)
280
+ // /** */
281
+ // return {
282
+ // fallthrough: true,
283
+ // tag: fallbackMatch[0],
284
+ // spaces: fallbackMatch[1] || '',
285
+ // length: fallbackMatch[0].length,
286
+ // }
287
+ // }
288
+ }
289
+
290
+ function getClosingTags(block, {
291
+ pattern,
292
+ // open,
293
+ // close
294
+ }) {
295
+ // console.log('closeTagRegex', closeTagRegex)
296
+ let matches
297
+ while ((matches = pattern.exec(block)) !== null) {
298
+ if (matches.index === pattern.lastIndex) {
299
+ pattern.lastIndex++ // avoid infinite loops with zero-width matches
300
+ }
301
+ const [ _tag, spaces, tagStart, tagEnd] = matches
302
+ /*
303
+ console.log('FULL CLOSE Tag >>>>>', matches[0])
304
+ console.log('closeTag Start', "'"+matches[1]+"'");
305
+ console.log('closeTag End', "'"+matches[2]+"'");
306
+ /**/
307
+ const tag = spaces + tagStart + tagEnd
308
+ return {
309
+ tag: tag,
310
+ length: tag.length,
311
+ spaces: spaces || '',
312
+ tagStart,
313
+ tagEnd
314
+ }
315
+ }
316
+ }
317
+
318
+
319
+ const EMOJI = '\\u00a9|\\u00ae|[\\u2000-\\u3300]|\\ud83c[\\ud000-\\udfff]|\\ud83d[\\ud000-\\udfff]|\\ud83e[\\ud000-\\udfff]'
320
+
321
+ /**
322
+ * Get Regex pattern to match block
323
+ * @param {object} options
324
+ * @param {string} [options.openComment] - comment syntax open
325
+ * @param {string} [options.closeComment] - comment syntax open
326
+ * @param {string} [options.openText] - comment open text
327
+ * @param {string} [options.closeText] - comment close text
328
+ * @returns {RegExp}
329
+ */
330
+ function getBlockRegex({ openComment, closeComment, openText, closeText }) {
331
+ return new RegExp(
332
+ `([ \\t]*)(?:${openComment}(?:.*|\\r?|\\n?|\\s*)${openText}\\s*([(\\[\\{]*[A-Za-z0-9_$-]*[)\\]\\}]*)\\s*)((?:.*?|.*?\\r?\\n?)*?)${openComment}(?:.*|\\r?|\\n?|\\s*)${closeText}(?:.|\\r?\\n)*?${closeComment}`,
333
+ 'gmi'
334
+ )
335
+ }
336
+
337
+ /* // Named matches
338
+ (?<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,}>
339
+ */
340
+ function newGetBlockRegex({ commentOpen, commentClose, start, ending }) {
341
+ // 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
342
+ return new RegExp(
343
+ `([ \\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}`,
344
+ 'gmi'
345
+ )
346
+ }
347
+
348
+ function getOpenCommentRegex(word, open, close) {
349
+ // console.log('open', open)
350
+ const boundary = word.indexOf('/') > -1 ? '' : '\\b'
351
+ // console.log('boundary', boundary)
352
+ // return new RegExp(`(\\<\\!--(?:.|\\r?\\n)*?${matchWord}:START)((?:.|\\r?\\n)*?--\\>)`, 'g')
353
+ return new RegExp(`([ \\t]*)(${open}(?:.|\r?|\n?|\\s*)${boundary}${word}${boundary})((?:.|\\r?\\n)*?${close}\n?)`, 'gi')
354
+ }
355
+
356
+ function getClosingCommentRegex(word, open, close) {
357
+ const boundary = word.indexOf('/') > -1 ? '' : '\\b'
358
+ return new RegExp(`${close}(?:.|\\r?\\n)*?([ \t]*)((?:${open}(?:.*|\\r?\\n)(?:.*|\\r?\\n))*?${boundary}${word}${boundary})((?:.|\\r?\\n)*?${close})`, 'gi')
359
+ // return new RegExp(`--\\>(?:.|\\r?\\n)*?([ \t]*)((?:\\<\\!--(?:.*|\\r?\\n)(?:.*|\\r?\\n))*?${word}:END)((?:.|\\r?\\n)*?--\\>)`, 'gi')
360
+ }
361
+
362
+ /**
363
+ * Strip brackets from string (functionName) or [functionName] or {functionName}
364
+ * @param {string} str
365
+ * @returns {string}
366
+ */
367
+ function stripBrackets(str) {
368
+ return str.replace(/[(\[\{]*([A-Z-a-z0-9_$-]*)[)\]\}]*/, '$1')
369
+ }
370
+
371
+ function legacyParseOptions(options) {
372
+ const returnOptions = {}
373
+ if (!options) {
374
+ return returnOptions
375
+ }
376
+ options.split('&').map((opt, i) => { // eslint-disable-line
377
+ const getValues = opt.split(/=(.+)/)
378
+ if (getValues[0] && getValues[1]) {
379
+ returnOptions[getValues[0]] = getValues[1]
380
+ }
381
+ })
382
+ return returnOptions
383
+ }
384
+
385
+
386
+ module.exports = {
387
+ getBlockRegex,
388
+ parseBlocks,
389
+ }
@@ -1,12 +1,7 @@
1
- const { inspect } = require('util')
2
1
  const { test } = require('uvu')
3
2
  const assert = require('uvu/assert')
4
- const weirdParse = require('./weird-parse')
5
- const { parseBlocks } = require('./new-parser')
6
-
7
- function deepLog(v) {
8
- console.log(inspect(v, {showHidden: false, depth: null}))
9
- }
3
+ const { parseBlocks } = require('./block-parser')
4
+ const { deepLog } = require('./utils/logs')
10
5
 
11
6
  const md = `<h1 id="jdjdj">Netlify + FaunaDB &nbsp;&nbsp;&nbsp;
12
7
  <a href="https://app.netlify.com/start/deploy?repository=https://github.com/netlify/netlify-faunadb-example&stack=fauna">
@@ -182,38 +177,65 @@ contents
182
177
  Markdown magic uses comment blocks in markdown files to automatically sync or transform its contents. <img align="right" width="200" height="183" src="https://cloud.githubusercontent.com/assets/532272/21507867/3376e9fe-cc4a-11e6-9350-7ec4f680da36.gif" />
183
178
  `
184
179
 
185
- test('parser', () => {
186
- const parsedValue = parseBlocks(md)
180
+ test('verify parser', () => {
181
+ const parsedValue = parseBlocks(md, {
182
+ open: 'XYZ:START',
183
+ close: 'XYZ:END'
184
+ })
187
185
  /*
188
186
  console.log('parsedValue')
189
187
  deepLog(parsedValue)
190
188
  /** */
191
- assert.equal(true, true)
189
+ assert.equal(typeof parsedValue, 'object')
190
+ assert.equal(parsedValue.blocks.length, 11)
192
191
  })
193
192
 
194
- const inlineOne = `<!--XYZ:START functionName foo={{ rad: 'bar' }}-->99<!--XYZ:END-->`
195
- const inlineTwo = ` <!-- XYZ:START transformX foo=111 -->99<!-- XYZ:END -->`
196
193
  test('inline parser', () => {
197
- const one = parseBlocks(inlineOne)
194
+ const inlineOne = `<!--XYZ:START functionName foo={{ rad: 'bar' }}-->99<!--XYZ:END-->`
195
+ const one = parseBlocks(inlineOne, {
196
+ open: 'XYZ:START',
197
+ close: 'XYZ:END'
198
+ })
198
199
  /*
199
200
  console.log('inline one')
200
201
  deepLog(one)
201
202
  /** */
202
- // assert.equal(one, [
203
- // {
204
- // transform: 'functionName',
205
- // args: { foo: { rad: 'bar' } },
206
- // location: 66
207
- // }
208
- // ])
209
- const two = parseBlocks(inlineTwo)
210
- //*
203
+ const values = [
204
+ {
205
+ type: 'functionName',
206
+ options: { foo: { rad: 'bar' } },
207
+ location: 66
208
+ }
209
+ ]
210
+ values.forEach((val, i) => {
211
+ const stub = val
212
+ const currentItem = one.blocks[i]
213
+ assert.equal(stub.type, currentItem.type, `${stub.type} ${i} transform`)
214
+ assert.equal(stub.options, currentItem.options, `${stub.type} ${i} options`)
215
+ })
216
+
217
+ const inlineTwo = ` <!-- XYZ:START transformX foo=111 -->99<!-- XYZ:END -->`
218
+ const two = parseBlocks(inlineTwo, {
219
+ open: 'XYZ:START',
220
+ close: 'XYZ:END'
221
+ })
222
+ /*
211
223
  console.log('inline two ───────────────────────')
212
224
  deepLog(two)
213
225
  /** */
214
- assert.equal(two, [
215
- { transform: 'transformX', args: { foo: 111 }, location: 55 }
216
- ])
226
+ const valuesTwo = [
227
+ {
228
+ type: 'transformX',
229
+ options: { foo: 111 },
230
+ location: 55
231
+ }
232
+ ]
233
+ valuesTwo.forEach((val, i) => {
234
+ const stub = val
235
+ const currentItem = two.blocks[i]
236
+ assert.equal(stub.type, currentItem.type, `${stub.type} ${i} transform`)
237
+ assert.equal(stub.options, currentItem.options, `${stub.type} ${i} options`)
238
+ })
217
239
  })
218
240
 
219
241
  const fnBlocks = `
@@ -246,53 +268,67 @@ nice
246
268
  <!-- XYZ:END -->
247
269
  `
248
270
 
249
- test('function names', () => {
250
- const parsedValue = parseBlocks(fnBlocks)
271
+ test('Handles function names', () => {
272
+ const parsedValue = parseBlocks(fnBlocks, {
273
+ open: 'XYZ:START',
274
+ close: 'XYZ:END'
275
+ })
251
276
  /*
252
277
  console.log('fn names')
253
278
  deepLog(parsedValue)
254
279
  /** */
255
- assert.equal(parsedValue, [
280
+ assert.equal(Array.isArray(parsedValue.blocks), true)
281
+ assert.equal(parsedValue.blocks.length, 7)
282
+
283
+ const values = [
256
284
  {
257
- transform: 'functionName',
258
- args: { foo: { rad: 'yellow' } },
285
+ type: 'functionName',
286
+ options: { foo: { rad: 'yellow' } },
259
287
  location: 78
260
288
  },
261
289
  {
262
- transform: 'functionName',
263
- args: { foo: { rad: 'blue' } },
290
+ type: 'functionName',
291
+ options: { foo: { rad: 'blue' } },
264
292
  location: 157
265
293
  },
266
294
  {
267
- transform: 'functionName',
268
- args: { foo: { rad: 'red' } },
295
+ type: 'functionName',
296
+ options: { foo: { rad: 'red' } },
269
297
  location: 235
270
298
  },
271
299
  {
272
- transform: 'functionName',
273
- args: { foo: { rad: 'purple' } },
300
+ type: 'functionName',
301
+ options: { foo: { rad: 'purple' } },
274
302
  location: 316
275
303
  },
276
304
  {
277
- transform: 'functionName',
278
- args: { foo: { rad: 'black' } },
305
+ type: 'functionName',
306
+ options: { foo: { rad: 'black' } },
279
307
  location: 398
280
308
  },
281
309
  {
282
- transform: 'functionName',
283
- args: { foo: { rad: 'white' } },
310
+ type: 'functionName',
311
+ options: { foo: { rad: 'white' } },
284
312
  location: 480
285
313
  },
286
314
  {
287
- transform: 'functionName',
288
- args: { foo: { rad: 'orange' } },
315
+ type: 'functionName',
316
+ options: { foo: { rad: 'orange' } },
289
317
  location: 563
290
318
  }
291
- ])
319
+ ]
320
+
321
+ values.forEach((val, i) => {
322
+ const stub = val
323
+ const currentItem = parsedValue.blocks[i]
324
+ assert.equal(stub.type, currentItem.type, `${stub.type} ${i} transform`)
325
+ assert.equal(stub.options, currentItem.options, `${stub.type} ${i} options`)
326
+ })
292
327
  })
293
328
 
294
329
 
295
- const backwardCompat = `
330
+ test('different function names', () => {
331
+ const backwardCompat = `
296
332
  <!-- XYZ:START functionName foo={{ rad: 'yellow' }} -->
297
333
  nice
298
334
  <!-- XYZ:END -->
@@ -305,20 +341,102 @@ nice
305
341
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer vitae mauris arcu, eu pretium nisi. Praesent fringilla ornare ullamcorper. Pellentesque diam orci, sodales in blandit ut, placerat quis felis. Vestibulum at sem massa, in tempus nisi. Vivamus ut fermentum odio. Etiam porttitor faucibus volutpat. Vivamus vitae mi ligula, non hendrerit urna. Suspendisse potenti. Quisque eget massa a massa semper mollis.
306
342
  <!-- XYZ:END -->
307
343
 
308
-
309
344
  <!-- XYZ:START (CODE:src=./relative/path/to/code.js&lines=22-44) -->
310
345
  This content will be dynamically replaced with code from the file lines 22 through 44
311
346
  <!-- XYZ:END -->
312
347
  `
313
-
314
- test.only('function names', () => {
315
- const parsedValue = parseBlocks(backwardCompat)
316
- //*
348
+ const parsedValue = parseBlocks(backwardCompat, {
349
+ open: 'XYZ:START',
350
+ close: 'XYZ:END'
351
+ })
352
+ /*
317
353
  console.log('backwardCompat')
318
354
  deepLog(parsedValue)
319
355
  /** */
320
- // assert.equal(parsedValue, [])
356
+ const answers = [
357
+ {
358
+ type: 'functionName',
359
+ options: { foo: { rad: 'yellow' } },
360
+ },
361
+ {
362
+ type: 'lol',
363
+ options: {
364
+ width: 999,
365
+ height: 111,
366
+ numberAsString: '12345',
367
+ great: [ 'scoot', 'sco ot', 'scooo ttt' ],
368
+ nope: false
369
+ },
370
+ },
371
+ {
372
+ type: 'CODE',
373
+ options: { src: './relative/path/to/code.js', lines: '22-44' },
374
+ },
375
+ ]
376
+ parsedValue.blocks.forEach((transform, i) => {
377
+ const stub = answers[i]
378
+ assert.equal(transform.type, stub.type, `type: ${stub.type} at index ${i}`)
379
+ assert.equal(transform.options, stub.options, `options: ${stub.type} at index ${i}`)
380
+ })
381
+ })
382
+
383
+ const defaultOpts = {
384
+ syntax: 'md',
385
+ open: 'DOCS:START',
386
+ close: 'DOCS:END',
387
+ }
388
+
389
+ const mdText = `
390
+ Very nice
391
+
392
+ <!-- DOCS:START(TOC) foo={{ rad: 'orange' }} ------>
393
+ ok
394
+ <!-- DOCS:END -->`
395
+
396
+ test('Parse md blocks', () => {
397
+ const parsedValue = parseBlocks(mdText, defaultOpts)
398
+ deepLog(parsedValue)
399
+ assert.equal(parsedValue.blocks, [
400
+ {
401
+ index: 1,
402
+ type: 'TOC',
403
+ options: { foo: { rad: 'orange' } },
404
+ context: { isMultiline: true },
405
+ open: {
406
+ value: "<!-- DOCS:START(TOC) foo={{ rad: 'orange' }} ------>\n",
407
+ start: 12,
408
+ end: 64
409
+ },
410
+ content: { value: 'ok', start: 64, end: 68, indentation: 0 },
411
+ close: { value: '\n<!-- DOCS:END -->', start: 68, end: 85 },
412
+ block: {
413
+ indentation: '',
414
+ lines: [ 4, 6 ],
415
+ start: 12,
416
+ end: 85,
417
+ rawType: '(TOC)',
418
+ rawArgs: "foo={{ rad: 'orange' }}",
419
+ rawContent: '\nok\n',
420
+ value: "<!-- DOCS:START(TOC) foo={{ rad: 'orange' }} ------>\n" +
421
+ 'ok\n' +
422
+ '<!-- DOCS:END -->'
423
+ }
424
+ }
425
+ ], 'Array contains details')
321
426
  })
322
427
 
428
+ test('Returns empty array', () => {
429
+ assert.equal(parseBlocks('', defaultOpts).blocks, [])
430
+ assert.equal(parseBlocks(' ', defaultOpts).blocks, [])
431
+ assert.equal(parseBlocks(`
432
+
433
+
434
+ `, defaultOpts).blocks, [])
435
+ assert.equal(parseBlocks(`
436
+ # No block in here
437
+
438
+ nope
439
+ `, defaultOpts).blocks, [])
440
+ })
323
441
 
324
442
  test.run()