markdown-magic 3.0.0 → 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.
- package/README.md +43 -29
- package/lib/block-parser-js.test.js +148 -156
- package/lib/block-parser.js +255 -262
- package/lib/block-parser.test.js +43 -6
- package/lib/cli.js +30 -19
- package/lib/cli.test.js +73 -73
- package/lib/globals.d.ts +66 -0
- package/lib/index.js +43 -9
- package/lib/process-contents.js +80 -39
- package/lib/process-file.js +4 -1
- package/lib/transforms/code.js +4 -10
- package/lib/transforms/file.js +7 -10
- package/lib/transforms/index.js +0 -0
- package/lib/transforms/remote.js +2 -3
- package/lib/transforms/sectionToc.js +18 -0
- package/lib/transforms/toc.js +10 -335
- package/lib/types.js +11 -0
- package/lib/utils/fs.js +21 -19
- package/lib/utils/fs.test.js +4 -5
- package/lib/utils/logs.js +7 -2
- package/lib/utils/md/filters.js +5 -5
- package/lib/utils/md/find-code-blocks.js +16 -8
- package/lib/utils/md/find-frontmatter.js +11 -13
- package/lib/utils/md/find-frontmatter.test.js +2 -2
- package/lib/utils/md/find-html-tags.js +1 -1
- package/lib/utils/md/find-images-md.js +27 -0
- package/lib/utils/md/find-images.js +39 -34
- package/lib/utils/md/find-links.js +72 -54
- package/lib/utils/md/find-unmatched-html-tags.js +1 -2
- package/lib/utils/md/fixtures/file-with-links.md +10 -0
- package/lib/utils/md/md.test.js +72 -4
- package/lib/utils/md/parse.js +91 -67
- package/lib/utils/regex-timeout.js +2 -1
- package/lib/utils/regex.js +3 -2
- package/lib/utils/remoteRequest.js +1 -0
- package/lib/utils/syntax.js +3 -0
- package/lib/utils/text.js +71 -3
- package/lib/utils/text.test.js +3 -9
- package/lib/utils/toc.js +315 -0
- package/package.json +7 -3
- package/lib/options-parser.js +0 -498
- package/lib/options-parser.test.js +0 -1237
- package/lib/utils/html-to-json/compat.js +0 -42
- package/lib/utils/html-to-json/format.js +0 -64
- package/lib/utils/html-to-json/index.js +0 -37
- package/lib/utils/html-to-json/lexer.js +0 -345
- package/lib/utils/html-to-json/parser.js +0 -146
- package/lib/utils/html-to-json/stringify.js +0 -37
- package/lib/utils/html-to-json/tags.js +0 -171
package/lib/process-contents.js
CHANGED
|
@@ -23,6 +23,13 @@ const { OPEN_WORD, CLOSE_WORD } = require('./defaults')
|
|
|
23
23
|
debug = false,
|
|
24
24
|
} = config
|
|
25
25
|
|
|
26
|
+
/*
|
|
27
|
+
console.log('OPEN_WORD', open)
|
|
28
|
+
console.log('CLOSE_WORD', close)
|
|
29
|
+
console.log('syntax', syntax)
|
|
30
|
+
// console.log('text', text)
|
|
31
|
+
/** */
|
|
32
|
+
|
|
26
33
|
let foundBlocks = {}
|
|
27
34
|
try {
|
|
28
35
|
foundBlocks = parseBlocks(text, {
|
|
@@ -86,9 +93,9 @@ const { OPEN_WORD, CLOSE_WORD } = require('./defaults')
|
|
|
86
93
|
/* Run transform plugins */
|
|
87
94
|
let tempContent = content.value
|
|
88
95
|
// console.log('transform', transform)
|
|
89
|
-
const
|
|
96
|
+
const currentTransformFn = getTransform(transform, transforms)
|
|
90
97
|
/* Run each transform */
|
|
91
|
-
if (
|
|
98
|
+
if (currentTransformFn) {
|
|
92
99
|
// console.log('context', context)
|
|
93
100
|
let returnedContent
|
|
94
101
|
/* DISABLED legacy syntax */
|
|
@@ -97,11 +104,11 @@ const { OPEN_WORD, CLOSE_WORD } = require('./defaults')
|
|
|
97
104
|
console.log(`CALL legacy ${transform}`, srcPath)
|
|
98
105
|
// backward compat maybe
|
|
99
106
|
// CODE(content, options, config)
|
|
100
|
-
returnedContent = await
|
|
107
|
+
returnedContent = await currentTransformFn(content.value, options, {
|
|
101
108
|
originalPath: srcPath
|
|
102
109
|
})
|
|
103
110
|
} else {
|
|
104
|
-
returnedContent = await
|
|
111
|
+
returnedContent = await currentTransformFn(transformApi({
|
|
105
112
|
srcPath,
|
|
106
113
|
...match,
|
|
107
114
|
regex: regexInfo,
|
|
@@ -111,13 +118,15 @@ const { OPEN_WORD, CLOSE_WORD } = require('./defaults')
|
|
|
111
118
|
}
|
|
112
119
|
/** */
|
|
113
120
|
|
|
114
|
-
returnedContent = await
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
returnedContent = await currentTransformFn(
|
|
122
|
+
transformApi({
|
|
123
|
+
srcPath,
|
|
124
|
+
...match,
|
|
125
|
+
regex: regexInfo,
|
|
126
|
+
originalContents: text,
|
|
127
|
+
currentContents: md,
|
|
128
|
+
}, config)
|
|
129
|
+
)
|
|
121
130
|
|
|
122
131
|
/* Run each transform */
|
|
123
132
|
// console.log('config', config)
|
|
@@ -143,7 +152,7 @@ const { OPEN_WORD, CLOSE_WORD } = require('./defaults')
|
|
|
143
152
|
console.log('afterContent', afterContent)
|
|
144
153
|
}
|
|
145
154
|
|
|
146
|
-
if (!
|
|
155
|
+
if (!currentTransformFn) {
|
|
147
156
|
missingTransforms.push(afterContent)
|
|
148
157
|
// console.log(`Missing "${transform}" transform`)
|
|
149
158
|
}
|
|
@@ -211,48 +220,74 @@ const { OPEN_WORD, CLOSE_WORD } = require('./defaults')
|
|
|
211
220
|
function transformApi(stuff, opts) {
|
|
212
221
|
const { transforms, srcPath, outputPath, ...rest } = opts
|
|
213
222
|
return {
|
|
214
|
-
srcPath: stuff.srcPath,
|
|
215
|
-
outputPath: outputPath,
|
|
216
223
|
transform: stuff.transform,
|
|
224
|
+
content: stuff.content.value,
|
|
217
225
|
options: stuff.options || {},
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
originalFileContent: stuff.originalContents,
|
|
226
|
+
srcPath: stuff.srcPath,
|
|
227
|
+
outputPath: outputPath,
|
|
221
228
|
settings: {
|
|
222
229
|
...rest,
|
|
223
230
|
regex: stuff.regex,
|
|
224
231
|
},
|
|
232
|
+
|
|
233
|
+
// blockContent: stuff.content.value,
|
|
234
|
+
fileContent: stuff.currentContents,
|
|
235
|
+
originalFileContent: stuff.originalContents,
|
|
236
|
+
/* Methods */
|
|
225
237
|
getOriginalContent: () => stuff.originalContents,
|
|
226
238
|
getOriginalBlock: () => stuff,
|
|
227
|
-
|
|
239
|
+
getBlockDetails: (content) => {
|
|
228
240
|
/* Re-parse current file for updated positions */
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const matchingBlocks = updatedBlocks.blocks.filter((block) => {
|
|
237
|
-
return block.open.value === stuff.open.value
|
|
241
|
+
return getDetails({
|
|
242
|
+
contents: content || stuff.currentContents,
|
|
243
|
+
openValue: stuff.open.value,
|
|
244
|
+
srcPath: stuff.srcPath,
|
|
245
|
+
index: stuff.index,
|
|
246
|
+
opts: opts
|
|
238
247
|
})
|
|
248
|
+
},
|
|
249
|
+
// getOriginalBlockDetails: () => {
|
|
250
|
+
// return getDetails({
|
|
251
|
+
// contents: stuff.originalContents,
|
|
252
|
+
// openValue: stuff.open.value,
|
|
253
|
+
// srcPath: stuff.srcPath,
|
|
254
|
+
// index: stuff.index,
|
|
255
|
+
// opts: opts
|
|
256
|
+
// })
|
|
257
|
+
// },
|
|
258
|
+
}
|
|
259
|
+
}
|
|
239
260
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
261
|
+
function getDetails({
|
|
262
|
+
contents,
|
|
263
|
+
openValue,
|
|
264
|
+
srcPath,
|
|
265
|
+
index,
|
|
266
|
+
opts
|
|
267
|
+
}) {
|
|
268
|
+
/* Re-parse current file for updated positions */
|
|
269
|
+
const blockData = parseBlocks(contents, opts)
|
|
270
|
+
|
|
271
|
+
const matchingBlocks = blockData.blocks.filter((block) => {
|
|
272
|
+
return block.open.value === openValue
|
|
273
|
+
})
|
|
244
274
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
})[0]
|
|
249
|
-
}
|
|
275
|
+
if (!matchingBlocks.length) {
|
|
276
|
+
return {}
|
|
277
|
+
}
|
|
250
278
|
|
|
251
|
-
|
|
279
|
+
let foundBlock = matchingBlocks[0]
|
|
280
|
+
if (matchingBlocks.length > 1 && index) {
|
|
281
|
+
foundBlock = matchingBlocks.filter((block) => {
|
|
282
|
+
return block.index === index
|
|
283
|
+
})[0]
|
|
284
|
+
}
|
|
252
285
|
|
|
253
|
-
|
|
254
|
-
|
|
286
|
+
if (srcPath) {
|
|
287
|
+
const location = getCodeLocation(srcPath, foundBlock.block.lines[0])
|
|
288
|
+
foundBlock.sourceLocation = location
|
|
255
289
|
}
|
|
290
|
+
return foundBlock
|
|
256
291
|
}
|
|
257
292
|
|
|
258
293
|
/**
|
|
@@ -299,6 +334,12 @@ function applyMiddleware(data, md, middlewares) {
|
|
|
299
334
|
}, Promise.resolve(data))
|
|
300
335
|
}
|
|
301
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Get Transform function
|
|
339
|
+
* @param {string} name - transform name
|
|
340
|
+
* @param {object} transforms - transform fns
|
|
341
|
+
* @returns {function}
|
|
342
|
+
*/
|
|
302
343
|
function getTransform(name, transforms = {}) {
|
|
303
344
|
return transforms[name] || transforms[name.toLowerCase()]
|
|
304
345
|
}
|
package/lib/process-file.js
CHANGED
|
@@ -2,11 +2,14 @@ const path = require('path')
|
|
|
2
2
|
const isValidFile = require('is-valid-path')
|
|
3
3
|
const { readFile } = require('./utils/fs')
|
|
4
4
|
const { processContents } = require('./process-contents')
|
|
5
|
+
// const { OPEN_WORD, CLOSE_WORD } = require('./defaults')
|
|
5
6
|
|
|
6
7
|
async function processFile(opts = {}) {
|
|
7
8
|
const { content, syntax } = opts
|
|
8
9
|
let srcPath = opts.srcPath
|
|
9
|
-
if (srcPath && content)
|
|
10
|
+
if (srcPath && content) {
|
|
11
|
+
throw new Error(`Can't set both "srcPath" & "content"`)
|
|
12
|
+
}
|
|
10
13
|
let fileContents
|
|
11
14
|
if (content) {
|
|
12
15
|
const isFile = isValidFile(content)
|
package/lib/transforms/code.js
CHANGED
|
@@ -11,11 +11,12 @@ const { getLineCount, getTextBetweenLines } = require('../utils/text')
|
|
|
11
11
|
// usage https://github.com/linear/linear/blame/93981d3a3db571e2f8efdce9f5271ea678941c43/packages/sdk/README.md#L1
|
|
12
12
|
|
|
13
13
|
module.exports = function CODE(api) {
|
|
14
|
-
const {
|
|
14
|
+
const { content, options, srcPath } = api
|
|
15
15
|
// console.log('CODE API', api)
|
|
16
|
+
// process.exit(1)
|
|
16
17
|
// console.log('options', options)
|
|
17
18
|
const { src, lines } = options
|
|
18
|
-
const originalContent =
|
|
19
|
+
const originalContent = content
|
|
19
20
|
let code
|
|
20
21
|
let syntax = options.syntax
|
|
21
22
|
if (!src) {
|
|
@@ -27,14 +28,7 @@ module.exports = function CODE(api) {
|
|
|
27
28
|
const fileDir = (srcPath) ? path.dirname(srcPath) : process.cwd()
|
|
28
29
|
codeFilePath = path.resolve(fileDir, src)
|
|
29
30
|
try {
|
|
30
|
-
code = fs.readFileSync(codeFilePath, 'utf8'
|
|
31
|
-
if (err) {
|
|
32
|
-
console.log(`FILE NOT FOUND ${codeFilePath}`)
|
|
33
|
-
console.log(err)
|
|
34
|
-
// throw err
|
|
35
|
-
}
|
|
36
|
-
return contents
|
|
37
|
-
})
|
|
31
|
+
code = fs.readFileSync(codeFilePath, 'utf8')
|
|
38
32
|
} catch (e) {
|
|
39
33
|
console.log(`FILE NOT FOUND ${codeFilePath}`)
|
|
40
34
|
throw e
|
package/lib/transforms/file.js
CHANGED
|
@@ -3,24 +3,21 @@ const fs = require('fs')
|
|
|
3
3
|
const isLocalPath = require('is-local-path')
|
|
4
4
|
|
|
5
5
|
module.exports = function FILE(api) {
|
|
6
|
+
/*
|
|
7
|
+
console.log('FILE API', api)
|
|
8
|
+
console.log(api.getBlockDetails())
|
|
9
|
+
process.exit(1)
|
|
10
|
+
/** */
|
|
6
11
|
const { options, srcPath } = api
|
|
7
|
-
// console.log('FILE API', api)
|
|
8
|
-
let fileContents
|
|
9
12
|
if (!options.src) {
|
|
10
13
|
return false
|
|
11
14
|
}
|
|
15
|
+
let fileContents = ''
|
|
12
16
|
if (isLocalPath(options.src)) {
|
|
13
17
|
const fileDir = path.dirname(srcPath)
|
|
14
18
|
const resolvedFilePath = path.resolve(fileDir, options.src)
|
|
15
19
|
try {
|
|
16
|
-
fileContents = fs.readFileSync(resolvedFilePath, 'utf8'
|
|
17
|
-
if (err) {
|
|
18
|
-
console.log(`FILE NOT FOUND ${resolvedFilePath}`)
|
|
19
|
-
console.log(err)
|
|
20
|
-
// throw err
|
|
21
|
-
}
|
|
22
|
-
return contents
|
|
23
|
-
})
|
|
20
|
+
fileContents = fs.readFileSync(resolvedFilePath, 'utf8')
|
|
24
21
|
} catch (e) {
|
|
25
22
|
console.log(`FILE NOT FOUND ${resolvedFilePath}`)
|
|
26
23
|
throw e
|
|
File without changes
|
package/lib/transforms/remote.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
const regexUtils = require('../utils/regex')
|
|
2
1
|
const remoteRequest = require('../utils/remoteRequest')
|
|
3
2
|
|
|
4
3
|
module.exports = function REMOTE(api) {
|
|
5
4
|
// console.log('REMOTE api', api)
|
|
6
|
-
const { options,
|
|
5
|
+
const { options, content, settings } = api
|
|
7
6
|
const { regex } = settings
|
|
8
7
|
// console.log('MAKE REMOTE REQUEST')
|
|
9
8
|
const remoteContent = remoteRequest(options.url)
|
|
10
9
|
if (!remoteContent) {
|
|
11
|
-
return
|
|
10
|
+
return content
|
|
12
11
|
}
|
|
13
12
|
if (options.keepComments) {
|
|
14
13
|
return remoteContent
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { generateToc } = require('../utils/toc')
|
|
2
|
+
|
|
3
|
+
module.exports = async function sectionToc(api) {
|
|
4
|
+
// console.log("sectionToc", api)
|
|
5
|
+
const { options, fileContent, srcPath, getBlockDetails } = api
|
|
6
|
+
const opts = options || {}
|
|
7
|
+
const subToc = await generateToc({
|
|
8
|
+
options: {
|
|
9
|
+
...opts,
|
|
10
|
+
// Set sub table of contents
|
|
11
|
+
sub: true
|
|
12
|
+
},
|
|
13
|
+
fileContent,
|
|
14
|
+
srcPath,
|
|
15
|
+
getBlockDetails: getBlockDetails
|
|
16
|
+
})
|
|
17
|
+
return subToc
|
|
18
|
+
}
|
package/lib/transforms/toc.js
CHANGED
|
@@ -1,338 +1,13 @@
|
|
|
1
|
-
const {
|
|
2
|
-
const { removeLeadingAndTrailingLineBreaks, escapeRegexString } = require('../utils/regex')
|
|
3
|
-
const { findMinIndent } = require('../utils/text')
|
|
1
|
+
const { generateToc } = require('../utils/toc')
|
|
4
2
|
|
|
5
3
|
module.exports = async function TOC(api) {
|
|
6
4
|
// console.log("TOC API", api)
|
|
7
|
-
const { options, fileContent,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
let contents = fileContent
|
|
17
|
-
// console.log('contents', contents)
|
|
18
|
-
|
|
19
|
-
let debugFileMatch
|
|
20
|
-
// console.log('config', config.originalPath)
|
|
21
|
-
// debugFileMatch = config.originalPath.match(/packages\/analytics\/README\.md/)
|
|
22
|
-
|
|
23
|
-
// https://www.npmjs.com/package/@technote-space/toc-generator
|
|
24
|
-
const tocOptions = {
|
|
25
|
-
// mode: 'github.com', // github.com | bitbucket.org | gitlab.com | nodejs.org | ghost.org (default: github.com)
|
|
26
|
-
isNotitle: true,
|
|
27
|
-
//isCustomMode: true,
|
|
28
|
-
openingComment: '',
|
|
29
|
-
closingComment: '',
|
|
30
|
-
maxHeaderLevel: (opts.maxDepth) ? Number(opts.maxDepth) : 4,
|
|
31
|
-
// maxHeaderLevel: 2, // default: 4
|
|
32
|
-
// title: '**Table of Contents**',
|
|
33
|
-
// isNotitle: true,
|
|
34
|
-
// isFolding: true,
|
|
35
|
-
// entryPrefix: '*',
|
|
36
|
-
// processAll: true,
|
|
37
|
-
// updateOnly: true,
|
|
38
|
-
// openingComment: '<!-- toc -->',
|
|
39
|
-
// closingComment: '<!-- tocstop --> ',
|
|
40
|
-
// checkOpeningComments: ['<!-- toc '],
|
|
41
|
-
// checkClosingComments: ['<!-- tocstop '],
|
|
42
|
-
// isCustomMode: false,
|
|
43
|
-
// customTemplate: '<p align="center">${ITEMS}</p>',
|
|
44
|
-
// itemTemplate: '<a href="${LINK}">${TEXT}</a>',
|
|
45
|
-
// separator: '<span>|</span>',
|
|
46
|
-
// footer: 'end',
|
|
47
|
-
}
|
|
48
|
-
const t = await transform(contents, tocOptions)
|
|
49
|
-
let outputText = t.wrappedToc || ''
|
|
50
|
-
|
|
51
|
-
if (debugFileMatch) {
|
|
52
|
-
console.log('before firsth1 removal', outputText)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/* remove first h1 */
|
|
56
|
-
if (!opts.firsth1) {
|
|
57
|
-
// console.log('outputText', outputText)
|
|
58
|
-
const lines = outputText.split('\n')
|
|
59
|
-
let firstH1
|
|
60
|
-
let firstLine
|
|
61
|
-
const countOfH1s = lines.reduce((acc, line, index) => {
|
|
62
|
-
if (line !== '' && !firstLine) {
|
|
63
|
-
firstLine = index
|
|
64
|
-
}
|
|
65
|
-
if (line.match(/^-\s+/)) {
|
|
66
|
-
if (!firstH1) {
|
|
67
|
-
const rawText = line.match(/\[(.*)\]/)[1]
|
|
68
|
-
firstH1 = [line, index, firstLine === index, rawText]
|
|
69
|
-
}
|
|
70
|
-
acc = acc + 1
|
|
71
|
-
}
|
|
72
|
-
return acc
|
|
73
|
-
}, 0)
|
|
74
|
-
|
|
75
|
-
const firstHeadingData = (firstH1 && Array.isArray(firstH1)) ? firstH1 : []
|
|
76
|
-
const [ lineText, lineIndex, isFirst, matchText ] = firstHeadingData
|
|
77
|
-
|
|
78
|
-
let docHasHeading = false
|
|
79
|
-
if (matchText) {
|
|
80
|
-
// verify its h1
|
|
81
|
-
const matchTextEscaped = escapeRegexString(matchText)
|
|
82
|
-
// console.log('matchTextEscaped', matchTextEscaped)
|
|
83
|
-
// /^#{1}\s+(.*)/
|
|
84
|
-
const FIRST_H1_REGEX = new RegExp(`^#\\s*\\[?${matchTextEscaped}\\]?(?:.*)?`, 'gim')
|
|
85
|
-
// /^<h1\b[^>]*>([\s\S]*?)<\/h1>/
|
|
86
|
-
const FIRST_HTML_H1_REGEX = new RegExp(`^<h1\\b[^>]*>[\\s]*?(${matchTextEscaped})[\\s]*?<\\/h1>`, 'gim')
|
|
87
|
-
// /^(.*)\n={3,}/
|
|
88
|
-
const FIRST_UNDERSCORE_H1 = new RegExp(`^(${matchTextEscaped})\n={3,}`, 'gim')
|
|
89
|
-
|
|
90
|
-
if (contents.match(FIRST_H1_REGEX)) {
|
|
91
|
-
docHasHeading = matchText
|
|
92
|
-
} else if (contents.match(FIRST_HTML_H1_REGEX)) {
|
|
93
|
-
docHasHeading = matchText
|
|
94
|
-
} else if (contents.match(FIRST_UNDERSCORE_H1)) {
|
|
95
|
-
docHasHeading = matchText
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (debugFileMatch) {
|
|
100
|
-
console.log('matchText', matchText)
|
|
101
|
-
if (docHasHeading) {
|
|
102
|
-
console.log('Found heading 1', docHasHeading)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (debugFileMatch) {
|
|
107
|
-
console.log('top level toc item count', countOfH1s)
|
|
108
|
-
if (docHasHeading) {
|
|
109
|
-
console.log('Found heading 1', docHasHeading)
|
|
110
|
-
console.log('firstH1', firstH1)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
let firstItemWithContent
|
|
115
|
-
let foundFirstH1
|
|
116
|
-
|
|
117
|
-
if (docHasHeading) {
|
|
118
|
-
outputText = lines.reduce((acc, line, i) => {
|
|
119
|
-
const isLineEmpty = line === ''
|
|
120
|
-
if (!isLineEmpty && !firstItemWithContent) {
|
|
121
|
-
firstItemWithContent = i
|
|
122
|
-
}
|
|
123
|
-
if (!foundFirstH1 && (firstItemWithContent === i) && line.match(/^-\s+/)) {
|
|
124
|
-
foundFirstH1 = true
|
|
125
|
-
return acc
|
|
126
|
-
}
|
|
127
|
-
// Add back line and remove level
|
|
128
|
-
let newLineValue = line
|
|
129
|
-
if (lineText) {
|
|
130
|
-
// const untilFirstH1 = i < lineIndex
|
|
131
|
-
/* @TODO make option? flatten all non first h1 tags */
|
|
132
|
-
if (countOfH1s > 0 && !isLineEmpty && newLineValue.match(/^\s+-/)) {
|
|
133
|
-
newLineValue = line.substring(2)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
acc = acc.concat(newLineValue)
|
|
138
|
-
return acc
|
|
139
|
-
}, []).join('\n')
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// console.log('outputText', outputText)
|
|
143
|
-
if (debugFileMatch) {
|
|
144
|
-
console.log('after firsth1 removal', outputText)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
// console.log(t)
|
|
148
|
-
// Fix <h1> with multiple lines
|
|
149
|
-
const spaces = outputText.match(/\[\n([\s\S]*?)\]/gm)
|
|
150
|
-
if (spaces) {
|
|
151
|
-
const fixed = spaces[0]
|
|
152
|
-
// all new lines
|
|
153
|
-
.replace(/\n/g, '')
|
|
154
|
-
// leading spaces
|
|
155
|
-
.replace(/\[\s+/, '[')
|
|
156
|
-
.trim()
|
|
157
|
-
outputText = outputText.replace(spaces[0], fixed)
|
|
158
|
-
}
|
|
159
|
-
// console.log(outputText)
|
|
160
|
-
outputText = outputText
|
|
161
|
-
.replace(/(.*)\[Table of Contents\]\(.*\)\n?/i, '')
|
|
162
|
-
.replace(/(.*)\[toc\]\(.*\)\n?/i, '')
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (opts.excludeText) {
|
|
166
|
-
|
|
167
|
-
outputText = excludeTocItem(outputText, opts.excludeText)
|
|
168
|
-
// // (\s+)?-(.*)\[Usage\]\(.*\)
|
|
169
|
-
// const regex = new RegExp(`\(\\s+\)?-(.*)\\[${opts.excludeText}\\]\\(.*\\)`, 'i')
|
|
170
|
-
// // /(\s+)?-(.*)\[Usage\]\(.*\)(\n\s+(.*))+/im
|
|
171
|
-
// const nestedRegex = new RegExp(`\(\\s+\)?-(.*)\\[${opts.excludeText}\\]\\(.*\\)\(\\n\\s+\(.*\)\)+`, 'i')
|
|
172
|
-
|
|
173
|
-
// const hasNested = nestedRegex.exec(outputText)
|
|
174
|
-
// if (hasNested) {
|
|
175
|
-
// // Count indentation of spaces
|
|
176
|
-
// const numberOfSpaces = hasNested[1].replace(/\n/g, '').length
|
|
177
|
-
// const subItems = numberOfSpaces + 1
|
|
178
|
-
// // Update regex to only remove sub items
|
|
179
|
-
// const nestedRegexSpaces = new RegExp(`\(\\s+\)?-(.*)\\[${opts.excludeText}\\]\\(.*\\)\(\\n\\s{${subItems},}\(.*\)\)+`, 'i')
|
|
180
|
-
// // console.log('nestedRegexSpaces', nestedRegexSpaces)
|
|
181
|
-
// // If exclude value has nested sections remove them as well.
|
|
182
|
-
// outputText = outputText.replace(nestedRegexSpaces, '')
|
|
183
|
-
// outputText = outputText.replace(regex, '')
|
|
184
|
-
// } else {
|
|
185
|
-
// outputText = outputText.replace(regex, '')
|
|
186
|
-
// }
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/* Sub table of contents */
|
|
190
|
-
if (isSub) {
|
|
191
|
-
// const start = fileContent.indexOf(open.value)
|
|
192
|
-
// const linesBefore = fileContent.substr(0, start).split('\n')
|
|
193
|
-
// const closestHeading = linesBefore.reverse().find((line) => {
|
|
194
|
-
// return line.match((/^#+/))
|
|
195
|
-
// })
|
|
196
|
-
const linesBefore = originalContents.substr(0, block.start).split('\n')
|
|
197
|
-
const closestHeading = linesBefore.reverse().find((line) => {
|
|
198
|
-
return line.match((/^#+/))
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
if (closestHeading) {
|
|
202
|
-
const headingText = closestHeading.replace(/^#*\s*/, '')
|
|
203
|
-
// console.log('BEFORE', linesBefore)
|
|
204
|
-
// console.log('closest parent heading', closestHeading)
|
|
205
|
-
// console.log('headingText', headingText)
|
|
206
|
-
// https://regex101.com/r/MB85zm/1
|
|
207
|
-
|
|
208
|
-
const findSubToc = new RegExp(`^\(\\s+\)?-(.*)\\[${headingText}\\]\\(.*\\)(\\n\\s.*)+`, 'gm')
|
|
209
|
-
// console.log('findSubToc', findSubToc)
|
|
210
|
-
const single = singleLinePattern(headingText)
|
|
211
|
-
// console.log('single', single)
|
|
212
|
-
const subItems = outputText.match(findSubToc)
|
|
213
|
-
if (subItems) {
|
|
214
|
-
const items = subItems[0].replace(single, '').split('\n')
|
|
215
|
-
.filter(Boolean)
|
|
216
|
-
// console.log('items', items)
|
|
217
|
-
const finalItems = items // .slice(1, items.length)
|
|
218
|
-
if (finalItems) {
|
|
219
|
-
const indent = findMinIndent(finalItems.join('\n'))
|
|
220
|
-
// console.log('min indent', indent)
|
|
221
|
-
// console.log('finalItems', finalItems)
|
|
222
|
-
outputText = finalItems.map((thing) => thing.substring(indent)).join('\n')
|
|
223
|
-
} else {
|
|
224
|
-
// console.log('No sub items')
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// If collapse wrap in <details>
|
|
231
|
-
if (opts.collapse) {
|
|
232
|
-
const text = (opts.collapseText) ? opts.collapseText : 'Table of Contents'
|
|
233
|
-
return `<details>
|
|
234
|
-
<summary>${text}</summary>
|
|
235
|
-
${outputText
|
|
236
|
-
// Replace leading double spaces
|
|
237
|
-
.replace(/^\n\n/, '\n')
|
|
238
|
-
// Replace trailing double spaces
|
|
239
|
-
.replace(/\n\n$/, '\n')}
|
|
240
|
-
</details>`
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return outputText.replace(removeLeadingAndTrailingLineBreaks, '')
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const HTML_TAG = /<([a-zA-Z1-6]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)/gim
|
|
247
|
-
|
|
248
|
-
function parseHtmlProps(mdContents) {
|
|
249
|
-
const htmlTags = mdContents
|
|
250
|
-
/* Fix non terminating <tags> */
|
|
251
|
-
.replace(/(['"`]<(.*)>['"`])/gm, '_$2_')
|
|
252
|
-
.match(HTML_TAG)
|
|
253
|
-
//console.log('htmlTags', htmlTags)
|
|
254
|
-
|
|
255
|
-
let tags = []
|
|
256
|
-
if (htmlTags) {
|
|
257
|
-
let propsValues = {}
|
|
258
|
-
var regexSingleTag = /<([a-zA-Z1-6]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)/
|
|
259
|
-
for (var i = 0; i < htmlTags.length; i++) {
|
|
260
|
-
var tagMatches = regexSingleTag.exec(htmlTags[i])
|
|
261
|
-
// console.log('tagMatches', tagMatches)
|
|
262
|
-
var [ match, tag, props ] = tagMatches
|
|
263
|
-
// console.log(`Tag #${i} ${tag}`)
|
|
264
|
-
if (props) {
|
|
265
|
-
const cleanProps = props
|
|
266
|
-
// Remove new lines and tabs
|
|
267
|
-
.replace(/\n\t/g, '')
|
|
268
|
-
// Remove extra spaces
|
|
269
|
-
.replace(/\s\s+/g, ' ')
|
|
270
|
-
.trim()
|
|
271
|
-
|
|
272
|
-
propsValues = cleanProps.split(" ").reduce((acc, curr) => {
|
|
273
|
-
const hasQuotes = curr.match(/=['"]/)
|
|
274
|
-
// Check key="value" | key='value' | key={value}
|
|
275
|
-
const propWithValue = /([A-Za-z-_$]+)=['{"](.*)['}"]/g.exec(curr)
|
|
276
|
-
if (propWithValue) {
|
|
277
|
-
return {
|
|
278
|
-
...acc,
|
|
279
|
-
[`${propWithValue[1]}`]: (hasQuotes) ? propWithValue[2] : convert(propWithValue[2])
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
// Check isLoading boolean props
|
|
283
|
-
const booleanProp = curr.match(/([A-Za-z]*)/)
|
|
284
|
-
if (booleanProp) {
|
|
285
|
-
return {
|
|
286
|
-
...acc,
|
|
287
|
-
[`${booleanProp[1]}`]: true
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return acc
|
|
291
|
-
}, {})
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
tags.push({
|
|
295
|
-
tag: tag,
|
|
296
|
-
props: propsValues,
|
|
297
|
-
raw: match
|
|
298
|
-
})
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return tags
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function singleLinePattern(text) {
|
|
305
|
-
/* (\s+)?-(.*)\[Usage\]\(.*\) */
|
|
306
|
-
return new RegExp(`\(\\s+\)?-(.*)\\[${text}\\]\\(.*\\)`, 'i')
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function excludeTocItem(str, excludeText) {
|
|
310
|
-
const matchTextEscaped = escapeRegexString(excludeText)
|
|
311
|
-
/* (\s+)?-(.*)\[Usage\]\(.*\) */
|
|
312
|
-
const regex = singleLinePattern(matchTextEscaped) // new RegExp(`\(\\s+\)?-(.*)\\[${matchTextEscaped}\\]\\(.*\\)`, 'i')
|
|
313
|
-
/* /(\s+)?-(.*)\[Usage\]\(.*\)(\n\s+(.*))+/im */
|
|
314
|
-
const nestedRegex = new RegExp(`\(\\s+\)?-(.*)\\[${matchTextEscaped}\\]\\(.*\\)\(\\n\\s+\(.*\)\)+`, 'i')
|
|
315
|
-
|
|
316
|
-
const hasNested = nestedRegex.exec(str)
|
|
317
|
-
if (hasNested) {
|
|
318
|
-
// Count indentation of spaces
|
|
319
|
-
const numberOfSpaces = (hasNested[1] || '').replace(/\n/g, '').length
|
|
320
|
-
const subItems = numberOfSpaces + 1
|
|
321
|
-
// Update regex to only remove sub items
|
|
322
|
-
const nestedRegexSpaces = new RegExp(`\(\\s+\)?-(.*)\\[${matchTextEscaped}\\]\\(.*\\)\(\\n\\s{${subItems},}\(.*\)\)+`, 'i')
|
|
323
|
-
// console.log('nestedRegexSpaces', nestedRegexSpaces)
|
|
324
|
-
// If exclude value has nested sections remove them as well.
|
|
325
|
-
str = str.replace(nestedRegexSpaces, '')
|
|
326
|
-
str = str.replace(regex, '')
|
|
327
|
-
} else {
|
|
328
|
-
str = str.replace(regex, '')
|
|
329
|
-
}
|
|
330
|
-
return str
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function removeUndefined(outputText) {
|
|
334
|
-
// remove undefined from new line and start of string if first H1 is missing
|
|
335
|
-
return outputText.replace(/\nundefined/g, '\n-').replace(/^undefined/g, '-')
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Alt https://github.com/thlorenz/doctoc
|
|
5
|
+
const { options, fileContent, srcPath, getBlockDetails } = api
|
|
6
|
+
const toc = await generateToc({
|
|
7
|
+
options: options || {},
|
|
8
|
+
fileContent,
|
|
9
|
+
srcPath,
|
|
10
|
+
getBlockDetails: getBlockDetails
|
|
11
|
+
})
|
|
12
|
+
return toc
|
|
13
|
+
}
|