markdown-magic 3.0.2 → 3.0.4

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 (40) hide show
  1. package/README.md +295 -101
  2. package/cli.js +4 -1
  3. package/lib/block-parser.js +32 -28
  4. package/lib/block-parser.test.js +2 -0
  5. package/lib/cli.js +101 -22
  6. package/lib/cli.test.js +12 -12
  7. package/lib/index.js +418 -119
  8. package/lib/process-contents.js +59 -23
  9. package/lib/process-file.js +39 -4
  10. package/lib/transforms/code.js +33 -10
  11. package/lib/transforms/file.js +4 -2
  12. package/lib/transforms/index.js +114 -0
  13. package/lib/transforms/sectionToc.js +2 -2
  14. package/lib/transforms/toc.js +22 -4
  15. package/lib/transforms/wordCount.js +2 -2
  16. package/lib/utils/fs.js +8 -172
  17. package/lib/utils/fs.test.js +4 -162
  18. package/lib/utils/hash-file.js +28 -0
  19. package/lib/utils/logs.js +16 -2
  20. package/lib/utils/syntax.js +1 -0
  21. package/lib/utils/text.js +1 -1
  22. package/lib/utils/toposort.js +131 -0
  23. package/package.json +4 -3
  24. package/lib/globals.d.ts +0 -66
  25. package/lib/utils/md/filters.js +0 -20
  26. package/lib/utils/md/find-code-blocks.js +0 -88
  27. package/lib/utils/md/find-date.js +0 -32
  28. package/lib/utils/md/find-frontmatter.js +0 -92
  29. package/lib/utils/md/find-frontmatter.test.js +0 -17
  30. package/lib/utils/md/find-html-tags.js +0 -105
  31. package/lib/utils/md/find-images-md.js +0 -27
  32. package/lib/utils/md/find-images.js +0 -107
  33. package/lib/utils/md/find-links.js +0 -220
  34. package/lib/utils/md/find-unmatched-html-tags.js +0 -32
  35. package/lib/utils/md/fixtures/2022-01-22-date-in-filename.md +0 -14
  36. package/lib/utils/md/fixtures/file-with-frontmatter.md +0 -32
  37. package/lib/utils/md/fixtures/file-with-links.md +0 -153
  38. package/lib/utils/md/md.test.js +0 -105
  39. package/lib/utils/md/parse.js +0 -146
  40. package/lib/utils/md/utils.js +0 -19
