Skip to content

Commit 88b990a

Browse files
moonmeistermxstbr
andauthored
fix(plugin-manifest): Allow for all valid WebAppManifest properties (#27951)
* bring in complete(albeit outdated) WebAppManifest validation frrom previous unmerged PR * Cleanup Gatsby Plugin Options * update validation to match recent spec changes * update compatible gatsby version * reomve unused service worker object and fix numberic string * lint * add details from suplementry spec * final return * Prettify Co-authored-by: Max Stoiber <[email protected]>
1 parent d4ab6d2 commit 88b990a

File tree

4 files changed

+276
-38
lines changed

4 files changed

+276
-38
lines changed

packages/gatsby-plugin-manifest/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,12 @@ manifest — https://developers.google.com/web/fundamentals/engage-and-retain/we
446446

447447
For more information, see the [W3C specification](https://www.w3.org/TR/appmanifest/), [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/Manifest) or [Web.Dev guide](https://web.dev/add-manifest/).
448448

449+
### Plugin options validation
450+
451+
This plugin validates plugin options set in the `gatsby-config.js`. It validates the options used by the plugin and the entire WebAppManifest spec. To see the exact implementation of the validator see [src/pluginOptionsSchema.js](src/pluginOptionsSchema.js).
452+
453+
The WebAppManifest spec is not stable at the time of writing. This version of the validator adheres the [most recent](https://www.w3.org/TR/2020/WD-appmanifest-20201019/) version of the specification available.
454+
449455
## Troubleshooting
450456

451457
### Incompatible library version: sharp.node requires version X or later, but Z provides version Y

packages/gatsby-plugin-manifest/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"license": "MIT",
3333
"main": "index.js",
3434
"peerDependencies": {
35-
"gatsby": "^2.4.0"
35+
"gatsby": "^2.25.0"
3636
},
3737
"repository": {
3838
"type": "git",

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

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
favicons,
1010
} from "./common"
1111

12+
import pluginOptionsSchema from "./pluginOptionsSchema"
13+
1214
sharp.simd(true)
1315

1416
// Handle Sharp's concurrency based on the Gatsby CPU count
@@ -58,43 +60,7 @@ async function checkCache(cache, icon, srcIcon, srcIconDigest, callback) {
5860
}
5961
}
6062

61-
exports.pluginOptionsSchema = ({ Joi }) =>
62-
Joi.object({
63-
name: Joi.string(),
64-
short_name: Joi.string(),
65-
description: Joi.string(),
66-
lang: Joi.string(),
67-
localize: Joi.array().items(
68-
Joi.object({
69-
start_url: Joi.string(),
70-
name: Joi.string(),
71-
short_name: Joi.string(),
72-
description: Joi.string(),
73-
lang: Joi.string(),
74-
})
75-
),
76-
start_url: Joi.string(),
77-
background_color: Joi.string(),
78-
theme_color: Joi.string(),
79-
display: Joi.string(),
80-
legacy: Joi.boolean(),
81-
include_favicon: Joi.boolean(),
82-
icon: Joi.string(),
83-
theme_color_in_head: Joi.boolean(),
84-
crossOrigin: Joi.string().valid(`use-credentials`, `anonymous`),
85-
cache_busting_mode: Joi.string().valid(`query`, `name`, `none`),
86-
icons: Joi.array().items(
87-
Joi.object({
88-
src: Joi.string(),
89-
sizes: Joi.string(),
90-
type: Joi.string(),
91-
purpose: Joi.string(),
92-
})
93-
),
94-
icon_options: Joi.object({
95-
purpose: Joi.string(),
96-
}),
97-
})
63+
exports.pluginOptionsSchema = pluginOptionsSchema
9864

9965
/**
10066
* Setup pluginOption defaults
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
export default function pluginOptionSchema({ Joi }) {
2+
/* Descriptions copied from or based on documentation at https://developer.mozilla.org/en-US/docs/Web/Manifest
3+
*
4+
* Currently based on https://www.w3.org/TR/2020/WD-appmanifest-20201019/
5+
* and the August 4th 2020 version of https://w3c.github.io/manifest-app-info/
6+
*/
7+
8+
const platform = Joi.string()
9+
.optional()
10+
.empty(``)
11+
.valid(
12+
`narrow`,
13+
`wide`,
14+
`chromeos`,
15+
`ios`,
16+
`kaios`,
17+
`macos`,
18+
`windows`,
19+
`windows10x`,
20+
`xbox`,
21+
`chrome_web_store`,
22+
`play`,
23+
`itunes`,
24+
`microsoft`
25+
) //https://w3c.github.io/manifest-app-info/#platform-member
26+
.description(`The platform on which the application can be found.`)
27+
28+
const FingerPrint = Joi.object().keys({
29+
type: Joi.string()
30+
.required()
31+
.description(`syntax and semantics are platform-defined`),
32+
value: Joi.string()
33+
.required()
34+
.description(`syntax and semantics are platform-defined`),
35+
})
36+
37+
const ImageResource = Joi.object().keys({
38+
sizes: Joi.string()
39+
.optional()
40+
.description(`A string containing space-separated image dimensions`),
41+
src: Joi.string()
42+
.required()
43+
.description(
44+
`The path to the image file. If src is a relative URL, the base URL will be the URL of the manifest.`
45+
),
46+
type: Joi.string()
47+
.optional()
48+
.description(
49+
`A hint as to the media type of the image. The purpose of this member is to allow a user agent to quickly ignore images with media types it does not support.`
50+
),
51+
})
52+
53+
const ManifestImageResource = ImageResource.keys({
54+
purpose: Joi.string()
55+
.optional()
56+
.description(
57+
`Defines the purpose of the image, for example if the image is intended to serve some special purpose in the context of the host OS.`
58+
),
59+
})
60+
61+
const ShortcutItem = Joi.object().keys({
62+
name: Joi.string()
63+
.required()
64+
.description(
65+
`The name member of a ShortcutItem is a string that represents the name of the shortcut as it is usually displayed to the user in a context menu. `
66+
),
67+
short_name: Joi.string()
68+
.optional()
69+
.description(
70+
`The short_name member of a ShortcutItem is a string that represents a short version of the name of the shortcut. `
71+
),
72+
description: Joi.string()
73+
.optional()
74+
.description(
75+
`The description member of a ShortcutItem is a string that allows the developer to describe the purpose of the shortcut. `
76+
),
77+
url: Joi.string()
78+
.required()
79+
.description(
80+
`The url member of a ShortcutItem is a URL within scope of a processed manifest that opens when the associated shortcut is activated. `
81+
),
82+
icons: Joi.array()
83+
.optional()
84+
.items(ManifestImageResource)
85+
.description(
86+
`The icons member of an ShortcutItem member serve as iconic representations of the shortcut in various contexts. `
87+
),
88+
})
89+
90+
const ExternalApplicationResource = Joi.object()
91+
.keys({
92+
platform: platform.required(),
93+
url: Joi.string()
94+
.uri()
95+
.required()
96+
.description(`The URL at which the application can be found.`),
97+
id: Joi.string()
98+
.required()
99+
.description(
100+
`The ID used to represent the application on the specified platform.`
101+
),
102+
min_version: Joi.string()
103+
.optional()
104+
.description(
105+
`The minimum version of the application that is considered related to this web app.`
106+
),
107+
fingerprints: Joi.array()
108+
.optional()
109+
.items(FingerPrint)
110+
.description(
111+
`Each Fingerprints represents a set of cryptographic fingerprints used for verifying the application.`
112+
),
113+
})
114+
.or(`url`, `id`)
115+
116+
const WebAppManifest = Joi.object().keys({
117+
background_color: Joi.string()
118+
.optional()
119+
.description(
120+
`The background_color member defines a placeholder background color for the application page to display before its stylesheet is loaded.`
121+
),
122+
categories: Joi.array()
123+
.items(Joi.string().empty(``))
124+
.optional()
125+
.description(
126+
`The categories member is an array of strings defining the names of categories that the application supposedly belongs to.`
127+
),
128+
description: Joi.string()
129+
.optional()
130+
.description(
131+
`The description member is a string in which developers can explain what the application does. `
132+
),
133+
dir: Joi.string()
134+
.optional()
135+
.valid(`auto`, `ltr`, `rtl`)
136+
.description(
137+
`The base direction in which to display direction-capable members of the manifest.`
138+
),
139+
display: Joi.string()
140+
.optional()
141+
.valid(`fullscreen`, `standalone`, `minimal-ui`, `browser`)
142+
.description(
143+
`The display member is a string that determines the developers’ preferred display mode for the website`
144+
),
145+
iarc_rating_id: Joi.string()
146+
.guid()
147+
.optional()
148+
.description(
149+
`The iarc_rating_id member is a string that represents the International Age Rating Coalition (IARC) certification code of the web application.`
150+
),
151+
icons: Joi.array()
152+
.optional()
153+
.items(ManifestImageResource)
154+
.description(
155+
`The icons member specifies an array of objects representing image files that can serve as application icons for different contexts.`
156+
),
157+
lang: Joi.string()
158+
.optional()
159+
.description(
160+
`The lang member is a string containing a single language tag.`
161+
),
162+
name: Joi.string()
163+
.optional()
164+
.description(
165+
`The name member is a string that represents the name of the web application as it is usually displayed to the user.`
166+
),
167+
orientation: Joi.string()
168+
.optional()
169+
.valid(
170+
`any`,
171+
`natural`,
172+
`landscape`,
173+
`landscape-primary`,
174+
`landscape-secondary`,
175+
`portrait`,
176+
`portrait-primary`,
177+
`portrait-secondary`
178+
) // From: https://www.w3.org/TR/screen-orientation/#screenorientation-interface
179+
.description(
180+
`The orientation member defines the default orientation for all the website's top-level browsing contexts`
181+
),
182+
prefer_related_applications: Joi.boolean()
183+
.optional()
184+
.description(
185+
`The prefer_related_applications member is a boolean value that specifies that applications listed in related_applications should be preferred over the web application.`
186+
),
187+
related_applications: Joi.array()
188+
.optional()
189+
.items(ExternalApplicationResource)
190+
.description(
191+
`The related_applications field is an array of objects specifying native applications that are installable by, or accessible to, the underlying platform.`
192+
),
193+
scope: Joi.string()
194+
.optional()
195+
.description(
196+
`The scope member is a string that defines the navigation scope of this web application's application context.`
197+
),
198+
screenshots: Joi.array()
199+
.optional()
200+
.items(
201+
ManifestImageResource.keys({
202+
label: Joi.string()
203+
.optional()
204+
.description(
205+
`The label member is a string that serves as the accessible name of that screenshots object.`
206+
),
207+
platform: platform,
208+
})
209+
)
210+
.description(
211+
`The screenshots member defines an array of screenshots intended to showcase the application.`
212+
),
213+
short_name: Joi.string()
214+
.optional()
215+
.description(
216+
`The short_name member is a string that represents the name of the web application displayed to the user if there is not enough space to display name.`
217+
),
218+
shortcuts: Joi.array()
219+
.optional()
220+
.items(ShortcutItem)
221+
.description(
222+
`Each ShortcutItem represents a link to a key task or page within a web app. `
223+
),
224+
start_url: Joi.string()
225+
.optional()
226+
.description(
227+
`The start_url member is a string that represents the start URL of the web application.`
228+
),
229+
theme_color: Joi.string()
230+
.optional()
231+
.description(
232+
`The theme_color member is a string that defines the default theme color for the application.`
233+
),
234+
})
235+
236+
const GatsbyPluginOptions = Joi.object()
237+
.keys({
238+
icon: Joi.string(),
239+
legacy: Joi.boolean().default(true),
240+
theme_color_in_head: Joi.boolean().default(true),
241+
cache_busting_mode: Joi.string()
242+
.valid(`none`, `query`, `name`)
243+
.default(`query`),
244+
crossOrigin: Joi.string()
245+
.valid(`anonymous`, `use-credentials`)
246+
.default(`anonymous`),
247+
include_favicon: Joi.boolean().default(true),
248+
icon_options: ManifestImageResource.keys({
249+
src: Joi.string().forbidden(),
250+
sizes: Joi.string().forbidden(),
251+
}),
252+
253+
localize: Joi.array()
254+
.items(
255+
WebAppManifest.keys({
256+
lang: Joi.required(),
257+
start_url: Joi.required(),
258+
})
259+
)
260+
.description(`Used for localizing your WebAppManifest`),
261+
})
262+
.or(`icon`, `icons`)
263+
.with(`localize`, `lang`)
264+
265+
return WebAppManifest.concat(GatsbyPluginOptions)
266+
}

0 commit comments

Comments
 (0)