Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | import { visit } from 'unist-util-visit';
// oEmbed (YouTube 等) の iframe を .oembed-container.video でラップする
// remark-embedder が hName/hChildren/hProperties 経由で構造化された iframe を
// 吐くため、rehype 段階では通常の hast element として扱える。
const VIDEO_SRC_PATTERNS = [
/\/\/(www\.)?youtube\.com\/embed\//i,
/\/\/(www\.)?youtube-nocookie\.com\/embed\//i,
/\/\/player\.vimeo\.com\//i,
];
function isVideoIframe(node) {
if (node.tagName !== 'iframe') return false;
const src = node.properties?.src;
if (typeof src !== 'string') return false;
return VIDEO_SRC_PATTERNS.some((re) => re.test(src));
}
export default function rehypeEmbedWrapper() {
return (tree) => {
visit(tree, 'element', (node, index, parent) => {
if (!parent || typeof index !== 'number') return;
if (!isVideoIframe(node)) return;
// すでにラップ済みならスキップ(冪等性)
if (
parent.type === 'element'
&& parent.tagName === 'div'
&& Array.isArray(parent.properties?.className)
&& parent.properties.className.includes('oembed-container')
) {
return;
}
const wrapper = {
type: 'element',
tagName: 'div',
properties: { className: ['oembed-container', 'video'] },
children: [node],
};
parent.children[index] = wrapper;
});
};
}
|