package/lib/globals.d.ts DELETED
@@ -1,66 +0,0 @@
1
- export {}
2
-
3
- declare global {
4
- const BUILD_VERSION: string;
5
- interface TestGlob {
6
- greeting: string;
7
- duration?: number;
8
- color?: string;
9
- }
10
- }
11
-
12
-
13
-
14
- export type TinyProps = {
15
- message: string;
16
- count: number;
17
- disabled: boolean;
18
- };
19
-
20
- declare var Age: number;
21
-
22
- declare function greetTwo(greeting: string): void;
23
-
24
- interface GreetingSettings {
25
- greeting: string;
26
- duration?: number;
27
- color?: string;
28
- }
29
- declare function greet(setting: GreetingSettings): void;
30
-
31
- export namespace testFnTypes {
32
- type input = boolean | Function;
33
- }
34
-
35
- // Use namespaces to organize types.
36
- // Ref like GreetingLib.LogOptions
37
- export declare namespace GreetingLib {
38
- interface LogOptions {
39
- verbose?: boolean;
40
- }
41
- interface AlertOptions {
42
- modal: boolean;
43
- title?: string;
44
- color?: string;
45
- }
46
- }
47
-
48
- export declare namespace Xyz.Options {
49
- // Refer to via Xyz.Options.Log
50
- interface Log {
51
- verbose?: boolean;
52
- }
53
- interface Alert {
54
- modal: boolean;
55
- title?: string;
56
- color?: string;
57
- }
58
- }
59
-
60
- export namespace Dotted {
61
- type Name = number;
62
- }
63
-
64
- export declare class A {
65
- static foo(): void;
66
- }
@@ -1,20 +0,0 @@
1
- const IMAGE_POSTFIX_REGEX = /\.(png|apng|jpe?g|gif|webp|svg|avif)$/
2
- const RELATIVE_LINK_REGEX = /^(?!(?:(?:https?|ftp):\/\/|data:))((?:\.\.?\/)*)*([\w\d\-_./?=#%:+&]+)/
3
-
4
- function onlyUnique(value, index, self) {
5
- return self.indexOf(value) === index
6
- }
7
-
8
- function isImage(link = '') {
9
- return link.match(IMAGE_POSTFIX_REGEX)
10
- }
11
-
12
- function isRelative(filepath = '') {
13
- return RELATIVE_LINK_REGEX.test(filepath)
14
- }
15
-
16
- module.exports = {
17
- onlyUnique,
18
- isImage,
19
- isRelative
20
- }
@@ -1,88 +0,0 @@
1
- const { getLineCount, getLineNumberFromMatch } = require('./utils')
2
-
3
- // https://regex101.com/r/nIlW1U/6
4
- const CODE_BLOCK_REGEX = /^([A-Za-z \t]*)```([A-Za-z]*)?\n([\s\S]*?)```([A-Za-z \t]*)*$/gm
5
- // https://regex101.com/r/oPKKoC/1
6
- const REMOVE_CODE_BLOCK_REGEX = /^(?:[A-Za-z \t]*)?(```(?:[A-Za-z]*)?\n(?:[\s\S]*?)```)([A-Za-z \t]*)*$/gm
7
-
8
- /**
9
- * Parse code blocks out of markdown
10
- * @param {string} block
11
- * @param {Object} opts
12
- * @returns {Object}
13
- * @example
14
- * const { blocks, errors } = findCodeBlocks(content)
15
- * console.log('blocks', blocks)
16
- * console.log('errors', errors)
17
- */
18
- function findCodeBlocks(block, opts = {}) {
19
- const { filePath = '', includePositions } = opts
20
- let matches
21
- let errors = []
22
- let blocks = []
23
- const msg = (filePath) ? ` in ${filePath}` : ''
24
- while ((matches = CODE_BLOCK_REGEX.exec(block)) !== null) {
25
- if (matches.index === CODE_BLOCK_REGEX.lastIndex) {
26
- CODE_BLOCK_REGEX.lastIndex++ // avoid infinite loops with zero-width matches
27
- }
28
- const [ match, prefix, syntax, content, postFix ] = matches
29
- const lineNumber = getLineNumberFromMatch(block, matches)
30
- let hasError = false
31
- /* // debug
32
- console.log(`prefix: "${prefix}"`)
33
- console.log(`postFix: "${postFix}"`)
34
- console.log('syntax:', lang)
35
- console.log('Content:')
36
- console.log(content.trim())
37
- console.log('───────────────────────')
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
50
-
51
- /* Validate code blocks */
52
- if (prefix && prefix.match(/\S/)) {
53
- hasError = true
54
- errors.push({
55
- line: lineNumber,
56
- index: matches.index,
57
- message: `Prefix "${prefix}" not allowed on line ${lineNumber}. Fix the code block${msg}.`,
58
- block: match
59
- })
60
- }
61
- if (postFix && postFix.match(/\S/)) {
62
- hasError = true
63
- const line = lineNumber + (getLineCount(match) - 1)
64
- errors.push({
65
- line,
66
- index: matches.index + match.length,
67
- message: `Postfix "${postFix}" not allowed on line ${line}. Fix the code block${msg}.`,
68
- block: match
69
- })
70
- }
71
-
72
- if (!hasError) {
73
- codeBlock.code = content.trim()
74
- blocks.push(codeBlock)
75
- }
76
- }
77
-
78
- return {
79
- errors,
80
- blocks
81
- }
82
- }
83
-
84
- module.exports = {
85
- findCodeBlocks,
86
- CODE_BLOCK_REGEX,
87
- REMOVE_CODE_BLOCK_REGEX
88
- }
@@ -1,32 +0,0 @@
1
- const DATE_FORMAT_REGEX = /(([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]))-?/g
2
-
3
- function findDate({
4
- frontmatter = {},
5
- dateKey = 'date',
6
- filePath
7
- }) {
8
- let date = frontmatter[dateKey]
9
- if (!date && filePath) {
10
- const dateFromFile = filePath.match(DATE_FORMAT_REGEX)
11
- if (dateFromFile) {
12
- date = dateFromFile[0].replace(/-$/, '')
13
- }
14
- }
15
- return convertDateToString(date)
16
- }
17
-
18
- function convertDateToString(dateValue) {
19
- let date = dateValue
20
- if (typeof dateValue === 'string') {
21
- date = dateValue
22
- } else if (dateValue instanceof Date) {
23
- var newDate = new Date(dateValue.toString())
24
- date = newDate.toISOString().substring(0, 10)
25
- }
26
- return date
27
- }
28
-
29
- module.exports = {
30
- findDate,
31
- convertDateToString
32
- }
@@ -1,92 +0,0 @@
1
- const matter = require('gray-matter')
2
- /* Match <!-- frontmatter --> https://regex101.com/r/Q9bBxC/1 */
3
- const HIDDEN_FRONTMATTER_REGEX = /^<!--+(?:\r\n|\r|\n)([\w\W]*?)--+>/g
4
- // const HIDDEN_FRONTMATTER_REGEX = /^<!--.*((.|\r?\n)*?.*-->)/g
5
-
6
- /* Match --- frontmatter --- https://regex101.com/r/d7eAw4/1 */
7
- const FRONTMATTER_REGEX = /(^--+(?:\r\n|\r|\n)([\w\W]*?)--+)/
8
- // const FRONTMATTER_REGEX = /^---.*((.|\r?\n)*?.*---)/gm
9
-
10
- function removeConflictingContent(str) {
11
- return str
12
- .replace(/[\t ]{1}---/g, '__LINE_BREAK__')
13
- .replace(/[\t ]--+>/g, '__CLOSE_COMMENT__')
14
- // TODO also handle nested <!-- comments -->
15
- }
16
-
17
- function replaceConflictingContent(str) {
18
- return str
19
- .replace(/__LINE_BREAK__/g, ' ---')
20
- .replace(/__CLOSE_COMMENT__/g, ' -->')
21
- }
22
-
23
- function findFrontmatter(content = '') {
24
- const text = removeConflictingContent(content.trim())
25
- const hasFrontMatter = text.match(FRONTMATTER_REGEX)
26
- const hasHiddenFrontMatter = text.match(HIDDEN_FRONTMATTER_REGEX)
27
- let raw = ''
28
- let match = ''
29
- let isHidden = false
30
- // console.log('hasFrontMatter', hasFrontMatter)
31
- // console.log('hasHiddenFrontMatter', hasHiddenFrontMatter)
32
- if (hasFrontMatter) {
33
- raw = hasFrontMatter[1]
34
- match = raw.trim()
35
- // Fix Leading frontmatter brackets
36
- .replace(/^---+/, '---')
37
- // Trailing frontmatter brackets
38
- .replace(/--+$/, `---`)
39
- } else if (hasHiddenFrontMatter) {
40
- isHidden = true
41
- raw = hasHiddenFrontMatter[1]
42
- match = raw.trim()
43
- // Leading frontmatter brackets
44
- .replace(/<!--+/, '---')
45
- // Trailing frontmatter brackets
46
- .replace(/--+>/, `---`)
47
- }
48
- return {
49
- frontMatterRaw: replaceConflictingContent(raw),
50
- frontMatter: replaceConflictingContent(match),
51
- isHidden
52
- }
53
- }
54
-
55
- function parseFrontmatter(text) {
56
- const { frontMatter, frontMatterRaw } = findFrontmatter(text)
57
- // console.log('frontMatter', frontMatter)
58
- let frontmatter = { data: {}, content: '' }
59
- /* Missing all frontmatter */
60
- if (!frontMatter) {
61
- // throw new Error(`Missing or broken frontmatter in ${filePath}. Double check file for --- frontmatter tags in files`)
62
- return frontmatter
63
- }
64
-
65
- let mdContent = text
66
- if (frontMatterRaw) {
67
- mdContent = text
68
- // Replace frontmatter brackets
69
- .replace(frontMatterRaw, frontMatter)
70
- // Replace leading lines
71
- // .replace(/---+\s+\n/g, '---\n')
72
- }
73
-
74
- try {
75
- frontmatter = matter(mdContent)
76
- } catch (err) {
77
- /* Add line numbers to output */
78
- const formattedError = frontMatterRaw.split('\n').map((line, i) => {
79
- return `${i + 1}. ${line}`
80
- })
81
- throw new Error(`Frontmatter error:\n${err.message}\n${formattedError.join('\n')}`)
82
- }
83
- // console.log('frontMatter', frontmatter)
84
- return Object.assign(frontmatter, { frontMatterRaw })
85
- }
86
-
87
- module.exports = {
88
- parseFrontmatter,
89
- findFrontmatter,
90
- HIDDEN_FRONTMATTER_REGEX,
91
- FRONTMATTER_REGEX
92
- }
@@ -1,17 +0,0 @@
1
- const path = require('path')
2
- const fs = require('fs')
3
- const { test } = require('uvu')
4
- const assert = require('uvu/assert')
5
- const { findLinks } = require('./find-links')
6
- const { parseFrontmatter } = require('./find-frontmatter')
7
-
8
- const FILE_PATH = path.join(__dirname, 'fixtures/file-with-frontmatter.md')
9
- const fileWithLinks = fs.readFileSync(FILE_PATH, 'utf-8')
10
-
11
- test('Find frontmatter', async () => {
12
- const frontmatter = parseFrontmatter(fileWithLinks)
13
- console.log('frontmatter', frontmatter)
14
- assert.is(typeof frontmatter.data, 'object')
15
- })
16
-
17
- test.run()
@@ -1,105 +0,0 @@
1
-
2
- // Might need ([\s\S]*?) instead of '*' in between tags
3
- const HTML_TAG = /<([a-zA-Z1-6]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)/gim
4
-
5
- const MATCH_HTML_TAGS_REGEX = /<([a-zA-Z1-6]+)\b([^>]*)>*(?:>([\s\S]*?)<\/\1>|\s?\/?>)/gm
6
- // old forces closes / - /<([a-zA-Z1-6]+)\b([^>]*)>*(?:>([\s\S]*?)<\/\1>|\s?\/>)/gm
7
-
8
- function findHtmlTags(mdContents) {
9
- const parents = mdContents
10
- /* Fix non terminating <tags> */
11
- .replace(/(['"`]<(.*)>['"`])/gm, '_$2_')
12
- .match(MATCH_HTML_TAGS_REGEX)
13
- // console.log('parents', parents)
14
-
15
- if (parents) {
16
- // const children = parents.filter(Boolean).map((p) => {
17
- // return p.match(HTML_TAG)
18
- // })
19
- // console.log('children', children)
20
- }
21
-
22
- const htmlTags = mdContents
23
- /* Fix non terminating <tags> */
24
- .replace(/(['"`]<(.*)>['"`])/gm, '_$2_')
25
- .match(MATCH_HTML_TAGS_REGEX)
26
- // console.log('htmlTags', htmlTags)
27
-
28
- let tags = []
29
- if (htmlTags) {
30
- let propsValues = {}
31
- // var regexSingleTag = /<([a-zA-Z1-6]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)/
32
- // var regexSingleTag = /<([a-zA-Z1-6]+)([^<]+)*(?:>([\s\S]*?)<\/\1>|\s*\/>)/
33
- var regexSingleTag = /<([a-zA-Z1-6]+)\b([^>]*)>*(?:>([\s\S]*?)<\/\1>|\s?\/?>)/
34
- for (var i = 0; i < htmlTags.length; i++) {
35
- // console.log('htmlTags[i]', htmlTags[i])
36
- var tagMatches = regexSingleTag.exec(htmlTags[i]) || []
37
- // console.log('tagMatches', tagMatches)
38
- var [ match, tag, props ] = tagMatches
39
- // console.log(`Tag #${i} ${tag}`)
40
- if (props) {
41
- const cleanProps = props
42
- // Remove new lines and tabs
43
- .replace(/\n\t/g, '')
44
- // Remove extra spaces
45
- .replace(/\s\s+/g, ' ')
46
- .trim()
47
-
48
- propsValues = cleanProps.split(" ").reduce((acc, curr) => {
49
- const hasQuotes = curr.match(/=['"]/)
50
- // Check key="value" | key='value' | key={value}
51
- const propWithValue = /([A-Za-z-_$]+)=['{"](.*)['}"]/g.exec(curr)
52
- if (propWithValue && propWithValue[1]) {
53
- return {
54
- ...acc,
55
- [`${propWithValue[1]}`]: (hasQuotes) ? propWithValue[2] : convert(propWithValue[2])
56
- }
57
- }
58
- // Check isLoading boolean props
59
- const booleanProp = curr.match(/([A-Za-z]*)/)
60
- if (booleanProp && booleanProp[1]) {
61
- return {
62
- ...acc,
63
- [`${booleanProp[1]}`]: true
64
- }
65
- }
66
- return acc
67
- }, {})
68
- }
69
-
70
- tags.push({
71
- tag: tag,
72
- props: propsValues,
73
- raw: match
74
- })
75
- }
76
- }
77
- return tags
78
- }
79
-
80
- function convert(value) {
81
- if (value === 'false') {
82
- return false
83
- }
84
- if (value === 'true') {
85
- return true
86
- }
87
- const isNumber = Number(value)
88
- if (typeof isNumber === 'number' && !isNaN(isNumber)) {
89
- return isNumber
90
- }
91
-
92
- try {
93
- const val = JSON.parse(value)
94
- return val
95
- } catch (err) {
96
-
97
- }
98
-
99
- return value
100
- }
101
-
102
- module.exports = {
103
- findHtmlTags,
104
- MATCH_HTML_TAGS_REGEX
105
- }
@@ -1,27 +0,0 @@
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,107 +0,0 @@
1
- const { onlyUnique, isImage, isRelative } = require('./filters')
2
- const { findLinks, findAbsoluteLinks } = require('./find-links')
3
- const { findMarkdownImages, MARKDOWN_IMAGE_REGEX } = require('./find-images-md')
4
-
5
- // https://regex101.com/r/Uxgu3P/1
6
- const RELATIVE_IMAGES_REGEX = /(<img.*?src=['"])(?!(?:(?:https?|ftp):\/\/|data:))(\.?\/)?(.*?)(['"].*?\/?>)/gim
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
-
23
- /**
24
- * Get image links from content
25
- * @param {string} content
26
- * @param {findImagesOpts} [opts] - optional links to use to avoid re-parse
27
- * @returns {findImagesResult}
28
- */
29
- function findImages(content = '', opts = {}) {
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
- }
38
- const imageLinks = foundLinks.filter(isImage)
39
- const markdownLinks = findMarkdownImages(content)
40
- const allImageLinks = imageLinks.concat(markdownLinks)
41
- const all = (!unique) ? allImageLinks : allImageLinks.filter(onlyUnique)
42
- return {
43
- all,
44
- absolute: all.filter((link) => !isRelative(link)),
45
- relative: all.filter((link) => isRelative(link)),
46
- md: markdownLinks
47
- }
48
- }
49
-
50
- /**
51
- * Get absolute url image links
52
- * @param {string} content
53
- * @param {array} [links] - optional links to use to avoid re-parse
54
- * @returns {array}
55
- */
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)
61
-
62
- return allImageLinks
63
- .filter(onlyUnique)
64
- .filter((link) => !isRelative(link))
65
- }
66
-
67
- /**
68
- * Get relative image links from content
69
- * @param {string} text
70
- * @returns {array}
71
- */
72
- function findRelativeImages(text) {
73
- const imgTags = findRelativeImgTags(text) || []
74
- const mdTags = findMarkdownImages(text).filter(isRelative)
75
- return imgTags.concat(mdTags)
76
- }
77
-
78
- /*
79
- // https://regex101.com/r/SvMfme/1
80
- <img src="img/deploy/button.svg" />
81
- <img src="/img/deploy/button.svg" />
82
- <img src='/img/deploy/button.svg' />
83
- <img src='./img/deploy/button.svg' />
84
- <img src='../img/deploy/button.svg' />
85
- <img src='../../img/deploy/button.svg' />
86
- */
87
- function findRelativeImgTags(block) {
88
- let matches
89
- let relLinks = []
90
- while ((matches = RELATIVE_IMAGES_REGEX.exec(block)) !== null) {
91
- if (matches.index === RELATIVE_IMAGES_REGEX.lastIndex) {
92
- RELATIVE_IMAGES_REGEX.lastIndex++ // avoid infinite loops with zero-width matches
93
- }
94
- const [ match, _, start, link ] = matches
95
- relLinks.push(`${start || ''}${link}`)
96
- }
97
- return relLinks.filter(onlyUnique)
98
- }
99
-
100
- module.exports = {
101
- findImages,
102
- findAbsoluteImages,
103
- findRelativeImages,
104
- findMarkdownImages,
105
- MARKDOWN_IMAGE_REGEX,
106
- RELATIVE_IMAGES_REGEX
107
- }