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/utils/fs.js
CHANGED
|
@@ -9,23 +9,6 @@ const { readdir, stat, readFile } = fs
|
|
|
9
9
|
|
|
10
10
|
const IS_HIDDEN_FILE = /(^|[\\\/])\.[^\\\/\.]/g
|
|
11
11
|
|
|
12
|
-
// https://github.com/lukeed/escalade
|
|
13
|
-
async function escalade(start, callback) {
|
|
14
|
-
let dir = resolve('.', start)
|
|
15
|
-
let tmp, stats = await stat(dir)
|
|
16
|
-
|
|
17
|
-
if (!stats.isDirectory()) {
|
|
18
|
-
dir = dirname(dir)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
while (true) {
|
|
22
|
-
tmp = await callback(dir, await readdir(dir))
|
|
23
|
-
if (tmp) return resolve(dir, tmp)
|
|
24
|
-
dir = dirname(tmp = dir)
|
|
25
|
-
if (tmp === dir) break;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
12
|
function isRegex(thing) {
|
|
30
13
|
return (thing instanceof RegExp)
|
|
31
14
|
}
|
|
@@ -57,6 +40,23 @@ async function findUp(start, fileName) {
|
|
|
57
40
|
return file
|
|
58
41
|
}
|
|
59
42
|
|
|
43
|
+
// https://github.com/lukeed/escalade
|
|
44
|
+
async function escalade(start, callback) {
|
|
45
|
+
let dir = resolve('.', start)
|
|
46
|
+
let tmp, stats = await stat(dir)
|
|
47
|
+
|
|
48
|
+
if (!stats.isDirectory()) {
|
|
49
|
+
dir = dirname(dir)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
while (true) {
|
|
53
|
+
tmp = await callback(dir, await readdir(dir))
|
|
54
|
+
if (tmp) return resolve(dir, tmp)
|
|
55
|
+
dir = dirname(tmp = dir)
|
|
56
|
+
if (tmp === dir) break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
60
|
// https://github.com/lukeed/totalist
|
|
61
61
|
async function totalist(dir, callback, pre='') {
|
|
62
62
|
dir = resolve('.', dir)
|
|
@@ -77,8 +77,10 @@ function combineRegexes(patterns = []) {
|
|
|
77
77
|
} else if (typeof pat === 'string' && REGEX_REGEX.test(pat)) {
|
|
78
78
|
const regexInfo = pat.match(REGEX_REGEX)
|
|
79
79
|
console.log('regexInfo', regexInfo)
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
if (regexInfo && regexInfo[1]) {
|
|
81
|
+
// escapeRegexString
|
|
82
|
+
return regexInfo[1]
|
|
83
|
+
}
|
|
82
84
|
} else if (isGlob(pat)) {
|
|
83
85
|
console.log('pat', pat)
|
|
84
86
|
const result = globrex(pat, { globstar: true, extended: true })
|
package/lib/utils/fs.test.js
CHANGED
|
@@ -15,14 +15,14 @@ test('Finds file from file', async () => {
|
|
|
15
15
|
const startDir = path.resolve(__dirname, '../index.js')
|
|
16
16
|
const file = await findUp(startDir, 'README.md')
|
|
17
17
|
assert.ok(file)
|
|
18
|
-
assert.equal(path.basename(file), 'README.md')
|
|
18
|
+
assert.equal(path.basename(file || ''), 'README.md')
|
|
19
19
|
})
|
|
20
20
|
|
|
21
21
|
test('Finds file from dir', async () => {
|
|
22
22
|
const startDir = path.resolve(__dirname, '../')
|
|
23
23
|
const file = await findUp(startDir, 'README.md')
|
|
24
24
|
assert.ok(file)
|
|
25
|
-
assert.equal(path.basename(file), 'README.md')
|
|
25
|
+
assert.equal(path.basename(file || ''), 'README.md')
|
|
26
26
|
})
|
|
27
27
|
|
|
28
28
|
test('getFilePaths /\.test\.js?$/', async () => {
|
|
@@ -42,7 +42,6 @@ test('getFilePaths /\.test\.js?$/', async () => {
|
|
|
42
42
|
'lib/block-parser.test.js',
|
|
43
43
|
'lib/cli.test.js',
|
|
44
44
|
'lib/index.test.js',
|
|
45
|
-
'lib/options-parser.test.js',
|
|
46
45
|
'lib/utils/fs.test.js',
|
|
47
46
|
"lib/utils/md/find-frontmatter.test.js",
|
|
48
47
|
"lib/utils/md/md.test.js",
|
|
@@ -76,13 +75,13 @@ test('getFilePaths /\.mdx?$/, /\.test\.js?$/', async () => {
|
|
|
76
75
|
'lib/block-parser.test.js',
|
|
77
76
|
'lib/cli.test.js',
|
|
78
77
|
'lib/index.test.js',
|
|
79
|
-
'lib/options-parser.test.js',
|
|
80
78
|
'lib/utils/fs.test.js',
|
|
81
79
|
"lib/utils/md/find-frontmatter.test.js",
|
|
82
80
|
"lib/utils/md/md.test.js",
|
|
83
81
|
"lib/utils/text.test.js",
|
|
84
82
|
'test/errors.test.js',
|
|
85
83
|
'test/fixtures/md/basic.md',
|
|
84
|
+
"test/fixtures/md/broken-inline.md",
|
|
86
85
|
'test/fixtures/md/error-missing-transforms-two.md',
|
|
87
86
|
'test/fixtures/md/error-missing-transforms.md',
|
|
88
87
|
'test/fixtures/md/error-no-block-transform-defined.md',
|
|
@@ -153,7 +152,6 @@ test('getFilePaths glob', async () => {
|
|
|
153
152
|
'lib/block-parser.test.js',
|
|
154
153
|
'lib/cli.test.js',
|
|
155
154
|
'lib/index.test.js',
|
|
156
|
-
'lib/options-parser.test.js',
|
|
157
155
|
'lib/utils/fs.test.js',
|
|
158
156
|
"lib/utils/md/find-frontmatter.test.js",
|
|
159
157
|
"lib/utils/md/md.test.js",
|
|
@@ -161,6 +159,7 @@ test('getFilePaths glob', async () => {
|
|
|
161
159
|
// 'misc/old-test/main.test.js',
|
|
162
160
|
'test/errors.test.js',
|
|
163
161
|
'test/fixtures/md/basic.md',
|
|
162
|
+
"test/fixtures/md/broken-inline.md",
|
|
164
163
|
'test/fixtures/md/error-missing-transforms-two.md',
|
|
165
164
|
'test/fixtures/md/error-missing-transforms.md',
|
|
166
165
|
'test/fixtures/md/error-no-block-transform-defined.md',
|
package/lib/utils/logs.js
CHANGED
|
@@ -61,8 +61,13 @@ function log(type, msg, customPrefix, noLog) {
|
|
|
61
61
|
console.log(logMsg)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
function deepLog(myObject) {
|
|
65
|
-
|
|
64
|
+
function deepLog(myObject, myObjectTwo) {
|
|
65
|
+
let obj = myObject
|
|
66
|
+
if (typeof myObject === 'string') {
|
|
67
|
+
obj = myObjectTwo
|
|
68
|
+
console.log(myObject)
|
|
69
|
+
}
|
|
70
|
+
console.log(util.inspect(obj, false, null, true /* enable colors */))
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
const success = log.bind(null, 'success')
|
package/lib/utils/md/filters.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
const IMAGE_POSTFIX_REGEX = /\.(png|apng|jpe?g|gif|webp|svg|avif)$/
|
|
2
2
|
const RELATIVE_LINK_REGEX = /^(?!(?:(?:https?|ftp):\/\/|data:))((?:\.\.?\/)*)*([\w\d\-_./?=#%:+&]+)/
|
|
3
3
|
|
|
4
4
|
function onlyUnique(value, index, self) {
|
|
5
|
-
return self.indexOf(value) === index
|
|
5
|
+
return self.indexOf(value) === index
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
function isImage(link) {
|
|
9
|
-
return link.match(
|
|
8
|
+
function isImage(link = '') {
|
|
9
|
+
return link.match(IMAGE_POSTFIX_REGEX)
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
function isRelative(filepath) {
|
|
12
|
+
function isRelative(filepath = '') {
|
|
13
13
|
return RELATIVE_LINK_REGEX.test(filepath)
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -8,13 +8,15 @@ const REMOVE_CODE_BLOCK_REGEX = /^(?:[A-Za-z \t]*)?(```(?:[A-Za-z]*)?\n(?:[\s\S]
|
|
|
8
8
|
/**
|
|
9
9
|
* Parse code blocks out of markdown
|
|
10
10
|
* @param {string} block
|
|
11
|
+
* @param {Object} opts
|
|
11
12
|
* @returns {Object}
|
|
12
13
|
* @example
|
|
13
14
|
* const { blocks, errors } = findCodeBlocks(content)
|
|
14
15
|
* console.log('blocks', blocks)
|
|
15
16
|
* console.log('errors', errors)
|
|
16
17
|
*/
|
|
17
|
-
function findCodeBlocks(block,
|
|
18
|
+
function findCodeBlocks(block, opts = {}) {
|
|
19
|
+
const { filePath = '', includePositions } = opts
|
|
18
20
|
let matches
|
|
19
21
|
let errors = []
|
|
20
22
|
let blocks = []
|
|
@@ -34,6 +36,17 @@ function findCodeBlocks(block, filePath = '') {
|
|
|
34
36
|
console.log(content.trim())
|
|
35
37
|
console.log('───────────────────────')
|
|
36
38
|
/** */
|
|
39
|
+
const codeBlock = {}
|
|
40
|
+
if (includePositions) {
|
|
41
|
+
codeBlock.line = lineNumber
|
|
42
|
+
codeBlock.index = matches.index
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (syntax) {
|
|
46
|
+
codeBlock.syntax = syntax
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
codeBlock.block = match
|
|
37
50
|
|
|
38
51
|
/* Validate code blocks */
|
|
39
52
|
if (prefix && prefix.match(/\S/)) {
|
|
@@ -57,13 +70,8 @@ function findCodeBlocks(block, filePath = '') {
|
|
|
57
70
|
}
|
|
58
71
|
|
|
59
72
|
if (!hasError) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
index: matches.index,
|
|
63
|
-
syntax: syntax || '',
|
|
64
|
-
block: match,
|
|
65
|
-
code: content.trim()
|
|
66
|
-
})
|
|
73
|
+
codeBlock.code = content.trim()
|
|
74
|
+
blocks.push(codeBlock)
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
77
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
const matter = require('gray-matter')
|
|
2
|
-
/* Match <!-- frontmatter --> */
|
|
3
|
-
// https://regex101.com/r/Q9bBxC/1
|
|
2
|
+
/* Match <!-- frontmatter --> https://regex101.com/r/Q9bBxC/1 */
|
|
4
3
|
const HIDDEN_FRONTMATTER_REGEX = /^<!--+(?:\r\n|\r|\n)([\w\W]*?)--+>/g
|
|
5
4
|
// const HIDDEN_FRONTMATTER_REGEX = /^<!--.*((.|\r?\n)*?.*-->)/g
|
|
6
5
|
|
|
7
|
-
/* Match --- frontmatter --- */
|
|
8
|
-
// https://regex101.com/r/d7eAw4/1
|
|
6
|
+
/* Match --- frontmatter --- https://regex101.com/r/d7eAw4/1 */
|
|
9
7
|
const FRONTMATTER_REGEX = /(^--+(?:\r\n|\r|\n)([\w\W]*?)--+)/
|
|
10
8
|
// const FRONTMATTER_REGEX = /^---.*((.|\r?\n)*?.*---)/gm
|
|
11
9
|
|
|
@@ -22,7 +20,7 @@ function replaceConflictingContent(str) {
|
|
|
22
20
|
.replace(/__CLOSE_COMMENT__/g, ' -->')
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
function
|
|
23
|
+
function findFrontmatter(content = '') {
|
|
26
24
|
const text = removeConflictingContent(content.trim())
|
|
27
25
|
const hasFrontMatter = text.match(FRONTMATTER_REGEX)
|
|
28
26
|
const hasHiddenFrontMatter = text.match(HIDDEN_FRONTMATTER_REGEX)
|
|
@@ -48,14 +46,14 @@ function findFrontmatterRaw(content = '') {
|
|
|
48
46
|
.replace(/--+>/, `---`)
|
|
49
47
|
}
|
|
50
48
|
return {
|
|
51
|
-
|
|
49
|
+
frontMatterRaw: replaceConflictingContent(raw),
|
|
52
50
|
frontMatter: replaceConflictingContent(match),
|
|
53
51
|
isHidden
|
|
54
52
|
}
|
|
55
53
|
}
|
|
56
54
|
|
|
57
|
-
function
|
|
58
|
-
const { frontMatter,
|
|
55
|
+
function parseFrontmatter(text) {
|
|
56
|
+
const { frontMatter, frontMatterRaw } = findFrontmatter(text)
|
|
59
57
|
// console.log('frontMatter', frontMatter)
|
|
60
58
|
let frontmatter = { data: {}, content: '' }
|
|
61
59
|
/* Missing all frontmatter */
|
|
@@ -65,10 +63,10 @@ function findFrontmatter(text) {
|
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
let mdContent = text
|
|
68
|
-
if (
|
|
66
|
+
if (frontMatterRaw) {
|
|
69
67
|
mdContent = text
|
|
70
68
|
// Replace frontmatter brackets
|
|
71
|
-
.replace(
|
|
69
|
+
.replace(frontMatterRaw, frontMatter)
|
|
72
70
|
// Replace leading lines
|
|
73
71
|
// .replace(/---+\s+\n/g, '---\n')
|
|
74
72
|
}
|
|
@@ -77,18 +75,18 @@ function findFrontmatter(text) {
|
|
|
77
75
|
frontmatter = matter(mdContent)
|
|
78
76
|
} catch (err) {
|
|
79
77
|
/* Add line numbers to output */
|
|
80
|
-
const formattedError =
|
|
78
|
+
const formattedError = frontMatterRaw.split('\n').map((line, i) => {
|
|
81
79
|
return `${i + 1}. ${line}`
|
|
82
80
|
})
|
|
83
81
|
throw new Error(`Frontmatter error:\n${err.message}\n${formattedError.join('\n')}`)
|
|
84
82
|
}
|
|
85
83
|
// console.log('frontMatter', frontmatter)
|
|
86
|
-
return Object.assign(frontmatter, {
|
|
84
|
+
return Object.assign(frontmatter, { frontMatterRaw })
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
module.exports = {
|
|
88
|
+
parseFrontmatter,
|
|
90
89
|
findFrontmatter,
|
|
91
|
-
findFrontmatterRaw,
|
|
92
90
|
HIDDEN_FRONTMATTER_REGEX,
|
|
93
91
|
FRONTMATTER_REGEX
|
|
94
92
|
}
|
|
@@ -3,13 +3,13 @@ const fs = require('fs')
|
|
|
3
3
|
const { test } = require('uvu')
|
|
4
4
|
const assert = require('uvu/assert')
|
|
5
5
|
const { findLinks } = require('./find-links')
|
|
6
|
-
const {
|
|
6
|
+
const { parseFrontmatter } = require('./find-frontmatter')
|
|
7
7
|
|
|
8
8
|
const FILE_PATH = path.join(__dirname, 'fixtures/file-with-frontmatter.md')
|
|
9
9
|
const fileWithLinks = fs.readFileSync(FILE_PATH, 'utf-8')
|
|
10
10
|
|
|
11
11
|
test('Find frontmatter', async () => {
|
|
12
|
-
const frontmatter =
|
|
12
|
+
const frontmatter = parseFrontmatter(fileWithLinks)
|
|
13
13
|
console.log('frontmatter', frontmatter)
|
|
14
14
|
assert.is(typeof frontmatter.data, 'object')
|
|
15
15
|
})
|
|
@@ -33,7 +33,7 @@ function findHtmlTags(mdContents) {
|
|
|
33
33
|
var regexSingleTag = /<([a-zA-Z1-6]+)\b([^>]*)>*(?:>([\s\S]*?)<\/\1>|\s?\/?>)/
|
|
34
34
|
for (var i = 0; i < htmlTags.length; i++) {
|
|
35
35
|
// console.log('htmlTags[i]', htmlTags[i])
|
|
36
|
-
var tagMatches = regexSingleTag.exec(htmlTags[i])
|
|
36
|
+
var tagMatches = regexSingleTag.exec(htmlTags[i]) || []
|
|
37
37
|
// console.log('tagMatches', tagMatches)
|
|
38
38
|
var [ match, tag, props ] = tagMatches
|
|
39
39
|
// console.log(`Tag #${i} ${tag}`)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const { onlyUnique } = require('./filters')
|
|
2
|
+
|
|
3
|
+
// https://regex101.com/r/u2DwY2/2/
|
|
4
|
+
const MARKDOWN_IMAGE_REGEX = /!\[[^\]]*\]\((.*?)\s*("(?:.*[^"])")?\s*\)/g
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get markdown style images from text
|
|
8
|
+
* @param {string} text
|
|
9
|
+
* @returns {string[]}
|
|
10
|
+
*/
|
|
11
|
+
function findMarkdownImages(text) {
|
|
12
|
+
let matches
|
|
13
|
+
let imageLinks = []
|
|
14
|
+
while ((matches = MARKDOWN_IMAGE_REGEX.exec(text)) !== null) {
|
|
15
|
+
if (matches.index === MARKDOWN_IMAGE_REGEX.lastIndex) {
|
|
16
|
+
MARKDOWN_IMAGE_REGEX.lastIndex++ // avoid infinite loops with zero-width matches
|
|
17
|
+
}
|
|
18
|
+
const [ match, image, altText ] = matches
|
|
19
|
+
imageLinks.push(image)
|
|
20
|
+
}
|
|
21
|
+
return imageLinks.filter(onlyUnique)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
findMarkdownImages,
|
|
26
|
+
MARKDOWN_IMAGE_REGEX
|
|
27
|
+
}
|
|
@@ -1,67 +1,72 @@
|
|
|
1
1
|
const { onlyUnique, isImage, isRelative } = require('./filters')
|
|
2
|
-
const { findLinks,
|
|
2
|
+
const { findLinks, findAbsoluteLinks } = require('./find-links')
|
|
3
|
+
const { findMarkdownImages, MARKDOWN_IMAGE_REGEX } = require('./find-images-md')
|
|
3
4
|
|
|
4
|
-
// https://regex101.com/r/u2DwY2/2/
|
|
5
|
-
const MARKDOWN_IMAGE_REGEX = /!\[[^\]]*\]\((.*?)\s*("(?:.*[^"])")?\s*\)/g
|
|
6
5
|
// https://regex101.com/r/Uxgu3P/1
|
|
7
6
|
const RELATIVE_IMAGES_REGEX = /(<img.*?src=['"])(?!(?:(?:https?|ftp):\/\/|data:))(\.?\/)?(.*?)(['"].*?\/?>)/gim
|
|
8
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {object} findImagesOpts
|
|
10
|
+
* @property {string[]} [links] - optional links to use to avoid re-parse
|
|
11
|
+
* @property {boolean} [unique=true] - ensure links unique
|
|
12
|
+
* @property {Record<string,any>} [frontmatter] - Frontmatter data
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {object} findImagesResult
|
|
17
|
+
* @property {string[]} all - All links
|
|
18
|
+
* @property {string[]} absolute - All absolute links
|
|
19
|
+
* @property {string[]} relative - All relative links
|
|
20
|
+
* @property {string[]} md - All md style links
|
|
21
|
+
*/
|
|
22
|
+
|
|
9
23
|
/**
|
|
10
24
|
* Get image links from content
|
|
11
25
|
* @param {string} content
|
|
12
|
-
* @param {
|
|
13
|
-
* @returns {
|
|
26
|
+
* @param {findImagesOpts} [opts] - optional links to use to avoid re-parse
|
|
27
|
+
* @returns {findImagesResult}
|
|
14
28
|
*/
|
|
15
29
|
function findImages(content = '', opts = {}) {
|
|
16
|
-
const { links, unique = true } = opts
|
|
17
|
-
|
|
30
|
+
const { links, unique = true, frontmatter } = opts
|
|
31
|
+
let foundLinks = []
|
|
32
|
+
if (links && Array.isArray(links)) {
|
|
33
|
+
foundLinks = links
|
|
34
|
+
} else {
|
|
35
|
+
const results = findLinks(content, { frontmatter })
|
|
36
|
+
foundLinks = results.links.concat(results.images)
|
|
37
|
+
}
|
|
18
38
|
const imageLinks = foundLinks.filter(isImage)
|
|
19
|
-
const
|
|
20
|
-
const allImageLinks = imageLinks.concat(
|
|
39
|
+
const markdownLinks = findMarkdownImages(content)
|
|
40
|
+
const allImageLinks = imageLinks.concat(markdownLinks)
|
|
21
41
|
const all = (!unique) ? allImageLinks : allImageLinks.filter(onlyUnique)
|
|
22
42
|
return {
|
|
23
43
|
all,
|
|
24
|
-
|
|
44
|
+
absolute: all.filter((link) => !isRelative(link)),
|
|
25
45
|
relative: all.filter((link) => isRelative(link)),
|
|
26
|
-
md:
|
|
46
|
+
md: markdownLinks
|
|
27
47
|
}
|
|
28
48
|
}
|
|
29
49
|
|
|
30
50
|
/**
|
|
31
|
-
* Get
|
|
51
|
+
* Get absolute url image links
|
|
32
52
|
* @param {string} content
|
|
33
53
|
* @param {array} [links] - optional links to use to avoid re-parse
|
|
34
54
|
* @returns {array}
|
|
35
55
|
*/
|
|
36
|
-
function
|
|
37
|
-
const foundLinks = (links && Array.isArray(links)) ? links :
|
|
38
|
-
const imageLinks = foundLinks.filter(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const mdLinks = findMarkdownImages(content)
|
|
42
|
-
const allImageLinks = imageLinks.concat(mdLinks)
|
|
56
|
+
function findAbsoluteImages(content = '', links) {
|
|
57
|
+
const foundLinks = (links && Array.isArray(links)) ? links : findAbsoluteLinks(content)
|
|
58
|
+
const imageLinks = foundLinks.filter(isImage)
|
|
59
|
+
const markdownLinks = findMarkdownImages(content)
|
|
60
|
+
const allImageLinks = imageLinks.concat(markdownLinks)
|
|
43
61
|
|
|
44
62
|
return allImageLinks
|
|
45
63
|
.filter(onlyUnique)
|
|
46
64
|
.filter((link) => !isRelative(link))
|
|
47
65
|
}
|
|
48
66
|
|
|
49
|
-
function findMarkdownImages(text) {
|
|
50
|
-
let matches
|
|
51
|
-
let imageLinks = []
|
|
52
|
-
while ((matches = MARKDOWN_IMAGE_REGEX.exec(text)) !== null) {
|
|
53
|
-
if (matches.index === MARKDOWN_IMAGE_REGEX.lastIndex) {
|
|
54
|
-
MARKDOWN_IMAGE_REGEX.lastIndex++ // avoid infinite loops with zero-width matches
|
|
55
|
-
}
|
|
56
|
-
const [ match, image, altText ] = matches
|
|
57
|
-
imageLinks.push(image)
|
|
58
|
-
}
|
|
59
|
-
return imageLinks.filter(onlyUnique)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
67
|
/**
|
|
63
68
|
* Get relative image links from content
|
|
64
|
-
* @param {string}
|
|
69
|
+
* @param {string} text
|
|
65
70
|
* @returns {array}
|
|
66
71
|
*/
|
|
67
72
|
function findRelativeImages(text) {
|
|
@@ -94,7 +99,7 @@ function findRelativeImgTags(block) {
|
|
|
94
99
|
|
|
95
100
|
module.exports = {
|
|
96
101
|
findImages,
|
|
97
|
-
|
|
102
|
+
findAbsoluteImages,
|
|
98
103
|
findRelativeImages,
|
|
99
104
|
findMarkdownImages,
|
|
100
105
|
MARKDOWN_IMAGE_REGEX,
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
const { onlyUnique, isImage, isRelative } = require('./filters')
|
|
2
|
+
const { findMarkdownImages } = require('./find-images-md')
|
|
3
|
+
// Alt https://github.com/MikeKovarik/link-extract
|
|
2
4
|
|
|
3
5
|
// https://regex101.com/r/In5HtG/3
|
|
4
|
-
const LIVE_LINKS_REGEX = /(?:['"(])((?:https?:\/\/)[\w\d\-_,./?=#%:+&]{3,})/gmi
|
|
6
|
+
// const LIVE_LINKS_REGEX = /(?:['"(])((?:https?:\/\/)[\w\d\-_,./?=#%:+&]{3,})/gmi
|
|
7
|
+
// https://regex101.com/r/In5HtG/4
|
|
8
|
+
const LIVE_LINKS_REGEX = /['"(]((?:https?:\/\/)[\w\d\-_,./?=#%:+&]{3,})|<(\S*:\/\/\S*)>/gmi
|
|
5
9
|
// https://regex101.com/r/Nywerx/3
|
|
6
10
|
const RELATIVE_LINKS_REGEX = /(src|href|\()=?(['"/])(?!(?:(?:https?|ftp):\/\/|data:))(\.?\/)?([\w\d-_./,?=#%:+&]+)(?:['")])?/gim
|
|
7
11
|
// https://regex101.com/r/u2DwY2/2/
|
|
8
12
|
const MARKDOWN_IMAGE_REGEX = /!\[[^\]]*\]\((.*?)\s*("(?:.*[^"])")?\s*\)/g
|
|
13
|
+
// https://regex101.com/r/UeQ049/2 <https://www.markdownguide.org>
|
|
14
|
+
const ANGLE_LINKS = /(<)(\S*[@:]\S*)(>)/
|
|
9
15
|
|
|
10
16
|
const LINK_PATTERN = /^https?:\/\//
|
|
11
17
|
|
|
@@ -18,43 +24,64 @@ function isRemote(link) {
|
|
|
18
24
|
}
|
|
19
25
|
|
|
20
26
|
/**
|
|
21
|
-
*
|
|
22
|
-
* @
|
|
23
|
-
* @
|
|
27
|
+
* @typedef {object} findLinksOpts
|
|
28
|
+
* @property {Record<string,any>} [frontmatter] - Frontmatter data
|
|
29
|
+
* @property {boolean} [unique=true] - ensure links unique
|
|
24
30
|
*/
|
|
25
|
-
function findLinks(text, opts = {}) {
|
|
26
|
-
const { unique = true } = opts
|
|
27
|
-
const live = findLiveLinks(text)
|
|
28
|
-
const relative = findRelativeLinks(text)
|
|
29
|
-
const normalLinks = live.concat(relative)
|
|
30
|
-
const mdImgLinks = findMarkdownImageLinks(text)
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {object} findLinksResult
|
|
34
|
+
* @property {string[]} links - All images
|
|
35
|
+
* @property {string[]} images - All live image links
|
|
36
|
+
*/
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Finds all links in text relative or otherwise
|
|
40
|
+
* @param {string} text
|
|
41
|
+
* @param {findLinksOpts} opts
|
|
42
|
+
* @returns {findLinksResult}
|
|
43
|
+
*/
|
|
44
|
+
function findLinks(text, opts = {}) {
|
|
45
|
+
const { unique = true, frontmatter } = opts
|
|
46
|
+
|
|
47
|
+
const absoluteLinks = findAbsoluteLinks(text)
|
|
48
|
+
// console.log('absoluteLinks', absoluteLinks)
|
|
49
|
+
const relativeLinks = findRelativeLinks(text)
|
|
50
|
+
// console.log('relativeLinks', relativeLinks)
|
|
51
|
+
const frontmatterLinks = (frontmatter) ? findLinksInFrontMatter(frontmatter) : []
|
|
52
|
+
// console.log('frontmatterLinks', frontmatterLinks)
|
|
53
|
+
const markdownImages = findMarkdownImages(text)
|
|
54
|
+
// console.log('markdownImages', markdownImages)
|
|
55
|
+
const foundLinks = frontmatterLinks
|
|
56
|
+
.concat(absoluteLinks)
|
|
57
|
+
.concat(relativeLinks)
|
|
58
|
+
.concat(markdownImages)
|
|
40
59
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
60
|
+
const allLinks = (!unique) ? foundLinks : foundLinks.filter(onlyUnique)
|
|
61
|
+
|
|
62
|
+
let _images = markdownImages
|
|
63
|
+
let _links = []
|
|
64
|
+
for (let i = 0; i < allLinks.length; i++) {
|
|
65
|
+
if (isImage(allLinks[i])) {
|
|
66
|
+
_images.push(allLinks[i])
|
|
47
67
|
} else {
|
|
48
|
-
|
|
68
|
+
_links.push(allLinks[i])
|
|
49
69
|
}
|
|
50
70
|
}
|
|
51
71
|
// const allImages = normalLinks.filter((link) => isImage(link)).concat(mdLinks)
|
|
52
72
|
// const links = normalLinks.filter((link) => !isImage(link))
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
return
|
|
73
|
+
const images = _images.filter(onlyUnique)
|
|
74
|
+
const links = _links.filter(onlyUnique).filter(function(el) {
|
|
75
|
+
return images.indexOf(el) < 0
|
|
56
76
|
})
|
|
77
|
+
|
|
57
78
|
return {
|
|
79
|
+
links,
|
|
80
|
+
images
|
|
81
|
+
}
|
|
82
|
+
/*
|
|
83
|
+
// Old return https://github.com/DavidWells/markdown-magic/blob/b148b4ad3876c4ea07371451d230a5e9cec57ce5/lib/utils/md/find-links.js#L32-L44
|
|
84
|
+
return {
|
|
58
85
|
// all,
|
|
59
86
|
// live,
|
|
60
87
|
// relative,
|
|
@@ -70,6 +97,7 @@ function findLinks(text, opts = {}) {
|
|
|
70
97
|
live: allImg.filter(isRemote)
|
|
71
98
|
},
|
|
72
99
|
}
|
|
100
|
+
*/
|
|
73
101
|
}
|
|
74
102
|
|
|
75
103
|
function findMarkdownImageLinks(text) {
|
|
@@ -90,15 +118,15 @@ function findMarkdownImageLinks(text) {
|
|
|
90
118
|
* @param {string} text
|
|
91
119
|
* @returns
|
|
92
120
|
*/
|
|
93
|
-
function
|
|
121
|
+
function findAbsoluteLinks(text) {
|
|
94
122
|
let matches
|
|
95
123
|
let links = []
|
|
96
124
|
while ((matches = LIVE_LINKS_REGEX.exec(text)) !== null) {
|
|
97
125
|
if (matches.index === LIVE_LINKS_REGEX.lastIndex) {
|
|
98
126
|
LIVE_LINKS_REGEX.lastIndex++ // avoid infinite loops with zero-width matches
|
|
99
127
|
}
|
|
100
|
-
const [ match, url ] = matches
|
|
101
|
-
links.push(url)
|
|
128
|
+
const [ match, url, bracketLink ] = matches
|
|
129
|
+
links.push(url || bracketLink)
|
|
102
130
|
}
|
|
103
131
|
return links.filter(onlyUnique)
|
|
104
132
|
}
|
|
@@ -140,32 +168,22 @@ function findRelativeLinks(text) {
|
|
|
140
168
|
return relLinks.filter(onlyUnique)
|
|
141
169
|
}
|
|
142
170
|
|
|
143
|
-
function findLinksInFrontMatter(data
|
|
171
|
+
function findLinksInFrontMatter(data) {
|
|
144
172
|
const yamlStrings = traverse(data)
|
|
173
|
+
// console.log('yamlStrings', yamlStrings)
|
|
145
174
|
const linksInYml = yamlStrings.map((string) => {
|
|
146
175
|
if (LINK_PATTERN.test(string)) {
|
|
147
176
|
return [string]
|
|
148
177
|
}
|
|
149
178
|
// console.log('string', string)
|
|
150
|
-
|
|
179
|
+
const results = findLinks(string)
|
|
180
|
+
return results.links.concat(results.images)
|
|
151
181
|
})
|
|
152
182
|
.filter((x) => {
|
|
153
|
-
if (
|
|
183
|
+
if (x && x.length) {
|
|
154
184
|
return x.length
|
|
155
185
|
}
|
|
156
|
-
|
|
157
|
-
return x.all.length
|
|
158
|
-
}
|
|
159
|
-
return true
|
|
160
|
-
})
|
|
161
|
-
.map((x) => {
|
|
162
|
-
if (typeof x === 'string') {
|
|
163
|
-
return x
|
|
164
|
-
}
|
|
165
|
-
if (typeof x === 'object' && x.all) {
|
|
166
|
-
return x.all
|
|
167
|
-
}
|
|
168
|
-
return x
|
|
186
|
+
return false
|
|
169
187
|
})
|
|
170
188
|
.flat()
|
|
171
189
|
// console.log('linksInYml', linksInYml)
|
|
@@ -175,7 +193,7 @@ function findLinksInFrontMatter(data, linkFinder) {
|
|
|
175
193
|
function traverse(x, arr = []) {
|
|
176
194
|
if (typeof x === 'string') {
|
|
177
195
|
arr.push(x)
|
|
178
|
-
} else if (isArray(x)) {
|
|
196
|
+
} else if (Array.isArray(x)) {
|
|
179
197
|
traverseArray(x, arr)
|
|
180
198
|
} else if ((typeof x === 'object') && (x !== null)) {
|
|
181
199
|
traverseObject(x, arr)
|
|
@@ -183,20 +201,20 @@ function traverse(x, arr = []) {
|
|
|
183
201
|
return arr
|
|
184
202
|
}
|
|
185
203
|
function traverseArray(arr, acc) {
|
|
186
|
-
|
|
204
|
+
for (let i = 0; i < arr.length; i++) {
|
|
205
|
+
traverse(arr[i], acc)
|
|
206
|
+
}
|
|
187
207
|
}
|
|
188
208
|
function traverseObject(obj, acc) {
|
|
189
209
|
for (var key in obj) {
|
|
190
|
-
if (obj.hasOwnProperty(key))
|
|
210
|
+
if (obj.hasOwnProperty(key)) {
|
|
211
|
+
traverse(obj[key], acc)
|
|
212
|
+
}
|
|
191
213
|
}
|
|
192
214
|
}
|
|
193
215
|
|
|
194
|
-
function isArray(o) {
|
|
195
|
-
return Object.prototype.toString.call(o) === '[object Array]'
|
|
196
|
-
}
|
|
197
|
-
|
|
198
216
|
module.exports = {
|
|
199
217
|
findLinks,
|
|
200
|
-
|
|
218
|
+
findAbsoluteLinks,
|
|
201
219
|
findRelativeLinks
|
|
202
220
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const { getLineNumberFromMatch } = require('./utils')
|
|
2
|
-
// const { voidTags } = require('../html-to-json/tags')
|
|
3
2
|
|
|
4
3
|
// https://regex101.com/r/he9l06/2
|
|
5
4
|
// http://xahlee.info/js/html5_non-closing_tag.html
|
|
@@ -28,6 +27,6 @@ function findUnmatchedHtmlTags(block, filePath) {
|
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
module.exports = {
|
|
30
|
+
CLOSE_TAG_REGEX,
|
|
31
31
|
findUnmatchedHtmlTags,
|
|
32
|
-
CLOSE_TAG_REGEX
|
|
33
32
|
}
|