diff --git a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-browser.js b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-browser.js new file mode 100644 index 0000000000000..3f55921e0f365 --- /dev/null +++ b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-browser.js @@ -0,0 +1,73 @@ +describe(`gatsby-plugin-manifest`, () => { + const pluginOptions = { + name: `My Website`, + start_url: `/`, + localize: [ + { + start_url: `/es/`, + lang: `es`, + }, + ], + } + let onRouteUpdate + + beforeEach(() => { + global.__PATH_PREFIX__ = `` + global.__MANIFEST_PLUGIN_HAS_LOCALISATION__ = true + onRouteUpdate = require(`../gatsby-browser`).onRouteUpdate + document.head.innerHTML = `` + }) + + afterAll(() => { + delete global.__MANIFEST_PLUGIN_HAS_LOCALISATION__ + }) + + test(`has manifest in head`, () => { + const location = { + pathname: `/`, + } + onRouteUpdate({ location }, pluginOptions) + expect(document.head).toMatchInlineSnapshot(` + + + + `) + }) + + test(`changes href of manifest if navigating to a localized app`, () => { + const location = { + pathname: `/es/`, + } + // add default lang + pluginOptions.lang = `en` + onRouteUpdate({ location }, pluginOptions) + expect(document.head).toMatchInlineSnapshot(` + + + + `) + }) + + test(`keeps default manifest if not navigating to a localized app`, () => { + const location = { + pathname: `/random-path/`, + } + // add default lang + pluginOptions.lang = `en` + onRouteUpdate({ location }, pluginOptions) + expect(document.head).toMatchInlineSnapshot(` + + + + `) + }) +}) diff --git a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js index 65a004a13f61e..359d102f8a46b 100644 --- a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js +++ b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js @@ -112,6 +112,7 @@ describe(`gatsby-plugin-manifest`, () => { it(testName, () => { onRenderBody(args, { start_url: `/`, + lang: `en`, localize: [ { start_url: `/de/`, diff --git a/packages/gatsby-plugin-manifest/src/gatsby-browser.js b/packages/gatsby-plugin-manifest/src/gatsby-browser.js new file mode 100644 index 0000000000000..9280241922bd8 --- /dev/null +++ b/packages/gatsby-plugin-manifest/src/gatsby-browser.js @@ -0,0 +1,18 @@ +/* global __MANIFEST_PLUGIN_HAS_LOCALISATION__ */ +import { withPrefix as fallbackWithPrefix, withAssetPrefix } from "gatsby" +import getManifestForPathname from "./get-manifest-pathname" + +// when we don't have localisation in our manifest, we tree shake everything away +if (__MANIFEST_PLUGIN_HAS_LOCALISATION__) { + const withPrefix = withAssetPrefix || fallbackWithPrefix + + exports.onRouteUpdate = function({ location }, pluginOptions) { + const { localize } = pluginOptions + const manifestFilename = getManifestForPathname(location.pathname, localize) + + const manifestEl = document.head.querySelector(`link[rel="manifest"]`) + if (manifestEl) { + manifestEl.setAttribute(`href`, withPrefix(manifestFilename)) + } + } +} diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index ff888fb5fb9ca..d9faf2877e38f 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -84,20 +84,26 @@ exports.onPostBootstrap = async ( cacheModeOverride = { cache_busting_mode: `name` } } - return makeManifest(cache, reporter, { - ...manifest, - ...locale, - ...cacheModeOverride, - }) + return makeManifest( + cache, + reporter, + { + ...manifest, + ...locale, + ...cacheModeOverride, + }, + true + ) }) ) } activity.end() } -const makeManifest = async (cache, reporter, pluginOptions) => { +const makeManifest = async (cache, reporter, pluginOptions, shouldLocalize) => { const { icon, ...manifest } = pluginOptions - const suffix = pluginOptions.lang ? `_${pluginOptions.lang}` : `` + const suffix = + shouldLocalize && pluginOptions.lang ? `_${pluginOptions.lang}` : `` // Delete options we won't pass to the manifest.webmanifest. delete manifest.plugins @@ -196,3 +202,14 @@ const makeManifest = async (cache, reporter, pluginOptions) => { JSON.stringify(manifest) ) } + +exports.onCreateWebpackConfig = ({ actions, plugins }, pluginOptions) => { + actions.setWebpackConfig({ + plugins: [ + plugins.define({ + __MANIFEST_PLUGIN_HAS_LOCALISATION__: + pluginOptions.localize && pluginOptions.localize.length, + }), + ], + }) +} diff --git a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js index 3db615e8c5bfb..cc972edfb5445 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-ssr.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-ssr.js @@ -2,8 +2,8 @@ import React from "react" import { withPrefix as fallbackWithPrefix, withAssetPrefix } from "gatsby" import fs from "fs" import { createContentDigest } from "gatsby-core-utils" - import { defaultIcons, addDigestToPath } from "./common.js" +import getManifestForPathname from "./get-manifest-pathname" // TODO: remove for v3 const withPrefix = withAssetPrefix || fallbackWithPrefix @@ -14,20 +14,6 @@ exports.onRenderBody = ( { setHeadComponents, pathname = `/` }, { localize, ...pluginOptions } ) => { - if (Array.isArray(localize)) { - const locales = pluginOptions.start_url - ? localize.concat(pluginOptions) - : localize - const manifest = locales.find(locale => - RegExp(`^${locale.start_url}.*`, `i`).test(pathname) - ) - pluginOptions = { - ...pluginOptions, - ...manifest, - } - 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 = [] @@ -66,14 +52,14 @@ exports.onRenderBody = ( } } - const suffix = pluginOptions.lang ? `_${pluginOptions.lang}` : `` + const manifestFileName = getManifestForPathname(pathname, localize) // Add manifest link tag. headComponents.push( ) diff --git a/packages/gatsby-plugin-manifest/src/get-manifest-pathname.js b/packages/gatsby-plugin-manifest/src/get-manifest-pathname.js new file mode 100644 index 0000000000000..2cadc49a33249 --- /dev/null +++ b/packages/gatsby-plugin-manifest/src/get-manifest-pathname.js @@ -0,0 +1,23 @@ +/** + * Get a manifest filename depending on localized pathname + * + * @param {string} pathname + * @param {Array<{start_url: string, lang: string}>} localizedManifests + * @return string + */ +export default (pathname, localizedManifests) => { + const defaultFilename = `manifest.webmanifest` + if (!Array.isArray(localizedManifests)) { + return defaultFilename + } + + const localizedManifest = localizedManifests.find(app => + pathname.startsWith(app.start_url) + ) + + if (!localizedManifest) { + return defaultFilename + } + + return `manifest_${localizedManifest.lang}.webmanifest` +}