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.
Files changed (49) hide show
  1. package/README.md +43 -29
  2. package/lib/block-parser-js.test.js +148 -156
  3. package/lib/block-parser.js +255 -262
  4. package/lib/block-parser.test.js +43 -6
  5. package/lib/cli.js +30 -19
  6. package/lib/cli.test.js +73 -73
  7. package/lib/globals.d.ts +66 -0
  8. package/lib/index.js +43 -9
  9. package/lib/process-contents.js +80 -39
  10. package/lib/process-file.js +4 -1
  11. package/lib/transforms/code.js +4 -10
  12. package/lib/transforms/file.js +7 -10
  13. package/lib/transforms/index.js +0 -0
  14. package/lib/transforms/remote.js +2 -3
  15. package/lib/transforms/sectionToc.js +18 -0
  16. package/lib/transforms/toc.js +10 -335
  17. package/lib/types.js +11 -0
  18. package/lib/utils/fs.js +21 -19
  19. package/lib/utils/fs.test.js +4 -5
  20. package/lib/utils/logs.js +7 -2
  21. package/lib/utils/md/filters.js +5 -5
  22. package/lib/utils/md/find-code-blocks.js +16 -8
  23. package/lib/utils/md/find-frontmatter.js +11 -13
  24. package/lib/utils/md/find-frontmatter.test.js +2 -2
  25. package/lib/utils/md/find-html-tags.js +1 -1
  26. package/lib/utils/md/find-images-md.js +27 -0
  27. package/lib/utils/md/find-images.js +39 -34
  28. package/lib/utils/md/find-links.js +72 -54
  29. package/lib/utils/md/find-unmatched-html-tags.js +1 -2
  30. package/lib/utils/md/fixtures/file-with-links.md +10 -0
  31. package/lib/utils/md/md.test.js +72 -4
  32. package/lib/utils/md/parse.js +91 -67
  33. package/lib/utils/regex-timeout.js +2 -1
  34. package/lib/utils/regex.js +3 -2
  35. package/lib/utils/remoteRequest.js +1 -0
  36. package/lib/utils/syntax.js +3 -0
  37. package/lib/utils/text.js +71 -3
  38. package/lib/utils/text.test.js +3 -9
  39. package/lib/utils/toc.js +315 -0
  40. package/package.json +7 -3
  41. package/lib/options-parser.js +0 -498
  42. package/lib/options-parser.test.js +0 -1237
  43. package/lib/utils/html-to-json/compat.js +0 -42
  44. package/lib/utils/html-to-json/format.js +0 -64
  45. package/lib/utils/html-to-json/index.js +0 -37
  46. package/lib/utils/html-to-json/lexer.js +0 -345
  47. package/lib/utils/html-to-json/parser.js +0 -146
  48. package/lib/utils/html-to-json/stringify.js +0 -37
  49. 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
- // escapeRegexString
81
- return regexInfo[1]
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 })
@@ -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
- console.log(util.inspect(myObject, false, null, true /* enable colors */))
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')
@@ -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(/(png|jpe?g|gif|webp|svg)$/)
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, filePath = '') {
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
- blocks.push({
61
- line: lineNumber,
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 findFrontmatterRaw(content = '') {
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
- rawFrontMatter: replaceConflictingContent(raw),
49
+ frontMatterRaw: replaceConflictingContent(raw),
52
50
  frontMatter: replaceConflictingContent(match),
53
51
  isHidden
54
52
  }
55
53
  }
56
54
 
57
- function findFrontmatter(text) {
58
- const { frontMatter, rawFrontMatter } = findFrontmatterRaw(text)
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 (rawFrontMatter) {
66
+ if (frontMatterRaw) {
69
67
  mdContent = text
70
68
  // Replace frontmatter brackets
71
- .replace(rawFrontMatter, frontMatter)
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 = rawFrontMatter.split('\n').map((line, i) => {
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, { rawFrontMatter })
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 { findFrontmatter } = require('./find-frontmatter')
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 = findFrontmatter(fileWithLinks, FILE_PATH)
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, findLiveLinks } = require('./find-links')
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 {array} [links] - optional links to use to avoid re-parse
13
- * @returns {array}
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
- const foundLinks = (links && Array.isArray(links)) ? links : findLinks(content).all
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 mdLinks = findMarkdownImages(content)
20
- const allImageLinks = imageLinks.concat(mdLinks)
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
- live: all.filter((link) => !isRelative(link)),
44
+ absolute: all.filter((link) => !isRelative(link)),
25
45
  relative: all.filter((link) => isRelative(link)),
26
- md: mdLinks
46
+ md: markdownLinks
27
47
  }
28
48
  }
29
49
 
30
50
  /**
31
- * Get live url image links
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 findLiveImages(content = '', links) {
37
- const foundLinks = (links && Array.isArray(links)) ? links : findLiveLinks(content)
38
- const imageLinks = foundLinks.filter((link) => {
39
- return link.match(/(png|jpe?g|gif|webp|svg)$/)
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} content
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
- findLiveImages,
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
- * Finds all links in text relative or otherwise
22
- * @param {string} mdContents
23
- * @returns
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
- let frontmatterLinks = []
34
- if (opts.frontmatter) {
35
- frontmatterLinks = findLinksInFrontMatter(opts.frontmatter, findLinks)
36
- }
37
-
38
- const allLinks = normalLinks.concat(mdImgLinks).concat(frontmatterLinks)
39
- const all = (!unique) ? allLinks : allLinks.filter(onlyUnique)
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
- let images = mdImgLinks
42
- let links = []
43
- for (let i = 0; i < all.length; i++) {
44
- const link = all[i]
45
- if (isImage(link)) {
46
- images.push(link)
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
- links.push(link)
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 allImg = images.filter(onlyUnique)
54
- const allLink = links.filter(onlyUnique).filter(function(el) {
55
- return allImg.indexOf(el) < 0
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 findLiveLinks(text) {
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, linkFinder) {
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
- return linkFinder(string)
179
+ const results = findLinks(string)
180
+ return results.links.concat(results.images)
151
181
  })
152
182
  .filter((x) => {
153
- if (Array.isArray(x)) {
183
+ if (x && x.length) {
154
184
  return x.length
155
185
  }
156
- if (x && x.all) {
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
- arr.forEach((x) => traverse(x, acc))
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)) traverse(obj[key], acc)
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
- findLiveLinks,
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
  }