2
0
mirror of https://github.com/tenrok/BBob.git synced 2026-05-15 11:59:37 +03:00

fix: bug with lost closing tag (#186)

* fix: closing tag bug

* chore: add changeset thin-crabs-wonder.md

* chore: remove debug code

* chore: add more tests
This commit is contained in:
Nikolay Kost
2023-08-28 15:26:27 +03:00
committed by GitHub
parent 867c1b0710
commit 603c3ead0f
10 changed files with 185 additions and 694 deletions
+41 -17
View File
@@ -6,15 +6,15 @@ import { createList } from './utils';
/**
* @public
* @param {String} input
* @param {string} input
* @param {Object} opts
* @param {Function} opts.createTokenizer
* @param {Array<string>} opts.onlyAllowTags
* @param {Array<string>} opts.contextFreeTags
* @param {Boolean} opts.enableEscapeTags
* @param {String} opts.openTag
* @param {String} opts.closeTag
* @return {Array}
* @param {string} opts.openTag
* @param {string} opts.closeTag
* @return {Array<string|TagNode>}
*/
const parse = (input, opts = {}) => {
const options = opts;
@@ -50,12 +50,12 @@ const parse = (input, opts = {}) => {
/**
* Cache for nested tags checks
* @type Set<string>
*/
const nestedTagsMap = new Set();
/**
*
* @param token
* @param {Token} token
* @returns {boolean}
*/
const isTokenNested = (token) => {
@@ -71,14 +71,15 @@ const parse = (input, opts = {}) => {
};
/**
* @param tagName
* @private
* @param {string} tagName
* @returns {boolean}
*/
const isTagNested = (tagName) => Boolean(nestedTagsMap.has(tagName));
/**
* @private
* @param {String} value
* @param {string} value
* @return {boolean}
*/
const isAllowedTag = (value) => {
@@ -114,6 +115,29 @@ const parse = (input, opts = {}) => {
return nodes.toArray();
};
/**
* @private
* @param {string|TagNode} node
* @param {boolean} isNested
*/
const appendNodeAsString = (node, isNested = true) => {
const items = getNodes();
if (Array.isArray(items)) {
items.push(node.toTagStart({ openTag, closeTag }));
if (node.content.length) {
node.content.forEach((item) => {
items.push(item);
});
if (isNested) {
items.push(node.toTagEnd({ openTag, closeTag }));
}
}
}
};
/**
* @private
* @param {string|TagNode} node
@@ -126,15 +150,7 @@ const parse = (input, opts = {}) => {
if (isAllowedTag(node.tag)) {
items.push(node.toTagNode());
} else {
items.push(node.toTagStart({ openTag, closeTag }));
if (node.content.length) {
node.content.forEach((item) => {
items.push(item);
});
items.push(node.toTagEnd({ openTag, closeTag }));
}
appendNodeAsString(node);
}
} else {
items.push(node);
@@ -269,6 +285,14 @@ const parse = (input, opts = {}) => {
// eslint-disable-next-line no-unused-vars
const tokens = tokenizer.tokenize();
// handles situations where we open tag, but forgot close them
// for ex [q]test[/q][u]some[/u][q]some [u]some[/u] // forgot to close [/q]
// so we need to flush nested content to nodes array
const lastNestedNode = nestedNodes.flushLast();
if (lastNestedNode && isTagNested(lastNestedNode.tag)) {
appendNodeAsString(lastNestedNode, false);
}
return nodes.toArray();
};
+56
View File
@@ -361,6 +361,62 @@ describe('Parser', () => {
]);
});
test('parse with lost closing tag in middle', () => {
const str = `[quote]some[/quote][color=red]test[/color]
[quote]xxxsdfasdf
sdfasdfasdf
[url=xxx]xxx[/url]`
expectOutput(
parse(str),
[
{ tag: 'quote', attrs: {}, content: ['some'] },
{ tag: 'color', attrs: { red: 'red' }, content: ['test'] },
'\n',
'[quote]',
'xxxsdfasdf',
'\n',
'sdfasdfasdf',
'\n',
'\n',
{ tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'] }
]
)
})
test('parse with lost closing tag on start', () => {
const str = `[quote]xxxsdfasdf[quote]some[/quote][color=red]test[/color]sdfasdfasdf[url=xxx]xxx[/url]`
expectOutput(
parse(str),
[
'[quote]',
'xxxsdfasdf',
{ tag: 'quote', attrs: {}, content: ['some'] },
{ tag: 'color', attrs: { red: 'red' }, content: ['test'] },
'sdfasdfasdf',
{ tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'] }
]
)
})
test('parse with lost closing tag on end', () => {
const str = `[quote]some[/quote][color=red]test[/color]sdfasdfasdf[url=xxx]xxx[/url][quote]xxxsdfasdf`
expectOutput(
parse(str),
[
{ tag: 'quote', attrs: {}, content: ['some'] },
{ tag: 'color', attrs: { red: 'red' }, content: ['test'] },
'sdfasdfasdf',
{ tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'] },
'[quote]',
'xxxsdfasdf',
]
)
})
describe('html', () => {
const parseHTML = input => parse(input, { openTag: '<', closeTag: '>' });