Skip to content

Commit 68fb32f

Browse files
authored
Add ability to generate favicons for all browsers and platforms
2 parents ad6325e + 986aefa commit 68fb32f

File tree

11 files changed

+757
-35
lines changed

11 files changed

+757
-35
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ Thumbs.db
1919

2020
# Tests
2121
icons
22+
favicons
2223
launch-screens
24+
config.xml
2325
manifest.json
2426
service-worker.js
2527
*.appcache

README.md

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,13 @@ You can also use `create-pwa` as a package.json script:
7373
}
7474
```
7575

76-
The above commands will generate `manifest.json` and `service-worker.js` files, several (8) png icons in the `/icons/` folder in your app's root folder and several (20) launch screen images in the `launch-screen` folder in your app's root folder.
76+
The above commands will generate:
77+
78+
- a `manifest.json` and a `service-worker.js` files
79+
- several (8) png icons in the `/icons/` folder in your app's root folder
80+
- several (19) favicons in the `/favicons` folder in your app's root folder
81+
- several (20) launch screen images in the `launch-screen` folder in your app's root folder
82+
- a `config.xml` file in your app's root folder - this file is required in Microsoft's browsers
7783

7884
You can edit the contents of the `manifest.json` and `service-worker.js` files.
7985

@@ -83,6 +89,8 @@ In order to create a customized experience for your users, feel advised to revis
8389

8490
When the files(`manifest.json` and `service-worker.js`) are ready for production, you need to let the world know about them:
8591

92+
Feel adviced to edit the content of the `<TileColor>` tag in the `config.xml` file as it matches the color of your application's status bar on Chrome (found in the `<meta name="color" />` tag);
93+
8694
1. Add the following to the `head` of your HTML file(s):
8795

8896
```html
@@ -112,7 +120,52 @@ The code above checks for service worker support and then registers a service wo
112120

