@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.
@@ -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,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
- 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
+ }, {
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
- status: types_1.CheckerStatus.Ok,
82
- id: types_1.MessageId.svgFaviconDeclared,
83
- }, {
84
- status: types_1.CheckerStatus.Ok,
85
- id: types_1.MessageId.svgFaviconDownloadable,
86
- }, {
87
- status: types_1.CheckerStatus.Ok,
88
- 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
+ },
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
- status: types_1.CheckerStatus.Ok,
116
- id: types_1.MessageId.desktopPngFaviconDeclared,
117
- }, {
118
- status: types_1.CheckerStatus.Ok,
119
- id: types_1.MessageId.desktopPngFaviconDownloadable,
120
- }, {
121
- status: types_1.CheckerStatus.Ok,
122
- id: types_1.MessageId.desktopPngFaviconRightSize,
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',
@@ -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;
@@ -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 declare const checkIcon: (iconUrl: string | undefined, processor: CheckIconProcessor, fetcher: Fetcher, mimeType: string | undefined, expectedWidthHeight?: number) => Promise<string | null>;
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;