comment-block-transformer 0.6.1 → 0.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/CHANGELOG.md +11 -0
- package/package.json +2 -2
- package/src/index.js +44 -1
- package/test/error-handling.test.js +49 -0
- package/test/normalize-blank-lines.test.js +181 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [0.7.0](https://github.com/DavidWells/markdown-magic/compare/comment-block-transformer@0.6.1...comment-block-transformer@0.7.0) (2026-01-19)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **block-transformer:** add normalizeBlankLines option and utility ([bde5038](https://github.com/DavidWells/markdown-magic/commit/bde50389800da417787c6c4aae8165dd9f7de0b5))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
## [0.6.1](https://github.com/DavidWells/markdown-magic/compare/comment-block-transformer@0.6.0...comment-block-transformer@0.6.1) (2026-01-19)
|
|
7
18
|
|
|
8
19
|
**Note:** Version bump only for package comment-block-transformer
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "comment-block-transformer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Transform markdown blocks based on configured transforms",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
"publishConfig": {
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "37ed26b98df488f005d9ef91ecd8cd9649c5c067"
|
|
29
29
|
}
|
package/src/index.js
CHANGED
|
@@ -67,6 +67,7 @@ const CLOSE_WORD = '/block'
|
|
|
67
67
|
* @property {Array<Middleware>} [afterMiddleware=[]] - Middleware functions change inner block content after transforms.
|
|
68
68
|
* @property {boolean} [removeComments=false] - Remove comments from the processed contents.
|
|
69
69
|
* @property {boolean} [forceRemoveComments=false] - Force remove comments even when srcPath === outputPath, strips comments directly in updatedContents.
|
|
70
|
+
* @property {boolean} [normalizeBlankLines=false] - Collapse multiple consecutive blank lines to single blank line (preserves blank lines in code blocks).
|
|
70
71
|
* @property {string} [srcPath] - The source path.
|
|
71
72
|
* @property {string} [outputPath] - The output path.
|
|
72
73
|
* @property {import('comment-block-parser').CustomPatterns} [customPatterns] - Custom regex patterns for open and close tags.
|
|
@@ -105,6 +106,7 @@ async function blockTransformer(inputText, config) {
|
|
|
105
106
|
afterMiddleware = [],
|
|
106
107
|
removeComments = false,
|
|
107
108
|
forceRemoveComments = false,
|
|
109
|
+
normalizeBlankLines: normalizeBlankLinesOpt = false,
|
|
108
110
|
customPatterns
|
|
109
111
|
} = opts
|
|
110
112
|
// Don't default close - let undefined pass through to enable pattern mode in block-parser
|
|
@@ -295,7 +297,10 @@ async function blockTransformer(inputText, config) {
|
|
|
295
297
|
|
|
296
298
|
let finalContents = updatedContents
|
|
297
299
|
if (forceRemoveComments && openPattern && closePattern) {
|
|
298
|
-
finalContents =
|
|
300
|
+
finalContents = finalContents.replace(openPattern, '').replace(closePattern, '')
|
|
301
|
+
}
|
|
302
|
+
if (normalizeBlankLinesOpt) {
|
|
303
|
+
finalContents = normalizeBlankLines(finalContents)
|
|
299
304
|
}
|
|
300
305
|
|
|
301
306
|
// console.log('inputText', inputText)
|
|
@@ -444,6 +449,43 @@ function getCodeLocation(srcPath, line, column = '0') {
|
|
|
444
449
|
return `${srcPath}:${line}:${column}`
|
|
445
450
|
}
|
|
446
451
|
|
|
452
|
+
/**
|
|
453
|
+
* Normalize blank lines in content, collapsing multiple consecutive blank lines to a single one.
|
|
454
|
+
* Preserves blank lines inside fenced code blocks (``` or ~~~).
|
|
455
|
+
* @param {string} content - The content to normalize
|
|
456
|
+
* @returns {string} Content with normalized blank lines
|
|
457
|
+
*/
|
|
458
|
+
function normalizeBlankLines(content) {
|
|
459
|
+
if (!content) return content
|
|
460
|
+
|
|
461
|
+
const lines = content.split('\n')
|
|
462
|
+
const result = []
|
|
463
|
+
let inCodeBlock = false
|
|
464
|
+
let consecutiveBlanks = 0
|
|
465
|
+
|
|
466
|
+
for (const line of lines) {
|
|
467
|
+
// Check for code fence (``` or ~~~), possibly with language specifier, possibly indented
|
|
468
|
+
if (/^\s*(`{3,}|~{3,})/.test(line)) {
|
|
469
|
+
inCodeBlock = !inCodeBlock
|
|
470
|
+
consecutiveBlanks = 0
|
|
471
|
+
result.push(line)
|
|
472
|
+
continue
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (line.trim() === '' && !inCodeBlock) {
|
|
476
|
+
consecutiveBlanks++
|
|
477
|
+
if (consecutiveBlanks <= 1) {
|
|
478
|
+
result.push(line)
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
consecutiveBlanks = 0
|
|
482
|
+
result.push(line)
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return result.join('\n')
|
|
487
|
+
}
|
|
488
|
+
|
|
447
489
|
if (require.main === module) {
|
|
448
490
|
const yaml = `
|
|
449
491
|
- name: Run tests two
|
|
@@ -456,4 +498,5 @@ if (require.main === module) {
|
|
|
456
498
|
module.exports = {
|
|
457
499
|
blockTransformer,
|
|
458
500
|
indentString,
|
|
501
|
+
normalizeBlankLines,
|
|
459
502
|
}
|
|
@@ -288,4 +288,53 @@ content
|
|
|
288
288
|
assert.ok(result.updatedContents.includes('transformed'))
|
|
289
289
|
})
|
|
290
290
|
|
|
291
|
+
test('normalizeBlankLines option collapses multiple blank lines', async () => {
|
|
292
|
+
const text = `## Section
|
|
293
|
+
|
|
294
|
+
<!-- block test -->
|
|
295
|
+
content
|
|
296
|
+
<!-- /block -->
|
|
297
|
+
|
|
298
|
+
<!-- block empty -->
|
|
299
|
+
<!-- /block -->
|
|
300
|
+
|
|
301
|
+
---`
|
|
302
|
+
const result = await blockTransformer(text, {
|
|
303
|
+
forceRemoveComments: true,
|
|
304
|
+
normalizeBlankLines: true,
|
|
305
|
+
transforms: {
|
|
306
|
+
test: () => 'transformed',
|
|
307
|
+
empty: () => ''
|
|
308
|
+
}
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
// Should not have 2+ consecutive blank lines outside code blocks
|
|
312
|
+
assert.not.ok(result.updatedContents.includes('\n\n\n'))
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
test('normalizeBlankLines preserves blank lines in code blocks', async () => {
|
|
316
|
+
const text = `## Section
|
|
317
|
+
|
|
318
|
+
<!-- block code -->
|
|
319
|
+
\`\`\`bash
|
|
320
|
+
line 1
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
line 2
|
|
324
|
+
\`\`\`
|
|
325
|
+
<!-- /block -->
|
|
326
|
+
|
|
327
|
+
---`
|
|
328
|
+
const result = await blockTransformer(text, {
|
|
329
|
+
forceRemoveComments: true,
|
|
330
|
+
normalizeBlankLines: true,
|
|
331
|
+
transforms: {
|
|
332
|
+
code: (api) => api.content
|
|
333
|
+
}
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
// Code block should preserve its blank lines
|
|
337
|
+
assert.ok(result.updatedContents.includes('line 1\n\n\nline 2'))
|
|
338
|
+
})
|
|
339
|
+
|
|
291
340
|
test.run()
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// Tests for normalizeBlankLines utility
|
|
2
|
+
|
|
3
|
+
const { test } = require('uvu')
|
|
4
|
+
const assert = require('uvu/assert')
|
|
5
|
+
const { normalizeBlankLines } = require('../src')
|
|
6
|
+
|
|
7
|
+
test('collapses multiple blank lines to single blank line', () => {
|
|
8
|
+
const input = `line 1
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
line 2`
|
|
12
|
+
const result = normalizeBlankLines(input)
|
|
13
|
+
assert.is(result, `line 1
|
|
14
|
+
|
|
15
|
+
line 2`)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('preserves single blank lines', () => {
|
|
19
|
+
const input = `line 1
|
|
20
|
+
|
|
21
|
+
line 2`
|
|
22
|
+
const result = normalizeBlankLines(input)
|
|
23
|
+
assert.is(result, input)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('collapses 3+ blank lines to single', () => {
|
|
27
|
+
const input = `line 1
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
line 2`
|
|
33
|
+
const result = normalizeBlankLines(input)
|
|
34
|
+
assert.is(result, `line 1
|
|
35
|
+
|
|
36
|
+
line 2`)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('preserves blank lines inside fenced code blocks (```)', () => {
|
|
40
|
+
const input = `before
|
|
41
|
+
|
|
42
|
+
\`\`\`bash
|
|
43
|
+
line 1
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
line 2
|
|
47
|
+
\`\`\`
|
|
48
|
+
|
|
49
|
+
after`
|
|
50
|
+
const result = normalizeBlankLines(input)
|
|
51
|
+
assert.is(result, input)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('preserves blank lines inside fenced code blocks (~~~)', () => {
|
|
55
|
+
const input = `before
|
|
56
|
+
|
|
57
|
+
~~~js
|
|
58
|
+
line 1
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
line 2
|
|
62
|
+
~~~
|
|
63
|
+
|
|
64
|
+
after`
|
|
65
|
+
const result = normalizeBlankLines(input)
|
|
66
|
+
assert.is(result, input)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('handles multiple code blocks', () => {
|
|
70
|
+
const input = `start
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
\`\`\`
|
|
74
|
+
code 1
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
more code
|
|
78
|
+
\`\`\`
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
middle
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
\`\`\`
|
|
85
|
+
code 2
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
more
|
|
89
|
+
\`\`\`
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
end`
|
|
93
|
+
const expected = `start
|
|
94
|
+
|
|
95
|
+
\`\`\`
|
|
96
|
+
code 1
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
more code
|
|
100
|
+
\`\`\`
|
|
101
|
+
|
|
102
|
+
middle
|
|
103
|
+
|
|
104
|
+
\`\`\`
|
|
105
|
+
code 2
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
more
|
|
109
|
+
\`\`\`
|
|
110
|
+
|
|
111
|
+
end`
|
|
112
|
+
const result = normalizeBlankLines(input)
|
|
113
|
+
assert.is(result, expected)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('handles code block with language specifier', () => {
|
|
117
|
+
const input = `text
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
\`\`\`javascript
|
|
121
|
+
const x = 1
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
const y = 2
|
|
125
|
+
\`\`\`
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
more text`
|
|
129
|
+
const expected = `text
|
|
130
|
+
|
|
131
|
+
\`\`\`javascript
|
|
132
|
+
const x = 1
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
const y = 2
|
|
136
|
+
\`\`\`
|
|
137
|
+
|
|
138
|
+
more text`
|
|
139
|
+
const result = normalizeBlankLines(input)
|
|
140
|
+
assert.is(result, expected)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('handles empty input', () => {
|
|
144
|
+
assert.is(normalizeBlankLines(''), '')
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test('handles input with no blank lines', () => {
|
|
148
|
+
const input = `line 1
|
|
149
|
+
line 2
|
|
150
|
+
line 3`
|
|
151
|
+
assert.is(normalizeBlankLines(input), input)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('handles indented code fences', () => {
|
|
155
|
+
const input = `text
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
\`\`\`
|
|
159
|
+
code
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
more
|
|
163
|
+
\`\`\`
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
end`
|
|
167
|
+
const expected = `text
|
|
168
|
+
|
|
169
|
+
\`\`\`
|
|
170
|
+
code
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
more
|
|
174
|
+
\`\`\`
|
|
175
|
+
|
|
176
|
+
end`
|
|
177
|
+
const result = normalizeBlankLines(input)
|
|
178
|
+
assert.is(result, expected)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test.run()
|