mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 20:16:15 -08:00
fix(agents): decode full HTML entities and media assets
This commit is contained in:
1 parent
a73538994a
commit
0ac3421594
3 files changed
+54
-9
No files matched your search
@@ -58,7 +58,9 @@ Rendered HTML anchor hrefs and image sources use the same local-reference checks
|
||||
as Markdown links, including decoded attributes and fragment validation.
|
||||
Image references check file existence without interpreting image fragments as
|
||||
Markdown headings; document links keep anchor checks even when sharing a target.
|
||||
HTML video, audio and source `src` assets use the same existence checks as images.
|
||||
HTML video, audio, source and track `src` assets and video posters use the same
|
||||
existence checks as images. Entity decoding uses full HTML text/attribute rules,
|
||||
including references whose semicolon may be omitted.
|
||||
Inline guidance imports are rejected after punctuation as well as whitespace.
|
||||
Declared scoped dependencies, scope wildcards and matching TypeScript path aliases
|
||||
are recognized as package/alias mentions. Traversal and document-file imports are
|
||||
|
||||
@@ -45,11 +45,15 @@ function inlineText(tokens) {
|
||||
.join('');
|
||||
}
|
||||
|
||||
function decodeEntities(text) {
|
||||
return text.replace(
|
||||
/&(?:#(?:x[0-9a-f]+|[0-9]+)|[a-z][a-z0-9]+);/giu,
|
||||
(entity) => parseFragment(entity).childNodes[0]?.value ?? entity
|
||||
);
|
||||
function decodeEntities(text, attribute = false) {
|
||||
if (attribute) {
|
||||
const html = `<a href="${text.replace(/"/gu, '"')}"></a>`;
|
||||
return parseFragment(html).childNodes[0].attrs[0].value;
|
||||
}
|
||||
// RCDATA decodes the full HTML character-reference grammar without
|
||||
// interpreting literal tags. The prefix preserves an initial newline.
|
||||
const html = `<textarea>x${text.replace(/</gu, '<')}</textarea>`;
|
||||
return parseFragment(html).childNodes[0].childNodes[0].value.slice(1);
|
||||
}
|
||||
|
||||
function htmlNavigation(html, inspect = () => {}) {
|
||||
@@ -66,8 +70,11 @@ function htmlNavigation(html, inspect = () => {}) {
|
||||
anchors.push(attribute.value);
|
||||
if (
|
||||
(node.tagName === 'a' && attribute.name === 'href') ||
|
||||
(['img', 'video', 'audio', 'source'].includes(node.tagName) &&
|
||||
attribute.name === 'src')
|
||||
(['img', 'video', 'audio', 'source', 'track'].includes(
|
||||
node.tagName
|
||||
) &&
|
||||
attribute.name === 'src') ||
|
||||
(node.tagName === 'video' && attribute.name === 'poster')
|
||||
)
|
||||
references.push({
|
||||
target: attribute.value,
|
||||
@@ -216,7 +223,7 @@ export function guidanceReferences(markdown, includeLiterals) {
|
||||
markdownLexer.walkTokens(tokens, (token) => {
|
||||
if (token.type === 'def')
|
||||
mark(token, {
|
||||
target: decodeEntities(token.href),
|
||||
target: decodeEntities(token.href, true),
|
||||
definition: true,
|
||||
});
|
||||
else if (token.type === 'unresolved-reference')
|
||||
|
||||
@@ -955,3 +955,39 @@ for (const html of [
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
for (const heading of ['A© B', 'A© B', 'A© B']) {
|
||||
test(`semicolonless heading entities decode: ${heading}`, async (t) => {
|
||||
assert.deepEqual(
|
||||
await diagnostics(t, {
|
||||
'AGENTS.md': '[Heading](docs/example.md#a-b)',
|
||||
'docs/example.md': '# ' + heading,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
});
|
||||
}
|
||||
test('semicolonless definition entities match rendered destinations', async (t) => {
|
||||
assert.deepEqual(
|
||||
await diagnostics(t, {
|
||||
'AGENTS.md': '[ref]: docs/a©.md',
|
||||
'docs/a©.md': '',
|
||||
}),
|
||||
[]
|
||||
);
|
||||
});
|
||||
for (const html of [
|
||||
'<video poster="docs/asset.png"></video>',
|
||||
'<video><track src="docs/asset.png"></video>',
|
||||
]) {
|
||||
test(`remaining media assets are checked: ${html}`, async (t) => {
|
||||
assert.match(
|
||||
(await diagnostics(t, { 'AGENTS.md': html })).join('\n'),
|
||||
/does not exist/
|
||||
);
|
||||
assert.deepEqual(
|
||||
await diagnostics(t, { 'AGENTS.md': html, 'docs/asset.png': '' }),
|
||||
[]
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user