markdown-magic 2.6.0 → 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 +26 -26
  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
@@ -0,0 +1,83 @@
1
+ // https://stackoverflow.com/questions/38859506/cancel-regex-match-if-timeout
2
+ const util = require('util')
3
+ const vm = require('vm')
4
+ const { getBlockRegex } = require('../block-parser')
5
+ const { getSyntaxInfo } = require('./syntax')
6
+
7
+ const goodString = `
8
+ # Test Fixture
9
+
10
+ This is normal text in markdown. <!-- doc-start (wordCount) -->x<!-- doc-end -->
11
+ Keep it.
12
+
13
+ <!-- doc-start (wordCount) -->
14
+ THIS CONTENT GETS AUTO GENERATED. Don't directly edit it
15
+ <!-- doc-end -->
16
+
17
+ This is normal text in markdown. Keep it.
18
+ `
19
+
20
+ const badString = `
21
+ # Test Fixture
22
+
23
+ This is normal text in markdown. <!-- doc-start (wordCount) -->x<!-- oc-end -->
24
+ Keep it.
25
+
26
+ <!-- doc-start (wordCount) -->
27
+ THIS CONTENT GETS AUTO GENERATED. Don't directly edit it
28
+ <!-- xoc-end -->
29
+
30
+ This is normal text in markdown. Keep it.
31
+ `
32
+
33
+ // var pattern = /([ \t]*)(?:<!-{2,}(?:.*|\r?|\n?|\s*)docs-start\s*([(\[\{]*[A-Za-z0-9_$-]*[)\]\}]*)\s*)((?:.*?|.*?\r?\n?)*?)<!-{2,}(?:.*|\r?|\n?|\s*)docs-end(?:.|\r?\n)*?-{2,}>/gim
34
+
35
+ function safeRegex(str) {
36
+ const syntaxInfo = getSyntaxInfo('md')
37
+ if (!syntaxInfo.pattern) {
38
+ throw new Error(`Unknown syntax "${syntax}"`)
39
+ }
40
+ const [ openComment, closeComment ] = syntaxInfo.pattern
41
+ const pattern = getBlockRegex({
42
+ openComment,
43
+ closeComment,
44
+ openText: 'doc-start',
45
+ closeText: 'doc-end'
46
+ })
47
+ console.log('Pattern', pattern)
48
+ const sandbox = {
49
+ // regex: /([ \t]*)(?:<!-{2,}(?:.*|\r?|\n?|\s*)docs-start\s*([(\[\{]*[A-Za-z0-9_$-]*[)\]\}]*)\s*)((?:.*?|.*?\r?\n?)*?)<!-{2,}(?:.*|\r?|\n?|\s*)docs-end(?:.|\r?\n)*?-{2,}>/gim,
50
+ regexToUse: pattern,
51
+ string: str,
52
+ //string: badStringTwo,
53
+ //string: string,
54
+ }
55
+
56
+ const context = vm.createContext(sandbox)
57
+ console.log('Sandbox initialized: ' + vm.isContext(sandbox))
58
+ // var script = new vm.Script('result = regex.exec(string)');
59
+ const script = new vm.Script(`
60
+ blocks = []
61
+ rawMatches = string.match(regexToUse)
62
+ while ((list = regexToUse.exec(string)) !== null) {
63
+ const [ block, spaces, __type, params ] = list
64
+ blocks.push({
65
+ __type,
66
+ block
67
+ })
68
+ if (list.index === regexToUse.lastIndex) regexToUse.lastIndex++
69
+ }
70
+ `);
71
+ try {
72
+ // One could argue if a RegExp hasn't processed in a given time.
73
+ // then, its likely it will take exponential time.
74
+ script.runInContext(context, { timeout: 1000 }); // milliseconds
75
+ } catch (e) {
76
+ console.log('ReDos occurred', e); // Take some remedial action here...
77
+ }
78
+ console.log('result:')
79
+ console.log(util.inspect(sandbox)); // Check the results
80
+ }
81
+
82
+ // safeRegex(goodString)
83
+ safeRegex(badString)
@@ -1,19 +1,34 @@
1
1
 
