markdown-magic 2.6.1 → 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 (58) hide show
  1. package/README.md +47 -37
  2. package/cli.js +5 -82
  3. package/lib/block-parser-js.test.js +171 -0
  4. package/lib/block-parser.js +382 -0
  5. package/lib/block-parser.test.js +479 -0
  6. package/lib/cli.js +245 -0
  7. package/lib/cli.test.js +409 -0
  8. package/lib/defaults.js +12 -0
  9. package/lib/globals.d.ts +66 -0
  10. package/lib/index.js +353 -184
  11. package/lib/index.test.js +11 -0
  12. package/lib/process-contents.js +371 -0
  13. package/lib/process-file.js +37 -0
  14. package/lib/transforms/code.js +67 -28
  15. package/lib/transforms/file.js +17 -17
  16. package/lib/transforms/index.js +0 -114
  17. package/lib/transforms/remote.js +8 -6
  18. package/lib/transforms/sectionToc.js +18 -0
  19. package/lib/transforms/toc.js +12 -265
  20. package/lib/transforms/wordCount.js +5 -0
  21. package/lib/types.js +11 -0
  22. package/lib/utils/fs.js +342 -0
  23. package/lib/utils/fs.test.js +267 -0
  24. package/lib/utils/index.js +19 -0
  25. package/{cli-utils.js → lib/utils/load-config.js} +2 -6
  26. package/lib/utils/logs.js +94 -0
  27. package/lib/utils/md/filters.js +20 -0
  28. package/lib/utils/md/find-code-blocks.js +88 -0
  29. package/lib/utils/md/find-date.js +32 -0
  30. package/lib/utils/md/find-frontmatter.js +92 -0
  31. package/lib/utils/md/find-frontmatter.test.js +17 -0
  32. package/lib/utils/md/find-html-tags.js +105 -0
  33. package/lib/utils/md/find-images-md.js +27 -0
  34. package/lib/utils/md/find-images.js +107 -0
  35. package/lib/utils/md/find-links.js +220 -0
  36. package/lib/utils/md/find-unmatched-html-tags.js +32 -0
  37. package/lib/utils/md/fixtures/2022-01-22-date-in-filename.md +14 -0
  38. package/lib/utils/md/fixtures/file-with-frontmatter.md +32 -0
  39. package/lib/utils/md/fixtures/file-with-links.md +153 -0
  40. package/lib/utils/md/md.test.js +105 -0
  41. package/lib/utils/md/parse.js +146 -0
  42. package/lib/utils/md/utils.js +19 -0
  43. package/lib/utils/regex-timeout.js +84 -0
  44. package/lib/utils/regex.js +40 -6
  45. package/lib/utils/remoteRequest.js +55 -0
  46. package/lib/utils/syntax.js +82 -0
  47. package/lib/utils/text.js +328 -0
  48. package/lib/utils/text.test.js +305 -0
  49. package/lib/utils/toc.js +315 -0
  50. package/package.json +30 -26
  51. package/index.js +0 -46
  52. package/lib/processFile.js +0 -154
  53. package/lib/updateContents.js +0 -125
  54. package/lib/utils/_md.test.js +0 -63
  55. package/lib/utils/new-parser.js +0 -412
  56. package/lib/utils/new-parser.test.js +0 -324
  57. package/lib/utils/weird-parse.js +0 -230
  58. package/lib/utils/weird-parse.test.js +0 -217
