Skip to content

Commit

Permalink
Merge pull request #509 from zenn-dev/feat-docswell
Browse files Browse the repository at this point in the history
ドクセル埋め込み対応
  • Loading branch information
cm-dyoshikawa authored Sep 18, 2024
2 parents 5502684 + 32a4876 commit f4b2df5
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 1 deletion.
6 changes: 6 additions & 0 deletions packages/zenn-cli/articles/305-example-embed-others.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ published: true

@[speakerdeck](4f926da9cb4cd0001f00a1ff)

## docswell

@[docswell](https://www.docswell.com/slide/LK7J5V/embed)

@[docswell](https://www.docswell.com/s/ku-suke/LK7J5V-hello-docswell)

## jsfiddle

@[jsfiddle](https://jsfiddle.net/9wkngdue/embedded)
Expand Down
1 change: 1 addition & 0 deletions packages/zenn-content-css/src/_embed.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ span.embed-block {
}
.embed-slideshare,
.embed-speakerdeck,
.embed-docswell,
.embed-codepen,
.embed-jsfiddle,
.embed-youtube,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, test, expect } from 'vitest';
import markdownToHtml from '../../../src/index';

describe('Docswell', () => {
describe('Docswellの埋め込み用URLの場合', () => {
test('Docswellのiframeを返すこと', () => {
const html = markdownToHtml(
'@[docswell](https://www.docswell.com/slide/LK7J5V/embed)'
);
expect(html).toContain(
'<span class="embed-block embed-docswell"><iframe src="https://www.docswell.com/slide/LK7J5V/embed" allowfullscreen="true" width="620" style="border:1px solid #ccc;display:block;margin:0px auto;padding:0px;aspect-ratio:620/349"></iframe></span>'
);
});
});

describe('DocswellのスライドURLの場合', () => {
test('Docswellのiframeを返すこと', () => {
const html = markdownToHtml(
'@[docswell](https://www.docswell.com/s/ku-suke/LK7J5V-hello-docswell)'
);
expect(html).toContain(
'<span class="embed-block embed-docswell"><iframe src="https://www.docswell.com/slide/LK7J5V/embed" allowfullscreen="true" width="620" style="border:1px solid #ccc;display:block;margin:0px auto;padding:0px;aspect-ratio:620/349"></iframe></span>'
);
});
});

describe('DocswellのURLが不正な場合', () => {
test('エラーメッセージを返すこと', () => {
const html = markdownToHtml(
'@[docswell](https://www.docswell.com/invalid)'
);
expect(html).toContain('DocswellのスライドURLが不正です');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { isDocswellUrl } from '../../src/utils/url-matcher';
import { describe, test, expect } from 'vitest';

describe('isDocswellUrlのテスト', () => {
describe('Docswellの埋め込み用URLの場合', () => {
test('trueを返すこと', () => {
const docswellEmbedUrl = 'https://www.docswell.com/slide/LK7J5V/embed';
expect(isDocswellUrl(docswellEmbedUrl)).toBe(true);
});
});

describe('DocswellのスライドURLの場合', () => {
test('trueを返すこと', () => {
const docswellSlideUrl =
'https://www.docswell.com/s/ku-suke/LK7J5V-hello-docswell';
expect(isDocswellUrl(docswellSlideUrl)).toBe(true);
});
});

describe('Docswellの他の画面のURLの場合', () => {
test('falseを返すこと', () => {
const docswellUrls = ['https://www.docswell.com/'];

docswellUrls.forEach((url) => {
expect(isDocswellUrl(url)).toBe(false);
});
});
});

describe('他のサイトのURLの場合', () => {
test('falseを返すこと', () => {
const otherSiteUrls = ['https://zenn.dev/', 'https://github.com/'];

otherSiteUrls.forEach((url) => {
expect(isDocswellUrl(url)).toBe(false);
});
});
});
});
16 changes: 15 additions & 1 deletion packages/zenn-markdown-html/src/embed.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { MarkdownOptions } from './types';

import { escapeHtml } from 'markdown-it/lib/common/utils';
import { extractYoutubeVideoParameters } from './utils/url-matcher';
import {
sanitizeEmbedToken,
generateEmbedServerIframe,
Expand All @@ -17,6 +16,9 @@ import {
isBlueprintUEUrl,
isFigmaUrl,
isValidHttpUrl,
isDocswellUrl,
extractYoutubeVideoParameters,
extractDocswellEmbedUrl,
} from './utils/url-matcher';

/* 埋め込み要素の種別 */
Expand All @@ -25,6 +27,7 @@ export type EmbedType =
| 'slideshare'
| 'speakerdeck'
| 'jsfiddle'
| 'docswell'
| 'codepen'
| 'codesandbox'
| 'stackblitz'
Expand Down Expand Up @@ -77,6 +80,17 @@ export const embedGenerators: Readonly<EmbedGeneratorList> = {
key
)}" scrolling="no" allowfullscreen allow="encrypted-media" loading="lazy"></iframe></span>`;
},
docswell(str) {
const errorMessage = 'DocswellのスライドURLが不正です';
if (!isDocswellUrl(str)) {
return errorMessage;
}
const slideUrl = extractDocswellEmbedUrl(str);
if (!slideUrl) {
return errorMessage;
}
return `<span class="embed-block embed-docswell"><iframe src="${slideUrl}" allowfullscreen="true" class="docswell-iframe" width="620" height="349" style="border: 1px solid #ccc; display: block; margin: 0px auto; padding: 0px; aspect-ratio: 620/349;"></iframe></span>`;
},
jsfiddle(str) {
if (!isJsfiddleUrl(str)) {
return 'jsfiddleのURLが不正です';
Expand Down
27 changes: 27 additions & 0 deletions packages/zenn-markdown-html/src/utils/url-matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ export function isJsfiddleUrl(url: string): boolean {
return /^(http|https):\/\/jsfiddle\.net\/[a-zA-Z0-9_,/-]+$/.test(url);
}

const docswellNormalUrlRegex =
/^https:\/\/www\.docswell\.com\/s\/[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-]+$/;
const docswellEmbedUrlRegex =
/^https:\/\/www\.docswell\.com\/slide\/[a-zA-Z0-9_-]+\/embed$/;

export function isDocswellUrl(url: string): boolean {
return [docswellNormalUrlRegex, docswellEmbedUrlRegex].some((pattern) =>
pattern.test(url)
);
}

export function isYoutubeUrl(url: string): boolean {
return [
/^https?:\/\/youtu\.be\/[\w-]+(?:\?[\w=&-]+)?$/,
Expand Down Expand Up @@ -80,6 +91,22 @@ export function extractYoutubeVideoParameters(
return { videoId, start };
}

export function extractDocswellEmbedUrl(url: string): string | null {
// Embed用URLの場合、そのまま返す
if (docswellEmbedUrlRegex.test(url)) {
return url;
}
// Embed用URLでない場合 https://www.docswell.com/s/:username/{slideId}-hello-docswell のslideIdを抽出する
const slideId = new URL(url).pathname.split('/').at(3)?.split('-').at(0);
if (!slideId) {
return null;
}
return new URL(
`/slide/${slideId}/embed`,
'https://www.docswell.com'
).toString();
}

/**
* 参考: https://blueprintue.com/
* 生成されるURLをもとに正規表現を定義した
Expand Down

0 comments on commit f4b2df5

Please sign in to comment.