2
- module.exports.matchCommentBlock = function(word) {
2
+ // REGEX to look for regex
3
+ // https://github.com/kgryte/regex-regex/blob/master/lib/index.js
4
+ const REGEX_REGEX = /^\/((?:\\\/|[^\/])+)\/([imgy]*)$/
5
+
6
+ function escapeRegexString(string) {
7
+ if (typeof string !== 'string') {
8
+ throw new TypeError('Expected a string')
9
+ }
10
+ // Escape characters with special meaning either inside or outside character sets.
11
+ // Use a simple backslash escape when it’s always valid, and a `\xnn` escape when the simpler form would be disallowed by Unicode patterns’ stricter grammar.
12
+ return string
13
+ .replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
14
+ .replace(/-/g, '\\x2d');
15
+ }
16
+
17
+ function matchCommentBlock(word) {
3
18
  //return new RegExp(`(?:\\<\\!--(?:.|\\r?\\n)*?${matchWord}:START(?:.|\\r?\\n)*?\\()(.*)\\)(?:.|\\r?\\n)*?<!--(?:.|\\r?\\n)*?${matchWord}:END(?:.|\\r?\\n)*?--\\>`, 'g')
4
19
  return new RegExp(`(.*)(?:\\<\\!--(?:.*|\r?|\n?|\s*)${word}:START(?:.|\\r?\\n)*?\\()(.*)\\)(?:.|\\r?\\n)*?<!--(?:.*|\r?|\n?|\s*)${word}:END(?:.|\\r?\\n)*?--\\>`, 'g')
5
20
  }
6
21
 
7
- module.exports.matchOpeningCommentTag = function (word) {
22
+ function matchOpeningCommentTag(word) {
8
23
  // return new RegExp(`(\\<\\!--(?:.|\\r?\\n)*?${matchWord}:START)((?:.|\\r?\\n)*?--\\>)`, 'g')
9
24
  return new RegExp(`(\\<\\!--(?:.*|\r?|\n?|\s*)${word}:START)((?:.|\\r?\\n)*?--\\>)`, 'g')
10
25
  }
11
26
 
12
- module.exports.matchClosingCommentTag = function (word) {
27
+ function matchClosingCommentTag(word) {
13
28
  return new RegExp(`--\\>(?:.|\\r?\\n)*?((?:\\<\\!--(?:.*|\\r?\\n)(?:.*|\\r?\\n))*?${word}:END)((?:.|\\r?\\n)*?--\\>)`, 'g')
14
29
  }
15
30
 
16
- module.exports.removeLeadingAndTrailingLineBreaks = /^(?:[\t ]*(?:\r?\n|\r))+|\s+$/g
31
+ const removeLeadingAndTrailingLineBreaks = /^(?:[\t ]*(?:\r?\n|\r))+|\s+$/g
17
32
 
18
33
  const jsRegex = /\/\* Step([\s\S]*?)\*\//g
19
34
  const ymlRegex = / *?# Step([\s\S]*?) #\n*?/g
@@ -56,6 +71,15 @@ function getSteps() {
56
71
  })
57
72
  }
58
73
 
74
+ /*
75
+ Alt attribute regex https://regex101.com/r/Ughcb7/1
76
+ const attrNameRegex = "[a-z0-9_]+";
77
+ const attrValueRegex = '(\\\\"|[^"])*';
78
+ const attrRegex = `(${attrNameRegex})\\s*=\\s*"(${attrValueRegex})"`;
79
+ const keyValue = new RegExp(attrRegex, "gi");
80
+ */
81
+
82
+
59
83
  // Regex to remove all comment blocks
60
84
  const REMOVE_COMMENTS_REGEX = / *?\<\!-- ([\s\S]*?) ?--\>\n\n*?/g
61
85
  // content.replace(removeComments, '')
@@ -73,4 +97,13 @@ const TABEL_ROW_REGEX = /^\s*\|.*?\|\s*$/
73
97
  * @param {string} text The text to validate.
74
98
  * @returns {boolean}
75
99
  */
76
- const isTableRow = (text) => text.match(TABEL_ROW_REGEX);
100
+ const isTableRow = (text) => text.match(TABEL_ROW_REGEX);
101
+
102
+ module.exports = {
103
+ REGEX_REGEX,
104
+ escapeRegexString,
105
+ matchCommentBlock,
106
+ matchOpeningCommentTag,
107
+ matchClosingCommentTag,
108
+ removeLeadingAndTrailingLineBreaks,
109
+ }
@@ -11,3 +11,57 @@ module.exports = function remoteRequest(url) {
11
11
  }
12
12
  return body
13
13
  }
