diff --git a/lib/unsafe.js b/lib/unsafe.js index c5c5d4a..5a4e0e5 100644 --- a/lib/unsafe.js +++ b/lib/unsafe.js @@ -130,7 +130,18 @@ export const unsafe = [ // Caret is not used in markdown for constructs. // An underscore can start emphasis, strong, or a thematic break. {atBreak: true, character: '_'}, - {character: '_', inConstruct: 'phrasing', notInConstruct: fullPhrasingSpans}, + { + character: '_', + before: '[\\s]', + inConstruct: 'phrasing', + notInConstruct: fullPhrasingSpans + }, + { + character: '_', + after: '[\\s]', + inConstruct: 'phrasing', + notInConstruct: fullPhrasingSpans + }, // A grave accent can start code (fenced or text), or it can break out of // a grave accent code fence. {atBreak: true, character: '`'}, diff --git a/test/index.js b/test/index.js index e47facd..a182b1e 100644 --- a/test/index.js +++ b/test/index.js @@ -3895,6 +3895,39 @@ test('escape', async function (t) { } ) + await t.test('should escape underscore at word start', async function () { + assert.equal( + to({ + type: 'paragraph', + children: [{type: 'text', value: '_WORLD'}] + }), + '\\_WORLD\n' + ) + }) + + await t.test('should escape underscore at word end', async function () { + assert.equal( + to({ + type: 'paragraph', + children: [{type: 'text', value: 'HELLO_'}] + }), + 'HELLO\\_\n' + ) + }) + + await t.test( + 'should not escape underscore inside of word', + async function () { + assert.equal( + to({ + type: 'paragraph', + children: [{type: 'text', value: 'HELLO_WORLD'}] + }), + 'HELLO_WORLD\n' + ) + } + ) + await t.test( 'should escape what would otherwise be a heading (atx)', async function () { @@ -4503,6 +4536,40 @@ test('roundtrip', async function (t) { ) }) + await t.test( + 'should roundtrip underscore without escaping', + async function () { + const value = `Separate paragraphs: + +:heavy_check_mark: + +[HELLO_WORLD.md](HELLO_WORLD.md) + +HELLO_WORLD https://some/web_site + +HELLO_WORLD. HELLO_WORLD, H_W; HELLO_OTHER! HELLO_YES? HELLO_NO + +HELLO_WORLD.HELLO_WORLD,H_W;HELLO_OTHER!HELLO_YES?HELLO_NO + +HELLO_WORLD - THIS_GOOD + +HELLO_WORLD-THIS_GOOD + +One Paragraph: + +:heavy_check_mark: +[HELLO_WORLD.md](HELLO_WORLD.md) +HELLO_WORLD https://some/web_site +HELLO_WORLD. HELLO_WORLD, H_W; HELLO_OTHER! HELLO_YES? HELLO_NO +HELLO_WORLD.HELLO_WORLD,H_W;HELLO_OTHER!HELLO_YES?HELLO_NO +HELLO_WORLD - THIS_GOOD +HELLO_WORLD-THIS_GOOD +` + + assert.equal(to(from(value)), value) + } + ) + await t.test( 'should roundtrip a sole blank line in fenced code', async function () { @@ -4781,6 +4848,20 @@ a __\\_ is this emphasis? __\\_ a _\\__ is this emphasis? _\\__ +a _ is_this emphasis? _ + +a __ is_this emphasis? __ + +a ___ is_this emphasis? ___ + +a _\\_ is_this emphasis? _\\_ + +a \\__ is_this emphasis? \\__ + +a __\\_ is_this emphasis? __\\_ + +a _\\__ is_this emphasis? _\\__ + One paragraph: a _ is this emphasis? _ @@ -4789,7 +4870,15 @@ a ___ is this emphasis? ___ a _\\_ is this emphasis? _\\_ a \\__ is this emphasis? \\__ a __\\_ is this emphasis? __\\_ -a _\\__ is this emphasis? _\\__` +a _\\__ is this emphasis? _\\__ + +a _ is_this emphasis? _ +a __ is_this emphasis? __ +a ___ is_this emphasis? ___ +a _\\_ is_this emphasis? _\\_ +a \\__ is_this emphasis? \\__ +a __\\_ is_this emphasis? __\\_ +a _\\__ is_this emphasis? _\\__` const tree = from(value) assert.deepEqual(