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
package/lib/index.js CHANGED
@@ -1,219 +1,388 @@
1
1
  const path = require('path')
2
- const fs = require('fs').promises
3
- const { getTextBetween, parseBlocks } = require('./utils/new-parser')
2
+ const { glob, globWithGit } = require('smart-glob')
3
+ const codeTransform = require('./transforms/code')
4
+ const fileTransform = require('./transforms/file')
5
+ const tocTransform = require('./transforms/toc')
6
+ const sectionTocTransform = require('./transforms/sectionToc')
7
+ const wordCountTransform = require('./transforms/wordCount')
8
+ const remoteTransform = require('./transforms/remote')
9
+ const { getSyntaxInfo } = require('./utils/syntax')
10
+ const { onlyUnique, getCodeLocation, pluralize } = require('./utils')
11
+ const { writeFile, resolveOutputPath, resolveFlatPath } = require('./utils/fs')
12
+ const { processFile } = require('./process-file')
13
+ const { processContents } = require('./process-contents')
14
+ const { parseMarkdown } = require('./utils/md/parse')
15
+ const { deepLog, success, error, info } = require('./utils/logs')
16
+ const { OPEN_WORD, CLOSE_WORD, DEFAULT_GLOB_PATTERN } = require('./defaults')
17
+ // const { getBlockRegex } = require('./block-parser')
18
+ // const diff = require('../misc/old-test/utils/diff')
4
19
 
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')
20
+ const LINE = '────────────────────────────────'
21
+
22
+ const defaultTransforms = {
23
+ CODE: codeTransform,
24
+ FILE: fileTransform,
25
+ TOC: tocTransform,
26
+ sectionToc: sectionTocTransform,
27
+ wordCount: wordCountTransform,
28
+ remote: remoteTransform
29
+ }
30
+
31
+ /**
32
+ * Allowed file syntaxes
33
+ * @typedef {'md' | 'js' | 'yml' | 'yaml'} SyntaxType
34
+ */
35
+
36
+ /**
37
+ * Configuration for markdown magic
38
+ *
39
+ * Below is the main config for markdown magic
40
+ *
41
+ * @typedef {object} MarkdownMagicOptions
42
+ * @property {Array} [transforms = defaultTransforms] - Custom commands to transform block contents, see transforms & custom transforms sections below.
43
+ * @property {string} [outputDir] - Change output path of new content. Default behavior is replacing the original file
44
+ * @property {SyntaxType} [syntax = 'md'] - Syntax to parse
45
+ * @property {boolean} [outputFlatten] - Flatten files that are output
46
+ * @property {function} [handleOutputPath] - Custom function for altering output paths
47
+ * @property {boolean} [useGitGlob] - Use git glob for LARGE file directories
48
+ * @property {boolean} [dryRun = false] - See planned execution of matched blocks
49
+ * @property {boolean} [debug = false] - See debug details
50
+ * @property {boolean} [failOnMissingTransforms = false] - Fail if transform functions are missing. Default skip blocks.
51
+ */
52
+
53
+ /**
54
+ * Markdown Magic
55
+ * @param {string|MarkdownMagicOptions} globOrOpts - Files to process or config. Uses [globby patterns](https://github.com/sindresorhus/multimatch/blob/master/test.js)
56
+ * @param {MarkdownMagicOptions} options - Markdown magic config
57
+ * @returns
58
+ * @example
59
+ markdownMagic(filePath, config)
60
+ */
61
+ async function markdownMagic(globOrOpts, options = {}) {
62
+ let opts = options || {}
63
+ let globPat
64
+ if (typeof globOrOpts === 'string' || Array.isArray(globOrOpts)) {
65
+ opts = options
66
+ globPat = globOrOpts
67
+ } else if (typeof globOrOpts === 'object') {
68
+ opts = globOrOpts
69
+ }
70
+ const {
71
+ transforms,
72
+ // open,
73
+ outputDir,
74
+ outputFlatten = false,
75
+ handleOutputPath,
76
+ useGitGlob = false,
77
+ failOnMissingTransforms = false,
78
+ dryRun = false,
79
+ debug = true,
80
+ syntax = 'md',
81
+ } = opts
82
+
83
+ let open = OPEN_WORD
84
+ if (opts.open) {
85
+ open = opts.open
86
+ } else if (opts.matchWord) {
87
+ open = `${opts.matchWord}:start`
88
+ }
89
+
90
+ let close = CLOSE_WORD
91
+ if (opts.close) {
92
+ close = opts.close
93
+ } else if (opts.matchWord) {
94
+ close = `${opts.matchWord}:end`
95
+ }
96
+
97
+ // console.log('outputDir', outputDir)
98
+ // console.log('outputFlatten', outputFlatten)
99
+ // return
17
100
 
18
- const foundBlocks = parseBlocks(originalContents, {
19
- syntax: syntaxType,
101
+ const cwd = opts.cwd || process.cwd()
102
+ // console.log('cwd', cwd)
103
+ const globPattern = globPat || globOrOpts.glob || globOrOpts.file || globOrOpts.files
104
+ // console.log('globPattern', globPattern)
105
+
106
+ const useTransforms = Object.assign({}, defaultTransforms, transforms)
107
+ let globs = []
108
+ if (!globPattern) {
109
+ globs = [ DEFAULT_GLOB_PATTERN ]
110
+ } else if (Array.isArray(globPattern)) {
111
+ globs = globPattern
112
+ } else if (typeof globPattern === 'string') {
113
+ globs = [ globPattern ]
114
+ }
115
+ opts.glob = globs
116
+ // console.log('globs', globs)
117
+ // return
118
+
119
+ info(`Running Markdown Magic:`)
120
+ logCommentBlockSyntax({
121
+ syntax: syntax,
20
122
  open,
21
- close,
123
+ close
22
124
  })
23
125
 
24
- if (DEBUG) {
25
- console.log('foundBlocks')
26
- console.log(foundBlocks)
126
+ if (dryRun || debug) {
127
+ info(`Glob patterns:`)
128
+ console.log(globs)
129
+ console.log()
130
+ /*
131
+ process.exit(1)
132
+ /** */
27
133
  }
28
134
 
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
- }
135
+ const globFn = (useGitGlob) ? globWithGit : glob
49
136
 
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
- }
137
+ const pathsPromise = globs.map((str) => {
138
+ // console.log('str', str)
139
+ // Glob pattern examples https://github.com/terkelg/globrex/blob/master/test/index.js
140
+ // return globWithGit(str, {
141
+ // const g = str.replace(/^(?:\.\.\/)+/, "")
142
+ // console.log('g', g)
143
+ return globFn(str, {
144
+ absolute: true,
145
+ alwaysReturnUnixPaths: true,
146
+ ignoreGlobs: ['**/node_modules/**'],
147
+ })
148
+ })
68
149
 
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)
150
+ let files = []
151
+ try {
152
+ files = (await Promise.all(pathsPromise)).flat().filter(onlyUnique)
153
+ opts.files = files
154
+ } catch (e) {
155
+ console.log(e.message)
156
+ throw new Error(e)
88
157
  }
158
+ // console.log('files', files)
159
+ // return
160
+ // console.log('globs', globs)
161
+ // const filex = await globby(globs)
162
+ // console.log('filex', filex)
89
163
 
90
164
  /*
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
- }
165
+ const relativeFilePaths = files.map((file) => file.replace(cwd, '').replace(/^\//, ''))
166
+ console.log('relativeFilePaths', relativeFilePaths)
167
+ process.exit(1)
104
168
  /** */
105
169
 
106
- return {
107
- filePath,
108
- transforms: transformsToRun,
109
- missingTransforms,
110
- originalContents,
111
- updatedContents
170
+ if (dryRun || debug) {
171
+ info(`${files.length} Files found:`)
172
+ console.log(files)
173
+ /*
174
+ process.exit(1)
175
+ /** */
112
176
  }
113
- }
114
177
 
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
178
+ const processedFiles = files.map((file) => {
179
+ // console.log('file', file)
180
+ let newPath = path.resolve(cwd, file)
181
+ /* Allow for different output directory */
182
+ if (outputDir) {
183
+ newPath = (outputFlatten) ? resolveFlatPath(cwd, outputDir, file) : resolveOutputPath(cwd, outputDir, file)
184
+ }
185
+ /* Allow for custom handling of individual files */
186
+ if (handleOutputPath) {
187
+ newPath = handleOutputPath(newPath)
188
+ }
189
+ // const cleanerDirPath = path.dirname(file)
190
+ // const baseDir = cleanerDirPath.replace(cwd, '')
191
+ // const cleanerDir = baseDir
192
+ // const resolvedDir = path.join(cleanerDir, outputDir)
193
+ // const cleanFinalPath = path.resolve(resolvedDir, path.basename(file))
194
+ /*
195
+ console.log('cleanerDir', cleanerDir)
196
+ console.log('resolvedDir', resolvedDir)
197
+ console.log('cleanFinalPath', cleanFinalPath)
198
+ /** */
199
+ // console.log('newPath', newPath)
200
+ return processFile({
201
+ ...opts,
202
+ open,
203
+ close,
204
+ srcPath: file,
205
+ outputPath: newPath,
206
+ transforms: useTransforms
207
+ })
208
+ })
209
+ // process.exit(1)
210
+ const plan = (await Promise.all(processedFiles)).filter((file) => {
211
+ /* Filter out files without transforms */
212
+ return file.transforms.length
213
+ })
214
+
215
+ if (dryRun || debug) {
216
+ /* Generate output paths */
217
+ const outputPaths = plan.map((item) => item.outputPath)
218
+ info(`Output files:`)
219
+ console.log(outputPaths)
220
+ /*
221
+ process.exit(1)
222
+ /** */
223
+ }
224
+ /*
225
+ console.log('plan')
226
+ deepLog(plan)
227
+ process.exit(1)
228
+ /** */
229
+
230
+ const missing = plan.filter((item) => {
231
+ return item.missingTransforms.length
232
+ })
233
+
234
+ let errors = []
235
+ if (missing.length) {
236
+ errors = missing.map((item, i) => {
237
+ const errorMessage = `Missing ${item.missingTransforms.length} transforms in ${item.srcPath}`
238
+ const issues = item.missingTransforms.map((trn) => {
239
+ // console.log('trn', trn)
240
+ // const rowData = getRowAndColumnFromCharPos(item.updatedContents, trn.open.start)
241
+ const location = `${item.srcPath}:${trn.block.lines[0]}:0`
242
+ const message = `Transform "${trn.transform}" at line ${trn.block.lines[0]} does not exist. → ${location}`
243
+ return {
244
+ message,
245
+ location
127
246
  }
247
+ })
248
+ return {
249
+ errorMessage,
250
+ errors: issues
128
251
  }
129
252
  })
130
- }, Promise.resolve(data))
131
- }
253
+ }
132
254
 
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
- }
255
+ /* If transform errors and should throw, exit early */
256
+ if (errors.length && failOnMissingTransforms) {
257
+ throw new Error(logErrors(errors))
258
+ }
259
+
260
+ /* Log out execution plan */
261
+ let planTotal = 0
262
+ let missingTotal = 0
263
+ const planOutput = plan.map((item, i) => {
264
+ const transformsToRun = item.transforms.filter((item) => {
265
+ if (item.context.isMissing) {
266
+ // console.log('item', item)
267
+ missingTotal = missingTotal + 1
268
+ }
269
+ return !item.context.isMissing
270
+ })
271
+ if (!transformsToRun.length) {
272
+ return
273
+ }
274
+ const count = `${i + 1}.`
275
+ let planMsg = `${count} Found ${transformsToRun.length} transforms in ${item.srcPath}`
276
+ planTotal = planTotal + transformsToRun.length
277
+ // console.log(`Found ${transformsToRun.length} transforms in ${item.srcPath}`)
278
+ transformsToRun.forEach((trn) => {
279
+ const line = trn.block.lines[0]
280
+ const location = getCodeLocation(item.srcPath, line)
281
+ const planData = ` - "${trn.transform}" at line ${line} → ${location}`
282
+ planMsg += `\n${planData}`
283
+ // console.log(` - "${trn.transform}" at line ${trn.block.lines[0]}`)
284
+ })
285
+ const newLine = plan.length !== i + 1 ? '\n' : ''
286
+ return `${planMsg}${newLine}`
287
+ }).filter(Boolean)
141
288
 
