@ox-content/vite-plugin 2.7.0 → 2.9.0
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.
- package/dist/github.cjs +8 -2
- package/dist/github.cjs.map +1 -1
- package/dist/github.mjs +299 -2
- package/dist/github.mjs.map +1 -0
- package/dist/index.cjs +99 -1088
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +5 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +180 -1206
- package/dist/index.mjs.map +1 -1
- package/dist/mermaid.cjs +22 -0
- package/dist/mermaid.cjs.map +1 -1
- package/dist/mermaid.mjs +57 -1
- package/dist/mermaid.mjs.map +1 -1
- package/dist/ogp.cjs +24 -3
- package/dist/ogp.cjs.map +1 -1
- package/dist/ogp.mjs +307 -2
- package/dist/ogp.mjs.map +1 -0
- package/dist/tabs.mjs +188 -2
- package/dist/tabs.mjs.map +1 -0
- package/dist/youtube.mjs +117 -2
- package/dist/youtube.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/github2.mjs +0 -286
- package/dist/github2.mjs.map +0 -1
- package/dist/mermaid2.mjs +0 -2
- package/dist/ogp2.mjs +0 -279
- package/dist/ogp2.mjs.map +0 -1
- package/dist/tabs2.mjs +0 -182
- package/dist/tabs2.mjs.map +0 -1
- package/dist/youtube2.mjs +0 -112
- package/dist/youtube2.mjs.map +0 -1
package/dist/github2.mjs
DELETED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
import { unified } from "unified";
|
|
2
|
-
import rehypeParse from "rehype-parse";
|
|
3
|
-
import rehypeStringify from "rehype-stringify";
|
|
4
|
-
//#region src/plugins/github.ts
|
|
5
|
-
/**
|
|
6
|
-
* GitHub Plugin - Repository card embedding
|
|
7
|
-
*
|
|
8
|
-
* Transforms <GitHub> components into static repository cards
|
|
9
|
-
* by fetching data from GitHub API at build time.
|
|
10
|
-
*/
|
|
11
|
-
const defaultOptions = {
|
|
12
|
-
token: "",
|
|
13
|
-
cache: true,
|
|
14
|
-
cacheTTL: 36e5
|
|
15
|
-
};
|
|
16
|
-
const repoCache = /* @__PURE__ */ new Map();
|
|
17
|
-
/**
|
|
18
|
-
* Get element attribute value.
|
|
19
|
-
*/
|
|
20
|
-
function getAttribute(el, name) {
|
|
21
|
-
const value = el.properties?.[name];
|
|
22
|
-
if (typeof value === "string") return value;
|
|
23
|
-
if (Array.isArray(value)) return value.join(" ");
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Format number with K/M suffix.
|
|
27
|
-
*/
|
|
28
|
-
function formatNumber(num) {
|
|
29
|
-
if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
|
|
30
|
-
if (num >= 1e3) return `${(num / 1e3).toFixed(1)}k`;
|
|
31
|
-
return String(num);
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Fetch repository data from GitHub API.
|
|
35
|
-
*/
|
|
36
|
-
async function fetchRepoData(repo, options) {
|
|
37
|
-
if (options.cache) {
|
|
38
|
-
const cached = repoCache.get(repo);
|
|
39
|
-
if (cached && Date.now() - cached.timestamp < options.cacheTTL) return cached.data;
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
const headers = {
|
|
43
|
-
Accept: "application/vnd.github.v3+json",
|
|
44
|
-
"User-Agent": "ox-content-github-plugin"
|
|
45
|
-
};
|
|
46
|
-
if (options.token) headers.Authorization = `Bearer ${options.token}`;
|
|
47
|
-
const response = await fetch(`https://api.github.com/repos/${repo}`, { headers });
|
|
48
|
-
if (!response.ok) {
|
|
49
|
-
console.warn(`Failed to fetch GitHub repo ${repo}: ${response.status}`);
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
const data = await response.json();
|
|
53
|
-
if (options.cache) repoCache.set(repo, {
|
|
54
|
-
data,
|
|
55
|
-
timestamp: Date.now()
|
|
56
|
-
});
|
|
57
|
-
return data;
|
|
58
|
-
} catch (error) {
|
|
59
|
-
console.warn(`Error fetching GitHub repo ${repo}:`, error);
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Create GitHub card element from repo data.
|
|
65
|
-
*/
|
|
66
|
-
function createGitHubCard(repoData) {
|
|
67
|
-
const statsChildren = [];
|
|
68
|
-
if (repoData.language) statsChildren.push({
|
|
69
|
-
type: "element",
|
|
70
|
-
tagName: "span",
|
|
71
|
-
properties: { className: ["ox-github-language"] },
|
|
72
|
-
children: [{
|
|
73
|
-
type: "element",
|
|
74
|
-
tagName: "span",
|
|
75
|
-
properties: {
|
|
76
|
-
className: ["ox-github-language-color"],
|
|
77
|
-
"data-lang": repoData.language.toLowerCase()
|
|
78
|
-
},
|
|
79
|
-
children: []
|
|
80
|
-
}, {
|
|
81
|
-
type: "text",
|
|
82
|
-
value: repoData.language
|
|
83
|
-
}]
|
|
84
|
-
});
|
|
85
|
-
statsChildren.push({
|
|
86
|
-
type: "element",
|
|
87
|
-
tagName: "span",
|
|
88
|
-
properties: { className: ["ox-github-stat"] },
|
|
89
|
-
children: [{
|
|
90
|
-
type: "element",
|
|
91
|
-
tagName: "svg",
|
|
92
|
-
properties: {
|
|
93
|
-
viewBox: "0 0 16 16",
|
|
94
|
-
fill: "currentColor"
|
|
95
|
-
},
|
|
96
|
-
children: [{
|
|
97
|
-
type: "element",
|
|
98
|
-
tagName: "path",
|
|
99
|
-
properties: { d: "M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z" },
|
|
100
|
-
children: []
|
|
101
|
-
}]
|
|
102
|
-
}, {
|
|
103
|
-
type: "text",
|
|
104
|
-
value: formatNumber(repoData.stargazers_count)
|
|
105
|
-
}]
|
|
106
|
-
});
|
|
107
|
-
statsChildren.push({
|
|
108
|
-
type: "element",
|
|
109
|
-
tagName: "span",
|
|
110
|
-
properties: { className: ["ox-github-stat"] },
|
|
111
|
-
children: [{
|
|
112
|
-
type: "element",
|
|
113
|
-
tagName: "svg",
|
|
114
|
-
properties: {
|
|
115
|
-
viewBox: "0 0 16 16",
|
|
116
|
-
fill: "currentColor"
|
|
117
|
-
},
|
|
118
|
-
children: [{
|
|
119
|
-
type: "element",
|
|
120
|
-
tagName: "path",
|
|
121
|
-
properties: { d: "M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z" },
|
|
122
|
-
children: []
|
|
123
|
-
}]
|
|
124
|
-
}, {
|
|
125
|
-
type: "text",
|
|
126
|
-
value: formatNumber(repoData.forks_count)
|
|
127
|
-
}]
|
|
128
|
-
});
|
|
129
|
-
return {
|
|
130
|
-
type: "element",
|
|
131
|
-
tagName: "a",
|
|
132
|
-
properties: {
|
|
133
|
-
className: ["ox-github-card"],
|
|
134
|
-
href: repoData.html_url,
|
|
135
|
-
target: "_blank",
|
|
136
|
-
rel: "noopener noreferrer"
|
|
137
|
-
},
|
|
138
|
-
children: [
|
|
139
|
-
{
|
|
140
|
-
type: "element",
|
|
141
|
-
tagName: "div",
|
|
142
|
-
properties: { className: ["ox-github-header"] },
|
|
143
|
-
children: [{
|
|
144
|
-
type: "element",
|
|
145
|
-
tagName: "svg",
|
|
146
|
-
properties: {
|
|
147
|
-
className: ["ox-github-icon"],
|
|
148
|
-
viewBox: "0 0 16 16",
|
|
149
|
-
fill: "currentColor"
|
|
150
|
-
},
|
|
151
|
-
children: [{
|
|
152
|
-
type: "element",
|
|
153
|
-
tagName: "path",
|
|
154
|
-
properties: { d: "M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.486 2.486 0 0 1 4.5 9h8ZM5 12.25a.25.25 0 0 1 .25-.25h3.5a.25.25 0 0 1 .25.25v3.25a.25.25 0 0 1-.4.2l-1.45-1.087a.249.249 0 0 0-.3 0L5.4 15.7a.25.25 0 0 1-.4-.2Z" },
|
|
155
|
-
children: []
|
|
156
|
-
}]
|
|
157
|
-
}, {
|
|
158
|
-
type: "element",
|
|
159
|
-
tagName: "span",
|
|
160
|
-
properties: { className: ["ox-github-repo"] },
|
|
161
|
-
children: [{
|
|
162
|
-
type: "text",
|
|
163
|
-
value: repoData.full_name
|
|
164
|
-
}]
|
|
165
|
-
}]
|
|
166
|
-
},
|
|
167
|
-
...repoData.description ? [{
|
|
168
|
-
type: "element",
|
|
169
|
-
tagName: "p",
|
|
170
|
-
properties: { className: ["ox-github-description"] },
|
|
171
|
-
children: [{
|
|
172
|
-
type: "text",
|
|
173
|
-
value: repoData.description
|
|
174
|
-
}]
|
|
175
|
-
}] : [],
|
|
176
|
-
{
|
|
177
|
-
type: "element",
|
|
178
|
-
tagName: "div",
|
|
179
|
-
properties: { className: ["ox-github-stats"] },
|
|
180
|
-
children: statsChildren
|
|
181
|
-
}
|
|
182
|
-
]
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Create fallback element when repo data is unavailable.
|
|
187
|
-
*/
|
|
188
|
-
function createFallbackCard(repo) {
|
|
189
|
-
return {
|
|
190
|
-
type: "element",
|
|
191
|
-
tagName: "a",
|
|
192
|
-
properties: {
|
|
193
|
-
className: ["ox-github-card", "error"],
|
|
194
|
-
href: `https://github.com/${repo}`,
|
|
195
|
-
target: "_blank",
|
|
196
|
-
rel: "noopener noreferrer"
|
|
197
|
-
},
|
|
198
|
-
children: [{
|
|
199
|
-
type: "element",
|
|
200
|
-
tagName: "div",
|
|
201
|
-
properties: { className: ["ox-github-header"] },
|
|
202
|
-
children: [{
|
|
203
|
-
type: "element",
|
|
204
|
-
tagName: "svg",
|
|
205
|
-
properties: {
|
|
206
|
-
className: ["ox-github-icon"],
|
|
207
|
-
viewBox: "0 0 16 16",
|
|
208
|
-
fill: "currentColor"
|
|
209
|
-
},
|
|
210
|
-
children: [{
|
|
211
|
-
type: "element",
|
|
212
|
-
tagName: "path",
|
|
213
|
-
properties: { d: "M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z" },
|
|
214
|
-
children: []
|
|
215
|
-
}]
|
|
216
|
-
}, {
|
|
217
|
-
type: "element",
|
|
218
|
-
tagName: "span",
|
|
219
|
-
properties: { className: ["ox-github-repo"] },
|
|
220
|
-
children: [{
|
|
221
|
-
type: "text",
|
|
222
|
-
value: repo
|
|
223
|
-
}]
|
|
224
|
-
}]
|
|
225
|
-
}]
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Collect all GitHub repos from HTML for pre-fetching.
|
|
230
|
-
*/
|
|
231
|
-
async function collectGitHubRepos(html) {
|
|
232
|
-
const repos = [];
|
|
233
|
-
const repoPattern = /<github[^>]*\s+repo=["']([^"']+)["']/gi;
|
|
234
|
-
let match;
|
|
235
|
-
while ((match = repoPattern.exec(html)) !== null) repos.push(match[1]);
|
|
236
|
-
return repos;
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Pre-fetch all GitHub repos data.
|
|
240
|
-
*/
|
|
241
|
-
async function prefetchGitHubRepos(repos, options) {
|
|
242
|
-
const mergedOptions = {
|
|
243
|
-
...defaultOptions,
|
|
244
|
-
...options
|
|
245
|
-
};
|
|
246
|
-
const results = /* @__PURE__ */ new Map();
|
|
247
|
-
await Promise.all(repos.map(async (repo) => {
|
|
248
|
-
const data = await fetchRepoData(repo, mergedOptions);
|
|
249
|
-
results.set(repo, data);
|
|
250
|
-
}));
|
|
251
|
-
return results;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Rehype plugin to transform GitHub components.
|
|
255
|
-
*/
|
|
256
|
-
function rehypeGitHub(repoDataMap) {
|
|
257
|
-
return (tree) => {
|
|
258
|
-
const visit = (node) => {
|
|
259
|
-
if ("children" in node) for (let i = 0; i < node.children.length; i++) {
|
|
260
|
-
const child = node.children[i];
|
|
261
|
-
if (child.type === "element") if (child.tagName.toLowerCase() === "github") {
|
|
262
|
-
const repo = getAttribute(child, "repo");
|
|
263
|
-
if (repo) {
|
|
264
|
-
const repoData = repoDataMap.get(repo);
|
|
265
|
-
const cardElement = repoData ? createGitHubCard(repoData) : createFallbackCard(repo);
|
|
266
|
-
node.children[i] = cardElement;
|
|
267
|
-
}
|
|
268
|
-
} else visit(child);
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
visit(tree);
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Transform GitHub components in HTML.
|
|
276
|
-
*/
|
|
277
|
-
async function transformGitHub(html, repoDataMap, options) {
|
|
278
|
-
let dataMap = repoDataMap;
|
|
279
|
-
if (!dataMap) dataMap = await prefetchGitHubRepos(await collectGitHubRepos(html), options);
|
|
280
|
-
const result = await unified().use(rehypeParse, { fragment: true }).use(rehypeGitHub, dataMap).use(rehypeStringify).process(html);
|
|
281
|
-
return String(result);
|
|
282
|
-
}
|
|
283
|
-
//#endregion
|
|
284
|
-
export { transformGitHub as i, fetchRepoData as n, prefetchGitHubRepos as r, collectGitHubRepos as t };
|
|
285
|
-
|
|
286
|
-
//# sourceMappingURL=github2.mjs.map
|
package/dist/github2.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"github2.mjs","names":[],"sources":["../src/plugins/github.ts"],"sourcesContent":["/**\n * GitHub Plugin - Repository card embedding\n *\n * Transforms <GitHub> components into static repository cards\n * by fetching data from GitHub API at build time.\n */\n\nimport { unified } from \"unified\";\nimport rehypeParse from \"rehype-parse\";\nimport rehypeStringify from \"rehype-stringify\";\nimport type { Root, Element } from \"hast\";\n\nexport interface GitHubRepoData {\n name: string;\n full_name: string;\n description: string | null;\n html_url: string;\n stargazers_count: number;\n forks_count: number;\n language: string | null;\n owner: {\n login: string;\n avatar_url: string;\n };\n}\n\nexport interface GitHubOptions {\n /** GitHub API token for higher rate limits. */\n token?: string;\n /** Cache fetched data. Default: true */\n cache?: boolean;\n /** Cache TTL in milliseconds. Default: 3600000 (1 hour) */\n cacheTTL?: number;\n}\n\nconst defaultOptions: Required<GitHubOptions> = {\n token: \"\",\n cache: true,\n cacheTTL: 3600000,\n};\n\n// Simple in-memory cache\nconst repoCache = new Map<string, { data: GitHubRepoData; timestamp: number }>();\n\n/**\n * Get element attribute value.\n */\nfunction getAttribute(el: Element, name: string): string | undefined {\n const value = el.properties?.[name];\n if (typeof value === \"string\") return value;\n if (Array.isArray(value)) return value.join(\" \");\n return undefined;\n}\n\n/**\n * Format number with K/M suffix.\n */\nfunction formatNumber(num: number): string {\n if (num >= 1000000) {\n return `${(num / 1000000).toFixed(1)}M`;\n }\n if (num >= 1000) {\n return `${(num / 1000).toFixed(1)}k`;\n }\n return String(num);\n}\n\n/**\n * Fetch repository data from GitHub API.\n */\nexport async function fetchRepoData(\n repo: string,\n options: Required<GitHubOptions>,\n): Promise<GitHubRepoData | null> {\n // Check cache\n if (options.cache) {\n const cached = repoCache.get(repo);\n if (cached && Date.now() - cached.timestamp < options.cacheTTL) {\n return cached.data;\n }\n }\n\n try {\n const headers: Record<string, string> = {\n Accept: \"application/vnd.github.v3+json\",\n \"User-Agent\": \"ox-content-github-plugin\",\n };\n\n if (options.token) {\n headers.Authorization = `Bearer ${options.token}`;\n }\n\n const response = await fetch(`https://api.github.com/repos/${repo}`, { headers });\n\n if (!response.ok) {\n console.warn(`Failed to fetch GitHub repo ${repo}: ${response.status}`);\n return null;\n }\n\n const data = (await response.json()) as GitHubRepoData;\n\n // Cache the result\n if (options.cache) {\n repoCache.set(repo, { data, timestamp: Date.now() });\n }\n\n return data;\n } catch (error) {\n console.warn(`Error fetching GitHub repo ${repo}:`, error);\n return null;\n }\n}\n\n/**\n * Create GitHub card element from repo data.\n */\nfunction createGitHubCard(repoData: GitHubRepoData): Element {\n const statsChildren: Element[\"children\"] = [];\n\n // Language\n if (repoData.language) {\n statsChildren.push({\n type: \"element\",\n tagName: \"span\",\n properties: { className: [\"ox-github-language\"] },\n children: [\n {\n type: \"element\",\n tagName: \"span\",\n properties: {\n className: [\"ox-github-language-color\"],\n \"data-lang\": repoData.language.toLowerCase(),\n },\n children: [],\n },\n { type: \"text\", value: repoData.language },\n ],\n });\n }\n\n // Stars\n statsChildren.push({\n type: \"element\",\n tagName: \"span\",\n properties: { className: [\"ox-github-stat\"] },\n children: [\n {\n type: \"element\",\n tagName: \"svg\",\n properties: {\n viewBox: \"0 0 16 16\",\n fill: \"currentColor\",\n },\n children: [\n {\n type: \"element\",\n tagName: \"path\",\n properties: {\n d: \"M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z\",\n },\n children: [],\n },\n ],\n },\n { type: \"text\", value: formatNumber(repoData.stargazers_count) },\n ],\n });\n\n // Forks\n statsChildren.push({\n type: \"element\",\n tagName: \"span\",\n properties: { className: [\"ox-github-stat\"] },\n children: [\n {\n type: \"element\",\n tagName: \"svg\",\n properties: {\n viewBox: \"0 0 16 16\",\n fill: \"currentColor\",\n },\n children: [\n {\n type: \"element\",\n tagName: \"path\",\n properties: {\n d: \"M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z\",\n },\n children: [],\n },\n ],\n },\n { type: \"text\", value: formatNumber(repoData.forks_count) },\n ],\n });\n\n return {\n type: \"element\",\n tagName: \"a\",\n properties: {\n className: [\"ox-github-card\"],\n href: repoData.html_url,\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n children: [\n // Header\n {\n type: \"element\",\n tagName: \"div\",\n properties: { className: [\"ox-github-header\"] },\n children: [\n {\n type: \"element\",\n tagName: \"svg\",\n properties: {\n className: [\"ox-github-icon\"],\n viewBox: \"0 0 16 16\",\n fill: \"currentColor\",\n },\n children: [\n {\n type: \"element\",\n tagName: \"path\",\n properties: {\n d: \"M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.486 2.486 0 0 1 4.5 9h8ZM5 12.25a.25.25 0 0 1 .25-.25h3.5a.25.25 0 0 1 .25.25v3.25a.25.25 0 0 1-.4.2l-1.45-1.087a.249.249 0 0 0-.3 0L5.4 15.7a.25.25 0 0 1-.4-.2Z\",\n },\n children: [],\n },\n ],\n },\n {\n type: \"element\",\n tagName: \"span\",\n properties: { className: [\"ox-github-repo\"] },\n children: [{ type: \"text\", value: repoData.full_name }],\n },\n ],\n },\n // Description\n ...(repoData.description\n ? [\n {\n type: \"element\" as const,\n tagName: \"p\",\n properties: { className: [\"ox-github-description\"] },\n children: [{ type: \"text\" as const, value: repoData.description }],\n },\n ]\n : []),\n // Stats\n {\n type: \"element\",\n tagName: \"div\",\n properties: { className: [\"ox-github-stats\"] },\n children: statsChildren,\n },\n ],\n };\n}\n\n/**\n * Create fallback element when repo data is unavailable.\n */\nfunction createFallbackCard(repo: string): Element {\n return {\n type: \"element\",\n tagName: \"a\",\n properties: {\n className: [\"ox-github-card\", \"error\"],\n href: `https://github.com/${repo}`,\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n children: [\n {\n type: \"element\",\n tagName: \"div\",\n properties: { className: [\"ox-github-header\"] },\n children: [\n {\n type: \"element\",\n tagName: \"svg\",\n properties: {\n className: [\"ox-github-icon\"],\n viewBox: \"0 0 16 16\",\n fill: \"currentColor\",\n },\n children: [\n {\n type: \"element\",\n tagName: \"path\",\n properties: {\n d: \"M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z\",\n },\n children: [],\n },\n ],\n },\n {\n type: \"element\",\n tagName: \"span\",\n properties: { className: [\"ox-github-repo\"] },\n children: [{ type: \"text\", value: repo }],\n },\n ],\n },\n ],\n };\n}\n\n/**\n * Collect all GitHub repos from HTML for pre-fetching.\n */\nexport async function collectGitHubRepos(html: string): Promise<string[]> {\n const repos: string[] = [];\n const repoPattern = /<github[^>]*\\s+repo=[\"']([^\"']+)[\"']/gi;\n\n let match;\n while ((match = repoPattern.exec(html)) !== null) {\n repos.push(match[1]);\n }\n\n return repos;\n}\n\n/**\n * Pre-fetch all GitHub repos data.\n */\nexport async function prefetchGitHubRepos(\n repos: string[],\n options?: GitHubOptions,\n): Promise<Map<string, GitHubRepoData | null>> {\n const mergedOptions = { ...defaultOptions, ...options };\n const results = new Map<string, GitHubRepoData | null>();\n\n await Promise.all(\n repos.map(async (repo) => {\n const data = await fetchRepoData(repo, mergedOptions);\n results.set(repo, data);\n }),\n );\n\n return results;\n}\n\n/**\n * Rehype plugin to transform GitHub components.\n */\nfunction rehypeGitHub(repoDataMap: Map<string, GitHubRepoData | null>) {\n return (tree: Root) => {\n const visit = (node: Root | Element) => {\n if (\"children\" in node) {\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n\n if (child.type === \"element\") {\n // Check for <GitHub> component\n if (child.tagName.toLowerCase() === \"github\") {\n const repo = getAttribute(child, \"repo\");\n\n if (repo) {\n const repoData = repoDataMap.get(repo);\n const cardElement = repoData\n ? createGitHubCard(repoData)\n : createFallbackCard(repo);\n node.children[i] = cardElement;\n }\n } else {\n visit(child);\n }\n }\n }\n }\n };\n\n visit(tree);\n };\n}\n\n/**\n * Transform GitHub components in HTML.\n */\nexport async function transformGitHub(\n html: string,\n repoDataMap?: Map<string, GitHubRepoData | null>,\n options?: GitHubOptions,\n): Promise<string> {\n // If no pre-fetched data, collect and fetch\n let dataMap = repoDataMap;\n if (!dataMap) {\n const repos = await collectGitHubRepos(html);\n dataMap = await prefetchGitHubRepos(repos, options);\n }\n\n const result = await unified()\n .use(rehypeParse, { fragment: true })\n .use(rehypeGitHub, dataMap)\n .use(rehypeStringify)\n .process(html);\n\n return String(result);\n}\n"],"mappings":"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"}
|
package/dist/mermaid2.mjs
DELETED
package/dist/ogp2.mjs
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
import { unified } from "unified";
|
|
2
|
-
import rehypeParse from "rehype-parse";
|
|
3
|
-
import rehypeStringify from "rehype-stringify";
|
|
4
|
-
//#region src/plugins/ogp.ts
|
|
5
|
-
/**
|
|
6
|
-
* OGP Card Plugin - Link card embedding
|
|
7
|
-
*
|
|
8
|
-
* Transforms <OgCard> components into static link preview cards
|
|
9
|
-
* by fetching OGP metadata at build time.
|
|
10
|
-
*/
|
|
11
|
-
const defaultOptions = {
|
|
12
|
-
timeout: 1e4,
|
|
13
|
-
cache: true,
|
|
14
|
-
cacheTTL: 36e5,
|
|
15
|
-
userAgent: "ox-content-ogp-bot/1.0 (compatible; +https://github.com/ubugeeei/ox-content)"
|
|
16
|
-
};
|
|
17
|
-
const ogpCache = /* @__PURE__ */ new Map();
|
|
18
|
-
/**
|
|
19
|
-
* Get element attribute value.
|
|
20
|
-
*/
|
|
21
|
-
function getAttribute(el, name) {
|
|
22
|
-
const value = el.properties?.[name];
|
|
23
|
-
if (typeof value === "string") return value;
|
|
24
|
-
if (Array.isArray(value)) return value.join(" ");
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Extract domain from URL.
|
|
28
|
-
*/
|
|
29
|
-
function extractDomain(url) {
|
|
30
|
-
try {
|
|
31
|
-
return new URL(url).hostname;
|
|
32
|
-
} catch {
|
|
33
|
-
return url;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Get favicon URL for a domain.
|
|
38
|
-
*/
|
|
39
|
-
function getFaviconUrl(url) {
|
|
40
|
-
try {
|
|
41
|
-
return `https://www.google.com/s2/favicons?domain=${new URL(url).hostname}&sz=32`;
|
|
42
|
-
} catch {
|
|
43
|
-
return "";
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Parse OGP metadata from HTML.
|
|
48
|
-
*/
|
|
49
|
-
function parseOgpFromHtml(html, url) {
|
|
50
|
-
const result = {
|
|
51
|
-
url,
|
|
52
|
-
title: ""
|
|
53
|
-
};
|
|
54
|
-
const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
|
|
55
|
-
result.title = (html.match(/<meta[^>]*property=["']og:title["'][^>]*content=["']([^"']+)["']/i) || html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:title["']/i))?.[1] || titleMatch?.[1] || extractDomain(url);
|
|
56
|
-
const descMatch = html.match(/<meta[^>]*property=["']og:description["'][^>]*content=["']([^"']+)["']/i) || html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:description["']/i) || html.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']+)["']/i) || html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*name=["']description["']/i);
|
|
57
|
-
if (descMatch) result.description = descMatch[1];
|
|
58
|
-
const imageMatch = html.match(/<meta[^>]*property=["']og:image["'][^>]*content=["']([^"']+)["']/i) || html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:image["']/i);
|
|
59
|
-
if (imageMatch) {
|
|
60
|
-
let imageUrl = imageMatch[1];
|
|
61
|
-
if (imageUrl.startsWith("/")) try {
|
|
62
|
-
const urlObj = new URL(url);
|
|
63
|
-
imageUrl = `${urlObj.protocol}//${urlObj.host}${imageUrl}`;
|
|
64
|
-
} catch {}
|
|
65
|
-
result.image = imageUrl;
|
|
66
|
-
}
|
|
67
|
-
const siteNameMatch = html.match(/<meta[^>]*property=["']og:site_name["'][^>]*content=["']([^"']+)["']/i) || html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:site_name["']/i);
|
|
68
|
-
if (siteNameMatch) result.siteName = siteNameMatch[1];
|
|
69
|
-
result.favicon = getFaviconUrl(url);
|
|
70
|
-
return result;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Fetch OGP data for a URL.
|
|
74
|
-
*/
|
|
75
|
-
async function fetchOgpData(url, options) {
|
|
76
|
-
if (options.cache) {
|
|
77
|
-
const cached = ogpCache.get(url);
|
|
78
|
-
if (cached && Date.now() - cached.timestamp < options.cacheTTL) return cached.data;
|
|
79
|
-
}
|
|
80
|
-
try {
|
|
81
|
-
const controller = new AbortController();
|
|
82
|
-
const timeoutId = setTimeout(() => controller.abort(), options.timeout);
|
|
83
|
-
const response = await fetch(url, {
|
|
84
|
-
headers: {
|
|
85
|
-
"User-Agent": options.userAgent,
|
|
86
|
-
Accept: "text/html,application/xhtml+xml"
|
|
87
|
-
},
|
|
88
|
-
signal: controller.signal
|
|
89
|
-
});
|
|
90
|
-
clearTimeout(timeoutId);
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
console.warn(`Failed to fetch OGP for ${url}: ${response.status}`);
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
const data = parseOgpFromHtml(await response.text(), url);
|
|
96
|
-
if (options.cache) ogpCache.set(url, {
|
|
97
|
-
data,
|
|
98
|
-
timestamp: Date.now()
|
|
99
|
-
});
|
|
100
|
-
return data;
|
|
101
|
-
} catch (error) {
|
|
102
|
-
if (error instanceof Error && error.name === "AbortError") console.warn(`Timeout fetching OGP for ${url}`);
|
|
103
|
-
else console.warn(`Error fetching OGP for ${url}:`, error);
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Create OGP card element.
|
|
109
|
-
*/
|
|
110
|
-
function createOgpCard(data) {
|
|
111
|
-
const children = [];
|
|
112
|
-
const contentChildren = [];
|
|
113
|
-
contentChildren.push({
|
|
114
|
-
type: "element",
|
|
115
|
-
tagName: "div",
|
|
116
|
-
properties: { className: ["ox-ogp-title"] },
|
|
117
|
-
children: [{
|
|
118
|
-
type: "text",
|
|
119
|
-
value: data.title
|
|
120
|
-
}]
|
|
121
|
-
});
|
|
122
|
-
if (data.description) contentChildren.push({
|
|
123
|
-
type: "element",
|
|
124
|
-
tagName: "div",
|
|
125
|
-
properties: { className: ["ox-ogp-description"] },
|
|
126
|
-
children: [{
|
|
127
|
-
type: "text",
|
|
128
|
-
value: data.description
|
|
129
|
-
}]
|
|
130
|
-
});
|
|
131
|
-
const metaChildren = [];
|
|
132
|
-
if (data.favicon) metaChildren.push({
|
|
133
|
-
type: "element",
|
|
134
|
-
tagName: "img",
|
|
135
|
-
properties: {
|
|
136
|
-
className: ["ox-ogp-favicon"],
|
|
137
|
-
src: data.favicon,
|
|
138
|
-
alt: "",
|
|
139
|
-
loading: "lazy"
|
|
140
|
-
},
|
|
141
|
-
children: []
|
|
142
|
-
});
|
|
143
|
-
metaChildren.push({
|
|
144
|
-
type: "element",
|
|
145
|
-
tagName: "span",
|
|
146
|
-
properties: { className: ["ox-ogp-domain"] },
|
|
147
|
-
children: [{
|
|
148
|
-
type: "text",
|
|
149
|
-
value: data.siteName || extractDomain(data.url)
|
|
150
|
-
}]
|
|
151
|
-
});
|
|
152
|
-
contentChildren.push({
|
|
153
|
-
type: "element",
|
|
154
|
-
tagName: "div",
|
|
155
|
-
properties: { className: ["ox-ogp-meta"] },
|
|
156
|
-
children: metaChildren
|
|
157
|
-
});
|
|
158
|
-
children.push({
|
|
159
|
-
type: "element",
|
|
160
|
-
tagName: "div",
|
|
161
|
-
properties: { className: ["ox-ogp-content"] },
|
|
162
|
-
children: contentChildren
|
|
163
|
-
});
|
|
164
|
-
if (data.image) children.push({
|
|
165
|
-
type: "element",
|
|
166
|
-
tagName: "img",
|
|
167
|
-
properties: {
|
|
168
|
-
className: ["ox-ogp-image"],
|
|
169
|
-
src: data.image,
|
|
170
|
-
alt: "",
|
|
171
|
-
loading: "lazy"
|
|
172
|
-
},
|
|
173
|
-
children: []
|
|
174
|
-
});
|
|
175
|
-
return {
|
|
176
|
-
type: "element",
|
|
177
|
-
tagName: "a",
|
|
178
|
-
properties: {
|
|
179
|
-
className: ["ox-ogp-card"],
|
|
180
|
-
href: data.url,
|
|
181
|
-
target: "_blank",
|
|
182
|
-
rel: "noopener noreferrer"
|
|
183
|
-
},
|
|
184
|
-
children
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Create fallback element when OGP data is unavailable.
|
|
189
|
-
*/
|
|
190
|
-
function createFallbackCard(url) {
|
|
191
|
-
return {
|
|
192
|
-
type: "element",
|
|
193
|
-
tagName: "a",
|
|
194
|
-
properties: {
|
|
195
|
-
className: ["ox-ogp-simple"],
|
|
196
|
-
href: url,
|
|
197
|
-
target: "_blank",
|
|
198
|
-
rel: "noopener noreferrer"
|
|
199
|
-
},
|
|
200
|
-
children: [{
|
|
201
|
-
type: "element",
|
|
202
|
-
tagName: "svg",
|
|
203
|
-
properties: {
|
|
204
|
-
viewBox: "0 0 24 24",
|
|
205
|
-
fill: "none",
|
|
206
|
-
stroke: "currentColor",
|
|
207
|
-
"stroke-width": "2"
|
|
208
|
-
},
|
|
209
|
-
children: [{
|
|
210
|
-
type: "element",
|
|
211
|
-
tagName: "path",
|
|
212
|
-
properties: { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3" },
|
|
213
|
-
children: []
|
|
214
|
-
}]
|
|
215
|
-
}, {
|
|
216
|
-
type: "text",
|
|
217
|
-
value: extractDomain(url)
|
|
218
|
-
}]
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Collect all OGP URLs from HTML for pre-fetching.
|
|
223
|
-
*/
|
|
224
|
-
async function collectOgpUrls(html) {
|
|
225
|
-
const urls = [];
|
|
226
|
-
const urlPattern = /<ogcard[^>]*\s+url=["']([^"']+)["']/gi;
|
|
227
|
-
let match;
|
|
228
|
-
while ((match = urlPattern.exec(html)) !== null) urls.push(match[1]);
|
|
229
|
-
return urls;
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Pre-fetch all OGP data.
|
|
233
|
-
*/
|
|
234
|
-
async function prefetchOgpData(urls, options) {
|
|
235
|
-
const mergedOptions = {
|
|
236
|
-
...defaultOptions,
|
|
237
|
-
...options
|
|
238
|
-
};
|
|
239
|
-
const results = /* @__PURE__ */ new Map();
|
|
240
|
-
await Promise.all(urls.map(async (url) => {
|
|
241
|
-
const data = await fetchOgpData(url, mergedOptions);
|
|
242
|
-
results.set(url, data);
|
|
243
|
-
}));
|
|
244
|
-
return results;
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Rehype plugin to transform OgCard components.
|
|
248
|
-
*/
|
|
249
|
-
function rehypeOgp(ogpDataMap) {
|
|
250
|
-
return (tree) => {
|
|
251
|
-
const visit = (node) => {
|
|
252
|
-
if ("children" in node) for (let i = 0; i < node.children.length; i++) {
|
|
253
|
-
const child = node.children[i];
|
|
254
|
-
if (child.type === "element") if (child.tagName.toLowerCase() === "ogcard") {
|
|
255
|
-
const url = getAttribute(child, "url");
|
|
256
|
-
if (url) {
|
|
257
|
-
const ogpData = ogpDataMap.get(url);
|
|
258
|
-
const cardElement = ogpData ? createOgpCard(ogpData) : createFallbackCard(url);
|
|
259
|
-
node.children[i] = cardElement;
|
|
260
|
-
}
|
|
261
|
-
} else visit(child);
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
visit(tree);
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Transform OgCard components in HTML.
|
|
269
|
-
*/
|
|
270
|
-
async function transformOgp(html, ogpDataMap, options) {
|
|
271
|
-
let dataMap = ogpDataMap;
|
|
272
|
-
if (!dataMap) dataMap = await prefetchOgpData(await collectOgpUrls(html), options);
|
|
273
|
-
const result = await unified().use(rehypeParse, { fragment: true }).use(rehypeOgp, dataMap).use(rehypeStringify).process(html);
|
|
274
|
-
return String(result);
|
|
275
|
-
}
|
|
276
|
-
//#endregion
|
|
277
|
-
export { transformOgp as i, fetchOgpData as n, prefetchOgpData as r, collectOgpUrls as t };
|
|
278
|
-
|
|
279
|
-
//# sourceMappingURL=ogp2.mjs.map
|
package/dist/ogp2.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ogp2.mjs","names":[],"sources":["../src/plugins/ogp.ts"],"sourcesContent":["/**\n * OGP Card Plugin - Link card embedding\n *\n * Transforms <OgCard> components into static link preview cards\n * by fetching OGP metadata at build time.\n */\n\nimport { unified } from \"unified\";\nimport rehypeParse from \"rehype-parse\";\nimport rehypeStringify from \"rehype-stringify\";\nimport type { Root, Element } from \"hast\";\n\nexport interface OgpData {\n url: string;\n title: string;\n description?: string;\n image?: string;\n siteName?: string;\n favicon?: string;\n}\n\nexport interface OgpOptions {\n /** Request timeout in milliseconds. Default: 10000 */\n timeout?: number;\n /** Cache fetched data. Default: true */\n cache?: boolean;\n /** Cache TTL in milliseconds. Default: 3600000 (1 hour) */\n cacheTTL?: number;\n /** User agent for requests */\n userAgent?: string;\n}\n\nconst defaultOptions: Required<OgpOptions> = {\n timeout: 10000,\n cache: true,\n cacheTTL: 3600000,\n userAgent: \"ox-content-ogp-bot/1.0 (compatible; +https://github.com/ubugeeei/ox-content)\",\n};\n\n// Simple in-memory cache\nconst ogpCache = new Map<string, { data: OgpData; timestamp: number }>();\n\n/**\n * Get element attribute value.\n */\nfunction getAttribute(el: Element, name: string): string | undefined {\n const value = el.properties?.[name];\n if (typeof value === \"string\") return value;\n if (Array.isArray(value)) return value.join(\" \");\n return undefined;\n}\n\n/**\n * Extract domain from URL.\n */\nfunction extractDomain(url: string): string {\n try {\n const urlObj = new URL(url);\n return urlObj.hostname;\n } catch {\n return url;\n }\n}\n\n/**\n * Get favicon URL for a domain.\n */\nfunction getFaviconUrl(url: string): string {\n try {\n const urlObj = new URL(url);\n // Use Google's favicon service as fallback\n return `https://www.google.com/s2/favicons?domain=${urlObj.hostname}&sz=32`;\n } catch {\n return \"\";\n }\n}\n\n/**\n * Parse OGP metadata from HTML.\n */\nfunction parseOgpFromHtml(html: string, url: string): OgpData {\n const result: OgpData = {\n url,\n title: \"\",\n };\n\n // Extract title\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const ogTitleMatch =\n html.match(/<meta[^>]*property=[\"']og:title[\"'][^>]*content=[\"']([^\"']+)[\"']/i) ||\n html.match(/<meta[^>]*content=[\"']([^\"']+)[\"'][^>]*property=[\"']og:title[\"']/i);\n\n result.title = ogTitleMatch?.[1] || titleMatch?.[1] || extractDomain(url);\n\n // Extract description\n const descMatch =\n html.match(/<meta[^>]*property=[\"']og:description[\"'][^>]*content=[\"']([^\"']+)[\"']/i) ||\n html.match(/<meta[^>]*content=[\"']([^\"']+)[\"'][^>]*property=[\"']og:description[\"']/i) ||\n html.match(/<meta[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']+)[\"']/i) ||\n html.match(/<meta[^>]*content=[\"']([^\"']+)[\"'][^>]*name=[\"']description[\"']/i);\n\n if (descMatch) {\n result.description = descMatch[1];\n }\n\n // Extract image\n const imageMatch =\n html.match(/<meta[^>]*property=[\"']og:image[\"'][^>]*content=[\"']([^\"']+)[\"']/i) ||\n html.match(/<meta[^>]*content=[\"']([^\"']+)[\"'][^>]*property=[\"']og:image[\"']/i);\n\n if (imageMatch) {\n let imageUrl = imageMatch[1];\n // Handle relative URLs\n if (imageUrl.startsWith(\"/\")) {\n try {\n const urlObj = new URL(url);\n imageUrl = `${urlObj.protocol}//${urlObj.host}${imageUrl}`;\n } catch {\n // Keep as is\n }\n }\n result.image = imageUrl;\n }\n\n // Extract site name\n const siteNameMatch =\n html.match(/<meta[^>]*property=[\"']og:site_name[\"'][^>]*content=[\"']([^\"']+)[\"']/i) ||\n html.match(/<meta[^>]*content=[\"']([^\"']+)[\"'][^>]*property=[\"']og:site_name[\"']/i);\n\n if (siteNameMatch) {\n result.siteName = siteNameMatch[1];\n }\n\n // Get favicon\n result.favicon = getFaviconUrl(url);\n\n return result;\n}\n\n/**\n * Fetch OGP data for a URL.\n */\nexport async function fetchOgpData(\n url: string,\n options: Required<OgpOptions>,\n): Promise<OgpData | null> {\n // Check cache\n if (options.cache) {\n const cached = ogpCache.get(url);\n if (cached && Date.now() - cached.timestamp < options.cacheTTL) {\n return cached.data;\n }\n }\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), options.timeout);\n\n const response = await fetch(url, {\n headers: {\n \"User-Agent\": options.userAgent,\n Accept: \"text/html,application/xhtml+xml\",\n },\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n console.warn(`Failed to fetch OGP for ${url}: ${response.status}`);\n return null;\n }\n\n const html = await response.text();\n const data = parseOgpFromHtml(html, url);\n\n // Cache the result\n if (options.cache) {\n ogpCache.set(url, { data, timestamp: Date.now() });\n }\n\n return data;\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n console.warn(`Timeout fetching OGP for ${url}`);\n } else {\n console.warn(`Error fetching OGP for ${url}:`, error);\n }\n return null;\n }\n}\n\n/**\n * Create OGP card element.\n */\nfunction createOgpCard(data: OgpData): Element {\n const children: Element[\"children\"] = [];\n\n // Content section\n const contentChildren: Element[\"children\"] = [];\n\n // Title\n contentChildren.push({\n type: \"element\",\n tagName: \"div\",\n properties: { className: [\"ox-ogp-title\"] },\n children: [{ type: \"text\", value: data.title }],\n });\n\n // Description\n if (data.description) {\n contentChildren.push({\n type: \"element\",\n tagName: \"div\",\n properties: { className: [\"ox-ogp-description\"] },\n children: [{ type: \"text\", value: data.description }],\n });\n }\n\n // Meta (favicon + domain)\n const metaChildren: Element[\"children\"] = [];\n\n if (data.favicon) {\n metaChildren.push({\n type: \"element\",\n tagName: \"img\",\n properties: {\n className: [\"ox-ogp-favicon\"],\n src: data.favicon,\n alt: \"\",\n loading: \"lazy\",\n },\n children: [],\n });\n }\n\n metaChildren.push({\n type: \"element\",\n tagName: \"span\",\n properties: { className: [\"ox-ogp-domain\"] },\n children: [{ type: \"text\", value: data.siteName || extractDomain(data.url) }],\n });\n\n contentChildren.push({\n type: \"element\",\n tagName: \"div\",\n properties: { className: [\"ox-ogp-meta\"] },\n children: metaChildren,\n });\n\n children.push({\n type: \"element\",\n tagName: \"div\",\n properties: { className: [\"ox-ogp-content\"] },\n children: contentChildren,\n });\n\n // Image\n if (data.image) {\n children.push({\n type: \"element\",\n tagName: \"img\",\n properties: {\n className: [\"ox-ogp-image\"],\n src: data.image,\n alt: \"\",\n loading: \"lazy\",\n },\n children: [],\n });\n }\n\n return {\n type: \"element\",\n tagName: \"a\",\n properties: {\n className: [\"ox-ogp-card\"],\n href: data.url,\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n children,\n };\n}\n\n/**\n * Create fallback element when OGP data is unavailable.\n */\nfunction createFallbackCard(url: string): Element {\n return {\n type: \"element\",\n tagName: \"a\",\n properties: {\n className: [\"ox-ogp-simple\"],\n href: url,\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n children: [\n {\n type: \"element\",\n tagName: \"svg\",\n properties: {\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n \"stroke-width\": \"2\",\n },\n children: [\n {\n type: \"element\",\n tagName: \"path\",\n properties: {\n d: \"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3\",\n },\n children: [],\n },\n ],\n },\n { type: \"text\", value: extractDomain(url) },\n ],\n };\n}\n\n/**\n * Collect all OGP URLs from HTML for pre-fetching.\n */\nexport async function collectOgpUrls(html: string): Promise<string[]> {\n const urls: string[] = [];\n const urlPattern = /<ogcard[^>]*\\s+url=[\"']([^\"']+)[\"']/gi;\n\n let match;\n while ((match = urlPattern.exec(html)) !== null) {\n urls.push(match[1]);\n }\n\n return urls;\n}\n\n/**\n * Pre-fetch all OGP data.\n */\nexport async function prefetchOgpData(\n urls: string[],\n options?: OgpOptions,\n): Promise<Map<string, OgpData | null>> {\n const mergedOptions = { ...defaultOptions, ...options };\n const results = new Map<string, OgpData | null>();\n\n await Promise.all(\n urls.map(async (url) => {\n const data = await fetchOgpData(url, mergedOptions);\n results.set(url, data);\n }),\n );\n\n return results;\n}\n\n/**\n * Rehype plugin to transform OgCard components.\n */\nfunction rehypeOgp(ogpDataMap: Map<string, OgpData | null>) {\n return (tree: Root) => {\n const visit = (node: Root | Element) => {\n if (\"children\" in node) {\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n\n if (child.type === \"element\") {\n // Check for <OgCard> component\n if (child.tagName.toLowerCase() === \"ogcard\") {\n const url = getAttribute(child, \"url\");\n\n if (url) {\n const ogpData = ogpDataMap.get(url);\n const cardElement = ogpData ? createOgpCard(ogpData) : createFallbackCard(url);\n node.children[i] = cardElement;\n }\n } else {\n visit(child);\n }\n }\n }\n }\n };\n\n visit(tree);\n };\n}\n\n/**\n * Transform OgCard components in HTML.\n */\nexport async function transformOgp(\n html: string,\n ogpDataMap?: Map<string, OgpData | null>,\n options?: OgpOptions,\n): Promise<string> {\n // If no pre-fetched data, collect and fetch\n let dataMap = ogpDataMap;\n if (!dataMap) {\n const urls = await collectOgpUrls(html);\n dataMap = await prefetchOgpData(urls, options);\n }\n\n const result = await unified()\n .use(rehypeParse, { fragment: true })\n .use(rehypeOgp, dataMap)\n .use(rehypeStringify)\n .process(html);\n\n return String(result);\n}\n"],"mappings":"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"}
|