remove-markdown 0.6.1 → 0.6.3
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/.claude/settings.local.json +8 -0
- package/.github/workflows/default.yaml +1 -1
- package/CHANGELOG.md +32 -0
- package/CLAUDE.md +70 -0
- package/README.md +8 -4
- package/index.d.ts +12 -11
- package/index.js +8 -3
- package/package.json +1 -1
- package/test/remove-markdown.js +21 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.6.3] - 2026-01-14
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- New `separateLinksAndTexts` option to replace inline links with text and URL separated by a custom string ([#101](https://github.com/zuchka/remove-markdown/pull/101) by [@tafel](https://github.com/tafel))
|
|
10
|
+
- Example: `removeMd('[link](http://example.com)', { separateLinksAndTexts: ': ' })` returns `'link: http://example.com'`
|
|
11
|
+
|
|
12
|
+
## [0.6.2] - 2025-05-02
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Improved handling of links with square brackets inside them ([#93](https://github.com/zuchka/remove-markdown/pull/93))
|
|
17
|
+
|
|
18
|
+
## [0.6.1] - 2025-05-02
|
|
19
|
+
|
|
20
|
+
### Improved
|
|
21
|
+
|
|
22
|
+
- Better support for multiline code blocks ([#96](https://github.com/zuchka/remove-markdown/pull/96) by [@johnjiang](https://github.com/johnjiang))
|
|
23
|
+
|
|
24
|
+
## [0.6.0] - 2024-12-16
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- `htmlTagsToSkip` option to preserve specific HTML tags while stripping others ([#88](https://github.com/zuchka/remove-markdown/pull/88))
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Horizontal rules regex pattern ([#91](https://github.com/zuchka/remove-markdown/pull/91))
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
**remove-markdown** is a Node.js module that strips Markdown formatting from text, leaving only plain text. It's designed for use cases like displaying excerpts without Markdown syntax.
|
|
8
|
+
|
|
9
|
+
The entire implementation is a single-file module (`index.js`) that exports one function which applies a series of regex replacements to remove various Markdown elements.
|
|
10
|
+
|
|
11
|
+
## Development Commands
|
|
12
|
+
|
|
13
|
+
### Running Tests
|
|
14
|
+
```bash
|
|
15
|
+
npm test
|
|
16
|
+
```
|
|
17
|
+
Runs the Mocha test suite with the spec reporter.
|
|
18
|
+
|
|
19
|
+
### Running a Single Test
|
|
20
|
+
To run a specific test, use Mocha's grep flag:
|
|
21
|
+
```bash
|
|
22
|
+
./node_modules/.bin/mocha -R spec test/remove-markdown.js --grep "test description pattern"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Code Architecture
|
|
26
|
+
|
|
27
|
+
### Core Structure
|
|
28
|
+
- **index.js** (97 lines): Main module that exports a single function
|
|
29
|
+
- Takes markdown text and an options object as parameters
|
|
30
|
+
- Applies regex replacements in a specific order to strip Markdown syntax
|
|
31
|
+
- Returns plain text or original input if error occurs (unless `throwError` option is set)
|
|
32
|
+
|
|
33
|
+
### Key Implementation Details
|
|
34
|
+
|
|
35
|
+
**Regex Application Order Matters**: The order of regex replacements is critical. For example, horizontal rules must be removed before list leaders to avoid conflicts (see index.js:15).
|
|
36
|
+
|
|
37
|
+
**Options System**: All options default to specific values if not provided. The module uses `options.hasOwnProperty()` checks rather than simple truthy checks to allow `false` values to be explicitly set.
|
|
38
|
+
|
|
39
|
+
**Error Handling**: The entire regex processing is wrapped in try-catch. By default, errors are logged and the original markdown is returned. Set `options.throwError: true` to propagate errors.
|
|
40
|
+
|
|
41
|
+
**HTML Tag Filtering**: The `htmlTagsToSkip` option dynamically builds a regex to preserve specific HTML tags while removing others (see index.js:41-49).
|
|
42
|
+
|
|
43
|
+
**Link Replacement**: Two mutually exclusive link handling modes:
|
|
44
|
+
- `replaceLinksWithURL: true` - replaces links with URLs (note: implementation at index.js:66 references `$2` which appears to be a bug as it should extract the URL)
|
|
45
|
+
- `separateLinksAndTexts` - replaces `[text](url)` with `text<separator>url` format (applied first at index.js:52)
|
|
46
|
+
|
|
47
|
+
### Test Structure
|
|
48
|
+
- **test/remove-markdown.js** (258 lines): Comprehensive test suite using Mocha and Chai
|
|
49
|
+
- Tests cover all markdown elements: headers, emphasis, lists, links, images, code blocks, blockquotes, HTML tags
|
|
50
|
+
- Includes performance tests to prevent ReDoS vulnerabilities
|
|
51
|
+
- Tests edge cases like emphasis with spaces, nested brackets, indentation
|
|
52
|
+
|
|
53
|
+
## Important Considerations
|
|
54
|
+
|
|
55
|
+
### When Adding Features
|
|
56
|
+
- New regex patterns must be carefully positioned in the replacement chain (index.js:18-89)
|
|
57
|
+
- Test edge cases thoroughly, especially patterns that could conflict with existing ones
|
|
58
|
+
- Consider performance implications - some patterns are vulnerable to ReDoS if not carefully written
|
|
59
|
+
|
|
60
|
+
### Known Quirks
|
|
61
|
+
- The `replaceLinksWithURL` option appears to have a bug where it references `$2` but the capturing group setup may not work as intended (index.js:66)
|
|
62
|
+
- Empty or invalid markdown input is handled gracefully by returning the original input
|
|
63
|
+
- The module preserves code block content (including newlines) while stripping the fencing
|
|
64
|
+
|
|
65
|
+
### Testing Strategy
|
|
66
|
+
When modifying regex patterns:
|
|
67
|
+
1. Run the full test suite first
|
|
68
|
+
2. Add specific test cases for your changes
|
|
69
|
+
3. Test performance with large inputs and repeated patterns
|
|
70
|
+
4. Verify your changes don't break the order-dependent behavior
|
package/README.md
CHANGED
|
@@ -26,10 +26,14 @@ You can also supply an options object to the function. Currently, the following
|
|
|
26
26
|
|
|
27
27
|
```js
|
|
28
28
|
const plainText = removeMd(markdown, {
|
|
29
|
-
stripListLeaders: true ,
|
|
30
|
-
listUnicodeChar: '',
|
|
31
|
-
gfm: true
|
|
32
|
-
useImgAltText: true
|
|
29
|
+
stripListLeaders: true , // strip list leaders (default: true)
|
|
30
|
+
listUnicodeChar: '', // char to insert instead of stripped list leaders (default: '')
|
|
31
|
+
gfm: true, // support GitHub-Flavored Markdown (default: true)
|
|
32
|
+
useImgAltText: true, // replace images with alt-text, if present (default: true)
|
|
33
|
+
abbr: true, // remove abbreviations, if present (default: false)
|
|
34
|
+
replaceLinksWithURL: true, // remove inline links, if present (default: false)
|
|
35
|
+
separateLinksAndTexts: ': ', // replace inline links with text, separator and link, if present (default: null)
|
|
36
|
+
htmlTagsToSkip: ['a', 'b'] // HTML tags to skip, if present (default: [])
|
|
33
37
|
});
|
|
34
38
|
```
|
|
35
39
|
|
package/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
declare function removeMd(md: string, options?: {
|
|
2
|
-
stripListLeaders?: boolean;
|
|
3
|
-
listUnicodeChar?: string;
|
|
4
|
-
gfm?: boolean;
|
|
5
|
-
useImgAltText: boolean;
|
|
6
|
-
abbr?: boolean;
|
|
7
|
-
replaceLinksWithURL?: boolean;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
declare function removeMd(md: string, options?: {
|
|
2
|
+
stripListLeaders?: boolean;
|
|
3
|
+
listUnicodeChar?: string;
|
|
4
|
+
gfm?: boolean;
|
|
5
|
+
useImgAltText: boolean;
|
|
6
|
+
abbr?: boolean;
|
|
7
|
+
replaceLinksWithURL?: boolean;
|
|
8
|
+
separateLinksAndTexts?: string;
|
|
9
|
+
htmlTagsToSkip?: string[];
|
|
10
|
+
}): string;
|
|
11
|
+
|
|
12
|
+
export = removeMd;
|
package/index.js
CHANGED
|
@@ -6,6 +6,7 @@ module.exports = function(md, options) {
|
|
|
6
6
|
options.useImgAltText = options.hasOwnProperty('useImgAltText') ? options.useImgAltText : true;
|
|
7
7
|
options.abbr = options.hasOwnProperty('abbr') ? options.abbr : false;
|
|
8
8
|
options.replaceLinksWithURL = options.hasOwnProperty('replaceLinksWithURL') ? options.replaceLinksWithURL : false;
|
|
9
|
+
options.separateLinksAndTexts = options.hasOwnProperty('separateLinksAndTexts') ? options.separateLinksAndTexts : null;
|
|
9
10
|
options.htmlTagsToSkip = options.hasOwnProperty('htmlTagsToSkip') ? options.htmlTagsToSkip : [];
|
|
10
11
|
options.throwError = options.hasOwnProperty('throwError') ? options.throwError : false;
|
|
11
12
|
|
|
@@ -36,7 +37,7 @@ module.exports = function(md, options) {
|
|
|
36
37
|
// Remove abbreviations
|
|
37
38
|
output = output.replace(/\*\[.*\]:.*\n/, '');
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
let htmlReplaceRegex = /<[^>]*>/g
|
|
41
42
|
if (options.htmlTagsToSkip && options.htmlTagsToSkip.length > 0) {
|
|
42
43
|
// Create a regex that matches tags not in htmlTagsToSkip
|
|
@@ -47,6 +48,10 @@ module.exports = function(md, options) {
|
|
|
47
48
|
)
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
if (options.separateLinksAndTexts) {
|
|
52
|
+
output = output.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1' + options.separateLinksAndTexts + '$2');
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
output = output
|
|
51
56
|
// Remove HTML tags
|
|
52
57
|
.replace(htmlReplaceRegex, '')
|
|
@@ -58,7 +63,7 @@ module.exports = function(md, options) {
|
|
|
58
63
|
// Remove images
|
|
59
64
|
.replace(/\!\[(.*?)\][\[\(].*?[\]\)]/g, options.useImgAltText ? '$1' : '')
|
|
60
65
|
// Remove inline links
|
|
61
|
-
.replace(/\[([
|
|
66
|
+
.replace(/\[([\s\S]*?)\]\s*[\(\[].*?[\)\]]/g, options.replaceLinksWithURL ? '$2' : '$1')
|
|
62
67
|
// Remove blockquotes
|
|
63
68
|
.replace(/^(\n)?\s{0,3}>\s?/gm, '$1')
|
|
64
69
|
// .replace(/(^|\n)\s{0,3}>\s?/g, '\n\n')
|
|
@@ -68,7 +73,7 @@ module.exports = function(md, options) {
|
|
|
68
73
|
.replace(/^(\n)?\s{0,}#{1,6}\s*( (.+))? +#+$|^(\n)?\s{0,}#{1,6}\s*( (.+))?$/gm, '$1$3$4$6')
|
|
69
74
|
// Remove * emphasis
|
|
70
75
|
.replace(/([\*]+)(\S)(.*?\S)??\1/g, '$2$3')
|
|
71
|
-
// Remove _ emphasis. Unlike *, _ emphasis gets rendered only if
|
|
76
|
+
// Remove _ emphasis. Unlike *, _ emphasis gets rendered only if
|
|
72
77
|
// 1. Either there is a whitespace character before opening _ and after closing _.
|
|
73
78
|
// 2. Or _ is at the start/end of the string.
|
|
74
79
|
.replace(/(^|\W)([_]+)(\S)(.*?\S)??\2($|\W)/g, '$1$3$4$5')
|
package/package.json
CHANGED
package/test/remove-markdown.js
CHANGED
|
@@ -62,13 +62,13 @@ describe('remove Markdown', function () {
|
|
|
62
62
|
const expected = 'code';
|
|
63
63
|
expect(removeMd(string)).to.equal(expected);
|
|
64
64
|
});
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
it('should strip complex multiline code blocks with language specified', function () {
|
|
67
67
|
const string = '```javascript\nconst x = 1;\nconst y = 2;\nconsole.log(x + y);\n```';
|
|
68
68
|
const expected = 'const x = 1;\nconst y = 2;\nconsole.log(x + y);';
|
|
69
69
|
expect(removeMd(string)).to.equal(expected);
|
|
70
70
|
});
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
it('should strip multiline code blocks with multiple paragraphs', function () {
|
|
73
73
|
const string = 'Text before\n\n```\ncode line 1\n\ncode line 2\n```\n\nText after';
|
|
74
74
|
const expected = 'Text before\n\ncode line 1\n\ncode line 2\n\nText after';
|
|
@@ -145,7 +145,7 @@ describe('remove Markdown', function () {
|
|
|
145
145
|
expect(removeMd(test.string)).to.equal(test.expected);
|
|
146
146
|
});
|
|
147
147
|
});
|
|
148
|
-
|
|
148
|
+
|
|
149
149
|
it('should remove blockquotes over multiple lines', function () {
|
|
150
150
|
const string = '> I am a blockquote firstline \n>I am a blockquote secondline';
|
|
151
151
|
const expected = 'I am a blockquote firstline \nI am a blockquote secondline';
|
|
@@ -197,6 +197,18 @@ describe('remove Markdown', function () {
|
|
|
197
197
|
expect(removeMd(paragraph)).to.equal(expected);
|
|
198
198
|
});
|
|
199
199
|
|
|
200
|
+
it('should remove links', function () {
|
|
201
|
+
const string = 'This is a [link](http://www.disney.com/).';
|
|
202
|
+
const expected = 'This is a link.';
|
|
203
|
+
expect(removeMd(string)).to.equal(expected);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should remove links with square brackets', function () {
|
|
207
|
+
const string = 'This is a [link [with brackets]](http://www.disney.com/).';
|
|
208
|
+
const expected = 'This is a link [with brackets].';
|
|
209
|
+
expect(removeMd(string)).to.equal(expected);
|
|
210
|
+
});
|
|
211
|
+
|
|
200
212
|
it('should not strip paragraphs without content', function() {
|
|
201
213
|
const paragraph = '\n#This paragraph\n##This paragraph#';
|
|
202
214
|
const expected = paragraph;
|
|
@@ -236,5 +248,11 @@ describe('remove Markdown', function () {
|
|
|
236
248
|
'HTML content <sub>Superscript</sub> <span>span text</span>',
|
|
237
249
|
)
|
|
238
250
|
})
|
|
251
|
+
|
|
252
|
+
it('should replace inline link with text and link, with separator', function () {
|
|
253
|
+
const string = 'some [inline link](http://www.disney.com/).';
|
|
254
|
+
const expected = 'some inline link: http://www.disney.com/.';
|
|
255
|
+
expect(removeMd(string, {separateLinksAndTexts: ': '})).to.equal(expected);
|
|
256
|
+
});
|
|
239
257
|
});
|
|
240
258
|
});
|