Skip to content

Commit

Permalink
Merge pull request #504 from senkenn/feat/add-source-map
Browse files Browse the repository at this point in the history
feat: [zenn-markdown-html] VS Code 拡張機能でのスクロール同期のためのソースマップ追加
  • Loading branch information
cm-igarashi-ryosuke authored Aug 15, 2024
2 parents 577c1e2 + 3eb6851 commit c85d438
Show file tree
Hide file tree
Showing 13 changed files with 248 additions and 79 deletions.
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'
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

0 comments on commit c85d438

Please sign in to comment.