Skip to content

Commit

Permalink
Add tests and optimize unescape (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
georgekaran authored Dec 17, 2023
1 parent 51a1129 commit 79e2b4c
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 34 deletions.
5 changes: 5 additions & 0 deletions src/parsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ export function parse(markdown: string): MarkdownNode {
}

export function unescape(input: string): string {
if (!input.includes('\\')) {
// Optimization for cases where there are no escape sequences
return input;
}

let text = '';

for (let index = 0; index < input.length; index++) {
Expand Down
11 changes: 10 additions & 1 deletion src/rendering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ type VisitedMarkdownNodeMap<C> = {
};

export type VisitedMarkdownNode<C, T extends MarkdownNodeType> = {
[K in MarkdownNodeType]: {type: K} & VisitedMarkdownNodeMap<C>[K]
[K in MarkdownNodeType]: {
type: K,
source: string,
} & VisitedMarkdownNodeMap<C>[K]
}[T];

export interface MarkdownRenderer<T> {
Expand Down Expand Up @@ -62,18 +65,21 @@ function visit<T>(node: MarkdownNode, visitor: MarkdownRenderer<T>): T {
return visitor.bold({
type: node.type,
children: visit(node.children, visitor),
source: node.source,
});

case 'italic':
return visitor.italic({
type: node.type,
children: visit(node.children, visitor),
source: node.source,
});

case 'strike':
return visitor.strike({
type: node.type,
children: visit(node.children, visitor),
source: node.source,
});

case 'code':
Expand All @@ -87,18 +93,21 @@ function visit<T>(node: MarkdownNode, visitor: MarkdownRenderer<T>): T {
type: node.type,
href: node.href,
children: visit(node.children, visitor),
source: node.source,
});

case 'paragraph':
return visitor.paragraph({
type: node.type,
children: node.children.map(child => visit(child, visitor)),
source: node.source,
});

case 'fragment':
return visitor.fragment({
type: node.type,
children: node.children.map(child => visit(child, visitor)),
source: node.source,
});
}
}
4 changes: 4 additions & 0 deletions test/parsing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,10 @@ describe('A Markdown parser function', () => {
'ABC\\D',
'ABCD',
],
[
'Not escaped',
'Not escaped',
],
])('should unescape %s to %s', (input, output) => {
expect(unescape(input)).toEqual(output);
});
Expand Down
94 changes: 61 additions & 33 deletions test/rendering.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,79 @@ import {MarkdownNode} from '../src/ast';
import {MarkdownRenderer, render, VisitedMarkdownNode} from '../src';

describe('A Markdown render function', () => {
class HtmlRenderer implements MarkdownRenderer<string> {
public fragment(node: VisitedMarkdownNode<string, 'fragment'>): string {
return node.children.join('');
class TestRenderer implements MarkdownRenderer<MarkdownNode> {
public fragment(node: VisitedMarkdownNode<MarkdownNode, 'fragment'>): MarkdownNode {
return {
type: 'fragment',
source: node.source,
children: node.children,
};
}

public text(node: VisitedMarkdownNode<string, 'text'>): string {
return node.content;
public text(node: VisitedMarkdownNode<MarkdownNode, 'text'>): MarkdownNode {
return {
type: 'text',
source: node.source,
content: node.content,
};
}

public bold(node: VisitedMarkdownNode<string, 'bold'>): string {
return `<b>${node.children}</b>`;
public bold(node: VisitedMarkdownNode<MarkdownNode, 'bold'>): MarkdownNode {
return {
type: 'bold',
source: node.source,
children: node.children,
};
}

public italic(node: VisitedMarkdownNode<string, 'italic'>): string {
return `<i>${node.children}</i>`;
public italic(node: VisitedMarkdownNode<MarkdownNode, 'italic'>): MarkdownNode {
return {
type: 'italic',
source: node.source,
children: node.children,
};
}

public strike(node: VisitedMarkdownNode<string, 'strike'>): string {
return `<s>${node.children}</s>`;
public strike(node: VisitedMarkdownNode<MarkdownNode, 'strike'>): MarkdownNode {
return {
type: 'strike',
source: node.source,
children: node.children,
};
}

public code(node: VisitedMarkdownNode<string, 'code'>): string {
return `<code>${node.content}</code>`;
public code(node: VisitedMarkdownNode<MarkdownNode, 'code'>): MarkdownNode {
return {
type: 'code',
source: node.source,
content: node.content,
};
}

public image(node: VisitedMarkdownNode<string, 'image'>): string {
return `<img src="${node.src}" alt="${node.alt}">`;
public image(node: VisitedMarkdownNode<MarkdownNode, 'image'>): MarkdownNode {
return {
type: 'image',
source: node.source,
alt: node.alt,
src: node.src,
};
}

public link(node: VisitedMarkdownNode<string, 'link'>): string {
return `<a href="${node.href}">${node.children}</a>`;
public link(node: VisitedMarkdownNode<MarkdownNode, 'link'>): MarkdownNode {
return {
type: 'link',
source: node.source,
href: node.href,
children: node.children,
};
}

public paragraph(node: VisitedMarkdownNode<string, 'paragraph'>): string {
return `<p>${node.children.join('')}</p>`;
public paragraph(node: VisitedMarkdownNode<MarkdownNode, 'paragraph'>): MarkdownNode {
return {
type: 'paragraph',
source: node.source,
children: node.children,
};
}
}

Expand Down Expand Up @@ -147,11 +185,11 @@ describe('A Markdown render function', () => {
children: [
{
type: 'link',
source: '![Link](https://example.com)',
source: '[Link](https://example.com)',
href: 'https://example.com',
children: {
type: 'text',
source: 'https://example.com',
source: 'Link',
content: 'Link',
},
},
Expand All @@ -160,21 +198,11 @@ describe('A Markdown render function', () => {
],
};

const html = [
'<p><b>Bold</b></p>',
'<p><i>Italic</i></p>',
'<p><b><i>Bold and italic</i></b></p>',
'<p><s>Strike</s></p>',
'<p><code>Code</code></p>',
'<p><img src="https://example.com/image.png" alt="Image"></p>',
'<p><a href="https://example.com">Link</a></p>',
].join('');

it('should render a Markdown tree', () => {
expect(render(tree, new HtmlRenderer())).toBe(html);
expect(render(tree, new TestRenderer())).toEqual(tree);
});

it('should parse and render a Markdown string', () => {
expect(render(markdown, new HtmlRenderer())).toBe(html);
expect(render(markdown, new TestRenderer())).toEqual(tree);
});
});

0 comments on commit 79e2b4c

Please sign in to comment.