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.
- package/README.md +47 -37
- package/cli.js +5 -82
- package/lib/block-parser-js.test.js +171 -0
- package/lib/block-parser.js +382 -0
- package/lib/block-parser.test.js +479 -0
- package/lib/cli.js +245 -0
- package/lib/cli.test.js +409 -0
- package/lib/defaults.js +12 -0
- package/lib/globals.d.ts +66 -0
- package/lib/index.js +353 -184
- package/lib/index.test.js +11 -0
- package/lib/process-contents.js +371 -0
- package/lib/process-file.js +37 -0
- package/lib/transforms/code.js +67 -28
- package/lib/transforms/file.js +17 -17
- package/lib/transforms/index.js +0 -114
- package/lib/transforms/remote.js +8 -6
- package/lib/transforms/sectionToc.js +18 -0
- package/lib/transforms/toc.js +12 -265
- package/lib/transforms/wordCount.js +5 -0
- package/lib/types.js +11 -0
- package/lib/utils/fs.js +342 -0
- package/lib/utils/fs.test.js +267 -0
- package/lib/utils/index.js +19 -0
- package/{cli-utils.js → lib/utils/load-config.js} +2 -6
- package/lib/utils/logs.js +94 -0
- package/lib/utils/md/filters.js +20 -0
- package/lib/utils/md/find-code-blocks.js +88 -0
- package/lib/utils/md/find-date.js +32 -0
- package/lib/utils/md/find-frontmatter.js +92 -0
- package/lib/utils/md/find-frontmatter.test.js +17 -0
- package/lib/utils/md/find-html-tags.js +105 -0
- package/lib/utils/md/find-images-md.js +27 -0
- package/lib/utils/md/find-images.js +107 -0
- package/lib/utils/md/find-links.js +220 -0
- package/lib/utils/md/find-unmatched-html-tags.js +32 -0
- package/lib/utils/md/fixtures/2022-01-22-date-in-filename.md +14 -0
- package/lib/utils/md/fixtures/file-with-frontmatter.md +32 -0
- package/lib/utils/md/fixtures/file-with-links.md +153 -0
- package/lib/utils/md/md.test.js +105 -0
- package/lib/utils/md/parse.js +146 -0
- package/lib/utils/md/utils.js +19 -0
- package/lib/utils/regex-timeout.js +84 -0
- package/lib/utils/regex.js +40 -6
- package/lib/utils/remoteRequest.js +55 -0
- package/lib/utils/syntax.js +82 -0
- package/lib/utils/text.js +328 -0
- package/lib/utils/text.test.js +305 -0
- package/lib/utils/toc.js +315 -0
- package/package.json +30 -26
- package/index.js +0 -46
- package/lib/processFile.js +0 -154
- package/lib/updateContents.js +0 -125
- package/lib/utils/_md.test.js +0 -63
- package/lib/utils/new-parser.js +0 -412
- package/lib/utils/new-parser.test.js +0 -324
- package/lib/utils/weird-parse.js +0 -230
- 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
|
|
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()
|