@@ -0,0 +1,267 @@
1
+ const path = require('path')
2
+ const { test } = require('uvu')
3
+ const assert = require('uvu/assert')
4
+ const { glob, globWithGit } = require('smart-glob')
5
+ const GREEN = '\x1b[32m%s\x1b[0m';
6
+ const { findUp, getFilePaths, getGitignoreContents, convertToRelativePath } = require('./fs')
7
+
8
+ const ROOT_DIR = path.resolve(__dirname, '../../')
9
+
10
+ test('Exports API', () => {
11
+ assert.equal(typeof findUp, 'function', 'undefined val')
12
+ })
13
+
14
+ test('Finds file from file', async () => {
15
+ const startDir = path.resolve(__dirname, '../index.js')
16
+ const file = await findUp(startDir, 'README.md')
17
+ assert.ok(file)
18
+ assert.equal(path.basename(file || ''), 'README.md')
19
+ })
20
+
21
+ test('Finds file from dir', async () => {
22
+ const startDir = path.resolve(__dirname, '../')
23
+ const file = await findUp(startDir, 'README.md')
24
+ assert.ok(file)
25
+ assert.equal(path.basename(file || ''), 'README.md')
26
+ })
27
+
28
+ test('getFilePaths /\.test\.js?$/', async () => {
29
+ const files = await getFilePaths(ROOT_DIR, {
30
+ patterns: [
31
+ /\.test\.js?$/,
32
+ ],
33
+ ignore: [
34
+ /node_modules/,
35
+ ],
36
+ })
37
+ const foundFiles = convertToRelative(files, ROOT_DIR)
38
+ console.log('foundFiles', foundFiles)
39
+ assert.is(Array.isArray(files), true)
40
+ assert.equal(foundFiles, [
41
+ 'lib/block-parser-js.test.js',
42
+ 'lib/block-parser.test.js',
43
+ 'lib/cli.test.js',
44
+ 'lib/index.test.js',
45
+ 'lib/utils/fs.test.js',
46
+ "lib/utils/md/find-frontmatter.test.js",
47
+ "lib/utils/md/md.test.js",
48
+ "lib/utils/text.test.js",
49
+ 'test/errors.test.js',
50
+ 'test/transforms.test.js'
51
+ ])
52
+ })
53
+
54
+ test('getFilePaths /\.mdx?$/, /\.test\.js?$/', async () => {
55
+ const files = await getFilePaths(ROOT_DIR, {
56
+ patterns: [
57
+ /fixtures\/md\/(.*)\.mdx?$/,
58
+ // /\.js$/,
59
+ /\.test\.js?$/,
60
+ ],
61
+ ignore: [
62
+ // /^node_modules\//,
63
+ /node_modules/,
64
+ // /\.git/,
65
+ // /NOTES\.md/
66
+ ],
67
+ //excludeGitIgnore: true,
68
+ excludeHidden: true,
69
+ })
70
+ const foundFiles = convertToRelative(files, ROOT_DIR)
71
+ //console.log('foundFiles', foundFiles)
72
+ assert.is(Array.isArray(files), true)
73
+ assert.equal(foundFiles, [
74
+ 'lib/block-parser-js.test.js',
75
+ 'lib/block-parser.test.js',
76
+ 'lib/cli.test.js',
77
+ 'lib/index.test.js',
78
+ 'lib/utils/fs.test.js',
79
+ "lib/utils/md/find-frontmatter.test.js",
80
+ "lib/utils/md/md.test.js",
81
+ "lib/utils/text.test.js",
82
+ 'test/errors.test.js',
83
+ 'test/fixtures/md/basic.md',
84
+ "test/fixtures/md/broken-inline.md",
85
+ 'test/fixtures/md/error-missing-transforms-two.md',
86
+ 'test/fixtures/md/error-missing-transforms.md',
87
+ 'test/fixtures/md/error-no-block-transform-defined.md',
88
+ 'test/fixtures/md/error-unbalanced.md',
89
+ 'test/fixtures/md/format-inline.md',
90
+ 'test/fixtures/md/format-with-wacky-indentation.md',
91
+ 'test/fixtures/md/inline-two.md',
92
+ 'test/fixtures/md/inline.md',
93
+ 'test/fixtures/md/mdx-file.mdx',
94
+ 'test/fixtures/md/missing-transform.md',
95
+ 'test/fixtures/md/mixed.md',
96
+ 'test/fixtures/md/nested/nested.md',
97
+ 'test/fixtures/md/no-transforms.md',
98
+ 'test/fixtures/md/string.md',
99
+ 'test/fixtures/md/syntax-legacy-colon.md',
100
+ 'test/fixtures/md/syntax-legacy-query.md',
101
+ 'test/fixtures/md/syntax-mixed.md',
102
+ 'test/fixtures/md/transform-code.md',
103
+ 'test/fixtures/md/transform-custom.md',
104
+ 'test/fixtures/md/transform-file.md',
105
+ 'test/fixtures/md/transform-remote.md',
106
+ 'test/fixtures/md/transform-toc.md',
107
+ 'test/fixtures/md/transform-wordCount.md',
108
+ 'test/transforms.test.js'
109
+ ])
110
+ })
111
+
112
+ test('getFilePaths glob', async () => {
113
+ /*
114
+ const x = await glob('**.md')
115
+ console.log(x)
116
+ process.exit(1)
117
+ /** */
118
+
119
+ const files = await getFilePaths(ROOT_DIR, {
120
+ patterns: [
121
+ /\.test\.js?$/,
122
+ // /(.*)\.mdx?$/,
123
+ 'test/fixtures/md/**.{md,mdx}'
124
+ // /^[^\/]+\.md?$/,
125
+ // '**.json',
126
+ // '**/**.js',
127
+ // '**.md',
128
+ //'/(.*).md$/',
129
+ // '/^test/',
130
+ // 'test/**'
131
+ ///(.*)\.md/g
132
+ ],
133
+ ignore: [
134
+ // /^node_modules\//,
135
+ /node_modules/,
136
+ // /(.*)\.js$/,
137
+ // /\.git/,
138
+ // /NOTES\.md/
139
+ ],
140
+ excludeGitIgnore: true,
141
+ excludeHidden: true,
142
+ })
143
+ const foundFiles = convertToRelative(files, ROOT_DIR)
144
+ console.log('foundFiles', foundFiles)
145
+ /*
146
+ aggregateReports()
147
+ process.exit(1)
148
+ /** */
149
+ assert.is(Array.isArray(files), true)
150
+ assert.equal(foundFiles, [
151
+ 'lib/block-parser-js.test.js',
152
+ 'lib/block-parser.test.js',
153
+ 'lib/cli.test.js',
154
+ 'lib/index.test.js',
155
+ 'lib/utils/fs.test.js',
156
+ "lib/utils/md/find-frontmatter.test.js",
157
+ "lib/utils/md/md.test.js",
158
+ 'lib/utils/text.test.js',
159
+ // 'misc/old-test/main.test.js',
160
+ 'test/errors.test.js',
161
+ 'test/fixtures/md/basic.md',
162
+ "test/fixtures/md/broken-inline.md",
163
+ 'test/fixtures/md/error-missing-transforms-two.md',
164
+ 'test/fixtures/md/error-missing-transforms.md',
165
+ 'test/fixtures/md/error-no-block-transform-defined.md',
166
+ 'test/fixtures/md/error-unbalanced.md',
167
+ 'test/fixtures/md/format-inline.md',
168
+ 'test/fixtures/md/format-with-wacky-indentation.md',
169
+ 'test/fixtures/md/inline-two.md',
170
+ 'test/fixtures/md/inline.md',
171
+ 'test/fixtures/md/mdx-file.mdx',
172
+ 'test/fixtures/md/missing-transform.md',
173
+ 'test/fixtures/md/mixed.md',
174
+ 'test/fixtures/md/no-transforms.md',
175
+ 'test/fixtures/md/string.md',
176
+ 'test/fixtures/md/syntax-legacy-colon.md',
177
+ 'test/fixtures/md/syntax-legacy-query.md',
178
+ 'test/fixtures/md/syntax-mixed.md',
179
+ 'test/fixtures/md/transform-code.md',
180
+ 'test/fixtures/md/transform-custom.md',
181
+ 'test/fixtures/md/transform-file.md',
182
+ 'test/fixtures/md/transform-remote.md',
183
+ 'test/fixtures/md/transform-toc.md',
184
+ 'test/fixtures/md/transform-wordCount.md',
185
+ 'test/transforms.test.js'
186
+ ])
187
+ })
188
+
189
+ test('getGitignoreContents', async () => {
190
+ const files = await getGitignoreContents()
191
+ console.log('files', files)
192
+ assert.is(Array.isArray(files), true)
193
+ assert.equal(files, [
194
+ 'logs',
195
+ '*.log',
196
+ 'npm-debug.log*',
197
+ 'yarn-debug.log*',
198
+ 'yarn-error.log*',
199
+ 'test/fixtures/output',
200
+ '_out.md',
201
+ 'misc',
202
+ 'misc/**/**.js',
203
+ '**/old-test/cool.md',
204
+ 'pids',
205
+ '*.pid',
206
+ '*.seed',
207
+ '*.pid.lock',
208
+ 'lib-cov',
209
+ 'coverage',
210
+ '.nyc_output',
211
+ '.grunt',
212
+ 'bower_components',
213
+ '.lock-wscript',
214
+ 'build/Release',
215
+ 'node_modules',
216
+ 'jspm_packages',
217
+ 'typings',
218
+ '.npm',
219
+ '.eslintcache',
220
+ '.node_repl_history',
221
+ '*.tgz',
222
+ '.yarn-integrity',
223
+ '.env',
224
+ '.env.test',
225
+ '.cache',
226
+ '.next',
227
+ '.nuxt',
228
+ '.vuepress/dist',
229
+ '.serverless',
230
+ '.fusebox',
231
+ '.dynamodb',
232
+ '.DS_Store',
233
+ '.AppleDouble',
234
+ '.LSOverride',
235
+ 'Icon',
236
+ '._*',
237
+ '.DocumentRevisions-V100',
238
+ '.fseventsd',
239
+ '.Spotlight-V100',
240
+ '.TemporaryItems',
241
+ '.Trashes',
242
+ '.VolumeIcon.icns',
243
+ '.com.apple.timemachine.donotpresent',
244
+ '.AppleDB',
245
+ '.AppleDesktop',
246
+ 'Network Trash Folder',
247
+ 'Temporary Items',
248
+ '.apdisk'
249
+ ])
250
+ })
251
+
252
+ async function aggregateReports() {
253
+ console.log(GREEN, `Done.`);
254
+ }
255
+
256
+ function convertToRelative(files, dir) {
257
+ return files.map((f) => convertToRelativePath(f, dir)).sort()
258
+ }
259
+
260
+ async function getIgnores(dir){
261
+ const files = await getGitignoreContents()
262
+ console.log('files', files)
263
+ }
264
+
265
+ //getIgnores(process.cwd())
266
+
267
+ test.run()
@@ -0,0 +1,19 @@
1
+
2
+ function onlyUnique(value, index, self) {
3
+ return self.indexOf(value) === index
4
+ }
5
+
6
+ function getCodeLocation(srcPath, line, column = '0') {
7
+ return `${srcPath}:${line}:${column}`
8
+ }
9
+
10
+ function pluralize(thing, single = '', plural = '') {
11
+ const count = Array.isArray(thing) ? thing.length : Number(thing)
12
+ return count === 1 ? single : plural
13
+ }
14
+
15
+ module.exports = {
16
+ pluralize,
17
+ onlyUnique,
18
+ getCodeLocation
19
+ }
@@ -1,11 +1,7 @@
1
- const resolve = require('path').resolve
1
+ const path = require('path')
2
2
 
