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,371 @@
|
|
|
1
|
+
const { parseBlocks } = require('./block-parser')
|
|
2
|
+
const { deepLog } = require('./utils/logs')
|
|
3
|
+
const { getCodeLocation } = require('./utils')
|
|
4
|
+
const { indentString, trimString } = require('./utils/text')
|
|
5
|
+
const { OPEN_WORD, CLOSE_WORD } = require('./defaults')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Pull comment blocks out of content and process them
|
|
9
|
+
* @param {string} text
|
|
10
|
+
* @param {object} config
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
13
|
+
async function processContents(text, config = {}) {
|
|
14
|
+
const {
|
|
15
|
+
srcPath,
|
|
16
|
+
outputPath,
|
|
17
|
+
open = OPEN_WORD, // 'DOCS:START',
|
|
18
|
+
close = CLOSE_WORD, // 'DOCS:END',
|
|
19
|
+
syntax = 'md',
|
|
20
|
+
transforms,
|
|
21
|
+
beforeMiddleware = [],
|
|
22
|
+
afterMiddleware = [],
|
|
23
|
+
debug = false,
|
|
24
|
+
} = config
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
console.log('OPEN_WORD', open)
|
|
28
|
+
console.log('CLOSE_WORD', close)
|
|
29
|
+
console.log('syntax', syntax)
|
|
30
|
+
// console.log('text', text)
|
|
31
|
+
/** */
|
|
32
|
+
|
|
33
|
+
let foundBlocks = {}
|
|
34
|
+
try {
|
|
35
|
+
foundBlocks = parseBlocks(text, {
|
|
36
|
+
syntax,
|
|
37
|
+
open,
|
|
38
|
+
close,
|
|
39
|
+
})
|
|
40
|
+
} catch (e) {
|
|
41
|
+
throw new Error(`${e.message} in file ${srcPath}\n`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (debug) {
|
|
45
|
+
console.log(`foundBlocks ${srcPath}`)
|
|
46
|
+
deepLog(foundBlocks)
|
|
47
|
+
}
|
|
48
|
+
/*
|
|
49
|
+
deepLog(foundBlocks)
|
|
50
|
+
process.exit(1)
|
|
51
|
+
/** */
|
|
52
|
+
const { COMMENT_OPEN_REGEX, COMMENT_CLOSE_REGEX } = foundBlocks
|
|
53
|
+
|
|
54
|
+
const blocksWithTransforms = foundBlocks.blocks
|
|
55
|
+
.filter((block) => block.type)
|
|
56
|
+
.map((block, i) => {
|
|
57
|
+
const transform = block.type
|
|
58
|
+
delete block.type
|
|
59
|
+
return Object.assign({ transform }, block)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
const regexInfo = {
|
|
64
|
+
blocks: foundBlocks.pattern,
|
|
65
|
+
open: COMMENT_OPEN_REGEX,
|
|
66
|
+
close: COMMENT_CLOSE_REGEX,
|
|
67
|
+
}
|
|
68
|
+
// console.log('blocksWithTransforms', blocksWithTransforms)
|
|
69
|
+
// process.exit(1)
|
|
70
|
+
|
|
71
|
+
const transformsToRun = sortTranforms(blocksWithTransforms, transforms)
|
|
72
|
+
// .map((transform) => {
|
|
73
|
+
// return {
|
|
74
|
+
// ...transform,
|
|
75
|
+
// srcPath
|
|
76
|
+
// }
|
|
77
|
+
// })
|
|
78
|
+
// console.log('transformsToRun', transformsToRun)
|
|
79
|
+
|
|
80
|
+
// if (!transformsToRun.length) {
|
|
81
|
+
// process.exit(1)
|
|
82
|
+
// }
|
|
83
|
+
// console.log('transformsToRun', transformsToRun)
|
|
84
|
+
let missingTransforms = []
|
|
85
|
+
const updatedContents = await transformsToRun.reduce(async (contentPromise, originalMatch) => {
|
|
86
|
+
const md = await contentPromise
|
|
87
|
+
/* Apply Before middleware to all transforms */
|
|
88
|
+
const match = await applyMiddleware(originalMatch, md, beforeMiddleware)
|
|
89
|
+
const { block, content, open, close, transform, options, context } = match
|
|
90
|
+
const closeTag = close.value
|
|
91
|
+
const openTag = open.value
|
|
92
|
+
|
|
93
|
+
/* Run transform plugins */
|
|
94
|
+
let tempContent = content.value
|
|
95
|
+
// console.log('transform', transform)
|
|
96
|
+
const currentTransformFn = getTransform(transform, transforms)
|
|
97
|
+
/* Run each transform */
|
|
98
|
+
if (currentTransformFn) {
|
|
99
|
+
// console.log('context', context)
|
|
100
|
+
let returnedContent
|
|
101
|
+
/* DISABLED legacy syntax */
|
|
102
|
+
/* // Support for legacy syntax... maybe later
|
|
103
|
+
if (context && context.isLegacy) {
|
|
104
|
+
console.log(`CALL legacy ${transform}`, srcPath)
|
|
105
|
+
// backward compat maybe
|
|
106
|
+
// CODE(content, options, config)
|
|
107
|
+
returnedContent = await currentTransformFn(content.value, options, {
|
|
108
|
+
originalPath: srcPath
|
|
109
|
+
})
|
|
110
|
+
} else {
|
|
111
|
+
returnedContent = await currentTransformFn(transformApi({
|
|
112
|
+
srcPath,
|
|
113
|
+
...match,
|
|
114
|
+
regex: regexInfo,
|
|
115
|
+
originalContents: text,
|
|
116
|
+
currentContents: md,
|
|
117
|
+
}, config))
|
|
118
|
+
}
|
|
119
|
+
/** */
|
|
120
|
+
|
|
121
|
+
returnedContent = await currentTransformFn(
|
|
122
|
+
transformApi({
|
|
123
|
+
srcPath,
|
|
124
|
+
...match,
|
|
125
|
+
regex: regexInfo,
|
|
126
|
+
originalContents: text,
|
|
127
|
+
currentContents: md,
|
|
128
|
+
}, config)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
/* Run each transform */
|
|
132
|
+
// console.log('config', config)
|
|
133
|
+
|
|
134
|
+
// console.log('returnedContent', returnedContent)
|
|
135
|
+
|
|
136
|
+
if (returnedContent) {
|
|
137
|
+
tempContent = returnedContent
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* Apply After middleware to all transforms */
|
|
142
|
+
const afterContent = await applyMiddleware({
|
|
143
|
+
...match,
|
|
144
|
+
...{
|
|
145
|
+
content: {
|
|
146
|
+
...match.content,
|
|
147
|
+
value: tempContent
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}, md, afterMiddleware)
|
|
151
|
+
if (debug) {
|
|
152
|
+
console.log('afterContent', afterContent)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!currentTransformFn) {
|
|
156
|
+
missingTransforms.push(afterContent)
|
|
157
|
+
// console.log(`Missing "${transform}" transform`)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const newContent = afterContent.content.value
|
|
161
|
+
const formattedNewContent = (options.noTrim) ? newContent : trimString(newContent)
|
|
162
|
+
// console.log('formattedNewContent', formattedNewContent)
|
|
163
|
+
/* Remove any conflicting imported comments */
|
|
164
|
+
const fix = removeConflictingComments(formattedNewContent, foundBlocks.commentOpen, COMMENT_CLOSE_REGEX)
|
|
165
|
+
// const fix = stripAllComments(formattedNewContent, foundBlocks.commentOpen, COMMENT_CLOSE_REGEX)
|
|
166
|
+
|
|
167
|
+
// console.log('COMMENT_CLOSE_REGEX', COMMENT_CLOSE_REGEX)
|
|
168
|
+
// console.log('formattedNewContent', formattedNewContent)
|
|
169
|
+
// console.log('fix', fix)
|
|
170
|
+
const preserveIndent = (true || options.preserveIndent) ? block.indentation.length + match.content.indentation : block.indentation.length
|
|
171
|
+
const indent = indentString(fix, preserveIndent)
|
|
172
|
+
const newCont = `${openTag}${indent}${closeTag}`
|
|
173
|
+
/* Replace original contents */
|
|
174
|
+
const newContents = md.replace(block.value, newCont)
|
|
175
|
+
return Promise.resolve(newContents)
|
|
176
|
+
}, Promise.resolve(text))
|
|
177
|
+
|
|
178
|
+
if (debug) {
|
|
179
|
+
console.log('Output Markdown')
|
|
180
|
+
console.log(updatedContents)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/*
|
|
184
|
+
if (missingTransforms.length) {
|
|
185
|
+
console.log('missingTransforms', missingTransforms)
|
|
186
|
+
let matchOne = missingTransforms[1]
|
|
187
|
+
matchOne = missingTransforms[missingTransforms.length - 1]
|
|
188
|
+
// console.log('matchOne', matchOne)
|
|
189
|
+
const { block, transform, args } = matchOne
|
|
190
|
+
const { openTag, closeTag, content, contentStart, contentEnd, start, end} = block
|
|
191
|
+
|
|
192
|
+
// console.log('contentStart', contentStart)
|
|
193
|
+
// console.log('contentEnd', contentEnd)
|
|
194
|
+
// console.log('original text between', `"${getTextBetweenChars(md, contentStart, contentEnd)}"`)
|
|
195
|
+
// console.log('original block between', `"${getTextBetweenChars(md, start, end)}"`)
|
|
196
|
+
}
|
|
197
|
+
/** */
|
|
198
|
+
|
|
199
|
+
// console.log('detect slow srcPath', srcPath)
|
|
200
|
+
|
|
201
|
+
const result = {
|
|
202
|
+
/* Has markdown content changed? */
|
|
203
|
+
isChanged: text !== updatedContents,
|
|
204
|
+
isNewPath: srcPath !== outputPath,
|
|
205
|
+
srcPath,
|
|
206
|
+
outputPath,
|
|
207
|
+
// config,
|
|
208
|
+
transforms: transformsToRun,
|
|
209
|
+
missingTransforms,
|
|
210
|
+
originalContents: text,
|
|
211
|
+
updatedContents
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// console.log('result')
|
|
215
|
+
// deepLog(result)
|
|
216
|
+
|
|
217
|
+
return result
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function transformApi(stuff, opts) {
|
|
221
|
+
const { transforms, srcPath, outputPath, ...rest } = opts
|
|
222
|
+
return {
|
|
223
|
+
transform: stuff.transform,
|
|
224
|
+
content: stuff.content.value,
|
|
225
|
+
options: stuff.options || {},
|
|
226
|
+
srcPath: stuff.srcPath,
|
|
227
|
+
outputPath: outputPath,
|
|
228
|
+
settings: {
|
|
229
|
+
...rest,
|
|
230
|
+
regex: stuff.regex,
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
// blockContent: stuff.content.value,
|
|
234
|
+
fileContent: stuff.currentContents,
|
|
235
|
+
originalFileContent: stuff.originalContents,
|
|
236
|
+
/* Methods */
|
|
237
|
+
getOriginalContent: () => stuff.originalContents,
|
|
238
|
+
getOriginalBlock: () => stuff,
|
|
239
|
+
getBlockDetails: (content) => {
|
|
240
|
+
/* Re-parse current file for updated positions */
|
|
241
|
+
return getDetails({
|
|
242
|
+
contents: content || stuff.currentContents,
|
|
243
|
+
openValue: stuff.open.value,
|
|
244
|
+
srcPath: stuff.srcPath,
|
|
245
|
+
index: stuff.index,
|
|
246
|
+
opts: opts
|
|
247
|
+
})
|
|
248
|
+
},
|
|
249
|
+
// getOriginalBlockDetails: () => {
|
|
250
|
+
// return getDetails({
|
|
251
|
+
// contents: stuff.originalContents,
|
|
252
|
+
// openValue: stuff.open.value,
|
|
253
|
+
// srcPath: stuff.srcPath,
|
|
254
|
+
// index: stuff.index,
|
|
255
|
+
// opts: opts
|
|
256
|
+
// })
|
|
257
|
+
// },
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function getDetails({
|
|
262
|
+
contents,
|
|
263
|
+
openValue,
|
|
264
|
+
srcPath,
|
|
265
|
+
index,
|
|
266
|
+
opts
|
|
267
|
+
}) {
|
|
268
|
+
/* Re-parse current file for updated positions */
|
|
269
|
+
const blockData = parseBlocks(contents, opts)
|
|
270
|
+
|
|
271
|
+
const matchingBlocks = blockData.blocks.filter((block) => {
|
|
272
|
+
return block.open.value === openValue
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
if (!matchingBlocks.length) {
|
|
276
|
+
return {}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
let foundBlock = matchingBlocks[0]
|
|
280
|
+
if (matchingBlocks.length > 1 && index) {
|
|
281
|
+
foundBlock = matchingBlocks.filter((block) => {
|
|
282
|
+
return block.index === index
|
|
283
|
+
})[0]
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (srcPath) {
|
|
287
|
+
const location = getCodeLocation(srcPath, foundBlock.block.lines[0])
|
|
288
|
+
foundBlock.sourceLocation = location
|
|
289
|
+
}
|
|
290
|
+
return foundBlock
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Remove conflicting comments that might have been inserted from transforms
|
|
295
|
+
* @param {*} content
|
|
296
|
+
* @param {*} openPattern
|
|
297
|
+
* @param {*} closePattern
|
|
298
|
+
* @returns
|
|
299
|
+
*/
|
|
300
|
+
function removeConflictingComments(content, openPattern, closePattern) {
|
|
301
|
+
const removeOpen = content.replace(openPattern, '')
|
|
302
|
+
// TODO this probably needs to be a loop for larger blocks
|
|
303
|
+
closePattern.lastIndex = 0; // reset regex
|
|
304
|
+
const hasClose = closePattern.exec(content)
|
|
305
|
+
// console.log('closePattern', closePattern)
|
|
306
|
+
// console.log('has', content)
|
|
307
|
+
// console.log('hasClose', hasClose)
|
|
308
|
+
if (!hasClose) {
|
|
309
|
+
return removeOpen
|
|
310
|
+
}
|
|
311
|
+
const closeTag = `${hasClose[2]}${hasClose[3] || ''}`
|
|
312
|
+
// console.log('closeTag', closeTag)
|
|
313
|
+
return removeOpen
|
|
314
|
+
.replaceAll(closeTag, '')
|
|
315
|
+
/* Trailing new line */
|
|
316
|
+
.replace(/\n$/, '')
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function applyMiddleware(data, md, middlewares) {
|
|
320
|
+
return middlewares.reduce(async (acc, curr) => {
|
|
321
|
+
const realAcc = await acc
|
|
322
|
+
// console.log(`Running "${curr.name}" Middleware on "${realAcc.transform}" block`)
|
|
323
|
+
const updatedContent = await curr.transform(realAcc, md)
|
|
324
|
+
// realAcc.block.content = updatedContent
|
|
325
|
+
return Promise.resolve({
|
|
326
|
+
...realAcc,
|
|
327
|
+
...{
|
|
328
|
+
content: {
|
|
329
|
+
...realAcc.content,
|
|
330
|
+
value: updatedContent
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
})
|
|
334
|
+
}, Promise.resolve(data))
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Get Transform function
|
|
339
|
+
* @param {string} name - transform name
|
|
340
|
+
* @param {object} transforms - transform fns
|
|
341
|
+
* @returns {function}
|
|
342
|
+
*/
|
|
343
|
+
function getTransform(name, transforms = {}) {
|
|
344
|
+
return transforms[name] || transforms[name.toLowerCase()]
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function sortTranforms(foundTransForms, registeredTransforms) {
|
|
348
|
+
// console.log('transforms', transforms)
|
|
349
|
+
if (!foundTransForms) return []
|
|
350
|
+
return foundTransForms.sort((a, b) => {
|
|
351
|
+
// put table of contents (TOC) at end of tranforms
|
|
352
|
+
if (a.transform === 'TOC') return 1
|
|
353
|
+
if (b.transform === 'TOC') return -1
|
|
354
|
+
return 0
|
|
355
|
+
}).map((item) => {
|
|
356
|
+
if (getTransform(item.transform, registeredTransforms)) {
|
|
357
|
+
return item
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
...item,
|
|
361
|
+
context: {
|
|
362
|
+
...item.context,
|
|
363
|
+
isMissing: true,
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
module.exports = {
|
|
370
|
+
processContents
|
|
371
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const isValidFile = require('is-valid-path')
|
|
3
|
+
const { readFile } = require('./utils/fs')
|
|
4
|
+
const { processContents } = require('./process-contents')
|
|
5
|
+
// const { OPEN_WORD, CLOSE_WORD } = require('./defaults')
|
|
6
|
+
|
|
7
|
+
async function processFile(opts = {}) {
|
|
8
|
+
const { content, syntax } = opts
|
|
9
|
+
let srcPath = opts.srcPath
|
|
10
|
+
if (srcPath && content) {
|
|
11
|
+
throw new Error(`Can't set both "srcPath" & "content"`)
|
|
12
|
+
}
|
|
13
|
+
let fileContents
|
|
14
|
+
if (content) {
|
|
15
|
+
const isFile = isValidFile(content)
|
|
16
|
+
srcPath = (isFile) ? content : undefined
|
|
17
|
+
fileContents = (!isFile) ? content : undefined
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!fileContents) {
|
|
21
|
+
fileContents = await readFile(srcPath || content, 'utf8')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let syntaxType = syntax
|
|
25
|
+
if (srcPath && !syntaxType) {
|
|
26
|
+
syntaxType = path.extname(srcPath).replace(/^\./, '')
|
|
27
|
+
}
|
|
28
|
+
return processContents(fileContents, {
|
|
29
|
+
...opts,
|
|
30
|
+
srcPath,
|
|
31
|
+
syntax: syntaxType,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
processFile
|
|
37
|
+
}
|
package/lib/transforms/code.js
CHANGED
|
@@ -1,52 +1,91 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
1
|
const fs = require('fs')
|
|
3
|
-
const
|
|
2
|
+
const path = require('path')
|
|
4
3
|
const remoteRequest = require('../utils/remoteRequest')
|
|
4
|
+
const { isLocalPath } = require('../utils/fs')
|
|
5
|
+
const { deepLog } = require('../utils/logs')
|
|
6
|
+
const { getLineCount, getTextBetweenLines } = require('../utils/text')
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
// TODO code sections
|
|
9
|
+
// https://github.com/linear/linear/blob/94af540244864fbe466fb933256278e04e87513e/docs/transforms/code-section.js
|
|
10
|
+
// https://github.com/linear/linear/blob/bc39d23af232f9fdbe7df458b0aaa9554ca83c57/packages/sdk/src/_tests/readme.test.ts#L133-L140
|
|
11
|
+
// usage https://github.com/linear/linear/blame/93981d3a3db571e2f8efdce9f5271ea678941c43/packages/sdk/README.md#L1
|
|
12
|
+
|
|
13
|
+
module.exports = function CODE(api) {
|
|
14
|
+
const { content, options, srcPath } = api
|
|
15
|
+
// console.log('CODE API', api)
|
|
16
|
+
// process.exit(1)
|
|
17
|
+
// console.log('options', options)
|
|
18
|
+
const { src, lines } = options
|
|
19
|
+
const originalContent = content
|
|
7
20
|
let code
|
|
8
21
|
let syntax = options.syntax
|
|
9
|
-
if (!
|
|
10
|
-
|
|
22
|
+
if (!src) {
|
|
23
|
+
deepLog(api.getCurrentBlock())
|
|
24
|
+
throw new Error('Missing "src" attribute')
|
|
11
25
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
26
|
+
let codeFilePath = src
|
|
27
|
+
if (isLocalPath(src)) {
|
|
28
|
+
const fileDir = (srcPath) ? path.dirname(srcPath) : process.cwd()
|
|
29
|
+
codeFilePath = path.resolve(fileDir, src)
|
|
15
30
|
try {
|
|
16
|
-
code = fs.readFileSync(
|
|
17
|
-
if (err) {
|
|
18
|
-
console.log(`FILE NOT FOUND ${filePath}`)
|
|
19
|
-
console.log(err)
|
|
20
|
-
// throw err
|
|
21
|
-
}
|
|
22
|
-
return contents
|
|
23
|
-
})
|
|
31
|
+
code = fs.readFileSync(codeFilePath, 'utf8')
|
|
24
32
|
} catch (e) {
|
|
25
|
-
console.log(`FILE NOT FOUND ${
|
|
33
|
+
console.log(`FILE NOT FOUND ${codeFilePath}`)
|
|
26
34
|
throw e
|
|
27
35
|
}
|
|
28
36
|
if (!syntax) {
|
|
29
|
-
syntax = path.extname(
|
|
37
|
+
syntax = path.extname(codeFilePath).replace(/^./, '')
|
|
30
38
|
}
|
|
31
39
|
} else {
|
|
32
40
|
// do remote request
|
|
33
|
-
// console.log(
|
|
34
|
-
const remoteContent = remoteRequest(
|
|
41
|
+
// console.log(src)
|
|
42
|
+
const remoteContent = remoteRequest(src)
|
|
35
43
|
if (!remoteContent) {
|
|
36
|
-
console.log(`WARNING: ${
|
|
37
|
-
return
|
|
44
|
+
console.log(`WARNING: ${src} URL NOT FOUND or internet connection is off`)
|
|
45
|
+
return originalContent
|
|
38
46
|
}
|
|
39
47
|
code = remoteContent
|
|
40
|
-
syntax = path.extname(
|
|
48
|
+
syntax = path.extname(src).replace(/^./, '')
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
/* handle option `lines` */
|
|
44
52
|
if (options.lines) {
|
|
45
|
-
const
|
|
46
|
-
|
|
53
|
+
const lineCount = getLineCount(code)
|
|
54
|
+
// console.log('lineCount', lineCount)
|
|
55
|
+
// console.log('src', src)
|
|
56
|
+
// console.log('lines', lines)
|
|
57
|
+
let startLine
|
|
58
|
+
let endLine
|
|
59
|
+
if (Array.isArray(lines)) {
|
|
60
|
+
startLine = lines[0]
|
|
61
|
+
endLine = lines[1]
|
|
62
|
+
} else if (typeof lines === 'string') {
|
|
63
|
+
const splitLines = lines.split('-')
|
|
64
|
+
startLine = splitLines[0]
|
|
65
|
+
endLine = splitLines[1]
|
|
66
|
+
}
|
|
47
67
|
if ((startLine) && (endLine) && parseInt(startLine, 10) <= parseInt(endLine, 10)) {
|
|
48
|
-
code = code
|
|
68
|
+
code = getTextBetweenLines(code, startLine, endLine)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Check for Id */
|
|
73
|
+
if (options.id) {
|
|
74
|
+
const lines = code.split("\n")
|
|
75
|
+
const startLine = lines.findIndex(line => line.includes(`CODE_SECTION:${options.id}:START`)) ?? 0
|
|
76
|
+
const endLine = lines.findIndex(line => line.includes(`CODE_SECTION:${options.id}:END`)) ?? lines.length - 1
|
|
77
|
+
// console.log('startLine', startLine)
|
|
78
|
+
// console.log('endLineendLine', endLine)
|
|
79
|
+
if (startLine === -1 && endLine === -1) {
|
|
80
|
+
throw new Error(`Missing ${options.id} code section from ${codeFilePath}`)
|
|
49
81
|
}
|
|
82
|
+
|
|
83
|
+
const selectedLines = lines.slice(startLine + 1, endLine);
|
|
84
|
+
|
|
85
|
+
const trimBy = selectedLines[0]?.match(/^(\s*)/)?.[1]?.length;
|
|
86
|
+
const newValue = `${selectedLines.map(line => line.substring(trimBy).replace(/^\/\/ CODE_SECTION:INCLUDE /g, "")).join("\n")}`
|
|
87
|
+
// console.log('newValue', newValue)
|
|
88
|
+
code = newValue
|
|
50
89
|
}
|
|
51
90
|
|
|
52
91
|
// trim leading and trailing spaces/line breaks in code and keeps the indentation of the first non-empty line
|
|
@@ -57,7 +96,7 @@ module.exports = function CODE(content, options, config) {
|
|
|
57
96
|
header = `\n${options.header}`
|
|
58
97
|
}
|
|
59
98
|
|
|
60
|
-
return `<!-- The below code snippet is automatically added from ${
|
|
99
|
+
return `<!-- The below code snippet is automatically added from ${src} -->
|
|
61
100
|
\`\`\`${syntax}${header}
|
|
62
101
|
${code}
|
|
63
102
|
\`\`\``
|
package/lib/transforms/file.js
CHANGED
|
@@ -2,31 +2,26 @@ const path = require('path')
|
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const isLocalPath = require('is-local-path')
|
|
4
4
|
|
|
5
|
-
module.exports = function FILE(
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
module.exports = function FILE(api) {
|
|
6
|
+
/*
|
|
7
|
+
console.log('FILE API', api)
|
|
8
|
+
console.log(api.getBlockDetails())
|
|
9
|
+
process.exit(1)
|
|
10
|
+
/** */
|
|
11
|
+
const { options, srcPath } = api
|
|
8
12
|
if (!options.src) {
|
|
9
13
|
return false
|
|
10
14
|
}
|
|
15
|
+
let fileContents = ''
|
|
11
16
|
if (isLocalPath(options.src)) {
|
|
12
|
-
const fileDir = path.dirname(
|
|
13
|
-
const
|
|
17
|
+
const fileDir = path.dirname(srcPath)
|
|
18
|
+
const resolvedFilePath = path.resolve(fileDir, options.src)
|
|
14
19
|
try {
|
|
15
|
-
fileContents = fs.readFileSync(
|
|
16
|
-
if (err) {
|
|
17
|
-
console.log(`FILE NOT FOUND ${filePath}`)
|
|
18
|
-
console.log(err)
|
|
19
|
-
// throw err
|
|
20
|
-
}
|
|
21
|
-
return contents
|
|
22
|
-
})
|
|
20
|
+
fileContents = fs.readFileSync(resolvedFilePath, 'utf8')
|
|
23
21
|
} catch (e) {
|
|
24
|
-
console.log(`FILE NOT FOUND ${
|
|
22
|
+
console.log(`FILE NOT FOUND ${resolvedFilePath}`)
|
|
25
23
|
throw e
|
|
26
24
|
}
|
|
27
|
-
if (!syntax) {
|
|
28
|
-
syntax = path.extname(filePath).replace(/^./, '')
|
|
29
|
-
}
|
|
30
25
|
}
|
|
31
26
|
|
|
32
27
|
// trim leading and trailing spaces/line breaks in code and keeps the indentation of the first non-empty line
|
|
@@ -35,3 +30,8 @@ module.exports = function FILE(content, options, config) {
|
|
|
35
30
|
return `<!-- The below content is automatically added from ${options.src} -->
|
|
36
31
|
${fileContents}`
|
|
37
32
|
}
|
|
33
|
+
|
|
34
|
+
// maybe support...
|
|
35
|
+
function legacyCODE(content, options, config) {
|
|
36
|
+
|
|
37
|
+
}
|
package/lib/transforms/index.js
CHANGED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
const code = require('./code')
|
|
2
|
-
const file = require('./file')
|
|
3
|
-
const remoteContent = require('./remote')
|
|
4
|
-
const toc = require('./toc')
|
|
5
|
-
|
|
6
|
-
const transforms = {
|
|
7
|
-
/**
|
|
8
|
-
* ### > TOC
|
|
9
|
-
*
|
|
10
|
-
* Generate table of contents from markdown file
|
|
11
|
-
*
|
|
12
|
-
* **Options:**
|
|
13
|
-
* - `firsth1` - *boolean* - (optional): Show first h1 of doc in table of contents. Default `false`
|
|
14
|
-
* - `collapse` - *boolean* - (optional): Collapse the table of contents in a detail accordian. Default `false`
|
|
15
|
-
* - `collapseText` - *string* - (optional): Text the toc accordian summary
|
|
16
|
-
* - `excludeText` - *string* - (optional): Text to exclude in the table of contents. Default `Table of Contents`
|
|
17
|
-
* - `maxDepth` - *number* - (optional): Max depth of headings. Default 4
|
|
18
|
-
*
|
|
19
|
-
* **Example:**
|
|
20
|
-
* ```md
|
|
21
|
-
* <!-- AUTO-GENERATED-CONTENT:START (TOC) -->
|
|
22
|
-
* toc will be generated here
|
|
23
|
-
* <!-- AUTO-GENERATED-CONTENT:END -->
|
|
24
|
-
* ```
|
|
25
|
-
*
|
|
26
|
-
* Default `MATCHWORD` is `AUTO-GENERATED-CONTENT`
|
|
27
|
-
*
|
|
28
|
-
* ---
|
|
29
|
-
* @param {string} content The current content of the comment block
|
|
30
|
-
* @param {object} options The options passed in from the comment declaration
|
|
31
|
-
* @return {string} Updated content to place in the content block
|
|
32
|
-
*/
|
|
33
|
-
TOC: toc,
|
|
34
|
-
/**
|
|
35
|
-
* ### > CODE
|
|
36
|
-
*
|
|
37
|
-
* Get code from file or URL and put in markdown
|
|
38
|
-
*
|
|
39
|
-
* **Options:**
|
|
40
|
-
* - `src`: The relative path to the code to pull in, or the `URL` where the raw code lives
|
|
41
|
-
* - `syntax` (optional): Syntax will be inferred by fileType if not specified
|
|
42
|
-
* - `header` (optional): Will add header comment to code snippet. Useful for pointing to relative source directory or adding live doc links
|
|
43
|
-
* - `lines` (optional): a range with lines of code which will then be replaced with code from the file. The line range should be defined as: "lines=*startLine*-*EndLine*" (for example: "lines=22-44"). Please see the example below
|
|
44
|
-
*
|
|
45
|
-
* **Example:**
|
|
46
|
-
* ```md
|
|
47
|
-
* <!-- AUTO-GENERATED-CONTENT:START (CODE:src=./relative/path/to/code.js) -->
|
|
48
|
-
* This content will be dynamically replaced with code from the file
|
|
49
|
-
* <!-- AUTO-GENERATED-CONTENT:END -->
|
|
50
|
-
* ```
|
|
51
|
-
*
|
|
52
|
-
* ```md
|
|
53
|
-
* <!-- AUTO-GENERATED-CONTENT:START (CODE:src=./relative/path/to/code.js&lines=22-44) -->
|
|
54
|
-
* This content will be dynamically replaced with code from the file lines 22 through 44
|
|
55
|
-
* <!-- AUTO-GENERATED-CONTENT:END -->
|
|
56
|
-
* ```
|
|
57
|
-
*
|
|
58
|
-
* Default `MATCHWORD` is `AUTO-GENERATED-CONTENT`
|
|
59
|
-
*
|
|
60
|
-
* ---
|
|
61
|
-
* @param {string} content The current content of the comment block
|
|
62
|
-
* @param {object} options The options passed in from the comment declaration
|
|
63
|
-
* @return {string} Updated inner contents of the comment block
|
|
64
|
-
*/
|
|
65
|
-
CODE: code,
|
|
66
|
-
/**
|
|
67
|
-
* ### > FILE
|
|
68
|
-
*
|
|
69
|
-
* Get local file contents.
|
|
70
|
-
*
|
|
71
|
-
* **Options:**
|
|
72
|
-
* - `src`: The relative path to the file to pull in
|
|
73
|
-
*
|
|
74
|
-
* **Example:**
|
|
75
|
-
* ```md
|
|
76
|
-
* <!-- AUTO-GENERATED-CONTENT:START (FILE:src=./path/to/file) -->
|
|
77
|
-
* This content will be dynamically replaced from the local file
|
|
78
|
-
* <!-- AUTO-GENERATED-CONTENT:END -->
|
|
79
|
-
* ```
|
|
80
|
-
*
|
|
81
|
-
* Default `MATCHWORD` is `AUTO-GENERATED-CONTENT`
|
|
82
|
-
*
|
|
83
|
-
* ---
|
|
84
|
-
* @param {string} content The current content of the comment block
|
|
85
|
-
* @param {object} options The options passed in from the comment declaration
|
|
86
|
-
* @return {string} Updated content to place in the content block
|
|
87
|
-
*/
|
|
88
|
-
FILE: file,
|
|
89
|
-
/**
|
|
90
|
-
* ### > REMOTE
|
|
91
|
-
*
|
|
92
|
-
* Get any remote Data and put in markdown
|
|
93
|
-
*
|
|
94
|
-
* **Options:**
|
|
95
|
-
* - `url`: The URL of the remote content to pull in
|
|
96
|
-
*
|
|
97
|
-
* **Example:**
|
|
98
|
-
* ```md
|
|
99
|
-
* <!-- AUTO-GENERATED-CONTENT:START (REMOTE:url=http://url-to-raw-md-file.md) -->
|
|
100
|
-
* This content will be dynamically replaced from the remote url
|
|
101
|
-
* <!-- AUTO-GENERATED-CONTENT:END -->
|
|
102
|
-
* ```
|
|
103
|
-
*
|
|
104
|
-
* Default `MATCHWORD` is `AUTO-GENERATED-CONTENT`
|
|
105
|
-
*
|
|
106
|
-
* ---
|
|
107
|
-
* @param {string} content The current content of the comment block
|
|
108
|
-
* @param {object} options The options passed in from the comment declaration
|
|
109
|
-
* @return {string} Updated content to place in the content block
|
|
110
|
-
*/
|
|
111
|
-
REMOTE: remoteContent,
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
module.exports = transforms
|