remove-markdown 0.6.3 → 0.6.4

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 CHANGED
@@ -33,7 +33,8 @@ const plainText = removeMd(markdown, {
33
33
  abbr: true, // remove abbreviations, if present (default: false)
34
34
  replaceLinksWithURL: true, // remove inline links, if present (default: false)
35
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: [])
36
+ htmlTagsToSkip: ['a', 'b'], // HTML tags to skip, if present (default: [])
37
+ throwError: false, // throw errors instead of catching and logging (default: false)
37
38
  });
38
39
  ```
39
40
 
package/index.d.ts CHANGED
@@ -2,11 +2,12 @@ declare function removeMd(md: string, options?: {
2
2
  stripListLeaders?: boolean;
3
3
  listUnicodeChar?: string;
4
4
  gfm?: boolean;
5
- useImgAltText: boolean;
5
+ useImgAltText?: boolean;
6
6
  abbr?: boolean;
7
7
  replaceLinksWithURL?: boolean;
8
8
  separateLinksAndTexts?: string;
9
9
  htmlTagsToSkip?: string[];
10
+ throwError?: boolean;
10
11
  }): string;
11
12
 
12
13
  export = removeMd;
package/index.js CHANGED
@@ -63,7 +63,7 @@ module.exports = function(md, options) {
63
63
  // Remove images
64
64
  .replace(/\!\[(.*?)\][\[\(].*?[\]\)]/g, options.useImgAltText ? '$1' : '')
65
65
  // Remove inline links
66
- .replace(/\[([\s\S]*?)\]\s*[\(\[].*?[\)\]]/g, options.replaceLinksWithURL ? '$2' : '$1')
66
+ .replace(/\[([\s\S]*?)\]\s*[\(\[](.*?)[\)\]]/g, options.replaceLinksWithURL ? '$2' : '$1')
67
67
  // Remove blockquotes
68
68
  .replace(/^(\n)?\s{0,3}>\s?/gm, '$1')
69
69
  // .replace(/(^|\n)\s{0,3}>\s?/g, '\n\n')
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "remove-markdown",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "Remove Markdown formatting from text",
5
5
  "main": "index.js",
6
+ "files": [
7
+ "index.js",
8
+ "index.d.ts",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
6
12
  "scripts": {
7
13
  "test": "./node_modules/.bin/mocha -R spec test/remove-markdown.js"
8
14
  },
@@ -1,8 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "mcp__acp__Bash",
5
- "mcp__acp__Write"
6
- ]
7
- }
8
- }
@@ -1,27 +0,0 @@
1
- name: Run tests
2
-
3
- on:
4
- push:
5
- branches: [ main ]
6
- pull_request:
7
- branches: [ main ]
8
-
9
- jobs:
10
- build:
11
- runs-on: ubuntu-latest
12
-
13
- strategy:
14
- matrix:
15
- node-version: [18.x, 20.x, 22.x]
16
- # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
17
-
18
- steps:
19
- - uses: actions/checkout@v3
20
- - name: Use Node.js ${{ matrix.node-version }}
21
- uses: actions/setup-node@v3
22
- with:
23
- node-version: ${{ matrix.node-version }}
24
- cache: 'npm'
25
- - run: npm ci
26
- - run: npm run build --if-present
27
- - run: npm test
package/CHANGELOG.md DELETED
@@ -1,32 +0,0 @@
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 DELETED
@@ -1,70 +0,0 @@
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
@@ -1,258 +0,0 @@
1
- 'use strict';
2
- const expect = require('chai').expect;
3
- const removeMd = require('../');
4
-
5
- describe('remove Markdown', function () {
6
- describe('removeMd', function () {
7
- it('should leave a string alone without markdown', function () {
8
- const string = 'Javascript Developers are the best.';
9
- expect(removeMd(string)).to.equal(string);
10
- });
11
-
12
- it('should strip out remaining markdown', function () {
13
- const string = '*Javascript* developers are the _best_.';
14
- const expected = 'Javascript developers are the best.';
15
- expect(removeMd(string)).to.equal(expected);
16
- });
17
-
18
- it('should leave non-matching markdown markdown', function () {
19
- const string = '*Javascript* developers* are the _best_.';
20
- const expected = 'Javascript developers* are the best.';
21
- expect(removeMd(string)).to.equal(expected);
22
- });
23
-
24
- it('should leave non-matching markdown, but strip empty anchors', function () {
25
- const string = '*Javascript* [developers]()* are the _best_.';
26
- const expected = 'Javascript developers* are the best.';
27
- expect(removeMd(string)).to.equal(expected);
28
- });
29
-
30
- it('should strip HTML', function () {
31
- const string = '<p>Hello World</p>';
32
- const expected = 'Hello World';
33
- expect(removeMd(string)).to.equal(expected);
34
- });
35
-
36
- it('should strip anchors', function () {
37
- const string = '*Javascript* [developers](https://engineering.condenast.io/)* are the _best_.';
38
- const expected = 'Javascript developers* are the best.';
39
- expect(removeMd(string)).to.equal(expected);
40
- });
41
-
42
- it('should strip img tags', function () {
43
- const string = '![](https://placebear.com/640/480)*Javascript* developers are the _best_.';
44
- const expected = 'Javascript developers are the best.';
45
- expect(removeMd(string)).to.equal(expected);
46
- });
47
-
48
- it('should use the alt-text of an image, if it is provided', function () {
49
- const string = '![This is the alt-text](https://www.example.com/images/logo.png)';
50
- const expected = 'This is the alt-text';
51
- expect(removeMd(string)).to.equal(expected);
52
- });
53
-
54
- it('should strip code tags', function () {
55
- const string = 'In `Getting Started` we set up `something` foo.';
56
- const expected = 'In Getting Started we set up something foo.';
57
- expect(removeMd(string)).to.equal(expected);
58
- });
59
-
60
- it('should strip simple multiline code tags', function () {
61
- const string = '```\ncode\n```';
62
- const expected = 'code';
63
- expect(removeMd(string)).to.equal(expected);
64
- });
65
-
66
- it('should strip complex multiline code blocks with language specified', function () {
67
- const string = '```javascript\nconst x = 1;\nconst y = 2;\nconsole.log(x + y);\n```';
68
- const expected = 'const x = 1;\nconst y = 2;\nconsole.log(x + y);';
69
- expect(removeMd(string)).to.equal(expected);
70
- });
71
-
72
- it('should strip multiline code blocks with multiple paragraphs', function () {
73
- const string = 'Text before\n\n```\ncode line 1\n\ncode line 2\n```\n\nText after';
74
- const expected = 'Text before\n\ncode line 1\n\ncode line 2\n\nText after';
75
- expect(removeMd(string)).to.equal(expected);
76
- });
77
-
78
- it('should leave hashtags in headings', function () {
79
- const string = '## This #heading contains #hashtags';
80
- const expected = 'This #heading contains #hashtags';
81
- expect(removeMd(string)).to.equal(expected);
82
- });
83
-
84
- it('should remove emphasis', function () {
85
- const string = 'I italicized an *I* and it _made_ me *sad*.';
86
- const expected = 'I italicized an I and it made me sad.';
87
- expect(removeMd(string)).to.equal(expected);
88
- });
89
-
90
- it('should remove emphasis only if there is no space between word and emphasis characters.', function () {
91
- const string = 'There should be no _space_, *before* *closing * _ephasis character _.';
92
- const expected = 'There should be no space, before *closing * _ephasis character _.';
93
- expect(removeMd(string)).to.equal(expected);
94
- });
95
-
96
- it('should remove "_" emphasis only if there is space before opening and after closing emphasis characters.', function () {
97
- const string = '._Spaces_ _ before_ and _after _ emphasised character results in no emphasis.';
98
- const expected = '.Spaces _ before_ and _after _ emphasised character results in no emphasis.';
99
- expect(removeMd(string)).to.equal(expected);
100
- });
101
-
102
- it('should remove double emphasis', function () {
103
- const string = '**this sentence has __double styling__**';
104
- const expected = 'this sentence has double styling';
105
- expect(removeMd(string)).to.equal(expected);
106
- });
107
-
108
- it('should not mistake a horizontal rule when symbols are mixed ', function () {
109
- const string = 'Some text on a line\n\n--*\n\nA line below';
110
- const expected = 'Some text on a line\n\n--*\n\nA line below';
111
- expect(removeMd(string)).to.equal(expected);
112
- });
113
-
114
- it('should remove horizontal rules', function () {
115
- const string = 'Some text on a line\n\n---\n\nA line below';
116
- const expected = 'Some text on a line\n\nA line below';
117
- expect(removeMd(string)).to.equal(expected);
118
- });
119
-
120
- it('should remove horizontal rules with space-separated asterisks', function () {
121
- const string = 'Some text on a line\n\n* * *\n\nA line below';
122
- const expected = 'Some text on a line\n\nA line below';
123
- expect(removeMd(string)).to.equal(expected);
124
- });
125
-
126
- it('should remove blockquotes', function () {
127
- const string = '>I am a blockquote';
128
- const expected = 'I am a blockquote';
129
- expect(removeMd(string)).to.equal(expected);
130
- });
131
-
132
- it('should remove blockquotes with spaces', function () {
133
- const string = '> I am a blockquote';
134
- const expected = 'I am a blockquote';
135
- expect(removeMd(string)).to.equal(expected);
136
- });
137
-
138
- it('should remove indented blockquotes', function () {
139
- var tests = [
140
- { string: ' > I am a blockquote', expected: 'I am a blockquote' },
141
- { string: ' > I am a blockquote', expected: 'I am a blockquote' },
142
- { string: ' > I am a blockquote', expected: 'I am a blockquote' },
143
- ];
144
- tests.forEach(function (test) {
145
- expect(removeMd(test.string)).to.equal(test.expected);
146
- });
147
- });
148
-
149
- it('should remove blockquotes over multiple lines', function () {
150
- const string = '> I am a blockquote firstline \n>I am a blockquote secondline';
151
- const expected = 'I am a blockquote firstline \nI am a blockquote secondline';
152
- expect(removeMd(string)).to.equal(expected);
153
- });
154
-
155
- it('should remove blockquotes following other content', function () {
156
- const string = '## A headline\n\nA paragraph of text\n\n> I am a blockquote';
157
- const expected = 'A headline\n\nA paragraph of text\n\nI am a blockquote';
158
-
159
- expect(removeMd(string)).to.equal(expected);
160
- });
161
-
162
- it('should not remove greater than signs', function () {
163
- var tests = [
164
- { string: '100 > 0', expected: '100 > 0' },
165
- { string: '100 >= 0', expected: '100 >= 0' },
166
- { string: '100>0', expected: '100>0' },
167
- { string: '> 100 > 0', expected: '100 > 0' },
168
- { string: '1 < 100', expected: '1 < 100' },
169
- { string: '1 <= 100', expected: '1 <= 100' },
170
- ];
171
- tests.forEach(function (test) {
172
- expect(removeMd(test.string)).to.equal(test.expected);
173
- });
174
- });
175
-
176
- it('should strip unordered list leaders', function () {
177
- const string = 'Some text on a line\n\n* A list Item\n* Another list item';
178
- const expected = 'Some text on a line\n\nA list Item\nAnother list item';
179
- expect(removeMd(string)).to.equal(expected);
180
- });
181
-
182
- it('should strip ordered list leaders', function () {
183
- const string = 'Some text on a line\n\n9. A list Item\n10. Another list item';
184
- const expected = 'Some text on a line\n\nA list Item\nAnother list item';
185
- expect(removeMd(string)).to.equal(expected);
186
- });
187
-
188
- it('should strip list items with bold word in the beginning', function () {
189
- const string = 'Some text on a line\n\n- **A** list Item\n- **Another** list item';
190
- const expected = 'Some text on a line\n\nA list Item\nAnother list item';
191
- expect(removeMd(string)).to.equal(expected);
192
- });
193
-
194
- it('should handle paragraphs with markdown', function () {
195
- const paragraph = '\n## This is a heading ##\n\nThis is a paragraph with [a link](http://www.disney.com/).\n\n### This is another heading\n\nIn `Getting Started` we set up `something` foo.\n\n * Some list\n * With items\n * Even indented';
196
- const expected = '\nThis is a heading\n\nThis is a paragraph with a link.\n\nThis is another heading\n\nIn Getting Started we set up something foo.\n\n Some list\n With items\n Even indented';
197
- expect(removeMd(paragraph)).to.equal(expected);
198
- });
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
-
212
- it('should not strip paragraphs without content', function() {
213
- const paragraph = '\n#This paragraph\n##This paragraph#';
214
- const expected = paragraph;
215
- expect(removeMd(paragraph)).to.equal(expected);
216
- });
217
-
218
- it('should not trigger ReDoS with atx-headers', function () {
219
- const start = Date.now();
220
-
221
- const paragraph = '\n## This is a long "'+' '.repeat(200)+'" heading ##\n';
222
- const expected = /\nThis is a long " {200}" heading\n/;
223
- expect(removeMd(paragraph)).to.match(expected);
224
-
225
- const duration = Date.now()-start;
226
- expect(duration).to.be.lt(1000);
227
- });
228
-
229
- it('should work fast even with lots of whitespace', function () {
230
- const string = 'Some text with lots of whitespace';
231
- const expected = 'Some text with lots of whitespace';
232
- expect(removeMd(string)).to.equal(expected);
233
- });
234
-
235
- it('should still remove escaped markdown syntax', function () {
236
- const string = '\# Heading in _italic_';
237
- const expected = 'Heading in italic';
238
- expect(removeMd(string)).to.equal(expected);
239
- });
240
-
241
- it('should skip specified HTML tags when htmlTagsToSkip option is provided', () => {
242
- const markdown =
243
- '<div>HTML content <sub>Superscript</sub> <span>span text</span></div>'
244
- const result = removeMd(markdown, {htmlTagsToSkip: ['sub']})
245
- expect(result).to.equal('HTML content <sub>Superscript</sub> span text')
246
- const result2 = removeMd(markdown, {htmlTagsToSkip: ['sub', 'span']})
247
- expect(result2).to.equal(
248
- 'HTML content <sub>Superscript</sub> <span>span text</span>',
249
- )
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
- });
257
- });
258
- });