mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fix(agents): validate visible headings and spaced paths
This commit is contained in:
1 parent
b0cf310074
commit
b54dc4c405
3 files changed
+66
-10
No files matched your search
@@ -37,6 +37,7 @@ Write generic filenames as prose; commands, templates, globs, URLs, package
|
||||
aliases and dotted code symbols are excluded. Bare dotted names with conventional
|
||||
file suffixes (such as .md, .json or .ts) are treated as filenames. Use a `./`
|
||||
prefix or Markdown link for other ambiguous filenames that resemble code symbols.
|
||||
Explicit relative literal paths may contain spaces; command-option snippets are excluded.
|
||||
Multi-part dotfiles are path candidates too.
|
||||
Conventional extensionless filenames such as Dockerfile, Makefile and LICENSE
|
||||
are also path candidates; use an explicit `./` prefix for other extensionless files.
|
||||
@@ -49,8 +50,9 @@ The parsed HTML tree also verifies that this paragraph is outside HTML container
|
||||
including templates split across Markdown tokens. Generated HTML is inspected
|
||||
in memory only; it is never executed or emitted.
|
||||
Heading anchors decode HTML character references in text and use `github-slugger`
|
||||
for GitHub-compatible character filtering and duplicate suffixes. Explicit HTML anchors
|
||||
use `parse5`, excluding comments, scripts, styles and template contents.
|
||||
for GitHub-compatible character filtering and duplicate suffixes.
|
||||
Only headings present outside inert HTML containers contribute slugs or duplicate counters.
|
||||
Explicit HTML anchors use `parse5`, excluding comments, scripts, styles and template contents.
|
||||
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
|
||||
|
||||
@@ -148,21 +148,37 @@ export function guidanceStandaloneImports(markdown) {
|
||||
export function guidanceAnchors(markdown) {
|
||||
const slugger = new GithubSlugger();
|
||||
const found = new Set();
|
||||
const html = [];
|
||||
markdownLexer.walkTokens(markdownLexer.lexer(markdown), (token) => {
|
||||
if (token.type === 'heading') {
|
||||
found.add(slugger.slug(inlineText(token.tokens)));
|
||||
}
|
||||
if (token.type === 'html') html.push(token.raw);
|
||||
const headings = [];
|
||||
const marker = `data-guidance-${randomUUID()}`;
|
||||
const renderer = new Marked({
|
||||
renderer: {
|
||||
heading(token) {
|
||||
const index = headings.push(inlineText(token.tokens)) - 1;
|
||||
return `<h${token.depth} ${marker}="${index}">${this.parser.parseInline(token.tokens)}</h${token.depth}>\n`;
|
||||
},
|
||||
},
|
||||
});
|
||||
return new Set([...found, ...htmlNavigation(html.join('\n')).anchors]);
|
||||
const navigation = htmlNavigation(renderer.parse(markdown), (node) => {
|
||||
const attribute = node.attrs?.find((attr) => attr.name === marker);
|
||||
if (attribute)
|
||||
found.add(slugger.slug(headings[Number(attribute.value)]));
|
||||
});
|
||||
return new Set([...found, ...navigation.anchors]);
|
||||
}
|
||||
|
||||
function isLiteralRepositoryPath(token) {
|
||||
// A typo in the directory or a new root filename must still be checked.
|
||||
// Exclude recognizable prose/code forms instead of allowlisting paths.
|
||||
if (/^(?:@|--|[a-z][a-z\d+.-]*:|\/\/)/iu.test(token)) return false;
|
||||
if (/[^\p{L}\p{N}_./#-]/u.test(token)) return false;
|
||||
const explicitRelative = /^(?:\.\/|\.\.\/)/u.test(token);
|
||||
if (
|
||||
(explicitRelative
|
||||
? /[^\p{L}\p{N}_./# -]/u
|
||||
: /[^\p{L}\p{N}_./#-]/u
|
||||
).test(token)
|
||||
)
|
||||
return false;
|
||||
if (/\s+-{1,2}[\p{L}]/u.test(token)) return false;
|
||||
if (token.includes('YYYY-MM-DD') || /(?:^|\/)\.\.\.(?:\/|$)/u.test(token))
|
||||
return false;
|
||||
const path = token.split('#')[0];
|
||||
|
||||
@@ -883,3 +883,41 @@ for (const content of [
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
test('inert headings do not define or consume anchor slugs', async (t) => {
|
||||
const body =
|
||||
'<template>\n\n# Hidden\n\n# Visible\n\n</template>\n\n# Visible';
|
||||
assert.match(
|
||||
(
|
||||
await diagnostics(t, {
|
||||
'AGENTS.md': '[Hidden](docs/example.md#hidden)',
|
||||
'docs/example.md': body,
|
||||
})
|
||||
).join('\n'),
|
||||
/missing anchor/
|
||||
);
|
||||
assert.deepEqual(
|
||||
await diagnostics(t, {
|
||||
'AGENTS.md': '[Visible](docs/example.md#visible)',
|
||||
'docs/example.md': body,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
});
|
||||
test('explicit relative literal paths support spaces', async (t) => {
|
||||
assert.match(
|
||||
(
|
||||
await diagnostics(t, {
|
||||
'AGENTS.md': '`./docs/My Guide.md`',
|
||||
})
|
||||
).join('\n'),
|
||||
/does not exist/
|
||||
);
|
||||
assert.deepEqual(
|
||||
await diagnostics(t, {
|
||||
'AGENTS.md': '`./docs/My Guide.md`',
|
||||
'docs/My Guide.md': '',
|
||||
}),
|
||||
[]
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user