diff --git a/src/nodes/html.ts b/src/nodes/html.ts
index 78f283e..9c6d312 100644
--- a/src/nodes/html.ts
+++ b/src/nodes/html.ts
@@ -1319,7 +1319,9 @@ export function parse(data: string, options = {} as Partial) {
// this is wrong, becouse this will put the H3 outside the current right position which should be inside the current Html Element, see issue 152 for more info
if (options.parseNoneClosedTags !== true) {
oneBefore.removeChild(last);
- reparentChildren(last, oneBefore.parentNode);
+ last.childNodes.forEach((child) => {
+ oneBefore.parentNode.appendChild(child);
+ });
stack.pop();
}
} else {
@@ -1328,7 +1330,9 @@ export function parse(data: string, options = {} as Partial) {
// eslint-disable-next-line no-lonely-if
if (options.parseNoneClosedTags !== true) {
oneBefore.removeChild(last);
- reparentChildren(last, oneBefore);
+ last.childNodes.forEach((child) => {
+ oneBefore.appendChild(child);
+ });
}
}
} else {
@@ -1343,17 +1347,6 @@ export function parse(data: string, options = {} as Partial) {
return root;
}
-function reparentChildren(source: HTMLElement, parent: HTMLElement) {
- const children = source.childNodes;
- source.childNodes = [];
- for (const child of children) {
- // Detach in bulk: appendChild() would otherwise filter the shrinking source
- // array once per child, making repair quadratic in the number of siblings.
- child.parentNode = null;
- parent.appendChild(child);
- }
-}
-
/**
* Resolves a list of {@link NodeInsertable} to a list of nodes,
* and removes nodes from any potential parent.
diff --git a/test/tests/issues/260-unclosed-tags.js b/test/tests/issues/260-unclosed-tags.js
deleted file mode 100644
index 1588502..0000000
--- a/test/tests/issues/260-unclosed-tags.js
+++ /dev/null
@@ -1,78 +0,0 @@
-const assert = require('node:assert/strict');
-const { spawnSync } = require('node:child_process');
-const { parse } = require('@test/test-target');
-
-function checkParents(parent) {
- parent.childNodes.forEach((child) => {
- assert.equal(child.parentNode, parent);
- checkParents(child);
- });
-}
-
-describe('unclosed tag repair', function () {
- const content = 'alphabetaomega';
- for (const sameTag of [false, true]) {
- const opening = sameTag ? '' : '';
- const input = `before${opening}${content}`;
- const repaired = `before${sameTag ? '' : ''}${content}`;
-
- for (const preserve of [undefined, false, true]) {
- it(`preserves child order and parent links (${sameTag ? 'same' : 'different'} tags, parseNoneClosedTags=${preserve})`, function () {
- const root = parse(input, { comment: true, parseNoneClosedTags: preserve });
- const expected = preserve ? `${input}${sameTag ? '' : ''}` : repaired;
- assert.equal(root.toString(), expected);
- checkParents(root);
- const bold = root.querySelector('b');
- assert.equal(bold.previousSibling.toString(), '');
- assert.equal(bold.nextSibling.toString(), 'omega');
- });
- }
-
- it(`handles empty unclosed ${sameTag ? 'same' : 'different'} tags`, function () {
- const root = parse(`${opening}`);
- assert.equal(root.toString(), `${sameTag ? '' : ''}`);
- checkParents(root);
- });
-
- it(`repairs many children of unclosed ${sameTag ? 'same' : 'different'} tags within a bounded process`, function () {
- this.timeout(15000);
- const args = [];
- if (process.env.TEST_TARGET !== 'dist' && !process.env.CI) {
- args.push('-r', require.resolve('ts-node/register'));
- }
- args.push('-e', `
- const assert = require('node:assert/strict');
- const { parse } = require(process.argv[1]);
- const sameTag = process.argv[2] === 'true';
- const count = 32000;
- const content = 'item'.repeat(count);
- const opening = sameTag ? '' : '';
- const root = parse('' + opening + content);
- const parent = root.firstChild;
- const items = sameTag ? parent.childNodes.slice(1) : parent.childNodes;
- assert.equal(items.length, count);
- for (const item of items) {
- assert.equal(item.rawTagName, 'b');
- assert.equal(item.text, 'item');
- assert.equal(item.parentNode, parent);
- assert.equal(item.firstChild.parentNode, item);
- }
- assert.equal(root.toString(), '' + (sameTag ? '' : '') + content + '');
- process.stdout.write('ok');
- `, require.resolve('@test/test-target'), String(sameTag));
- // Isolate synchronous parsing so a regression cannot hang the test runner.
- // The linear path has ample headroom; repeated sibling filtering times out.
- const result = spawnSync(process.execPath, args, { timeout: 8000, encoding: 'utf8', windowsHide: true });
- assert.ifError(result.error);
- assert.equal(result.status, 0, result.stderr);
- assert.equal(result.stdout, 'ok');
- });
- }
-
- it('keeps properly closed parents and their mixed children', function () {
- const html = `beforeafter`;
- const root = parse(html, { comment: true });
- assert.equal(root.toString(), html);
- checkParents(root);
- });
-});