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