@realfavicongenerator/check-favicon 0.4.18 → 0.5.2
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/README.md +0 -0
- package/dist/desktop/ico.js +73 -43
- package/dist/desktop/ico.test.d.ts +1 -0
- package/dist/desktop/ico.test.js +263 -0
- package/dist/touch-icon.js +7 -7
- package/dist/touch-icon.test.js +23 -5
- package/dist/types.d.ts +67 -66
- package/dist/types.js +65 -64
- package/dist/web-app-manifest.js +1 -1
- package/dist/web-app-manifest.test.js +6 -1
- package/fixtures/16x16.png +0 -0
- package/fixtures/180x180.png +0 -0
- package/fixtures/192x192.png +0 -0
- package/fixtures/32x32.png +0 -0
- package/fixtures/48x48.png +0 -0
- package/fixtures/512x512.png +0 -0
- package/fixtures/96x96.png +0 -0
- package/fixtures/happy-face.svg +0 -0
- package/fixtures/logo-transparent.png +0 -0
- package/fixtures/non-square.png +0 -0
- package/fixtures/simple-ico.ico +0 -0
- package/jest.config.js +0 -0
- package/package.json +2 -2
- package/src/check.ts +0 -0
- package/src/desktop/desktop.test.ts +0 -0
- package/src/desktop/desktop.ts +0 -0
- package/src/desktop/ico.test.ts +280 -0
- package/src/desktop/ico.ts +77 -47
- package/src/google.test.ts +0 -0
- package/src/google.ts +0 -0
- package/src/helper.test.ts +0 -0
- package/src/helper.ts +0 -0
- package/src/index.ts +0 -0
- package/src/test-helper.ts +0 -0
- package/src/touch-icon.test.ts +25 -8
- package/src/touch-icon.ts +7 -7
- package/src/types.ts +3 -2
- package/src/web-app-manifest.test.ts +6 -1
- package/src/web-app-manifest.ts +1 -1
- package/tsconfig.json +0 -0
package/src/desktop/ico.ts
CHANGED
|
@@ -28,20 +28,16 @@ export const checkIcoFavicon = async (url: string, head: HTMLElement | null, fet
|
|
|
28
28
|
|
|
29
29
|
let iconUrl: string | null = null;
|
|
30
30
|
let images;
|
|
31
|
+
let isDeclared = false;
|
|
31
32
|
|
|
32
|
-
if (icos.length
|
|
33
|
-
messages.push({
|
|
34
|
-
status: CheckerStatus.Error,
|
|
35
|
-
id: MessageId.noIcoFavicon,
|
|
36
|
-
text: 'There is no ICO favicon'
|
|
37
|
-
});
|
|
38
|
-
} else if (icos.length > 1) {
|
|
33
|
+
if (icos.length > 1) {
|
|
39
34
|
messages.push({
|
|
40
35
|
status: CheckerStatus.Error,
|
|
41
36
|
id: MessageId.multipleIcoFavicons,
|
|
42
37
|
text: `There are ${icos.length} ICO favicons`
|
|
43
38
|
});
|
|
44
|
-
} else {
|
|
39
|
+
} else if (icos.length === 1) {
|
|
40
|
+
isDeclared = true;
|
|
45
41
|
messages.push({
|
|
46
42
|
status: CheckerStatus.Ok,
|
|
47
43
|
id: MessageId.icoFaviconDeclared,
|
|
@@ -57,63 +53,97 @@ export const checkIcoFavicon = async (url: string, head: HTMLElement | null, fet
|
|
|
57
53
|
});
|
|
58
54
|
} else {
|
|
59
55
|
iconUrl = mergeUrlAndPath(url, href);
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
// No declared ICO favicon, try the implicit /favicon.ico convention
|
|
59
|
+
iconUrl = mergeUrlAndPath(url, '/favicon.ico');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// If we have an iconUrl (either from declaration or implicit), try to fetch it
|
|
63
|
+
if (iconUrl) {
|
|
64
|
+
const iconResponse = await fetcher(iconUrl, 'image/x-icon');
|
|
65
|
+
if (iconResponse.status === 404) {
|
|
66
|
+
if (isDeclared) {
|
|
62
67
|
messages.push({
|
|
63
68
|
status: CheckerStatus.Error,
|
|
64
69
|
id: MessageId.icoFavicon404,
|
|
65
70
|
text: `ICO favicon not found at ${iconUrl}`
|
|
66
71
|
});
|
|
67
|
-
} else
|
|
72
|
+
} else {
|
|
73
|
+
// Implicit favicon not found, report no ICO favicon
|
|
74
|
+
messages.push({
|
|
75
|
+
status: CheckerStatus.Error,
|
|
76
|
+
id: MessageId.noIcoFavicon,
|
|
77
|
+
text: 'There is no ICO favicon'
|
|
78
|
+
});
|
|
79
|
+
iconUrl = null;
|
|
80
|
+
}
|
|
81
|
+
} else if (iconResponse.status >= 300 || !iconResponse.readableStream) {
|
|
82
|
+
if (isDeclared) {
|
|
68
83
|
messages.push({
|
|
69
84
|
status: CheckerStatus.Error,
|
|
70
85
|
id: MessageId.icoFaviconCannotGet,
|
|
71
86
|
text: `Error fetching ICO favicon at ${iconUrl} (status ${iconResponse.status})`
|
|
72
87
|
});
|
|
73
88
|
} else {
|
|
89
|
+
// Implicit favicon cannot be fetched, report no ICO favicon
|
|
90
|
+
messages.push({
|
|
91
|
+
status: CheckerStatus.Error,
|
|
92
|
+
id: MessageId.noIcoFavicon,
|
|
93
|
+
text: 'There is no ICO favicon'
|
|
94
|
+
});
|
|
95
|
+
iconUrl = null;
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
if (!isDeclared) {
|
|
74
99
|
messages.push({
|
|
75
100
|
status: CheckerStatus.Ok,
|
|
76
|
-
id: MessageId.
|
|
77
|
-
text: 'ICO favicon found'
|
|
101
|
+
id: MessageId.icoFaviconImplicitInRoot,
|
|
102
|
+
text: 'An implicit ICO favicon is found at /favicon.ico'
|
|
78
103
|
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
messages.push({
|
|
107
|
+
status: CheckerStatus.Ok,
|
|
108
|
+
id: MessageId.icoFaviconDownloadable,
|
|
109
|
+
text: 'ICO favicon found'
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const iconBuffer = await readableStreamToBuffer(iconResponse.readableStream);
|
|
113
|
+
images = decodeIco(iconBuffer);
|
|
114
|
+
|
|
115
|
+
const imageSizes = images.map(image => `${image.width}x${image.height}`);
|
|
116
|
+
|
|
117
|
+
const expectedSizes = IcoFaviconSizes.map(size => `${size}x${size}`);
|
|
118
|
+
|
|
119
|
+
const extraSizes = imageSizes.filter(size => !expectedSizes.includes(size));
|
|
120
|
+
if (extraSizes.length > 0) {
|
|
121
|
+
messages.push({
|
|
122
|
+
status: CheckerStatus.Warning,
|
|
123
|
+
id: MessageId.icoFaviconExtraSizes,
|
|
124
|
+
text: `Extra sizes found in ICO favicon: ${extraSizes.join(', ')}`
|
|
125
|
+
});
|
|
126
|
+
}
|
|
79
127
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
})
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const missingSizes = expectedSizes.filter(size => !imageSizes.includes(size));
|
|
97
|
-
if (missingSizes.length > 0) {
|
|
98
|
-
messages.push({
|
|
99
|
-
status: CheckerStatus.Warning,
|
|
100
|
-
id: MessageId.icoFaviconMissingSizes,
|
|
101
|
-
text: `Missing sizes in ICO favicon: ${missingSizes.join(', ')}`
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (extraSizes.length === 0 && missingSizes.length === 0) {
|
|
106
|
-
messages.push({
|
|
107
|
-
status: CheckerStatus.Ok,
|
|
108
|
-
id: MessageId.icoFaviconExpectedSizes,
|
|
109
|
-
text: `The ICO favicon has the expected sizes (${imageSizes.join(', ')})`
|
|
110
|
-
});
|
|
111
|
-
}
|
|
128
|
+
const missingSizes = expectedSizes.filter(size => !imageSizes.includes(size));
|
|
129
|
+
if (missingSizes.length > 0) {
|
|
130
|
+
messages.push({
|
|
131
|
+
status: CheckerStatus.Warning,
|
|
132
|
+
id: MessageId.icoFaviconMissingSizes,
|
|
133
|
+
text: `Missing sizes in ICO favicon: ${missingSizes.join(', ')}`
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (extraSizes.length === 0 && missingSizes.length === 0) {
|
|
138
|
+
messages.push({
|
|
139
|
+
status: CheckerStatus.Ok,
|
|
140
|
+
id: MessageId.icoFaviconExpectedSizes,
|
|
141
|
+
text: `The ICO favicon has the expected sizes (${imageSizes.join(', ')})`
|
|
142
|
+
});
|
|
112
143
|
}
|
|
113
144
|
}
|
|
114
145
|
}
|
|
115
146
|
|
|
116
|
-
let content: string | null = null;
|
|
117
147
|
const theIcon: CheckedIcon = {
|
|
118
148
|
content: null,
|
|
119
149
|
url: iconUrl,
|
|
@@ -123,7 +153,7 @@ export const checkIcoFavicon = async (url: string, head: HTMLElement | null, fet
|
|
|
123
153
|
if (images) {
|
|
124
154
|
const image = images[0];
|
|
125
155
|
const mimeType = (image.type === "bmp") ? "image/bmp" : "image/png";
|
|
126
|
-
theIcon.content =
|
|
156
|
+
theIcon.content = bufferToDataUrl(Buffer.from(image.data.buffer, image.data.byteOffset, image.data.byteLength), mimeType);
|
|
127
157
|
theIcon.width = image.width;
|
|
128
158
|
theIcon.height = image.height;
|
|
129
159
|
}
|
package/src/google.test.ts
CHANGED
|
File without changes
|
package/src/google.ts
CHANGED
|
File without changes
|
package/src/helper.test.ts
CHANGED
|
File without changes
|
package/src/helper.ts
CHANGED
|
File without changes
|
package/src/index.ts
CHANGED
|
File without changes
|
package/src/test-helper.ts
CHANGED
|
File without changes
|
package/src/touch-icon.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parse } from 'node-html-parser'
|
|
2
|
-
import { CheckerMessage, CheckerStatus, FetchResponse, MessageId } from "./types";
|
|
2
|
+
import { CheckedIcon, CheckerMessage, CheckerStatus, FetchResponse, MessageId } from "./types";
|
|
3
3
|
import { checkTouchIcon, checkTouchIconIcon, checkTouchIconTitle, getDuplicatedSizes } from "./touch-icon";
|
|
4
4
|
import { testFetcher } from './test-helper';
|
|
5
5
|
import { bufferToDataUrl, filePathToReadableStream, readableStreamToBuffer } from './helper';
|
|
@@ -7,7 +7,7 @@ import { bufferToDataUrl, filePathToReadableStream, readableStreamToBuffer } fro
|
|
|
7
7
|
type TestOutput = {
|
|
8
8
|
messages: Pick<CheckerMessage, 'id' | 'status'>[],
|
|
9
9
|
appTitle?: string,
|
|
10
|
-
|
|
10
|
+
icon?: CheckedIcon | null,
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const runCheckTouchIconTitleTest = async (
|
|
@@ -67,10 +67,10 @@ const runCheckTouchIconTest = async (
|
|
|
67
67
|
const filteredMessages = result.messages.map(m => ({ status: m.status, id: m.id }));
|
|
68
68
|
expect({
|
|
69
69
|
messages: filteredMessages,
|
|
70
|
-
|
|
70
|
+
icon: result.icon
|
|
71
71
|
}).toEqual({
|
|
72
72
|
...output,
|
|
73
|
-
|
|
73
|
+
icon: output.icon || null
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -98,7 +98,13 @@ test('checkTouchIcon - multipleTouchIcon - no size', async () => {
|
|
|
98
98
|
}, {
|
|
99
99
|
status: CheckerStatus.Error,
|
|
100
100
|
id: MessageId.duplicatedTouchIconSizes,
|
|
101
|
-
}]
|
|
101
|
+
}], icon: {
|
|
102
|
+
content: null,
|
|
103
|
+
url: 'https://example.com/some-icon.png',
|
|
104
|
+
width: null,
|
|
105
|
+
height: null
|
|
106
|
+
},
|
|
107
|
+
}, {
|
|
102
108
|
'https://example.com/some-icon.png': {
|
|
103
109
|
status: 200,
|
|
104
110
|
contentType: 'image/png',
|
|
@@ -122,7 +128,14 @@ test('checkTouchIcon - multipleTouchIcon - specific size', async () => {
|
|
|
122
128
|
}, {
|
|
123
129
|
status: CheckerStatus.Error,
|
|
124
130
|
id: MessageId.duplicatedTouchIconSizes,
|
|
125
|
-
}]
|
|
131
|
+
}],
|
|
132
|
+
icon: {
|
|
133
|
+
content: null,
|
|
134
|
+
url: 'https://example.com/some-icon.png',
|
|
135
|
+
width: null,
|
|
136
|
+
height: null
|
|
137
|
+
},
|
|
138
|
+
}, {
|
|
126
139
|
'https://example.com/some-icon.png': {
|
|
127
140
|
status: 200,
|
|
128
141
|
contentType: 'image/png',
|
|
@@ -150,7 +163,12 @@ test('checkTouchIcon - Regular case', async () => {
|
|
|
150
163
|
},{
|
|
151
164
|
status: CheckerStatus.Ok,
|
|
152
165
|
id: MessageId.touchIconSquare
|
|
153
|
-
}],
|
|
166
|
+
}], icon: {
|
|
167
|
+
content: bufferToDataUrl(await readableStreamToBuffer(await filePathToReadableStream(testIcon)), 'image/png'),
|
|
168
|
+
url: 'https://example.com/some-other-icon.png',
|
|
169
|
+
width: 180,
|
|
170
|
+
height: 180,
|
|
171
|
+
}
|
|
154
172
|
}, {
|
|
155
173
|
'https://example.com/some-other-icon.png': {
|
|
156
174
|
status: 200,
|
|
@@ -161,7 +179,6 @@ test('checkTouchIcon - Regular case', async () => {
|
|
|
161
179
|
})
|
|
162
180
|
|
|
163
181
|
|
|
164
|
-
|
|
165
182
|
test('getDuplicatedSizes', () => {
|
|
166
183
|
// No duplicates
|
|
167
184
|
expect(getDuplicatedSizes([])).toEqual([]);
|
package/src/touch-icon.ts
CHANGED
|
@@ -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
|
|
62
|
+
let icon: CheckIconOutput | null = null;
|
|
63
63
|
|
|
64
64
|
if (!head) {
|
|
65
65
|
messages.push({
|
|
@@ -68,7 +68,7 @@ export const checkTouchIconIcon = async (baseUrl: string, head: HTMLElement | nu
|
|
|
68
68
|
text: 'No <head> element'
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
return { messages,
|
|
71
|
+
return { messages, icon };
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
const iconMarkup = head.querySelectorAll("link[rel='apple-touch-icon']");
|
|
@@ -79,7 +79,7 @@ export const checkTouchIconIcon = async (baseUrl: string, head: HTMLElement | nu
|
|
|
79
79
|
text: 'No touch icon declared'
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
return { messages,
|
|
82
|
+
return { messages, icon };
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
messages.push({
|
|
@@ -105,7 +105,7 @@ export const checkTouchIconIcon = async (baseUrl: string, head: HTMLElement | nu
|
|
|
105
105
|
text: 'The touch icon has no href'
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
return { messages,
|
|
108
|
+
return { messages, icon };
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
const touchIconUrl = mergeUrlAndPath(baseUrl, iconHref);
|
|
@@ -169,14 +169,14 @@ export const checkTouchIconIcon = async (baseUrl: string, head: HTMLElement | nu
|
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
|
|
172
|
+
icon = await checkIcon(
|
|
173
173
|
touchIconUrl,
|
|
174
174
|
processor,
|
|
175
175
|
fetcher,
|
|
176
176
|
undefined
|
|
177
177
|
);
|
|
178
178
|
|
|
179
|
-
return { messages,
|
|
179
|
+
return { messages, icon };
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
export const getDuplicatedSizes = (sizes: (string | undefined)[]): (string | undefined)[] => {
|
|
@@ -191,6 +191,6 @@ export const checkTouchIcon = async (baseUrl: string, head: HTMLElement | null,
|
|
|
191
191
|
return {
|
|
192
192
|
messages: [...titleReport.messages, ...iconReport.messages],
|
|
193
193
|
appTitle: titleReport.appTitle,
|
|
194
|
-
|
|
194
|
+
icon: iconReport.icon
|
|
195
195
|
}
|
|
196
196
|
}
|
package/src/types.ts
CHANGED
|
@@ -21,6 +21,7 @@ export enum MessageId {
|
|
|
21
21
|
noIcoFavicon,
|
|
22
22
|
multipleIcoFavicons,
|
|
23
23
|
icoFaviconDeclared,
|
|
24
|
+
icoFaviconImplicitInRoot,
|
|
24
25
|
noIcoFaviconHref,
|
|
25
26
|
icoFavicon404,
|
|
26
27
|
icoFaviconCannotGet,
|
|
@@ -134,7 +135,7 @@ export type TouchIconTitleReport = {
|
|
|
134
135
|
|
|
135
136
|
export type TouchIconIconReport = {
|
|
136
137
|
messages: CheckerMessage[],
|
|
137
|
-
|
|
138
|
+
icon: CheckedIcon | null,
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
export type WebAppManifestReport = {
|
|
@@ -143,7 +144,7 @@ export type WebAppManifestReport = {
|
|
|
143
144
|
shortName?: string,
|
|
144
145
|
backgroundColor?: string,
|
|
145
146
|
themeColor?: string,
|
|
146
|
-
icon:
|
|
147
|
+
icon: CheckedIcon | null
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
export type FaviconReport = {
|
|
@@ -191,6 +191,11 @@ test('checkWebAppManifestFile - Everything is fine', async () => {
|
|
|
191
191
|
shortName: 'Short!',
|
|
192
192
|
backgroundColor: '#123456',
|
|
193
193
|
themeColor: '#abcdef',
|
|
194
|
-
icon:
|
|
194
|
+
icon: {
|
|
195
|
+
content: bufferToDataUrl(await readableStreamToBuffer(await filePathToReadableStream(testIcon512)), 'image/png'),
|
|
196
|
+
url: "https://example.com/icon-512.png",
|
|
197
|
+
width: 512,
|
|
198
|
+
height: 512,
|
|
199
|
+
},
|
|
195
200
|
});
|
|
196
201
|
})
|
package/src/web-app-manifest.ts
CHANGED
|
@@ -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 };
|
|
261
261
|
}
|
package/tsconfig.json
CHANGED
|
File without changes
|