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.
- package/README.md +295 -101
- package/cli.js +4 -1
- package/lib/block-parser.js +32 -28
- package/lib/block-parser.test.js +2 -0
- package/lib/cli.js +101 -19
- package/lib/cli.test.js +12 -12
- package/lib/index.js +418 -119
- package/lib/process-contents.js +61 -23
- package/lib/process-file.js +40 -4
- package/lib/transforms/code.js +33 -10
- package/lib/transforms/file.js +4 -2
- package/lib/transforms/index.js +114 -0
- package/lib/transforms/sectionToc.js +2 -2
- package/lib/transforms/toc.js +22 -4
- package/lib/transforms/wordCount.js +2 -2
- package/lib/utils/fs.js +8 -172
- package/lib/utils/fs.test.js +4 -162
- package/lib/utils/hash-file.js +28 -0
- package/lib/utils/logs.js +16 -2
- package/lib/utils/syntax.js +1 -0
- package/lib/utils/text.js +1 -1
- package/lib/utils/toposort.js +131 -0
- package/package.json +4 -3
- package/lib/utils/md/filters.js +0 -20
- package/lib/utils/md/find-code-blocks.js +0 -88
- package/lib/utils/md/find-date.js +0 -32
- package/lib/utils/md/find-frontmatter.js +0 -92
- package/lib/utils/md/find-frontmatter.test.js +0 -17
- package/lib/utils/md/find-html-tags.js +0 -105
- package/lib/utils/md/find-images-md.js +0 -27
- package/lib/utils/md/find-images.js +0 -107
- package/lib/utils/md/find-links.js +0 -220
- package/lib/utils/md/find-unmatched-html-tags.js +0 -32
- package/lib/utils/md/fixtures/2022-01-22-date-in-filename.md +0 -14
- package/lib/utils/md/fixtures/file-with-frontmatter.md +0 -32
- package/lib/utils/md/fixtures/file-with-links.md +0 -153
- package/lib/utils/md/md.test.js +0 -105
- package/lib/utils/md/parse.js +0 -146
- 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 {
|
|
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('
|
|
15
|
-
const {
|
|
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
|
-
|
|
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
|
|
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 {
|
|
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
|
-
*
|
|
55
|
-
* @
|
|
56
|
-
* @
|
|
57
|
-
* @
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
103
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
129
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
228
|
+
// logger('files', files)
|
|
159
229
|
// return
|
|
160
|
-
//
|
|
230
|
+
// logger('globs', globs)
|
|
161
231
|
// const filex = await globby(globs)
|
|
162
|
-
//
|
|
232
|
+
// logger('filex', filex)
|
|
163
233
|
|
|
164
234
|
/*
|
|
165
235
|
const relativeFilePaths = files.map((file) => file.replace(cwd, '').replace(/^\//, ''))
|
|
166
|
-
|
|
236
|
+
logger('relativeFilePaths', relativeFilePaths)
|
|
167
237
|
process.exit(1)
|
|
168
238
|
/** */
|
|
169
239
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
179
|
-
|
|
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 (
|
|
187
|
-
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
323
|
+
logger('cleanerDir', cleanerDir)
|
|
324
|
+
logger('resolvedDir', resolvedDir)
|
|
325
|
+
logger('cleanFinalPath', cleanFinalPath)
|
|
198
326
|
/** */
|
|
199
|
-
//
|
|
200
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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 =
|
|
466
|
+
const count = ` ${i + 1}.`
|
|
275
467
|
let planMsg = `${count} Found ${transformsToRun.length} transforms in ${item.srcPath}`
|
|
276
468
|
planTotal = planTotal + transformsToRun.length
|
|
277
|
-
//
|
|
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 = `
|
|
473
|
+
const planData = ` - "${trn.transform}" at line ${line} → ${location}`
|
|
282
474
|
planMsg += `\n${planData}`
|
|
283
|
-
//
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
info(`
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
312
|
-
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
|
651
|
+
const hasNewLine = errors.length !== n - 1 ? '' : '\n'
|
|
362
652
|
const lineMessage = ` - ${message}`
|
|
363
653
|
msg += `\n${lineMessage}`
|
|
364
|
-
console.log(`${lineMessage}${
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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 = {
|