fucking around, pro

This commit is contained in:
2025-03-28 08:31:35 +01:00
parent c31962fc63
commit 060ef763d3
16 changed files with 2176 additions and 13 deletions
+114
View File
@@ -0,0 +1,114 @@
import { describe, it, expect } from 'vitest';
import { filterMarkdownLinks } from './markdown.js';
describe('filterMarkdownLinks', () => {
it('should filter out exact URLs', () => {
const markdown = 'Check out [link1](https://example.com) and [link2](https://other.com)';
const filters = [{ pattern: 'https://example.com' }];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('Check out link1 and [link2](https://other.com)');
});
it('should replace URLs with custom text', () => {
const markdown = 'Check out [link1](https://example.com) and [link2](https://other.com)';
const filters = [{ pattern: 'https://example.com', replacement: 'REPLACED' }];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('Check out REPLACED and [link2](https://other.com)');
});
it('should filter out URLs matching regex patterns', () => {
const markdown = 'Visit [spam](https://spam.com) and [ads](https://advertisement.com)';
const filters = [
{ pattern: /spam\.com/ },
{ pattern: /advertisement/ }
];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('Visit spam and ads');
});
it('should replace URLs matching regex patterns with custom text', () => {
const markdown = 'Visit [spam](https://spam.com) and [ads](https://advertisement.com)';
const filters = [
{ pattern: /spam\.com/, replacement: 'SPAM' },
{ pattern: /advertisement/, replacement: 'ADS' }
];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('Visit SPAM and ADS');
});
it('should handle nested markdown elements', () => {
const markdown = '**Bold text with [link](https://example.com) inside**';
const filters = [{ pattern: 'https://example.com' }];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('**Bold text with link inside**');
});
it('should handle nested markdown elements with replacement', () => {
const markdown = '**Bold text with [link](https://example.com) inside**';
const filters = [{ pattern: 'https://example.com', replacement: 'REPLACED' }];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('**Bold text with REPLACED inside**');
});
it('should preserve non-matching links', () => {
const markdown = '[keep](https://keep.com) and [remove](https://remove.com)';
const filters = [{ pattern: 'https://remove.com' }];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('[keep](https://keep.com) and remove');
});
it('should handle empty filters array', () => {
const markdown = '[link](https://example.com)';
const filters: { pattern: string | RegExp; replacement?: string }[] = [];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('[link](https://example.com)');
});
it('should handle complex markdown with multiple links', () => {
const markdown = '# Title\n\nSome text with [link1](https://example.com) and [link2](https://spam.com).\n\n## Subtitle\n\nMore text with [link3](https://advertisement.com) and [link4](https://good.com)';
const filters = [
{ pattern: 'https://example.com' },
{ pattern: /spam\.com/ },
{ pattern: /advertisement/ }
];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('# Title\n\nSome text with link1 and link2.\n\n## Subtitle\n\nMore text with link3 and [link4](https://good.com)');
});
it('should handle complex markdown with multiple replacements', () => {
const markdown = '# Title\n\nSome text with [link1](https://example.com) and [link2](https://spam.com).\n\n## Subtitle\n\nMore text with [link3](https://advertisement.com) and [link4](https://good.com)';
const filters = [
{ pattern: 'https://example.com', replacement: 'REPLACED1' },
{ pattern: /spam\.com/, replacement: 'REPLACED2' },
{ pattern: /advertisement/, replacement: 'REPLACED3' }
];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('# Title\n\nSome text with REPLACED1 and REPLACED2.\n\n## Subtitle\n\nMore text with REPLACED3 and [link4](https://good.com)');
});
it('should handle links with special characters', () => {
const markdown = '[special](https://example.com/path?param=value#fragment)';
const filters = [{ pattern: 'https://example.com' }];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('special');
});
it('should handle links with special characters and replacement', () => {
const markdown = '[special](https://example.com/path?param=value#fragment)';
const filters = [{ pattern: 'https://example.com', replacement: 'REPLACED' }];
const result = filterMarkdownLinks(markdown, filters);
expect(result).toBe('REPLACED');
});
});
+78
View File
@@ -0,0 +1,78 @@
import { fromMarkdown } from 'mdast-util-from-markdown';
import { toMarkdown } from 'mdast-util-to-markdown';
import { Root } from 'mdast';
type LinkFilter = {
pattern: string | RegExp;
replacement?: string;
};
/**
* Filters out or replaces specified links from markdown content
* @param markdown - The markdown string to filter
* @param filters - Array of link filters with optional replacement text
* @returns Filtered markdown string
*/
export function filterMarkdownLinks(markdown: string, filters: LinkFilter[]): string {
// Parse markdown to AST
const tree = fromMarkdown(markdown) as Root;
// Function to check if a URL should be filtered and get replacement text
const shouldFilter = (url: string): string | null => {
for (const filter of filters) {
if (filter.pattern instanceof RegExp) {
if (filter.pattern.test(url)) {
return filter.replacement || '';
}
} else if (url.includes(filter.pattern)) {
return filter.replacement || '';
}
}
return null;
};
// Visit all nodes and remove filtered links
const visit = (node: any) => {
if (node.type === 'link' && node.url) {
const replacement = shouldFilter(node.url);
if (replacement !== null) {
// Replace link with replacement text or its text content
node.type = 'text';
node.value = replacement || node.children?.[0]?.value || '';
delete node.url;
delete node.children;
}
}
if (node.children) {
node.children = node.children.map((child: any) => visit(child));
}
return node;
};
// Process the tree
const filteredTree = visit(tree);
// Convert back to markdown with options to minimize extra newlines
return toMarkdown(filteredTree, {
bullet: '-',
listItemIndent: 'one',
tightDefinitions: true,
handlers: {
text: (node, _, context) => {
const value = node.value.replace(/\n+$/, '');
return value;
}
}
}).trim();
}
/**
* Example usage:
* const filtered = filterMarkdownLinks(markdown, [
* { pattern: 'https://example.com', replacement: 'REPLACED' },
* { pattern: /spam\.com/, replacement: 'SPAM' },
* { pattern: /advertisement/ }
* ]);
*/
+5 -5
View File
@@ -176,7 +176,7 @@ const to_github = async (item: IHowto) => {
'',
`# ${item.title}`,
'',
item.cover_image ? `![${item.title}](${item.cover_image.name})` : '',
item.cover_image ? `![${item.title}](${sanitizeFilename(item.cover_image.name)})` : '',
'',
item.description,
item.user?.geo ? `\nUser Location: ${item.user.geo.city ? `${item.user.geo.city}, ` : ''}${item.user.geo.countryName || ''}` : '',
@@ -189,7 +189,7 @@ const to_github = async (item: IHowto) => {
step.text,
'',
// Add step images if any
...step.images.map(img => `\n![${img.name}](./${img.name})\n`)
...step.images.map(img => `\n![${img.name}](./${sanitizeFilename(img.name)})\n`)
].join('\n')),
'',
'## Resources',
@@ -222,7 +222,7 @@ const to_mdx = async (item: IHowto) => {
'',
`# ${item.title}`,
'',
item.cover_image ? `<Image src={import('./${item.cover_image.name}')} alt="${item.title}" />` : '',
item.cover_image ? `<Image src={import('./${sanitizeFilename(item.cover_image.name)}')} alt="${item.title}" />` : '',
'',
item.description,
item.user?.geo ? `\nUser Location: ${item.user.geo.city ? `${item.user.geo.city}, ` : ''}${item.user.geo.countryName || ''}` : '',
@@ -235,7 +235,7 @@ const to_mdx = async (item: IHowto) => {
step.text,
'',
// Add step images if any using Astro's Image component
...step.images.map(img => `\n<Image src={import('./${img.name}')} alt="${img.name}" />\n`)
...step.images.map(img => `\n<Image src={import('./${sanitizeFilename(img.name)}')} alt="${img.name}" />\n`)
].join('\n'))
].filter(Boolean).join('\n')
write(path.join(itemDir, 'index.mdx'), mdxContent)
@@ -337,7 +337,7 @@ const complete = async (item: IHowto) => {
}
await to_github(item)
await to_mdx(item)
await to_astro(item)
// await to_astro(item)
return item
}