113121
You can read more about Service Workers [here](https://developers.google.com/web/fundamentals/primers/service-workers/).
114122

115-
3. Add the following to the `head` of your HTML file(s):
123+
After that, add references to all icons which were generated by `create-pwa`:
124+
125+
3. Add the following favicons and meta tags in the `head` of your HTML file(s):
126+
127+
For more info about the favicons and meta tags below see [here](https://github.com/audreyr/favicon-cheat-sheet).
128+
129+
```html
130+
<!-- All Apple touch icons for iPad, iPhone, iPod -->
131+
<link rel="apple-touch-icon" sizes="57x57" href="favicons/apple-touch-icon-57x57.png" />
132+
<link rel="apple-touch-icon" sizes="60x60" href="favicons/apple-touch-icon-60x60.png" />
133+
<link rel="apple-touch-icon" sizes="72x72" href="favicons/apple-touch-icon-72x72.png" />
134+
<link rel="apple-touch-icon" sizes="76x76" href="favicons/apple-touch-icon-76x76.png" />
135+
<link rel="apple-touch-icon" sizes="114x114" href="favicons/apple-touch-icon-114x114.png" />
136+
<link rel="apple-touch-icon" sizes="120x120" href="favicons/apple-touch-icon-120x120.png" />
137+
<link rel="apple-touch-icon" sizes="144x144" href="favicons/apple-touch-icon-144x144.png" />
138+
<link rel="apple-touch-icon" sizes="152x152" href="favicons/apple-touch-icon-152x152.png" />
139+
140+
<!-- All favicon sizes - for all devices and browsers -->
141+
<link rel="icon" type="image/png" href="favicons/favicon-196x196.png" sizes="196x196" />
142+
<link rel="icon" type="image/png" href="favicons/favicon-96x96.png" sizes="96x96" />
143+
<link rel="icon" type="image/png" href="favicons/favicon-32x32.png" sizes="32x32" />
144+
<link rel="icon" type="image/png" href="favicons/favicon-16x16.png" sizes="16x16" />
145+
<link rel="icon" type="image/png" href="favicons/favicon-128.png" sizes="128x128" />
146+
147+
<!-- A favicon with four different sizes -->
148+
<link rel="shortcut icon" type="image/x-icon" href="favicons/favicon.ico" />
149+
150+
<!-- Application color for Microsoft Windows app tile and Android status bar -->
151+
<meta name="theme-color" content="#edc22e" />
152+
<meta name="msapplication-TileColor" content="#edc22e" />
153+
154+
<!-- Application name for Microsoft Windows app tile -->
155+
<meta name="application-name" content="Create PWA" />
156+
157+
<!-- Application icons for Microsoft Windows app tile -->
158+
<meta name="msapplication-TileImage" content="favicons/mstile-144x144.png" />
159+
<meta name="msapplication-square70x70logo" content="favicons/mstile-70x70.png" />
160+
<meta name="msapplication-square150x150logo" content="favicons/mstile-150x150.png" />
161+
<meta name="msapplication-wide310x150logo" content="favicons/mstile-310x150.png" />
162+
<meta name="msapplication-square310x310logo" content="favicons/mstile-310x310.png" />
163+
164+
<!-- Application config file for Microsoft browsers -->
165+
<meta name="msapplication-config" content="config.xml" />
166+
```
167+
168+
4. (Optional) Add the following launch screens in the `head` of your HTML file(s):
116169

117170
```html
118171
<!-- 12.9" iPad Pro Portrait -->
@@ -340,7 +393,7 @@ You can read more about Service Workers [here](https://developers.google.com/web
340393
/>
341394
```
342395

343-
4. (Optional) Add the following attribute to your `html` tag: `manifest="<YOUR_APP_NAME>.appcache"`. It should look something like this:
396+
5. (Optional) Add the following attribute to your `html` tag: `manifest="<YOUR_APP_NAME>.appcache"`. It should look something like this:
344397

345398
```html
346399
<html lang="en" manifest="create-pwa.appcache">

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "create-pwa",
3-
"version": "2.0.1",
3+
"version": "2.1.0",
44
"description": "Easily create a progressive web app",
55
"scripts": {
66
"test": "tape test.js"
@@ -14,7 +14,8 @@
1414
"progressive web app",
1515
"manifest",
1616
"web app",
17-
"service-worker"
17+
"service worker",
18+
"favicon generator"
1819
],
1920
"main": "src/index.js",
2021
"repository": "git@github.com:scriptex/create-pwa.git",
@@ -25,6 +26,7 @@
2526
"author": "Atanas Atanasov <scriptex.bg@gmail.com> (https://atanas.info)",
2627
"license": "MIT",
2728
"dependencies": {
29+
"png-to-ico": "2.0.4",
2830
"sharp": "0.23.0",
2931
"yargs": "14.0.0"
3032
},

src/appcache.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Internal dependencies
33
*/
4-
const { iconFiles, launchScreenFiles } = require('./helpers');
4+
const { iconFiles, launchScreenFiles, appleTouchIconFiles, faviconFiles, msTileFiles } = require('./helpers');
55

66
module.exports = () => `CACHE MANIFEST
77
@@ -11,6 +11,12 @@ CACHE:
1111
# See below for example
1212
# on how to add files to cache:
1313
14+
# Favicons
15+
${appleTouchIconFiles.join('\n')}
16+
${faviconFiles.join('\n')}
17+
${msTileFiles.join('\n')}
18+
favicons/favicon.ico
19+
1420
# App Icons
1521
${iconFiles.join('\n')}
1622

src/favicons.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* External dependencies
3+
*/
4+
const pngToIco = require('png-to-ico');
5+
const { writeFileSync } = require('fs');
6+
7+
/**
8+
* Internal dependencies
9+
*/
10+
const { generateFile, msTileSizes, faviconSizes, appleTouchIconSizes } = require('./helpers');
11+
12+
/**
13+
* Generate all app icons
14+
*/
15+
module.exports = (icon, folder) => {
16+
generateFile(icon, folder, msTileSizes, 'ms-tile');
17+
generateFile(icon, folder, faviconSizes, 'favicon');
18+
generateFile(icon, folder, appleTouchIconSizes, 'apple-touch-icon');
19+
20+
pngToIco(icon)
21+
.then(buf => {
22+
writeFileSync(`${folder}/favicon.ico`, buf);
23+
})
24+
.catch(console.error);
25+
};

src/helpers.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ const launchScreenSizes = [
4747
'1136x640' // iPhone SE Landscape
4848
];
4949

50+
/**
51+
* Sizes for all apple touch icons
52+
*/
53+
const appleTouchIconSizes = ['57x57', '60x60', '72x72', '76x76', '114x114', '120x120', '144x144', '152x152'];
54+
55+
/**
56+
* Sizes for all favicons
57+
*/
58+
const faviconSizes = ['16x16', '32x32', '96x96', '128x128', '196x196'];
59+
60+
/**
61+
* Sizes for all microsoft windows tiles
62+
*/
63+
const msTileSizes = ['70x70', '144x144', '150x150', '310x150', '310x310'];
64+
5065
/**
5166
* Create files out of all icon sizes
5267
*/
@@ -57,6 +72,21 @@ const iconFiles = iconSizes.map(size => `icons/icon-${size}.png`);
5772
*/
5873
const launchScreenFiles = launchScreenSizes.map(size => `launch-screens/launch-screen-${size}.png`);
5974

75+
/**
76+
* Create files our of all apple touch icon sizes
77+
*/
78+
const appleTouchIconFiles = appleTouchIconSizes.map(size => `favicons/apple-touch-icon-${size}.png`);
79+
80+
/**
81+
* Create files our of all favicon sizes
82+
*/
83+
const faviconFiles = faviconSizes.map(size => `favicons/favicon-${size}.png`);
84+
85+
/**
86+
* Create files our of all microsoft windows tile sizes
87+
*/
88+
const msTileFiles = msTileSizes.map(size => `favicons/mstile-${size}.png`);
89+
6090
/**
6191
* Generate a png file with `sharp`
6292
* @param {File} file
@@ -80,5 +110,11 @@ module.exports = {
80110
iconFiles,
81111
generateFile,
82112
launchScreenSizes,
83-
launchScreenFiles
113+
launchScreenFiles,
114+
appleTouchIconSizes,
115+
appleTouchIconFiles,
116+
faviconSizes,
117+
faviconFiles,
118+
msTileSizes,
119+
msTileFiles
84120
};

src/index.js

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const argv = require('yargs').argv;
1717
const generateIcons = require('./icons');
1818
const manifestTemplate = require('./manifest');
1919
const appCacheTemplate = require('./appcache');
20+
const generateFavicons = require('./favicons');
21+
const msTileConfigTemplate = require('./mstile');
2022
const generateLaunchScreens = require('./launch-screens');
2123
const serviceWorkerTemplate = require('./sw');
2224

@@ -25,6 +27,14 @@ const serviceWorkerTemplate = require('./sw');
2527
*/
2628
const pwd = process.env.PWD;
2729

30+
/**
31+
* Default options
32+
*/
33+
const DEFAULTS = {
34+
icon: './icon.png',
35+
launch: './launch.png'
36+
};
37+
2838
/**
2939
* Get application's name
3040
*/
@@ -95,28 +105,45 @@ const setAppCache = name => {
95105
*/
96106
const setLaunchScreens = launchScreen => generateImages(launchScreen, 'launch-screens', generateLaunchScreens);
97107

108+
/**
109+
* Create app's config for Microsoft browsers
110+
*/
111+
const setMsTileConfig = () => {
112+
writeFileSync(resolve(pwd, 'config.xml'), msTileConfigTemplate());
113+
};
114+
115+
/**
116+
* Create app's favicons
117+
* @param {File} icon
118+
*/
119+
const setFavicons = icon => generateImages(icon, 'favicons', generateFavicons);
120+
98121
/**
99122
* Create all PWA required files
100123
* @param {Object} => { icon: File, launch: File}
101124
*/
102-
const create = ({ icon, launch }) => {
125+
const create = (options = DEFAULTS) => {
103126
const name = getAppName();
104127

105-
setIcons(argv.icon || icon);
128+
let { icon, launch } = options;
129+
130+
icon = argv.icon || icon;
131+
launch = argv.launch || launch;
132+
133+
setIcons(icon);
106134
setAppCache(name);
107135
setManifest(name);
136+
setFavicons(icon);
137+
setMsTileConfig();
108138
setServiceWorker(name);
109-
setLaunchScreens(argv.launch || launch);
139+
setLaunchScreens(launch);
110140
};
111141

112-
create({
113-
icon: './icon.png',
114-
launch: './launch.png'
115-
});
116-
117142
module.exports = create;
118143
module.exports.setIcons = setIcons;
119144
module.exports.setAppCache = setAppCache;
120145
module.exports.setManifest = setManifest;
146+
module.exports.setFavicons = setFavicons;
147+
module.exports.setMsTileConfig = setMsTileConfig;
121148
module.exports.setServiceWorker = setServiceWorker;
122149
module.exports.setLaunchScreens = setLaunchScreens;

src/mstile.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports = () => `<?xml version="1.0" encoding="utf-8"?>
2+
<browserconfig>
3+
<msapplication>
4+
<tile>
5+
<square70x70logo src="favicons/mstile-70x70.png" />
6+
<square150x150logo src="favicons/mstile-150x150.png" />
7+
<wide310x150logo src="favicons/mstile-310x150.png" />
8+
<square310x310logo src="favicons/mstile-310x310.png" />
9+
<TileColor>#000000</TileColor>
10+
</tile>
11+
</msapplication>
12+
</browserconfig>`;

src/sw.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Internal dependencies
33
*/
4-
const { iconFiles, launchScreenFiles } = require('./helpers');
4+
const { iconFiles, launchScreenFiles, appleTouchIconFiles, faviconFiles, msTileFiles } = require('./helpers');
55

66
/**
77
* Convert a list of file to human readable list
@@ -16,6 +16,10 @@ const routes = `[
1616
'/',
1717
${filesToString(iconFiles)},
1818
${filesToString(launchScreenFiles)},
19+
${filesToString(appleTouchIconFiles)},
20+
${filesToString(faviconFiles)},
21+
${filesToString(msTileFiles)},
22+
'favicons/favicon.ico'
1923
]`;
2024

2125
/**

test.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,12 @@ const { existsSync, readdir } = require('fs');
99
*/
1010
const tape = require('tape');
1111
const createPWA = require('./src');
12-
const { iconSizes, launchScreenSizes } = require('./src/helpers');
12+
const { iconSizes, faviconSizes, msTileSizes, launchScreenSizes, appleTouchIconSizes } = require('./src/helpers');
1313

1414
/**
1515
* Init
1616
*/
17-
createPWA({
18-
icon: './icon.png',
19-
launch: './launch.png'
20-
});
17+
createPWA();
2118

2219
/**
2320
* Test if a manifest is created
@@ -35,7 +32,6 @@ tape('Should create a manifest', t => {
3532
*/
3633
tape('The name of the app should be "create-pwa"', t => {
3734
t.equal(require(resolve(__dirname, './manifest.json')).name, 'create-pwa', 'The name of the PWA is "create-pwa"');
38-
3935
t.end();
4036
});
4137

@@ -85,3 +81,29 @@ tape('Should generate launch screens', t => {
8581

8682
t.end();
8783
});
84+
85+
/**
86+
* Test if favicons are being created
87+
*/
88+
tape('Should generate favicons', t => {
89+
readdir(resolve(__dirname, 'favicons'), (err, files) => {
90+
const len = faviconSizes.length + msTileSizes.length + appleTouchIconSizes.length;
91+
92+
files = files.filter(file => !file.endsWith('ico'));
93+
94+
t.equal(len, files.length, `There should be ${len} favicon files.`);
95+
});
96+
97+
t.end();
98+
});
99+
100+
/**
101+
* Test if config.xml is being created
102+
*/
103+
tape('Should generate a config file for Microsoft browsers', t => {
104+
const ac = resolve(__dirname, './config.xml');
105+
const configFileExists = existsSync(ac);
106+
107+
t.ok(configFileExists, 'config.xml exists');
108+
t.end();
109+
});

0 commit comments

Comments
 (0)