@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.
@@ -1,12 +1,12 @@
1
- import { CheckerMessage, CheckerStatus, DesktopFaviconReport, Fetcher, MessageId } from "../types";
1
+ import { CheckerMessage, CheckerStatus, DesktopFaviconReport, DesktopSingleReport, Fetcher, MessageId } from "../types";
2
2
  import { HTMLElement } from 'node-html-parser'
3
3
  import sharp from 'sharp'
4
- import { CheckIconProcessor, checkIcon, fetchFetcher, mergeUrlAndPath, readableStreamToString } from "../helper";
4
+ import { CheckIconProcessor, bufferToDataUrl, checkIcon, fetchFetcher, mergeUrlAndPath, readableStreamToString } from "../helper";
5
5
  import { checkIcoFavicon } from "./ico";
6
6
 
7
7
  export const PngFaviconFileSize = 96;
8
8
 
9
- export const checkSvgFavicon = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<CheckerMessage[]> => {
9
+ export const checkSvgFavicon = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<DesktopSingleReport> => {
10
10
  const messages: CheckerMessage[] = [];
11
11
 
12
12
  if (!head) {
@@ -16,7 +16,10 @@ export const checkSvgFavicon = async (baseUrl: string, head: HTMLElement | null,
16
16
  text: 'No <head> element'
17
17
  });
18
18
 
19
- return messages;
19
+ return {
20
+ messages,
21
+ icon: { content: null, url: null, width: null, height: null }
22
+ };
20
23
  }
21
24
 
22
25
  const svgs = head?.querySelectorAll("link[rel='icon'][type='image/svg+xml']");
@@ -47,20 +50,29 @@ export const checkSvgFavicon = async (baseUrl: string, head: HTMLElement | null,
47
50
  text: 'The SVG markup has no href attribute'
48
51
  });
49
52
  } else {
50
- const iconMessages = await checkSvgFaviconFile(baseUrl, href, fetcher)
51
- return [ ...messages, ...iconMessages ];
53
+ const iconReport = await checkSvgFaviconFile(baseUrl, href, fetcher)
54
+ return {
55
+ messages: [ ...messages, ...iconReport.messages ],
56
+ icon: iconReport.icon
57
+ };
52
58
  }
53
59
  }
54
60
 
55
- return messages;
61
+ return {
62
+ messages,
63
+ icon: { content: null, url: null, width: null, height: null }
64
+ };
56
65
  }
57
66
 
