@realfavicongenerator/check-favicon 0.0.1 → 0.1.0
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 +26 -0
- package/dist/check.d.ts +3 -0
- package/dist/check.js +27 -0
- package/dist/desktop/desktop.d.ts +7 -0
- package/dist/desktop/desktop.js +213 -0
- package/dist/desktop/desktop.test.d.ts +1 -0
- package/dist/desktop/desktop.test.js +97 -0
- package/dist/desktop/ico.d.ts +4 -0
- package/dist/desktop/ico.js +115 -0
- package/dist/helper.d.ts +23 -0
- package/dist/helper.js +189 -0
- package/dist/helper.test.d.ts +1 -0
- package/dist/helper.test.js +121 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +15 -0
- package/dist/test-helper.d.ts +4 -0
- package/dist/test-helper.js +22 -0
- package/dist/touch-icon.d.ts +6 -0
- package/dist/touch-icon.js +187 -0
- package/dist/touch-icon.test.d.ts +1 -0
- package/dist/touch-icon.test.js +195 -0
- package/dist/types.d.ts +113 -0
- package/dist/types.js +81 -0
- package/dist/web-manifest.d.ts +4 -0
- package/dist/web-manifest.js +262 -0
- package/dist/web-manifest.test.d.ts +1 -0
- package/dist/web-manifest.test.js +172 -0
- package/package.json +6 -3
- package/src/check.ts +18 -0
- package/src/desktop/desktop.test.ts +1 -1
- package/src/desktop/desktop.ts +2 -2
- package/src/desktop/ico.ts +1 -1
- package/src/helper.ts +33 -1
- package/src/index.ts +16 -160
- package/src/test-helper.ts +1 -1
- package/src/touch-icon.test.ts +1 -1
- package/src/touch-icon.ts +2 -2
- package/src/types.ts +129 -0
- package/src/web-manifest.test.ts +3 -3
- package/src/web-manifest.ts +3 -3
- package/tsconfig.json +2 -2
|
@@ -0,0 +1,195 @@
|
|
|
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 node_html_parser_1 = require("node-html-parser");
|
|
13
|
+
const types_1 = require("./types");
|
|
14
|
+
const touch_icon_1 = require("./touch-icon");
|
|
15
|
+
const test_helper_1 = require("./test-helper");
|
|
16
|
+
const helper_1 = require("./helper");
|
|
17
|
+
const runCheckTouchIconTitleTest = (headFragment_1, output_1, ...args_1) => __awaiter(void 0, [headFragment_1, output_1, ...args_1], void 0, function* (headFragment, output, fetchDatabase = {}) {
|
|
18
|
+
const root = headFragment ? (0, node_html_parser_1.parse)(headFragment) : null;
|
|
19
|
+
const result = yield (0, touch_icon_1.checkTouchIconTitle)('https://example.com/', root, (0, test_helper_1.testFetcher)(fetchDatabase));
|
|
20
|
+
const filteredMessages = result.messages.map(m => ({ status: m.status, id: m.id }));
|
|
21
|
+
expect({
|
|
22
|
+
messages: filteredMessages,
|
|
23
|
+
appTitle: result.appTitle
|
|
24
|
+
}).toEqual(output);
|
|
25
|
+
});
|
|
26
|
+
test('checkTouchIconTitle - noHead', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
|
+
yield runCheckTouchIconTitleTest(null, { messages: [{
|
|
28
|
+
status: types_1.CheckerStatus.Error,
|
|
29
|
+
id: types_1.MessageId.noHead,
|
|
30
|
+
}] });
|
|
31
|
+
}));
|
|
32
|
+
test('checkTouchIconTitle - noTouchWebAppTitle', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
+
yield runCheckTouchIconTitleTest('<title>Some text</title>', { messages: [{
|
|
34
|
+
status: types_1.CheckerStatus.Warning,
|
|
35
|
+
id: types_1.MessageId.noTouchWebAppTitle,
|
|
36
|
+
}] });
|
|
37
|
+
}));
|
|
38
|
+
test('checkTouchIconTitle - multipleTouchWebAppTitles', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
+
yield runCheckTouchIconTitleTest(`
|
|
40
|
+
<meta name="apple-mobile-web-app-title" content="First title">
|
|
41
|
+
<meta name="apple-mobile-web-app-title" content="Second title">
|
|
42
|
+
`, { messages: [{
|
|
43
|
+
status: types_1.CheckerStatus.Error,
|
|
44
|
+
id: types_1.MessageId.multipleTouchWebAppTitles,
|
|
45
|
+
}] });
|
|
46
|
+
}));
|
|
47
|
+
test('checkTouchIconTitle - touchWebAppTitleDeclared', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
48
|
+
yield runCheckTouchIconTitleTest(`
|
|
49
|
+
<meta name="apple-mobile-web-app-title" content="The App Name">
|
|
50
|
+
`, { messages: [{
|
|
51
|
+
status: types_1.CheckerStatus.Ok,
|
|
52
|
+
id: types_1.MessageId.touchWebAppTitleDeclared,
|
|
53
|
+
}], appTitle: 'The App Name' });
|
|
54
|
+
}));
|
|
55
|
+
const runCheckTouchIconTest = (headFragment_2, output_2, ...args_2) => __awaiter(void 0, [headFragment_2, output_2, ...args_2], void 0, function* (headFragment, output, fetchDatabase = {}) {
|
|
56
|
+
const root = headFragment ? (0, node_html_parser_1.parse)(headFragment) : null;
|
|
57
|
+
const result = yield (0, touch_icon_1.checkTouchIconIcon)('https://example.com/', root, (0, test_helper_1.testFetcher)(fetchDatabase));
|
|
58
|
+
const filteredMessages = result.messages.map(m => ({ status: m.status, id: m.id }));
|
|
59
|
+
expect({
|
|
60
|
+
messages: filteredMessages,
|
|
61
|
+
touchIcon: result.touchIcon
|
|
62
|
+
}).toEqual(Object.assign(Object.assign({}, output), { touchIcon: output.touchIcon || null }));
|
|
63
|
+
});
|
|
64
|
+
test('checkTouchIcon - noHead', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
65
|
+
yield runCheckTouchIconTest(null, { messages: [{
|
|
66
|
+
status: types_1.CheckerStatus.Error,
|
|
67
|
+
id: types_1.MessageId.noHead,
|
|
68
|
+
}] });
|
|
69
|
+
}));
|
|
70
|
+
test('checkTouchIcon - noTouchIcon', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
71
|
+
yield runCheckTouchIconTest('<title>Some text</title>', { messages: [{
|
|
72
|
+
status: types_1.CheckerStatus.Error,
|
|
73
|
+
id: types_1.MessageId.noTouchIcon,
|
|
74
|
+
}] });
|
|
75
|
+
}));
|
|
76
|
+
test('checkTouchIcon - touchIconWithSize', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
77
|
+
yield runCheckTouchIconTest(`
|
|
78
|
+
<link rel="apple-touch-icon" sizes="152x152" href="some-other-icon.png">
|
|
79
|
+
`, { messages: [{
|
|
80
|
+
status: types_1.CheckerStatus.Ok,
|
|
81
|
+
id: types_1.MessageId.touchIconDeclared,
|
|
82
|
+
}, {
|
|
83
|
+
status: types_1.CheckerStatus.Warning,
|
|
84
|
+
id: types_1.MessageId.touchIconWithSize,
|
|
85
|
+
}] }, {
|
|
86
|
+
'https://example.com/some-other-icon.png': {
|
|
87
|
+
status: 200,
|
|
88
|
+
contentType: 'image/png',
|
|
89
|
+
readableStream: null
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}));
|
|
93
|
+
test('checkTouchIcon - multipleTouchIcon - no size', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
94
|
+
yield runCheckTouchIconTest(`
|
|
95
|
+
<link rel="apple-touch-icon" href="some-icon.png">
|
|
96
|
+
<link rel="apple-touch-icon" href="some-other-icon.png">
|
|
97
|
+
`, { messages: [{
|
|
98
|
+
status: types_1.CheckerStatus.Ok,
|
|
99
|
+
id: types_1.MessageId.touchIconDeclared,
|
|
100
|
+
}, {
|
|
101
|
+
status: types_1.CheckerStatus.Error,
|
|
102
|
+
id: types_1.MessageId.duplicatedTouchIconSizes,
|
|
103
|
+
}] }, {
|
|
104
|
+
'https://example.com/some-icon.png': {
|
|
105
|
+
status: 200,
|
|
106
|
+
contentType: 'image/png',
|
|
107
|
+
readableStream: null
|
|
108
|
+
},
|
|
109
|
+
'https://example.com/some-other-icon.png': {
|
|
110
|
+
status: 200,
|
|
111
|
+
contentType: 'image/png',
|
|
112
|
+
readableStream: null
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}));
|
|
116
|
+
test('checkTouchIcon - multipleTouchIcon - specific size', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
117
|
+
yield runCheckTouchIconTest(`
|
|
118
|
+
<link rel="apple-touch-icon" sizes="180x180" href="some-icon.png">
|
|
119
|
+
<link rel="apple-touch-icon" sizes="180x180" href="some-other-icon.png">
|
|
120
|
+
`, { messages: [{
|
|
121
|
+
status: types_1.CheckerStatus.Ok,
|
|
122
|
+
id: types_1.MessageId.touchIconDeclared,
|
|
123
|
+
}, {
|
|
124
|
+
status: types_1.CheckerStatus.Warning,
|
|
125
|
+
id: types_1.MessageId.touchIconWithSize,
|
|
126
|
+
}, {
|
|
127
|
+
status: types_1.CheckerStatus.Error,
|
|
128
|
+
id: types_1.MessageId.duplicatedTouchIconSizes,
|
|
129
|
+
}] }, {
|
|
130
|
+
'https://example.com/some-icon.png': {
|
|
131
|
+
status: 200,
|
|
132
|
+
contentType: 'image/png',
|
|
133
|
+
readableStream: null
|
|
134
|
+
},
|
|
135
|
+
'https://example.com/some-other-icon.png': {
|
|
136
|
+
status: 200,
|
|
137
|
+
contentType: 'image/png',
|
|
138
|
+
readableStream: null
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}));
|
|
142
|
+
test('checkTouchIcon - touchIconWithSize', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
143
|
+
yield runCheckTouchIconTest(`
|
|
144
|
+
<link rel="apple-touch-icon" sizes="180x180" href="some-other-icon.png">
|
|
145
|
+
`, { messages: [{
|
|
146
|
+
status: types_1.CheckerStatus.Ok,
|
|
147
|
+
id: types_1.MessageId.touchIconDeclared,
|
|
148
|
+
}, {
|
|
149
|
+
status: types_1.CheckerStatus.Warning,
|
|
150
|
+
id: types_1.MessageId.touchIconWithSize,
|
|
151
|
+
}] }, {
|
|
152
|
+
'https://example.com/some-other-icon.png': {
|
|
153
|
+
status: 200,
|
|
154
|
+
contentType: 'image/png',
|
|
155
|
+
readableStream: null
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}));
|
|
159
|
+
const testIcon = './fixtures/180x180.png';
|
|
160
|
+
test('checkTouchIcon - Regular case', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
161
|
+
yield runCheckTouchIconTest(`
|
|
162
|
+
<link rel="apple-touch-icon" href="some-other-icon.png">
|
|
163
|
+
`, { messages: [{
|
|
164
|
+
status: types_1.CheckerStatus.Ok,
|
|
165
|
+
id: types_1.MessageId.touchIconDeclared,
|
|
166
|
+
}, {
|
|
167
|
+
status: types_1.CheckerStatus.Ok,
|
|
168
|
+
id: types_1.MessageId.touchIconDownloadable,
|
|
169
|
+
}, {
|
|
170
|
+
status: types_1.CheckerStatus.Ok,
|
|
171
|
+
id: types_1.MessageId.touchIconSquare
|
|
172
|
+
}], touchIcon: (0, helper_1.bufferToDataUrl)(yield (0, helper_1.readableStreamToBuffer)(yield (0, helper_1.filePathToReadableStream)(testIcon)), 'image/png')
|
|
173
|
+
}, {
|
|
174
|
+
'https://example.com/some-other-icon.png': {
|
|
175
|
+
status: 200,
|
|
176
|
+
contentType: 'image/png',
|
|
177
|
+
readableStream: yield (0, helper_1.filePathToReadableStream)(testIcon)
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}));
|
|
181
|
+
test('getDuplicatedSizes', () => {
|
|
182
|
+
// No duplicates
|
|
183
|
+
expect((0, touch_icon_1.getDuplicatedSizes)([])).toEqual([]);
|
|
184
|
+
expect((0, touch_icon_1.getDuplicatedSizes)([undefined])).toEqual([]);
|
|
185
|
+
expect((0, touch_icon_1.getDuplicatedSizes)(['180x180'])).toEqual([]);
|
|
186
|
+
expect((0, touch_icon_1.getDuplicatedSizes)([undefined, '180x180'])).toEqual([]);
|
|
187
|
+
// Duplicates
|
|
188
|
+
expect((0, touch_icon_1.getDuplicatedSizes)(['152x152', '180x180', '180x180'])).toEqual(['180x180']);
|
|
189
|
+
expect((0, touch_icon_1.getDuplicatedSizes)([undefined, '180x180', undefined, undefined])).toEqual([undefined]);
|
|
190
|
+
expect((0, touch_icon_1.getDuplicatedSizes)([
|
|
191
|
+
'152x152', '180x180', '152x152', undefined, '152x152', undefined
|
|
192
|
+
])).toEqual([
|
|
193
|
+
'152x152', undefined
|
|
194
|
+
]);
|
|
195
|
+
});
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export declare enum CheckerStatus {
|
|
2
|
+
Ok = "Ok",
|
|
3
|
+
Error = "Error",
|
|
4
|
+
Warning = "Warning"
|
|
5
|
+
}
|
|
6
|
+
export declare enum MessageId {
|
|
7
|
+
noHead = 0,
|
|
8
|
+
svgFaviconDeclared = 1,
|
|
9
|
+
noSvgFavicon = 2,
|
|
10
|
+
multipleSvgFavicons = 3,
|
|
11
|
+
noSvgFaviconHref = 4,
|
|
12
|
+
svgFavicon404 = 5,
|
|
13
|
+
svgFaviconCannotGet = 6,
|
|
14
|
+
svgFaviconDownloadable = 7,
|
|
15
|
+
svgFaviconSquare = 8,
|
|
16
|
+
svgFaviconNotSquare = 9,
|
|
17
|
+
noIcoFavicon = 10,
|
|
18
|
+
multipleIcoFavicons = 11,
|
|
19
|
+
icoFaviconDeclared = 12,
|
|
20
|
+
noIcoFaviconHref = 13,
|
|
21
|
+
icoFavicon404 = 14,
|
|
22
|
+
icoFaviconCannotGet = 15,
|
|
23
|
+
icoFaviconDownloadable = 16,
|
|
24
|
+
icoFaviconExtraSizes = 17,
|
|
25
|
+
icoFaviconMissingSizes = 18,
|
|
26
|
+
icoFaviconExpectedSizes = 19,
|
|
27
|
+
noDesktopPngFavicon = 20,
|
|
28
|
+
no48x48DesktopPngFavicon = 21,
|
|
29
|
+
desktopPngFaviconDeclared = 22,
|
|
30
|
+
noDesktopPngFaviconHref = 23,
|
|
31
|
+
desktopPngFaviconCannotGet = 24,
|
|
32
|
+
desktopPngFaviconDownloadable = 25,
|
|
33
|
+
desktopPngFavicon404 = 26,
|
|
34
|
+
desktopPngFaviconWrongSize = 27,
|
|
35
|
+
desktopPngFaviconRightSize = 28,
|
|
36
|
+
noTouchWebAppTitle = 29,
|
|
37
|
+
multipleTouchWebAppTitles = 30,
|
|
38
|
+
emptyTouchWebAppTitle = 31,
|
|
39
|
+
touchWebAppTitleDeclared = 32,
|
|
40
|
+
noTouchIcon = 33,
|
|
41
|
+
duplicatedTouchIconSizes = 34,
|
|
42
|
+
touchIconWithSize = 35,
|
|
43
|
+
touchIconDeclared = 36,
|
|
44
|
+
noTouchIconHref = 37,
|
|
45
|
+
touchIcon404 = 38,
|
|
46
|
+
touchIconCannotGet = 39,
|
|
47
|
+
touchIconDownloadable = 40,
|
|
48
|
+
touchIconSquare = 41,
|
|
49
|
+
touchIcon180x180 = 42,
|
|
50
|
+
touchIconNotSquare = 43,
|
|
51
|
+
touchIconWrongSize = 44,
|
|
52
|
+
noManifest = 45,
|
|
53
|
+
noManifestHref = 46,
|
|
54
|
+
manifest404 = 47,
|
|
55
|
+
manifestCannotGet = 48,
|
|
56
|
+
manifestInvalidJson = 49,
|
|
57
|
+
manifestName = 50,
|
|
58
|
+
noManifestName = 51,
|
|
59
|
+
manifestShortName = 52,
|
|
60
|
+
noManifestShortName = 53,
|
|
61
|
+
manifestBackgroundColor = 54,
|
|
62
|
+
noManifestBackgroundColor = 55,
|
|
63
|
+
manifestThemeColor = 56,
|
|
64
|
+
noManifestThemeColor = 57,
|
|
65
|
+
noManifestIcons = 58,
|
|
66
|
+
noManifestIcon = 59,
|
|
67
|
+
manifestIconDeclared = 60,
|
|
68
|
+
manifestIconCannotGet = 61,
|
|
69
|
+
manifestIconDownloadable = 62,
|
|
70
|
+
manifestIcon404 = 63,
|
|
71
|
+
manifestIconNoHref = 64,
|
|
72
|
+
manifestIconNotSquare = 65,
|
|
73
|
+
manifestIconRightSize = 66,
|
|
74
|
+
manifestIconSquare = 67,
|
|
75
|
+
manifestIconWrongSize = 68
|
|
76
|
+
}
|
|
77
|
+
export type CheckerMessage = {
|
|
78
|
+
status: CheckerStatus;
|
|
79
|
+
id: MessageId;
|
|
80
|
+
text: string;
|
|
81
|
+
};
|
|
82
|
+
export type FetchResponse = {
|
|
83
|
+
status: number;
|
|
84
|
+
contentType: string | null;
|
|
85
|
+
readableStream?: ReadableStream | null;
|
|
86
|
+
};
|
|
87
|
+
export type Fetcher = (url: string, contentType?: string) => Promise<FetchResponse>;
|
|
88
|
+
export type DesktopFaviconReport = {
|
|
89
|
+
messages: CheckerMessage[];
|
|
90
|
+
icon: string | null;
|
|
91
|
+
};
|
|
92
|
+
export type TouchIconTitleReport = {
|
|
93
|
+
messages: CheckerMessage[];
|
|
94
|
+
appTitle?: string;
|
|
95
|
+
};
|
|
96
|
+
export type TouchIconIconReport = {
|
|
97
|
+
messages: CheckerMessage[];
|
|
98
|
+
touchIcon: string | null;
|
|
99
|
+
};
|
|
100
|
+
export type WebManifestReport = {
|
|
101
|
+
messages: CheckerMessage[];
|
|
102
|
+
name?: string;
|
|
103
|
+
shortName?: string;
|
|
104
|
+
backgroundColor?: string;
|
|
105
|
+
themeColor?: string;
|
|
106
|
+
icon: string | null;
|
|
107
|
+
};
|
|
108
|
+
export type FaviconReport = {
|
|
109
|
+
desktop: DesktopFaviconReport;
|
|
110
|
+
touchIcon: TouchIconReport;
|
|
111
|
+
webAppManifest: WebManifestReport;
|
|
112
|
+
};
|
|
113
|
+
export type TouchIconReport = TouchIconIconReport & TouchIconTitleReport;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MessageId = exports.CheckerStatus = void 0;
|
|
4
|
+
var CheckerStatus;
|
|
5
|
+
(function (CheckerStatus) {
|
|
6
|
+
CheckerStatus["Ok"] = "Ok";
|
|
7
|
+
CheckerStatus["Error"] = "Error";
|
|
8
|
+
CheckerStatus["Warning"] = "Warning";
|
|
9
|
+
})(CheckerStatus || (exports.CheckerStatus = CheckerStatus = {}));
|
|
10
|
+
var MessageId;
|
|
11
|
+
(function (MessageId) {
|
|
12
|
+
MessageId[MessageId["noHead"] = 0] = "noHead";
|
|
13
|
+
MessageId[MessageId["svgFaviconDeclared"] = 1] = "svgFaviconDeclared";
|
|
14
|
+
MessageId[MessageId["noSvgFavicon"] = 2] = "noSvgFavicon";
|
|
15
|
+
MessageId[MessageId["multipleSvgFavicons"] = 3] = "multipleSvgFavicons";
|
|
16
|
+
MessageId[MessageId["noSvgFaviconHref"] = 4] = "noSvgFaviconHref";
|
|
17
|
+
MessageId[MessageId["svgFavicon404"] = 5] = "svgFavicon404";
|
|
18
|
+
MessageId[MessageId["svgFaviconCannotGet"] = 6] = "svgFaviconCannotGet";
|
|
19
|
+
MessageId[MessageId["svgFaviconDownloadable"] = 7] = "svgFaviconDownloadable";
|
|
20
|
+
MessageId[MessageId["svgFaviconSquare"] = 8] = "svgFaviconSquare";
|
|
21
|
+
MessageId[MessageId["svgFaviconNotSquare"] = 9] = "svgFaviconNotSquare";
|
|
22
|
+
MessageId[MessageId["noIcoFavicon"] = 10] = "noIcoFavicon";
|
|
23
|
+
MessageId[MessageId["multipleIcoFavicons"] = 11] = "multipleIcoFavicons";
|
|
24
|
+
MessageId[MessageId["icoFaviconDeclared"] = 12] = "icoFaviconDeclared";
|
|
25
|
+
MessageId[MessageId["noIcoFaviconHref"] = 13] = "noIcoFaviconHref";
|
|
26
|
+
MessageId[MessageId["icoFavicon404"] = 14] = "icoFavicon404";
|
|
27
|
+
MessageId[MessageId["icoFaviconCannotGet"] = 15] = "icoFaviconCannotGet";
|
|
28
|
+
MessageId[MessageId["icoFaviconDownloadable"] = 16] = "icoFaviconDownloadable";
|
|
29
|
+
MessageId[MessageId["icoFaviconExtraSizes"] = 17] = "icoFaviconExtraSizes";
|
|
30
|
+
MessageId[MessageId["icoFaviconMissingSizes"] = 18] = "icoFaviconMissingSizes";
|
|
31
|
+
MessageId[MessageId["icoFaviconExpectedSizes"] = 19] = "icoFaviconExpectedSizes";
|
|
32
|
+
MessageId[MessageId["noDesktopPngFavicon"] = 20] = "noDesktopPngFavicon";
|
|
33
|
+
MessageId[MessageId["no48x48DesktopPngFavicon"] = 21] = "no48x48DesktopPngFavicon";
|
|
34
|
+
MessageId[MessageId["desktopPngFaviconDeclared"] = 22] = "desktopPngFaviconDeclared";
|
|
35
|
+
MessageId[MessageId["noDesktopPngFaviconHref"] = 23] = "noDesktopPngFaviconHref";
|
|
36
|
+
MessageId[MessageId["desktopPngFaviconCannotGet"] = 24] = "desktopPngFaviconCannotGet";
|
|
37
|
+
MessageId[MessageId["desktopPngFaviconDownloadable"] = 25] = "desktopPngFaviconDownloadable";
|
|
38
|
+
MessageId[MessageId["desktopPngFavicon404"] = 26] = "desktopPngFavicon404";
|
|
39
|
+
MessageId[MessageId["desktopPngFaviconWrongSize"] = 27] = "desktopPngFaviconWrongSize";
|
|
40
|
+
MessageId[MessageId["desktopPngFaviconRightSize"] = 28] = "desktopPngFaviconRightSize";
|
|
41
|
+
MessageId[MessageId["noTouchWebAppTitle"] = 29] = "noTouchWebAppTitle";
|
|
42
|
+
MessageId[MessageId["multipleTouchWebAppTitles"] = 30] = "multipleTouchWebAppTitles";
|
|
43
|
+
MessageId[MessageId["emptyTouchWebAppTitle"] = 31] = "emptyTouchWebAppTitle";
|
|
44
|
+
MessageId[MessageId["touchWebAppTitleDeclared"] = 32] = "touchWebAppTitleDeclared";
|
|
45
|
+
MessageId[MessageId["noTouchIcon"] = 33] = "noTouchIcon";
|
|
46
|
+
MessageId[MessageId["duplicatedTouchIconSizes"] = 34] = "duplicatedTouchIconSizes";
|
|
47
|
+
MessageId[MessageId["touchIconWithSize"] = 35] = "touchIconWithSize";
|
|
48
|
+
MessageId[MessageId["touchIconDeclared"] = 36] = "touchIconDeclared";
|
|
49
|
+
MessageId[MessageId["noTouchIconHref"] = 37] = "noTouchIconHref";
|
|
50
|
+
MessageId[MessageId["touchIcon404"] = 38] = "touchIcon404";
|
|
51
|
+
MessageId[MessageId["touchIconCannotGet"] = 39] = "touchIconCannotGet";
|
|
52
|
+
MessageId[MessageId["touchIconDownloadable"] = 40] = "touchIconDownloadable";
|
|
53
|
+
MessageId[MessageId["touchIconSquare"] = 41] = "touchIconSquare";
|
|
54
|
+
MessageId[MessageId["touchIcon180x180"] = 42] = "touchIcon180x180";
|
|
55
|
+
MessageId[MessageId["touchIconNotSquare"] = 43] = "touchIconNotSquare";
|
|
56
|
+
MessageId[MessageId["touchIconWrongSize"] = 44] = "touchIconWrongSize";
|
|
57
|
+
MessageId[MessageId["noManifest"] = 45] = "noManifest";
|
|
58
|
+
MessageId[MessageId["noManifestHref"] = 46] = "noManifestHref";
|
|
59
|
+
MessageId[MessageId["manifest404"] = 47] = "manifest404";
|
|
60
|
+
MessageId[MessageId["manifestCannotGet"] = 48] = "manifestCannotGet";
|
|
61
|
+
MessageId[MessageId["manifestInvalidJson"] = 49] = "manifestInvalidJson";
|
|
62
|
+
MessageId[MessageId["manifestName"] = 50] = "manifestName";
|
|
63
|
+
MessageId[MessageId["noManifestName"] = 51] = "noManifestName";
|
|
64
|
+
MessageId[MessageId["manifestShortName"] = 52] = "manifestShortName";
|
|
65
|
+
MessageId[MessageId["noManifestShortName"] = 53] = "noManifestShortName";
|
|
66
|
+
MessageId[MessageId["manifestBackgroundColor"] = 54] = "manifestBackgroundColor";
|
|
67
|
+
MessageId[MessageId["noManifestBackgroundColor"] = 55] = "noManifestBackgroundColor";
|
|
68
|
+
MessageId[MessageId["manifestThemeColor"] = 56] = "manifestThemeColor";
|
|
69
|
+
MessageId[MessageId["noManifestThemeColor"] = 57] = "noManifestThemeColor";
|
|
70
|
+
MessageId[MessageId["noManifestIcons"] = 58] = "noManifestIcons";
|
|
71
|
+
MessageId[MessageId["noManifestIcon"] = 59] = "noManifestIcon";
|
|
72
|
+
MessageId[MessageId["manifestIconDeclared"] = 60] = "manifestIconDeclared";
|
|
73
|
+
MessageId[MessageId["manifestIconCannotGet"] = 61] = "manifestIconCannotGet";
|
|
74
|
+
MessageId[MessageId["manifestIconDownloadable"] = 62] = "manifestIconDownloadable";
|
|
75
|
+
MessageId[MessageId["manifestIcon404"] = 63] = "manifestIcon404";
|
|
76
|
+
MessageId[MessageId["manifestIconNoHref"] = 64] = "manifestIconNoHref";
|
|
77
|
+
MessageId[MessageId["manifestIconNotSquare"] = 65] = "manifestIconNotSquare";
|
|
78
|
+
MessageId[MessageId["manifestIconRightSize"] = 66] = "manifestIconRightSize";
|
|
79
|
+
MessageId[MessageId["manifestIconSquare"] = 67] = "manifestIconSquare";
|
|
80
|
+
MessageId[MessageId["manifestIconWrongSize"] = 68] = "manifestIconWrongSize";
|
|
81
|
+
})(MessageId || (exports.MessageId = MessageId = {}));
|
|
@@ -0,0 +1,4 @@
|
|
|
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>;
|
|
@@ -0,0 +1,262 @@
|
|
|
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 __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
12
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
13
|
+
var m = o[Symbol.asyncIterator], i;
|
|
14
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
15
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
16
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
17
|
+
};
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.checkWebManifestFile = exports.checkWebAppManifest = void 0;
|
|
20
|
+
const types_1 = require("./types");
|
|
21
|
+
const helper_1 = require("./helper");
|
|
22
|
+
const checkWebAppManifest = (baseUrl_1, head_1, ...args_1) => __awaiter(void 0, [baseUrl_1, head_1, ...args_1], void 0, function* (baseUrl, head, fetcher = helper_1.fetchFetcher) {
|
|
23
|
+
const messages = [];
|
|
24
|
+
let name = undefined;
|
|
25
|
+
let shortName = undefined;
|
|
26
|
+
let backgroundColor = undefined;
|
|
27
|
+
let themeColor = undefined;
|
|
28
|
+
let icon = null;
|
|
29
|
+
if (!head) {
|
|
30
|
+
messages.push({
|
|
31
|
+
status: types_1.CheckerStatus.Error,
|
|
32
|
+
id: types_1.MessageId.noHead,
|
|
33
|
+
text: 'No <head> element'
|
|
34
|
+
});
|
|
35
|
+
return { messages, name, shortName, backgroundColor, themeColor, icon };
|
|
36
|
+
}
|
|
37
|
+
const manifestMarkup = head.querySelectorAll("link[rel='manifest']");
|
|
38
|
+
if (manifestMarkup.length === 0) {
|
|
39
|
+
messages.push({
|
|
40
|
+
status: types_1.CheckerStatus.Error,
|
|
41
|
+
id: types_1.MessageId.noManifest,
|
|
42
|
+
text: 'No web app manifest'
|
|
43
|
+
});
|
|
44
|
+
return { messages, name, shortName, backgroundColor, themeColor, icon };
|
|
45
|
+
}
|
|
46
|
+
const href = manifestMarkup[0].getAttribute('href');
|
|
47
|
+
if (!href) {
|
|
48
|
+
messages.push({
|
|
49
|
+
status: types_1.CheckerStatus.Error,
|
|
50
|
+
id: types_1.MessageId.noManifestHref,
|
|
51
|
+
text: 'The web app manifest markup has no `href` attribute'
|
|
52
|
+
});
|
|
53
|
+
return { messages, name, shortName, backgroundColor, themeColor, icon };
|
|
54
|
+
}
|
|
55
|
+
const manifestUrl = (0, helper_1.mergeUrlAndPath)(baseUrl, href);
|
|
56
|
+
const manifest = yield fetcher(manifestUrl, 'application/json');
|
|
57
|
+
if (manifest.status === 404) {
|
|
58
|
+
messages.push({
|
|
59
|
+
status: types_1.CheckerStatus.Error,
|
|
60
|
+
id: types_1.MessageId.manifest404,
|
|
61
|
+
text: `The web app manifest at \`${href}\` is not found`
|
|
62
|
+
});
|
|
63
|
+
return { messages, name, shortName, backgroundColor, themeColor, icon };
|
|
64
|
+
}
|
|
65
|
+
else if (manifest.status >= 300 || !manifest.readableStream) {
|
|
66
|
+
messages.push({
|
|
67
|
+
status: types_1.CheckerStatus.Error,
|
|
68
|
+
id: types_1.MessageId.manifestCannotGet,
|
|
69
|
+
text: `Cannot get the web app manifest at \`${href}\` (${manifest.status} error)`
|
|
70
|
+
});
|
|
71
|
+
return { messages, name, shortName, backgroundColor, themeColor, icon };
|
|
72
|
+
}
|
|
73
|
+
let parsedManifest;
|
|
74
|
+
try {
|
|
75
|
+
parsedManifest = yield readableStreamToJson(manifest.readableStream);
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
messages.push({
|
|
79
|
+
status: types_1.CheckerStatus.Error,
|
|
80
|
+
id: types_1.MessageId.manifestInvalidJson,
|
|
81
|
+
text: `Cannot parse the web app manifest at \`${href}\``
|
|
82
|
+
});
|
|
83
|
+
return { messages, name, shortName, backgroundColor, themeColor, icon };
|
|
84
|
+
}
|
|
85
|
+
const manifestReport = yield (0, exports.checkWebManifestFile)(parsedManifest, manifestUrl, fetcher);
|
|
86
|
+
return Object.assign(Object.assign({}, manifestReport), { messages: messages.concat(manifestReport.messages) });
|
|
87
|
+
});
|
|
88
|
+
exports.checkWebAppManifest = checkWebAppManifest;
|
|
89
|
+
const readableStreamToJson = (stream) => __awaiter(void 0, void 0, void 0, function* () {
|
|
90
|
+
const reader = stream.getReader();
|
|
91
|
+
const decoder = new TextDecoder();
|
|
92
|
+
let result = '';
|
|
93
|
+
while (true) {
|
|
94
|
+
const { done, value } = yield reader.read();
|
|
95
|
+
if (done) {
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
result += decoder.decode(value);
|
|
99
|
+
}
|
|
100
|
+
return JSON.parse(result);
|
|
101
|
+
});
|
|
102
|
+
const checkWebManifestFile = (manifest, baseUrl, fetcher) => __awaiter(void 0, void 0, void 0, function* () {
|
|
103
|
+
var _a, e_1, _b, _c;
|
|
104
|
+
const messages = [];
|
|
105
|
+
let icon = null;
|
|
106
|
+
const name = manifest.name || undefined;
|
|
107
|
+
if (!name) {
|
|
108
|
+
messages.push({
|
|
109
|
+
status: types_1.CheckerStatus.Error,
|
|
110
|
+
id: types_1.MessageId.noManifestName,
|
|
111
|
+
text: 'The web app manifest has no `name`'
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
messages.push({
|
|
116
|
+
status: types_1.CheckerStatus.Ok,
|
|
117
|
+
id: types_1.MessageId.manifestName,
|
|
118
|
+
text: `The web app manifest has the name "${name}"`
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
const shortName = manifest.short_name || undefined;
|
|
122
|
+
if (!shortName) {
|
|
123
|
+
messages.push({
|
|
124
|
+
status: types_1.CheckerStatus.Error,
|
|
125
|
+
id: types_1.MessageId.noManifestShortName,
|
|
126
|
+
text: 'The web app manifest has no `short_name`'
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
messages.push({
|
|
131
|
+
status: types_1.CheckerStatus.Ok,
|
|
132
|
+
id: types_1.MessageId.manifestShortName,
|
|
133
|
+
text: `The web app manifest has the short name "${shortName}"`
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const backgroundColor = manifest.background_color || undefined;
|
|
137
|
+
if (!backgroundColor) {
|
|
138
|
+
messages.push({
|
|
139
|
+
status: types_1.CheckerStatus.Error,
|
|
140
|
+
id: types_1.MessageId.noManifestBackgroundColor,
|
|
141
|
+
text: 'The web app manifest has no `background_color`'
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
messages.push({
|
|
146
|
+
status: types_1.CheckerStatus.Ok,
|
|
147
|
+
id: types_1.MessageId.manifestBackgroundColor,
|
|
148
|
+
text: `The web app manifest has the background color \`${backgroundColor}\``
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const themeColor = manifest.theme_color || undefined;
|
|
152
|
+
if (!themeColor) {
|
|
153
|
+
messages.push({
|
|
154
|
+
status: types_1.CheckerStatus.Error,
|
|
155
|
+
id: types_1.MessageId.noManifestThemeColor,
|
|
156
|
+
text: 'The web app manifest has no `theme_color`'
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
messages.push({
|
|
161
|
+
status: types_1.CheckerStatus.Ok,
|
|
162
|
+
id: types_1.MessageId.manifestThemeColor,
|
|
163
|
+
text: `The web app manifest has the theme color \`${themeColor}\``
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const icons = manifest.icons;
|
|
167
|
+
if (!icons || !Array.isArray(icons) || icons.length === 0) {
|
|
168
|
+
messages.push({
|
|
169
|
+
status: types_1.CheckerStatus.Error,
|
|
170
|
+
id: types_1.MessageId.noManifestIcons,
|
|
171
|
+
text: 'The web app manifest has no `icons`'
|
|
172
|
+
});
|
|
173
|
+
return { messages, name, shortName, backgroundColor, themeColor, icon };
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
for (var _d = true, _e = __asyncValues([192, 512]), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
177
|
+
_c = _f.value;
|
|
178
|
+
_d = false;
|
|
179
|
+
const size = _c;
|
|
180
|
+
const iconEntry = icons.find((icon) => icon.sizes === `${size}x${size}`);
|
|
181
|
+
if (!iconEntry) {
|
|
182
|
+
messages.push({
|
|
183
|
+
status: types_1.CheckerStatus.Error,
|
|
184
|
+
id: types_1.MessageId.noManifestIcon,
|
|
185
|
+
text: `The web app manifest has no ${size}x${size} icon`
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
messages.push({
|
|
190
|
+
status: types_1.CheckerStatus.Ok,
|
|
191
|
+
id: types_1.MessageId.manifestIconDeclared,
|
|
192
|
+
text: `The web app manifest has a ${size}x${size} icon`
|
|
193
|
+
});
|
|
194
|
+
const iconUrl = (0, helper_1.mergeUrlAndPath)(baseUrl, iconEntry.src);
|
|
195
|
+
const processor = {
|
|
196
|
+
cannotGet: (status) => {
|
|
197
|
+
messages.push({
|
|
198
|
+
status: types_1.CheckerStatus.Error,
|
|
199
|
+
id: types_1.MessageId.manifestIconCannotGet,
|
|
200
|
+
text: `The ${size}x${size} icon cannot be fetched (${status})`
|
|
201
|
+
});
|
|
202
|
+
},
|
|
203
|
+
downloadable: () => {
|
|
204
|
+
messages.push({
|
|
205
|
+
status: types_1.CheckerStatus.Ok,
|
|
206
|
+
id: types_1.MessageId.manifestIconDownloadable,
|
|
207
|
+
text: `The ${size}x${size} icon is downloadable`
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
icon404: () => {
|
|
211
|
+
messages.push({
|
|
212
|
+
status: types_1.CheckerStatus.Error,
|
|
213
|
+
id: types_1.MessageId.manifestIcon404,
|
|
214
|
+
text: `The ${size}x${size} icon is not found`
|
|
215
|
+
});
|
|
216
|
+
},
|
|
217
|
+
noHref: () => {
|
|
218
|
+
messages.push({
|
|
219
|
+
status: types_1.CheckerStatus.Error,
|
|
220
|
+
id: types_1.MessageId.manifestIconNoHref,
|
|
221
|
+
text: `The ${size}x${size} icon has no \`href\` attribute`
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
notSquare: () => {
|
|
225
|
+
messages.push({
|
|
226
|
+
status: types_1.CheckerStatus.Error,
|
|
227
|
+
id: types_1.MessageId.manifestIconNotSquare,
|
|
228
|
+
text: `The ${size}x${size} icon is not square`
|
|
229
|
+
});
|
|
230
|
+
},
|
|
231
|
+
rightSize: () => {
|
|
232
|
+
messages.push({
|
|
233
|
+
status: types_1.CheckerStatus.Ok,
|
|
234
|
+
id: types_1.MessageId.manifestIconRightSize,
|
|
235
|
+
text: `The ${size}x${size} icon has the right size`
|
|
236
|
+
});
|
|
237
|
+
},
|
|
238
|
+
square: () => {
|
|
239
|
+
// Ignore this, just check the size
|
|
240
|
+
},
|
|
241
|
+
wrongSize: (actualSize) => {
|
|
242
|
+
messages.push({
|
|
243
|
+
status: types_1.CheckerStatus.Error,
|
|
244
|
+
id: types_1.MessageId.manifestIconWrongSize,
|
|
245
|
+
text: `The ${size}x${size} icon has the wrong size (${actualSize})`
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
icon = yield (0, helper_1.checkIcon)(iconUrl, processor, fetcher, iconEntry.type || (0, helper_1.pathToMimeType)(iconEntry.src), size);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
254
|
+
finally {
|
|
255
|
+
try {
|
|
256
|
+
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
257
|
+
}
|
|
258
|
+
finally { if (e_1) throw e_1.error; }
|
|
259
|
+
}
|
|
260
|
+
return { messages, name, shortName, backgroundColor, themeColor, icon };
|
|
261
|
+
});
|
|
262
|
+
exports.checkWebManifestFile = checkWebManifestFile;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|