@realfavicongenerator/check-favicon 0.4.6 → 0.4.8
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/desktop/desktop.d.ts +4 -4
- package/dist/desktop/desktop.js +40 -14
- package/dist/desktop/desktop.test.js +157 -57
- package/dist/desktop/ico.d.ts +2 -2
- package/dist/desktop/ico.js +26 -4
- package/dist/google.d.ts +8 -0
- package/dist/google.js +107 -0
- package/dist/google.test.js +71 -0
- package/dist/helper.d.ts +8 -1
- package/dist/helper.js +32 -9
- package/dist/helper.test.js +22 -3
- package/dist/touch-icon.js +1 -1
- package/dist/types.d.ts +29 -1
- package/dist/types.js +8 -0
- package/dist/web-app-manifest.js +1 -1
- package/package.json +5 -4
- package/src/desktop/desktop.test.ts +162 -58
- package/src/desktop/desktop.ts +46 -19
- package/src/desktop/ico.ts +31 -7
- package/src/google.test.ts +82 -0
- package/src/google.ts +103 -0
- package/src/helper.test.ts +24 -4
- package/src/helper.ts +40 -9
- package/src/touch-icon.ts +3 -3
- package/src/types.ts +33 -1
- package/src/web-app-manifest.ts +3 -3
- package/dist/web-manifest.d.ts +0 -4
- package/dist/web-manifest.js +0 -262
- package/dist/web-manifest.test.js +0 -172
- /package/dist/{web-manifest.test.d.ts → google.test.d.ts} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DesktopFaviconReport, DesktopSingleReport, Fetcher } from "../types";
|
|
2
2
|
import { HTMLElement } from 'node-html-parser';
|
|
3
3
|
export declare const PngFaviconFileSize = 96;
|
|
4
|
-
export declare const checkSvgFavicon: (baseUrl: string, head: HTMLElement | null, fetcher?: Fetcher) => Promise<
|
|
5
|
-
export declare const checkSvgFaviconFile: (baseUrl: string, url: string, fetcher: Fetcher) => Promise<
|
|
6
|
-
export declare const checkPngFavicon: (baseUrl: string, head: HTMLElement | null, fetcher?: Fetcher) => Promise<
|
|
4
|
+
export declare const checkSvgFavicon: (baseUrl: string, head: HTMLElement | null, fetcher?: Fetcher) => Promise<DesktopSingleReport>;
|
|
5
|
+
export declare const checkSvgFaviconFile: (baseUrl: string, url: string, fetcher: Fetcher) => Promise<DesktopSingleReport>;
|
|
6
|
+
export declare const checkPngFavicon: (baseUrl: string, head: HTMLElement | null, fetcher?: Fetcher) => Promise<DesktopSingleReport>;
|
|
7
7
|
export declare const checkDesktopFavicon: (baseUrl: string, head: HTMLElement | null, fetcher?: Fetcher) => Promise<DesktopFaviconReport>;
|
package/dist/desktop/desktop.js
CHANGED
|
@@ -26,7 +26,10 @@ const checkSvgFavicon = (baseUrl_1, head_1, ...args_1) => __awaiter(void 0, [bas
|
|
|
26
26
|
id: types_1.MessageId.noHead,
|
|
27
27
|
text: 'No <head> element'
|
|
28
28
|
});
|
|
29
|
-
return
|
|
29
|
+
return {
|
|
30
|
+
messages,
|
|
31
|
+
icon: { content: null, url: null, width: null, height: null }
|
|
32
|
+
};
|
|
30
33
|
}
|
|
31
34
|
const svgs = head === null || head === void 0 ? void 0 : head.querySelectorAll("link[rel='icon'][type='image/svg+xml']");
|
|
32
35
|
if (svgs.length === 0) {
|
|
@@ -58,17 +61,26 @@ const checkSvgFavicon = (baseUrl_1, head_1, ...args_1) => __awaiter(void 0, [bas
|
|
|
58
61
|
});
|
|
59
62
|
}
|
|
60
63
|
else {
|
|
61
|
-
const
|
|
62
|
-
return
|
|
64
|
+
const iconReport = yield (0, exports.checkSvgFaviconFile)(baseUrl, href, fetcher);
|
|
65
|
+
return {
|
|
66
|
+
messages: [...messages, ...iconReport.messages],
|
|
67
|
+
icon: iconReport.icon
|
|
68
|
+
};
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
|
-
return
|
|
71
|
+
return {
|
|
72
|
+
messages,
|
|
73
|
+
icon: { content: null, url: null, width: null, height: null }
|
|
74
|
+
};
|
|
66
75
|
});
|
|
67
76
|
exports.checkSvgFavicon = checkSvgFavicon;
|
|
68
77
|
const checkSvgFaviconFile = (baseUrl, url, fetcher) => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
78
|
const messages = [];
|
|
70
79
|
const svgUrl = (0, helper_1.mergeUrlAndPath)(baseUrl, url);
|
|
71
80
|
const res = yield fetcher(svgUrl, 'image/svg+xml');
|
|
81
|
+
let content;
|
|
82
|
+
let width = null;
|
|
83
|
+
let height = null;
|
|
72
84
|
if (res.status === 404) {
|
|
73
85
|
messages.push({
|
|
74
86
|
status: types_1.CheckerStatus.Error,
|
|
@@ -89,24 +101,33 @@ const checkSvgFaviconFile = (baseUrl, url, fetcher) => __awaiter(void 0, void 0,
|
|
|
89
101
|
id: types_1.MessageId.svgFaviconDownloadable,
|
|
90
102
|
text: `The SVG favicon is accessible at \`${url}\``
|
|
91
103
|
});
|
|
92
|
-
|
|
104
|
+
content = yield (0, helper_1.readableStreamToString)(res.readableStream);
|
|
93
105
|
const meta = yield (0, sharp_1.default)(Buffer.from(content)).metadata();
|
|
94
|
-
|
|
106
|
+
width = meta.width || null;
|
|
107
|
+
height = meta.height || null;
|
|
108
|
+
if (width && height && width !== height) {
|
|
95
109
|
messages.push({
|
|
96
110
|
status: types_1.CheckerStatus.Error,
|
|
97
111
|
id: types_1.MessageId.svgFaviconNotSquare,
|
|
98
|
-
text: `The SVG is not square (${
|
|
112
|
+
text: `The SVG is not square (${width}x${height})`
|
|
99
113
|
});
|
|
100
114
|
}
|
|
101
115
|
else {
|
|
102
116
|
messages.push({
|
|
103
117
|
status: types_1.CheckerStatus.Ok,
|
|
104
118
|
id: types_1.MessageId.svgFaviconSquare,
|
|
105
|
-
text: `The SVG is square (${
|
|
119
|
+
text: `The SVG is square (${width}x${height})`
|
|
106
120
|
});
|
|
107
121
|
}
|
|
108
122
|
}
|
|
109
|
-
return
|
|
123
|
+
return {
|
|
124
|
+
messages,
|
|
125
|
+
icon: {
|
|
126
|
+
content: content ? yield (0, helper_1.bufferToDataUrl)(Buffer.from(content), 'image/svg+xml') : null,
|
|
127
|
+
url: svgUrl,
|
|
128
|
+
width, height
|
|
129
|
+
}
|
|
130
|
+
};
|
|
110
131
|
});
|
|
111
132
|
exports.checkSvgFaviconFile = checkSvgFaviconFile;
|
|
112
133
|
const checkPngFavicon = (baseUrl_2, head_2, ...args_2) => __awaiter(void 0, [baseUrl_2, head_2, ...args_2], void 0, function* (baseUrl, head, fetcher = helper_1.fetchFetcher) {
|
|
@@ -117,7 +138,7 @@ const checkPngFavicon = (baseUrl_2, head_2, ...args_2) => __awaiter(void 0, [bas
|
|
|
117
138
|
id: types_1.MessageId.noHead,
|
|
118
139
|
text: 'No <head> element'
|
|
119
140
|
});
|
|
120
|
-
return { messages, icon: null };
|
|
141
|
+
return { messages, icon: { content: null, url: null, width: null, height: null } };
|
|
121
142
|
}
|
|
122
143
|
const icons = head === null || head === void 0 ? void 0 : head.querySelectorAll("link[rel='icon'][type='image/png']");
|
|
123
144
|
if (icons.length === 0) {
|
|
@@ -198,16 +219,21 @@ const checkPngFavicon = (baseUrl_2, head_2, ...args_2) => __awaiter(void 0, [bas
|
|
|
198
219
|
}
|
|
199
220
|
}
|
|
200
221
|
}
|
|
201
|
-
return { messages, icon: null };
|
|
222
|
+
return { messages, icon: { content: null, url: null, width: null, height: null } };
|
|
202
223
|
});
|
|
203
224
|
exports.checkPngFavicon = checkPngFavicon;
|
|
204
225
|
const checkDesktopFavicon = (baseUrl_3, head_3, ...args_3) => __awaiter(void 0, [baseUrl_3, head_3, ...args_3], void 0, function* (baseUrl, head, fetcher = helper_1.fetchFetcher) {
|
|
205
|
-
const
|
|
226
|
+
const svgReport = yield (0, exports.checkSvgFavicon)(baseUrl, head, fetcher);
|
|
206
227
|
const pngReport = yield (0, exports.checkPngFavicon)(baseUrl, head, fetcher);
|
|
207
228
|
const icoReport = yield (0, ico_1.checkIcoFavicon)(baseUrl, head, fetcher);
|
|
208
229
|
return {
|
|
209
|
-
messages: [...
|
|
210
|
-
icon: pngReport.icon
|
|
230
|
+
messages: [...svgReport.messages, ...pngReport.messages, ...icoReport.messages],
|
|
231
|
+
icon: pngReport.icon ? pngReport.icon.content : null,
|
|
232
|
+
icons: {
|
|
233
|
+
png: pngReport.icon,
|
|
234
|
+
ico: icoReport.icon,
|
|
235
|
+
svg: svgReport.icon
|
|
236
|
+
}
|
|
211
237
|
};
|
|
212
238
|
});
|
|
213
239
|
exports.checkDesktopFavicon = checkDesktopFavicon;
|
|
@@ -17,56 +17,133 @@ const test_helper_1 = require("../test-helper");
|
|
|
17
17
|
const runSvgTest = (headFragment_1, output_1, ...args_1) => __awaiter(void 0, [headFragment_1, output_1, ...args_1], void 0, function* (headFragment, output, fetchDatabase = {}) {
|
|
18
18
|
const root = headFragment ? (0, node_html_parser_1.parse)(headFragment) : null;
|
|
19
19
|
const result = yield (0, desktop_1.checkSvgFavicon)('https://example.com/', root, (0, test_helper_1.testFetcher)(fetchDatabase));
|
|
20
|
-
const filteredMessages = result.map(m => ({ status: m.status, id: m.id }));
|
|
21
|
-
expect(filteredMessages).toEqual(output);
|
|
20
|
+
const filteredMessages = result.messages.map(m => ({ status: m.status, id: m.id }));
|
|
21
|
+
expect(filteredMessages).toEqual(output.messages);
|
|
22
22
|
});
|
|
23
23
|
test('checkSvgFavicon - noHead', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
24
|
-
yield runSvgTest(null,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
yield runSvgTest(null, {
|
|
25
|
+
messages: [{
|
|
26
|
+
status: types_1.CheckerStatus.Error,
|
|
27
|
+
id: types_1.MessageId.noHead,
|
|
28
|
+
}],
|
|
29
|
+
icons: {
|
|
30
|
+
png: null,
|
|
31
|
+
ico: null,
|
|
32
|
+
svg: null,
|
|
33
|
+
}
|
|
34
|
+
});
|
|
28
35
|
}));
|
|
29
36
|
test('checkSvgFavicon - noSvgFavicon', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
30
|
-
yield runSvgTest(`<title>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
yield runSvgTest(`<title>Some text</title>`, {
|
|
38
|
+
messages: [{
|
|
39
|
+
status: types_1.CheckerStatus.Error,
|
|
40
|
+
id: types_1.MessageId.noSvgFavicon,
|
|
41
|
+
}],
|
|
42
|
+
icons: {
|
|
43
|
+
png: null,
|
|
44
|
+
ico: null,
|
|
45
|
+
svg: null,
|
|
46
|
+
}
|
|
47
|
+
});
|
|
34
48
|
}));
|
|
35
49
|
test('checkSvgFavicon - multipleSvgFavicons', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
50
|
yield runSvgTest(`
|
|
37
51
|
<link rel="icon" type="image/svg+xml" href="/the-icon.svg" />
|
|
38
52
|
<link rel="icon" type="image/svg+xml" href="/another-icon.svg" />
|
|
39
|
-
`,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
`, {
|
|
54
|
+
messages: [{
|
|
55
|
+
status: types_1.CheckerStatus.Error,
|
|
56
|
+
id: types_1.MessageId.multipleSvgFavicons,
|
|
57
|
+
}], icons: {
|
|
58
|
+
png: null,
|
|
59
|
+
ico: null,
|
|
60
|
+
svg: null,
|
|
61
|
+
}
|
|
62
|
+
});
|
|
43
63
|
}));
|
|
44
64
|
test('checkSvgFavicon - svgFaviconDeclared & noSvgFaviconHref', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
45
|
-
yield runSvgTest(`<link rel="icon" type="image/svg+xml" />`,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
65
|
+
yield runSvgTest(`<link rel="icon" type="image/svg+xml" />`, {
|
|
66
|
+
messages: [{
|
|
67
|
+
status: types_1.CheckerStatus.Ok,
|
|
68
|
+
id: types_1.MessageId.svgFaviconDeclared,
|
|
69
|
+
}, {
|
|
70
|
+
status: types_1.CheckerStatus.Error,
|
|
71
|
+
id: types_1.MessageId.noSvgFaviconHref,
|
|
72
|
+
}],
|
|
73
|
+
icons: {
|
|
74
|
+
png: null,
|
|
75
|
+
ico: null,
|
|
76
|
+
svg: null,
|
|
77
|
+
}
|
|
78
|
+
});
|
|
52
79
|
}));
|
|
53
80
|
test('checkSvgFavicon - svgFaviconDeclared & svgFavicon404', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
-
yield runSvgTest(`<link rel="icon" type="image/svg+xml" href="/the-icon.svg" />`,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
81
|
+
yield runSvgTest(`<link rel="icon" type="image/svg+xml" href="/the-icon.svg" />`, {
|
|
82
|
+
messages: [{
|
|
83
|
+
status: types_1.CheckerStatus.Ok,
|
|
84
|
+
id: types_1.MessageId.svgFaviconDeclared,
|
|
85
|
+
}, {
|
|
86
|
+
status: types_1.CheckerStatus.Error,
|
|
87
|
+
id: types_1.MessageId.svgFavicon404,
|
|
88
|
+
}],
|
|
89
|
+
icons: {
|
|
90
|
+
png: null,
|
|
91
|
+
ico: null,
|
|
92
|
+
svg: {
|
|
93
|
+
content: null,
|
|
94
|
+
url: 'https://example.com/the-icon.svg',
|
|
95
|
+
width: null,
|
|
96
|
+
height: null,
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
});
|
|
61
100
|
}));
|
|
62
101
|
test('checkSvgFavicon - svgFaviconDeclared & svgFaviconCannotGet', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
|
-
yield runSvgTest(`<link rel="icon" type="image/svg+xml" href="/the-icon.svg" />`,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
102
|
+
yield runSvgTest(`<link rel="icon" type="image/svg+xml" href="/the-icon.svg" />`, {
|
|
103
|
+
messages: [{
|
|
104
|
+
status: types_1.CheckerStatus.Ok,
|
|
105
|
+
id: types_1.MessageId.svgFaviconDeclared,
|
|
106
|
+
}, {
|
|
107
|
+
status: types_1.CheckerStatus.Error,
|
|
108
|
+
id: types_1.MessageId.svgFaviconCannotGet,
|
|
109
|
+
}],
|
|
110
|
+
icons: {
|
|
111
|
+
png: null,
|
|
112
|
+
ico: null,
|
|
113
|
+
svg: {
|
|
114
|
+
content: null,
|
|
115
|
+
url: 'https://example.com/the-icon.svg',
|
|
116
|
+
width: null,
|
|
117
|
+
height: null,
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
}, {
|
|
121
|
+
'https://example.com/the-icon.svg': {
|
|
122
|
+
status: 403,
|
|
123
|
+
contentType: 'image/svg+xml'
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}));
|
|
127
|
+
// For https://github.com/RealFaviconGenerator/core/issues/2
|
|
128
|
+
test('checkSvgFavicon - Protocol-relative URL', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
129
|
+
yield runSvgTest(`<link rel="icon" type="image/svg+xml" href="//example.com/the-icon.svg" />`, {
|
|
130
|
+
messages: [{
|
|
131
|
+
status: types_1.CheckerStatus.Ok,
|
|
132
|
+
id: types_1.MessageId.svgFaviconDeclared,
|
|
133
|
+
}, {
|
|
134
|
+
status: types_1.CheckerStatus.Error,
|
|
135
|
+
id: types_1.MessageId.svgFaviconCannotGet,
|
|
136
|
+
}], icons: {
|
|
137
|
+
png: null,
|
|
138
|
+
ico: null,
|
|
139
|
+
svg: {
|
|
140
|
+
content: null,
|
|
141
|
+
url: 'https://example.com/the-icon.svg',
|
|
142
|
+
width: null,
|
|
143
|
+
height: null,
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
}, {
|
|
70
147
|
'https://example.com/the-icon.svg': {
|
|
71
148
|
status: 403,
|
|
72
149
|
contentType: 'image/svg+xml'
|
|
@@ -76,18 +153,30 @@ test('checkSvgFavicon - svgFaviconDeclared & svgFaviconCannotGet', () => __await
|
|
|
76
153
|
test('checkSvgFavicon - svgFaviconDeclared & svgFaviconDownloadable & svgFaviconSquare', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
77
154
|
const testIconPath = './fixtures/happy-face.svg';
|
|
78
155
|
const serpIcon = yield (0, helper_1.filePathToString)(testIconPath);
|
|
79
|
-
yield runSvgTest(`<link rel="icon" type="image/svg+xml" href="/the-icon.svg" />`,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
156
|
+
yield runSvgTest(`<link rel="icon" type="image/svg+xml" href="/the-icon.svg" />`, {
|
|
157
|
+
messages: [
|
|
158
|
+
{
|
|
159
|
+
status: types_1.CheckerStatus.Ok,
|
|
160
|
+
id: types_1.MessageId.svgFaviconDeclared,
|
|
161
|
+
}, {
|
|
162
|
+
status: types_1.CheckerStatus.Ok,
|
|
163
|
+
id: types_1.MessageId.svgFaviconDownloadable,
|
|
164
|
+
}, {
|
|
165
|
+
status: types_1.CheckerStatus.Ok,
|
|
166
|
+
id: types_1.MessageId.svgFaviconSquare,
|
|
167
|
+
}
|
|
168
|
+
],
|
|
169
|
+
icons: {
|
|
170
|
+
png: null,
|
|
171
|
+
ico: null,
|
|
172
|
+
svg: {
|
|
173
|
+
content: yield (0, helper_1.filePathToDataUrl)(testIconPath),
|
|
174
|
+
url: 'https://example.com/the-icon.svg',
|
|
175
|
+
width: 36,
|
|
176
|
+
height: 36,
|
|
177
|
+
},
|
|
89
178
|
}
|
|
90
|
-
|
|
179
|
+
}, {
|
|
91
180
|
'https://example.com/the-icon.svg': {
|
|
92
181
|
status: 200,
|
|
93
182
|
contentType: 'image/svg+xml',
|
|
@@ -99,7 +188,7 @@ const runPngTest = (headFragment_2, output_2, ...args_2) => __awaiter(void 0, [h
|
|
|
99
188
|
const root = headFragment ? (0, node_html_parser_1.parse)(headFragment) : null;
|
|
100
189
|
const result = yield (0, desktop_1.checkPngFavicon)('https://example.com/', root, (0, test_helper_1.testFetcher)(fetchDatabase));
|
|
101
190
|
const filteredMessages = result.messages.map(m => ({ status: m.status, id: m.id }));
|
|
102
|
-
expect(filteredMessages).toEqual(output);
|
|
191
|
+
expect(filteredMessages).toEqual(output.messages);
|
|
103
192
|
});
|
|
104
193
|
const testIcon16 = './fixtures/16x16.png';
|
|
105
194
|
const testIcon32 = './fixtures/32x32.png';
|
|
@@ -111,16 +200,27 @@ test('checkSvgFavicon - Three PNG icons with different sizes', () => __awaiter(v
|
|
|
111
200
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
|
|
112
201
|
<link rel="icon" type="image/png" sizes="48x48" href="/favicon/favicon-48x48.png">
|
|
113
202
|
<link rel="icon" type="image/png" sizes="96x96" href="/favicon/favicon-96x96.png">
|
|
114
|
-
`, [{
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
203
|
+
`, { messages: [{
|
|
204
|
+
status: types_1.CheckerStatus.Ok,
|
|
205
|
+
id: types_1.MessageId.desktopPngFaviconDeclared,
|
|
206
|
+
}, {
|
|
207
|
+
status: types_1.CheckerStatus.Ok,
|
|
208
|
+
id: types_1.MessageId.desktopPngFaviconDownloadable,
|
|
209
|
+
}, {
|
|
210
|
+
status: types_1.CheckerStatus.Ok,
|
|
211
|
+
id: types_1.MessageId.desktopPngFaviconRightSize,
|
|
212
|
+
}],
|
|
213
|
+
icons: {
|
|
214
|
+
png: {
|
|
215
|
+
content: yield (0, helper_1.filePathToDataUrl)(testIcon96),
|
|
216
|
+
url: 'https://example.com/favicon/favicon-96x96.png',
|
|
217
|
+
width: 96,
|
|
218
|
+
height: 96,
|
|
219
|
+
},
|
|
220
|
+
ico: null,
|
|
221
|
+
svg: null,
|
|
222
|
+
}
|
|
223
|
+
}, {
|
|
124
224
|
'https://example.com/favicon/favicon-16x16.png': {
|
|
125
225
|
status: 200,
|
|
126
226
|
contentType: 'image/png',
|
package/dist/desktop/ico.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DesktopSingleReport, Fetcher } from "../types";
|
|
2
2
|
import { HTMLElement } from 'node-html-parser';
|
|
3
3
|
export declare const IcoFaviconSizes: number[];
|
|
4
|
-
export declare const checkIcoFavicon: (url: string, head: HTMLElement | null, fetcher: Fetcher) => Promise<
|
|
4
|
+
export declare const checkIcoFavicon: (url: string, head: HTMLElement | null, fetcher: Fetcher) => Promise<DesktopSingleReport>;
|
package/dist/desktop/ico.js
CHANGED
|
@@ -25,10 +25,15 @@ const checkIcoFavicon = (url, head, fetcher) => __awaiter(void 0, void 0, void 0
|
|
|
25
25
|
id: types_1.MessageId.noHead,
|
|
26
26
|
text: 'No <head> element'
|
|
27
27
|
});
|
|
28
|
-
return
|
|
28
|
+
return {
|
|
29
|
+
messages,
|
|
30
|
+
icon: { content: null, url: null, width: null, height: null }
|
|
31
|
+
};
|
|
29
32
|
}
|
|
30
33
|
const icos = head.querySelectorAll('link[rel="shortcut icon"]') ||
|
|
31
34
|
head.querySelectorAll('link[rel="icon"][type="image/x-icon"]');
|
|
35
|
+
let iconUrl = null;
|
|
36
|
+
let images;
|
|
32
37
|
if (icos.length === 0) {
|
|
33
38
|
messages.push({
|
|
34
39
|
status: types_1.CheckerStatus.Error,
|
|
@@ -58,7 +63,7 @@ const checkIcoFavicon = (url, head, fetcher) => __awaiter(void 0, void 0, void 0
|
|
|
58
63
|
});
|
|
59
64
|
}
|
|
60
65
|
else {
|
|
61
|
-
|
|
66
|
+
iconUrl = (0, helper_1.mergeUrlAndPath)(url, href);
|
|
62
67
|
const iconResponse = yield fetcher(iconUrl, 'image/x-icon');
|
|
63
68
|
if (iconResponse.status === 404) {
|
|
64
69
|
messages.push({
|
|
@@ -81,7 +86,7 @@ const checkIcoFavicon = (url, head, fetcher) => __awaiter(void 0, void 0, void 0
|
|
|
81
86
|
text: 'ICO favicon found'
|
|
82
87
|
});
|
|
83
88
|
const iconBuffer = yield (0, helper_1.readableStreamToBuffer)(iconResponse.readableStream);
|
|
84
|
-
|
|
89
|
+
images = yield (0, decode_ico_1.default)(iconBuffer);
|
|
85
90
|
const imageSizes = images.map(image => `${image.width}x${image.height}`);
|
|
86
91
|
const expectedSizes = exports.IcoFaviconSizes.map(size => `${size}x${size}`);
|
|
87
92
|
const extraSizes = imageSizes.filter(size => !expectedSizes.includes(size));
|
|
@@ -110,6 +115,23 @@ const checkIcoFavicon = (url, head, fetcher) => __awaiter(void 0, void 0, void 0
|
|
|
110
115
|
}
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
|
-
|
|
118
|
+
let content = null;
|
|
119
|
+
const theIcon = {
|
|
120
|
+
content: null,
|
|
121
|
+
url: iconUrl,
|
|
122
|
+
width: null,
|
|
123
|
+
height: null
|
|
124
|
+
};
|
|
125
|
+
if (images) {
|
|
126
|
+
const image = images[0];
|
|
127
|
+
const mimeType = (image.type === "bmp") ? "image/bmp" : "image/png";
|
|
128
|
+
theIcon.content = yield (0, helper_1.bufferToDataUrl)(Buffer.from(image.data), mimeType);
|
|
129
|
+
theIcon.width = image.width;
|
|
130
|
+
theIcon.height = image.height;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
messages,
|
|
134
|
+
icon: theIcon,
|
|
135
|
+
};
|
|
114
136
|
});
|
|
115
137
|
exports.checkIcoFavicon = checkIcoFavicon;
|
package/dist/google.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CheckerMessage, DesktopFaviconReport, Fetcher, GoogleReport } from "./types";
|
|
2
|
+
import { HTMLElement } from "node-html-parser";
|
|
3
|
+
export declare const GoogleBot = "Googlebot";
|
|
4
|
+
export declare const GoogleImageBot = "Googlebot-Image";
|
|
5
|
+
export declare const getRobotsFileUrl: (baseUrl: string) => string;
|
|
6
|
+
export declare const checkRobotsFile: (baseUrl: string, iconUrls: string[], fetcher?: Fetcher) => Promise<CheckerMessage[]>;
|
|
7
|
+
export declare const checkGoogleFaviconFromDesktopReport: (baseUrl: string, desktopReport: DesktopFaviconReport, fetcher?: Fetcher) => Promise<GoogleReport>;
|
|
8
|
+
export declare const checkGoogleFavicon: (baseUrl: string, head: HTMLElement | null, fetcher?: Fetcher) => Promise<GoogleReport>;
|
package/dist/google.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.checkGoogleFavicon = exports.checkGoogleFaviconFromDesktopReport = exports.checkRobotsFile = exports.getRobotsFileUrl = exports.GoogleImageBot = exports.GoogleBot = void 0;
|
|
16
|
+
const robots_parser_1 = __importDefault(require("robots-parser"));
|
|
17
|
+
const desktop_1 = require("./desktop/desktop");
|
|
18
|
+
const helper_1 = require("./helper");
|
|
19
|
+
const types_1 = require("./types");
|
|
20
|
+
exports.GoogleBot = 'Googlebot';
|
|
21
|
+
exports.GoogleImageBot = 'Googlebot-Image';
|
|
22
|
+
const getRobotsFileUrl = (baseUrl) => {
|
|
23
|
+
try {
|
|
24
|
+
const url = new URL(baseUrl);
|
|
25
|
+
url.pathname = '/robots.txt';
|
|
26
|
+
return url.toString();
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
throw new Error(`Invalid URL ${baseUrl}`);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
exports.getRobotsFileUrl = getRobotsFileUrl;
|
|
33
|
+
const checkRobotsFile = (baseUrl_1, iconUrls_1, ...args_1) => __awaiter(void 0, [baseUrl_1, iconUrls_1, ...args_1], void 0, function* (baseUrl, iconUrls, fetcher = helper_1.fetchFetcher) {
|
|
34
|
+
const robotsUrl = (0, exports.getRobotsFileUrl)(baseUrl);
|
|
35
|
+
const robotsResponse = yield fetcher(robotsUrl);
|
|
36
|
+
const messages = [];
|
|
37
|
+
if (robotsResponse.status === 200) {
|
|
38
|
+
messages.push({
|
|
39
|
+
status: types_1.CheckerStatus.Ok,
|
|
40
|
+
text: `robots.txt file found at ${robotsUrl}`,
|
|
41
|
+
id: types_1.MessageId.googleRobotsFileFound
|
|
42
|
+
});
|
|
43
|
+
const robotsFile = robotsResponse.readableStream ? yield (0, helper_1.readableStreamToString)(robotsResponse.readableStream) : '';
|
|
44
|
+
const robots = (0, robots_parser_1.default)(robotsUrl, robotsFile);
|
|
45
|
+
iconUrls.forEach(url => {
|
|
46
|
+
if (url) {
|
|
47
|
+
if (robots.isAllowed(url, exports.GoogleImageBot)) {
|
|
48
|
+
messages.push({
|
|
49
|
+
status: types_1.CheckerStatus.Ok,
|
|
50
|
+
text: `Access to \`${url}\` is allowed for \`${exports.GoogleImageBot}\``,
|
|
51
|
+
id: types_1.MessageId.googlePngIconAllowedByRobots
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const line = robots.getMatchingLineNumber(url, exports.GoogleImageBot);
|
|
56
|
+
messages.push({
|
|
57
|
+
status: types_1.CheckerStatus.Error,
|
|
58
|
+
text: `Access to \`${url}\` is blocked for \`${exports.GoogleImageBot}\` (\`${robotsUrl}\`, line ${line})`,
|
|
59
|
+
id: types_1.MessageId.googlePngIconBlockedByRobots
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
messages.push({
|
|
67
|
+
status: types_1.CheckerStatus.Ok,
|
|
68
|
+
text: `No \`robots.txt\` file found at \`${robotsUrl}\`. Also this is not a recommanded setup, at least Google is not restricted from accessing favicon assets.`,
|
|
69
|
+
id: types_1.MessageId.googleNoRobotsFile
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return messages;
|
|
73
|
+
});
|
|
74
|
+
exports.checkRobotsFile = checkRobotsFile;
|
|
75
|
+
const checkGoogleFaviconFromDesktopReport = (baseUrl_2, desktopReport_1, ...args_2) => __awaiter(void 0, [baseUrl_2, desktopReport_1, ...args_2], void 0, function* (baseUrl, desktopReport, fetcher = helper_1.fetchFetcher) {
|
|
76
|
+
const allIcons = [
|
|
77
|
+
desktopReport.icons.png,
|
|
78
|
+
desktopReport.icons.ico,
|
|
79
|
+
desktopReport.icons.svg
|
|
80
|
+
].filter((i) => !!i);
|
|
81
|
+
const allIconUrls = allIcons.map(i => i.url).filter((i) => !!i);
|
|
82
|
+
const robotsMessages = yield (0, exports.checkRobotsFile)(baseUrl, allIconUrls, fetcher);
|
|
83
|
+
const messages = [...desktopReport.messages, ...robotsMessages];
|
|
84
|
+
let finalIcon = null;
|
|
85
|
+
let icons = [];
|
|
86
|
+
let maxWidth = 0;
|
|
87
|
+
allIcons.forEach(icon => {
|
|
88
|
+
if (icon.content && icon.width && icon.height && icon.url) {
|
|
89
|
+
icons.push(icon);
|
|
90
|
+
if (icon.width > maxWidth) {
|
|
91
|
+
finalIcon = icon.content;
|
|
92
|
+
maxWidth = icon.width;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
messages,
|
|
98
|
+
icon: finalIcon,
|
|
99
|
+
icons
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
exports.checkGoogleFaviconFromDesktopReport = checkGoogleFaviconFromDesktopReport;
|
|
103
|
+
const checkGoogleFavicon = (baseUrl_3, head_1, ...args_3) => __awaiter(void 0, [baseUrl_3, head_1, ...args_3], void 0, function* (baseUrl, head, fetcher = helper_1.fetchFetcher) {
|
|
104
|
+
const desktopReport = yield (0, desktop_1.checkDesktopFavicon)(baseUrl, head, fetcher);
|
|
105
|
+
return (0, exports.checkGoogleFaviconFromDesktopReport)(baseUrl, desktopReport, fetcher);
|
|
106
|
+
});
|
|
107
|
+
exports.checkGoogleFavicon = checkGoogleFavicon;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const google_1 = require("./google");
|
|
13
|
+
const helper_1 = require("./helper");
|
|
14
|
+
const test_helper_1 = require("./test-helper");
|
|
15
|
+
const types_1 = require("./types");
|
|
16
|
+
test('getRobotsFileUrl', () => {
|
|
17
|
+
expect((0, google_1.getRobotsFileUrl)('https://example.com')).toEqual('https://example.com/robots.txt');
|
|
18
|
+
expect((0, google_1.getRobotsFileUrl)('https://example.com/some-path')).toEqual('https://example.com/robots.txt');
|
|
19
|
+
});
|
|
20
|
+
const runRobotsTest = (urls, robotsFile, messages) => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
|
+
const report = yield (0, google_1.checkRobotsFile)('https://example.com', urls, (0, test_helper_1.testFetcher)(robotsFile ? {
|
|
22
|
+
'https://example.com/robots.txt': {
|
|
23
|
+
status: 200,
|
|
24
|
+
contentType: 'text/plain',
|
|
25
|
+
readableStream: yield (0, helper_1.stringToReadableStream)(robotsFile)
|
|
26
|
+
}
|
|
27
|
+
} : {}));
|
|
28
|
+
const filteredMessages = report.map(m => ({ status: m.status, id: m.id }));
|
|
29
|
+
expect(filteredMessages).toEqual(messages);
|
|
30
|
+
});
|
|
31
|
+
test('checkRobotsFile - No robots file', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
|
+
yield runRobotsTest(['https://example.com/favicon.png'], null, [
|
|
33
|
+
{
|
|
34
|
+
status: types_1.CheckerStatus.Ok,
|
|
35
|
+
id: types_1.MessageId.googleNoRobotsFile
|
|
36
|
+
}
|
|
37
|
+
]);
|
|
38
|
+
}));
|
|
39
|
+
test('checkRobotsFile - PNG favicon is accessible', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
+
yield runRobotsTest(['https://example.com/favicon.png'], `
|
|
41
|
+
User-agent: *
|
|
42
|
+
Allow: /`, [
|
|
43
|
+
{
|
|
44
|
+
status: types_1.CheckerStatus.Ok,
|
|
45
|
+
id: types_1.MessageId.googleRobotsFileFound
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
status: types_1.CheckerStatus.Ok,
|
|
49
|
+
id: types_1.MessageId.googlePngIconAllowedByRobots
|
|
50
|
+
}
|
|
51
|
+
]);
|
|
52
|
+
}));
|
|
53
|
+
test('checkRobotsFile - PNG favicon is *not* accessible', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
+
yield runRobotsTest(['https://example.com/favicon.png'], `
|
|
55
|
+
# *
|
|
56
|
+
User-agent: *
|
|
57
|
+
Allow: /
|
|
58
|
+
|
|
59
|
+
User-agent: Googlebot-Image
|
|
60
|
+
Disallow: /*.png
|
|
61
|
+
`, [
|
|
62
|
+
{
|
|
63
|
+
status: types_1.CheckerStatus.Ok,
|
|
64
|
+
id: types_1.MessageId.googleRobotsFileFound
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
status: types_1.CheckerStatus.Error,
|
|
68
|
+
id: types_1.MessageId.googlePngIconBlockedByRobots
|
|
69
|
+
}
|
|
70
|
+
]);
|
|
71
|
+
}));
|
package/dist/helper.d.ts
CHANGED
|
@@ -16,8 +16,15 @@ export type CheckIconProcessor = {
|
|
|
16
16
|
wrongSize: (widthHeight: number) => void;
|
|
17
17
|
};
|
|
18
18
|
export declare const pathToMimeType: (path: string) => string;
|
|
19
|
-
export
|
|
19
|
+
export type CheckIconOutput = {
|
|
20
|
+
content: string | null;
|
|
21
|
+
url: string | null;
|
|
22
|
+
width: number | null;
|
|
23
|
+
height: number | null;
|
|
24
|
+
};
|
|
25
|
+
export declare const checkIcon: (iconUrl: string | undefined, processor: CheckIconProcessor, fetcher: Fetcher, mimeType: string | undefined, expectedWidthHeight?: number) => Promise<CheckIconOutput | null>;
|
|
20
26
|
export declare const mergeUrlAndPath: (baseUrl: string, absoluteOrRelativePath: string) => string;
|
|
21
27
|
export declare const parseSizesAttribute: (sizes: string | undefined | null) => number | null;
|
|
22
28
|
export declare const bufferToDataUrl: (buffer: Buffer, mimeType: string) => string;
|
|
29
|
+
export declare const filePathToDataUrl: (filePath: string) => Promise<string>;
|
|
23
30
|
export declare const fetchFetcher: Fetcher;
|