Skip to content

Commit 93a2071

Browse files
authored
feat(gatsby,gatsby-plugin-utils): feature toggle and availability (#34748)
1 parent e63e934 commit 93a2071

File tree

9 files changed

+89
-8
lines changed

9 files changed

+89
-8
lines changed

packages/gatsby-plugin-utils/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,20 @@ if (isGatsbyNodeLifecycleSupported(`createSchemaCustomization`)) {
6363
}
6464
}
6565
```
66+
67+
### `hasFeature`
68+
69+
Feature detection is now part of Gatsby. As a plugin author you don't know what version of Gatsby a user is using. `hasFeature` allows you to check if the current version of Gatsby has a certain feature.
70+
71+
Here's a list of features:
72+
// TODO
73+
74+
#### Example
75+
76+
```js
77+
const { hasFeature } = require(`gatsby-plugin-utils`)
78+
79+
if (!hasFeature(`image-service`)) {
80+
// You can polyfill image-service here so older versions have support as well
81+
}
82+
```
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { hasFeature } from "../has-feature"
2+
import apis from "gatsby/apis.json"
3+
4+
jest.mock(`gatsby/apis.json`, () => {
5+
return {}
6+
})
7+
8+
describe(`hasFeature`, () => {
9+
it(`should return true if gatsby has the feature`, () => {
10+
apis.features = [`image-service`]
11+
expect(hasFeature(`image-service`)).toBe(true)
12+
})
13+
it(`should return false if the feature has a typo`, () => {
14+
apis.features = [`image-service`]
15+
expect(hasFeature(`image-services`)).toBe(false)
16+
})
17+
18+
it(`should return false if gatsby doesn't have the feature`, () => {
19+
apis.features = []
20+
expect(hasFeature(`image-service`)).toBe(false)
21+
})
22+
23+
it(`should return false if gatsby doesn't support features section yet`, () => {
24+
// @ts-ignore - we want to test old versions too
25+
delete apis.features
26+
expect(hasFeature(`image-service`)).toBe(false)
27+
})
28+
})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { AvailableFeatures } from "gatsby"
2+
3+
/**
4+
* Check the readme for a list of available features.
5+
*/
6+
export function hasFeature(name: AvailableFeatures): boolean {
7+
try {
8+
const availableAPIs = require(`gatsby/apis.json`)
9+
10+
return !!availableAPIs?.features?.includes(name)
11+
} catch (e) {
12+
throw new Error(
13+
`Couldn't check available APIs. Make sure you are on gatsby version >=2.13.41`
14+
)
15+
}
16+
}

packages/gatsby-plugin-utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from "./validate"
22
export * from "./test-plugin-options-schema"
33
export * from "./joi"
44
export * from "./node-api-is-supported"
5+
export * from "./has-feature"

packages/gatsby-plugin-utils/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"extends": "../../tsconfig.json",
33
"include": ["src"],
4+
"exclude": ["**/__tests__/**/*"],
45
"compilerOptions": {
56
"skipLibCheck": true
67
}

packages/gatsby/index.d.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { GraphQLOutputType } from "graphql"
1717
import { PluginOptionsSchemaJoi, ObjectSchema } from "gatsby-plugin-utils"
1818
import { IncomingMessage, ServerResponse } from "http"
1919

20+
export type AvailableFeatures = never // "image-service"
21+
2022
export {
2123
default as Link,
2224
GatsbyLinkProps,
@@ -156,21 +158,24 @@ export type GetServerDataProps = {
156158
query?: Record<string, unknown>
157159
params?: Record<string, unknown>
158160
pageContext: Record<string, unknown>
159-
};
161+
}
160162

161163
/**
162164
* The return type (promise payload) from the [getServerData](https://www.gatsbyjs.com/docs/reference/rendering-options/server-side-rendering/) function.
163165
*/
164-
export type GetServerDataReturn<ServerDataType = Record<string, unknown>> = Promise<{
165-
headers?: Map<string, unknown>
166-
props?: ServerDataType
167-
status?: number
168-
}>;
166+
export type GetServerDataReturn<ServerDataType = Record<string, unknown>> =
167+
Promise<{
168+
headers?: Map<string, unknown>
169+
props?: ServerDataType
170+
status?: number
171+
}>
169172

170173
/**
171174
* A shorthand type for combining the props and return type for the [getServerData](https://www.gatsbyjs.com/docs/reference/rendering-options/server-side-rendering/) function.
172175
*/
173-
export type GetServerData<ServerDataType> = (props: GetServerDataProps) => GetServerDataReturn<ServerDataType>;
176+
export type GetServerData<ServerDataType> = (
177+
props: GetServerDataProps
178+
) => GetServerDataReturn<ServerDataType>
174179

175180
/**
176181
* Constructor arguments for the PageRenderer.
@@ -442,7 +447,7 @@ export interface GatsbyNode<
442447

443448
/** The first API called during Gatsby execution, runs as soon as plugins are loaded, before cache initialization and bootstrap preparation. If you indend to use this API in a plugin, use "onPluginInit" instead. */
444449
onPreInit?(
445-
args: ParentSpanPluginArgs,
450+
args: PreInitArgs,
446451
options: PluginOptions,
447452
callback: PluginCallback<void>
448453
): void | Promise<void>
@@ -905,6 +910,9 @@ export interface NodePluginSchema {
905910
buildEnumType(config: ComposeEnumTypeConfig): GatsbyGraphQLEnumType
906911
buildScalarType(config: ComposeScalarTypeConfig): GatsbyGraphQLScalarType
907912
}
913+
export interface PreInitArgs extends ParentSpanPluginArgs {
914+
actions: Actions
915+
}
908916

909917
export interface SourceNodesArgs extends ParentSpanPluginArgs {
910918
traceId: "initial-sourceNodes"

packages/gatsby/scripts/__tests__/api.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ it("generates the expected api output", done => {
3030
"wrapPageElement": Object {},
3131
"wrapRootElement": Object {},
3232
},
33+
"features": Array [],
3334
"node": Object {
3435
"createPages": Object {},
3536
"createPagesStatefully": Object {},

packages/gatsby/scripts/output-api-file.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ async function outputFile() {
4040
return merged
4141
}, {})
4242

43+
output.features = [];
44+
4345
return fs.writeFile(
4446
path.resolve(OUTPUT_FILE_NAME),
4547
JSON.stringify(output, null, 2),

packages/gatsby/src/utils/api-node-docs.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,13 @@ export const onCreateWebpackConfig = true
403403

404404
/**
405405
* The first API called during Gatsby execution, runs as soon as plugins are loaded, before cache initialization and bootstrap preparation.
406+
*
407+
* @param {object} $0
408+
* @param {object} $0.actions
409+
* @example
410+
* exports.onPreInit = ({ actions }) => {
411+
*
412+
* }
406413
*/
407414
export const onPreInit = true
408415

0 commit comments

Comments
 (0)