This repository has been archived by the owner on Mar 3, 2020. It is now read-only.
/
NativeAppLinkResolver.js
121 lines (105 loc) · 3.62 KB
/
NativeAppLinkResolver.js
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/**
* Copyright (c) 2004-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NativeAppLinkResolver
* @flow
*/
'use strict';
var AppLinkResolver = require('./AppLinkResolver');
var Parser = require('parse5').Parser;
var AppLink = require('./AppLink');
class NativeAppLinkResolver extends AppLinkResolver {
resolve(web_url: string, success: Function, error: Function) {
var webUrl = super.normalizeUrl(web_url);
fetch(webUrl, {method: 'get', headers: {'Prefer-Html-Meta-Tags': 'al'}})
.then(res => res.text())
.then((body) => {
var appLinkHost = NativeAppLinkResolver.parseHTML(body);
success(new AppLink(webUrl, appLinkHost));
})
.catch(function(err) {
error(err);
});
}
/**
* This method is parsing html and scans for 'al:' meta tags.
* Learn App Links meta tags schema at http://applinks.org/
*/
static parseHTML(html_str: String) {
var parser = new Parser();
var dom = parser.parse(html_str);
var html = dom.childNodes.filter(n => n.tagName === 'html')[0];
var head = html.childNodes.filter(n => n.tagName === 'head')[0];
var metaTags = head.childNodes.filter(t => t.tagName === 'meta');
var alDataTags = [];
// filter only 'al:' tags and make them lowercase
// For example '<meta property="al:ios:url" content="applinks://docs" />'
metaTags.forEach((metaTag) => {
if (metaTag.attrs) {
var propertyAttributes = metaTag.attrs
.filter(a => a.name === 'property'
&& a.value.toLowerCase().indexOf('al:') === 0);
var contentAttributes = metaTag.attrs.filter(a => a.name === 'content');
if (propertyAttributes.length > 0) {
var alTag = propertyAttributes[0].value;
var alContent = '';
if (contentAttributes.length > 0) {
alContent = contentAttributes[0].value || '';
}
alDataTags.push({
property: alTag.toLowerCase(),
content: alContent
});
}
}
});
var result = {};
alDataTags.forEach((tag) => {
var [al, platform, attribute] = tag.property.split(':');
var content = tag.content;
if (al !== 'al' || platform === undefined) {
// just 'al:' is not valid and should be skipped
return;
}
// Initialize the result
if (!result[platform]) {
result[platform] = [];
}
if (attribute !== undefined) {
var lastItem = result[platform].pop() || {};
lastItem[attribute] = content;
result[platform].push(lastItem);
} else {
var lastItem = result[platform].pop();
// keep last item if it exists
if (lastItem) {
result[platform].push(lastItem);
}
result[platform].push({});
}
});
// Handle web separately as it should be just single object, not array
if (result.web && result.web.length > 0) {
result.web = result.web[0];
// should fallback can only be boolean
if (result.web.should_fallback) {
var parsedValue;
try {
parsedValue = JSON.parse(result.web.should_fallback);
} catch (ex) {}
if (parsedValue && typeof parsedValue === 'boolean') {
result.web.should_fallback = parsedValue;
} else {
delete result.web.should_fallback;
}
}
}
return result;
}
}
module.exports = NativeAppLinkResolver;