From 455d2dddae8ee2b1e3e4714b51b6b3c2a81cdd50 Mon Sep 17 00:00:00 2001 From: CanRau Date: Thu, 18 Apr 2019 17:50:55 -0500 Subject: [PATCH 01/17] feat(gatsby-plugin-manifest): add i18n, localization --- .../__snapshots__/gatsby-ssr.js.snap | 98 +++++++++++++++++++ .../src/__tests__/gatsby-node.js | 43 ++++++++ .../src/__tests__/gatsby-ssr.js | 38 +++++++ .../gatsby-plugin-manifest/src/gatsby-node.js | 26 +++-- .../gatsby-plugin-manifest/src/gatsby-ssr.js | 17 +++- 5 files changed, 212 insertions(+), 10 deletions(-) diff --git a/packages/gatsby-plugin-manifest/src/__tests__/__snapshots__/gatsby-ssr.js.snap b/packages/gatsby-plugin-manifest/src/__tests__/__snapshots__/gatsby-ssr.js.snap index 7512d688892f4..3a0930cb062c4 100644 --- a/packages/gatsby-plugin-manifest/src/__tests__/__snapshots__/gatsby-ssr.js.snap +++ b/packages/gatsby-plugin-manifest/src/__tests__/__snapshots__/gatsby-ssr.js.snap @@ -492,6 +492,104 @@ Array [ ] `; +exports[`gatsby-plugin-manifest Manifest Link Generation Adds correct (default) i18n "manifest" link to head 1`] = ` +Array [ + , + , + , + , + , + , + , + , + , +] +`; + +exports[`gatsby-plugin-manifest Manifest Link Generation Adds correct (es) i18n "manifest" link to head 1`] = ` +Array [ + , + , + , + , + , + , + , + , + , +] +`; + exports[`gatsby-plugin-manifest Manifest Link Generation Does not add a "theme color" meta tag if "theme_color_in_head" is set to false 1`] = ` Array [ { expect(content.icons[0].purpose).toEqual(`all`) expect(content.icons[1].purpose).toEqual(`maskable`) }) + + it(`generates all language versions`, async () => { + fs.statSync.mockReturnValueOnce({ isFile: () => true }) + const pluginSpecificOptions = { + manifests: [ + { + ...manifestOptions, + start_url: `/de/`, + regex: `^/de/.*`, + language: `de`, + }, + { + ...manifestOptions, + start_url: `/es/`, + regex: `^/es/.*`, + language: `es`, + }, + { + ...manifestOptions, + start_url: `/`, + regex: `.*`, + }, + ], + } + const expectedResults = pluginSpecificOptions.manifests.map( + ({ regex, language, ...x }) => x + ) + + await onPostBootstrap(apiArgs, pluginSpecificOptions) + + expect(fs.writeFileSync).toHaveBeenCalledWith( + expect.anything(), + JSON.stringify(expectedResults[0]) + ) + expect(fs.writeFileSync).toHaveBeenCalledWith( + expect.anything(), + JSON.stringify(expectedResults[1]) + ) + expect(fs.writeFileSync).toHaveBeenCalledWith( + expect.anything(), + JSON.stringify(expectedResults[2]) + ) + }) }) diff --git a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js index 056c9cad504fc..6f7275a91a67a 100644 --- a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js +++ b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js @@ -15,6 +15,7 @@ const setHeadComponents = args => (headComponents = headComponents.concat(args)) const ssrArgs = { setHeadComponents, + pathname: `/`, } describe(`gatsby-plugin-manifest`, () => { @@ -91,6 +92,43 @@ describe(`gatsby-plugin-manifest`, () => { }) expect(headComponents).toMatchSnapshot() }) + + const i18nArgs = [ + { + ...ssrArgs, + pathname: `/about-us`, + testName: `Adds correct (default) i18n "manifest" link to head`, + }, + { + ...ssrArgs, + pathname: `/es/sobre-nosotros`, + testName: `Adds correct (es) i18n "manifest" link to head`, + }, + ] + + i18nArgs.forEach(({ testName, ...args }) => + it(testName, () => { + onRenderBody(args, { + manifests: [ + { + start_url: `/de/`, + regex: `^/de/.*`, + language: `de`, + }, + { + start_url: `/es/`, + regex: `^/es/.*`, + language: `es`, + }, + { + start_url: `/`, + regex: `.*`, + }, + ], + }) + expect(headComponents).toMatchSnapshot() + }) + ) }) describe(`Legacy Icons`, () => { diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index ab9b13e27f43c..ae39e01e3f682 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -43,8 +43,22 @@ function generateIcons(icons, srcIcon) { ) } -exports.onPostBootstrap = async ({ reporter }, pluginOptions) => { - const { icon, ...manifest } = pluginOptions +exports.onPostBootstrap = async ({ reporter }, { manifests, ...manifest }) => { + const activity = reporter.activityTimer(`Build manifest and related icons`) + activity.start() + + if (Array.isArray(manifests)) { + await Promise.all(manifests.map(x => makeManifest(reporter, x))) + } else { + await makeManifest(reporter, manifest) + } + + activity.end() +} + +const makeManifest = async (reporter, pluginOptions) => { + const { icon, language, ...manifest } = pluginOptions + const suffix = language ? `_${language}` : `` // Delete options we won't pass to the manifest.webmanifest. delete manifest.plugins @@ -54,9 +68,7 @@ exports.onPostBootstrap = async ({ reporter }, pluginOptions) => { delete manifest.crossOrigin delete manifest.icon_options delete manifest.include_favicon - - const activity = reporter.activityTimer(`Build manifest and related icons`) - activity.start() + delete manifest.regex // If icons are not manually defined, use the default icon set. if (!manifest.icons) { @@ -132,9 +144,7 @@ exports.onPostBootstrap = async ({ reporter }, pluginOptions) => { //Write manifest fs.writeFileSync( - path.join(`public`, `manifest.webmanifest`), + path.join(`public`, `manifest${suffix}.webmanifest`), JSON.stringify(manifest) ) - - activity.end() } diff --git a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js index fb2625893571f..081f8c65d1338 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js @@ -10,7 +10,17 @@ const withPrefix = withAssetPrefix || fallbackWithPrefix let iconDigest = null -exports.onRenderBody = ({ setHeadComponents }, pluginOptions) => { +exports.onRenderBody = ( + { setHeadComponents, pathname = `/` }, + pluginOptions +) => { + if (Array.isArray(pluginOptions.manifests)) { + pluginOptions = pluginOptions.manifests.find(x => + RegExp(x.regex || `^/${x.language}/.*`).test(pathname) + ) + if (!pluginOptions) return false + } + // We use this to build a final array to pass as the argument to setHeadComponents at the end of onRenderBody. let headComponents = [] @@ -49,12 +59,14 @@ exports.onRenderBody = ({ setHeadComponents }, pluginOptions) => { } } + const suffix = pluginOptions.language ? `_${pluginOptions.language}` : `` + // Add manifest link tag. headComponents.push( ) @@ -97,4 +109,5 @@ exports.onRenderBody = ({ setHeadComponents }, pluginOptions) => { } setHeadComponents(headComponents) + return true } From 004285c2e93aa676712f401954437d47db8e36ae Mon Sep 17 00:00:00 2001 From: CanRau Date: Thu, 18 Apr 2019 18:15:55 -0500 Subject: [PATCH 02/17] feat(gatsby-plugin-manifest): update readme --- packages/gatsby-plugin-manifest/README.md | 77 +++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/packages/gatsby-plugin-manifest/README.md b/packages/gatsby-plugin-manifest/README.md index 876bd3bf2342b..7a3eddb847bad 100644 --- a/packages/gatsby-plugin-manifest/README.md +++ b/packages/gatsby-plugin-manifest/README.md @@ -69,6 +69,10 @@ There are three modes in which icon generation can function: automatic, hybrid, - Legacy icon support - yes - Cache busting - never +- i18n - Generate separate versions for multiple languages + + - Supports all 3 modes + **_IMPORTANT:_** For best results, if you're providing an icon for generation it should be... - ...at least as big as the largest icon being generated (512x512 by default). @@ -130,6 +134,79 @@ icons: [ In the manual mode, you are responsible for defining the entire web app manifest and providing the defined icons in the [static](https://www.gatsbyjs.org/docs/static-folder/) folder. Only icons you provide will be available. There is no automatic resizing done for you. +### i18n – Multilang configuration + +```js +// in gatsby-config.js +module.exports = { + plugins: [ + { + resolve: `gatsby-plugin-manifest`, + options: { + manifests: [ + { + name: `GatsbyJS`, + short_name: `GatsbyJS`, + start_url: `/de/`, + regex: `^/de/.*`, + language: `de`, + background_color: `#f7f0eb`, + theme_color: `#a2466c`, + display: `standalone`, + }, + { + name: `GatsbyJS`, + short_name: `GatsbyJS`, + start_url: `/`, + regex: `.*`, + background_color: `#f7f0eb`, + theme_color: `#a2466c`, + display: `standalone`, + }, + ], + }, + }, + ], +} +``` + +You can add as many languages as you want, as well as mix and match auto, hybrid & manual icon modes. +Use basic composition like `Object.assign` or `rest/spread` operator (if supported by your Node version) to merge in shared configs so you don't have to repeat yourself. The above example could be written as: + +```js +// in gatsby-config.js +const sharedManifestOptions = { + name: `GatsbyJS`, + short_name: `GatsbyJS`, + background_color: `#f7f0eb`, + theme_color: `#a2466c`, + display: `standalone`, +} +module.exports = { + plugins: [ + { + resolve: `gatsby-plugin-manifest`, + options: { + manifests: [ + Object.assign({}, sharedManifestOptions, { + start_url: `/de/`, + regex: `^/de/.*`, + language: `de`, + }), + Object.assign({}, sharedManifestOptions, { + start_url: `/`, + regex: `.*`, + }), + ], + }, + }, + ], +} +``` + +Make sure the default language comes last. +You don't have to specify a "default" language though, all pathes can start with a language, or you can tweak the regex to match whatever part of the `pathname` you like. + ### Feature configuration - **Optional** #### Iterative icon options From 6d270ba3c49e8bcd07a74a7ea8f833668bae34cc Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Tue, 23 Apr 2019 23:48:39 -0500 Subject: [PATCH 03/17] fix(gatsby-plugin-manifest): make map callback prop more readable Co-Authored-By: CanRau --- packages/gatsby-plugin-manifest/src/gatsby-node.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index ae39e01e3f682..e7a84277f6667 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -48,7 +48,7 @@ exports.onPostBootstrap = async ({ reporter }, { manifests, ...manifest }) => { activity.start() if (Array.isArray(manifests)) { - await Promise.all(manifests.map(x => makeManifest(reporter, x))) + await Promise.all(manifests.map(manifest => makeManifest(reporter, manifest))) } else { await makeManifest(reporter, manifest) } From 75447d269eeb70221e14a935434543de38eaeda6 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Tue, 23 Apr 2019 23:50:31 -0500 Subject: [PATCH 04/17] fix(gatsby-plugin-manifest): make find callback prop more readable Co-Authored-By: CanRau --- packages/gatsby-plugin-manifest/src/gatsby-ssr.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js index 081f8c65d1338..f4bee164fc4b9 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js @@ -15,7 +15,7 @@ exports.onRenderBody = ( pluginOptions ) => { if (Array.isArray(pluginOptions.manifests)) { - pluginOptions = pluginOptions.manifests.find(x => + pluginOptions = pluginOptions.manifests.find(manifest => RegExp(x.regex || `^/${x.language}/.*`).test(pathname) ) if (!pluginOptions) return false From 4e7eaf8e3dd6d1381d976b429baf7a6f98ffc96e Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Tue, 23 Apr 2019 23:54:41 -0500 Subject: [PATCH 05/17] fix(gatsby-plugin-manifest): make find callback prop more readable Co-Authored-By: CanRau --- packages/gatsby-plugin-manifest/src/gatsby-ssr.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js index f4bee164fc4b9..88dfb358d4b99 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js @@ -16,7 +16,7 @@ exports.onRenderBody = ( ) => { if (Array.isArray(pluginOptions.manifests)) { pluginOptions = pluginOptions.manifests.find(manifest => - RegExp(x.regex || `^/${x.language}/.*`).test(pathname) + RegExp(manifest.regex || `^/${manifest.language}/.*`).test(pathname) ) if (!pluginOptions) return false } From 5ea90c417c543f4d902ecc278e98444efb0fc5d6 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Tue, 23 Apr 2019 23:56:28 -0500 Subject: [PATCH 06/17] docs(gatsby-plugin-manifest): decrease config header level Co-Authored-By: CanRau --- packages/gatsby-plugin-manifest/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gatsby-plugin-manifest/README.md b/packages/gatsby-plugin-manifest/README.md index 7a3eddb847bad..2c3364ba49526 100644 --- a/packages/gatsby-plugin-manifest/README.md +++ b/packages/gatsby-plugin-manifest/README.md @@ -134,7 +134,7 @@ icons: [ In the manual mode, you are responsible for defining the entire web app manifest and providing the defined icons in the [static](https://www.gatsbyjs.org/docs/static-folder/) folder. Only icons you provide will be available. There is no automatic resizing done for you. -### i18n – Multilang configuration +#### i18n – Multilang configuration ```js // in gatsby-config.js From baece08b3ceea7b150246968cbb9608bdb4f050f Mon Sep 17 00:00:00 2001 From: CanRau Date: Wed, 24 Apr 2019 00:17:44 -0500 Subject: [PATCH 07/17] feat(gatsby-plugin-manifest): integrate suggestions by moonmeister language manifests in the `manifests` prop now merge top level options as suggested here https://github.com/gatsbyjs/gatsby/pull/13471#issuecomment-486056407 use option merging style in README docs: change features as suggested here https://github.com/gatsbyjs/gatsby/pull/13471#discussion_r277955596 --- packages/gatsby-plugin-manifest/README.md | 56 ++++--------------- .../src/__tests__/gatsby-node.js | 46 +++++++++++++++ .../gatsby-plugin-manifest/src/gatsby-node.js | 6 +- .../gatsby-plugin-manifest/src/gatsby-ssr.js | 10 +++- 4 files changed, 68 insertions(+), 50 deletions(-) diff --git a/packages/gatsby-plugin-manifest/README.md b/packages/gatsby-plugin-manifest/README.md index 2c3364ba49526..deab10edea69c 100644 --- a/packages/gatsby-plugin-manifest/README.md +++ b/packages/gatsby-plugin-manifest/README.md @@ -56,22 +56,21 @@ There are three modes in which icon generation can function: automatic, hybrid, - Favicon - yes - Legacy icon support - yes - Cache busting - yes + - internationalization - optional - Hybrid - Generate a manually configured set of icons from a single source icon. - Favicon - yes - Legacy icon support - yes - Cache busting - yes + - internationalization - optional - Manual - Don't generate or pre-configure any icons. - Favicon - never - Legacy icon support - yes - Cache busting - never - -- i18n - Generate separate versions for multiple languages - - - Supports all 3 modes + - internationalization - optional **_IMPORTANT:_** For best results, if you're providing an icon for generation it should be... @@ -143,25 +142,22 @@ module.exports = { { resolve: `gatsby-plugin-manifest`, options: { + name: `GatsbyJS`, + short_name: `Gatsby`, + display: `standalone`, manifests: [ { - name: `GatsbyJS`, - short_name: `GatsbyJS`, start_url: `/de/`, regex: `^/de/.*`, language: `de`, background_color: `#f7f0eb`, theme_color: `#a2466c`, - display: `standalone`, }, { - name: `GatsbyJS`, - short_name: `GatsbyJS`, start_url: `/`, regex: `.*`, - background_color: `#f7f0eb`, - theme_color: `#a2466c`, - display: `standalone`, + background_color: `#663399`, + theme_color: `#fff`, }, ], }, @@ -171,41 +167,9 @@ module.exports = { ``` You can add as many languages as you want, as well as mix and match auto, hybrid & manual icon modes. -Use basic composition like `Object.assign` or `rest/spread` operator (if supported by your Node version) to merge in shared configs so you don't have to repeat yourself. The above example could be written as: - -```js -// in gatsby-config.js -const sharedManifestOptions = { - name: `GatsbyJS`, - short_name: `GatsbyJS`, - background_color: `#f7f0eb`, - theme_color: `#a2466c`, - display: `standalone`, -} -module.exports = { - plugins: [ - { - resolve: `gatsby-plugin-manifest`, - options: { - manifests: [ - Object.assign({}, sharedManifestOptions, { - start_url: `/de/`, - regex: `^/de/.*`, - language: `de`, - }), - Object.assign({}, sharedManifestOptions, { - start_url: `/`, - regex: `.*`, - }), - ], - }, - }, - ], -} -``` +Shared values can be defined as root options, as in the example above. -Make sure the default language comes last. -You don't have to specify a "default" language though, all pathes can start with a language, or you can tweak the regex to match whatever part of the `pathname` you like. +Make sure the default language comes last. You don't have to specify a "default" language though, all paths can start with a language, or you can tweak the regex to match whatever part of the `pathname` you like. ### Feature configuration - **Optional** diff --git a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js index 5f418dee248dc..c0d42d7ad2cdc 100644 --- a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js @@ -318,4 +318,50 @@ describe(`Test plugin manifest options`, () => { JSON.stringify(expectedResults[2]) ) }) + + it(`merges default and language options`, async () => { + fs.statSync.mockReturnValueOnce({ isFile: () => true }) + const pluginSpecificOptions = { + ...manifestOptions, + manifests: [ + { + start_url: `/de/`, + regex: `^/de/.*`, + language: `de`, + }, + { + start_url: `/es/`, + regex: `^/es/.*`, + language: `es`, + }, + { + start_url: `/`, + regex: `.*`, + }, + ], + } + const expectedResults = pluginSpecificOptions.manifests.map( + ({ regex, language, manifest }) => { + return { + ...manifestOptions, + ...manifest, + } + } + ) + + await onPostBootstrap(apiArgs, pluginSpecificOptions) + + expect(fs.writeFileSync).toHaveBeenCalledWith( + expect.anything(), + JSON.stringify(expectedResults[0]) + ) + expect(fs.writeFileSync).toHaveBeenCalledWith( + expect.anything(), + JSON.stringify(expectedResults[1]) + ) + expect(fs.writeFileSync).toHaveBeenCalledWith( + expect.anything(), + JSON.stringify(expectedResults[2]) + ) + }) }) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index e7a84277f6667..d73cfe7764a27 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -48,7 +48,11 @@ exports.onPostBootstrap = async ({ reporter }, { manifests, ...manifest }) => { activity.start() if (Array.isArray(manifests)) { - await Promise.all(manifests.map(manifest => makeManifest(reporter, manifest))) + await Promise.all( + manifests.map(_manifest => + makeManifest(reporter, { ...manifest, ..._manifest }) + ) + ) } else { await makeManifest(reporter, manifest) } diff --git a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js index 88dfb358d4b99..85fcd42e5b271 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js @@ -12,12 +12,16 @@ let iconDigest = null exports.onRenderBody = ( { setHeadComponents, pathname = `/` }, - pluginOptions + { manifests, ...pluginOptions } ) => { - if (Array.isArray(pluginOptions.manifests)) { - pluginOptions = pluginOptions.manifests.find(manifest => + if (Array.isArray(manifests)) { + const manifest = manifests.find(manifest => RegExp(manifest.regex || `^/${manifest.language}/.*`).test(pathname) ) + pluginOptions = { + ...pluginOptions, + ...manifest, + } if (!pluginOptions) return false } From a0daefe4b4b08a9c8a02680992def57a5e7b46be Mon Sep 17 00:00:00 2001 From: CanRau Date: Wed, 24 Apr 2019 14:14:45 -0500 Subject: [PATCH 08/17] feat(gatsby-plugin-manifest): incorporate suggestions * rename `manifests` to `localize` * rename `language` to `lang` * include `lang` in manifest file * merge root options and locales * ensure root only merges if it has start_url provided * always generate root options * remove regex * use start_url as matcher * update docs & test --- packages/gatsby-plugin-manifest/README.md | 15 +++----- .../src/__tests__/gatsby-node.js | 37 ++++++++----------- .../src/__tests__/gatsby-ssr.js | 13 ++----- .../gatsby-plugin-manifest/src/gatsby-node.js | 10 ++--- .../gatsby-plugin-manifest/src/gatsby-ssr.js | 11 ++++-- 5 files changed, 37 insertions(+), 49 deletions(-) diff --git a/packages/gatsby-plugin-manifest/README.md b/packages/gatsby-plugin-manifest/README.md index deab10edea69c..ba4165f834144 100644 --- a/packages/gatsby-plugin-manifest/README.md +++ b/packages/gatsby-plugin-manifest/README.md @@ -145,20 +145,17 @@ module.exports = { name: `GatsbyJS`, short_name: `Gatsby`, display: `standalone`, - manifests: [ + icon: `src/images/icon.png`, + start_url: `/`, + background_color: `#663399`, + theme_color: `#fff`, + localize: [ { start_url: `/de/`, - regex: `^/de/.*`, - language: `de`, + lang: `de`, background_color: `#f7f0eb`, theme_color: `#a2466c`, }, - { - start_url: `/`, - regex: `.*`, - background_color: `#663399`, - theme_color: `#fff`, - }, ], }, }, diff --git a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js index c0d42d7ad2cdc..5f83afadceb19 100644 --- a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js @@ -279,29 +279,27 @@ describe(`Test plugin manifest options`, () => { it(`generates all language versions`, async () => { fs.statSync.mockReturnValueOnce({ isFile: () => true }) const pluginSpecificOptions = { - manifests: [ + localize: [ { ...manifestOptions, start_url: `/de/`, - regex: `^/de/.*`, - language: `de`, + lang: `de`, }, { ...manifestOptions, start_url: `/es/`, - regex: `^/es/.*`, - language: `es`, + lang: `es`, }, { ...manifestOptions, start_url: `/`, - regex: `.*`, }, ], } - const expectedResults = pluginSpecificOptions.manifests.map( - ({ regex, language, ...x }) => x - ) + const { localize, ...manifest } = pluginSpecificOptions + const expectedResults = localize.concat(manifest).map(x => { + return { ...manifest, ...x } + }) await onPostBootstrap(apiArgs, pluginSpecificOptions) @@ -323,31 +321,26 @@ describe(`Test plugin manifest options`, () => { fs.statSync.mockReturnValueOnce({ isFile: () => true }) const pluginSpecificOptions = { ...manifestOptions, - manifests: [ + localize: [ { start_url: `/de/`, - regex: `^/de/.*`, - language: `de`, + lang: `de`, }, { start_url: `/es/`, - regex: `^/es/.*`, - language: `es`, - }, - { - start_url: `/`, - regex: `.*`, + lang: `es`, }, ], } - const expectedResults = pluginSpecificOptions.manifests.map( - ({ regex, language, manifest }) => { + const { localize, ...manifest } = pluginSpecificOptions + const expectedResults = localize + .concat(manifest) + .map(({ language, manifest }) => { return { ...manifestOptions, ...manifest, } - } - ) + }) await onPostBootstrap(apiArgs, pluginSpecificOptions) diff --git a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js index 6f7275a91a67a..89cebf4958514 100644 --- a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js +++ b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js @@ -109,20 +109,15 @@ describe(`gatsby-plugin-manifest`, () => { i18nArgs.forEach(({ testName, ...args }) => it(testName, () => { onRenderBody(args, { - manifests: [ + start_url: `/`, + localize: [ { start_url: `/de/`, - regex: `^/de/.*`, - language: `de`, + lang: `de`, }, { start_url: `/es/`, - regex: `^/es/.*`, - language: `es`, - }, - { - start_url: `/`, - regex: `.*`, + lang: `es`, }, ], }) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index d73cfe7764a27..8a14c1dfc0bdb 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -43,11 +43,12 @@ function generateIcons(icons, srcIcon) { ) } -exports.onPostBootstrap = async ({ reporter }, { manifests, ...manifest }) => { +exports.onPostBootstrap = async ({ reporter }, { localize, ...manifest }) => { const activity = reporter.activityTimer(`Build manifest and related icons`) activity.start() - if (Array.isArray(manifests)) { + if (Array.isArray(localize)) { + const manifests = manifest.start_url ? localize.concat(manifest) : localize await Promise.all( manifests.map(_manifest => makeManifest(reporter, { ...manifest, ..._manifest }) @@ -61,8 +62,8 @@ exports.onPostBootstrap = async ({ reporter }, { manifests, ...manifest }) => { } const makeManifest = async (reporter, pluginOptions) => { - const { icon, language, ...manifest } = pluginOptions - const suffix = language ? `_${language}` : `` + const { icon, ...manifest } = pluginOptions + const suffix = pluginOptions.lang ? `_${pluginOptions.lang}` : `` // Delete options we won't pass to the manifest.webmanifest. delete manifest.plugins @@ -72,7 +73,6 @@ const makeManifest = async (reporter, pluginOptions) => { delete manifest.crossOrigin delete manifest.icon_options delete manifest.include_favicon - delete manifest.regex // If icons are not manually defined, use the default icon set. if (!manifest.icons) { diff --git a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js index 85fcd42e5b271..50f79e61aa611 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js @@ -12,11 +12,14 @@ let iconDigest = null exports.onRenderBody = ( { setHeadComponents, pathname = `/` }, - { manifests, ...pluginOptions } + { localize, ...pluginOptions } ) => { - if (Array.isArray(manifests)) { + if (Array.isArray(localize)) { + const manifests = pluginOptions.start_url + ? localize.concat(pluginOptions) + : localize const manifest = manifests.find(manifest => - RegExp(manifest.regex || `^/${manifest.language}/.*`).test(pathname) + RegExp(`^${manifest.start_url}.*`, `i`).test(pathname) ) pluginOptions = { ...pluginOptions, @@ -63,7 +66,7 @@ exports.onRenderBody = ( } } - const suffix = pluginOptions.language ? `_${pluginOptions.language}` : `` + const suffix = pluginOptions.lang ? `_${pluginOptions.lang}` : `` // Add manifest link tag. headComponents.push( From 7af0716ad583f04e1f4cdf7f0ddec369b68bc83a Mon Sep 17 00:00:00 2001 From: CanRau Date: Mon, 29 Apr 2019 00:55:40 -0500 Subject: [PATCH 09/17] docs(gatsby-plugin-manifest): merge moonmeisters description --- l10ndocs.patch | 107 ++++++++++++++++++++++ packages/gatsby-plugin-manifest/README.md | 37 +++++--- 2 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 l10ndocs.patch diff --git a/l10ndocs.patch b/l10ndocs.patch new file mode 100644 index 0000000000000..6268779196d9c --- /dev/null +++ b/l10ndocs.patch @@ -0,0 +1,107 @@ +From 7379ac1dec0a4fd31a6528d59064d30870dcb29c Mon Sep 17 00:00:00 2001 +From: Alex Moon +Date: Mon, 29 Apr 2019 05:14:52 +0000 +Subject: [PATCH] docs: did some reorganization to make more consistent with + existing docs and added some exeternal refereces. + +--- + packages/gatsby-plugin-manifest/README.md | 37 ++++++++++++++--------- + 1 file changed, 22 insertions(+), 15 deletions(-) + +diff --git a/packages/gatsby-plugin-manifest/README.md b/packages/gatsby-plugin-manifest/README.md +index fe92be4d6..3c25d155b 100644 +--- a/packages/gatsby-plugin-manifest/README.md ++++ b/packages/gatsby-plugin-manifest/README.md +@@ -9,6 +9,7 @@ This plugin provides several features beyond manifest configuration to make your + - [Favicon support](https://www.w3.org/2005/10/howto-favicon) + - Legacy icon support (iOS)[^1] + - [Cache busting](https://www.keycdn.com/support/what-is-cache-busting) ++- Localization - Provides unqiue manifests for path-based localization + + Each of these features has extensive configuration available so you're always in control. + +@@ -56,21 +57,21 @@ There are three modes in which icon generation can function: automatic, hybrid, + - Favicon - yes + - Legacy icon support - yes + - Cache busting - yes +- - internationalization - optional ++ - Localization - optional + + - Hybrid - Generate a manually configured set of icons from a single source icon. + + - Favicon - yes + - Legacy icon support - yes + - Cache busting - yes +- - internationalization - optional ++ - Localization - optional + + - Manual - Don't generate or pre-configure any icons. + + - Favicon - never + - Legacy icon support - yes + - Cache busting - never +- - internationalization - optional ++ - Localization - optional + + **_IMPORTANT:_** For best results, if you're providing an icon for generation it should be... + +@@ -133,7 +134,17 @@ icons: [ + + In the manual mode, you are responsible for defining the entire web app manifest and providing the defined icons in the [static](https://www.gatsbyjs.org/docs/static-folder/) folder. Only icons you provide will be available. There is no automatic resizing done for you. + +-#### i18n – Multilang configuration ++### Feature configuration - **Optional** ++ ++#### Localization configuration ++ ++Localization allows you to crete unique manifests for each localized version of your site. As many languages as you want are supported. Localization requires unique paths for each language (e.g. if your default about page is at `/about`, the german(`de`) version would be `/de/about`) ++ ++The default site language should be configured in your root plugin options. Any additional languages should be defined in the `localize` array. The root settings will be used as defaults if not overridden in a locale. Any configuration option available in the root is also available in the `localize` array. ++ ++`lang` and `start_url` are the only _required_ options in the array objects. `name`, `short_name`, and `description` are [recommended](https://www.w3.org/TR/appmanifest/#dfn-directionality-capable-members) to be translated if being used in the default language. All other config options are optional. This is helpful if you want to provide unique icons for each locale. ++ ++The [`lang` option](https://www.w3.org/TR/appmanifest/#lang-member) is part of the web app manifest specification and thus is required to be a [valid language tag](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) + + ```js + // in gatsby-config.js +@@ -142,8 +153,10 @@ module.exports = { + { + resolve: `gatsby-plugin-manifest`, + options: { +- name: `GatsbyJS`, +- short_name: `Gatsby`, ++ name: `The Cool Application`, ++ short_name: `Cool App`, ++ description: `The application does cool things and makes your life better.`, ++ lang: `en`, + display: `standalone`, + icon: `src/images/icon.png`, + start_url: `/`, +@@ -153,8 +166,9 @@ module.exports = { + { + start_url: `/de/`, + lang: `de`, +- background_color: `#f7f0eb`, +- theme_color: `#a2466c`, ++ name: `Die coole Anwendung`, ++ short_name: `Coole Anwendung`, ++ description: `Die Anwendung macht coole Dinge und macht Ihr Leben besser.`, + }, + ], + }, +@@ -163,13 +177,6 @@ module.exports = { + } + ``` + +-You can add as many languages as you want, as well as mix and match auto, hybrid & manual icon modes. +-Shared values can be defined as root options, as in the example above. +- +-Make sure the default language comes last. You don't have to specify a "default" language though, all paths can start with a language, or you can tweak the regex to match whatever part of the `pathname` you like. +- +-### Feature configuration - **Optional** +- + #### Iterative icon options + + The `icon_options` object may be used to iteratively add configuration items to the `icons` array. Any options included in this object will be merged with each object of the `icons` array (custom or default). Key value pairs already in the `icons` array will take precedence over duplicate items in the `icon_options` array. +-- +2.21.0 diff --git a/packages/gatsby-plugin-manifest/README.md b/packages/gatsby-plugin-manifest/README.md index ba4165f834144..4ddbd77de3d06 100644 --- a/packages/gatsby-plugin-manifest/README.md +++ b/packages/gatsby-plugin-manifest/README.md @@ -9,6 +9,7 @@ This plugin provides several features beyond manifest configuration to make your - [Favicon support](https://www.w3.org/2005/10/howto-favicon) - Legacy icon support (iOS)[^1] - [Cache busting](https://www.keycdn.com/support/what-is-cache-busting) +- Localization - Provides unqiue manifests for path-based localization Each of these features has extensive configuration available so you're always in control. @@ -56,21 +57,21 @@ There are three modes in which icon generation can function: automatic, hybrid, - Favicon - yes - Legacy icon support - yes - Cache busting - yes - - internationalization - optional + - Localization - optional - Hybrid - Generate a manually configured set of icons from a single source icon. - Favicon - yes - Legacy icon support - yes - Cache busting - yes - - internationalization - optional + - Localization - optional - Manual - Don't generate or pre-configure any icons. - Favicon - never - Legacy icon support - yes - Cache busting - never - - internationalization - optional + - Localization - optional **_IMPORTANT:_** For best results, if you're providing an icon for generation it should be... @@ -133,7 +134,17 @@ icons: [ In the manual mode, you are responsible for defining the entire web app manifest and providing the defined icons in the [static](https://www.gatsbyjs.org/docs/static-folder/) folder. Only icons you provide will be available. There is no automatic resizing done for you. -#### i18n – Multilang configuration +### Feature configuration - **Optional** + +#### Localization configuration + +Localization allows you to crete unique manifests for each localized version of your site. As many languages as you want are supported. Localization requires unique paths for each language (e.g. if your default about page is at `/about`, the german(`de`) version would be `/de/about`) + +The default site language should be configured in your root plugin options. Any additional languages should be defined in the `localize` array. The root settings will be used as defaults if not overridden in a locale. Any configuration option available in the root is also available in the `localize` array. + +`lang` and `start_url` are the only _required_ options in the array objects. `name`, `short_name`, and `description` are [recommended](https://www.w3.org/TR/appmanifest/#dfn-directionality-capable-members) to be translated if being used in the default language. All other config options are optional. This is helpful if you want to provide unique icons for each locale. + +The [`lang` option](https://www.w3.org/TR/appmanifest/#lang-member) is part of the web app manifest specification and thus is required to be a [valid language tag](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) ```js // in gatsby-config.js @@ -142,8 +153,10 @@ module.exports = { { resolve: `gatsby-plugin-manifest`, options: { - name: `GatsbyJS`, - short_name: `Gatsby`, + name: `The Cool Application`, + short_name: `Cool App`, + description: `The application does cool things and makes your life better.`, + lang: `en`, display: `standalone`, icon: `src/images/icon.png`, start_url: `/`, @@ -153,8 +166,9 @@ module.exports = { { start_url: `/de/`, lang: `de`, - background_color: `#f7f0eb`, - theme_color: `#a2466c`, + name: `Die coole Anwendung`, + short_name: `Coole Anwendung`, + description: `Die Anwendung macht coole Dinge und macht Ihr Leben besser.`, }, ], }, @@ -163,13 +177,6 @@ module.exports = { } ``` -You can add as many languages as you want, as well as mix and match auto, hybrid & manual icon modes. -Shared values can be defined as root options, as in the example above. - -Make sure the default language comes last. You don't have to specify a "default" language though, all paths can start with a language, or you can tweak the regex to match whatever part of the `pathname` you like. - -### Feature configuration - **Optional** - #### Iterative icon options The `icon_options` object may be used to iteratively add configuration items to the `icons` array. Any options included in this object will be merged with each object of the `icons` array (custom or default). Key value pairs already in the `icons` array will take precedence over duplicate items in the `icon_options` array. From 31300136f6216b056d9638e4c6130c1caa0f5e1c Mon Sep 17 00:00:00 2001 From: CanRau Date: Mon, 29 Apr 2019 01:04:42 -0500 Subject: [PATCH 10/17] feat(gatsby-plugin-manifest): merge moonmeisters naming suggestions --- packages/gatsby-plugin-manifest/src/gatsby-node.js | 6 ++---- packages/gatsby-plugin-manifest/src/gatsby-ssr.js | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index 8a14c1dfc0bdb..864809f6c889a 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -48,11 +48,9 @@ exports.onPostBootstrap = async ({ reporter }, { localize, ...manifest }) => { activity.start() if (Array.isArray(localize)) { - const manifests = manifest.start_url ? localize.concat(manifest) : localize + const locales = manifest.start_url ? localize.concat(manifest) : localize await Promise.all( - manifests.map(_manifest => - makeManifest(reporter, { ...manifest, ..._manifest }) - ) + locales.map(locale => makeManifest(reporter, { ...manifest, ...locale })) ) } else { await makeManifest(reporter, manifest) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js index 50f79e61aa611..ba1108e0d60d8 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js @@ -15,11 +15,11 @@ exports.onRenderBody = ( { localize, ...pluginOptions } ) => { if (Array.isArray(localize)) { - const manifests = pluginOptions.start_url + const locales = pluginOptions.start_url ? localize.concat(pluginOptions) : localize - const manifest = manifests.find(manifest => - RegExp(`^${manifest.start_url}.*`, `i`).test(pathname) + const manifest = locales.find(locale => + RegExp(`^${locale.start_url}.*`, `i`).test(pathname) ) pluginOptions = { ...pluginOptions, From a938cba1cda19989ee5ad75ce95030c89fcf3ed9 Mon Sep 17 00:00:00 2001 From: CanRau Date: Mon, 29 Apr 2019 01:37:51 -0500 Subject: [PATCH 11/17] remove accidentally commited .patch file --- l10ndocs.patch | 107 ------------------------------------------------- 1 file changed, 107 deletions(-) delete mode 100644 l10ndocs.patch diff --git a/l10ndocs.patch b/l10ndocs.patch deleted file mode 100644 index 6268779196d9c..0000000000000 --- a/l10ndocs.patch +++ /dev/null @@ -1,107 +0,0 @@ -From 7379ac1dec0a4fd31a6528d59064d30870dcb29c Mon Sep 17 00:00:00 2001 -From: Alex Moon -Date: Mon, 29 Apr 2019 05:14:52 +0000 -Subject: [PATCH] docs: did some reorganization to make more consistent with - existing docs and added some exeternal refereces. - ---- - packages/gatsby-plugin-manifest/README.md | 37 ++++++++++++++--------- - 1 file changed, 22 insertions(+), 15 deletions(-) - -diff --git a/packages/gatsby-plugin-manifest/README.md b/packages/gatsby-plugin-manifest/README.md -index fe92be4d6..3c25d155b 100644 ---- a/packages/gatsby-plugin-manifest/README.md -+++ b/packages/gatsby-plugin-manifest/README.md -@@ -9,6 +9,7 @@ This plugin provides several features beyond manifest configuration to make your - - [Favicon support](https://www.w3.org/2005/10/howto-favicon) - - Legacy icon support (iOS)[^1] - - [Cache busting](https://www.keycdn.com/support/what-is-cache-busting) -+- Localization - Provides unqiue manifests for path-based localization - - Each of these features has extensive configuration available so you're always in control. - -@@ -56,21 +57,21 @@ There are three modes in which icon generation can function: automatic, hybrid, - - Favicon - yes - - Legacy icon support - yes - - Cache busting - yes -- - internationalization - optional -+ - Localization - optional - - - Hybrid - Generate a manually configured set of icons from a single source icon. - - - Favicon - yes - - Legacy icon support - yes - - Cache busting - yes -- - internationalization - optional -+ - Localization - optional - - - Manual - Don't generate or pre-configure any icons. - - - Favicon - never - - Legacy icon support - yes - - Cache busting - never -- - internationalization - optional -+ - Localization - optional - - **_IMPORTANT:_** For best results, if you're providing an icon for generation it should be... - -@@ -133,7 +134,17 @@ icons: [ - - In the manual mode, you are responsible for defining the entire web app manifest and providing the defined icons in the [static](https://www.gatsbyjs.org/docs/static-folder/) folder. Only icons you provide will be available. There is no automatic resizing done for you. - --#### i18n – Multilang configuration -+### Feature configuration - **Optional** -+ -+#### Localization configuration -+ -+Localization allows you to crete unique manifests for each localized version of your site. As many languages as you want are supported. Localization requires unique paths for each language (e.g. if your default about page is at `/about`, the german(`de`) version would be `/de/about`) -+ -+The default site language should be configured in your root plugin options. Any additional languages should be defined in the `localize` array. The root settings will be used as defaults if not overridden in a locale. Any configuration option available in the root is also available in the `localize` array. -+ -+`lang` and `start_url` are the only _required_ options in the array objects. `name`, `short_name`, and `description` are [recommended](https://www.w3.org/TR/appmanifest/#dfn-directionality-capable-members) to be translated if being used in the default language. All other config options are optional. This is helpful if you want to provide unique icons for each locale. -+ -+The [`lang` option](https://www.w3.org/TR/appmanifest/#lang-member) is part of the web app manifest specification and thus is required to be a [valid language tag](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) - - ```js - // in gatsby-config.js -@@ -142,8 +153,10 @@ module.exports = { - { - resolve: `gatsby-plugin-manifest`, - options: { -- name: `GatsbyJS`, -- short_name: `Gatsby`, -+ name: `The Cool Application`, -+ short_name: `Cool App`, -+ description: `The application does cool things and makes your life better.`, -+ lang: `en`, - display: `standalone`, - icon: `src/images/icon.png`, - start_url: `/`, -@@ -153,8 +166,9 @@ module.exports = { - { - start_url: `/de/`, - lang: `de`, -- background_color: `#f7f0eb`, -- theme_color: `#a2466c`, -+ name: `Die coole Anwendung`, -+ short_name: `Coole Anwendung`, -+ description: `Die Anwendung macht coole Dinge und macht Ihr Leben besser.`, - }, - ], - }, -@@ -163,13 +177,6 @@ module.exports = { - } - ``` - --You can add as many languages as you want, as well as mix and match auto, hybrid & manual icon modes. --Shared values can be defined as root options, as in the example above. -- --Make sure the default language comes last. You don't have to specify a "default" language though, all paths can start with a language, or you can tweak the regex to match whatever part of the `pathname` you like. -- --### Feature configuration - **Optional** -- - #### Iterative icon options - - The `icon_options` object may be used to iteratively add configuration items to the `icons` array. Any options included in this object will be merged with each object of the `icons` array (custom or default). Key value pairs already in the `icons` array will take precedence over duplicate items in the `icon_options` array. --- -2.21.0 From 29510de05c2a5382f7c290cd23cb240c209399a8 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Mon, 24 Jun 2019 20:11:06 +0000 Subject: [PATCH 12/17] fix: issue when makeManifest is run multiple times it concatenates the cache busting to file name. --- packages/gatsby-plugin-manifest/src/gatsby-node.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index 864809f6c889a..feee68f07266e 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -74,7 +74,7 @@ const makeManifest = async (reporter, pluginOptions) => { // If icons are not manually defined, use the default icon set. if (!manifest.icons) { - manifest.icons = defaultIcons + manifest.icons = [...defaultIcons] } // Specify extra options for each icon (if requested). @@ -131,10 +131,10 @@ const makeManifest = async (reporter, pluginOptions) => { } if (cacheMode !== `none`) { - const iconDigest = createContentDigest(fs.readFileSync(icon)) - - manifest.icons.forEach(icon => { - icon.src = addDigestToPath(icon.src, iconDigest, cacheMode) + manifest.icons = manifest.icons.map(icon => { + let newIcon = { ...icon } + newIcon.src = addDigestToPath(icon.src, iconDigest, cacheMode) + return newIcon }) } From 78d22f5c272ddb5d90de70a07b1a3b4625425ef2 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Mon, 24 Jun 2019 21:58:25 +0000 Subject: [PATCH 13/17] feat: add basic caching so an icon isn't generated multiple times durring a single build. --- .../gatsby-plugin-manifest/src/gatsby-node.js | 93 +++++++++++++------ 1 file changed, 65 insertions(+), 28 deletions(-) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index feee68f07266e..e7b3f2a524a69 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -17,49 +17,76 @@ try { // doesn't support cpu-core-count utility. } -function generateIcons(icons, srcIcon) { - return Promise.all( - icons.map(async icon => { - const size = parseInt( - icon.sizes.substring(0, icon.sizes.lastIndexOf(`x`)) - ) - const imgPath = path.join(`public`, icon.src) - - // For vector graphics, instruct sharp to use a pixel density - // suitable for the resolution we're rasterizing to. - // For pixel graphics sources this has no effect. - // Sharp accept density from 1 to 2400 - const density = Math.min(2400, Math.max(1, size)) - - return sharp(srcIcon, { density }) - .resize({ - width: size, - height: size, - fit: `contain`, - background: { r: 255, g: 255, b: 255, alpha: 0 }, - }) - .toFile(imgPath) +async function generateIcon(icon, srcIcon) { + const imgPath = path.join(`public`, icon.src) + + // console.log(`generating icon: `, icon.src) + // if (fs.existsSync(imgPath)) { + // console.log(`icon already Exists, not regenerating`) + // return true + // } + const size = parseInt(icon.sizes.substring(0, icon.sizes.lastIndexOf(`x`))) + + // For vector graphics, instruct sharp to use a pixel density + // suitable for the resolution we're rasterizing to. + // For pixel graphics sources this has no effect. + // Sharp accept density from 1 to 2400 + const density = Math.min(2400, Math.max(1, size)) + + return sharp(srcIcon, { density }) + .resize({ + width: size, + height: size, + fit: `contain`, + background: { r: 255, g: 255, b: 255, alpha: 0 }, }) - ) + .toFile(imgPath) +} + +async function checkCache(cache, icon, srcIcon, srcIconDigest, callback) { + const cacheKey = createContentDigest(`${icon.src}${icon}${srcIconDigest}`) + + let created = cache.get(cacheKey) + + if (!created) { + cache.set(cacheKey, true) + + try { + console.log(`creating icon`, icon.src) + await callback(icon, srcIcon) + } catch (e) { + cache.set(cacheKey, false) + throw e + } + } else { + console.log(`icon exists`, icon.src) + } } exports.onPostBootstrap = async ({ reporter }, { localize, ...manifest }) => { const activity = reporter.activityTimer(`Build manifest and related icons`) activity.start() + let cache = new Map() + if (Array.isArray(localize)) { const locales = manifest.start_url ? localize.concat(manifest) : localize await Promise.all( - locales.map(locale => makeManifest(reporter, { ...manifest, ...locale })) + locales.map(locale => + makeManifest(cache, reporter, { + ...manifest, + ...locale, + }) + ) ) } else { - await makeManifest(reporter, manifest) + await makeManifest(cache, reporter, manifest) } activity.end() } -const makeManifest = async (reporter, pluginOptions) => { +const makeManifest = async (cache, reporter, pluginOptions) => { const { icon, ...manifest } = pluginOptions const suffix = pluginOptions.lang ? `_${pluginOptions.lang}` : `` @@ -125,9 +152,15 @@ const makeManifest = async (reporter, pluginOptions) => { ? pluginOptions.cache_busting_mode : `query` + const iconDigest = createContentDigest(fs.readFileSync(icon)) + //if cacheBusting is being done via url query icons must be generated before cache busting runs if (cacheMode === `query`) { - await generateIcons(manifest.icons, icon) + await Promise.all( + manifest.icons.map(dstIcon => + checkCache(cache, dstIcon, icon, iconDigest, generateIcon) + ) + ) } if (cacheMode !== `none`) { @@ -140,7 +173,11 @@ const makeManifest = async (reporter, pluginOptions) => { //if file names are being modified by cacheBusting icons must be generated after cache busting runs if (cacheMode !== `query`) { - await generateIcons(manifest.icons, icon) + await Promise.all( + manifest.icons.map(dstIcon => + checkCache(cache, dstIcon, icon, iconDigest, generateIcon) + ) + ) } } From 137e081834ac9a3bfe483328c4dae93d74778330 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Mon, 24 Jun 2019 22:01:03 +0000 Subject: [PATCH 14/17] fix: overriting manifest icons when using unique images for different locales. require name based cache busting fixes this issue the simplest. --- packages/gatsby-plugin-manifest/src/gatsby-node.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index e7b3f2a524a69..70edfac097f55 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -76,6 +76,10 @@ exports.onPostBootstrap = async ({ reporter }, { localize, ...manifest }) => { makeManifest(cache, reporter, { ...manifest, ...locale, + ...{ cache_busting_mode: `name` }, + /* localization requires unique filenames for output files if a different src Icon is defined. + otherwise one language would override anothers icons. + there are potentially some other methods to do this but requiring the existing name based cache busting was the easiest.*/ }) ) ) From b853cb21bbb8300142c44f14c39bcb556a28e863 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Mon, 24 Jun 2019 23:08:51 +0000 Subject: [PATCH 15/17] refactor: modify code to only require name chache busting when a unique icon is specified for a locale in automatic mode --- packages/gatsby-plugin-manifest/README.md | 6 ++-- .../gatsby-plugin-manifest/src/gatsby-node.js | 34 +++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/packages/gatsby-plugin-manifest/README.md b/packages/gatsby-plugin-manifest/README.md index 4ddbd77de3d06..df4eaaac7ffd5 100644 --- a/packages/gatsby-plugin-manifest/README.md +++ b/packages/gatsby-plugin-manifest/README.md @@ -138,7 +138,7 @@ In the manual mode, you are responsible for defining the entire web app manifest #### Localization configuration -Localization allows you to crete unique manifests for each localized version of your site. As many languages as you want are supported. Localization requires unique paths for each language (e.g. if your default about page is at `/about`, the german(`de`) version would be `/de/about`) +Localization allows you to create unique manifests for each localized version of your site. As many languages as you want are supported. Localization requires unique paths for each language (e.g. if your default about page is at `/about`, the german(`de`) version would be `/de/about`) The default site language should be configured in your root plugin options. Any additional languages should be defined in the `localize` array. The root settings will be used as defaults if not overridden in a locale. Any configuration option available in the root is also available in the `localize` array. @@ -146,6 +146,8 @@ The default site language should be configured in your root plugin options. Any The [`lang` option](https://www.w3.org/TR/appmanifest/#lang-member) is part of the web app manifest specification and thus is required to be a [valid language tag](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) +Using localization requires name based cache busting when using a unique icon in automatic mode for a specific locale. This is automatically enabled if you provide and `icon` in a specific locale without uniquely defining `icons`. If you're using icon creation in hybrid or manual mode for your locales, rememmber to provide unique icon paths. + ```js // in gatsby-config.js module.exports = { @@ -269,7 +271,7 @@ Cache busting works by calculating a unique "digest" of the provided icon and mo - **\`query\`** - This is the default mode. File names are unmodified but a URL query is appended to all links. e.g. `icons/icon-48x48.png?digest=abc123` -- **\`name\`** - Changes the cache busting mode to be done by file name. File names and links are modified with the icon digest. e.g. `icons/icon-48x48-abc123.png` (only needed if your CDN does not support URL query based cache busting) +- **\`name\`** - Changes the cache busting mode to be done by file name. File names and links are modified with the icon digest. e.g. `icons/icon-48x48-abc123.png` (only needed if your CDN does not support URL query based cache busting). This mode is required and automatically enabled for a locale's icons if you are providing a unique icon for a specific locale in automatic mode using the localization features. - **\`none\`** - Disables cache busting. File names and links remain unmodified. diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index 70edfac097f55..1943a67eb3780 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -46,20 +46,20 @@ async function generateIcon(icon, srcIcon) { async function checkCache(cache, icon, srcIcon, srcIconDigest, callback) { const cacheKey = createContentDigest(`${icon.src}${icon}${srcIconDigest}`) - let created = cache.get(cacheKey) + let created = cache.get(cacheKey, srcIcon) if (!created) { cache.set(cacheKey, true) try { - console.log(`creating icon`, icon.src) + // console.log(`creating icon`, icon.src, srcIcon) await callback(icon, srcIcon) } catch (e) { cache.set(cacheKey, false) throw e } } else { - console.log(`icon exists`, icon.src) + // console.log(`icon exists`, icon.src, srcIcon) } } @@ -69,24 +69,30 @@ exports.onPostBootstrap = async ({ reporter }, { localize, ...manifest }) => { let cache = new Map() + await makeManifest(cache, reporter, manifest) + if (Array.isArray(localize)) { - const locales = manifest.start_url ? localize.concat(manifest) : localize + const locales = [...localize] await Promise.all( - locales.map(locale => - makeManifest(cache, reporter, { + locales.map(locale => { + let cacheModeOverride = {} + + /* localization requires unique filenames for output files if a different src Icon is defined. + otherwise one language would override anothers icons in automatic mode. + */ + if (locale.hasOwnProperty(`icon`) && !locale.hasOwnProperty(`icons`)) { + // console.debug(`OVERRIDING CACHE BUSTING`, locale) + cacheModeOverride = { cache_busting_mode: `name` } + } + + return makeManifest(cache, reporter, { ...manifest, ...locale, - ...{ cache_busting_mode: `name` }, - /* localization requires unique filenames for output files if a different src Icon is defined. - otherwise one language would override anothers icons. - there are potentially some other methods to do this but requiring the existing name based cache busting was the easiest.*/ + ...cacheModeOverride, }) - ) + }) ) - } else { - await makeManifest(cache, reporter, manifest) } - activity.end() } From 8ff54096d9301a50ec6e32adc248d50f64d8a4fe Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Mon, 24 Jun 2019 23:26:40 +0000 Subject: [PATCH 16/17] docs: update docs with link to i18n example --- packages/gatsby-plugin-manifest/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gatsby-plugin-manifest/README.md b/packages/gatsby-plugin-manifest/README.md index df4eaaac7ffd5..ee3fd3817c1aa 100644 --- a/packages/gatsby-plugin-manifest/README.md +++ b/packages/gatsby-plugin-manifest/README.md @@ -9,9 +9,9 @@ This plugin provides several features beyond manifest configuration to make your - [Favicon support](https://www.w3.org/2005/10/howto-favicon) - Legacy icon support (iOS)[^1] - [Cache busting](https://www.keycdn.com/support/what-is-cache-busting) -- Localization - Provides unqiue manifests for path-based localization +- Localization - Provides unqiue manifests for path-based localization ([Gatsby Example](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-i18n)) -Each of these features has extensive configuration available so you're always in control. +Each of these features has extensive configuration available so you are always in control. ## Install From 1bfd8deaa9447e110b90d7d4965830362c151827 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Tue, 25 Jun 2019 00:20:38 +0000 Subject: [PATCH 17/17] fix: tests and digest cache bug --- .../__tests__/__snapshots__/gatsby-node.js.snap | 17 +++++++++++++++++ .../src/__tests__/gatsby-node.js | 12 +++++------- .../gatsby-plugin-manifest/src/gatsby-node.js | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/gatsby-plugin-manifest/src/__tests__/__snapshots__/gatsby-node.js.snap b/packages/gatsby-plugin-manifest/src/__tests__/__snapshots__/gatsby-node.js.snap index 3f6f3d8ce0ac6..783940566560d 100644 --- a/packages/gatsby-plugin-manifest/src/__tests__/__snapshots__/gatsby-node.js.snap +++ b/packages/gatsby-plugin-manifest/src/__tests__/__snapshots__/gatsby-node.js.snap @@ -1,3 +1,20 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Test plugin manifest options correctly works with default parameters 1`] = `"{\\"name\\":\\"GatsbyJS\\",\\"short_name\\":\\"GatsbyJS\\",\\"start_url\\":\\"/\\",\\"background_color\\":\\"#f7f0eb\\",\\"theme_color\\":\\"#a2466c\\",\\"display\\":\\"standalone\\",\\"icons\\":[{\\"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\\"}]}"`; + +exports[`Test plugin manifest options does file name based cache busting 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "public/manifest.webmanifest", + "{\\"name\\":\\"GatsbyJS\\",\\"short_name\\":\\"GatsbyJS\\",\\"start_url\\":\\"/\\",\\"background_color\\":\\"#f7f0eb\\",\\"theme_color\\":\\"#a2466c\\",\\"display\\":\\"standalone\\",\\"icons\\":[{\\"src\\":\\"icons/icon-48x48-contentDigest.png\\",\\"sizes\\":\\"48x48\\",\\"type\\":\\"image/png\\",\\"purpose\\":\\"all\\"},{\\"src\\":\\"icons/icon-128x128-contentDigest.png\\",\\"sizes\\":\\"128x128\\",\\"type\\":\\"image/png\\"}]}", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; diff --git a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js index 5f83afadceb19..6468c82223248 100644 --- a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js @@ -10,6 +10,7 @@ jest.mock(`fs`, () => { /* * We mock sharp because it depends on fs implementation (which is mocked) * this causes test failures, so mock it to avoid + * */ jest.mock(`sharp`, () => { @@ -229,11 +230,8 @@ describe(`Test plugin manifest options`, () => { ...pluginSpecificOptions, }) - expect(sharp).toHaveBeenCalledTimes(3) - expect(fs.writeFileSync).toHaveBeenCalledWith( - expect.anything(), - JSON.stringify(manifestOptions) - ) + expect(sharp).toHaveBeenCalledTimes(2) + expect(fs.writeFileSync).toMatchSnapshot() }) it(`does not do cache cache busting`, async () => { @@ -249,7 +247,7 @@ describe(`Test plugin manifest options`, () => { ...pluginSpecificOptions, }) - expect(sharp).toHaveBeenCalledTimes(3) + expect(sharp).toHaveBeenCalledTimes(2) expect(fs.writeFileSync).toHaveBeenCalledWith( expect.anything(), JSON.stringify(manifestOptions) @@ -270,7 +268,7 @@ describe(`Test plugin manifest options`, () => { ...pluginSpecificOptions, }) - expect(sharp).toHaveBeenCalledTimes(3) + expect(sharp).toHaveBeenCalledTimes(2) const content = JSON.parse(fs.writeFileSync.mock.calls[0][1]) expect(content.icons[0].purpose).toEqual(`all`) expect(content.icons[1].purpose).toEqual(`maskable`) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index 1943a67eb3780..9877d5ac5af09 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -44,7 +44,7 @@ async function generateIcon(icon, srcIcon) { } async function checkCache(cache, icon, srcIcon, srcIconDigest, callback) { - const cacheKey = createContentDigest(`${icon.src}${icon}${srcIconDigest}`) + const cacheKey = createContentDigest(`${icon.src}${srcIcon}${srcIconDigest}`) let created = cache.get(cacheKey, srcIcon)