3
3
  const loadJSConfig = getAttemptModuleRequireFn(function onFail(configPath, requirePath) {
4
4
  console.log(`Unable to find JS config at "${configPath}". Attempted to require as "${requirePath}"`)
5
- // log.error({
6
- // message: colors.red(`Unable to find JS config at "${configPath}". Attempted to require as "${requirePath}"`),
7
- // ref: 'unable-to-find-config',
8
- // })
9
5
  return undefined
10
6
  })
11
7
 
@@ -15,7 +11,7 @@ const loadJSConfig = getAttemptModuleRequireFn(function onFail(configPath, requi
15
11
  * @return {String} the module path to require
16
12
  */
17
13
  function getModuleRequirePath(moduleName) {
18
- return moduleName[0] === '.' ? resolve(process.cwd(), moduleName) : moduleName
14
+ return moduleName[0] === '.' ? path.resolve(process.cwd(), moduleName) : moduleName
19
15
  }
20
16
 
21
17
  function getAttemptModuleRequireFn(onFail) {
@@ -0,0 +1,94 @@
1
+ const util = require('util')
2
+ const ansi = require('ansi-styles') // https://github.com/chalk/ansi-styles/blob/main/index.js
3
+ const process = require('process')
4
+
5
+ // via https://github.com/sindresorhus/is-unicode-supported/blob/main/index.js
6
+ function isUnicodeSupported() {
7
+ if (process.platform !== 'win32') return process.env.TERM !== 'linux' // Linux console (kernel)
8
+ return Boolean(process.env.CI)
9
+ || Boolean(process.env.WT_SESSION) // Windows Terminal
10
+ || process.env.ConEmuTask === '{cmd::Cmder}' // ConEmu and cmder
11
+ || process.env.TERM_PROGRAM === 'vscode' || process.env.TERM === 'xterm-256color' || process.env.TERM === 'alacritty'
12
+ }
13
+
14
+ const isObject = (obj) => obj !== null && typeof obj === 'object'
15
+ function neverNull(obj) {
16
+ const match = (some, none = () => {}) => (obj !== null) ? some(obj) : none()
17
+ return new Proxy((some, none) => {
18
+ if (some) return some === 'string' ? '' : some
19
+ if (!some && !none) return obj
20
+ return match(some, none)
21
+ },
22
+ {
23
+ get: (target, key) => {
24
+ const obj = target()
25
+ if (isObject(obj)) return neverNull(obj[key])
26
+ return neverNull()
27
+ },
28
+ set: (target, key, val) => {
29
+ const obj = target()
30
+ if (isObject(obj)) obj[key] = val
31
+ return true
32
+ },
33
+ })
34
+ }
35
+
36
+ function safeColors(disableColors) {
37
+ return (disableColors) ? neverNull(ansi) : ansi
38
+ }
39
+
40
+ const allowed = isUnicodeSupported()
41
+ const styles = safeColors(process.env.DISABLE_COLOR)
42
+ const SPACES = ' '
43
+ const SUCCESS = allowed ? '✔' : '√'
44
+ const INFO = allowed ? 'ℹ' : 'i'
45
+ const WARNING = allowed ? '⚠' : '‼'
46
+ const ERROR = allowed ? '✖' : '×'
47
+
48
+ const colors = {
49
+ default: ['white', ''],
50
+ success: ['greenBright', `${SUCCESS}${SPACES}`],
51
+ info: ['cyanBright', `${INFO}${SPACES}`],
52
+ warning: ['yellowBright', `${WARNING}${SPACES}`],
53
+ error: ['redBright', `${ERROR}${SPACES}`]
54
+ }
55
+
56
+ function log(type, msg, customPrefix, noLog) {
57
+ const [color, prefix] = colors[type] || colors.default
58
+ const finalPrefix = typeof customPrefix !== 'undefined' ? customPrefix : prefix
59
+ const logMsg = `${styles[color].open}${finalPrefix}${msg}${styles[color].close}`
60
+ if (noLog) return logMsg
61
+ console.log(logMsg)
62
+ }
63
+
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 */))
71
+ }
72
+
73
+ const success = log.bind(null, 'success')
74
+ const info = log.bind(null, 'info')
75
+ const warning = log.bind(null, 'warning')
76
+ const error = log.bind(null, 'error')
77
+
78
+ /*
79
+ // Usage:
80
+ console.log('Nice logs')
81
+ deepLog({ deep: {object }})
82
+ success('Success! Yay it worked')
83
+ info('Info: Additional details here')
84
+ warning('Warning: Watch out')
85
+ error('Error: Oh no!')
86
+ /**/
87
+
88
+ module.exports = {
89
+ deepLog,
90
+ success,
91
+ info,
92
+ warning,
93
+ error,
94
+ }
@@ -0,0 +1,20 @@
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
+ }
@@ -0,0 +1,88 @@
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
+ }
@@ -0,0 +1,32 @@
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
+ }
@@ -0,0 +1,92 @@
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
+ }
@@ -0,0 +1,17 @@
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()