zenn-markdown-html 0.1.126-alpha.3 → 0.1.126

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,26 +1,16 @@
1
- export declare type EmbedType = keyof typeof embedGenerators;
2
- /** 渡された文字列から埋め込み要素のHTMLを生成する関数をまとめたオブジェクト */
3
- declare const embedGenerators: {
4
- youtube(videoId: string): string;
5
- slideshare(key: string): string;
6
- speakerdeck(key: string): string;
7
- jsfiddle(str: string): string;
8
- codepen(str: string): string;
9
- codesandbox(str: string): string;
10
- stackblitz(str: string): string;
11
- tweet(str: string): string;
12
- blueprintue(str: string): string;
13
- figma(str: string): string;
14
- card(str: string): string;
15
- gist(str: string): string;
16
- github(str: string): string;
1
+ export declare type EmbedType = 'youtube' | 'slideshare' | 'speakerdeck' | 'jsfiddle' | 'codepen' | 'codesandbox' | 'stackblitz' | 'tweet' | 'blueprintue' | 'figma' | 'card' | 'gist' | 'github';
2
+ /** embedサーバーで表示する埋め込み要素の種別 */
3
+ export declare type ZennEmbedTypes = 'tweet' | 'link-card' | 'mermaid' | 'github' | 'gist';
4
+ /** 渡された埋め込みURLまたはTokenを検証する */
5
+ export declare const validateEmbedToken: (str: string, type?: "youtube" | "slideshare" | "speakerdeck" | "jsfiddle" | "codepen" | "codesandbox" | "stackblitz" | "tweet" | "blueprintue" | "figma" | "card" | "gist" | "github" | "link-card" | "mermaid" | undefined) => {
6
+ isValid: boolean;
7
+ message: string;
17
8
  };
9
+ /** Embedサーバーを使った埋め込み要素の文字列を生成する */
10
+ export declare function generateEmbedIframe(type: ZennEmbedTypes, src: string): string;
18
11
  /** `EmbedType`か判定する */
19
12
  export declare function isEmbedType(type: unknown): type is EmbedType;
20
- /** 渡された埋め込みURLを検証する */
21
- export declare const validateEbedUrl: (url: string) => boolean;
22
13
  /** 渡された`type`の埋め込み要素のHTML文字列を返す */
23
- export declare const generateEmbedHTML: (type: EmbedType, url: string) => string;
14
+ export declare const generateEmbedHTML: (type: EmbedType, str: string) => string;
24
15
  /** Linkifyな埋め込み要素のHTML生成する */
25
16
  export declare const generateLinkifyEmbedHTML: (url: string) => string;
26
- export {};
@@ -3,27 +3,105 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.generateLinkifyEmbedHTML = exports.generateEmbedHTML = void 0;
6
+ exports.generateEmbedHTML = void 0;
7
+ exports.generateEmbedIframe = generateEmbedIframe;
8
+ exports.generateLinkifyEmbedHTML = void 0;
7
9
  exports.isEmbedType = isEmbedType;
8
- exports.validateEbedUrl = void 0;
10
+ exports.validateEmbedToken = void 0;
9
11
 
10
12
  var _utils = require("markdown-it/lib/common/utils");
11
13
 
12
- var _helper = require("./helper");
13
-
14
14
  var _urlMatcher = require("./url-matcher");
15
15
 
16
- /** 埋め込みURLの最大文字数( LinkCardは除く ) */
17
- const MAX_EMBED_URL_LENGTH = 300;
16
+ /** 検証から除外する埋め込みの種別 */
17
+ const ignoredEmbedType = ['card', 'link-card', 'github'];
18
+ /** 埋め込みURLまたはTokenの最大文字数( excludeEmbedTypeは除く ) */
19
+
20
+ const MAX_EMBED_TOKEN_LENGTH = 300;
21
+ /** 渡された埋め込みURLまたはTokenを検証する */
22
+
23
+ const validateEmbedToken = (str, type) => {
24
+ if (type && ignoredEmbedType.includes(type)) {
25
+ return {
26
+ isValid: true,
27
+ message: ''
28
+ };
29
+ }
30
+
31
+ if (str.length > MAX_EMBED_TOKEN_LENGTH) {
32
+ return {
33
+ isValid: false,
34
+ message: `埋め込みURLは${MAX_EMBED_TOKEN_LENGTH}文字以内にする必要があります`
35
+ };
36
+ }
37
+
38
+ return {
39
+ isValid: true,
40
+ message: ''
41
+ };
42
+ };
43
+ /** 渡された文字列をサニタイズする */
44
+
45
+
46
+ exports.validateEmbedToken = validateEmbedToken;
47
+
48
+ function sanitaizeEmbedToken(str) {
49
+ return str.replace(/"/g, '%22');
50
+ }
51
+ /** URL文字列か判定する */
52
+
53
+
54
+ function isValidHttpUrl(str) {
55
+ try {
56
+ const url = new URL(str);
57
+ return url.protocol === 'http:' || url.protocol === 'https:';
58
+ } catch (_) {
59
+ return false;
60
+ }
61
+ }
62
+ /** `videoId`から Youtube の埋め込み要素の文字列を生成する */
63
+
64
+
65
+ function generateYoutubeHtmlFromVideoId(videoId, start) {
66
+ const escapedVideoId = (0, _utils.escapeHtml)(videoId);
67
+ const time = Math.min(Number(start || 0), 48 * 60 * 60); // 48時間以内
68
+
69
+ const startQuery = time ? `&start=${time}` : '';
70
+ return `<div class="embed-youtube"><iframe src="https://www.youtube.com/embed/${escapedVideoId}?loop=1&playlist=${escapedVideoId}${startQuery}" allowfullscreen loading="lazy"></iframe></div>`;
71
+ }
72
+ /** Youtube の埋め込み要素の文字列を生成する */
73
+
74
+
75
+ function generateYoutubeHtmlFromUrl(url) {
76
+ const params = (0, _urlMatcher.extractYoutubeVideoParameters)(url);
77
+
78
+ if (!params) {
79
+ return generateEmbedIframe('link-card', url);
80
+ } else {
81
+ return generateYoutubeHtmlFromVideoId(params.videoId, params.start);
82
+ }
83
+ }
84
+ /** Embedサーバーを使った埋め込み要素の文字列を生成する */
85
+
86
+
87
+ function generateEmbedIframe(type, src) {
88
+ // ユーザーからの入力値が引数として渡されたときのために念のためencodeする
89
+ const encodedType = encodeURIComponent(type);
90
+ const encodedSrc = encodeURIComponent(src);
91
+ const id = `zenn-embedded__${Math.random().toString(16).slice(2)}`;
92
+ const iframeSrc = `https://embed.zenn.studio/${encodedType}#${id}`;
93
+ return `<div class="zenn-embedded zenn-embedded-${encodedType}"><iframe id="${id}" src="${iframeSrc}" data-content="${encodedSrc}" frameborder="0" scrolling="no" loading="lazy"></iframe></div>`;
94
+ }
18
95
  /** 渡された文字列から埋め込み要素のHTMLを生成する関数をまとめたオブジェクト */
19
96
 
97
+
20
98
  const embedGenerators = {
21
99
  youtube(videoId) {
22
100
  if (!(videoId !== null && videoId !== void 0 && videoId.match(/^[a-zA-Z0-9_-]+$/))) {
23
101
  return 'YouTubeのvideoIDが不正です';
24
102
  }
25
103
 
26
- return (0, _helper.generateYoutubeHtmlFromVideoId)(videoId);
104
+ return generateYoutubeHtmlFromVideoId(videoId);
27
105
  },
28
106
 
29
107
  slideshare(key) {
@@ -55,7 +133,7 @@ const embedGenerators = {
55
133
  url = url.endsWith('/') ? `${url}embedded/` : `${url}/embedded/`;
56
134
  }
57
135
 
58
- return `<div class="embed-jsfiddle"><iframe src="${encodeDoubleQuote(url)}" scrolling="no" frameborder="no" loading="lazy"></iframe></div>`;
136
+ return `<div class="embed-jsfiddle"><iframe src="${sanitaizeEmbedToken(url)}" scrolling="no" frameborder="no" loading="lazy"></iframe></div>`;
59
137
  },
60
138
 
61
139
  codepen(str) {
@@ -65,7 +143,7 @@ const embedGenerators = {
65
143
 
66
144
  const url = new URL(str.replace('/pen/', '/embed/'));
67
145
  url.searchParams.set('embed-version', '2');
68
- return `<div class="embed-codepen"><iframe src="${encodeDoubleQuote(url.toString())}" scrolling="no" frameborder="no" loading="lazy"></iframe></div>`;
146
+ return `<div class="embed-codepen"><iframe src="${sanitaizeEmbedToken(url.toString())}" scrolling="no" frameborder="no" loading="lazy"></iframe></div>`;
69
147
  },
70
148
 
71
149
  codesandbox(str) {
@@ -73,7 +151,7 @@ const embedGenerators = {
73
151
  return '「https://codesandbox.io/embed/」から始まる正しいURLを入力してください';
74
152
  }
75
153
 
76
- return `<div class="embed-codesandbox"><iframe src="${encodeDoubleQuote(str)}" style="width:100%;height:500px;border:none;overflow:hidden;" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" loading="lazy" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe></div>`;
154
+ return `<div class="embed-codesandbox"><iframe src="${sanitaizeEmbedToken(str)}" style="width:100%;height:500px;border:none;overflow:hidden;" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" loading="lazy" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe></div>`;
77
155
  },
78
156
 
79
157
  stackblitz(str) {
@@ -81,27 +159,27 @@ const embedGenerators = {
81
159
  return 'StackBlitzのembed用のURLを指定してください';
82
160
  }
83
161
 
84
- return `<div class="embed-stackblitz"><iframe src="${encodeDoubleQuote(str)}" scrolling="no" frameborder="no" loading="lazy"></iframe></div>`;
162
+ return `<div class="embed-stackblitz"><iframe src="${sanitaizeEmbedToken(str)}" scrolling="no" frameborder="no" loading="lazy"></iframe></div>`;
85
163
  },
86
164
 
87
165
  tweet(str) {
88
166
  if (!(0, _urlMatcher.isTweetUrl)(str)) return 'ツイートページのURLを指定してください';
89
- return (0, _helper.generateEmbedIframe)('tweet', str);
167
+ return generateEmbedIframe('tweet', str);
90
168
  },
91
169
 
92
170
  blueprintue(str) {
93
171
  if (!(0, _urlMatcher.isBlueprintUEUrl)(str)) return '「https://blueprintue.com/render/」から始まる正しいURLを指定してください';
94
- return `<div class="embed-blueprintue"><iframe src="${encodeDoubleQuote(str)}" width="100%" style="aspect-ratio: 16/9" scrolling="no" frameborder="no" loading="lazy" allowfullscreen></iframe></div>`;
172
+ return `<div class="embed-blueprintue"><iframe src="${sanitaizeEmbedToken(str)}" width="100%" style="aspect-ratio: 16/9" scrolling="no" frameborder="no" loading="lazy" allowfullscreen></iframe></div>`;
95
173
  },
96
174
 
97
175
  figma(str) {
98
176
  if (!(0, _urlMatcher.isFigmaUrl)(str)) return 'ファイルまたはプロトタイプのFigma URLを指定してください';
99
- return `<div class="embed-figma"><iframe src="https://www.figma.com/embed?embed_host=zenn&url=${encodeDoubleQuote(str)}" width="100%" style="aspect-ratio: 16/9" scrolling="no" frameborder="no" loading="lazy" allowfullscreen></iframe></div>`;
177
+ return `<div class="embed-figma"><iframe src="https://www.figma.com/embed?embed_host=zenn&url=${sanitaizeEmbedToken(str)}" width="100%" style="aspect-ratio: 16/9" scrolling="no" frameborder="no" loading="lazy" allowfullscreen></iframe></div>`;
100
178
  },
101
179
 
102
180
  card(str) {
103
- if (!(0, _helper.isValidHttpUrl)(str)) return 'URLが不正です';
104
- return (0, _helper.generateEmbedIframe)('link-card', str);
181
+ if (!isValidHttpUrl(str)) return 'URLが不正です';
182
+ return generateEmbedIframe('link-card', str);
105
183
  },
106
184
 
107
185
  gist(str) {
@@ -113,42 +191,29 @@ const embedGenerators = {
113
191
  * のような形式
114
192
  */
115
193
 
116
- return (0, _helper.generateEmbedIframe)('gist', str);
194
+ return generateEmbedIframe('gist', str);
117
195
  },
118
196
 
119
197
  github(str) {
120
198
  if (!(0, _urlMatcher.isGithubUrl)(str)) return 'GitHub のファイルURLまたはパーマリンクを指定してください';
121
- return (0, _helper.generateEmbedIframe)('github', str);
199
+ return generateEmbedIframe('github', str);
122
200
  }
123
201
 
124
202
  };
125
-
126
- function encodeDoubleQuote(str) {
127
- return str.replace(/"/g, '%22');
128
- }
129
203
  /** `EmbedType`か判定する */
130
204
 
131
-
132
205
  function isEmbedType(type) {
133
206
  return typeof type === 'string' && type in embedGenerators;
134
207
  }
135
- /** 渡された埋め込みURLを検証する */
136
-
137
-
138
- const validateEbedUrl = url => {
139
- return url.length <= MAX_EMBED_URL_LENGTH;
140
- };
141
208
  /** 渡された`type`の埋め込み要素のHTML文字列を返す */
142
209
 
143
210
 
144
- exports.validateEbedUrl = validateEbedUrl;
145
-
146
- const generateEmbedHTML = (type, url) => {
147
- if (type !== 'card' && !validateEbedUrl(url)) {
148
- return `埋め込みURLは${MAX_EMBED_URL_LENGTH}文字以内にする必要があります`;
149
- }
150
-
151
- return embedGenerators[type](url);
211
+ const generateEmbedHTML = (type, str) => {
212
+ const {
213
+ isValid,
214
+ message
215
+ } = validateEmbedToken(str, type);
216
+ return isValid ? embedGenerators[type](str) : message;
152
217
  };
153
218
  /** Linkifyな埋め込み要素のHTML生成する */
154
219
 
@@ -156,10 +221,15 @@ const generateEmbedHTML = (type, url) => {
156
221
  exports.generateEmbedHTML = generateEmbedHTML;
157
222
 
158
223
  const generateLinkifyEmbedHTML = url => {
159
- let html = null;
160
- if ((0, _urlMatcher.isTweetUrl)(url)) html = (0, _helper.generateEmbedIframe)('tweet', url);else if ((0, _urlMatcher.isYoutubeUrl)(url)) html = (0, _helper.generateYoutubeHtmlFromUrl)(url);else if ((0, _urlMatcher.isGithubUrl)(url)) html = (0, _helper.generateEmbedIframe)('github', url);
161
- if (!html) return (0, _helper.generateEmbedIframe)('link-card', url);
162
- return validateEbedUrl(url) ? html : `埋め込みURLは${MAX_EMBED_URL_LENGTH}文字以内にする必要があります`;
224
+ const {
225
+ isValid,
226
+ message: msg
227
+ } = validateEmbedToken(url);
228
+ if ((0, _urlMatcher.isTweetUrl)(url)) return isValid ? generateEmbedIframe('tweet', url) : msg;
229
+ if ((0, _urlMatcher.isYoutubeUrl)(url)) return isValid ? generateYoutubeHtmlFromUrl(url) : msg; // GitHub は URL が長くなりやすいためバリデーション(`validateEmbedToken`)から外す
230
+
231
+ if ((0, _urlMatcher.isGithubUrl)(url)) return generateEmbedIframe('github', url);
232
+ return generateEmbedIframe('link-card', url);
163
233
  };
164
234
 
165
235
  exports.generateLinkifyEmbedHTML = generateLinkifyEmbedHTML;
@@ -8,7 +8,7 @@ exports.parseInfo = parseInfo;
8
8
 
9
9
  var _utils = require("markdown-it/lib/common/utils");
10
10
 
11
- var _helper = require("./helper");
11
+ var _embedHelper = require("./embed-helper");
12
12
 
13
13
  var _highlight = require("./highlight");
14
14
 
@@ -88,7 +88,7 @@ function mdRendererFence(md) {
88
88
  } = parseInfo(info);
89
89
 
90
90
  if (langName === 'mermaid') {
91
- return (0, _helper.generateEmbedIframe)('mermaid', content.trim());
91
+ return (0, _embedHelper.generateEmbedIframe)('mermaid', content.trim());
92
92
  }
93
93
 
94
94
  const className = getClassName({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zenn-markdown-html",
3
- "version": "0.1.126-alpha.3",
3
+ "version": "0.1.126",
4
4
  "license": "MIT",
5
5
  "description": "Convert markdown to zenn flavor html.",
6
6
  "main": "lib/index.js",
@@ -53,7 +53,7 @@
53
53
  "markdown-it-task-lists": "^2.1.1",
54
54
  "prismjs": "^1.27.0"
55
55
  },
56
- "gitHead": "3d78ffb0fc67efde08b361120b087c2b4db8087d",
56
+ "gitHead": "2068f91c365dff20abbfc8de7eed6580a81bdbc0",
57
57
  "publishConfig": {
58
58
  "access": "public"
59
59
  }
@@ -1,6 +0,0 @@
1
- export declare function generateYoutubeHtmlFromUrl(url: string): string;
2
- export declare function generateYoutubeHtmlFromVideoId(videoId: string): string;
3
- export declare function isValidHttpUrl(str: string): boolean;
4
- declare type ZennEmbedTypes = 'tweet' | 'link-card' | 'mermaid' | 'github' | 'gist';
5
- export declare function generateEmbedIframe(type: ZennEmbedTypes, src: string): string;
6
- export {};
@@ -1,53 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.generateEmbedIframe = generateEmbedIframe;
7
- exports.generateYoutubeHtmlFromUrl = generateYoutubeHtmlFromUrl;
8
- exports.generateYoutubeHtmlFromVideoId = generateYoutubeHtmlFromVideoId;
9
- exports.isValidHttpUrl = isValidHttpUrl;
10
-
11
- var _utils = require("markdown-it/lib/common/utils");
12
-
13
- var _urlMatcher = require("./url-matcher");
14
-
15
- function generateYoutubeHtml(videoId, start) {
16
- const escapedVideoId = (0, _utils.escapeHtml)(videoId); // 48時間以内
17
-
18
- const time = Math.min(Number(start || 0), 48 * 60 * 60);
19
- const startQuery = time ? `&start=${time}` : '';
20
- return `<div class="embed-youtube"><iframe src="https://www.youtube.com/embed/${escapedVideoId}?loop=1&playlist=${escapedVideoId}${startQuery}" allowfullscreen loading="lazy"></iframe></div>`;
21
- }
22
-
23
- function generateYoutubeHtmlFromUrl(url) {
24
- const params = (0, _urlMatcher.extractYoutubeVideoParameters)(url);
25
-
26
- if (!params) {
27
- return generateEmbedIframe('link-card', url);
28
- } else {
29
- return generateYoutubeHtml(params.videoId, params.start);
30
- }
31
- }
32
-
33
- function generateYoutubeHtmlFromVideoId(videoId) {
34
- return generateYoutubeHtml(videoId);
35
- }
36
-
37
- function isValidHttpUrl(str) {
38
- try {
39
- const url = new URL(str);
40
- return url.protocol === 'http:' || url.protocol === 'https:';
41
- } catch (_) {
42
- return false;
43
- }
44
- }
45
-
46
- function generateEmbedIframe(type, src) {
47
- // ユーザーからの入力値が引数として渡されたときのために念のためencodeする
48
- const encodedType = encodeURIComponent(type);
49
- const encodedSrc = encodeURIComponent(src);
50
- const id = `zenn-embedded__${Math.random().toString(16).slice(2)}`;
51
- const iframeSrc = `https://embed.zenn.studio/${encodedType}#${id}`;
52
- return `<div class="zenn-embedded zenn-embedded-${encodedType}"><iframe id="${id}" src="${iframeSrc}" data-content="${encodedSrc}" frameborder="0" scrolling="no" loading="lazy"></iframe></div>`;
53
- }