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
@@ -0,0 +1,315 @@
1
+ const { transform } = require('@technote-space/doctoc')
2
+ const { removeLeadingAndTrailingLineBreaks, escapeRegexString } = require('./regex')
3
+ const { findMinIndent } = require('./text')
4
+ const { readFile } = require('./fs')
5
+
6
+ // Alt https://github.com/thlorenz/doctoc
7
+
8
+ /**
9
+ * @typedef {object} TocOptions
10
+ * @property {boolean} [collapse = false] - Collapse toc in <details> pane
11
+ * @property {string} [collapseText] - Text in expand pane
12
+ * @property {string} [excludeText] - Text to exclude from toc
13
+ * @property {boolean} [firsth1 = true] - Exclude first heading from toc
14
+ * @property {boolean} [sub = false] - Mark as sub section table of contents
15
+ * @property {number} [maxDepth = 4] - Max depth of headings to add to toc.
16
+ */
17
+
18
+ /**
19
+ * @typedef {object} TocAPI
20
+ * @property {TocOptions} options - Toc generation options
21
+ * @property {string} fileContent - Markdown contents
22
+ * @property {string} srcPath - Path to markdown file
23
+ * @property {function} getBlockDetails - Get block details for sub tables
24
+ */
25
+
26
+ /**
27
+ * Generate a table of contents
28
+ * @param {TocAPI} api
29
+ * @returns
30
+ */
31
+ async function generateToc({
32
+ options = {},
33
+ fileContent,
34
+ srcPath,
35
+ getBlockDetails
36
+ }) {
37
+ // const currentBlock = api.getCurrentBlock()
38
+ // console.log('originalBlock', originalBlock)
39
+
40
+ // process.exit(1)
41
+ const opts = options || {}
42
+ const isSub = opts.sub
43
+ opts.firsth1 = (opts.firsth1) ? true : false
44
+ let contents = fileContent
45
+ // console.log('contents', contents)
46
+
47
+ let collapseText = opts.collapseText
48
+
49
+ let debugFileMatch
50
+ // console.log('config', config.originalPath)
51
+ // debugFileMatch = config.originalPath.match(/packages\/analytics\/README\.md/)
52
+
53
+ // https://www.npmjs.com/package/@technote-space/toc-generator
54
+ const tocOptions = {
55
+ // mode: 'github.com', // github.com | bitbucket.org | gitlab.com | nodejs.org | ghost.org (default: github.com)
56
+ isNotitle: true,
57
+ //isCustomMode: true,
58
+ openingComment: '',
59
+ closingComment: '',
60
+ maxHeaderLevel: (opts.maxDepth) ? Number(opts.maxDepth) : 4,
61
+ // maxHeaderLevel: 2, // default: 4
62
+ // title: '**Table of Contents**',
63
+ // isNotitle: true,
64
+ // isFolding: true,
65
+ // entryPrefix: '*',
66
+ // processAll: true,
67
+ // updateOnly: true,
68
+ // openingComment: '<!-- toc -->',
69
+ // closingComment: '<!-- tocstop --> ',
70
+ // checkOpeningComments: ['<!-- toc '],
71
+ // checkClosingComments: ['<!-- tocstop '],
72
+ // isCustomMode: false,
73
+ // customTemplate: '<p align="center">${ITEMS}</p>',
74
+ // itemTemplate: '<a href="${LINK}">${TEXT}</a>',
75
+ // separator: '<span>|</span>',
76
+ // footer: 'end',
77
+ }
78
+ const t = await transform(contents, tocOptions)
79
+ let outputText = t.wrappedToc || ''
80
+
81
+ if (debugFileMatch) {
82
+ console.log('before firsth1 removal', outputText)
83
+ }
84
+
85
+ /* remove first h1 */
86
+ if (!opts.firsth1) {
87
+ // console.log('outputText', outputText)
88
+ const lines = outputText.split('\n')
89
+ let firstH1
90
+ let firstLine
91
+ const countOfH1s = lines.reduce((acc, line, index) => {
92
+ if (line !== '' && !firstLine) {
93
+ firstLine = index
94
+ }
95
+ if (line.match(/^-\s+/)) {
96
+ if (!firstH1) {
97
+ const headingText = line.match(/\[(.*)\]/)
98
+ if (headingText) {
99
+ const rawText = headingText[1]
100
+ firstH1 = [line, index, firstLine === index, rawText]
101
+ }
102
+ }
103
+ acc = acc + 1
104
+ }
105
+ return acc
106
+ }, 0)
107
+
108
+ const firstHeadingData = (firstH1 && Array.isArray(firstH1)) ? firstH1 : []
109
+ const [ lineText, lineIndex, isFirst, matchText ] = firstHeadingData
110
+
111
+ let docHasHeading = false
112
+ if (matchText) {
113
+ // verify its h1
114
+ const matchTextEscaped = escapeRegexString(matchText)
115
+ // console.log('matchTextEscaped', matchTextEscaped)
116
+ // /^#{1}\s+(.*)/
117
+ const FIRST_H1_REGEX = new RegExp(`^#\\s*\\[?${matchTextEscaped}\\]?(?:.*)?`, 'gim')
118
+ // /^<h1\b[^>]*>([\s\S]*?)<\/h1>/
119
+ const FIRST_HTML_H1_REGEX = new RegExp(`^<h1\\b[^>]*>[\\s]*?(${matchTextEscaped})[\\s]*?<\\/h1>`, 'gim')
120
+ // /^(.*)\n={3,}/
121
+ const FIRST_UNDERSCORE_H1 = new RegExp(`^(${matchTextEscaped})\n={3,}`, 'gim')
122
+
123
+ if (contents.match(FIRST_H1_REGEX)) {
124
+ docHasHeading = matchText
125
+ } else if (contents.match(FIRST_HTML_H1_REGEX)) {
126
+ docHasHeading = matchText
127
+ } else if (contents.match(FIRST_UNDERSCORE_H1)) {
128
+ docHasHeading = matchText
129
+ }
130
+ }
131
+
132
+ if (debugFileMatch) {
133
+ console.log('matchText', matchText)
134
+ if (docHasHeading) {
135
+ console.log('Found heading 1', docHasHeading)
136
+ }
137
+ }
138
+
139
+ if (debugFileMatch) {
140
+ console.log('top level toc item count', countOfH1s)
141
+ if (docHasHeading) {
142
+ console.log('Found heading 1', docHasHeading)
143
+ console.log('firstH1', firstH1)
144
+ }
145
+ }
146
+
147
+ let firstItemWithContent
148
+ let foundFirstH1
149
+
150
+ if (docHasHeading) {
151
+ outputText = lines.reduce((acc, line, i) => {
152
+ const isLineEmpty = line === ''
153
+ if (!isLineEmpty && !firstItemWithContent) {
154
+ firstItemWithContent = i
155
+ }
156
+ if (!foundFirstH1 && (firstItemWithContent === i) && line.match(/^-\s+/)) {
157
+ foundFirstH1 = true
158
+ return acc
159
+ }
160
+ // Add back line and remove level
161
+ let newLineValue = line
162
+ if (lineText) {
163
+ // const untilFirstH1 = i < lineIndex
164
+ /* @TODO make option? flatten all non first h1 tags */
165
+ if (countOfH1s > 0 && !isLineEmpty && newLineValue.match(/^\s+-/)) {
166
+ newLineValue = line.substring(2)
167
+ }
168
+ }
169
+
170
+ acc = acc.concat(newLineValue)
171
+ return acc
172
+ }, []).join('\n')
173
+ }
174
+
175
+ // console.log('outputText', outputText)
176
+ if (debugFileMatch) {
177
+ console.log('after firsth1 removal', outputText)
178
+ }
179
+ }
180
+ // console.log(t)
181
+ // Fix <h1> with multiple lines
182
+ const spaces = outputText.match(/\[\n([\s\S]*?)\]/gm)
183
+ if (spaces) {
184
+ const fixed = spaces[0]
185
+ // all new lines
186
+ .replace(/\n/g, '')
187
+ // leading spaces
188
+ .replace(/\[\s+/, '[')
189
+ .trim()
190
+ outputText = outputText.replace(spaces[0], fixed)
191
+ }
192
+ // console.log(outputText)
193
+ outputText = outputText
194
+ .replace(/(.*)\[Table of Contents\]\(.*\)\n?/i, '')
195
+ .replace(/(.*)\[toc\]\(.*\)\n?/i, '')
196
+
197
+
198
+ if (opts.excludeText) {
199
+
200
+ outputText = excludeTocItem(outputText, opts.excludeText)
201
+ // // (\s+)?-(.*)\[Usage\]\(.*\)
202
+ // const regex = new RegExp(`\(\\s+\)?-(.*)\\[${opts.excludeText}\\]\\(.*\\)`, 'i')
203
+ // // /(\s+)?-(.*)\[Usage\]\(.*\)(\n\s+(.*))+/im
204
+ // const nestedRegex = new RegExp(`\(\\s+\)?-(.*)\\[${opts.excludeText}\\]\\(.*\\)\(\\n\\s+\(.*\)\)+`, 'i')
205
+
206
+ // const hasNested = nestedRegex.exec(outputText)
207
+ // if (hasNested) {
208
+ // // Count indentation of spaces
209
+ // const numberOfSpaces = hasNested[1].replace(/\n/g, '').length
210
+ // const subItems = numberOfSpaces + 1
211
+ // // Update regex to only remove sub items
212
+ // const nestedRegexSpaces = new RegExp(`\(\\s+\)?-(.*)\\[${opts.excludeText}\\]\\(.*\\)\(\\n\\s{${subItems},}\(.*\)\)+`, 'i')
213
+ // // console.log('nestedRegexSpaces', nestedRegexSpaces)
214
+ // // If exclude value has nested sections remove them as well.
215
+ // outputText = outputText.replace(nestedRegexSpaces, '')
216
+ // outputText = outputText.replace(regex, '')
217
+ // } else {
218
+ // outputText = outputText.replace(regex, '')
219
+ // }
220
+ }
221
+
222
+ /* Sub table of contents */
223
+ if (isSub) {
224
+ // const start = fileContent.indexOf(open.value)
225
+ // const linesBefore = fileContent.substr(0, start).split('\n')
226
+ // const closestHeading = linesBefore.reverse().find((line) => {
227
+ // return line.match((/^#+/))
228
+ // })
229
+ const originalContents = await readFile(srcPath, { encoding: 'utf8' })
230
+ const originalBlock = getBlockDetails(originalContents)
231
+ const linesBefore = originalContents.substr(0, originalBlock.block.start).split('\n')
232
+ const closestHeading = linesBefore.reverse().find((line) => {
233
+ return line.match((/^#+/))
234
+ })
235
+
236
+ if (closestHeading) {
237
+ const headingText = closestHeading.replace(/^#*\s*/, '')
238
+ // console.log('BEFORE', linesBefore)
239
+ // console.log('closest parent heading', closestHeading)
240
+ // console.log('headingText', headingText)
241
+ collapseText = collapseText || `${headingText} contents`
242
+ // https://regex101.com/r/MB85zm/1
243
+
244
+ const findSubToc = new RegExp(`^\(\\s+\)?-(.*)\\[${headingText}\\]\\(.*\\)(\\n\\s.*)+`, 'gm')
245
+ // console.log('findSubToc', findSubToc)
246
+ const single = singleLinePattern(headingText)
247
+ // console.log('single', single)
248
+ const subItems = outputText.match(findSubToc)
249
+ if (subItems) {
250
+ const items = subItems[0].replace(single, '').split('\n')
251
+ .filter(Boolean)
252
+ // console.log('items', items)
253
+ const finalItems = items // .slice(1, items.length)
254
+ if (finalItems) {
255
+ const indent = findMinIndent(finalItems.join('\n'))
256
+ // console.log('min indent', indent)
257
+ // console.log('finalItems', finalItems)
258
+ outputText = finalItems.map((thing) => thing.substring(indent)).join('\n')
259
+ } else {
260
+ // console.log('No sub items')
261
+ }
262
+ }
263
+ }
264
+ }
265
+
266
+ // If collapse wrap in <details>
267
+ if (opts.collapse) {
268
+ return `<details>
269
+ <summary>${collapseText || 'Table of Contents'}</summary>
270
+
271
+ ${outputText
272
+ // Replace leading double spaces
273
+ .replace(/^\n*/g, '')
274
+ // Replace trailing double spaces
275
+ .replace(/\n*$/g, '')
276
+ }
277
+
278
+ </details>`
279
+ }
280
+
281
+ return outputText.replace(removeLeadingAndTrailingLineBreaks, '')
282
+ }
283
+
284
+ function singleLinePattern(text) {
285
+ /* (\s+)?-(.*)\[Usage\]\(.*\) */
286
+ return new RegExp(`\(\\s+\)?-(.*)\\[${text}\\]\\(.*\\)`, 'i')
287
+ }
288
+
289
+ function excludeTocItem(str, excludeText) {
290
+ const matchTextEscaped = escapeRegexString(excludeText)
291
+ /* (\s+)?-(.*)\[Usage\]\(.*\) */
292
+ const regex = singleLinePattern(matchTextEscaped) // new RegExp(`\(\\s+\)?-(.*)\\[${matchTextEscaped}\\]\\(.*\\)`, 'i')
293
+ /* /(\s+)?-(.*)\[Usage\]\(.*\)(\n\s+(.*))+/im */
294
+ const nestedRegex = new RegExp(`\(\\s+\)?-(.*)\\[${matchTextEscaped}\\]\\(.*\\)\(\\n\\s+\(.*\)\)+`, 'i')
295
+
296
+ const hasNested = nestedRegex.exec(str)
297
+ if (hasNested) {
298
+ // Count indentation of spaces
299
+ const numberOfSpaces = (hasNested[1] || '').replace(/\n/g, '').length
300
+ const subItems = numberOfSpaces + 1
301
+ // Update regex to only remove sub items
302
+ const nestedRegexSpaces = new RegExp(`\(\\s+\)?-(.*)\\[${matchTextEscaped}\\]\\(.*\\)\(\\n\\s{${subItems},}\(.*\)\)+`, 'i')
303
+ // console.log('nestedRegexSpaces', nestedRegexSpaces)
304
+ // If exclude value has nested sections remove them as well.
305
+ str = str.replace(nestedRegexSpaces, '')
306
+ str = str.replace(regex, '')
307
+ } else {
308
+ str = str.replace(regex, '')
309
+ }
310
+ return str
311
+ }
312
+
313
+ module.exports = {
314
+ generateToc
315
+ }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "markdown-magic",
3
- "version": "2.6.1",
3
+ "version": "3.0.1",
4
4
  "description": "Automatically update markdown files with content from external sources",
5
- "main": "index.js",
5
+ "main": "lib/index.js",
6
6
  "bin": {
7
7
  "markdown": "./cli.js",
8
8
  "md-magic": "./cli.js"
@@ -10,18 +10,27 @@
10
10
  "files": [
11
11
  "README.md",
12
12
  "package.json",
13
- "index.js",
13
+ "package-lock.json",
14
14
  "cli.js",
15
- "cli-utils.js",
16
15
  "/lib"
17
16
  ],
18
17
  "scripts": {
18
+ "types": "tsc",
19
+ "emit-types": "tsc --noEmit false --emitDeclarationOnly true",
19
20
  "docs": "node examples/generate-readme.js",
20
- "uvu": "uvu lib '.test.([mc]js|[jt]sx?)$'",
21
- "test": "ava --verbose",
22
- "test:watch": "ava --verbose --watch",
21
+ "test": "npm run test:lib && npm run test:test && echo 'tests done'",
22
+ "test:lib": "uvu lib '.test.([mc]js|[jt]sx?)$'",
23
+ "test:test": "uvu test '.test.([mc]js|[jt]sx?)$'",
24
+ "test:options": "uvu lib 'options-parser.test.([mc]js|[jt]sx?)$'",
25
+ "test:block": "uvu lib 'block-parser.test.([mc]js|[jt]sx?)$'",
26
+ "test:cli": "uvu lib 'cli.test.([mc]js|[jt]sx?)$'",
27
+ "test:md": "uvu lib 'md.test.([mc]js|[jt]sx?)$'",
28
+ "test:fs": "uvu lib 'fs.test.([mc]js|[jt]sx?)$'",
29
+ "test:js": "uvu lib 'block-parser-js.test.([mc]js|[jt]sx?)$'",
30
+ "test:errors": "uvu test 'errors.test.([mc]js|[jt]sx?)$'",
31
+ "test:transforms": "uvu test 'transforms.test.([mc]js|[jt]sx?)$'",
32
+ "test:text": "uvu lib 'text.test.([mc]js|[jt]sx?)$'",
23
33
  "cli": "node ./cli.js --path 'README.md' --config ./markdown.config.js",
24
- "lint": "eslint .",
25
34
  "publish": "git push origin && git push origin --tags",
26
35
  "release:patch": "npm version patch && npm publish",
27
36
  "release:minor": "npm version minor && npm publish",
@@ -36,29 +45,24 @@
36
45
  },
37
46
  "dependencies": {
38
47
  "@technote-space/doctoc": "2.4.7",
39
- "commander": "^7.2.0",
40
- "deepmerge": "^4.2.2",
41
- "find-up": "^5.0.0",
42
- "globby": "^10.0.2",
48
+ "globrex": "^0.1.2",
49
+ "gray-matter": "^4.0.3",
50
+ "is-glob": "^4.0.3",
43
51
  "is-local-path": "^0.1.6",
44
- "json-alexander": "^0.1.8",
45
- "mkdirp": "^1.0.4",
52
+ "is-valid-path": "^0.1.1",
53
+ "micro-mdx-parser": "^1.1.0",
54
+ "mri": "^1.2.0",
55
+ "oparser": "^2.1.1",
56
+ "smart-glob": "^1.0.2",
46
57
  "sync-request": "^6.1.0"
47
58
  },
48
59
  "devDependencies": {
49
- "ava": "^3.15.0",
50
- "doxxx": "^1.0.0",
51
- "rimraf": "^3.0.2",
52
- "sinon": "^11.1.1",
53
- "uvu": "^0.5.1",
54
60
  "ansi-styles": "^4.2.1",
55
61
  "concordance": "^5.0.1",
56
- "safe-chalk": "^1.0.0"
57
- },
58
- "ava": {
59
- "files": [
60
- "test/**/*.test.js"
61
- ],
62
- "verbose": true
62
+ "doxxx": "^2.0.1",
63
+ "rimraf": "^3.0.2",
64
+ "safe-chalk": "^1.0.0",
65
+ "typescript": "^5.0.2",
66
+ "uvu": "^0.5.1"
63
67
  }
64
68
  }
package/index.js DELETED
@@ -1,46 +0,0 @@
1
- const globby = require('globby')
2
- const processFile = require('./lib/processFile')
3
- /**
4
- * ### API
5
- * ```js
6
- * markdownMagic(filePath, config, callback)
7
- * ```
8
- * - `filePaths` - *String or Array* - Path or glob pattern. Uses [globby patterns](https://github.com/sindresorhus/multimatch/blob/master/test.js)
9
- * - `config` - See configuration options below
10
- * - `callback` - callback to run after markdown updates
11
- *
12
- * @param {string} filePath - Path to markdown file
13
- * @param {object} [config] - configuration object
14
- * @param {Function} [callback] - callback function with updated contents
15
- */
16
- module.exports = async function markdownMagic(filePaths, config, callback) {
17
- const files = globby.sync(filePaths)
18
- const configuration = config || {}
19
- if (!callback && typeof configuration === 'function') {
20
- callback = configuration // eslint-disable-line
21
- } else if (typeof config === 'object' && config.callback) {
22
- // set callback in config for CLI usage
23
- callback = config.callback // eslint-disable-line
24
- }
25
- if (!files.length) {
26
- callback && callback('No files matched')
27
- console.log('No files matched pattern', filePaths)
28
- return false
29
- }
30
- configuration.originalFilePaths = files
31
-
32
- const data = files.map(async (file) => {
33
- const x = await processFile(file, configuration)
34
- return x
35
- })
36
-
37
- const values = await Promise.all(data)
38
-
39
- if (callback) {
40
- callback(null, values)
41
- }
42
- return values
43
- }
44
-
45
- // expose globby for use in plugins
46
- module.exports.globby = globby
@@ -1,154 +0,0 @@
1
- const fs = require('fs')
2
- const mkdirp = require('mkdirp')
3
- const path = require('path')
4
- const merge = require('deepmerge')
5
- const transforms = require('./transforms')
6
- const regexUtils = require('./utils/regex')
7
- const pluginSortOrder = require('./utils/sortOrder')
8
- const updateContents = require('./updateContents')
9
- const cwd = process.cwd()
10
-
11
- module.exports = async function processFile(filePath, config) {
12
- let content
13
- try {
14
- content = fs.readFileSync(filePath, 'utf8')
15
- } catch (e) {
16
- console.log(`FILE NOT FOUND ${filePath}`)
17
- throw e
18
- }
19
- /**
20
- * ### Configuration Options
21
- */
22
- const defaultConfig = {
23
- /**
24
- * - `transforms` - *object* - (optional) Custom commands to transform block contents, see transforms & custom transforms sections below.
25
- * @type {Object}
26
- */
27
- transforms: transforms,
28
- /**
29
- * - `outputDir` - *string* - (optional) Change output path of new content. Default behavior is replacing the original file
30
- * @type {string}
31
- */
32
- outputDir: path.dirname(filePath),
33
- /**
34
- * - `matchWord` - *string* - (optional) Comment pattern to look for & replace inner contents. Default `AUTO-GENERATED-CONTENT`
35
- * @type {string}
36
- * @default [AUTO-GENERATED-CONTENT]
37
- */
38
- matchWord: 'AUTO-GENERATED-CONTENT',
39
- /**
40
- * - `DEBUG` - *Boolean* - (optional) set debug flag to `true` to inspect the process
41
- * @type {boolean}
42
- */
43
- DEBUG: false,
44
- }
45
-
46
- const userConfig = (config && typeof config === 'object') ? config : {}
47
- const mergedConfig = merge(defaultConfig, userConfig)
48
-
49
- const registeredTransforms = Object.keys(mergedConfig.transforms)
50
- // Set originalPath constant
51
- mergedConfig.originalPath = filePath
52
- // contents of original MD file
53
- mergedConfig.originalContent = content
54
- // set default outputContents for first pass for single commands
55
- mergedConfig.outputContent = content
56
-
57
- const regex = regexUtils.matchCommentBlock(mergedConfig.matchWord)
58
- // console.log(regex)
59
- const match = content.match(regex)
60
- const transformsFound = []
61
-
62
- if (match) {
63
- let commentMatches
64
- let matchIndex = 0
65
- while ((commentMatches = regex.exec(content)) !== null) {
66
- if (commentMatches.index === regex.lastIndex) {
67
- regex.lastIndex++ // This is necessary to avoid infinite loops
68
- }
69
-
70
- // console.log('commentMatches', commentMatches)
71
- // const command = `Transform ${commentMatches[2]}`
72
- // console.log(command)
73
- transformsFound.push({
74
- spaces: commentMatches[1], // Preserve indentation
75
- transform: commentMatches[2],
76
- match: match[matchIndex]
77
- })
78
- // wait
79
- matchIndex++
80
- }
81
-
82
- // console.log('registeredTransforms', registeredTransforms)
83
-
84
- const transformsToRun = pluginSortOrder(registeredTransforms, transformsFound)
85
- if (mergedConfig.DEBUG) {
86
- console.log('↓ transformsToRun')
87
- console.log(transformsToRun)
88
- }
89
-
90
- const fileName = path.basename(filePath)
91
- const outputFilePath = path.join(mergedConfig.outputDir, fileName)
92
-
93
- // create folder path if doesnt exist
94
- mkdirp.sync(mergedConfig.outputDir)
95
-
96
- // run sort
97
- let transformMsg = ''
98
- for (const element of transformsToRun) {
99
- // console.log('element', element)
100
- transformMsg += ` ⁕ ${element.transform} \n`
101
- // console.log('order', element.transform)
102
- const newContent = await updateContents(element.match, mergedConfig)
103
- const firstLineIndentation = element.spaces
104
- const contentWithIndentation = newContent.split('\n').join(`\n` + element.spaces)
105
- const preserveTabs = `${firstLineIndentation}${contentWithIndentation}`
106
-
107
- content = content.replace(element.match, preserveTabs)
108
- mergedConfig.outputContent = content
109
- }
110
-
111
- let notFoundNotice
112
- if (!transformMsg) {
113
- // console.log('config', config)
114
- const notFound = transformsFound.map((x) => {
115
- return `"${x.transform}"`
116
- })
117
- notFoundNotice = notFound
118
- }
119
-
120
- // console.log('transformMsg', transformMsg)
121
-
122
- const msg = outputFilePath.replace(cwd, '')
123
-
124
- if (transformMsg) {
125
- console.log(`✔ ${msg} Updated`)
126
- // update file contents
127
- fs.writeFileSync(outputFilePath, content)
128
- console.log(` Transforms run`)
129
- console.log(transformMsg)
130
- }
131
-
132
- if (notFoundNotice) {
133
- const word = notFoundNotice.length > 1 ? 'transforms' : 'transform'
134
- console.log(`ℹ Notice:`)
135
- console.log(` Missing ${word} ${notFoundNotice.join(',')} in ${msg}`)
136
- console.log()
137
- }
138
-
139
-
140
- // set return values
141
- mergedConfig.outputFilePath = outputFilePath
142
- mergedConfig.outputContent = content
143
-
144
- return mergedConfig
145
- }
146
-
147
- if (mergedConfig.DEBUG) {
148
- console.log(`↓ ${filePath}`)
149
- console.log(`[No match] <!-- ${mergedConfig.matchWord} --> comment found`)
150
- }
151
-
152
- // no match return original contents
153
- return mergedConfig
154
- }