diff --git a/README.md b/README.md index dae41c1..4fbe7e8 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,8 @@ Parse the data provided, wrap the result in a new node, and return the root of t noscript: true, // keep text content when parsing style: true, // keep text content when parsing pre: true // keep text content when parsing - } + }, + closeAllOnClosing: false // Close all non-closed tags when containing element closes } ``` diff --git a/src/nodes/html.ts b/src/nodes/html.ts index e2e8ebb..5929931 100644 --- a/src/nodes/html.ts +++ b/src/nodes/html.ts @@ -1042,6 +1042,7 @@ export interface Options { */ closingSlash?: boolean; }; + closeAllByClosing?: boolean; } const frameflag = 'documentfragmentcontainer'; @@ -1227,6 +1228,22 @@ export function base_parse(data: string, options = {} as Partial) { } } } + if (options.closeAllByClosing === true) { + // If tag was opened, close all nested tags + let i; + for (i = stack.length - 2; i >= 0; i--) { + if (stack[i].rawTagName === tagName) break; + } + if (i >= 0) { + while (stack.length > i) { + // Update range end for closed tag + (<[number, number]>currentParent.range)[1] = createRange(-1, Math.max(lastTextPos, tagEndPos))[1]; + stack.pop(); + currentParent = arr_back(stack); + } + continue; + } + } // Use aggressive strategy to handle unmatching markups. break; } diff --git a/test/tests/unclosedtags.js b/test/tests/unclosedtags.js new file mode 100644 index 0000000..f4e86c2 --- /dev/null +++ b/test/tests/unclosedtags.js @@ -0,0 +1,16 @@ +const { parse, valid } = require('@test/test-target'); + +describe('Unclosed tags', function () { + it('Unclosed tags should be closed at end of parent element', function () { + const html = '

not boldbold

more

'; + valid(html, { closeAllByClosing: true }).should.be.true(); + const root = parse(html, { closeAllByClosing: true }); + root.outerHTML.should.equal('

not boldbold

more

'); + }); + it('Nested unclosed tags should be closed at end of parent element', function () { + const html = '

not boldboldbold italic

more

'; + valid(html, { closeAllByClosing: true }).should.be.true(); + const root = parse(html, { closeAllByClosing: true }); + root.outerHTML.should.equal('

not boldboldbold italic

more

'); + }); +});