comment-block-transformer 0.5.7 → 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 +30 -0
- package/package.json +2 -2
- package/src/index.js +53 -5
- package/test/error-handling.test.js +89 -0
- package/test/normalize-blank-lines.test.js +181 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,36 @@
|
|
|
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
|
+
|
|
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)
|
|
18
|
+
|
|
19
|
+
**Note:** Version bump only for package comment-block-transformer
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# [0.6.0](https://github.com/DavidWells/markdown-magic/compare/comment-block-transformer@0.5.7...comment-block-transformer@0.6.0) (2026-01-19)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* **block-transformer:** add forceRemoveComments option ([7503338](https://github.com/DavidWells/markdown-magic/commit/7503338ee182a935b04e7162276fd3df072b59a9))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
6
36
|
## [0.5.7](https://github.com/DavidWells/markdown-magic/compare/comment-block-transformer@0.5.6...comment-block-transformer@0.5.7) (2026-01-19)
|
|
7
37
|
|
|
8
38
|
**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
|
@@ -66,6 +66,8 @@ const CLOSE_WORD = '/block'
|
|
|
66
66
|
* @property {Array<Middleware>} [beforeMiddleware=[]] - Middleware functions change inner block content before transforms.
|
|
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
|
+
* @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).
|
|
69
71
|
* @property {string} [srcPath] - The source path.
|
|
70
72
|
* @property {string} [outputPath] - The output path.
|
|
71
73
|
* @property {import('comment-block-parser').CustomPatterns} [customPatterns] - Custom regex patterns for open and close tags.
|
|
@@ -103,6 +105,8 @@ async function blockTransformer(inputText, config) {
|
|
|
103
105
|
beforeMiddleware = [],
|
|
104
106
|
afterMiddleware = [],
|
|
105
107
|
removeComments = false,
|
|
108
|
+
forceRemoveComments = false,
|
|
109
|
+
normalizeBlankLines: normalizeBlankLinesOpt = false,
|
|
106
110
|
customPatterns
|
|
107
111
|
} = opts
|
|
108
112
|
// Don't default close - let undefined pass through to enable pattern mode in block-parser
|
|
@@ -285,15 +289,22 @@ async function blockTransformer(inputText, config) {
|
|
|
285
289
|
|
|
286
290
|
const isNewPath = srcPath !== outputPath
|
|
287
291
|
|
|
288
|
-
if (removeComments && !isNewPath) {
|
|
292
|
+
if (removeComments && !isNewPath && !forceRemoveComments) {
|
|
289
293
|
throw new Error('"removeComments" can only be used if "outputPath" option is set. Otherwise this will break doc generation.')
|
|
290
294
|
}
|
|
291
295
|
|
|
292
|
-
const stripComments = isNewPath && removeComments
|
|
296
|
+
const stripComments = (isNewPath && removeComments) || forceRemoveComments
|
|
293
297
|
|
|
298
|
+
let finalContents = updatedContents
|
|
299
|
+
if (forceRemoveComments && openPattern && closePattern) {
|
|
300
|
+
finalContents = finalContents.replace(openPattern, '').replace(closePattern, '')
|
|
301
|
+
}
|
|
302
|
+
if (normalizeBlankLinesOpt) {
|
|
303
|
+
finalContents = normalizeBlankLines(finalContents)
|
|
304
|
+
}
|
|
294
305
|
|
|
295
306
|
// console.log('inputText', inputText)
|
|
296
|
-
// console.log('updatedContents', updatedContents)
|
|
307
|
+
// console.log('updatedContents', updatedContents)
|
|
297
308
|
return {
|
|
298
309
|
isChanged: inputText !== updatedContents,
|
|
299
310
|
isNewPath,
|
|
@@ -303,12 +314,11 @@ async function blockTransformer(inputText, config) {
|
|
|
303
314
|
transforms: transformsToRun,
|
|
304
315
|
missingTransforms,
|
|
305
316
|
originalContents: inputText,
|
|
306
|
-
updatedContents,
|
|
317
|
+
updatedContents: finalContents,
|
|
307
318
|
patterns: regexInfo,
|
|
308
319
|
}
|
|
309
320
|
}
|
|
310
321
|
|
|
311
|
-
|
|
312
322
|
/** @typedef {BlockData & { sourceLocation?: string, transform?: string }} BlockDataExtended */
|
|
313
323
|
|
|
314
324
|
function getDetails({
|
|
@@ -439,6 +449,43 @@ function getCodeLocation(srcPath, line, column = '0') {
|
|
|
439
449
|
return `${srcPath}:${line}:${column}`
|
|
440
450
|
}
|
|
441
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
|
+
|
|
442
489
|
if (require.main === module) {
|
|
443
490
|
const yaml = `
|
|
444
491
|
- name: Run tests two
|
|
@@ -451,4 +498,5 @@ if (require.main === module) {
|
|
|
451
498
|
module.exports = {
|
|
452
499
|
blockTransformer,
|
|
453
500
|
indentString,
|
|
501
|
+
normalizeBlankLines,
|
|
454
502
|
}
|
|
@@ -248,4 +248,93 @@ original content
|
|
|
248
248
|
assert.ok(result.updatedContents.includes('original content'))
|
|
249
249
|
})
|
|
250
250
|
|
|
251
|
+
test('forceRemoveComments bypasses safety check and strips comments from updatedContents', async () => {
|
|
252
|
+
const text = `
|
|
253
|
+
<!-- block test -->
|
|
254
|
+
content
|
|
255
|
+
<!-- /block -->
|
|
256
|
+
`
|
|
257
|
+
const result = await blockTransformer(text, {
|
|
258
|
+
srcPath: '/same/path.md',
|
|
259
|
+
outputPath: '/same/path.md',
|
|
260
|
+
forceRemoveComments: true,
|
|
261
|
+
transforms: {
|
|
262
|
+
test: (api) => 'transformed'
|
|
263
|
+
}
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
assert.is(result.stripComments, true)
|
|
267
|
+
assert.not.ok(result.updatedContents.includes('<!-- block'))
|
|
268
|
+
assert.not.ok(result.updatedContents.includes('<!-- /block'))
|
|
269
|
+
assert.ok(result.updatedContents.includes('transformed'))
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
test('forceRemoveComments works without outputPath', async () => {
|
|
273
|
+
const text = `
|
|
274
|
+
<!-- block test -->
|
|
275
|
+
content
|
|
276
|
+
<!-- /block -->
|
|
277
|
+
`
|
|
278
|
+
const result = await blockTransformer(text, {
|
|
279
|
+
forceRemoveComments: true,
|
|
280
|
+
transforms: {
|
|
281
|
+
test: (api) => 'transformed'
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
assert.is(result.stripComments, true)
|
|
286
|
+
assert.not.ok(result.updatedContents.includes('<!-- block'))
|
|
287
|
+
assert.not.ok(result.updatedContents.includes('<!-- /block'))
|
|
288
|
+
assert.ok(result.updatedContents.includes('transformed'))
|
|
289
|
+
})
|
|
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
|
+
|
|
251
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()
|