@realfavicongenerator/check-favicon 0.4.7 → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 RealFaviconGenerator
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,7 +1,7 @@
1
- import { CheckerMessage, DesktopFaviconReport, Fetcher } from "../types";
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<CheckerMessage[]>;
5
- export declare const checkSvgFaviconFile: (baseUrl: string, url: string, fetcher: Fetcher) => Promise<CheckerMessage[]>;
6
- export declare const checkPngFavicon: (baseUrl: string, head: HTMLElement | null, fetcher?: Fetcher) => Promise<DesktopFaviconReport>;
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>;
@@ -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 messages;
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 iconMessages = yield (0, exports.checkSvgFaviconFile)(baseUrl, href, fetcher);
62
- return [...messages, ...iconMessages];
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 messages;
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
- const content = yield (0, helper_1.readableStreamToString)(res.readableStream);
104
+ content = yield (0, helper_1.readableStreamToString)(res.readableStream);
93
105
  const meta = yield (0, sharp_1.default)(Buffer.from(content)).metadata();
94
- if (meta.width !== meta.height) {
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 (${meta.width}x${meta.height})`
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 (${meta.width}x${meta.height})`
119
+ text: `The SVG is square (${width}x${height})`
106
120
  });
107
121
  }
108
122
  }
109
- return messages;
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 svgMessages = yield (0, exports.checkSvgFavicon)(baseUrl, head, fetcher);
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: [...svgMessages, ...pngReport.messages, ...icoReport],
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,107 @@ 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
- status: types_1.CheckerStatus.Error,
26
- id: types_1.MessageId.noHead,
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>SOme text</title>`, [{
31
- status: types_1.CheckerStatus.Error,
32
- id: types_1.MessageId.noSvgFavicon,
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
- status: types_1.CheckerStatus.Error,
41
- id: types_1.MessageId.multipleSvgFavicons,
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
- status: types_1.CheckerStatus.Ok,
47
- id: types_1.MessageId.svgFaviconDeclared,
48
- }, {
49
- status: types_1.CheckerStatus.Error,
50
- id: types_1.MessageId.noSvgFaviconHref,
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
- status: types_1.CheckerStatus.Ok,
56
- id: types_1.MessageId.svgFaviconDeclared,
57
- }, {
58
- status: types_1.CheckerStatus.Error,
59
- id: types_1.MessageId.svgFavicon404,
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
- status: types_1.CheckerStatus.Ok,
65
- id: types_1.MessageId.svgFaviconDeclared,
66
- }, {
67
- status: types_1.CheckerStatus.Error,
68
- id: types_1.MessageId.svgFaviconCannotGet,
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
+ }, {
70
121
  'https://example.com/the-icon.svg': {
71
122
  status: 403,
72
123
  contentType: 'image/svg+xml'
@@ -75,13 +126,24 @@ test('checkSvgFavicon - svgFaviconDeclared & svgFaviconCannotGet', () => __await
75
126
  }));
76
127
  // For https://github.com/RealFaviconGenerator/core/issues/2
77
128
  test('checkSvgFavicon - Protocol-relative URL', () => __awaiter(void 0, void 0, void 0, function* () {
78
- yield runSvgTest(`<link rel="icon" type="image/svg+xml" href="//example.com/the-icon.svg" />`, [{
79
- status: types_1.CheckerStatus.Ok,
80
- id: types_1.MessageId.svgFaviconDeclared,
81
- }, {
82
- status: types_1.CheckerStatus.Error,
83
- id: types_1.MessageId.svgFaviconCannotGet,
84
- }], {
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
+ }, {
85
147
  'https://example.com/the-icon.svg': {
86
148
  status: 403,
87
149
  contentType: 'image/svg+xml'
@@ -91,18 +153,30 @@ test('checkSvgFavicon - Protocol-relative URL', () => __awaiter(void 0, void 0,
91
153
  test('checkSvgFavicon - svgFaviconDeclared & svgFaviconDownloadable & svgFaviconSquare', () => __awaiter(void 0, void 0, void 0, function* () {
92
154
  const testIconPath = './fixtures/happy-face.svg';
93
155
  const serpIcon = yield (0, helper_1.filePathToString)(testIconPath);
94
- yield runSvgTest(`<link rel="icon" type="image/svg+xml" href="/the-icon.svg" />`, [
95
- {
96
- status: types_1.CheckerStatus.Ok,
97
- id: types_1.MessageId.svgFaviconDeclared,
98
- }, {
99
- status: types_1.CheckerStatus.Ok,
100
- id: types_1.MessageId.svgFaviconDownloadable,
101
- }, {
102
- status: types_1.CheckerStatus.Ok,
103
- id: types_1.MessageId.svgFaviconSquare,
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
+ },
104
178
  }
105
- ], {
179
+ }, {
106
180
  'https://example.com/the-icon.svg': {
107
181
  status: 200,
108
182
  contentType: 'image/svg+xml',
@@ -114,7 +188,7 @@ const runPngTest = (headFragment_2, output_2, ...args_2) => __awaiter(void 0, [h
114
188
  const root = headFragment ? (0, node_html_parser_1.parse)(headFragment) : null;
115
189
  const result = yield (0, desktop_1.checkPngFavicon)('https://example.com/', root, (0, test_helper_1.testFetcher)(fetchDatabase));
116
190
  const filteredMessages = result.messages.map(m => ({ status: m.status, id: m.id }));
117
- expect(filteredMessages).toEqual(output);
191
+ expect(filteredMessages).toEqual(output.messages);
118
192
  });
119
193
  const testIcon16 = './fixtures/16x16.png';
120
194
  const testIcon32 = './fixtures/32x32.png';
@@ -126,16 +200,27 @@ test('checkSvgFavicon - Three PNG icons with different sizes', () => __awaiter(v
126
200
  <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
127
201
  <link rel="icon" type="image/png" sizes="48x48" href="/favicon/favicon-48x48.png">
128
202
  <link rel="icon" type="image/png" sizes="96x96" href="/favicon/favicon-96x96.png">
129
- `, [{
130
- status: types_1.CheckerStatus.Ok,
131
- id: types_1.MessageId.desktopPngFaviconDeclared,
132
- }, {
133
- status: types_1.CheckerStatus.Ok,
134
- id: types_1.MessageId.desktopPngFaviconDownloadable,
135
- }, {
136
- status: types_1.CheckerStatus.Ok,
137
- id: types_1.MessageId.desktopPngFaviconRightSize,
138
- }], {
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
+ }, {
139
224
  'https://example.com/favicon/favicon-16x16.png': {
140
225
  status: 200,
141
226
  contentType: 'image/png',
@@ -1,4 +1,4 @@
1
- import { CheckerMessage, Fetcher } from "../types";
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<CheckerMessage[]>;
4
+ export declare const checkIcoFavicon: (url: string, head: HTMLElement | null, fetcher: Fetcher) => Promise<DesktopSingleReport>;
@@ -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 messages;
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
- const iconUrl = (0, helper_1.mergeUrlAndPath)(url, href);
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
- const images = yield (0, decode_ico_1.default)(iconBuffer);
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
- return messages;
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;
@@ -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;