Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [zenn-markdown-html] VS Code 拡張機能でのスクロール同期のためのソースマップ追加 #504

Merged
merged 3 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/zenn-cli/src/server/__tests__/preview/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('/api/articles/:slug', () => {
type: 'tech',
topics: [],
published: true,
bodyHtml: expect.stringContaining('<p>Hello!</p>'),
bodyHtml: expect.stringMatching(/<p.*>Hello!<\/p>/),
})
);
});
Expand Down Expand Up @@ -216,7 +216,7 @@ describe('/api/books/:book_slug/chapters/:chapter_filename', () => {
title: 'title2',
free: true,
position: 0,
bodyHtml: expect.stringContaining('<p>Hello!</p>'),
bodyHtml: expect.stringMatching(/<p.*>Hello!<\/p>/),
})
);
});
Expand Down
17 changes: 13 additions & 4 deletions packages/zenn-markdown-html/__tests__/basic.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { describe, test, expect } from 'vitest';
import markdownToHtml from '../src/index';
import { parse } from 'node-html-parser';

describe('MarkdownからHTMLへの変換テスト', () => {
test('markdownからhtmlへ変換する', () => {
const html = markdownToHtml('Hello\n## hey\n\n- first\n- second\n');
expect(html).toContain(`<p>Hello</p>`);
expect(html).toContain(
`<h2 id="hey"><a class="header-anchor-link" href="#hey" aria-hidden="true"></a> hey</h2>`
const p = parse(html).querySelector('p');
const h2 = parse(html).querySelector('h2');
const ul = parse(html).querySelector('ul');
const liElms = parse(html).querySelectorAll('li');

expect(p?.innerHTML).toBe('Hello');
expect(h2?.innerHTML).toBe(
'<a class="header-anchor-link" href="#hey" aria-hidden="true"></a> hey'
);
expect(html).toContain(`<ul>\n<li>first</li>\n<li>second</li>\n</ul>\n`);
expect(ul).not.toBeNull();
expect(liElms?.length).toBe(2);
expect(liElms[0].innerHTML).toBe('first');
expect(liElms[1].innerHTML).toBe('second');
});

test('インラインコメントはhtmlに変換しない', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/zenn-markdown-html/__tests__/br.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('<br /> のテスト', () => {
const patterns = ['foo<br>bar', 'foo<br/>bar', 'foo<br />bar'];
patterns.forEach((pattern) => {
const html = markdownToHtml(pattern);
expect(html).toMatch(/<p>foo<br \/>bar<\/p>/);
expect(html).toContain('foo<br />bar');
});
});
test('テーブル内の<br />は保持する', () => {
Expand All @@ -20,7 +20,7 @@ describe('<br /> のテスト', () => {
});
test('インラインコード内の<br />はエスケープする', () => {
const html = markdownToHtml('foo`<br>`bar');
expect(html).toMatch(/<p>foo<code>&lt;br&gt;<\/code>bar<\/p>/);
expect(html).toContain('foo<code>&lt;br&gt;</code>bar');
});
test('コードブロック内の<br />はエスケープする', () => {
const html = markdownToHtml('```\n<br>\n```');
Expand Down
36 changes: 18 additions & 18 deletions packages/zenn-markdown-html/__tests__/dollar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ import markdownToHtml from '../src/index';
describe('$ マークのテスト', () => {
test('リンクと同じ行にある $ は katex に変換される', () => {
const html = markdownToHtml('$a,b,c$foo[foo](https://foo.bar)bar');
expect(html).toEqual(
'<p><embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="https://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>bar</p>\n'
expect(html).toContain(
'<embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="https://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>bar'
);
});

test('リンクの後に無効な $ が続く場合はそのままにする', () => {
const html = markdownToHtml('$a,b,c$foo[foo](http://foo.bar)$bar');
expect(html).toEqual(
'<p><embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="http://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>$bar</p>\n'
expect(html).toContain(
'<embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="http://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>$bar'
);
});

test('リンク名に $ が含まれる場合はそのままにする', () => {
const html = markdownToHtml('$a,b,c$foo[$bar](http://foo.bar)bar');
expect(html).toEqual(
'<p><embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="http://foo.bar" target="_blank" rel="nofollow noopener noreferrer">$bar</a>bar</p>\n'
expect(html).toContain(
'<embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="http://foo.bar" target="_blank" rel="nofollow noopener noreferrer">$bar</a>bar'
);
});
test('リンクのhrefに $ が含まれる場合はそのままにする', () => {
Expand All @@ -35,45 +35,45 @@ describe('$ マークのテスト', () => {
describe('HTMLタグにエスケープするテスト', () => {
test('katex内の<sscript />をエスケープする', () => {
const html = markdownToHtml('$a,<script>alert("XSS")</script>,c$');
expect(html).toEqual(
`<p><embed-katex><eq class="zenn-katex">a,&lt;script&gt;alert("XSS")&lt;/script&gt;,c</eq></embed-katex></p>\n`
expect(html).toContain(
`<embed-katex><eq class="zenn-katex">a,&lt;script&gt;alert("XSS")&lt;/script&gt;,c</eq></embed-katex>`
);
});
});

describe('$ のペアのテスト', () => {
test('リンクの前後にある一文字だけを含む$のペアをkatexに変換する', () => {
const html = markdownToHtml('$a$foo[foo](https://foo.bar)bar,refs:$(2)$');
expect(html).toEqual(
'<p><embed-katex><eq class="zenn-katex">a</eq></embed-katex>foo<a href="https://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>bar,refs:<embed-katex><eq class="zenn-katex">(2)</eq></embed-katex></p>\n'
expect(html).toContain(
'<embed-katex><eq class="zenn-katex">a</eq></embed-katex>foo<a href="https://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>bar,refs:<embed-katex><eq class="zenn-katex">(2)</eq></embed-katex>'
);
});
test('リンク前後にある$のペアをkatexに変換する', () => {
const html = markdownToHtml(
'$a,b,c$foo[foo](https://foo.bar)bar,refs:$(2)$'
);
expect(html).toEqual(
'<p><embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="https://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>bar,refs:<embed-katex><eq class="zenn-katex">(2)</eq></embed-katex></p>\n'
expect(html).toContain(
'<embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="https://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>bar,refs:<embed-katex><eq class="zenn-katex">(2)</eq></embed-katex>'
);
});
test('リンク前後にある三つの$のペアをkatexに変換する', () => {
const html = markdownToHtml(
'$a,b,c$foo[foo](https://foo.bar)bar,refs:$(2)$,and:$(3)$'
);
expect(html).toEqual(
'<p><embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="https://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>bar,refs:<embed-katex><eq class="zenn-katex">(2)</eq></embed-katex>,and:<embed-katex><eq class="zenn-katex">(3)</eq></embed-katex></p>\n'
expect(html).toContain(
'<embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="https://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>bar,refs:<embed-katex><eq class="zenn-katex">(2)</eq></embed-katex>,and:<embed-katex><eq class="zenn-katex">(3)</eq></embed-katex>'
);
});
test('リンク周りにある$のペアをkatexに変換する', () => {
const html = markdownToHtml('$a,b,c$foo[foo](https://foo.bar)bar,refs:$2$');
expect(html).toEqual(
'<p><embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="https://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>bar,refs:<embed-katex><eq class="zenn-katex">2</eq></embed-katex></p>\n'
expect(html).toContain(
'<embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foo<a href="https://foo.bar" target="_blank" rel="nofollow noopener noreferrer">foo</a>bar,refs:<embed-katex><eq class="zenn-katex">2</eq></embed-katex>'
);
});
test('二つの$のペアをkatexに変換する', () => {
const html = markdownToHtml('$a,b,c$foobar,refs:$(2)$');
expect(html).toEqual(
'<p><embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foobar,refs:<embed-katex><eq class="zenn-katex">(2)</eq></embed-katex></p>\n'
expect(html).toContain(
'<embed-katex><eq class="zenn-katex">a,b,c</eq></embed-katex>foobar,refs:<embed-katex><eq class="zenn-katex">(2)</eq></embed-katex>'
);
});
});
6 changes: 5 additions & 1 deletion packages/zenn-markdown-html/__tests__/highlight.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import loadLanguages from 'prismjs/components/index';

import { describe, test, expect } from 'vitest';
import markdownToHtml from '../src/index';
import parse from 'node-html-parser';

// markdownToHtml で diff を使っているので、あらかじめ読み込んでおく
loadLanguages('diff');
Expand All @@ -11,7 +12,10 @@ describe('コードハイライトのテスト', () => {
const html = markdownToHtml(
`\`\`\`js:foo.js\nconsole.log("hello")\n\`\`\``
);
expect(html).toContain('<code class="language-js">');
// <code />が取得できないので<pre />で取得する
const pre: any = parse(html).querySelector('pre');
const code = parse(pre?.innerHTML).querySelector('code.language-js');
expect(code).toBeTruthy();
expect(html).toContain('<span class="code-block-filename">foo.js</span>');
});

Expand Down
48 changes: 24 additions & 24 deletions packages/zenn-markdown-html/__tests__/link.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,73 +51,73 @@ describe('Linkifyのテスト', () => {

test('URLの前にテキストが存在する場合はリンクをリンクカードに変換しない', () => {
const html = renderLink('foo https://example.com');
expect(html).toEqual(
'<p>foo <a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a></p>\n'
expect(html).toContain(
'foo <a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a>'
);
});

test('意図的にリンクしているURLはリンクカードに変換しない', () => {
const html = renderLink('[https://example.com](https://example.com)');
expect(html).toEqual(
'<p><a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a></p>\n'
);
});

test('リンク内のリンクを変換しない', () => {
const html = renderLink('- https://example.com\n- second');
expect(html).toEqual(
'<ul>\n<li><a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a></li>\n<li>second</li>\n</ul>\n'
Comment on lines -66 to -69

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[memo] テストケースがL89と重複しているため削除。

expect(html).toContain(
'<a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a>'
);
});

test('<details />内のリンクはリンクカードに変換しない', () => {
const html = renderLink(':::message alert\nhttps://example.com\n:::');
expect(html).toEqual(
'<aside class="msg alert"><span class="msg-symbol">!</span><div class="msg-content"><p><a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a></p>\n</div></aside>\n'
const iframe = parse(html).querySelector('aside iframe');
expect(iframe).toBeNull();
expect(html).toContain(
'<a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a>'
);
});

test('<details />内の2段落空いたリンクをリンクカードに変換しない', () => {
const html = renderLink(
':::message alert\nhello\n\nhttps://example.com\n:::'
);
const iframes = parse(html).querySelectorAll('aside iframe');
expect(iframes.length).toBe(0);
console.log(html);
expect(html).toContain(
'<aside class="msg alert"><span class="msg-symbol">!</span><div class="msg-content"><p>hello</p>\n<p><a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a></p>\n</div></aside>'
'<a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a>'
);
});

test('リスト内のリンクをリンクカードに変換しない', () => {
const html = renderLink('- https://example.com\n- second');
expect(html).toEqual(
'<ul>\n<li><a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a></li>\n<li>second</li>\n</ul>\n'
const iframe = parse(html).querySelector('aside iframe');
expect(iframe).toBeNull();
expect(html).toContain(
'<a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a>'
);
});

test('URLにテキストが続く場合はリンクカードに変換しない', () => {
const html = renderLink('https://example.com foo');
expect(html).toEqual(
'<p><a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a> foo</p>\n'
expect(html).toContain(
'<a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a> foo'
);
});

test('同じ段落内のテキストを含むリンクをリンクカードに変換しない', () => {
const html = renderLink(`a: https://example.com\nb: https://example.com`);
expect(html).toEqual(
'<p>a: <a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a><br />\nb: <a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a></p>\n'
expect(html).toContain(
'a: <a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a><br />\nb: <a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a>'
);
});

test('URLにテキストが続くならリンクが先頭であってもリンクカードに変換しない', () => {
const html = renderLink('\n\nhttps://example.com text');
expect(html).toEqual(
'<p><a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a> text</p>\n'
expect(html).toContain(
'<a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a> text'
);
});

test('URLの前にテキストがあるならリンクが行末でもリンクカードに変換しない', () => {
const html = renderLink('text https://example.com\n\n');
expect(html).toEqual(
'<p>text <a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a></p>\n'
expect(html).toContain(
'text <a href="https://example.com" target="_blank" rel="nofollow noopener noreferrer">https://example.com</a>'
);
});
});
Expand Down
Loading
Loading