From 3e6b0cbc2cad67489e4819f478a25929931e6e1d Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Thu, 5 Apr 2018 11:35:42 -0700 Subject: [PATCH] Updating gatsby-plugin-manifest (#4382) * add auto image generation support, update docs * fix spelling error and clarify wording * Update docs with shannon's feedback * rewrite since I forgot to save * Update README.md * Update gatsby-node.js * I made some of the language consistent like shannon requested but using the "mode" idea. made some formatting more consistent. Fixed the Hybrid description to be more clear. Hybrid only gerates icons from the provided `icons` array, not a amalgamation of both. * Update package.json Add some more keywords so this plugin will show up for searches for those in our plugin library. --- packages/gatsby-plugin-manifest/README.md | 126 +++++++++++++++++- packages/gatsby-plugin-manifest/package.json | 5 +- .../gatsby-plugin-manifest/src/gatsby-node.js | 92 ++++++++++++- .../gatsby-plugin-manifest/src/gatsby-ssr.js | 6 +- 4 files changed, 221 insertions(+), 8 deletions(-) diff --git a/packages/gatsby-plugin-manifest/README.md b/packages/gatsby-plugin-manifest/README.md index ba4d719c6b7b7..dd01f7ba62a4d 100644 --- a/packages/gatsby-plugin-manifest/README.md +++ b/packages/gatsby-plugin-manifest/README.md @@ -1,14 +1,16 @@ # gatsby-plugin-manifest Adds support for shipping a manifest.json with your site. The web application -manifest is a JSON file that lets users (on Android Chrome — -[support in MS Edge & Firefox is under development](http://caniuse.com/#feat=web-app-manifest)) +manifest is a JSON file that lets users (on Android Chrome, Firefox, and Opera — +[support in MS Edge & Safari is under development](http://caniuse.com/#feat=web-app-manifest)) save your web application to their smartphone home screen so it behaves similar to native apps. This article from the Chrome DevRel team is a good intro to the web app manifest—https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/ +For more information see the w3 spec https://www.w3.org/TR/appmanifest/ or Mozilla Docs https://developer.mozilla.org/en-US/docs/Web/Manifest. + If you're using this plugin together with `gatsby-plugin-offline` (recommended), this plugin should be listed _before_ the offline plugin so that it can cache the created manifest.json. @@ -19,6 +21,124 @@ the created manifest.json. ## How to use +This plugin configures Gatsby to create a `manifest.json` file on every site build. + +## Generating icons + +It can be tedious creating the multitude of icon sizes required by different devices and browsers. This plugin includes code to auto-generate smaller icons from a larger src image. + +There are three modes in which icon generation can function: automatic, hybrid, and manual. These three modes are explained below. Icon generation functions differently depending on which of the three you choose. + +### Automatic mode + +In the automatic mode, you are responsible for defining the entire web app manifest except for the icons portion. You only provide a high resolution source icon. The icons themselves and the needed config will be generated at build time. See the example below: + +```javascript +// In your gatsby-config.js +plugins: [ + { + resolve: `gatsby-plugin-manifest`, + options: { + name: "GatsbyJS", + short_name: "GatsbyJS", + start_url: "/", + background_color: "#f7f0eb", + theme_color: "#a2466c", + display: "minimal-ui", + icon: src/images/icon.png // This path is relative to the root of the site. + }, + }, +]; +``` + +When in automatic mode the following json array is injected into the manifest configuration you provide and the icons are generated from it. The source icon you provide should be at least as big as the largest icon being generated. + +```javascript +[ + { + "src": `icons/icon-48x48.png`, + "sizes": `48x48`, + "type": `image/png`, + }, + { + "src": `icons/icon-72x72.png`, + "sizes": `72x72`, + "type": `image/png`, + }, + { + "src": `icons/icon-96x96.png`, + "sizes": `96x96`, + "type": `image/png`, + }, + { + "src": `icons/icon-144x144.png`, + "sizes": `144x144`, + "type": `image/png`, + }, + { + "src": `icons/icon-192x192.png`, + "sizes": `192x192`, + "type": `image/png`, + }, + { + "src": `icons/icon-256x256.png`, + "sizes": `256x256`, + "type": `image/png`, + }, + { + "src": `icons/icon-384x384.png`, + "sizes": `384x384`, + "type": `image/png`, + }, + { + "src": `icons/icon-512x512.png`, + "sizes": `512x512`, + "type": `image/png`, + }, +] +``` + +The automatic mode is the easiest option for most people. + +### Hybrid mode + + However, if you want to include more or fewer sizes, then the hybrid option is for you. Like automatic mode, you should include a high resolution icon to generate smaller icons from. But unlike automatic mode, you provide the `icons` array config and icons are generated based on the sizes defined in your config. Here's an example: + +```javascript +// In your gatsby-config.js +plugins: [ + { + resolve: `gatsby-plugin-manifest`, + options: { + name: "GatsbyJS", + short_name: "GatsbyJS", + start_url: "/", + background_color: "#f7f0eb", + theme_color: "#a2466c", + display: "minimal-ui", + icon: src/images/icon.png // This path is relative to the root of the site. + icons: [ + { + src: `/favicons/android-chrome-192x192.png`, + sizes: `192x192`, + type: `image/png`, + }, + { + src: `/favicons/android-chrome-512x512.png`, + sizes: `512x512`, + type: `image/png`, + }, + ], + }, + }, +]; +``` + +The hybrid option allows the most flexibility while still not requiring you to create most icons sizes manually. + +### Manual mode +In the manual mode, you are responsible for defining the entire web app manifest and providing the defined icons in the static directory. Only icons you provide will be available. There is no automatic resizing done for you. See the example below: + ```javascript // In your gatsby-config.js plugins: [ @@ -51,5 +171,3 @@ plugins: [ }, ]; ``` - -To create `manifest.json`, you need to run `gatsby build`. diff --git a/packages/gatsby-plugin-manifest/package.json b/packages/gatsby-plugin-manifest/package.json index 04a29fe73c02d..302294e934305 100644 --- a/packages/gatsby-plugin-manifest/package.json +++ b/packages/gatsby-plugin-manifest/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "babel-runtime": "^6.26.0", - "bluebird": "^3.5.0" + "bluebird": "^3.5.0", + "sharp": "^0.20.1" }, "devDependencies": { "babel-cli": "^6.26.0", @@ -18,6 +19,8 @@ "keywords": [ "gatsby", "gatsby-plugin", + "favicon", + "icons", "manifest.json", "progressive-web-app", "pwa" diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index 6b1d21f88d4ff..c56eade99654b 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -1,10 +1,98 @@ const fs = require(`fs`) const Promise = require(`bluebird`) +const sharp = require(`sharp`) + +// default icons for generating icons +const defaultIcons = [ + { + "src": `icons/icon-48x48.png`, + "sizes": `48x48`, + "type": `image/png`, + }, + { + "src": `icons/icon-72x72.png`, + "sizes": `72x72`, + "type": `image/png`, + }, + { + "src": `icons/icon-96x96.png`, + "sizes": `96x96`, + "type": `image/png`, + }, + { + "src": `icons/icon-144x144.png`, + "sizes": `144x144`, + "type": `image/png`, + }, + { + "src": `icons/icon-192x192.png`, + "sizes": `192x192`, + "type": `image/png`, + }, + { + "src": `icons/icon-256x256.png`, + "sizes": `256x256`, + "type": `image/png`, + }, + { + "src": `icons/icon-384x384.png`, + "sizes": `384x384`, + "type": `image/png`, + }, + { + "src": `icons/icon-512x512.png`, + "sizes": `512x512`, + "type": `image/png`, + }, +] + +sharp.simd(true) + +function generateIcons(icons, srcIcon) { + return Promise.map(icons, icon => { + const size = parseInt(icon.sizes.substring(0, icon.sizes.lastIndexOf(`x`))) + const imgPath = `./public/` + icon.src + + return sharp(srcIcon) + .resize(size) + .toFile(imgPath) + .then(() => { + }) + }) +} exports.onPostBuild = (args, pluginOptions) => new Promise(resolve => { + const { icon } = pluginOptions const manifest = { ...pluginOptions } + + // Delete options we won't pass to the manifest.json. delete manifest.plugins - fs.writeFileSync(`./public/manifest.json`, JSON.stringify(manifest)) - resolve() + delete manifest.icon + + // If icons are not manually defined, use the default icon set. + if (!manifest.icons) { + manifest.icons = defaultIcons + } + + // Determine destination path for icons. + const iconPath = `./public/` + manifest.icons[0].src.substring(0, manifest.icons[0].src.lastIndexOf(`/`)) + + //create destination directory if it doesn't exist + if (!fs.existsSync(iconPath)){ + fs.mkdirSync(iconPath) + } + + fs.writeFileSync(`${iconPath}/manifest.json`, JSON.stringify(manifest)) + + // Only auto-generate icons if a src icon is defined. + if (icon !== undefined) { + generateIcons(manifest.icons, icon).then(() => { + //images have been generated + console.log(`done`) + resolve() + }) + } else { + resolve() + } }) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js index 000b8c8ab6c95..222d6d0013205 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js @@ -2,11 +2,15 @@ import React from "react" import { withPrefix } from "gatsby-link" exports.onRenderBody = ({ setHeadComponents }, pluginOptions) => { + + const { icons } = pluginOptions + const iconPath = icons[0].src.substring(0, icons[0].src.lastIndexOf(`/`)) + setHeadComponents([ ,