Skip to content

Commit 3187ec9

Browse files
committed
metatag api
1 parent a230936 commit 3187ec9

File tree

3 files changed

+111
-0
lines changed

3 files changed

+111
-0
lines changed

src/bindings/Jsdom.res

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
type document = {title: option<string>}
2+
type window = {document: Dom.document}
3+
type t = {window: window}
4+
5+
@module("jsdom") @new
6+
external make: string => t = "JSDOM"
7+
8+
external document: t => document = "%identity"

src/bindings/Webapi.res

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ module Document = {
22
@val external document: Dom.element = "document"
33
@scope("document") @val external createElement: string => Dom.element = "createElement"
44
@scope("document") @val external createTextNode: string => Dom.element = "createTextNode"
5+
@send
6+
external querySelector: (Dom.document, string) => Nullable.t<Dom.element> = "querySelector"
7+
@send
8+
external querySelectorAll: (Dom.document, string) => Js.Array2.array_like<Dom.element> =
9+
"querySelectorAll"
510
}
611

712
module ClassList = {
@@ -17,6 +22,7 @@ module Element = {
1722
@get external classList: Dom.element => ClassList.t = "classList"
1823
@send external getBoundingClientRect: Dom.element => {..} = "getBoundingClientRect"
1924
@send external addEventListener: (Dom.element, string, unit => unit) => unit = "addEventListener"
25+
@send external getAttribute: (Dom.element, string) => Nullable.t<string> = "getAttribute"
2026

2127
@send
2228
external getElementById: (Dom.element, string) => Nullable.t<Dom.element> = "getElementById"

src/common/MetaTagsApi.res

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
type t = {
2+
title: option<string>,
3+
description: option<string>,
4+
image: option<string>,
5+
}
6+
7+
/**
8+
This function uses JSDOM to fetch a webpage and extract the meta tags from it.
9+
JSDOM is required since this runs on Node.
10+
*/
11+
let extractMetaTags = async (url: string) => {
12+
open Webapi
13+
try {
14+
let response = await Fetch.fetch(url)
15+
16+
let html = await response->Fetch.Response.text
17+
let dom = Jsdom.make(html)
18+
let document = dom.window.document
19+
20+
let metaTags =
21+
document
22+
->Document.querySelectorAll("meta")
23+
->Array.fromArrayLike
24+
->Array.reduce(Dict.fromArray([]), (tags, meta) => {
25+
let name = meta->Element.getAttribute("name")->Nullable.toOption
26+
let property = meta->Element.getAttribute("property")->Nullable.toOption
27+
let itemprop = meta->Element.getAttribute("itemprop")->Nullable.toOption
28+
29+
let name = switch (name, property, itemprop) {
30+
| (Some(name), _, _) => Some(name)
31+
| (_, Some(property), _) => Some(property)
32+
| (_, _, Some(itemprop)) => Some(itemprop)
33+
| _ => None
34+
}
35+
36+
let content = meta->Element.getAttribute("content")->Nullable.toOption
37+
38+
switch (name, content) {
39+
| (Some(name), Some(content)) => tags->Dict.set(name, content)
40+
| _ => ()
41+
}
42+
43+
tags
44+
})
45+
46+
let document = dom->Jsdom.document
47+
48+
let title =
49+
document.title
50+
->Option.orElse(metaTags->Dict.get("og:title"))
51+
->Option.orElse(metaTags->Dict.get("twitter:title"))
52+
53+
let description =
54+
metaTags
55+
->Dict.get("description")
56+
->Option.orElse(metaTags->Dict.get("og:description"))
57+
->Option.orElse(metaTags->Dict.get("twitter:description"))
58+
59+
let image =
60+
metaTags
61+
->Dict.get("image")
62+
->Option.orElse(metaTags->Dict.get("og:image"))
63+
->Option.orElse(metaTags->Dict.get("twitter:image"))
64+
65+
Some({
66+
title,
67+
description,
68+
image,
69+
})
70+
} catch {
71+
| _ => {
72+
Console.error(`Error fetching Open Graph details for ${url}`)
73+
None
74+
}
75+
}
76+
}
77+
78+
/*
79+
Pass an array of URLs and get back an array of meta tags for each URL.
80+
*/
81+
let getMetaTags = async (urls: array<string>) => {
82+
let metaTags = []
83+
for i in 0 to Array.length(urls) - 1 {
84+
let url = urls[i]
85+
switch url {
86+
| Some(url) => {
87+
let tags = await extractMetaTags(url)
88+
switch tags {
89+
| Some(tags) => metaTags->Array.push(tags)
90+
| None => ()
91+
}
92+
}
93+
| None => ()
94+
}
95+
}
96+
metaTags
97+
}

0 commit comments

Comments
 (0)