markdown-magic 3.6.5 → 4.0.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/cli.js +1 -1
- package/package.json +23 -28
- package/src/{cli.js → cli-run.js} +0 -0
- package/src/{cli.test.js → cli-run.test.js} +1 -1
- package/src/defaults.js +2 -2
- package/src/index.js +30 -22
- package/src/index.test.js +2 -2
- package/src/process-contents.js +2 -458
- package/src/transforms/code/index.js +6 -6
- package/src/transforms/fileTree.js +213 -0
- package/src/transforms/index.js +87 -19
- package/src/transforms/install.js +3 -3
- package/src/transforms/sectionToc.js +4 -4
- package/src/transforms/toc.js +7 -7
- package/src/transforms/wordCount.js +3 -2
- package/src/types.js +1 -1
- package/src/utils/fs.js +22 -0
- package/src/utils/fs.test.js +5 -2
- package/src/utils/index.js +21 -0
- package/src/utils/regex-timeout.js +1 -1
- package/src/utils/syntax.js +29 -1
- package/src/utils/text.js +115 -16
- package/src/utils/text.test.js +76 -3
- package/README.md +0 -605
- package/src/block-parser-js.test.js +0 -171
- package/src/block-parser.js +0 -405
- package/src/block-parser.test.js +0 -481
- package/src/process-file.js +0 -66
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`
|
|
@@ -20,12 +21,12 @@ const transforms = {
|
|
|
20
21
|
*
|
|
21
22
|
* **Example:**
|
|
22
23
|
* ```md
|
|
23
|
-
* <!--
|
|
24
|
+
* <!-- docs TOC -->
|
|
24
25
|
* toc will be generated here
|
|
25
|
-
* <!--
|
|
26
|
+
* <!-- /docs -->
|
|
26
27
|
* ```
|
|
27
28
|
*
|
|
28
|
-
* Default `matchWord` is `
|
|
29
|
+
* Default `matchWord` is `docs`
|
|
29
30
|
*
|
|
30
31
|
* ---
|
|
31
32
|
* @param {string} content The current content of the comment block
|
|
@@ -47,18 +48,18 @@ const transforms = {
|
|
|
47
48
|
*
|
|
48
49
|
* **Example:**
|
|
49
50
|
* ```md
|
|
50
|
-
* <!--
|
|
51
|
+
* <!-- docs CODE src="./relative/path/to/code.js" -->
|
|
51
52
|
* This content will be dynamically replaced with code from the file
|
|
52
|
-
* <!--
|
|
53
|
+
* <!-- /docs -->
|
|
53
54
|
* ```
|
|
54
55
|
*
|
|
55
56
|
* ```md
|
|
56
|
-
* <!--
|
|
57
|
+
* <!-- docs CODE src="./relative/path/to/code.js" lines=22-44 -->
|
|
57
58
|
* This content will be dynamically replaced with code from the file lines 22 through 44
|
|
58
|
-
* <!--
|
|
59
|
+
* <!-- /docs -->
|
|
59
60
|
* ```
|
|
60
|
-
*
|
|
61
|
-
* Default `matchWord` is `
|
|
61
|
+
*
|
|
62
|
+
* Default `matchWord` is `docs`
|
|
62
63
|
*
|
|
63
64
|
* ---
|
|
64
65
|
* @param {string} content The current content of the comment block
|
|
@@ -76,12 +77,12 @@ const transforms = {
|
|
|
76
77
|
*
|
|
77
78
|
* **Example:**
|
|
78
79
|
* ```md
|
|
79
|
-
* <!--
|
|
80
|
+
* <!-- docs FILE src=./path/to/file -->
|
|
80
81
|
* This content will be dynamically replaced from the local file
|
|
81
|
-
* <!--
|
|
82
|
+
* <!-- /docs -->
|
|
82
83
|
* ```
|
|
83
84
|
*
|
|
84
|
-
* Default `matchWord` is `
|
|
85
|
+
* Default `matchWord` is `docs`
|
|
85
86
|
*
|
|
86
87
|
* ---
|
|
87
88
|
* @param {string} content The current content of the comment block
|
|
@@ -99,12 +100,12 @@ const transforms = {
|
|
|
99
100
|
*
|
|
100
101
|
* **Example:**
|
|
101
102
|
* ```md
|
|
102
|
-
* <!--
|
|
103
|
+
* <!-- docs REMOTE url=http://url-to-raw-md-file.md -->
|
|
103
104
|
* This content will be dynamically replaced from the remote url
|
|
104
|
-
* <!--
|
|
105
|
+
* <!-- /docs -->
|
|
105
106
|
* ```
|
|
106
107
|
*
|
|
107
|
-
* Default `matchWord` is `
|
|
108
|
+
* Default `matchWord` is `docs`
|
|
108
109
|
*
|
|
109
110
|
* ---
|
|
110
111
|
* @param {string} content The current content of the comment block
|
|
@@ -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
|
+
* <!-- docs fileTree src="./src" maxDepth=2 -->
|
|
132
|
+
* file tree will be generated here
|
|
133
|
+
* <!-- /docs -->
|
|
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 `docs`
|
|
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
|
*
|
|
@@ -125,12 +193,12 @@ const transforms = {
|
|
|
125
193
|
*
|
|
126
194
|
* **Example:**
|
|
127
195
|
* ```md
|
|
128
|
-
* <!--
|
|
196
|
+
* <!-- docs install -->
|
|
129
197
|
* Installation instructions will be generated here
|
|
130
|
-
* <!--
|
|
198
|
+
* <!-- /docs -->
|
|
131
199
|
* ```
|
|
132
200
|
*
|
|
133
|
-
* Default `matchWord` is `
|
|
201
|
+
* Default `matchWord` is `docs`
|
|
134
202
|
*
|
|
135
203
|
* ---
|
|
136
204
|
* @param {string} content The current content of the comment block
|
|
@@ -11,12 +11,12 @@ const path = require('path')
|
|
|
11
11
|
*
|
|
12
12
|
* **Example:**
|
|
13
13
|
* ```md
|
|
14
|
-
* <!--
|
|
14
|
+
* <!-- docs INSTALL packageName=my-package -->
|
|
15
15
|
* Installation instructions will be generated here
|
|
16
|
-
* <!--
|
|
16
|
+
* <!-- /docs -->
|
|
17
17
|
* ```
|
|
18
18
|
*
|
|
19
|
-
* Default `matchWord` is `
|
|
19
|
+
* Default `matchWord` is `docs`
|
|
20
20
|
*
|
|
21
21
|
* ---
|
|
22
22
|
* @param {string} content The current content of the comment block
|
|
@@ -5,15 +5,15 @@ const { readFile } = require('../utils/fs')
|
|
|
5
5
|
const details = require('../utils/details')
|
|
6
6
|
|
|
7
7
|
module.exports = async function sectionToc(api) {
|
|
8
|
-
const { options,
|
|
8
|
+
const { options, currentContent, originalContent, srcPath, getBlockDetails } = api
|
|
9
9
|
const opts = options || {}
|
|
10
10
|
let { collapseText, collapse } = opts
|
|
11
11
|
/* Sub table of contents */
|
|
12
|
-
const originalBlock = getBlockDetails(
|
|
13
|
-
const closestHeading = findClosestParentHeading(
|
|
12
|
+
const originalBlock = getBlockDetails(originalContent)
|
|
13
|
+
const closestHeading = findClosestParentHeading(originalContent, originalBlock.block.start)
|
|
14
14
|
// console.log('closestHeading', closestHeading)
|
|
15
15
|
|
|
16
|
-
const subToc = await generateToc(
|
|
16
|
+
const subToc = await generateToc(currentContent, {
|
|
17
17
|
...opts,
|
|
18
18
|
normalizeLevels: true,
|
|
19
19
|
// Set sub table of contents
|
package/src/transforms/toc.js
CHANGED
|
@@ -11,15 +11,15 @@ const sectionToc = require('./sectionToc')
|
|
|
11
11
|
* @property {number} [maxDepth=4] - Maximum depth of headings to include in the table of contents. Default is `4`.
|
|
12
12
|
* @example
|
|
13
13
|
```md
|
|
14
|
-
<!--
|
|
14
|
+
<!-- docs (TOC) -->
|
|
15
15
|
toc will be generated here
|
|
16
|
-
<!--
|
|
17
|
-
```
|
|
16
|
+
<!-- /docs -->
|
|
17
|
+
```
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
module.exports = async function TOC(api) {
|
|
21
21
|
// console.log("TOC API", api)
|
|
22
|
-
const {
|
|
22
|
+
const { currentContent, srcPath, getBlockDetails } = api
|
|
23
23
|
/** @type {ToCTransformOptions} */
|
|
24
24
|
const options = api.options || {}
|
|
25
25
|
// console.log("TOC OPTIONS", options)
|
|
@@ -33,8 +33,8 @@ module.exports = async function TOC(api) {
|
|
|
33
33
|
return sectionToc(api)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
opts.firsth1 = (opts.firsth1) ? true : false
|
|
37
|
-
let contents =
|
|
36
|
+
opts.firsth1 = (opts.firsth1 || opts.firstH1) ? true : false
|
|
37
|
+
let contents = currentContent
|
|
38
38
|
// console.log('contents', contents)
|
|
39
39
|
|
|
40
40
|
let collapseText = opts.collapseText
|
|
@@ -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,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { getWordCount } = require('../utils/text')
|
|
2
2
|
|
|
3
|
-
module.exports = function wordCount(
|
|
4
|
-
|
|
3
|
+
module.exports = function wordCount(api) {
|
|
4
|
+
const { currentContent } = api
|
|
5
|
+
return getWordCount(currentContent)
|
|
5
6
|
}
|
package/src/types.js
CHANGED
package/src/utils/fs.js
CHANGED
|
@@ -9,10 +9,21 @@ const { readdir, stat, readFile } = fs
|
|
|
9
9
|
|
|
10
10
|
const IS_HIDDEN_FILE = /(^|[\\\/])\.[^\\\/\.]/g
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Check if value is a RegExp
|
|
14
|
+
* @param {any} thing - Value to check
|
|
15
|
+
* @returns {boolean} True if value is RegExp
|
|
16
|
+
*/
|
|
12
17
|
function isRegex(thing) {
|
|
13
18
|
return (thing instanceof RegExp)
|
|
14
19
|
}
|
|
15
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Write file with directory creation if needed
|
|
23
|
+
* @param {string} filePath - File path to write to
|
|
24
|
+
* @param {string} content - Content to write
|
|
25
|
+
* @returns {Promise<void>}
|
|
26
|
+
*/
|
|
16
27
|
async function writeFile(filePath, content) {
|
|
17
28
|
try {
|
|
18
29
|
await fs.writeFile(filePath, content)
|
|
@@ -69,6 +80,12 @@ async function escalade(start, callback) {
|
|
|
69
80
|
|
|
70
81
|
// }
|
|
71
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Convert absolute path to relative path
|
|
85
|
+
* @param {string} file - Absolute file path
|
|
86
|
+
* @param {string} cwd - Current working directory
|
|
87
|
+
* @returns {string} Relative path
|
|
88
|
+
*/
|
|
72
89
|
function convertToRelativePath(file, cwd) {
|
|
73
90
|
return file.replace(cwd, '').replace(/^\//, '')
|
|
74
91
|
}
|
|
@@ -165,6 +182,11 @@ function depth(string) {
|
|
|
165
182
|
return path.normalize(string).split(path.sep).length - 1;
|
|
166
183
|
}
|
|
167
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Check if path is local (not remote)
|
|
187
|
+
* @param {string} filePath - File path to check
|
|
188
|
+
* @returns {boolean} True if path is local
|
|
189
|
+
*/
|
|
168
190
|
function isLocalPath(filePath) {
|
|
169
191
|
if (filePath.startsWith('github.com/') || filePath.startsWith('raw.githubusercontent.com/')) return false
|
|
170
192
|
return _isLocalPath(filePath)
|
package/src/utils/fs.test.js
CHANGED
|
@@ -26,8 +26,8 @@ test('Finds file from dir', async () => {
|
|
|
26
26
|
})
|
|
27
27
|
|
|
28
28
|
test('getGitignoreContents', async () => {
|
|
29
|
-
const files = await getGitignoreContents()
|
|
30
|
-
|
|
29
|
+
const files = await getGitignoreContents(path.resolve(__dirname, '../../', '.gitignore'))
|
|
30
|
+
//*
|
|
31
31
|
console.log('files', files)
|
|
32
32
|
/** */
|
|
33
33
|
assert.is(Array.isArray(files), true)
|
|
@@ -42,6 +42,9 @@ test('getGitignoreContents', async () => {
|
|
|
42
42
|
'misc',
|
|
43
43
|
'misc/**/**.js',
|
|
44
44
|
"__misc",
|
|
45
|
+
'large-table.md',
|
|
46
|
+
'large-table.js',
|
|
47
|
+
'large-table-data.json',
|
|
45
48
|
'**/old-test/cool.md',
|
|
46
49
|
'pids',
|
|
47
50
|
'*.pid',
|
package/src/utils/index.js
CHANGED
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
|
|
2
|
+
/**
|
|
3
|
+
* Filter function to get only unique values
|
|
4
|
+
* @param {any} value - Current value
|
|
5
|
+
* @param {number} index - Current index
|
|
6
|
+
* @param {any[]} self - Original array
|
|
7
|
+
* @returns {boolean} True if value is unique
|
|
8
|
+
*/
|
|
2
9
|
function onlyUnique(value, index, self) {
|
|
3
10
|
return self.indexOf(value) === index
|
|
4
11
|
}
|
|
5
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Get code location string in format path:line:column
|
|
15
|
+
* @param {string} srcPath - Source file path
|
|
16
|
+
* @param {string|number} line - Line number
|
|
17
|
+
* @param {string} [column='0'] - Column number
|
|
18
|
+
* @returns {string} Location string
|
|
19
|
+
*/
|
|
6
20
|
function getCodeLocation(srcPath, line, column = '0') {
|
|
7
21
|
return `${srcPath}:${line}:${column}`
|
|
8
22
|
}
|
|
9
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Pluralize word based on count
|
|
26
|
+
* @param {any[]|number} thing - Array or number to count
|
|
27
|
+
* @param {string} [single=''] - Singular form
|
|
28
|
+
* @param {string} [plural=''] - Plural form
|
|
29
|
+
* @returns {string} Singular or plural form
|
|
30
|
+
*/
|
|
10
31
|
function pluralize(thing, single = '', plural = '') {
|
|
11
32
|
const count = Array.isArray(thing) ? thing.length : Number(thing)
|
|
12
33
|
return count === 1 ? single : plural
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// https://stackoverflow.com/questions/38859506/cancel-regex-match-if-timeout
|
|
2
2
|
const util = require('util')
|
|
3
3
|
const vm = require('vm')
|
|
4
|
-
const { getBlockRegex } = require('
|
|
4
|
+
const { getBlockRegex } = require('comment-block-parser')
|
|
5
5
|
const { getSyntaxInfo } = require('./syntax')
|
|
6
6
|
|
|
7
7
|
const goodString = `
|
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
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
const { syntaxMap } = require('./syntax')
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Split string into lines
|
|
5
|
+
* @param {string} str - Input string
|
|
6
|
+
* @returns {string[]} Array of lines
|
|
7
|
+
*/
|
|
3
8
|
function getLines(str = '') {
|
|
4
9
|
return str.split(/\r\n|\r|\n/)
|
|
5
10
|
}
|
|
6
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Get line count from string
|
|
14
|
+
* @param {string} str - Input string
|
|
15
|
+
* @returns {number} Number of lines
|
|
16
|
+
*/
|
|
7
17
|
function getLineCount(str = '') {
|
|
8
18
|
return getLines(str).length
|
|
9
19
|
}
|
|
10
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Get row and column position from character index
|
|
23
|
+
* @param {string} input - Input string
|
|
24
|
+
* @param {number} indexToFind - Character index to find
|
|
25
|
+
* @returns {{row: number, col: number}} Row and column position
|
|
26
|
+
*/
|
|
11
27
|
function getRowAndColumnFromCharPos(input, indexToFind) {
|
|
12
28
|
const preChunk = input.substr(0, indexToFind);
|
|
13
29
|
const row = preChunk.split('\n').length - 1
|
|
@@ -16,23 +32,50 @@ function getRowAndColumnFromCharPos(input, indexToFind) {
|
|
|
16
32
|
return { row, col }
|
|
17
33
|
};
|
|
18
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Get word count from string
|
|
37
|
+
* @param {string} str - Input string
|
|
38
|
+
* @returns {number} Word count
|
|
39
|
+
*/
|
|
19
40
|
function getWordCount(str = '') {
|
|
20
41
|
return str.trim().split(/\s+/).length
|
|
21
42
|
}
|
|
22
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Get first character from string
|
|
46
|
+
* @param {string} str - Input string
|
|
47
|
+
* @returns {string} First character
|
|
48
|
+
*/
|
|
23
49
|
function getFirstCharacter(str) {
|
|
24
50
|
return str.charAt(0)
|
|
25
51
|
}
|
|
26
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Get last character from string
|
|
55
|
+
* @param {string} str - Input string
|
|
56
|
+
* @returns {string} Last character
|
|
57
|
+
*/
|
|
27
58
|
function getLastCharacter(str) {
|
|
28
59
|
return str.substr(-1)
|
|
29
60
|
}
|
|
30
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Get leading spaces from text
|
|
64
|
+
* @param {string} text - Input text
|
|
65
|
+
* @returns {string} Leading spaces
|
|
66
|
+
*/
|
|
31
67
|
function getLeadingSpaces(text) {
|
|
32
68
|
const matches = text.match(/^\s*/)
|
|
33
69
|
return (matches && matches[0]) ? matches[0] : ''
|
|
34
70
|
}
|
|
35
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Get text between character positions
|
|
74
|
+
* @param {string} text - Input text
|
|
75
|
+
* @param {number} start - Start position
|
|
76
|
+
* @param {number} end - End position
|
|
77
|
+
* @returns {string} Text between positions
|
|
78
|
+
*/
|
|
36
79
|
function getTextBetweenChars(text, start, end) {
|
|
37
80
|
return text.slice(start, end)
|
|
38
81
|
}
|
|
@@ -56,6 +99,14 @@ function getTextBetweenWords(s, prefix, suffix) {
|
|
|
56
99
|
return s
|
|
57
100
|
}
|
|
58
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Replace text between character positions
|
|
104
|
+
* @param {string} str - Input string
|
|
105
|
+
* @param {number} start - Start position
|
|
106
|
+
* @param {number} end - End position
|
|
107
|
+
* @param {string} newStr - Replacement string
|
|
108
|
+
* @returns {string} Modified string
|
|
109
|
+
*/
|
|
59
110
|
function replaceTextBetweenChars(str = '', start, end, newStr) {
|
|
60
111
|
return str.substring(0, start) + newStr + str.substring(end)
|
|
61
112
|
}
|
|
@@ -77,22 +128,39 @@ function getTextBetweenLines(content, startLine, endLine) {
|
|
|
77
128
|
if (startDefined && !endDefined) {
|
|
78
129
|
return lines.slice(startLine - 1, startLine).join('')
|
|
79
130
|
}
|
|
80
|
-
|
|
131
|
+
// @ts-ignore
|
|
132
|
+
if (startLine && endLine && Number(startLine) <= Number(endLine)) {
|
|
81
133
|
return lines.slice(startLine - 1, endLine).join('\n')
|
|
82
134
|
}
|
|
83
135
|
}
|
|
84
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Check if string is uppercase
|
|
139
|
+
* @param {string} str - Input string
|
|
140
|
+
* @returns {boolean} True if uppercase
|
|
141
|
+
*/
|
|
85
142
|
function isUpperCase(str) {
|
|
86
143
|
return str === str.toUpperCase()
|
|
87
144
|
}
|
|
88
145
|
|
|
89
146
|
// https://github.com/jamiebuilds/min-indent/blob/master/index.js
|
|
147
|
+
/**
|
|
148
|
+
* Find minimum indentation in string
|
|
149
|
+
* @param {string} string - Input string
|
|
150
|
+
* @returns {number} Minimum indentation level
|
|
151
|
+
*/
|
|
90
152
|
function findMinIndent(string) {
|
|
91
153
|
const match = string.match(/^[ \t]*(?=\S)/gm)
|
|
92
154
|
if (!match) return 0
|
|
93
155
|
return match.reduce((r, a) => Math.min(r, a.length), Infinity)
|
|
94
156
|
}
|
|
95
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Strip indentation from string
|
|
160
|
+
* @param {string} string - Input string
|
|
161
|
+
* @param {number} [indentation] - Indentation level to strip
|
|
162
|
+
* @returns {string} String with indentation stripped
|
|
163
|
+
*/
|
|
96
164
|
function stripIndent(string, indentation) {
|
|
97
165
|
const indent = typeof indentation !== 'undefined' ? indentation : findMinIndent(string);
|
|
98
166
|
if (indent === 0) {
|
|
@@ -105,7 +173,7 @@ function stripIndent(string, indentation) {
|
|
|
105
173
|
/**
|
|
106
174
|
* Trim leading & trailing spaces/line breaks in code and keeps the indentation of the first non-empty line
|
|
107
175
|
* @param {string|number} str
|
|
108
|
-
* @returns string
|
|
176
|
+
* @returns {string}
|
|
109
177
|
*/
|
|
110
178
|
function trimString(str = '') {
|
|
111
179
|
let content = (typeof str === 'number') ? str.toString() : str
|
|
@@ -113,6 +181,15 @@ function stripIndent(string, indentation) {
|
|
|
113
181
|
return content.replace(/^(?:[\t ]*(?:\r?\n|\r))+|\s+$/g, '')
|
|
114
182
|
}
|
|
115
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Add indentation to string
|
|
186
|
+
* @param {string} string - Input string
|
|
187
|
+
* @param {number} [count=1] - Number of indentations to add
|
|
188
|
+
* @param {object} [options={}] - Options for indentation
|
|
189
|
+
* @param {string} [options.indent=' '] - Character(s) to use for indentation
|
|
190
|
+
* @param {boolean} [options.includeEmptyLines=false] - Whether to indent empty lines
|
|
191
|
+
* @returns {string} Indented string
|
|
192
|
+
*/
|
|
116
193
|
function indentString(string, count = 1, options = {}) {
|
|
117
194
|
const {
|
|
118
195
|
indent = ' ',
|
|
@@ -130,8 +207,8 @@ function indentString(string, count = 1, options = {}) {
|
|
|
130
207
|
/**
|
|
131
208
|
* Removes the indentation of multiline strings
|
|
132
209
|
* @link https://github.com/victornpb/tiny-dedent/
|
|
133
|
-
* @param
|
|
134
|
-
* @
|
|
210
|
+
* @param {string} str - A template literal string
|
|
211
|
+
* @returns {string} A string without the indentation
|
|
135
212
|
*/
|
|
136
213
|
function dedentString(str) {
|
|
137
214
|
str = str.replace(/^[ \t]*\r?\n/, ''); // remove leading blank line
|
|
@@ -180,7 +257,7 @@ function stripCommentBlockOld(str, syntax = 'md') {
|
|
|
180
257
|
/**
|
|
181
258
|
* Strip out comment blocks
|
|
182
259
|
* @param {string} str
|
|
183
|
-
* @param {
|
|
260
|
+
* @param {import('../types').SyntaxType} syntax
|
|
184
261
|
* @returns {string} clean comment-less string
|
|
185
262
|
*/
|
|
186
263
|
function stripComments(str, syntax = 'md') {
|
|
@@ -188,11 +265,18 @@ function stripComments(str, syntax = 'md') {
|
|
|
188
265
|
const [ openPattern, closePattern ] = syntaxData.pattern
|
|
189
266
|
const OR = (syntaxData.singleLine) ? `|\\s?[ \\t]*${syntaxData.singleLine}` : ''
|
|
190
267
|
const CONTENT = syntaxData.content || '[\\s\\S]*?'
|
|
191
|
-
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
268
|
+
|
|
269
|
+
// Handle multi-line comments
|
|
270
|
+
const multiLinePattern = new RegExp(`\\s?[ \\t]*${openPattern}${CONTENT}${closePattern}`, 'gim')
|
|
271
|
+
let result = str.replace(multiLinePattern, '')
|
|
272
|
+
|
|
273
|
+
// Handle single-line comments if they exist
|
|
274
|
+
if (syntaxData.singleLine) {
|
|
275
|
+
const singleLinePattern = new RegExp(`\\s?[ \\t]*${syntaxData.singleLine}.*$`, 'gm')
|
|
276
|
+
result = result.replace(singleLinePattern, '')
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return result
|
|
196
280
|
}
|
|
197
281
|
|
|
198
282
|
|
|
@@ -291,33 +375,48 @@ function convertCommentSyntax(str, { from, to }) {
|
|
|
291
375
|
}
|
|
292
376
|
|
|
293
377
|
/**
|
|
294
|
-
*
|
|
295
|
-
* @param {string} str
|
|
296
|
-
* @returns
|
|
378
|
+
* Capitalize first letter
|
|
379
|
+
* @param {string} str - Input string
|
|
380
|
+
* @returns {string} String with first letter capitalized
|
|
297
381
|
*/
|
|
298
382
|
function capitalizeFirstLetter(str) {
|
|
299
383
|
return capitalize(str.charAt(0)) + str.slice(1)
|
|
300
384
|
}
|
|
301
385
|
|
|
302
386
|
/**
|
|
303
|
-
*
|
|
304
|
-
* @param {string} str
|
|
305
|
-
* @returns
|
|
387
|
+
* Capitalize string
|
|
388
|
+
* @param {string} str - Input string
|
|
389
|
+
* @returns {string} Capitalized string
|
|
306
390
|
*/
|
|
307
391
|
function capitalize(str = '') {
|
|
308
392
|
return str.toUpperCase()
|
|
309
393
|
}
|
|
310
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Convert string to camelCase
|
|
397
|
+
* @param {string} str - Input string
|
|
398
|
+
* @returns {string} camelCase string
|
|
399
|
+
*/
|
|
311
400
|
function camelCase(str = '') {
|
|
312
401
|
return str.replace(/[-_ ](\w)/g, (_, c) => c.toUpperCase())
|
|
313
402
|
}
|
|
314
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Convert string to kebab-case
|
|
406
|
+
* @param {string} str - Input string
|
|
407
|
+
* @returns {string} kebab-case string
|
|
408
|
+
*/
|
|
315
409
|
function kebabCase(str = '') {
|
|
316
410
|
return str.replace(/\B([A-Z])/g, '-$1').toLowerCase()
|
|
317
411
|
}
|
|
318
412
|
|
|
319
413
|
const smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|the|to|vs?\.?|via)$/i;
|
|
320
414
|
|
|
415
|
+
/**
|
|
416
|
+
* Convert string to Title Case
|
|
417
|
+
* @param {string} str - Input string
|
|
418
|
+
* @returns {string} Title Case string
|
|
419
|
+
*/
|
|
321
420
|
function toTitleCase(str = '') {
|
|
322
421
|
return str.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, (match, index, title) => {
|
|
323
422
|
if (index > 0
|