markdown-magic 2.4.0 β 2.6.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.
- package/README.md +40 -1
- package/cli.js +0 -0
- package/lib/index.js +219 -0
- package/lib/processFile.js +2 -1
- package/lib/transforms/file.js +37 -0
- package/lib/transforms/index.js +25 -1
- package/lib/transforms/toc.js +19 -4
- package/lib/updateContents.js +8 -2
- package/lib/utils/_md.test.js +63 -0
- package/lib/utils/new-parser.js +412 -0
- package/lib/utils/new-parser.test.js +324 -0
- package/lib/utils/regex.js +20 -6
- package/lib/utils/weird-parse.js +230 -0
- package/lib/utils/weird-parse.test.js +217 -0
- package/package.json +16 -2
- package/.github/FUNDING.yml +0 -1
- package/.github/workflows/test.yml +0 -40
- package/.travis.yml +0 -10
- package/examples/basic-usage.js +0 -5
- package/examples/generate-readme.js +0 -37
- package/examples/package.json +0 -14
- package/examples/plugin-example.js +0 -16
- package/markdown.config.js +0 -13
- package/test/fixtures/CODE-test.md +0 -21
- package/test/fixtures/CUSTOM-async.md +0 -9
- package/test/fixtures/CUSTOM-test.md +0 -9
- package/test/fixtures/REMOTE-test.md +0 -12
- package/test/fixtures/TOC-test.md +0 -39
- package/test/fixtures/custom-match-word-test.md +0 -9
- package/test/fixtures/local-code-file-lines.js +0 -6
- package/test/fixtures/local-code-file.js +0 -6
- package/test/fixtures/nested/file.md +0 -9
- package/test/main.test.js +0 -293
package/README.md
CHANGED
|
@@ -32,7 +32,9 @@ This `README.md` is generated with `markdown-magic` [view the raw file](https://
|
|
|
32
32
|
- [Transforms](#transforms)
|
|
33
33
|
- [> TOC](#-toc)
|
|
34
34
|
- [> CODE](#-code)
|
|
35
|
+
- [> FILE](#-file)
|
|
35
36
|
- [> REMOTE](#-remote)
|
|
37
|
+
- [Inline transforms](#inline-transforms)
|
|
36
38
|
- [π Markdown magic plugins](#-markdown-magic-plugins)
|
|
37
39
|
- [Adding Custom Transforms](#adding-custom-transforms)
|
|
38
40
|
- [Plugin Example](#plugin-example)
|
|
@@ -189,6 +191,24 @@ Default `MATCHWORD` is `AUTO-GENERATED-CONTENT`
|
|
|
189
191
|
|
|
190
192
|
---
|
|
191
193
|
|
|
194
|
+
### > FILE
|
|
195
|
+
|
|
196
|
+
Get local file contents.
|
|
197
|
+
|
|
198
|
+
**Options:**
|
|
199
|
+
- `src`: The relative path to the file to pull in
|
|
200
|
+
|
|
201
|
+
**Example:**
|
|
202
|
+
```md
|
|
203
|
+
<!-- AUTO-GENERATED-CONTENT:START (FILE:src=./path/to/file) -->
|
|
204
|
+
This content will be dynamically replaced from the local file
|
|
205
|
+
<!-- AUTO-GENERATED-CONTENT:END -->
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Default `MATCHWORD` is `AUTO-GENERATED-CONTENT`
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
192
212
|
### > REMOTE
|
|
193
213
|
|
|
194
214
|
Get any remote Data and put in markdown
|
|
@@ -199,7 +219,7 @@ Get any remote Data and put in markdown
|
|
|
199
219
|
**Example:**
|
|
200
220
|
```md
|
|
201
221
|
<!-- AUTO-GENERATED-CONTENT:START (REMOTE:url=http://url-to-raw-md-file.md) -->
|
|
202
|
-
This content will be dynamically
|
|
222
|
+
This content will be dynamically replaced from the remote url
|
|
203
223
|
<!-- AUTO-GENERATED-CONTENT:END -->
|
|
204
224
|
```
|
|
205
225
|
|
|
@@ -208,6 +228,17 @@ Default `MATCHWORD` is `AUTO-GENERATED-CONTENT`
|
|
|
208
228
|
---
|
|
209
229
|
<!-- βοΈ MD-MAGIC-EXAMPLE:END - Do not remove or modify this section -->
|
|
210
230
|
|
|
231
|
+
## Inline transforms
|
|
232
|
+
|
|
233
|
+
Any transform, including custom transforms can be used inline as well to insert content into paragraphs and other places.
|
|
234
|
+
|
|
235
|
+
The face symbol π <!-- MD-MAGIC-EXAMPLE:START (INLINE_EXAMPLE) -->**βββΏβγ€**<!-- MD-MAGIC-EXAMPLE:END --> is auto generated inline.
|
|
236
|
+
|
|
237
|
+
**Example:**
|
|
238
|
+
```md
|
|
239
|
+
<!-- AUTO-GENERATED-CONTENT:START (FILE:src=./path/to/file) -->xyz<!-- AUTO-GENERATED-CONTENT:END -->
|
|
240
|
+
```
|
|
241
|
+
|
|
211
242
|
## π Markdown magic plugins
|
|
212
243
|
|
|
213
244
|
* [wordcount](https://github.com/DavidWells/markdown-magic-wordcount/) - Add wordcount to markdown files
|
|
@@ -266,6 +297,9 @@ const config = {
|
|
|
266
297
|
})
|
|
267
298
|
return updatedContent.replace(/^\s+|\s+$/g, '')
|
|
268
299
|
},
|
|
300
|
+
INLINE_EXAMPLE: () => {
|
|
301
|
+
return '**βββΏβγ€**'
|
|
302
|
+
},
|
|
269
303
|
/* Match <!-- AUTO-GENERATED-CONTENT:START (pluginExample) --> */
|
|
270
304
|
pluginExample: require('./plugin-example')({ addNewLine: true }),
|
|
271
305
|
/* Include plugins from NPM */
|
|
@@ -359,3 +393,8 @@ This section was generated by the cli config markdown.config.js file
|
|
|
359
393
|
|
|
360
394
|
- [Project using markdown-magic](https://github.com/search?o=desc&q=filename%3Apackage.json+%22markdown-magic%22&s=indexed&type=Code)
|
|
361
395
|
- [Examples in md](https://github.com/search?l=Markdown&o=desc&q=AUTO-GENERATED-CONTENT&s=indexed&type=Code)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
## Misc Markdown helpers
|
|
399
|
+
|
|
400
|
+
- https://github.com/azu/markdown-function
|
package/cli.js
CHANGED
|
File without changes
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const fs = require('fs').promises
|
|
3
|
+
const { getTextBetween, parseBlocks } = require('./utils/new-parser')
|
|
4
|
+
|
|
5
|
+
async function processContents({
|
|
6
|
+
filePath,
|
|
7
|
+
syntax,
|
|
8
|
+
open,
|
|
9
|
+
close,
|
|
10
|
+
transforms,
|
|
11
|
+
beforeMiddelwares,
|
|
12
|
+
afterMiddelwares,
|
|
13
|
+
DEBUG = false
|
|
14
|
+
}) {
|
|
15
|
+
const syntaxType = syntax || path.extname(filePath).replace(/^\./, '')
|
|
16
|
+
const originalContents = await fs.readFile(filePath, 'utf8')
|
|
17
|
+
|
|
18
|
+
const foundBlocks = parseBlocks(originalContents, {
|
|
19
|
+
syntax: syntaxType,
|
|
20
|
+
open,
|
|
21
|
+
close,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
if (DEBUG) {
|
|
25
|
+
console.log('foundBlocks')
|
|
26
|
+
console.log(foundBlocks)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const transformsToRun = sortPlugins(foundBlocks.transforms)
|
|
30
|
+
// console.log('transformsToRun', transformsToRun)
|
|
31
|
+
|
|
32
|
+
// if (!transformsToRun.length) {
|
|
33
|
+
// process.exit(1)
|
|
34
|
+
// }
|
|
35
|
+
|
|
36
|
+
let missingTransforms = []
|
|
37
|
+
const updatedContents = await transformsToRun.reduce(async (updatedContent, ogmatch) => {
|
|
38
|
+
const md = await updatedContent
|
|
39
|
+
/* Apply Before middleware to all transforms */
|
|
40
|
+
const match = await applyMiddleware(ogmatch, md, beforeMiddelwares)
|
|
41
|
+
const { block, raw, transform, args } = match
|
|
42
|
+
const { openTag, closeTag, content, contentStart, contentEnd, start, end } = block
|
|
43
|
+
|
|
44
|
+
/* Run transform plugins */
|
|
45
|
+
let tempContent = content
|
|
46
|
+
if (transforms[transform]) {
|
|
47
|
+
tempContent = await transforms[transform](match, md)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Apply After middleware to all transforms */
|
|
51
|
+
const afterContent = await applyMiddleware({
|
|
52
|
+
...match,
|
|
53
|
+
...{
|
|
54
|
+
block: {
|
|
55
|
+
...match.block,
|
|
56
|
+
content: tempContent
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}, md, afterMiddelwares)
|
|
60
|
+
if (DEBUG) {
|
|
61
|
+
console.log('afterContent', afterContent)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!transforms[transform]) {
|
|
65
|
+
missingTransforms.push(afterContent)
|
|
66
|
+
console.log(`Missing "${transform}" transform`)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const newContent = afterContent.block.content
|
|
70
|
+
const formattedNewContent = (args.noTrim) ? newContent : smartTrim(newContent)
|
|
71
|
+
/* Remove any conflicting imported comments */
|
|
72
|
+
// const fix = removeConflictingComments(formattedNewContent, foundBlocks.commentOpen, foundBlocks.commentClose)
|
|
73
|
+
const fix = stripAllComments(formattedNewContent, foundBlocks.commentOpen, foundBlocks.commentClose)
|
|
74
|
+
// console.log('foundBlocks.commentClose', foundBlocks.commentClose)
|
|
75
|
+
// console.log('formattedNewContent', formattedNewContent)
|
|
76
|
+
// console.log('fix', fix)
|
|
77
|
+
const preserveIndent = (true || args.preserveIndent) ? block.indentation.length + block.contentIndent : block.indentation.length
|
|
78
|
+
const indent = indentString(fix, preserveIndent)
|
|
79
|
+
const newCont = `${openTag}${indent}${closeTag}`
|
|
80
|
+
/* Replace original contents */
|
|
81
|
+
const newContents = md.replace(raw.block, newCont)
|
|
82
|
+
return Promise.resolve(newContents)
|
|
83
|
+
}, Promise.resolve(originalContents))
|
|
84
|
+
|
|
85
|
+
if (DEBUG) {
|
|
86
|
+
console.log('Output Markdown')
|
|
87
|
+
console.log(updatedContents)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/*
|
|
91
|
+
if (missingTransforms.length) {
|
|
92
|
+
console.log('missingTransforms', missingTransforms)
|
|
93
|
+
let matchOne = missingTransforms[1]
|
|
94
|
+
matchOne = missingTransforms[missingTransforms.length - 1]
|
|
95
|
+
// console.log('matchOne', matchOne)
|
|
96
|
+
const { block, transform, args } = matchOne
|
|
97
|
+
const { openTag, closeTag, content, contentStart, contentEnd, start, end} = block
|
|
98
|
+
|
|
99
|
+
// console.log('contentStart', contentStart)
|
|
100
|
+
// console.log('contentEnd', contentEnd)
|
|
101
|
+
// console.log('original text between', `"${getTextBetween(md, contentStart, contentEnd)}"`)
|
|
102
|
+
// console.log('original block between', `"${getTextBetween(md, start, end)}"`)
|
|
103
|
+
}
|
|
104
|
+
/** */
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
filePath,
|
|
108
|
+
transforms: transformsToRun,
|
|
109
|
+
missingTransforms,
|
|
110
|
+
originalContents,
|
|
111
|
+
updatedContents
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function applyMiddleware(data, md, middlewares) {
|
|
116
|
+
return middlewares.reduce(async (acc, curr) => {
|
|
117
|
+
const realAcc = await acc
|
|
118
|
+
// console.log(`Running "${curr.name}" Middleware on "${realAcc.transform}" block`)
|
|
119
|
+
const updatedContent = await curr.transform(realAcc, md)
|
|
120
|
+
// realAcc.block.content = updatedContent
|
|
121
|
+
return Promise.resolve({
|
|
122
|
+
...realAcc,
|
|
123
|
+
...{
|
|
124
|
+
block: {
|
|
125
|
+
...realAcc.block,
|
|
126
|
+
content: updatedContent
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
}, Promise.resolve(data))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Trim leading & trailing spaces/line breaks in code and keeps the indentation of the first non-empty line
|
|
135
|
+
* @param {string} str
|
|
136
|
+
* @returns string
|
|
137
|
+
*/
|
|
138
|
+
function smartTrim(str) {
|
|
139
|
+
return str.replace(/^(?:[\t ]*(?:\r?\n|\r))+|\s+$/g, '')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function stripAllComments(block) {
|
|
143
|
+
const pattern = new RegExp(`([^\\s]*)?([ \\t]*)?(<!-+\\s?([\\s\\S]*?)?-+>)([^\\s<]*)?(\n{1,2})?`, 'gi')
|
|
144
|
+
|
|
145
|
+
// console.log('closeTagRegex', closeTagRegex)
|
|
146
|
+
let matches
|
|
147
|
+
let remove = []
|
|
148
|
+
while ((matches = pattern.exec(block)) !== null) {
|
|
149
|
+
if (matches.index === pattern.lastIndex) {
|
|
150
|
+
pattern.lastIndex++ // avoid infinite loops with zero-width matches
|
|
151
|
+
}
|
|
152
|
+
const [ match, leadingText, leadingSpace, comment, insideComment, trailingText, trailingNewLine ] = matches
|
|
153
|
+
/*
|
|
154
|
+
console.log('match', match)
|
|
155
|
+
console.log('leadingText', leadingText)
|
|
156
|
+
console.log('leadingSpace', leadingSpace)
|
|
157
|
+
console.log('comment', comment)
|
|
158
|
+
console.log('insideComment', insideComment)
|
|
159
|
+
console.log('trailingText', trailingText)
|
|
160
|
+
console.log('trailingNewLine', trailingNewLine)
|
|
161
|
+
/** */
|
|
162
|
+
const newLineCount = (trailingNewLine || '').length
|
|
163
|
+
const trailing = (!trailingText && newLineCount > 1) ? `${trailingNewLine || ''}` : ''
|
|
164
|
+
const leading = (leadingSpace) ? leadingSpace.slice(1) : ''
|
|
165
|
+
remove.push(`${leading}${comment}${trailing}`)
|
|
166
|
+
}
|
|
167
|
+
return remove.reduce((acc, curr) => {
|
|
168
|
+
return acc.replaceAll(curr, '')
|
|
169
|
+
}, block)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Remove conflicting comments that might have been inserted from transforms
|
|
174
|
+
* @param {*} content
|
|
175
|
+
* @param {*} openPattern
|
|
176
|
+
* @param {*} closePattern
|
|
177
|
+
* @returns
|
|
178
|
+
*/
|
|
179
|
+
function removeConflictingComments(content, openPattern, closePattern) {
|
|
180
|
+
const removeOpen = content.replace(openPattern, '')
|
|
181
|
+
// TODO this probably needs to be a loop for larger blocks
|
|
182
|
+
closePattern.lastIndex = 0; // reset regex
|
|
183
|
+
const hasClose = closePattern.exec(content)
|
|
184
|
+
// console.log('closePattern', closePattern)
|
|
185
|
+
// console.log('has', content)
|
|
186
|
+
// console.log('hasClose', hasClose)
|
|
187
|
+
if (!hasClose) {
|
|
188
|
+
return removeOpen
|
|
189
|
+
}
|
|
190
|
+
const closeTag = `${hasClose[2]}${hasClose[3] || ''}`
|
|
191
|
+
// console.log('closeTag', closeTag)
|
|
192
|
+
return removeOpen
|
|
193
|
+
.replaceAll(closeTag, '')
|
|
194
|
+
/* Trailing new line */
|
|
195
|
+
.replace(/\n$/, '')
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function sortPlugins(data) {
|
|
199
|
+
return data.sort((a, b) => {
|
|
200
|
+
// put table of contents (TOC) at end of tranforms
|
|
201
|
+
if (a.transform === 'TOC') return 1
|
|
202
|
+
if (b.transform === 'TOC') return -1
|
|
203
|
+
return 0
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function indentString(string, count = 1, options = {}) {
|
|
208
|
+
const {
|
|
209
|
+
indent = ' ',
|
|
210
|
+
includeEmptyLines = false
|
|
211
|
+
} = options;
|
|
212
|
+
if (count === 0) return string
|
|
213
|
+
const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm
|
|
214
|
+
return string.replace(regex, indent.repeat(count))
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = {
|
|
218
|
+
processContents
|
|
219
|
+
}
|
package/lib/processFile.js
CHANGED
|
@@ -38,7 +38,7 @@ module.exports = async function processFile(filePath, config) {
|
|
|
38
38
|
matchWord: 'AUTO-GENERATED-CONTENT',
|
|
39
39
|
/**
|
|
40
40
|
* - `DEBUG` - *Boolean* - (optional) set debug flag to `true` to inspect the process
|
|
41
|
-
* @type {
|
|
41
|
+
* @type {boolean}
|
|
42
42
|
*/
|
|
43
43
|
DEBUG: false,
|
|
44
44
|
}
|
|
@@ -55,6 +55,7 @@ module.exports = async function processFile(filePath, config) {
|
|
|
55
55
|
mergedConfig.outputContent = content
|
|
56
56
|
|
|
57
57
|
const regex = regexUtils.matchCommentBlock(mergedConfig.matchWord)
|
|
58
|
+
// console.log(regex)
|
|
58
59
|
const match = content.match(regex)
|
|
59
60
|
const transformsFound = []
|
|
60
61
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const isLocalPath = require('is-local-path')
|
|
4
|
+
|
|
5
|
+
module.exports = function FILE(content, options, config) {
|
|
6
|
+
let fileContents
|
|
7
|
+
let syntax = options.syntax
|
|
8
|
+
if (!options.src) {
|
|
9
|
+
return false
|
|
10
|
+
}
|
|
11
|
+
if (isLocalPath(options.src)) {
|
|
12
|
+
const fileDir = path.dirname(config.originalPath)
|
|
13
|
+
const filePath = path.join(fileDir, options.src)
|
|
14
|
+
try {
|
|
15
|
+
fileContents = fs.readFileSync(filePath, 'utf8', (err, contents) => {
|
|
16
|
+
if (err) {
|
|
17
|
+
console.log(`FILE NOT FOUND ${filePath}`)
|
|
18
|
+
console.log(err)
|
|
19
|
+
// throw err
|
|
20
|
+
}
|
|
21
|
+
return contents
|
|
22
|
+
})
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.log(`FILE NOT FOUND ${filePath}`)
|
|
25
|
+
throw e
|
|
26
|
+
}
|
|
27
|
+
if (!syntax) {
|
|
28
|
+
syntax = path.extname(filePath).replace(/^./, '')
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// trim leading and trailing spaces/line breaks in code and keeps the indentation of the first non-empty line
|
|
33
|
+
fileContents = fileContents.replace(/^(?:[\t ]*(?:\r?\n|\r))+|\s+$/g, '')
|
|
34
|
+
|
|
35
|
+
return `<!-- The below content is automatically added from ${options.src} -->
|
|
36
|
+
${fileContents}`
|
|
37
|
+
}
|
package/lib/transforms/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const code = require('./code')
|
|
2
|
+
const file = require('./file')
|
|
2
3
|
const remoteContent = require('./remote')
|
|
3
4
|
const toc = require('./toc')
|
|
4
5
|
|
|
@@ -62,6 +63,29 @@ const transforms = {
|
|
|
62
63
|
* @return {string} Updated inner contents of the comment block
|
|
63
64
|
*/
|
|
64
65
|
CODE: code,
|
|
66
|
+
/**
|
|
67
|
+
* ### > FILE
|
|
68
|
+
*
|
|
69
|
+
* Get local file contents.
|
|
70
|
+
*
|
|
71
|
+
* **Options:**
|
|
72
|
+
* - `src`: The relative path to the file to pull in
|
|
73
|
+
*
|
|
74
|
+
* **Example:**
|
|
75
|
+
* ```md
|
|
76
|
+
* <!-- AUTO-GENERATED-CONTENT:START (FILE:src=./path/to/file) -->
|
|
77
|
+
* This content will be dynamically replaced from the local file
|
|
78
|
+
* <!-- AUTO-GENERATED-CONTENT:END -->
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* Default `MATCHWORD` is `AUTO-GENERATED-CONTENT`
|
|
82
|
+
*
|
|
83
|
+
* ---
|
|
84
|
+
* @param {string} content The current content of the comment block
|
|
85
|
+
* @param {object} options The options passed in from the comment declaration
|
|
86
|
+
* @return {string} Updated content to place in the content block
|
|
87
|
+
*/
|
|
88
|
+
FILE: file,
|
|
65
89
|
/**
|
|
66
90
|
* ### > REMOTE
|
|
67
91
|
*
|
|
@@ -73,7 +97,7 @@ const transforms = {
|
|
|
73
97
|
* **Example:**
|
|
74
98
|
* ```md
|
|
75
99
|
* <!-- AUTO-GENERATED-CONTENT:START (REMOTE:url=http://url-to-raw-md-file.md) -->
|
|
76
|
-
* This content will be dynamically
|
|
100
|
+
* This content will be dynamically replaced from the remote url
|
|
77
101
|
* <!-- AUTO-GENERATED-CONTENT:END -->
|
|
78
102
|
* ```
|
|
79
103
|
*
|
package/lib/transforms/toc.js
CHANGED
|
@@ -66,13 +66,14 @@ module.exports = async function TOC(content, options, config) {
|
|
|
66
66
|
const [ lineText, lineIndex, isFirst, matchText ] = firstHeadingData
|
|
67
67
|
|
|
68
68
|
// verify its h1
|
|
69
|
-
|
|
69
|
+
const matchTextEscaped = escapeStringRegexp(matchText)
|
|
70
|
+
// console.log('matchTextEscaped', matchTextEscaped)
|
|
70
71
|
// /^#{1}\s+(.*)/
|
|
71
|
-
const FIRST_H1_REGEX = new RegExp(`^#\\s
|
|
72
|
+
const FIRST_H1_REGEX = new RegExp(`^#\\s*\\[?${matchTextEscaped}\\]?(?:.*)?`, 'gim')
|
|
72
73
|
// /^<h1\b[^>]*>([\s\S]*?)<\/h1>/
|
|
73
|
-
const FIRST_HTML_H1_REGEX = new RegExp(`^<h1\\b[^>]*>[\\s]*?(${
|
|
74
|
+
const FIRST_HTML_H1_REGEX = new RegExp(`^<h1\\b[^>]*>[\\s]*?(${matchTextEscaped})[\\s]*?<\\/h1>`, 'gim')
|
|
74
75
|
// /^(.*)\n={3,}/
|
|
75
|
-
const FIRST_UNDERSCORE_H1 = new RegExp(`^(${
|
|
76
|
+
const FIRST_UNDERSCORE_H1 = new RegExp(`^(${matchTextEscaped})\n={3,}`, 'gim')
|
|
76
77
|
|
|
77
78
|
let docHasHeading = false
|
|
78
79
|
if (contents.match(FIRST_H1_REGEX)) {
|
|
@@ -245,7 +246,21 @@ function parseHtmlProps(mdContents) {
|
|
|
245
246
|
return tags
|
|
246
247
|
}
|
|
247
248
|
|
|
249
|
+
function escapeStringRegexp(string) {
|
|
250
|
+
if (typeof string !== 'string') {
|
|
251
|
+
throw new TypeError('Expected a string');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Escape characters with special meaning either inside or outside character sets.
|
|
255
|
+
// 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.
|
|
256
|
+
return string
|
|
257
|
+
.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
|
|
258
|
+
.replace(/-/g, '\\x2d');
|
|
259
|
+
}
|
|
260
|
+
|
|
248
261
|
function removeUndefined(output) {
|
|
249
262
|
// remove undefined from new line and start of string if first H1 is missing
|
|
250
263
|
return output.replace(/\nundefined/g, '\n-').replace(/^undefined/g, '-')
|
|
251
264
|
}
|
|
265
|
+
|
|
266
|
+
// Alt https://github.com/thlorenz/doctoc
|
package/lib/updateContents.js
CHANGED
|
@@ -42,12 +42,18 @@ module.exports = async function updateContents(block, config) {
|
|
|
42
42
|
if (!newContent) {
|
|
43
43
|
newContent = originalContent
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
|
|
46
|
+
// If original block or new content contains a new line, preserve it
|
|
47
|
+
if (block.indexOf('\n') > -1 || newContent.indexOf('\n') > -1) {
|
|
48
|
+
return `${openingTag.openTag}
|
|
47
49
|
${newContent}
|
|
48
50
|
${closingTag.closeTag}`
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
// Block has no newline, inline contents
|
|
54
|
+
return `${openingTag.openTag}${newContent}${closingTag.closeTag}`
|
|
55
|
+
}
|
|
56
|
+
|
|
51
57
|
function parseOptions(options) {
|
|
52
58
|
if (!options) {
|
|
53
59
|
return null
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const { test } = require('uvu')
|
|
2
|
+
const assert = require('uvu/assert')
|
|
3
|
+
const { parseBlocks, replaceContent } = require('./new-parser')
|
|
4
|
+
|
|
5
|
+
const defaultOpts = {
|
|
6
|
+
syntax: 'md',
|
|
7
|
+
open: 'DOCS:START',
|
|
8
|
+
close: 'DOCS:END',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
test('Returns empty array', () => {
|
|
12
|
+
assert.equal(parseBlocks('', defaultOpts).transforms, [])
|
|
13
|
+
assert.equal(parseBlocks(' ', defaultOpts).transforms, [])
|
|
14
|
+
assert.equal(parseBlocks(`
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
`, defaultOpts).transforms, [])
|
|
18
|
+
assert.equal(parseBlocks(`
|
|
19
|
+
# No block in here
|
|
20
|
+
|
|
21
|
+
nope
|
|
22
|
+
`, defaultOpts).transforms, [])
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const md = `
|
|
26
|
+
Very nice
|
|
27
|
+
|
|
28
|
+
<!-- DOCS:START(TOC) foo={{ rad: 'orange' }} ------>
|
|
29
|
+
ok
|
|
30
|
+
<!-- DOCS:END -->`
|
|
31
|
+
|
|
32
|
+
test('Parse md blocks', () => {
|
|
33
|
+
const parsedValue = parseBlocks(md, defaultOpts)
|
|
34
|
+
console.log('parsedValue', parsedValue)
|
|
35
|
+
assert.equal(parsedValue.transforms, [
|
|
36
|
+
{
|
|
37
|
+
transform: 'TOC',
|
|
38
|
+
args: { foo: { rad: 'orange' } },
|
|
39
|
+
block: {
|
|
40
|
+
indentation: '',
|
|
41
|
+
start: 12,
|
|
42
|
+
end: 85,
|
|
43
|
+
contentStart: 64,
|
|
44
|
+
contentEnd: 68,
|
|
45
|
+
contentIndent: 0,
|
|
46
|
+
openTag: "<!-- DOCS:START(TOC) foo={{ rad: 'orange' }} ------>\n",
|
|
47
|
+
content: 'ok',
|
|
48
|
+
closeTag: '\n<!-- DOCS:END -->'
|
|
49
|
+
},
|
|
50
|
+
raw: {
|
|
51
|
+
transform: '(TOC)',
|
|
52
|
+
args: "foo={{ rad: 'orange' }} ----",
|
|
53
|
+
content: '\nok\n',
|
|
54
|
+
block: "<!-- DOCS:START(TOC) foo={{ rad: 'orange' }} ------>\n" +
|
|
55
|
+
'ok\n' +
|
|
56
|
+
'<!-- DOCS:END -->'
|
|
57
|
+
},
|
|
58
|
+
meta: { isMultiline: true }
|
|
59
|
+
}
|
|
60
|
+
], '')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test.run()
|