markdown-magic 3.6.4 → 3.7.0
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 +96 -1
- package/package.json +1 -1
- package/src/index.js +24 -11
- package/src/transforms/fileTree.js +213 -0
- package/src/transforms/index.js +69 -1
- package/src/transforms/toc.js +2 -2
- package/src/types.js +1 -1
- package/src/utils/fs.test.js +3 -0
- package/src/utils/syntax.js +29 -1
- package/src/utils/text.js +12 -5
- package/src/utils/text.test.js +76 -3
package/README.md
CHANGED
|
@@ -43,6 +43,8 @@ This `README.md` is generated with `markdown-magic` [view the raw file](https://
|
|
|
43
43
|
- [> CODE](#-code)
|
|
44
44
|
- [> FILE](#-file)
|
|
45
45
|
- [> REMOTE](#-remote)
|
|
46
|
+
- [> fileTree](#-filetree)
|
|
47
|
+
- [> install](#-install)
|
|
46
48
|
- [Inline transforms](#inline-transforms)
|
|
47
49
|
- [Legacy v1 & v2 plugins](#legacy-v1--v2-plugins)
|
|
48
50
|
- [Adding Custom Transforms](#adding-custom-transforms)
|
|
@@ -267,7 +269,7 @@ Markdown Magic comes with a couple of built-in transforms for you to use or you
|
|
|
267
269
|
Generate table of contents from markdown file
|
|
268
270
|
|
|
269
271
|
**Options:**
|
|
270
|
-
- `
|
|
272
|
+
- `firstH1` - *boolean* - (optional): Show first h1 of doc in table of contents. Default `false`
|
|
271
273
|
- `collapse` - *boolean* - (optional): Collapse the table of contents in a detail accordion. Default `false`
|
|
272
274
|
- `collapseText` - *string* - (optional): Text the toc accordion summary
|
|
273
275
|
- `excludeText` - *string* - (optional): Text to exclude in the table of contents. Default `Table of Contents`
|
|
@@ -362,6 +364,99 @@ Default `matchWord` is `doc-gen`
|
|
|
362
364
|
|
|
363
365
|
---
|
|
364
366
|
|
|
367
|
+
| Name | Type | Description |
|
|
368
|
+
|:---------------------------|:---------------:|:-----------|
|
|
369
|
+
| `content` | `string` | The current content of the comment block. |
|
|
370
|
+
| `options` | `object` | The options passed in from the comment declaration. |
|
|
371
|
+
|
|
372
|
+
### > fileTree
|
|
373
|
+
|
|
374
|
+
Generate a file tree table of contents
|
|
375
|
+
|
|
376
|
+
**Options:**
|
|
377
|
+
- `src` (optional): The directory path to generate the file tree for. Default `.` (current directory)
|
|
378
|
+
- `maxDepth` (optional): Maximum depth to traverse in the directory tree. Default `3`
|
|
379
|
+
- `includeFiles` (optional): Whether to include files in the tree or just directories. Default `true`
|
|
380
|
+
- `exclude` (optional): Array of glob patterns to exclude from the tree. Default `[]`
|
|
381
|
+
- `showSize` (optional): Whether to show file sizes. Default `false`
|
|
382
|
+
- `format` (optional): Output format: "tree" or "list". Default `"tree"`
|
|
383
|
+
|
|
384
|
+
**Example:**
|
|
385
|
+
```md
|
|
386
|
+
<!-- doc-gen fileTree src="./src" maxDepth=2 -->
|
|
387
|
+
file tree will be generated here
|
|
388
|
+
<!-- end-doc-gen -->
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Example Output (tree format):**
|
|
392
|
+
```
|
|
393
|
+
└── src/
|
|
394
|
+
├── transforms/
|
|
395
|
+
│ ├── code/
|
|
396
|
+
│ │ ...
|
|
397
|
+
│ ├── fileTree.js
|
|
398
|
+
│ ├── index.js
|
|
399
|
+
│ └── toc.js
|
|
400
|
+
├── utils/
|
|
401
|
+
│ ├── fs.js
|
|
402
|
+
│ ├── logs.js
|
|
403
|
+
│ └── text.js
|
|
404
|
+
└── index.js
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**Example Output (list format):**
|
|
408
|
+
```md
|
|
409
|
+
- **src/**
|
|
410
|
+
- **transforms/**
|
|
411
|
+
- **code/**
|
|
412
|
+
- ...
|
|
413
|
+
- fileTree.js
|
|
414
|
+
- index.js
|
|
415
|
+
- toc.js
|
|
416
|
+
- **utils/**
|
|
417
|
+
- fs.js
|
|
418
|
+
- logs.js
|
|
419
|
+
- text.js
|
|
420
|
+
- index.js
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Example with file sizes:**
|
|
424
|
+
```
|
|
425
|
+
└── src/
|
|
426
|
+
├── index.js (15.2 KB)
|
|
427
|
+
└── package.json (552 B)
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Default `matchWord` is `doc-gen`
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
| Name | Type | Description |
|
|
435
|
+
|:---------------------------|:---------------:|:-----------|
|
|
436
|
+
| `content` | `string` | The current content of the comment block. |
|
|
437
|
+
| `options` | `object` | The options passed in from the comment declaration. |
|
|
438
|
+
|
|
439
|
+
### > install
|
|
440
|
+
|
|
441
|
+
Generate installation instructions in a markdown table format
|
|
442
|
+
|
|
443
|
+
**Options:**
|
|
444
|
+
- `packageName` (optional): The name of the package to install. If not provided, will try to read from package.json
|
|
445
|
+
- `isDev` (optional): Whether to install the package as a dev dependency. Default `false`
|
|
446
|
+
- `header` (optional): The header to use for the installation instructions. Default `# Installation`
|
|
447
|
+
- `body` (optional): The body to use for the installation instructions. Default `Install the \`${packageName}\` cli using your favorite package manager.`
|
|
448
|
+
|
|
449
|
+
**Example:**
|
|
450
|
+
```md
|
|
451
|
+
<!-- doc-gen install -->
|
|
452
|
+
Installation instructions will be generated here
|
|
453
|
+
<!-- end-doc-gen -->
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Default `matchWord` is `doc-gen`
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
365
460
|
| Name | Type | Description |
|
|
366
461
|
|:---------------------------|:---------------:|:-----------|
|
|
367
462
|
| `content` | `string` | The current content of the comment block. |
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
// @TODO remove this once we have swapped out node-fetch@2.7
|
|
3
|
-
const moduleAlias = require('module-alias') // "Fix" for
|
|
4
|
-
moduleAlias.addAlias('punycode', 'punycode/') // "Fix" for
|
|
3
|
+
const moduleAlias = require('module-alias') // "Fix" for punycode dep warning in node 22+
|
|
4
|
+
moduleAlias.addAlias('punycode', 'punycode/') // "Fix" for punycode dep warning in node 22+
|
|
5
5
|
const { glob, globWithGit } = require('smart-glob')
|
|
6
6
|
const codeTransform = require('./transforms/code')
|
|
7
7
|
const fileTransform = require('./transforms/file')
|
|
8
|
+
const fileTreeTransform = require('./transforms/fileTree')
|
|
8
9
|
const tocTransform = require('./transforms/toc')
|
|
9
10
|
const sectionTocTransform = require('./transforms/sectionToc')
|
|
10
11
|
const wordCountTransform = require('./transforms/wordCount')
|
|
@@ -32,6 +33,7 @@ const LINE = '──────────────────────
|
|
|
32
33
|
const defaultTransforms = {
|
|
33
34
|
CODE: codeTransform,
|
|
34
35
|
FILE: fileTransform,
|
|
36
|
+
fileTree: fileTreeTransform,
|
|
35
37
|
TOC: tocTransform,
|
|
36
38
|
sectionToc: sectionTocTransform,
|
|
37
39
|
wordCount: wordCountTransform,
|
|
@@ -113,7 +115,7 @@ const defaultOptions = {
|
|
|
113
115
|
})
|
|
114
116
|
*/
|
|
115
117
|
async function markdownMagic(globOrOpts = {}, options = {}) {
|
|
116
|
-
const
|
|
118
|
+
const hrStart = process.hrtime.bigint()
|
|
117
119
|
let opts = options || {}
|
|
118
120
|
let globPat
|
|
119
121
|
if (typeof globOrOpts === 'string' || Array.isArray(globOrOpts)) {
|
|
@@ -134,12 +136,13 @@ async function markdownMagic(globOrOpts = {}, options = {}) {
|
|
|
134
136
|
useGitGlob = false,
|
|
135
137
|
failOnMissingTransforms = false,
|
|
136
138
|
failOnMissingRemote = true,
|
|
137
|
-
dryRun = false,
|
|
138
139
|
debug = false,
|
|
139
140
|
syntax = 'md',
|
|
140
141
|
silent = false,
|
|
141
142
|
} = opts
|
|
142
143
|
|
|
144
|
+
let dryRun = opts.dryRun || opts.dry || opts.plan || false
|
|
145
|
+
|
|
143
146
|
// @ts-ignore
|
|
144
147
|
const outputDir = output.directory || opts.outputDir
|
|
145
148
|
const removeComments = output.removeComments || false
|
|
@@ -203,7 +206,8 @@ async function markdownMagic(globOrOpts = {}, options = {}) {
|
|
|
203
206
|
opts.globPattern = globs
|
|
204
207
|
|
|
205
208
|
logger(LINE)
|
|
206
|
-
|
|
209
|
+
const dryRunPostFix = (dryRun) ? ' - Dry run' : ''
|
|
210
|
+
success(` Markdown Magic Starting...${dryRunPostFix}`, silent, '✨ ')
|
|
207
211
|
logger(`${LINE}\n`)
|
|
208
212
|
|
|
209
213
|
info(` Searching for comment blocks...`, silent, '🔎 ')
|
|
@@ -213,9 +217,9 @@ async function markdownMagic(globOrOpts = {}, options = {}) {
|
|
|
213
217
|
logger(`Searching: `, globs)
|
|
214
218
|
|
|
215
219
|
if (dryRun || debug) {
|
|
220
|
+
logger()
|
|
216
221
|
info(`Glob patterns:`, silent)
|
|
217
222
|
logger(globs)
|
|
218
|
-
logger()
|
|
219
223
|
/*
|
|
220
224
|
process.exit(1)
|
|
221
225
|
/** */
|
|
@@ -360,7 +364,7 @@ async function markdownMagic(globOrOpts = {}, options = {}) {
|
|
|
360
364
|
const sortedItems = sortedIds.map(id => blocks.find(item => item.id === id)).filter(Boolean);
|
|
361
365
|
|
|
362
366
|
// topoSort(blocks)
|
|
363
|
-
const orderedFiles = sortedItems.map((
|
|
367
|
+
const orderedFiles = sortedItems.map((block) => block.id)
|
|
364
368
|
// console.log('sortedItems', sortedItems)
|
|
365
369
|
// console.log('orderedFiles', orderedFiles)
|
|
366
370
|
|
|
@@ -674,8 +678,17 @@ async function markdownMagic(globOrOpts = {}, options = {}) {
|
|
|
674
678
|
process.exit(1)
|
|
675
679
|
/** */
|
|
676
680
|
logger()
|
|
677
|
-
info(` Results:`, silent, "💫 ")
|
|
678
681
|
|
|
682
|
+
if (dryRun) {
|
|
683
|
+
logger('Dry run complete. Exiting markdown magic early.')
|
|
684
|
+
return {
|
|
685
|
+
errors,
|
|
686
|
+
filesChanged: [],
|
|
687
|
+
results: plan
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
info(` Results:`, silent, "💫 ")
|
|
679
692
|
/*
|
|
680
693
|
TODO:
|
|
681
694
|
- Output to new file
|
|
@@ -686,14 +699,14 @@ async function markdownMagic(globOrOpts = {}, options = {}) {
|
|
|
686
699
|
logErrors(errors)
|
|
687
700
|
}
|
|
688
701
|
|
|
689
|
-
const
|
|
702
|
+
const elapsed = convertHrtime(process.hrtime.bigint() - hrStart)
|
|
690
703
|
|
|
691
704
|
logger()
|
|
692
705
|
logger(`${LINE}`)
|
|
693
|
-
success(`Markdown Magic Done. ${
|
|
706
|
+
success(`Markdown Magic Done. ${elapsed.seconds} seconds`, silent)
|
|
694
707
|
logger(`${LINE}`)
|
|
695
708
|
return {
|
|
696
|
-
// @TODO maybe
|
|
709
|
+
// @TODO maybe separate changed and output files
|
|
697
710
|
filesChanged: plan.filter(({ isChanged, isNewPath }) => isChanged || isNewPath).map(({ outputPath }) => outputPath),
|
|
698
711
|
results: plan,
|
|
699
712
|
errors,
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Options for configuring the file tree table of contents.
|
|
6
|
+
* @typedef {Object} FileTreeTransformOptions
|
|
7
|
+
* @property {string} [src="."] - The directory path to generate the file tree for. Default is `.` (current directory).
|
|
8
|
+
* @property {number} [maxDepth=3] - Maximum depth to traverse in the directory tree. Default is `3`.
|
|
9
|
+
* @property {boolean} [includeFiles=true] - Whether to include files in the tree or just directories. Default is `true`.
|
|
10
|
+
* @property {string[]} [exclude=[]] - Array of glob patterns to exclude from the tree.
|
|
11
|
+
* @property {boolean} [showSize=false] - Whether to show file sizes. Default is `false`.
|
|
12
|
+
* @property {string} [format="tree"] - Output format: "tree" or "list". Default is `"tree"`.
|
|
13
|
+
* @example
|
|
14
|
+
```md
|
|
15
|
+
<!-- doc-gen fileTree src="./src" maxDepth=2 -->
|
|
16
|
+
file tree will be generated here
|
|
17
|
+
<!-- end-doc-gen -->
|
|
18
|
+
```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generate a file tree table of contents
|
|
23
|
+
* @param {Object} api - The markdown-magic API object
|
|
24
|
+
* @returns {string} The generated file tree markdown
|
|
25
|
+
*/
|
|
26
|
+
module.exports = function fileTree(api) {
|
|
27
|
+
const { options, srcPath } = api
|
|
28
|
+
/** @type {FileTreeTransformOptions} */
|
|
29
|
+
const opts = options || {}
|
|
30
|
+
|
|
31
|
+
const targetPath = opts.src || '.'
|
|
32
|
+
const maxDepth = opts.maxDepth ? Number(opts.maxDepth) : 3
|
|
33
|
+
const includeFiles = opts.includeFiles !== false
|
|
34
|
+
const exclude = opts.exclude || []
|
|
35
|
+
const showSize = opts.showSize === true
|
|
36
|
+
const format = opts.format || 'tree'
|
|
37
|
+
|
|
38
|
+
// Resolve the target path relative to the source file
|
|
39
|
+
const fileDir = path.dirname(srcPath)
|
|
40
|
+
const resolvedPath = path.resolve(fileDir, targetPath)
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const tree = generateFileTree(resolvedPath, {
|
|
44
|
+
maxDepth,
|
|
45
|
+
includeFiles,
|
|
46
|
+
exclude,
|
|
47
|
+
showSize,
|
|
48
|
+
currentDepth: 0
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
if (format === 'list') {
|
|
52
|
+
return formatAsList(tree)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return formatAsTree(tree)
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error(`Error generating file tree for ${resolvedPath}:`, error.message)
|
|
58
|
+
return `<!-- Error: Could not generate file tree for ${targetPath} -->`
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate file tree structure
|
|
64
|
+
* @param {string} dirPath - Directory path to scan
|
|
65
|
+
* @param {Object} options - Options for tree generation
|
|
66
|
+
* @returns {Object} Tree structure
|
|
67
|
+
*/
|
|
68
|
+
function generateFileTree(dirPath, options) {
|
|
69
|
+
const { maxDepth, includeFiles, exclude, showSize, currentDepth } = options
|
|
70
|
+
|
|
71
|
+
if (currentDepth >= maxDepth) {
|
|
72
|
+
return { type: 'directory', name: path.basename(dirPath), children: [], truncated: true }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let items
|
|
76
|
+
try {
|
|
77
|
+
items = fs.readdirSync(dirPath)
|
|
78
|
+
} catch (error) {
|
|
79
|
+
return { type: 'directory', name: path.basename(dirPath), children: [], error: true }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Filter out excluded items
|
|
83
|
+
items = items.filter(item => {
|
|
84
|
+
// Skip hidden files and common ignored directories
|
|
85
|
+
if (item.startsWith('.') && !item.match(/\.(md|txt|json)$/)) {
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
if (['node_modules', '.git', '.DS_Store', 'dist', 'build'].includes(item)) {
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Apply custom exclude patterns
|
|
93
|
+
return !exclude.some(pattern => {
|
|
94
|
+
const regex = new RegExp(pattern.replace(/\*/g, '.*'))
|
|
95
|
+
return regex.test(item)
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const children = []
|
|
100
|
+
|
|
101
|
+
for (const item of items) {
|
|
102
|
+
const itemPath = path.join(dirPath, item)
|
|
103
|
+
const stats = fs.statSync(itemPath)
|
|
104
|
+
|
|
105
|
+
if (stats.isDirectory()) {
|
|
106
|
+
const subTree = generateFileTree(itemPath, {
|
|
107
|
+
...options,
|
|
108
|
+
currentDepth: currentDepth + 1
|
|
109
|
+
})
|
|
110
|
+
children.push(subTree)
|
|
111
|
+
} else if (includeFiles) {
|
|
112
|
+
children.push({
|
|
113
|
+
type: 'file',
|
|
114
|
+
name: item,
|
|
115
|
+
size: showSize ? stats.size : undefined
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Sort: directories first, then files, alphabetically
|
|
121
|
+
children.sort((a, b) => {
|
|
122
|
+
if (a.type !== b.type) {
|
|
123
|
+
return a.type === 'directory' ? -1 : 1
|
|
124
|
+
}
|
|
125
|
+
return a.name.localeCompare(b.name)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
type: 'directory',
|
|
130
|
+
name: path.basename(dirPath) || path.basename(path.resolve(dirPath)),
|
|
131
|
+
children
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Format tree as ASCII tree structure
|
|
137
|
+
* @param {Object} tree - Tree structure
|
|
138
|
+
* @returns {string} Formatted tree
|
|
139
|
+
*/
|
|
140
|
+
function formatAsTree(tree) {
|
|
141
|
+
const lines = []
|
|
142
|
+
|
|
143
|
+
function traverse(node, prefix = '', isLast = true) {
|
|
144
|
+
const connector = isLast ? '└── ' : '├── '
|
|
145
|
+
const name = node.type === 'directory' ? `${node.name}/` : node.name
|
|
146
|
+
const size = node.size ? ` (${formatBytes(node.size)})` : ''
|
|
147
|
+
|
|
148
|
+
lines.push(`${prefix}${connector}${name}${size}`)
|
|
149
|
+
|
|
150
|
+
if (node.children && node.children.length > 0) {
|
|
151
|
+
const extension = isLast ? ' ' : '│ '
|
|
152
|
+
node.children.forEach((child, index) => {
|
|
153
|
+
const childIsLast = index === node.children.length - 1
|
|
154
|
+
traverse(child, prefix + extension, childIsLast)
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (node.truncated) {
|
|
159
|
+
const extension = isLast ? ' ' : '│ '
|
|
160
|
+
lines.push(`${prefix}${extension}...`)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
traverse(tree)
|
|
165
|
+
|
|
166
|
+
return '```\n' + lines.join('\n') + '\n```'
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Format tree as a list
|
|
171
|
+
* @param {Object} tree - Tree structure
|
|
172
|
+
* @returns {string} Formatted list
|
|
173
|
+
*/
|
|
174
|
+
function formatAsList(tree) {
|
|
175
|
+
const lines = []
|
|
176
|
+
|
|
177
|
+
function traverse(node, depth = 0) {
|
|
178
|
+
const indent = ' '.repeat(depth)
|
|
179
|
+
const name = node.type === 'directory' ? `**${node.name}/**` : node.name
|
|
180
|
+
const size = node.size ? ` *(${formatBytes(node.size)})*` : ''
|
|
181
|
+
|
|
182
|
+
lines.push(`${indent}- ${name}${size}`)
|
|
183
|
+
|
|
184
|
+
if (node.children && node.children.length > 0) {
|
|
185
|
+
node.children.forEach(child => {
|
|
186
|
+
traverse(child, depth + 1)
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (node.truncated) {
|
|
191
|
+
lines.push(`${indent} - ...`)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
traverse(tree)
|
|
196
|
+
|
|
197
|
+
return lines.join('\n')
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Format bytes as human readable string
|
|
202
|
+
* @param {number} bytes - Bytes to format
|
|
203
|
+
* @returns {string} Formatted string
|
|
204
|
+
*/
|
|
205
|
+
function formatBytes(bytes) {
|
|
206
|
+
if (bytes === 0) return '0 B'
|
|
207
|
+
|
|
208
|
+
const k = 1024
|
|
209
|
+
const sizes = ['B', 'KB', 'MB', 'GB']
|
|
210
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
211
|
+
|
|
212
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
|
213
|
+
}
|
package/src/transforms/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const code = require('./code')
|
|
2
2
|
const file = require('./file')
|
|
3
|
+
const fileTree = require('./fileTree')
|
|
3
4
|
const remoteContent = require('./remote')
|
|
4
5
|
const toc = require('./toc')
|
|
5
6
|
const sectionToc = require('./sectionToc')
|
|
@@ -12,7 +13,7 @@ const transforms = {
|
|
|
12
13
|
* Generate table of contents from markdown file
|
|
13
14
|
*
|
|
14
15
|
* **Options:**
|
|
15
|
-
* - `
|
|
16
|
+
* - `firstH1` - *boolean* - (optional): Show first h1 of doc in table of contents. Default `false`
|
|
16
17
|
* - `collapse` - *boolean* - (optional): Collapse the table of contents in a detail accordion. Default `false`
|
|
17
18
|
* - `collapseText` - *string* - (optional): Text the toc accordion summary
|
|
18
19
|
* - `excludeText` - *string* - (optional): Text to exclude in the table of contents. Default `Table of Contents`
|
|
@@ -112,6 +113,73 @@ const transforms = {
|
|
|
112
113
|
* @return {string} Updated content to place in the content block
|
|
113
114
|
*/
|
|
114
115
|
REMOTE: remoteContent,
|
|
116
|
+
/**
|
|
117
|
+
* ### > fileTree
|
|
118
|
+
*
|
|
119
|
+
* Generate a file tree table of contents
|
|
120
|
+
*
|
|
121
|
+
* **Options:**
|
|
122
|
+
* - `src` (optional): The directory path to generate the file tree for. Default `.` (current directory)
|
|
123
|
+
* - `maxDepth` (optional): Maximum depth to traverse in the directory tree. Default `3`
|
|
124
|
+
* - `includeFiles` (optional): Whether to include files in the tree or just directories. Default `true`
|
|
125
|
+
* - `exclude` (optional): Array of glob patterns to exclude from the tree. Default `[]`
|
|
126
|
+
* - `showSize` (optional): Whether to show file sizes. Default `false`
|
|
127
|
+
* - `format` (optional): Output format: "tree" or "list". Default `"tree"`
|
|
128
|
+
*
|
|
129
|
+
* **Example:**
|
|
130
|
+
* ```md
|
|
131
|
+
* <!-- doc-gen fileTree src="./src" maxDepth=2 -->
|
|
132
|
+
* file tree will be generated here
|
|
133
|
+
* <!-- end-doc-gen -->
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* **Example Output (tree format):**
|
|
137
|
+
* ```
|
|
138
|
+
* └── src/
|
|
139
|
+
* ├── transforms/
|
|
140
|
+
* │ ├── code/
|
|
141
|
+
* │ │ ...
|
|
142
|
+
* │ ├── fileTree.js
|
|
143
|
+
* │ ├── index.js
|
|
144
|
+
* │ └── toc.js
|
|
145
|
+
* ├── utils/
|
|
146
|
+
* │ ├── fs.js
|
|
147
|
+
* │ ├── logs.js
|
|
148
|
+
* │ └── text.js
|
|
149
|
+
* └── index.js
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* **Example Output (list format):**
|
|
153
|
+
* ```md
|
|
154
|
+
* - **src/**
|
|
155
|
+
* - **transforms/**
|
|
156
|
+
* - **code/**
|
|
157
|
+
* - ...
|
|
158
|
+
* - fileTree.js
|
|
159
|
+
* - index.js
|
|
160
|
+
* - toc.js
|
|
161
|
+
* - **utils/**
|
|
162
|
+
* - fs.js
|
|
163
|
+
* - logs.js
|
|
164
|
+
* - text.js
|
|
165
|
+
* - index.js
|
|
166
|
+
* ```
|
|
167
|
+
*
|
|
168
|
+
* **Example with file sizes:**
|
|
169
|
+
* ```
|
|
170
|
+
* └── src/
|
|
171
|
+
* ├── index.js (15.2 KB)
|
|
172
|
+
* └── package.json (552 B)
|
|
173
|
+
* ```
|
|
174
|
+
*
|
|
175
|
+
* Default `matchWord` is `doc-gen`
|
|
176
|
+
*
|
|
177
|
+
* ---
|
|
178
|
+
* @param {string} content The current content of the comment block
|
|
179
|
+
* @param {object} options The options passed in from the comment declaration
|
|
180
|
+
* @return {string} Updated content to place in the content block
|
|
181
|
+
*/
|
|
182
|
+
fileTree: fileTree,
|
|
115
183
|
/**
|
|
116
184
|
* ### > install
|
|
117
185
|
*
|
package/src/transforms/toc.js
CHANGED
|
@@ -33,7 +33,7 @@ module.exports = async function TOC(api) {
|
|
|
33
33
|
return sectionToc(api)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
opts.firsth1 = (opts.firsth1) ? true : false
|
|
36
|
+
opts.firsth1 = (opts.firsth1 || opts.firstH1) ? true : false
|
|
37
37
|
let contents = currentFileContent
|
|
38
38
|
// console.log('contents', contents)
|
|
39
39
|
|
|
@@ -54,7 +54,7 @@ module.exports = async function TOC(api) {
|
|
|
54
54
|
trimLeadingHeading: true,
|
|
55
55
|
// maxHeaderLevel: 2, // default: 4
|
|
56
56
|
// title: '**Table of Contents**',
|
|
57
|
-
//
|
|
57
|
+
// isNoTitle: true,
|
|
58
58
|
// isFolding: true,
|
|
59
59
|
// entryPrefix: '*',
|
|
60
60
|
// processAll: true,
|
package/src/types.js
CHANGED
package/src/utils/fs.test.js
CHANGED
package/src/utils/syntax.js
CHANGED
|
@@ -53,6 +53,30 @@ const yaml = {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
const sql = {
|
|
57
|
+
tags: ['/*', '*/'],
|
|
58
|
+
pattern: [
|
|
59
|
+
'\/\\*+',
|
|
60
|
+
'\\*+/'
|
|
61
|
+
],
|
|
62
|
+
singleLineTag: '--',
|
|
63
|
+
singleLinePattern: '--',
|
|
64
|
+
singleLine: '--.*$',
|
|
65
|
+
content: '[\\s\\S]*?'
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const toml = {
|
|
69
|
+
tags: ['#', '#'],
|
|
70
|
+
pattern: [
|
|
71
|
+
'#+',
|
|
72
|
+
'#+'
|
|
73
|
+
],
|
|
74
|
+
singleLineTag: '#',
|
|
75
|
+
singleLinePattern: '#+',
|
|
76
|
+
singleLine: '#.*$',
|
|
77
|
+
content: '[ \\t\\S]*?'
|
|
78
|
+
}
|
|
79
|
+
|
|
56
80
|
const syntaxMap = {
|
|
57
81
|
// <!-- x -->
|
|
58
82
|
md: html,
|
|
@@ -66,8 +90,12 @@ const syntaxMap = {
|
|
|
66
90
|
jsx: jsx,
|
|
67
91
|
mdx: jsx,
|
|
68
92
|
// ## x ##
|
|
93
|
+
yml: yaml,
|
|
69
94
|
yaml: yaml,
|
|
70
|
-
|
|
95
|
+
// -- x
|
|
96
|
+
sql: sql,
|
|
97
|
+
// # x
|
|
98
|
+
toml: toml
|
|
71
99
|
}
|
|
72
100
|
|
|
73
101
|
// Additional comment syntaxes for future
|
package/src/utils/text.js
CHANGED
|
@@ -188,11 +188,18 @@ function stripComments(str, syntax = 'md') {
|
|
|
188
188
|
const [ openPattern, closePattern ] = syntaxData.pattern
|
|
189
189
|
const OR = (syntaxData.singleLine) ? `|\\s?[ \\t]*${syntaxData.singleLine}` : ''
|
|
190
190
|
const CONTENT = syntaxData.content || '[\\s\\S]*?'
|
|
191
|
-
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
191
|
+
|
|
192
|
+
// Handle multi-line comments
|
|
193
|
+
const multiLinePattern = new RegExp(`\\s?[ \\t]*${openPattern}${CONTENT}${closePattern}`, 'gim')
|
|
194
|
+
let result = str.replace(multiLinePattern, '')
|
|
195
|
+
|
|
196
|
+
// Handle single-line comments if they exist
|
|
197
|
+
if (syntaxData.singleLine) {
|
|
198
|
+
const singleLinePattern = new RegExp(`\\s?[ \\t]*${syntaxData.singleLine}.*$`, 'gm')
|
|
199
|
+
result = result.replace(singleLinePattern, '')
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return result
|
|
196
203
|
}
|
|
197
204
|
|
|
198
205
|
|
package/src/utils/text.test.js
CHANGED
|
@@ -50,7 +50,7 @@ nice
|
|
|
50
50
|
nice={{ value: nice, cool: "true" }}
|
|
51
51
|
soclose=[jdjdjd, hdhfhfhffh]
|
|
52
52
|
rad="boss"
|
|
53
|
-
cool=true
|
|
53
|
+
cool=true notCool=false
|
|
54
54
|
nooooo={[one, two, 3, 4]}
|
|
55
55
|
numberZero=0,
|
|
56
56
|
xyz=999,
|
|
@@ -72,7 +72,7 @@ actual content
|
|
|
72
72
|
<!-- XYZ:START(cool) xxx
|
|
73
73
|
hhddh=cool -->
|
|
74
74
|
wowow
|
|
75
|
-
whatever we want
|
|
75
|
+
whatever we want
|
|
76
76
|
<!-- XYZ:END -->
|
|
77
77
|
|
|
78
78
|
|
|
@@ -135,7 +135,7 @@ test('Remove Markdown comments', () => {
|
|
|
135
135
|
'',
|
|
136
136
|
'',
|
|
137
137
|
'wowow',
|
|
138
|
-
'whatever we want
|
|
138
|
+
'whatever we want ',
|
|
139
139
|
'',
|
|
140
140
|
'',
|
|
141
141
|
'xyz',
|
|
@@ -297,6 +297,79 @@ test('Convert comment syntax', () => {
|
|
|
297
297
|
// assert.equal(typeof parsedValue, 'string')
|
|
298
298
|
})
|
|
299
299
|
|
|
300
|
+
test('Remove TOML comments', () => {
|
|
301
|
+
const parsedValue = stripComments(`
|
|
302
|
+
# This is a TOML comment
|
|
303
|
+
title = "TOML Example"
|
|
304
|
+
# Another comment
|
|
305
|
+
[owner]
|
|
306
|
+
name = "Tom Preston-Werner"
|
|
307
|
+
# Inline comment
|
|
308
|
+
organization = "GitHub" # This is an inline comment
|
|
309
|
+
# Multi-line comment
|
|
310
|
+
# that continues
|
|
311
|
+
# on multiple lines
|
|
312
|
+
created = 1979-05-27T07:32:00Z
|
|
313
|
+
|
|
314
|
+
[database]
|
|
315
|
+
server = "192.168.1.1"
|
|
316
|
+
ports = [ 8001, 8001, 8002 ]
|
|
317
|
+
connection_max = 5000
|
|
318
|
+
enabled = true
|
|
319
|
+
`, 'toml')
|
|
320
|
+
|
|
321
|
+
assert.equal(typeof parsedValue, 'string')
|
|
322
|
+
assert.equal(parsedValue.match(/#/), null)
|
|
323
|
+
assert.equal(parsedValue.split('\n'), [
|
|
324
|
+
'',
|
|
325
|
+
'title = "TOML Example"',
|
|
326
|
+
'[owner]',
|
|
327
|
+
'name = "Tom Preston-Werner"',
|
|
328
|
+
'organization = "GitHub"',
|
|
329
|
+
'created = 1979-05-27T07:32:00Z',
|
|
330
|
+
'',
|
|
331
|
+
'[database]',
|
|
332
|
+
'server = "192.168.1.1"',
|
|
333
|
+
'ports = [ 8001, 8001, 8002 ]',
|
|
334
|
+
'connection_max = 5000',
|
|
335
|
+
'enabled = true',
|
|
336
|
+
''
|
|
337
|
+
])
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
test('Remove SQL comments', () => {
|
|
341
|
+
const parsedValue = stripComments(`
|
|
342
|
+
-- This is a single line comment
|
|
343
|
+
SELECT * FROM users
|
|
344
|
+
-- Another single line comment
|
|
345
|
+
WHERE id = 1
|
|
346
|
+
/* This is a multi-line
|
|
347
|
+
comment that spans
|
|
348
|
+
multiple lines */
|
|
349
|
+
|
|
350
|
+
AND active = true
|
|
351
|
+
SELECT name -- inline comment
|
|
352
|
+
FROM products
|
|
353
|
+
WHERE price > 100 /* another inline comment */
|
|
354
|
+
`, 'sql')
|
|
355
|
+
|
|
356
|
+
console.log('parsedValue', parsedValue)
|
|
357
|
+
assert.equal(typeof parsedValue, 'string')
|
|
358
|
+
assert.equal(parsedValue.match(/--/), null, 'SQL comments should be removed 1')
|
|
359
|
+
assert.equal(parsedValue.match(/\/\*/), null, 'SQL comments should be removed 2')
|
|
360
|
+
assert.equal(parsedValue.match(/\*\//), null, 'SQL comments should be removed 3')
|
|
361
|
+
assert.equal(parsedValue.split('\n'), [
|
|
362
|
+
'',
|
|
363
|
+
'SELECT * FROM users',
|
|
364
|
+
'WHERE id = 1',
|
|
365
|
+
'',
|
|
366
|
+
'AND active = true',
|
|
367
|
+
'SELECT name',
|
|
368
|
+
'FROM products',
|
|
369
|
+
'WHERE price > 100',
|
|
370
|
+
''
|
|
371
|
+
])
|
|
372
|
+
})
|
|
300
373
|
|
|
301
374
|
function logOutput(value) {
|
|
302
375
|
console.log(value.split('\n'))
|