142
- function stripAllComments(block) {
143
- const pattern = new RegExp(`([^\\s]*)?([ \\t]*)?(<!-+\\s?([\\s\\S]*?)?-+>)([^\\s<]*)?(\n{1,2})?`, 'gi')
289
+ if (files.length) {
290
+ console.log(LINE)
291
+ info(`Markdown Magic updates`, '')
292
+ const total = planTotal + missingTotal
293
+ console.log(`Parsed files: ${files.length}`)
294
+ console.log(`Block Open: ${open}`)
295
+ console.log(`Block Close: ${close}`)
296
+ console.log(`Files w/ transforms: ${plan.length} / ${files.length}`)
297
+ console.log(`Total transforms: ${total}`)
298
+ console.log(`Valid transforms: ${planTotal} / ${total}`)
299
+ console.log(`Invalid transforms: ${missingTotal} / ${total}`)
300
+ console.log('Syntax:')
301
+ logCommentBlockSyntax({
302
+ syntax: syntax,
303
+ open,
304
+ close
305
+ })
306
+ console.log(LINE)
307
+ console.log(planOutput.join('\n'))
308
+ }
144
309
 
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
310
+ /* Execute updates based on plan */
311
+ if (!dryRun) {
312
+ const changed = plan.filter(({ isChanged }) => isChanged)
313
+ if (changed.length) {
314
+ console.log(LINE)
315
+ console.log()
316
+ const execute = changed.map(({ srcPath, outputPath, updatedContents, originalContents }, i) => {
317
+ // console.log(`${i + 1}. newPath`, newPath)
318
+ console.log(`- Update file ${outputPath}`)
319
+ return writeFile(outputPath, updatedContents)
320
+ })
321
+
322
+ await Promise.all(execute)
323
+ } else {
324
+ console.log(LINE)
325
+ console.log('No changes. Skipping file writes')
151
326
  }
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
327
  }
167
- return remove.reduce((acc, curr) => {
168
- return acc.replaceAll(curr, '')
169
- }, block)
170
- }
328
+ /*
329
+ TODO:
330
+ - Output to new file
331
+ - Clean up default transforms
332
+ - Expose md utils
333
+ */
334
+ if (errors.length) {
335
+ logErrors(errors)
336
+ }
171
337
 
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
338
+ console.log(LINE)
339
+ success(`Markdown Magic Done`)
340
+ console.log(`${LINE}\n`)
341
+
342
+ return {
343
+ errors,
344
+ changes: plan,
345
+ data: plan
189
346
  }
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
347
  }
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
348
+
349
+ function logErrors(allErrors) {
350
+ const word = pluralize(allErrors, 'error', 'errors')
351
+ const title = `Markdown Magic ${word}: ${allErrors.length}`
352
+ console.log('\n──────────────────────────────')
353
+ error(title)
354
+ console.log('──────────────────────────────\n')
355
+ let msg = title
356
+ allErrors.forEach(({ errorMessage, errors }, i) => {
357
+ const finalMessage = `${i + 1}. ${errorMessage}`
358
+ msg += `\n${finalMessage}`
359
+ error(finalMessage, ``)
360
+ errors.forEach(({ message }, n) => {
361
+ const newLineX = errors.length !== n + 1 ? '' : '\n'
362
+ const lineMessage = ` - ${message}`
363
+ msg += `\n${lineMessage}`
364
+ console.log(`${lineMessage}${newLineX}`)
365
+ })
204
366
  })
367
+ return msg
205
368
  }
206
369
 
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))
370
+ function logCommentBlockSyntax({
371
+ syntax,
372
+ open,
373
+ close
374
+ }) {
375
+ const syntaxDetails = getSyntaxInfo(syntax)
376
+ console.log(`
377
+ ${syntaxDetails.tags[0]} ${open} transformName ${syntaxDetails.tags[1]}
378
+ generated content here
379
+ ${syntaxDetails.tags[0]} ${close} ${syntaxDetails.tags[1]}
380
+ `)
215
381
  }
216
382
 
217
383
  module.exports = {
218
- processContents
384
+ markdownMagic,
385
+ parseMarkdown,
386
+ processContents,
387
+ processFile
219
388
  }
@@ -0,0 +1,11 @@
1
+ const { test } = require('uvu')
2
+ const assert = require('uvu/assert')
3
+ const { markdownMagic, processContents, processFile } = require('./')
4
+
5
+ test('Main API', () => {
6
+ assert.equal(typeof markdownMagic, 'function')
7
+ assert.equal(typeof processContents, 'function')
8
+ assert.equal(typeof processFile, 'function')
9
+ })
10
+
11
+ test.run()