markdown-magic 3.0.3 → 3.0.5

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 (39) hide show
  1. package/README.md +295 -101
  2. package/cli.js +4 -1
  3. package/lib/block-parser.js +32 -28
  4. package/lib/block-parser.test.js +2 -0
  5. package/lib/cli.js +101 -19
  6. package/lib/cli.test.js +12 -12
  7. package/lib/index.js +418 -119
  8. package/lib/process-contents.js +61 -23
  9. package/lib/process-file.js +40 -4
  10. package/lib/transforms/code.js +33 -10
  11. package/lib/transforms/file.js +4 -2
  12. package/lib/transforms/index.js +114 -0
  13. package/lib/transforms/sectionToc.js +2 -2
  14. package/lib/transforms/toc.js +22 -4
  15. package/lib/transforms/wordCount.js +2 -2
  16. package/lib/utils/fs.js +8 -172
  17. package/lib/utils/fs.test.js +4 -162
  18. package/lib/utils/hash-file.js +28 -0
  19. package/lib/utils/logs.js +16 -2
  20. package/lib/utils/syntax.js +1 -0
  21. package/lib/utils/text.js +1 -1
  22. package/lib/utils/toposort.js +131 -0
  23. package/package.json +4 -3
  24. package/lib/utils/md/filters.js +0 -20
  25. package/lib/utils/md/find-code-blocks.js +0 -88
  26. package/lib/utils/md/find-date.js +0 -32
  27. package/lib/utils/md/find-frontmatter.js +0 -92
  28. package/lib/utils/md/find-frontmatter.test.js +0 -17
  29. package/lib/utils/md/find-html-tags.js +0 -105
  30. package/lib/utils/md/find-images-md.js +0 -27
  31. package/lib/utils/md/find-images.js +0 -107
  32. package/lib/utils/md/find-links.js +0 -220
  33. package/lib/utils/md/find-unmatched-html-tags.js +0 -32
  34. package/lib/utils/md/fixtures/2022-01-22-date-in-filename.md +0 -14
  35. package/lib/utils/md/fixtures/file-with-frontmatter.md +0 -32
  36. package/lib/utils/md/fixtures/file-with-links.md +0 -153
  37. package/lib/utils/md/md.test.js +0 -105
  38. package/lib/utils/md/parse.js +0 -146
  39. package/lib/utils/md/utils.js +0 -19
package/lib/index.js CHANGED
@@ -8,16 +8,21 @@ const wordCountTransform = require('./transforms/wordCount')
8
8
  const remoteTransform = require('./transforms/remote')
9
9
  const { getSyntaxInfo } = require('./utils/syntax')
10
10
  const { onlyUnique, getCodeLocation, pluralize } = require('./utils')
11
- const { writeFile, resolveOutputPath, resolveFlatPath } = require('./utils/fs')
11
+ const { readFile, resolveOutputPath, resolveFlatPath } = require('./utils/fs')
12
12
  const { processFile } = require('./process-file')
13
13
  const { processContents } = require('./process-contents')
14
- const { parseMarkdown } = require('./utils/md/parse')
15
- const { deepLog, success, error, info } = require('./utils/logs')
14
+ const { parseMarkdown } = require('@davidwells/md-utils')
15
+ const { success, error, info, convertHrtime, deepLog } = require('./utils/logs')
16
16
  const { OPEN_WORD, CLOSE_WORD, DEFAULT_GLOB_PATTERN } = require('./defaults')
17
+ const { getBlockRegex, parseBlocks } = require('./block-parser')
18
+ const toposort = require('./utils/toposort');
19
+ // const { hashFile } = require('./utils/hash-file')
17
20
  // const { getBlockRegex } = require('./block-parser')
18
21
  // const diff = require('../misc/old-test/utils/diff')
19
22
 
20
- const LINE = '────────────────────────────────'
23
+ // old https://raw.githubusercontent.com/DavidWells/markdown-magic/add-package-scripts-plugin/index.js
24
+
25
+ const LINE = '──────────────────────────────────────────────────────────'
21
26
 
