Skip to content

Commit f88a9e2

Browse files
DK the Humanwardpeet
DK the Human
andcommitted
feat(gatsby-plugin-manifest): Update manifest on navigation (#17426)
With the addition of the localize option in gatsby-plugin-manifest (#13471), multiple manifests can be generated and served depending on the path of the URL. While the correct manifest is served on initial page load, the link to the manifest is never updated even if the user navigates to a page that should include a different manifest. Co-authored-by: Ward Peeters <[email protected]>
1 parent be26f4e commit f88a9e2

File tree

6 files changed

+142
-24
lines changed

6 files changed

+142
-24
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
describe(`gatsby-plugin-manifest`, () => {
2+
const pluginOptions = {
3+
name: `My Website`,
4+
start_url: `/`,
5+
localize: [
6+
{
7+
start_url: `/es/`,
8+
lang: `es`,
9+
},
10+
],
11+
}
12+
let onRouteUpdate
13+
14+
beforeEach(() => {
15+
global.__PATH_PREFIX__ = ``
16+
global.__MANIFEST_PLUGIN_HAS_LOCALISATION__ = true
17+
onRouteUpdate = require(`../gatsby-browser`).onRouteUpdate
18+
document.head.innerHTML = `<link rel="manifest" href="/manifest.webmanifest">`
19+
})
20+
21+
afterAll(() => {
22+
delete global.__MANIFEST_PLUGIN_HAS_LOCALISATION__
23+
})
24+
25+
test(`has manifest in head`, () => {
26+
const location = {
27+
pathname: `/`,
28+
}
29+
onRouteUpdate({ location }, pluginOptions)
30+
expect(document.head).toMatchInlineSnapshot(`
31+
<head>
32+
<link
33+
href="/manifest.webmanifest"
34+
rel="manifest"
35+
/>
36+
</head>
37+
`)
38+
})
39+
40+
test(`changes href of manifest if navigating to a localized app`, () => {
41+
const location = {
42+
pathname: `/es/`,
43+
}
44+
// add default lang
45+
pluginOptions.lang = `en`
46+
onRouteUpdate({ location }, pluginOptions)
47+
expect(document.head).toMatchInlineSnapshot(`
48+
<head>
49+
<link
50+
href="/manifest_es.webmanifest"
51+
rel="manifest"
52+
/>
53+
</head>
54+
`)
55+
})
56+
57+
test(`keeps default manifest if not navigating to a localized app`, () => {
58+
const location = {
59+
pathname: `/random-path/`,
60+
}
61+
// add default lang
62+
pluginOptions.lang = `en`
63+
onRouteUpdate({ location }, pluginOptions)
64+
expect(document.head).toMatchInlineSnapshot(`
65+
<head>
66+
<link
67+
href="/manifest.webmanifest"
68+
rel="manifest"
69+
/>
70+
</head>
71+
`)
72+
})
73+
})

packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ describe(`gatsby-plugin-manifest`, () => {
112112
it(testName, () => {
113113
onRenderBody(args, {
114114
start_url: `/`,
115+
lang: `en`,
115116
localize: [
116117
{
117118
start_url: `/de/`,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* global __MANIFEST_PLUGIN_HAS_LOCALISATION__ */
2+
import { withPrefix as fallbackWithPrefix, withAssetPrefix } from "gatsby"
3+
import getManifestForPathname from "./get-manifest-pathname"
4+
5+
// when we don't have localisation in our manifest, we tree shake everything away
6+
if (__MANIFEST_PLUGIN_HAS_LOCALISATION__) {
7+
const withPrefix = withAssetPrefix || fallbackWithPrefix
8+
9+
exports.onRouteUpdate = function({ location }, pluginOptions) {
10+
const { localize } = pluginOptions
11+
const manifestFilename = getManifestForPathname(location.pathname, localize)
12+
13+
const manifestEl = document.head.querySelector(`link[rel="manifest"]`)
14+
if (manifestEl) {
15+
manifestEl.setAttribute(`href`, withPrefix(manifestFilename))
16+
}
17+
}
18+
}

packages/gatsby-plugin-manifest/src/gatsby-node.js

+24-7
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,26 @@ exports.onPostBootstrap = async (
8484
cacheModeOverride = { cache_busting_mode: `name` }
8585
}
8686

87-
return makeManifest(cache, reporter, {
88-
...manifest,
89-
...locale,
90-
...cacheModeOverride,
91-
})
87+
return makeManifest(
88+
cache,
89+
reporter,
90+
{
91+
...manifest,
92+
...locale,
93+
...cacheModeOverride,
94+
},
95+
true
96+
)
9297
})
9398
)
9499
}
95100
activity.end()
96101
}
97102

98-
const makeManifest = async (cache, reporter, pluginOptions) => {
103+
const makeManifest = async (cache, reporter, pluginOptions, shouldLocalize) => {
99104
const { icon, ...manifest } = pluginOptions
100-
const suffix = pluginOptions.lang ? `_${pluginOptions.lang}` : ``
105+
const suffix =
106+
shouldLocalize && pluginOptions.lang ? `_${pluginOptions.lang}` : ``
101107

102108
// Delete options we won't pass to the manifest.webmanifest.
103109
delete manifest.plugins
@@ -196,3 +202,14 @@ const makeManifest = async (cache, reporter, pluginOptions) => {
196202
JSON.stringify(manifest)
197203
)
198204
}
205+
206+
exports.onCreateWebpackConfig = ({ actions, plugins }, pluginOptions) => {
207+
actions.setWebpackConfig({
208+
plugins: [
209+
plugins.define({
210+
__MANIFEST_PLUGIN_HAS_LOCALISATION__:
211+
pluginOptions.localize && pluginOptions.localize.length,
212+
}),
213+
],
214+
})
215+
}

packages/gatsby-plugin-manifest/src/gatsby-ssr.js

+3-17
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import React from "react"
22
import { withPrefix as fallbackWithPrefix, withAssetPrefix } from "gatsby"
33
import fs from "fs"
44
import { createContentDigest } from "gatsby-core-utils"
5-
65
import { defaultIcons, addDigestToPath } from "./common.js"
6+
import getManifestForPathname from "./get-manifest-pathname"
77

88
// TODO: remove for v3
99
const withPrefix = withAssetPrefix || fallbackWithPrefix
@@ -14,20 +14,6 @@ exports.onRenderBody = (
1414
{ setHeadComponents, pathname = `/` },
1515
{ localize, ...pluginOptions }
1616
) => {
17-
if (Array.isArray(localize)) {
18-
const locales = pluginOptions.start_url
19-
? localize.concat(pluginOptions)
20-
: localize
21-
const manifest = locales.find(locale =>
22-
RegExp(`^${locale.start_url}.*`, `i`).test(pathname)
23-
)
24-
pluginOptions = {
25-
...pluginOptions,
26-
...manifest,
27-
}
28-
if (!pluginOptions) return false
29-
}
30-
3117
// We use this to build a final array to pass as the argument to setHeadComponents at the end of onRenderBody.
3218
let headComponents = []
3319

@@ -66,14 +52,14 @@ exports.onRenderBody = (
6652
}
6753
}
6854

69-
const suffix = pluginOptions.lang ? `_${pluginOptions.lang}` : ``
55+
const manifestFileName = getManifestForPathname(pathname, localize)
7056

7157
// Add manifest link tag.
7258
headComponents.push(
7359
<link
7460
key={`gatsby-plugin-manifest-link`}
7561
rel="manifest"
76-
href={withPrefix(`/manifest${suffix}.webmanifest`)}
62+
href={withPrefix(`/${manifestFileName}`)}
7763
crossOrigin={pluginOptions.crossOrigin}
7864
/>
7965
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Get a manifest filename depending on localized pathname
3+
*
4+
* @param {string} pathname
5+
* @param {Array<{start_url: string, lang: string}>} localizedManifests
6+
* @return string
7+
*/
8+
export default (pathname, localizedManifests) => {
9+
const defaultFilename = `manifest.webmanifest`
10+
if (!Array.isArray(localizedManifests)) {
11+
return defaultFilename
12+
}
13+
14+
const localizedManifest = localizedManifests.find(app =>
15+
pathname.startsWith(app.start_url)
16+
)
17+
18+
if (!localizedManifest) {
19+
return defaultFilename
20+
}
21+
22+
return `manifest_${localizedManifest.lang}.webmanifest`
23+
}

0 commit comments

Comments
 (0)