58
- export const checkSvgFaviconFile = async (baseUrl: string, url: string, fetcher: Fetcher): Promise<CheckerMessage[]> => {
67
+ export const checkSvgFaviconFile = async (baseUrl: string, url: string, fetcher: Fetcher): Promise<DesktopSingleReport> => {
59
68
  const messages: CheckerMessage[] = [];
60
69
 
61
70
  const svgUrl = mergeUrlAndPath(baseUrl, url);
62
71
 
63
72
  const res = await fetcher(svgUrl, 'image/svg+xml');
73
+ let content;
74
+ let width: number | null = null;
75
+ let height: number | null = null;
64
76
  if (res.status === 404) {
65
77
  messages.push({
66
78
  status: CheckerStatus.Error,
@@ -80,28 +92,37 @@ export const checkSvgFaviconFile = async (baseUrl: string, url: string, fetcher:
80
92
  text: `The SVG favicon is accessible at \`${url}\``
81
93
  });
82
94
 
83
- const content = await readableStreamToString(res.readableStream);
95
+ content = await readableStreamToString(res.readableStream);
84
96
  const meta = await sharp(Buffer.from(content)).metadata();
97
+ width = meta.width || null;
98
+ height = meta.height || null;
85
99
 
86
- if (meta.width !== meta.height) {
100
+ if (width && height && width !== height) {
87
101
  messages.push({
88
102
  status: CheckerStatus.Error,
89
103
  id: MessageId.svgFaviconNotSquare,
90
- text: `The SVG is not square (${meta.width}x${meta.height})`
104
+ text: `The SVG is not square (${width}x${height})`
91
105
  });
92
106
  } else {
93
107
  messages.push({
94
108
  status: CheckerStatus.Ok,
95
109
  id: MessageId.svgFaviconSquare,
96
- text: `The SVG is square (${meta.width}x${meta.height})`
110
+ text: `The SVG is square (${width}x${height})`
97
111
  });
98
112
  }
99
113
  }
100
114
 
101
- return messages;
115
+ return {
116
+ messages,
117
+ icon: {
118
+ content: content ? await bufferToDataUrl(Buffer.from(content), 'image/svg+xml') : null,
119
+ url: svgUrl,
120
+ width, height
121
+ }
122
+ };
102
123
  }
103
124
 
104
- export const checkPngFavicon = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<DesktopFaviconReport> => {
125
+ export const checkPngFavicon = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<DesktopSingleReport> => {
105
126
  const messages: CheckerMessage[] = [];
106
127
 
107
128
  if (!head) {
@@ -111,7 +132,7 @@ export const checkPngFavicon = async (baseUrl: string, head: HTMLElement | null,
111
132
  text: 'No <head> element'
112
133
  });
113
134
 
114
- return { messages, icon: null };
135
+ return { messages, icon: { content: null, url: null, width: null, height: null } };
115
136
  }
116
137
 
117
138
  const icons = head?.querySelectorAll("link[rel='icon'][type='image/png']");
@@ -193,15 +214,21 @@ export const checkPngFavicon = async (baseUrl: string, head: HTMLElement | null,
193
214
  }
194
215
  }
195
216
 
196
- return { messages, icon: null };
217
+ return { messages, icon: { content: null, url: null, width: null, height: null } };
197
218
  }
198
219
 
199
220
  export const checkDesktopFavicon = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<DesktopFaviconReport> => {
200
- const svgMessages = await checkSvgFavicon(baseUrl, head, fetcher);
221
+ const svgReport = await checkSvgFavicon(baseUrl, head, fetcher);
201
222
  const pngReport = await checkPngFavicon(baseUrl, head, fetcher);
202
223
  const icoReport = await checkIcoFavicon(baseUrl, head, fetcher);
224
+
203
225
  return {
204
- messages: [ ...svgMessages, ...pngReport.messages, ...icoReport ],
205
- icon: pngReport.icon
226
+ messages: [ ...svgReport.messages, ...pngReport.messages, ...icoReport.messages ],
227
+ icon: pngReport.icon ? pngReport.icon.content : null,
228
+ icons: {
229
+ png: pngReport.icon,
230
+ ico: icoReport.icon,
231
+ svg: svgReport.icon
232
+ }
206
233
  };
207
234
  }
@@ -1,11 +1,11 @@
1
- import { CheckerMessage, CheckerStatus, Fetcher, MessageId } from "../types";
1
+ import { CheckedIcon, CheckerMessage, CheckerStatus, DesktopSingleReport, Fetcher, MessageId } from "../types";
2
2
  import { HTMLElement } from 'node-html-parser'
3
- import { mergeUrlAndPath, readableStreamToBuffer } from "../helper";
3
+ import { bufferToDataUrl, mergeUrlAndPath, readableStreamToBuffer } from "../helper";
4
4
  import decodeIco from "decode-ico";
5
5
 
6
6
  export const IcoFaviconSizes = [ 48, 32, 16 ];
7
7
 
8
- export const checkIcoFavicon = async (url: string, head: HTMLElement | null, fetcher: Fetcher): Promise<CheckerMessage[]> => {
8
+ export const checkIcoFavicon = async (url: string, head: HTMLElement | null, fetcher: Fetcher): Promise<DesktopSingleReport> => {
9
9
  const messages: CheckerMessage[] = [];
10
10
 
11
11
  if (!head) {
@@ -15,13 +15,19 @@ export const checkIcoFavicon = async (url: string, head: HTMLElement | null, fet
15
15
  text: 'No <head> element'
16
16
  });
17
17
 
18
- return messages;
18
+ return {
19
+ messages,
20
+ icon : { content: null, url: null, width: null, height: null }
21
+ };
19
22
  }
20
23
 
21
24
  const icos =
22
25
  head.querySelectorAll('link[rel="shortcut icon"]') ||
23
26
  head.querySelectorAll('link[rel="icon"][type="image/x-icon"]');
24
27
 
28
+ let iconUrl: string | null = null;
29
+ let images;
30
+
25
31
  if (icos.length === 0) {
26
32
  messages.push({
27
33
  status: CheckerStatus.Error,
@@ -49,7 +55,7 @@ export const checkIcoFavicon = async (url: string, head: HTMLElement | null, fet
49
55
  text: 'The ICO markup has no href attribute'
50
56
  });
51
57
  } else {
52
- const iconUrl = mergeUrlAndPath(url, href);
58
+ iconUrl = mergeUrlAndPath(url, href);
53
59
  const iconResponse = await fetcher(iconUrl, 'image/x-icon');
54
60
  if (iconResponse.status === 404) {
55
61
  messages.push({
@@ -71,7 +77,7 @@ export const checkIcoFavicon = async (url: string, head: HTMLElement | null, fet
71
77
  });
72
78
 
73
79
  const iconBuffer = await readableStreamToBuffer(iconResponse.readableStream);
74
- const images = await decodeIco(iconBuffer);
80
+ images = await decodeIco(iconBuffer);
75
81
 
76
82
  const imageSizes = images.map(image => `${image.width}x${image.height}`);
77
83
 
@@ -106,5 +112,23 @@ export const checkIcoFavicon = async (url: string, head: HTMLElement | null, fet
106
112
  }
107
113
  }
108
114
 
109
- return messages;
115
+ let content: string | null = null;
116
+ const theIcon: CheckedIcon = {
117
+ content: null,
118
+ url: iconUrl,
119
+ width: null,
120
+ height: null
121
+ };
122
+ if (images) {
123
+ const image = images[0];
124
+ const mimeType = (image.type === "bmp") ? "image/bmp" : "image/png";
125
+ theIcon.content = await bufferToDataUrl(Buffer.from(image.data), mimeType);
126
+ theIcon.width = image.width;
127
+ theIcon.height = image.height;
128
+ }
129
+
130
+ return {
131
+ messages,
132
+ icon: theIcon,
133
+ };
110
134
  }
@@ -0,0 +1,82 @@
1
+ import { checkRobotsFile, getRobotsFileUrl } from "./google";
2
+ import { stringToReadableStream } from "./helper";
3
+ import { testFetcher } from "./test-helper";
4
+ import { CheckerMessage, CheckerStatus, DesktopFaviconReport, MessageId } from "./types";
5
+
6
+ test('getRobotsFileUrl', () => {
7
+ expect(getRobotsFileUrl('https://example.com')).toEqual('https://example.com/robots.txt');
8
+ expect(getRobotsFileUrl('https://example.com/some-path')).toEqual('https://example.com/robots.txt');
9
+ });
10
+
11
+ const runRobotsTest = async (urls: string[], robotsFile: string | null, messages: Pick<CheckerMessage, 'id' | 'status'>[]) => {
12
+ const report = await checkRobotsFile(
13
+ 'https://example.com',
14
+ urls,
15
+ testFetcher(robotsFile ? {
16
+ 'https://example.com/robots.txt': {
17
+ status: 200,
18
+ contentType: 'text/plain',
19
+ readableStream: await stringToReadableStream(robotsFile)
20
+ }
21
+ } : {})
22
+ );
23
+
24
+ const filteredMessages = report.map(m => ({ status: m.status, id: m.id }));
25
+ expect(filteredMessages).toEqual(messages);
26
+ }
27
+
28
+ test('checkRobotsFile - No robots file', async () => {
29
+ await runRobotsTest(
30
+ [ 'https://example.com/favicon.png' ],
31
+ null,
32
+ [
33
+ {
34
+ status: CheckerStatus.Ok,
35
+ id: MessageId.googleNoRobotsFile
36
+ }
37
+ ]
38
+ );
39
+ });
40
+
41
+ test('checkRobotsFile - PNG favicon is accessible', async () => {
42
+ await runRobotsTest(
43
+ [ 'https://example.com/favicon.png' ],
44
+ `
45
+ User-agent: *
46
+ Allow: /`,
47
+ [
48
+ {
49
+ status: CheckerStatus.Ok,
50
+ id: MessageId.googleRobotsFileFound
51
+ },
52
+ {
53
+ status: CheckerStatus.Ok,
54
+ id: MessageId.googlePngIconAllowedByRobots
55
+ }
56
+ ]
57
+ );
58
+ });
59
+
60
+ test('checkRobotsFile - PNG favicon is *not* accessible', async () => {
61
+ await runRobotsTest(
62
+ [ 'https://example.com/favicon.png' ],
63
+ `
64
+ # *
65
+ User-agent: *
66
+ Allow: /
67
+
68
+ User-agent: Googlebot-Image
69
+ Disallow: /*.png
70
+ `,
71
+ [
72
+ {
73
+ status: CheckerStatus.Ok,
74
+ id: MessageId.googleRobotsFileFound
75
+ },
76
+ {
77
+ status: CheckerStatus.Error,
78
+ id: MessageId.googlePngIconBlockedByRobots
79
+ }
80
+ ]
81
+ );
82
+ });
package/src/google.ts ADDED
@@ -0,0 +1,103 @@
1
+ import robotsParser from "robots-parser";
2
+ import { checkDesktopFavicon } from "./desktop/desktop";
3
+ import { fetchFetcher, readableStreamToBuffer, readableStreamToString } from "./helper";
4
+ import { CheckedIcon, CheckerMessage, CheckerStatus, DesktopFaviconReport, Fetcher, GoogleReport, MessageId } from "./types";
5
+ import { HTMLElement } from "node-html-parser";
6
+
7
+ export const GoogleBot = 'Googlebot';
8
+ export const GoogleImageBot = 'Googlebot-Image';
9
+
10
+ export const getRobotsFileUrl = (baseUrl: string): string => {
11
+ try {
12
+ const url = new URL(baseUrl);
13
+ url.pathname = '/robots.txt';
14
+ return url.toString();
15
+ } catch (error) {
16
+ throw new Error(`Invalid URL ${baseUrl}`);
17
+ }
18
+ }
19
+
20
+ export const checkRobotsFile = async (baseUrl: string, iconUrls: string[], fetcher: Fetcher = fetchFetcher): Promise<CheckerMessage[]> => {
21
+ const robotsUrl = getRobotsFileUrl(baseUrl);
22
+ const robotsResponse = await fetcher(robotsUrl);
23
+
24
+ const messages: CheckerMessage[] = [];
25
+
26
+ if (robotsResponse.status === 200) {
27
+ messages.push({
28
+ status: CheckerStatus.Ok,
29
+ text: `robots.txt file found at ${robotsUrl}`,
30
+ id: MessageId.googleRobotsFileFound
31
+ });
32
+
33
+ const robotsFile = robotsResponse.readableStream ? await readableStreamToString(robotsResponse.readableStream) : '';
34
+
35
+ const robots = robotsParser(robotsUrl, robotsFile);
36
+
37
+ iconUrls.forEach(url => {
38
+ if (url) {
39
+ if (robots.isAllowed(url, GoogleImageBot)) {
40
+ messages.push({
41
+ status: CheckerStatus.Ok,
42
+ text: `Access to \`${url}\` is allowed for \`${GoogleImageBot}\``,
43
+ id: MessageId.googlePngIconAllowedByRobots
44
+ });
45
+ } else {
46
+ const line = robots.getMatchingLineNumber(url, GoogleImageBot);
47
+ messages.push({
48
+ status: CheckerStatus.Error,
49
+ text: `Access to \`${url}\` is blocked for \`${GoogleImageBot}\` (\`${robotsUrl}\`, line ${line})`,
50
+ id: MessageId.googlePngIconBlockedByRobots
51
+ });
52
+ }
53
+ }
54
+ });
55
+ } else {
56
+ messages.push({
57
+ status: CheckerStatus.Ok,
58
+ 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.`,
59
+ id: MessageId.googleNoRobotsFile
60
+ });
61
+ }
62
+
63
+ return messages;
64
+ }
65
+
66
+ export const checkGoogleFaviconFromDesktopReport = async (baseUrl: string, desktopReport: DesktopFaviconReport, fetcher: Fetcher = fetchFetcher): Promise<GoogleReport> => {
67
+ const allIcons: CheckedIcon[] = [
68
+ desktopReport.icons.png,
69
+ desktopReport.icons.ico,
70
+ desktopReport.icons.svg
71
+ ].filter((i): i is CheckedIcon => !!i);
72
+
73
+ const allIconUrls: string[] = allIcons.map(i => i.url).filter((i): i is string => !!i);
74
+
75
+ const robotsMessages = await checkRobotsFile(baseUrl, allIconUrls, fetcher);
76
+
77
+ const messages: CheckerMessage[] = [ ...desktopReport.messages, ...robotsMessages ];
78
+
79
+ let finalIcon: string | null = null;
80
+ let icons: CheckedIcon[] = [];
81
+ let maxWidth = 0;
82
+
83
+ allIcons.forEach(icon => {
84
+ if (icon.content && icon.width && icon.height && icon.url) {
85
+ icons.push(icon);
86
+ if (icon.width > maxWidth) {
87
+ finalIcon = icon.content;
88
+ maxWidth = icon.width;
89
+ }
90
+ }
91
+ });
92
+
93
+ return {
94
+ messages,
95
+ icon: finalIcon,
96
+ icons
97
+ }
98
+ }
99
+
100
+ export const checkGoogleFavicon = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<GoogleReport> => {
101
+ const desktopReport = await checkDesktopFavicon(baseUrl, head, fetcher);
102
+ return checkGoogleFaviconFromDesktopReport(baseUrl, desktopReport, fetcher);
103
+ }
@@ -1,5 +1,5 @@
1
1
  import sharp from "sharp";
2
- import { CheckIconProcessor, checkIcon, filePathToReadableStream, mergeUrlAndPath, parseSizesAttribute } from "./helper";
2
+ import { CheckIconProcessor, bufferToDataUrl, checkIcon, filePathToDataUrl, filePathToReadableStream, filePathToString, mergeUrlAndPath, parseSizesAttribute } from "./helper";
3
3
  import { testFetcher } from "./test-helper";
4
4
 
5
5
  const getTestProcessor = () => {
@@ -30,7 +30,12 @@ test('checkIcon - noHref', async () => {
30
30
 
31
31
  test('checkIcon - icon404', async () => {
32
32
  const processor = getTestProcessor();
33
- expect(await checkIcon('/does-not-exist.png', processor.processor, testFetcher({}), 'image/png')).toBeNull();
33
+ expect(await checkIcon('/does-not-exist.png', processor.processor, testFetcher({}), 'image/png')).toEqual({
34
+ content: null,
35
+ url: '/does-not-exist.png',
36
+ width: null,
37
+ height: null
38
+ });
34
39
  expect(processor.messages).toEqual(['icon404']);
35
40
  })
36
41
 
@@ -41,7 +46,12 @@ test('checkIcon - icon404', async () => {
41
46
  contentType: 'image/png',
42
47
  status: 500
43
48
  }
44
- }), 'image/png')).toBeNull();
49
+ }), 'image/png')).toEqual({
50
+ content: null,
51
+ url: '/bad-icon.png',
52
+ width: null,
53
+ height: null
54
+ });
45
55
  expect(processor.messages).toEqual(['cannotGet 500']);
46
56
  })
47
57
 
@@ -100,7 +110,12 @@ test('checkIcon - downloadable & notSquare', async () => {
100
110
  contentType: 'image/png',
101
111
  readableStream: await filePathToReadableStream(nonSquareIcon)
102
112
  }
103
- }), 'image/png', 500)).toBeNull();
113
+ }), 'image/png', 500)).toEqual({
114
+ content: await filePathToDataUrl(nonSquareIcon),
115
+ url: '/non-square-icon.png',
116
+ width: 240,
117
+ height: 180
118
+ });
104
119
  expect(processor.messages).toEqual([
105
120
  'downloadable',
106
121
  'notSquare 240x180'
package/src/helper.ts CHANGED
@@ -24,9 +24,12 @@ export const filePathToString = async (path: string): Promise<string> => (
24
24
  )
25
25
 
26
26
  export const stringToReadableStream = (str: string): ReadableStream => {
27
+ const encoder = new TextEncoder();
28
+ const uint8Array = encoder.encode(str);
29
+
27
30
  return new ReadableStream({
28
31
  start(controller) {
29
- controller.enqueue(str);
32
+ controller.enqueue(uint8Array);
30
33
  controller.close();
31
34
  }
32
35
  });
@@ -100,13 +103,20 @@ export const pathToMimeType = (path: string): string => {
100
103
  }
101
104
  }
102
105
 
106
+ export type CheckIconOutput = {
107
+ content: string | null,
108
+ url: string | null,
109
+ width: number | null,
110
+ height: number | null,
111
+ }
112
+
103
113
  export const checkIcon = async (
104
114
  iconUrl: string | undefined,
105
115
  processor: CheckIconProcessor,
106
116
  fetcher: Fetcher,
107
117
  mimeType: string | undefined,
108
118
  expectedWidthHeight?: number
109
- ): Promise<string | null> => {
119
+ ): Promise<CheckIconOutput | null> => {
110
120
  if (!iconUrl) {
111
121
  processor.noHref();
112
122
  return null;
@@ -120,15 +130,16 @@ export const checkIcon = async (
120
130
  } else if (res.readableStream) {
121
131
  processor.downloadable();
122
132
 
123
- const content = await readableStreamToBuffer(res.readableStream);
124
- const meta = await sharp(content).metadata();
133
+ const rawContent = await readableStreamToBuffer(res.readableStream);
134
+ const meta = await sharp(rawContent).metadata();
125
135
 
126
136
  const contentType = res.contentType || pathToMimeType(iconUrl);
127
137
 
138
+ const content = await bufferToDataUrl(rawContent, contentType);
139
+
128
140
  if (meta.width && meta.height) {
129
141
  if (meta.width !== meta.height) {
130
142
  processor.notSquare(meta.width, meta.height);
131
- return null;
132
143
  } else {
133
144
  processor.square(meta.width);
134
145
 
@@ -142,10 +153,20 @@ export const checkIcon = async (
142
153
  }
143
154
  }
144
155
 
145
- return bufferToDataUrl(content, contentType);
156
+ return {
157
+ content,
158
+ url: iconUrl,
159
+ width: meta.width || null,
160
+ height: meta.height || null
161
+ }
146
162
  }
147
163
 
148
- return null;
164
+ return {
165
+ content: null,
166
+ url: iconUrl,
167
+ width: null,
168
+ height: null
169
+ };
149
170
  }
150
171
 
151
172
  export const mergeUrlAndPath = (baseUrl: string, absoluteOrRelativePath: string): string => {
@@ -189,6 +210,13 @@ export const bufferToDataUrl = (buffer: Buffer, mimeType: string): string => {
189
210
  return `data:${mimeType};base64,${buffer.toString('base64')}`;
190
211
  }
191
212
 
213
+ export const filePathToDataUrl = async (filePath: string): Promise<string> => {
214
+ const readStream = await filePathToReadableStream(filePath);
215
+ const rawContent = await readableStreamToBuffer(readStream);
216
+ const contentType = pathToMimeType(filePath);
217
+ return bufferToDataUrl(rawContent, contentType);
218
+ }
219
+
192
220
  export const fetchFetcher: Fetcher = async (url, contentType) => {
193
221
  const res = await fetch(url, {
194
222
  headers: {
package/src/touch-icon.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CheckerMessage, CheckerStatus, Fetcher, MessageId, TouchIconIconReport, TouchIconReport, TouchIconTitleReport } from "./types";
2
2
  import { HTMLElement } from 'node-html-parser'
3
- import { CheckIconProcessor, checkIcon, fetchFetcher, mergeUrlAndPath } from "./helper";
3
+ import { CheckIconOutput, CheckIconProcessor, checkIcon, fetchFetcher, mergeUrlAndPath } from "./helper";
4
4
 
5
5
  export const checkTouchIconTitle = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<TouchIconTitleReport> => {
6
6
  const messages: CheckerMessage[] = [];
@@ -59,7 +59,7 @@ export const checkTouchIconTitle = async (baseUrl: string, head: HTMLElement | n
59
59
 
60
60
  export const checkTouchIconIcon = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<TouchIconIconReport> => {
61
61
  const messages: CheckerMessage[] = [];
62
- let touchIcon: string | null = null;
62
+ let touchIcon: CheckIconOutput | null = null;
63
63
 
64
64
  if (!head) {
65
65
  messages.push({
@@ -176,7 +176,7 @@ export const checkTouchIconIcon = async (baseUrl: string, head: HTMLElement | nu
176
176
  undefined
177
177
  );
178
178
 
179
- return { messages, touchIcon };
179
+ return { messages, touchIcon: touchIcon ? touchIcon.content : null };
180
180
  }
181
181
 
182
182
  export const getDuplicatedSizes = (sizes: (string | undefined)[]): (string | undefined)[] => {
package/src/types.ts CHANGED
@@ -79,7 +79,16 @@ export enum MessageId {
79
79
  manifestIconNotSquare,
80
80
  manifestIconRightSize,
81
81
  manifestIconSquare,
82
- manifestIconWrongSize
82
+ manifestIconWrongSize,
83
+
84
+ googleNoRobotsFile,
85
+ googleRobotsFileFound,
86
+ googleIcoBlockedByRobots,
87
+ googleIcoAllowedByRobots,
88
+ googleSvgIconBlockedByRobots,
89
+ googleSvgIconAllowedByRobots,
90
+ googlePngIconBlockedByRobots,
91
+ googlePngIconAllowedByRobots,
83
92
  }
84
93
 
85
94
  export type CheckerMessage = {
@@ -96,9 +105,26 @@ export type FetchResponse = {
96
105
 
97
106
  export type Fetcher = (url: string, contentType?: string) => Promise<FetchResponse>;
98
107
 
108
+ export type CheckedIcon = {
109
+ content: string | null,
110
+ url: string | null,
111
+ width: number | null,
112
+ height: number | null
113
+ }
114
+
115
+ export type DesktopSingleReport = {
116
+ messages: CheckerMessage[],
117
+ icon: CheckedIcon | null,
118
+ }
119
+
99
120
  export type DesktopFaviconReport = {
100
121
  messages: CheckerMessage[],
101
122
  icon: string | null,
123
+ icons: {
124
+ png: CheckedIcon | null,
125
+ ico: CheckedIcon | null,
126
+ svg: CheckedIcon | null,
127
+ }
102
128
  }
103
129
 
104
130
  export type TouchIconTitleReport = {
@@ -127,3 +153,9 @@ export type FaviconReport = {
127
153
  }
128
154
 
129
155
  export type TouchIconReport = TouchIconIconReport & TouchIconTitleReport;
156
+
157
+ export type GoogleReport = {
158
+ messages: CheckerMessage[],
159
+ icon: string | null,
160
+ icons: CheckedIcon[]
161
+ }
@@ -1,6 +1,6 @@
1
1
  import { HTMLElement } from "node-html-parser";
2
2
  import { CheckerMessage, CheckerStatus, Fetcher, MessageId, WebAppManifestReport } from "./types";
3
- import { CheckIconProcessor, checkIcon, fetchFetcher, mergeUrlAndPath, pathToMimeType } from "./helper";
3
+ import { CheckIconOutput, CheckIconProcessor, checkIcon, fetchFetcher, mergeUrlAndPath, pathToMimeType } from "./helper";
4
4
 
5
5
  export const checkWebAppManifest = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<WebAppManifestReport> => {
6
6
  const messages: CheckerMessage[] = [];
@@ -101,7 +101,7 @@ const readableStreamToJson = async (stream: ReadableStream): Promise<any> => {
101
101
 
102
102
  export const checkWebAppManifestFile = async (manifest: any, baseUrl: string, fetcher: Fetcher): Promise<WebAppManifestReport> => {
103
103
  const messages: CheckerMessage[] = [];
104
- let icon = null;
104
+ let icon: CheckIconOutput | null = null;
105
105
 
106
106
  const name = manifest.name || undefined;
107
107
  if (!name) {
@@ -257,5 +257,5 @@ export const checkWebAppManifestFile = async (manifest: any, baseUrl: string, fe
257
257
  }
258
258
  }
259
259
 
260
- return { messages, name, shortName, backgroundColor, themeColor, icon };
260
+ return { messages, name, shortName, backgroundColor, themeColor, icon: icon ? icon.content : null };
261
261
  }
@@ -1,4 +0,0 @@
1
- import { HTMLElement } from "node-html-parser";
2
- import { Fetcher, WebManifestReport } from "./types";
3
- export declare const checkWebAppManifest: (baseUrl: string, head: HTMLElement | null, fetcher?: Fetcher) => Promise<WebManifestReport>;
4
- export declare const checkWebManifestFile: (manifest: any, baseUrl: string, fetcher: Fetcher) => Promise<WebManifestReport>;