22
27
  const defaultTransforms = {
23
28
  CODE: codeTransform,
@@ -28,37 +33,74 @@ const defaultTransforms = {
28
33
  remote: remoteTransform
29
34
  }
30
35
 
31
- /**
36
+ /**!
32
37
  * Allowed file syntaxes
33
38
  * @typedef {'md' | 'js' | 'yml' | 'yaml'} SyntaxType
34
39
  */
35
40
 
41
+ /**!
42
+ * Path to file, files or Glob string or Glob Array
43
+ * @typedef {string|Array<string>} FilePathsOrGlobs
44
+ */
45
+
36
46
  /**
37
47
  * Configuration for markdown magic
38
48
  *
39
- * Below is the main config for markdown magic
49
+ * Below is the main config for `markdown-magic`
40
50
  *
41
51
  * @typedef {object} MarkdownMagicOptions
52
+ * @property {FilePathsOrGlobs} [files] - Files to process.
42
53
  * @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
54
+ * @property {OutputConfig} [output] - Output configuration
44
55
  * @property {SyntaxType} [syntax = 'md'] - Syntax to parse
56
+ * @property {string} [open = 'doc-gen'] - Opening match word
57
+ * @property {string} [close = 'end-doc-gen'] - Closing match word. If not defined will be same as opening word.
58
+ * @property {string} [cwd = process.cwd() ] - Current working directory. Default process.cwd()
45
59
  * @property {boolean} [outputFlatten] - Flatten files that are output
46
- * @property {function} [handleOutputPath] - Custom function for altering output paths
47
60
  * @property {boolean} [useGitGlob] - Use git glob for LARGE file directories
48
61
  * @property {boolean} [dryRun = false] - See planned execution of matched blocks
49
62
  * @property {boolean} [debug = false] - See debug details
63
+ * @property {boolean} [silent = false] - Silence all console output
50
64
  * @property {boolean} [failOnMissingTransforms = false] - Fail if transform functions are missing. Default skip blocks.
51
65
  */
52
66
 
53
67
  /**
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
68
+ * Optional output configuration
69
+ * @typedef {object} OutputConfig
70
+ * @property {string} [directory] - Change output path of new content. Default behavior is replacing the original file
71
+ * @property {boolean} [removeComments = false] - Remove comments from output. Default is false.
72
+ * @property {function} [pathFormatter] - Custom function for altering output paths
73
+ * @property {boolean} [applyTransformsToSource = false] - Apply transforms to source file. Default is true. This is for when outputDir is set.
74
+ */
75
+
76
+ /**
77
+ * Result of markdown processing
78
+ *
79
+ * @typedef {object} MarkdownMagicResult
80
+ * @property {Array} errors - Any errors encountered.
81
+ * @property {Array<string>} filesChanged - Modified files
82
+ * @property {Array} results - md data
83
+ */
84
+
85
+ /**
86
+ * ### API
87
+ *
88
+ * Markdown Magic Instance
89
+ *
90
+ * ```js
91
+ * markdownMagic(globOrOpts, options)
92
+ * ```
93
+ *
94
+ * @param {FilePathsOrGlobs|MarkdownMagicOptions} globOrOpts - Files to process or config.
95
+ * @param {MarkdownMagicOptions} [options] - Markdown magic config
96
+ * @returns {Promise<MarkdownMagicResult>}
58
97
  * @example
59
- markdownMagic(filePath, config)
98
+ markdownMagic(['**.**.md'], options).then((result) => {
99
+ console.log(`Processing complete`, result)
100
+ })
60
101
  */
61
- async function markdownMagic(globOrOpts, options = {}) {
102
+ async function markdownMagic(globOrOpts = {}, options = {}) {
103
+ const hrstart = process.hrtime.bigint()
62
104
  let opts = options || {}
63
105
  let globPat
64
106
  if (typeof globOrOpts === 'string' || Array.isArray(globOrOpts)) {
@@ -70,40 +112,59 @@ async function markdownMagic(globOrOpts, options = {}) {
70
112
  const {
71
113
  transforms,
72
114
  // open,
73
- outputDir,
115
+ /** lol @type {OutputConfig} */
116
+ output = {},
74
117
  outputFlatten = false,
75
- handleOutputPath,
76
118
  useGitGlob = false,
77
119
  failOnMissingTransforms = false,
78
120
  dryRun = false,
79
- debug = true,
121
+ debug = false,
80
122
  syntax = 'md',
123
+ silent = false,
81
124
  } = opts
82
125
 
126
+ // @ts-ignore
127
+ const outputDir = output.directory || opts.outputDir
128
+ const removeComments = output.removeComments || false
129
+ const pathFormatter = output.pathFormatter
130
+
131
+ let applyTransformsToSource = false
132
+ if (typeof output.applyTransformsToSource !== 'undefined') {
133
+ applyTransformsToSource = output.applyTransformsToSource
134
+ // @ts-ignore
135
+ } else if (typeof opts.applyTransformsToSource !== 'undefined') {
136
+ // @ts-ignore
137
+ applyTransformsToSource = opts.applyTransformsToSource
138
+ }
139
+
140
+ const logger = (silent) ? () => {} : console.log
141
+
83
142
  let open = OPEN_WORD
84
143
  if (opts.open) {
85
144
  open = opts.open
145
+ // @ts-ignore legacy
86
146
  } else if (opts.matchWord) {
147
+ // @ts-ignore legacy
87
148
  open = `${opts.matchWord}:start`
88
149
  }
89
150
 
90
151
  let close = CLOSE_WORD
91
152
  if (opts.close) {
92
153
  close = opts.close
154
+ // @ts-ignore legacy
93
155
  } else if (opts.matchWord) {
156
+ // @ts-ignore legacy
94
157
  close = `${opts.matchWord}:end`
95
158
  }
96
-
97
- // console.log('outputDir', outputDir)
98
- // console.log('outputFlatten', outputFlatten)
99
- // return
100
159
 
101
160
  const cwd = opts.cwd || process.cwd()
102
- // console.log('cwd', cwd)
103
- const globPattern = globPat || globOrOpts.glob || globOrOpts.file || globOrOpts.files
161
+
162
+ // @ts-ignore
163
+ const globPattern = globPat || globOrOpts.files || globOrOpts.file || globOrOpts.glob
104
164
  // console.log('globPattern', globPattern)
105
165
 
106
166
  const useTransforms = Object.assign({}, defaultTransforms, transforms)
167
+
107
168
  let globs = []
108
169
  if (!globPattern) {
109
170
  globs = [ DEFAULT_GLOB_PATTERN ]
@@ -112,21 +173,30 @@ async function markdownMagic(globOrOpts, options = {}) {
112
173
  } else if (typeof globPattern === 'string') {
113
174
  globs = [ globPattern ]
114
175
  }
115
- opts.glob = globs
116
- // console.log('globs', globs)
117
- // return
118
176
 
119
- info(`Running Markdown Magic:`)
120
- logCommentBlockSyntax({
121
- syntax: syntax,
122
- open,
123
- close
124
- })
177
+ /* Trim globs inputs */
178
+ globs = globs
179
+ .filter((pat) => Boolean(pat))
180
+ .map((pat) => pat.trim())
181
+
182
+ // Opts passed into plugins
183
+ // @ts-ignore
184
+ opts.globPattern = globs
185
+
186
+ logger(LINE)
187
+ success(` Markdown Magic Starting...`, silent, '✨ ')
188
+ logger(`${LINE}\n`)
189
+
190
+ info(`Searching for comment blocks...`, silent, '🔎 ')
191
+ logger(`\nSyntax: ${syntax}`)
192
+ logger(`Block Open: ${open}`)
193
+ logger(`Block Close: ${close}`)
194
+ logger(`Searching: `, globs)
125
195
 
126
196
  if (dryRun || debug) {
127
- info(`Glob patterns:`)
128
- console.log(globs)
129
- console.log()
197
+ info(`Glob patterns:`, silent)
198
+ logger(globs)
199
+ logger()
130
200
  /*
131
201
  process.exit(1)
132
202
  /** */
@@ -135,11 +205,11 @@ async function markdownMagic(globOrOpts, options = {}) {
135
205
  const globFn = (useGitGlob) ? globWithGit : glob
136
206
 
137
207
  const pathsPromise = globs.map((str) => {
138
- // console.log('str', str)
208
+ // logger('str', str)
139
209
  // Glob pattern examples https://github.com/terkelg/globrex/blob/master/test/index.js
140
210
  // return globWithGit(str, {
141
211
  // const g = str.replace(/^(?:\.\.\/)+/, "")
142
- // console.log('g', g)
212
+ // logger('g', g)
143
213
  return globFn(str, {
144
214
  absolute: true,
145
215
  alwaysReturnUnixPaths: true,
@@ -150,41 +220,99 @@ async function markdownMagic(globOrOpts, options = {}) {
150
220
  let files = []
151
221
  try {
152
222
  files = (await Promise.all(pathsPromise)).flat().filter(onlyUnique)
153
- opts.files = files
223
+ // opts.files = files
154
224
  } catch (e) {
155
- console.log(e.message)
225
+ // console.log(e.message)
156
226
  throw new Error(e)
157
227
  }
158
- // console.log('files', files)
228
+ // logger('files', files)
159
229
  // return
160
- // console.log('globs', globs)
230
+ // logger('globs', globs)
161
231
  // const filex = await globby(globs)
162
- // console.log('filex', filex)
232
+ // logger('filex', filex)
163
233
 
164
234
  /*
165
235
  const relativeFilePaths = files.map((file) => file.replace(cwd, '').replace(/^\//, ''))
166
- console.log('relativeFilePaths', relativeFilePaths)
236
+ logger('relativeFilePaths', relativeFilePaths)
167
237
  process.exit(1)
168
238
  /** */
169
239
 
170
- if (dryRun || debug) {
171
- info(`${files.length} Files found:`)
172
- console.log(files)
173
- /*
174
- process.exit(1)
175
- /** */
176
- }
240
+
241
+
242
+ const patterns = getBlockRegex({
243
+ syntax,
244
+ openText: open,
245
+ closeText: close
246
+ })
247
+
248
+ const blocksByPath = files.map(async (file) => {
249
+ const text = await readFile(file, 'utf8')
250
+ let foundBlocks = {}
251
+ try {
252
+ foundBlocks = parseBlocks(text, {
253
+ syntax,
254
+ open,
255
+ close,
256
+ })
257
+ } catch (e) {
258
+ throw new Error(`${e.message}\nFix content in ${file}\n`)
259
+ }
260
+ return {
261
+ name: file,
262
+ id: file,
263
+ srcPath: file,
264
+ blocks: foundBlocks.blocks
265
+ }
266
+ })
267
+
268
+ const blocks = (await Promise.all(blocksByPath)).map((item) => {
269
+ const dir = path.dirname(item.srcPath)
270
+ item.dependencies = []
271
+ item.blocks.forEach((block) => {
272
+ if (block.options && block.options.src) {
273
+ const resolvedPath = path.resolve(dir, block.options.src)
274
+ // if (resolvedPath.match(/\.md$/)) {
275
+ // console.log('resolvedPath', resolvedPath)
276
+ item.dependencies = item.dependencies.concat(resolvedPath)
277
+ //}
278
+ }
279
+ })
280
+ return item
281
+ })
282
+ /*
283
+ console.log('blocks')
284
+ deepLog(blocks)
285
+ /** */
286
+
287
+ // Convert items into a format suitable for toposort
288
+ const graph = blocks
289
+ .filter((item) => item.blocks && item.blocks.length)
290
+ .map((item) => {
291
+ return [ item.id, ...item.dependencies ]
292
+ })
293
+ // console.log('graph', graph)
294
+ // Perform the topological sort and reverse for execution order
295
+ const sortedIds = toposort(graph).reverse();
296
+
297
+ // Reorder items based on sorted ids
298
+ const sortedItems = sortedIds.map(id => blocks.find(item => item.id === id)).filter(Boolean);
177
299
 
178
- const processedFiles = files.map((file) => {
179
- // console.log('file', file)
300
+ // topoSort(blocks)
301
+ const orderedFiles = sortedItems.map((item) => item.id)
302
+ // console.log('sortedItems', sortedItems)
303
+ // console.log('orderedFiles', orderedFiles)
304
+
305
+ const processedFiles = []
306
+ await asyncForEach(orderedFiles, async (file) => {
307
+ // logger('file', file)
180
308
  let newPath = path.resolve(cwd, file)
181
309
  /* Allow for different output directory */
182
310
  if (outputDir) {
183
311
  newPath = (outputFlatten) ? resolveFlatPath(cwd, outputDir, file) : resolveOutputPath(cwd, outputDir, file)
184
312
  }
185
313
  /* Allow for custom handling of individual files */
186
- if (handleOutputPath) {
187
- newPath = handleOutputPath(newPath)
314
+ if (pathFormatter) {
315
+ newPath = pathFormatter({ filePath: newPath })
188
316
  }
189
317
  // const cleanerDirPath = path.dirname(file)
190
318
  // const baseDir = cleanerDirPath.replace(cwd, '')
@@ -192,37 +320,101 @@ async function markdownMagic(globOrOpts, options = {}) {
192
320
  // const resolvedDir = path.join(cleanerDir, outputDir)
193
321
  // const cleanFinalPath = path.resolve(resolvedDir, path.basename(file))
194
322
  /*
195
- console.log('cleanerDir', cleanerDir)
196
- console.log('resolvedDir', resolvedDir)
197
- console.log('cleanFinalPath', cleanFinalPath)
323
+ logger('cleanerDir', cleanerDir)
324
+ logger('resolvedDir', resolvedDir)
325
+ logger('cleanFinalPath', cleanFinalPath)
198
326
  /** */
199
- // console.log('newPath', newPath)
200
- return processFile({
327
+ // logger('newPath', newPath)
328
+ const result = await processFile({
201
329
  ...opts,
330
+ patterns,
202
331
  open,
203
332
  close,
204
333
  srcPath: file,
205
334
  outputPath: newPath,
206
- transforms: useTransforms
335
+ transforms: useTransforms,
336
+ removeComments,
337
+ processedFiles
207
338
  })
339
+
340
+ processedFiles.push(result)
208
341
  })
342
+
343
+ /* @TODO
344
+
345
+ Blocks are already parsed, pass them into processFile to avoid reparse
346
+ */
347
+ // const processedFiles = orderedFiles.map(async (file) => {
348
+ // // logger('file', file)
349
+ // let newPath = path.resolve(cwd, file)
350
+ // /* Allow for different output directory */
351
+ // if (outputDir) {
352
+ // newPath = (outputFlatten) ? resolveFlatPath(cwd, outputDir, file) : resolveOutputPath(cwd, outputDir, file)
353
+ // }
354
+ // /* Allow for custom handling of individual files */
355
+ // if (pathFormatter) {
356
+ // newPath = pathFormatter({ filePath: newPath })
357
+ // }
358
+ // // const cleanerDirPath = path.dirname(file)
359
+ // // const baseDir = cleanerDirPath.replace(cwd, '')
360
+ // // const cleanerDir = baseDir
361
+ // // const resolvedDir = path.join(cleanerDir, outputDir)
362
+ // // const cleanFinalPath = path.resolve(resolvedDir, path.basename(file))
363
+ // /*
364
+ // logger('cleanerDir', cleanerDir)
365
+ // logger('resolvedDir', resolvedDir)
366
+ // logger('cleanFinalPath', cleanFinalPath)
367
+ // /** */
368
+ // // logger('newPath', newPath)
369
+ // const result = await processFile({
370
+ // ...opts,
371
+ // open,
372
+ // close,
373
+ // srcPath: file,
374
+ // outputPath: newPath,
375
+ // transforms: useTransforms,
376
+ // removeComments,
377
+ // })
378
+
379
+ // return result
380
+ // })
381
+
382
+ /*
383
+ console.log('processedFiles', processedFiles)
209
384
  // process.exit(1)
385
+ /** */
386
+
210
387
  const plan = (await Promise.all(processedFiles)).filter((file) => {
211
388
  /* Filter out files without transforms */
212
389
  return file.transforms.length
213
390
  })
214
391
 
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
- /** */
392
+ /*
393
+ process.exit(1)
394
+ /** */
395
+
396
+ if (debug) {
397
+ logger()
398
+ logger(`Pattern open:`)
399
+ logger(patterns.openPattern)
400
+ logger(`Pattern close:`)
401
+ logger(patterns.closePattern)
223
402
  }
403
+
404
+ logger()
405
+ info(`Available Transforms:`, silent, '🔁 ')
406
+ logger(`\n${Object.keys(useTransforms).join(', ')}\n`)
407
+ info(`Syntax example:`, silent, '🧱 ')
408
+ logger()
409
+ logCommentBlockSyntax({
410
+ syntax: syntax,
411
+ open,
412
+ close,
413
+ logger
414
+ })
415
+
224
416
  /*
225
- console.log('plan')
417
+ logger('plan')
226
418
  deepLog(plan)
227
419
  process.exit(1)
228
420
  /** */
@@ -236,7 +428,7 @@ async function markdownMagic(globOrOpts, options = {}) {
236
428
  errors = missing.map((item, i) => {
237
429
  const errorMessage = `Missing ${item.missingTransforms.length} transforms in ${item.srcPath}`
238
430
  const issues = item.missingTransforms.map((trn) => {
239
- // console.log('trn', trn)
431
+ // logger('trn', trn)
240
432
  // const rowData = getRowAndColumnFromCharPos(item.updatedContents, trn.open.start)
241
433
  const location = `${item.srcPath}:${trn.block.lines[0]}:0`
242
434
  const message = `Transform "${trn.transform}" at line ${trn.block.lines[0]} does not exist. → ${location}`
@@ -263,7 +455,7 @@ async function markdownMagic(globOrOpts, options = {}) {
263
455
  const planOutput = plan.map((item, i) => {
264
456
  const transformsToRun = item.transforms.filter((item) => {
265
457
  if (item.context.isMissing) {
266
- // console.log('item', item)
458
+ // logger('item', item)
267
459
  missingTotal = missingTotal + 1
268
460
  }
269
461
  return !item.context.isMissing
@@ -271,60 +463,155 @@ async function markdownMagic(globOrOpts, options = {}) {
271
463
  if (!transformsToRun.length) {
272
464
  return
273
465
  }
274
- const count = `${i + 1}.`
466
+ const count = ` ${i + 1}.`
275
467
  let planMsg = `${count} Found ${transformsToRun.length} transforms in ${item.srcPath}`
276
468
  planTotal = planTotal + transformsToRun.length
277
- // console.log(`Found ${transformsToRun.length} transforms in ${item.srcPath}`)
469
+ // logger(`Found ${transformsToRun.length} transforms in ${item.srcPath}`)
278
470
  transformsToRun.forEach((trn) => {
279
471
  const line = trn.block.lines[0]
280
472
  const location = getCodeLocation(item.srcPath, line)
281
- const planData = ` - "${trn.transform}" at line ${line} → ${location}`
473
+ const planData = ` - "${trn.transform}" at line ${line} → ${location}`
282
474
  planMsg += `\n${planData}`
283
- // console.log(` - "${trn.transform}" at line ${trn.block.lines[0]}`)
475
+ // logger(` - "${trn.transform}" at line ${trn.block.lines[0]}`)
284
476
  })
285
477
  const newLine = plan.length !== i + 1 ? '\n' : ''
286
478
  return `${planMsg}${newLine}`
287
479
  }).filter(Boolean)
288
-
289
- if (files.length) {
290
- console.log(LINE)
291
- info(`Markdown Magic updates`, '')
480
+
481
+ logger()
482
+ if (!files.length) {
483
+ info(`Results:`, silent)
484
+ logger("No files found. Exiting early.", silent)
485
+ return {
486
+ errors,
487
+ filesChanged: [],
488
+ results: plan
489
+ }
490
+ } else {
491
+ info(`Parse results:`, silent, "🟢 ")
292
492
  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'))
493
+ logger()
494
+ logger(`Total transforms: ${total}`)
495
+ logger(`Files with transforms: ${plan.length} / ${files.length}`)
496
+ logger(`Valid transforms: ${planTotal} / ${total}`)
497
+ logger(`Invalid transforms: ${missingTotal} / ${total}`)
498
+
499
+ logger(`\nFiles scanned: ${files.length}`)
500
+ logger(files)
501
+
502
+ logger(`\nFiles with transforms: ${plan.length}`)
503
+ if (plan.length) {
504
+ logger(plan.map(({ srcPath }) => srcPath))
505
+ logger()
506
+ }
507
+ // logger('Syntax:')
508
+ // logCommentBlockSyntax({
509
+ // syntax: syntax,
510
+ // open,
511
+ // close
512
+ // })
513
+ // logger('\nStats:')
514
+
515
+ if (planOutput.length) {
516
+ info(`Execution plan:`, silent, "📑 ")
517
+ logger()
518
+ logger(planOutput.join('\n'))
519
+ } else {
520
+ info(`Execution plan:`, silent, "📑 ")
521
+ logger('\nNo transforms to run. Exiting early.')
522
+ logger('If you think this is incorrect. Verify your comment blocks in your src and the settings in your config.')
523
+ logger()
524
+ }
525
+ }
526
+
527
+ /* Generate output paths */
528
+ const outputPaths = plan.map((item) => item.outputPath)
529
+ const changed = (outputDir) ? plan : changedFiles(plan)
530
+ const label = (outputDir) ? 'Files to create or update' : 'Files to update'
531
+
532
+ if (outputPaths.length && changed.length) {
533
+ logger()
534
+ info(`${label}`, silent)
535
+ logger()
536
+ if (outputDir) {
537
+ logger('Output directory:')
538
+ logger(outputDir)
539
+ logger()
540
+ }
541
+ logger('Output files:')
542
+ logger(outputPaths)
543
+ logger()
308
544
  }
309
545
 
310
546
  /* Execute updates based on plan */
311
- if (!dryRun) {
312
- const changed = plan.filter(({ isChanged }) => isChanged)
547
+ const alwaysUpdateOutput = true
548
+
549
+ if (!dryRun && plan.length) {
550
+ logger()
551
+ info(`Apply changes:`, silent, "✏️ ")
552
+
553
+ /* Check for diffs in src vs output only */
554
+ // if (outputDir) {
555
+ // const compareFiles = plan.map(async (item) => {
556
+ // /* fast check */
557
+ // if (item.isChanged) {
558
+ // return item
559
+ // }
560
+ // /* Maybe make an option? */
561
+ // if (alwaysUpdateOutput) {
562
+ // return Object.assign({}, item, { isChanged: true })
563
+ // }
564
+
565
+ // /* else slower check hashes of destination */
566
+ // const outputHash = await hashFile(item.outputPath)
567
+ // const srcHash = await hashFile(item.srcPath)
568
+ // return {
569
+ // ...item,
570
+ // isChanged: outputHash !== srcHash
571
+ // }
572
+ // })
573
+ // /* Set new changed array */
574
+ // changed = (await Promise.all(compareFiles)).filter(({ isChanged }) => isChanged)
575
+ // }
576
+
313
577
  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)
578
+ logger()
579
+
580
+ changed.forEach(({
581
+ srcPath,
582
+ outputPath,
583
+ updatedContents,
584
+ stripComments,
585
+ originalContents,
586
+ }, i) => {
587
+ // logger(`${i + 1}. newPath`, newPath)
588
+ let cleanContents = updatedContents
589
+ if (stripComments && patterns.openPattern && patterns.closePattern) {
590
+ cleanContents = updatedContents.replace(patterns.openPattern, '').replace(patterns.closePattern, '')
591
+ }
592
+
593
+ let promises = []
594
+ if (outputDir) {
595
+ logger(`- Update output file: ${outputPath}`)
596
+ // promises = promises.concat(writeFile(outputPath, cleanContents))
597
+ }
598
+ if (!outputDir || applyTransformsToSource) {
599
+ logger(`- Update source file: ${srcPath}`)
600
+ // promises = promises.concat(writeFile(srcPath, updatedContents))
601
+ }
602
+ /* Apply file transforms to source templates */
603
+ // return Promise.all(promises)
320
604
  })
321
-
322
- await Promise.all(execute)
323
605
  } else {
324
- console.log(LINE)
325
- console.log('No changes. Skipping file writes')
606
+ logger('\nNo changes detected. Skipping file writes...')
326
607
  }
327
608
  }
609
+
610
+
611
+ /*
612
+ process.exit(1)
613
+ /** */
614
+
328
615
  /*
329
616
  TODO:
330
617
  - Output to new file
@@ -335,14 +622,17 @@ async function markdownMagic(globOrOpts, options = {}) {
335
622
  logErrors(errors)
336
623
  }
337
624
 
338
- console.log(LINE)
339
- success(`Markdown Magic Done`)
340
- console.log(`${LINE}\n`)
625
+ const elasped = convertHrtime(process.hrtime.bigint() - hrstart)
626
+
627
+ logger()
628
+ logger(`${LINE}`)
629
+ success(`Markdown Magic Done. ${elasped.seconds} seconds`, silent)
630
+ logger(`${LINE}`)
341
631
 
342
632
  return {
633
+ filesChanged: plan.filter(({ isChanged }) => isChanged).map(({ outputPath }) => outputPath),
634
+ results: plan,
343
635
  errors,
344
- changes: plan,
345
- data: plan
346
636
  }
347
637
  }
348
638
 
@@ -358,10 +648,10 @@ function logErrors(allErrors) {
358
648
  msg += `\n${finalMessage}`
359
649
  error(finalMessage, ``)
360
650
  errors.forEach(({ message }, n) => {
361
- const newLineX = errors.length !== n + 1 ? '' : '\n'
651
+ const hasNewLine = errors.length !== n - 1 ? '' : '\n'
362
652
  const lineMessage = ` - ${message}`
363
653
  msg += `\n${lineMessage}`
364
- console.log(`${lineMessage}${newLineX}`)
654
+ console.log(`${lineMessage}${hasNewLine}`)
365
655
  })
366
656
  })
367
657
  return msg
@@ -370,14 +660,23 @@ function logErrors(allErrors) {
370
660
  function logCommentBlockSyntax({
371
661
  syntax,
372
662
  open,
373
- close
663
+ close,
664
+ logger
374
665
  }) {
375
666
  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
- `)
667
+ logger(`${syntaxDetails.tags[0]} ${open} transformName option="xyz" ${syntaxDetails.tags[1]}
668
+ Contents to be replaced/updated
669
+ ${syntaxDetails.tags[0]} ${close} ${syntaxDetails.tags[1]}`)
670
+ }
671
+
672
+ function changedFiles(files) {
673
+ return files.filter(({ isChanged }) => isChanged)
674
+ }
675
+
676
+ async function asyncForEach(array, callback) {
677
+ for (let index = 0; index < array.length; index++) {
678
+ await callback(array[index], index, array)
679
+ }
381
680
  }
382
681
 
383
682
  module.exports = {