14
+
15
+ /*
16
+ TODO add file caching?
17
+ */
18
+ // const path = require('path')
19
+ // const cacheManager = require('cache-manager')
20
+ // const fsStoreHash = require('cache-manager-fs-hash')
21
+ // const CACHE_KEY = 'foo'
22
+ // const STORAGE_PATH = (process.env.IS_OFFLINE) ? path.join(__dirname, '../tmp') : '/tmp'
23
+ // const SECONDS = 60
24
+ // const MINUTES = 60
25
+ // const ONE_HOUR = SECONDS * MINUTES
26
+ // const mbOfStorage = 512
27
+ // /* initialize caching on disk */
28
+ // const diskCache = cacheManager.caching({
29
+ // store: fsStoreHash,
30
+ // options: {
31
+ // /* TTL in seconds */
32
+ // ttl: ONE_HOUR,
33
+ // /* max size in bytes on disk */
34
+ // // maxsize: mbOfStorage * 1000 * 1000,
35
+ // path: STORAGE_PATH,
36
+ // }
37
+ // })
38
+
39
+ // async function usage() {
40
+ // const hasCache = await diskCache.get(CACHE_KEY)
41
+ // if (hasCache && hasCache.length) {
42
+ // /* If cache NOT empty return it */
43
+ // // console.log('Using cached value', hasCache)
44
+ // return hasCache
45
+ // }
46
+ // // else do fetch
47
+ // const data = await getStuff()
48
+ // // Then save cache
49
+ // console.log('Saving value')
50
+ // await diskCache.set(CACHE_KEY, data)
51
+ // }
52
+
53
+ // function getCacheSize(filePath) {
54
+ // return new Promise((resolve, reject) => {
55
+ // fs.stat(filePath, (err, stats) => {
56
+ // if (err) {
57
+ // return resolve({ sizeInBytes: 0, sizeInMB: 0 })
58
+ // }
59
+ // const byteSize = stats.size
60
+ // const megaByteSize = byteSize / (1024 * 1024)
61
+ // return resolve({
62
+ // sizeInBytes: byteSize,
63
+ // sizeInMB: megaByteSize
64
+ // })
65
+ // })
66
+ // })
67
+ // }
@@ -0,0 +1,79 @@
1
+ const html = {
2
+ tags: [
3
+ '<!--',
4
+ '-->'
5
+ ],
6
+ pattern: [
7
+ '<!-{2,}',
8
+ '-{2,}>' // '-->'
9
+ ],
10
+ }
11
+
12
+ // Kramdown https://github.com/HHS/nih-oite-website/blob/480244abfd0f0d741b27d6232273dd03073e06ad/app/javascript/kramdown/parser.js#L12-L48
13
+
14
+ // JS https://regex101.com/r/XKHU18/5
15
+ const js = {
16
+ tags: ['/*', '*/'],
17
+ pattern: [
18
+ '\/\\*+',
19
+ // Old ^ '\/\\*{1,}[\n\\*]*', // '\/\\*+', '\/\*[\*\n\s\t]+', //
20
+ '\\*+/'
21
+ ],
22
+ /* Match single line JS comment */
23
+ singleLineTag: '//',
24
+ singleLinePattern: '//+',
25
+ singleLine: '\/\/.*$'
26
+ }
27
+
28
+ const jsx = {
29
+ tags: [
30
+ '{/*',
31
+ '*/}'
32
+ ],
33
+ pattern: [
34
+ '\{\/\\*+',
35
+ '\\*+/\}'
36
+ ]
37
+ }
38
+
39
+ const yaml = {
40
+ tags: ['##', '##'],
41
+ pattern: [
42
+ '##+',
43
+ '##+'
44
+ ],
45
+ singleLineTag: '#',
46
+ singleLinePattern: '#+',
47
+ singleLine: '#.*$',
48
+ content: '[ \\t\\S]*?',
49
+ converter: (str) => {
50
+ return str.split('\n').map((line) => {
51
+ return line[0] === '#' ? line : `#${line}`
52
+ }).join()
53
+ }
54
+ }
55
+
56
+ const syntaxMap = {
57
+ // <!-- x -->
58
+ md: html,
59
+ markdown: html,
60
+ // <!-- x -->
61
+ html: html,
62
+ // /* x */
63
+ js: js,
64
+ // {/* x */}
65
+ jsx: jsx,
66
+ mdx: jsx,
67
+ // ## x ##
68
+ yaml: yaml,
69
+ yml: yaml
70
+ }
71
+
72
+ function getSyntaxInfo(syntax = '') {
73
+ return syntaxMap[syntax.toLowerCase()] || {}
74
+ }
75
+
76
+ module.exports = {
77
+ getSyntaxInfo,
78
+ syntaxMap
79
+ }
@@ -0,0 +1,260 @@
1
+ const { syntaxMap } = require('./syntax')
2
+
3
+ function getLines(str = '') {
4
+ return str.split(/\r\n|\r|\n/)
5
+ }
6
+
7
+ function getLineCount(str = '') {
8
+ return getLines(str).length
9
+ }
10
+
11
+ function getRowAndColumnFromCharPos(input, indexToFind) {
12
+ const preChunk = input.substr(0, indexToFind);
13
+ const row = preChunk.split('\n').length - 1
14
+ const lastIndexOfNewLine = input.lastIndexOf('\n', indexToFind);
15
+ const col = lastIndexOfNewLine > 0 ? indexToFind - lastIndexOfNewLine - 1 : indexToFind;
16
+ return { row, col }
17
+ };
18
+
19
+ function getWordCount(str = '') {
20
+ return str.trim().split(/\s+/).length
21
+ }
22
+
23
+ function getFirstCharacter(str) {
24
+ return str.charAt(0)
25
+ }
26
+
27
+ function getLastCharacter(str) {
28
+ return str.substr(-1)
29
+ }
30
+
31
+ function getLeadingSpaces(text) {
32
+ const matches = text.match(/^\s*/)
33
+ return (matches && matches[0]) ? matches[0] : ''
34
+ }
35
+
36
+ function getTextBetweenChars(text, start, end) {
37
+ return text.slice(start, end)
38
+ }
39
+
40
+ function replaceTextBetweenChars(str = '', start, end, newStr) {
41
+ return str.substring(0, start) + newStr + str.substring(end)
42
+ }
43
+
44
+ function getTextBetweenLines(content, startLine, endLine) {
45
+ const startDefined = typeof startLine !== 'undefined'
46
+ const endDefined = typeof endLine !== 'undefined'
47
+ if (!startDefined && !endDefined) return
48
+
49
+ const lines = getLines(content)
50
+ if (startDefined && !endDefined) {
51
+ return lines.slice(startLine - 1, startLine).join('')
52
+ }
53
+ if ((startLine) && (endLine) && parseInt(startLine, 10) <= parseInt(endLine, 10)) {
54
+ return lines.slice(startLine - 1, endLine).join('\n')
55
+ }
56
+ }
57
+
58
+ function isUpperCase(str) {
59
+ return str === str.toUpperCase()
60
+ }
61
+
62
+ // https://github.com/jamiebuilds/min-indent/blob/master/index.js
63
+ function findMinIndent(string) {
64
+ const match = string.match(/^[ \t]*(?=\S)/gm)
65
+ if (!match) return 0
66
+ return match.reduce((r, a) => Math.min(r, a.length), Infinity)
67
+ }
68
+
69
+ function stripIndent(string, indentation) {
70
+ const indent = typeof indentation !== 'undefined' ? indentation : findMinIndent(string);
71
+ if (indent === 0) {
72
+ return string
73
+ }
74
+ const regex = new RegExp(`^[ \\t]{${indent}}`, 'gm')
75
+ return string.replace(regex, '')
76
+ }
77
+
78
+ /**
79
+ * Trim leading & trailing spaces/line breaks in code and keeps the indentation of the first non-empty line
80
+ * @param {string} str
81
+ * @returns string
82
+ */
83
+ function trimString(str = '') {
84
+ let content = (typeof str === 'number') ? str.toString() : str
85
+ // console.log('content', `"${content}"`)
86
+ return content.replace(/^(?:[\t ]*(?:\r?\n|\r))+|\s+$/g, '')
87
+ }
88
+
89
+ function indentString(string, count = 1, options = {}) {
90
+ const {
91
+ indent = ' ',
92
+ includeEmptyLines = false
93
+ } = options;
94
+ if (count === 0) return string
95
+ const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm
96
+ return string.replace(regex, indent.repeat(count))
97
+ }
98
+
99
+ /**
100
+ * Removes the indentation of multiline strings
101
+ * @link https://github.com/victornpb/tiny-dedent/
102
+ * @param {string} str A template literal string
103
+ * @return {string} A string without the indentation
104
+ */
105
+ function dedentString(str) {
106
+ str = str.replace(/^[ \t]*\r?\n/, ''); // remove leading blank line
107
+ var indent = /^[ \t]+/m.exec(str); // detected indent
108
+ if (indent) str = str.replace(new RegExp('^' + indent[0], 'gm'), ''); // remove indent
109
+ return str.replace(/(\r?\n)[ \t]+$/, '$1'); // remove trailling blank line
110
+ }
111
+
112
+ /**
113
+ * Strip out comment blocks
114
+ * @param {string} str
115
+ * @param {'md' | 'js'} syntax
116
+ * @returns {string} clean commentless string
117
+ */
118
+ function stripCommentBlockOld(str, syntax = 'md') {
119
+ const [ openPattern, closePattern ] = syntaxMap[syntax].pattern
120
+ const pattern = new RegExp(`^([ \\S]*)${openPattern}(\\s?[\\s\\S]*?)?${closePattern}\n?`, 'gim')
121
+ // console.log('pattern', pattern)
122
+ let newString = str
123
+ let matches
124
+ while ((matches = pattern.exec(str)) !== null) {
125
+ if (matches.index === pattern.lastIndex) {
126
+ pattern.lastIndex++ // avoid infinite loops with zero-width matches
127
+ }
128
+ const [ _match, leadingText ] = matches
129
+ /*
130
+ console.log('_match', _match)
131
+ console.log('leadingText', `"${leadingText}"`)
132
+ console.log('───────────────────────')
133
+ /**/
134
+ /* Handle comments that start midway through line after text */
135
+ if (leadingText) {
136
+ /* Trim trailing tabs/spaces */
137
+ const trimmed = leadingText.replace(/([ \t]*)$/, '')
138
+ const replacement = _match.replace(trimmed, '')
139
+ // console.log('replacement', `"${replacement}"`)
140
+ newString = newString.replace(replacement, `\n`)
141
+ // console.log('new str', newString)
142
+ }
143
+ }
144
+
145
+ // const pattern = new RegExp(`([ \\t]*)${openPattern}\\s?([\\s\\S]*?)?${closePattern}\n?`, 'gi')
146
+ return newString.replace(pattern, '')
147
+ }
148
+
149
+ /**
150
+ * Strip out comment blocks
151
+ * @param {string} str
152
+ * @param {'md' | 'js'} syntax
153
+ * @returns {string} clean commentless string
154
+ */
155
+ function stripComments(str, { syntax = 'md' }) {
156
+ const syntaxData = syntaxMap[syntax]
157
+ const [ openPattern, closePattern ] = syntaxData.pattern
158
+ const OR = (syntaxData.singleLine) ? `|\\s?[ \\t]*${syntaxData.singleLine}` : ''
159
+ const CONTENT = syntaxData.content || '[\\s\\S]*?'
160
+ const pattern = new RegExp(`\\s?[ \\t]*${openPattern}(${CONTENT})?${closePattern}${OR}`, 'gim')
161
+ console.log('pattern', pattern)
162
+ return str.replace(pattern, '')
163
+ // https://regex101.com/r/XKHU18/5
164
+ return str.replace(/\s?[ \t]*\/\*[\s\S]*?\*\/|\s?[ \t]*\/\/.*$|\/\*{1,}[\n\*]*(\s?[\s\S]*?)?\*+\//gm, '')
165
+ }
166
+
167
+ // @TODO export as util to import into CODE
168
+ function stripAllComments(block) {
169
+ // ([^\s]*)?([ \t]*)?(\/\*{1,}[\n\*]*(\s?[\s\S]*?)?\*\/)([^\s<]*)?(\n{1,2})?
170
+ // https://regex101.com/r/WSioZ7/1
171
+ const pattern = new RegExp(`([^\\s]*)?([ \\t]*)?(<!-{2,}(\\s?[\\s\\S]*?)?-{2,}>)([^\\s<]*)?(\n{1,2})?`, 'gi')
172
+ // ALT https://regex101.com/r/hxppia/1
173
+ // Alt HTML comments https://regex101.com/r/EJyioz/1
174
+
175
+ // console.log('closeTagRegex', closeTagRegex)
176
+ let matches
177
+ let remove = []
178
+ while ((matches = pattern.exec(block)) !== null) {
179
+ if (matches.index === pattern.lastIndex) {
180
+ pattern.lastIndex++ // avoid infinite loops with zero-width matches
181
+ }
182
+ const [ match, leadingText, leadingSpace, comment, insideComment, trailingText, trailingNewLine ] = matches
183
+ /*
184
+ console.log('match', match)
185
+ console.log('leadingText', leadingText)
186
+ console.log('leadingSpace', leadingSpace)
187
+ console.log('comment', comment)
188
+ console.log('insideComment', insideComment)
189
+ console.log('trailingText', trailingText)
190
+ console.log('trailingNewLine', trailingNewLine)
191
+ /** */
192
+ const newLineCount = (trailingNewLine || '').length
193
+ const trailing = (!trailingText && newLineCount > 1) ? `${trailingNewLine || ''}` : ''
194
+ const leading = (leadingSpace) ? leadingSpace.slice(1) : ''
195
+ remove.push(`${leading}${comment}${trailing}`)
196
+ }
197
+ return remove.reduce((acc, curr) => {
198
+ return acc.replaceAll(curr, '')
199
+ }, block)
200
+ }
201
+
202
+ function convertCommentSyntax(str, { from, to }) {
203
+ const syntaxData = syntaxMap[from]
204
+ const [ openPattern, closePattern ] = syntaxData.pattern
205
+ const [ openTag, closeTag ] = syntaxMap[to].tags
206
+ // const match = ` *?\\${openPattern}([\\s\\S]*?)?${closePattern}\\n\\n*?`
207
+ const UseLeadingLine = ''// || '|\\s?[ \\t]*'
208
+ const OR = (syntaxData.singleLine) ? `|${UseLeadingLine}${syntaxData.singleLine}` : ''
209
+ const CONTENT = syntaxData.content || '[\\s\\S]*?'
210
+ const match = `${UseLeadingLine}${openPattern}(${CONTENT})?${closePattern}${OR}`
211
+ // const match = `${openPattern}(.*|\\r?|\\n?|\\s*)*${closePattern}`
212
+ const regexToUse = new RegExp(match, 'gm')
213
+ // console.log('regexToUse', regexToUse)
214
+ const found = str.match(regexToUse)
215
+ // console.log('found', found)
216
+ if (!found) {
217
+ return str
218
+ }
219
+
220
+ const pattern = syntaxData.singleLinePattern ? new RegExp(syntaxData.singleLinePattern) : new RegExp(openPattern)
221
+ // console.log('pattern', pattern)
222
+ for (let index = 0; index < found.length; index++) {
223
+ const comment = found[index]
224
+ const cleanComment = comment.replace(pattern, '')
225
+ /*
226
+ console.log('comment', comment)
227
+ console.log('cleanComment', cleanComment)
228
+ /** */
229
+ str = str.replace(comment, `${openTag}${cleanComment} ${closeTag}`)
230
+ }
231
+ return str
232
+
233
+ const newComment = found[0].replace(regexToUse, `${openTag}$1${closeTag}`)
234
+ const converter = syntaxMap[to].converter
235
+ const newText = (converter) ? converter(newComment) : newComment
236
+ return str.replace(regexToUse, newText)
237
+ }
238
+
239
+ module.exports = {
240
+ getLines,
241
+ getLineCount,
242
+ getWordCount,
243
+ getLeadingSpaces,
244
+ getFirstCharacter,
245
+ getLastCharacter,
246
+ getRowAndColumnFromCharPos,
247
+ getTextBetweenChars,
248
+ getTextBetweenLines,
249
+ replaceTextBetweenChars,
250
+ stripIndent,
251
+ indentString,
252
+ dedentString,
253
+ stripComments,
254
+ convertCommentSyntax,
255
+ // stripCommentBlockJS,
256
+ trimString,
257
+ // future https://github.com/junfengliang/autowrap
258
+ findMinIndent,
259
+ isUpperCase
260
+ }