Skip to content

Commit

Permalink
add support for disabling adapters (gatsbyjs#38542)
Browse files Browse the repository at this point in the history
  • Loading branch information
techfg committed Sep 15, 2023
1 parent db248ab commit 6a74794
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 20 deletions.
20 changes: 20 additions & 0 deletions docs/docs/how-to/previews-deploys-hosting/adapters.md
Expand Up @@ -30,6 +30,13 @@ Can't find an adapter for your platform? Consider [creating an adapter](/docs/ho

## Using adapters

If you use one of the [supported deployment platforms](/docs/how-to/previews-deploys-hosting/zero-configuration-deployments/#supported-deployment-platforms), Gatsby's [zero-configuration deployments](/docs/how-to/previews-deploys-hosting/zero-configuration-deployments/) will automatically install and use the correct adapter when you deploy your project.

If would like to use a custom adapter, specify adapter options or disable adapters, you can do so via the [Gatsby Config API](/docs/reference/config-files/gatsby-config/#adapter).

> [!NOTE]
> If you plan on staying on a specific deployment platform, it is recommended to install the platform adapter to your `dependencies` as it will give you faster and more robust installs.
Use the `adapter` option inside `gatsby-config`:

```js:title=gatsby-config.js
Expand All @@ -52,6 +59,19 @@ module.exports = {
}
```

## Disabling Adapters

> [!NOTE}
> Support for disabling adapters was added in `gatsby@5.12.5`.
Use the `adapter` option inside `gatsby-config`:

```js:title=gatsby-config.js
module.exports = {
adapter: false
}
```

## Additional resources

- [Zero-Configuration Deployments](/docs/how-to/previews-deploys-hosting/zero-configuration-deployments/)
Expand Down
Expand Up @@ -16,12 +16,14 @@ Gatsby currently supports these platforms for zero-configuration deployments:

### Manually installing the adapter

If you plan on staying on a specific deployment platform, consider installing the adapter to your `dependencies`. This will give you faster and more robust installs.

If your adapter provides options that you can set, you must manually install the adapter to your `dependencies` to change any of the values. Read the [adapters guide](/docs/how-to/previews-deploys-hosting/adapters/) to learn how to use an adapter.

If you plan on using a specific deployment platform for a long period of time, you may also want to install the adapter to your `dependencies`. This will give you faster and more robust installs.

### Disabling adapters

Zero-configuration deployments are intended to simplify and improve the build and deploy experience. However, if you find that you need more control over your process, you can [disable adapters](/docs/how-to/previews-deploys-hosting/adapters/#disabling-adapters).

## Adding additional adapters

Gatsby's zero-configuration deployment setup is controlled through an [`adapters.js`](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/adapters.js) file. If you followed the [Creating an Adapter guide](/docs/how-to/previews-deploys-hosting/creating-an-adapter/) and want to add your adapter to this manifest, you can open a pull request.
Expand Down
10 changes: 9 additions & 1 deletion docs/docs/reference/config-files/gatsby-config.md
Expand Up @@ -230,7 +230,7 @@ By default, `graphqlTypegen` is only run during `gatsby develop`. Set this optio

> Support added in `gatsby@5.12.0`
You can set an [adapter](/docs/how-to/previews-deploys-hosting/adapters/) or configure [zero-configuration deployments](/docs/how-to/previews-deploys-hosting/zero-configuration-deployments/) through the `adapter` setting.
You can set an [adapter](/docs/how-to/previews-deploys-hosting/adapters/), [disable adapters](/docs/how-to/previews-deploys-hosting/adapters/#disabling-adapters), or configure [zero-configuration deployments](/docs/how-to/previews-deploys-hosting/zero-configuration-deployments/) through the `adapter` setting.

```js:title=gatsby-config.js
const adapter = require("gatsby-adapter-foo")
Expand All @@ -242,6 +242,14 @@ module.exports = {
}
```

> Support added in `gatsby@5.12.5`
```js:title=gatsby-config.js
module.exports = {
adapter: false // disable adapters
}
```

Read the [adapters guide](/docs/how-to/previews-deploys-hosting/adapters/) to learn more.

## headers
Expand Down
2 changes: 1 addition & 1 deletion packages/gatsby/index.d.ts
Expand Up @@ -391,7 +391,7 @@ export interface GatsbyConfig {
* Adapters are responsible for taking the production output from Gatsby and turning it into something your deployment platform understands. They make it easier to build and deploy Gatsby on any deployment platform.
* @see http://www.gatsbyjs.com/docs/how-to/previews-deploys-hosting/adapters/
*/
adapter?: IAdapter
adapter?: IAdapter | false
}

/**
Expand Down
21 changes: 20 additions & 1 deletion packages/gatsby/src/joi-schemas/__tests__/joi.ts
Expand Up @@ -344,14 +344,24 @@ describe(`gatsby config`, () => {
expect(result.value?.adapter).toEqual(config.adapter)
})

it(`lets you disable adapters`, () => {
const config = {
adapter: false,
}

const result = gatsbyConfigSchema.validate(config)
expect(result.error).toBeNil()
expect(result.value?.adapter).toEqual(config.adapter)
})

it(`throws on incorrect adapter setting`, () => {
const configOne = {
adapter: `gatsby-adapter-name`,
}

const resultOne = gatsbyConfigSchema.validate(configOne)
expect(resultOne.error).toMatchInlineSnapshot(
`[ValidationError: "adapter" must be of type object]`
`[ValidationError: "adapter" must be one of [false, object]]`
)

const configTwo = {
Expand All @@ -364,6 +374,15 @@ describe(`gatsby config`, () => {
expect(resultTwo.error).toMatchInlineSnapshot(
`[ValidationError: "adapter.adapt" is required]`
)

const configThree = {
adapter: true,
}

const resultThree = gatsbyConfigSchema.validate(configThree)
expect(resultThree.error).toMatchInlineSnapshot(
`[ValidationError: "adapter" must be one of [false, object]]`
)
})
})

Expand Down
29 changes: 16 additions & 13 deletions packages/gatsby/src/joi-schemas/joi.ts
Expand Up @@ -103,19 +103,22 @@ export const gatsbyConfigSchema: Joi.ObjectSchema<IGatsbyConfig> = Joi.object()
.unknown(false)
)
.default([]),
adapter: Joi.object()
.keys({
name: Joi.string().required(),
cache: Joi.object()
.keys({
restore: Joi.func(),
store: Joi.func(),
})
.unknown(false),
adapt: Joi.func().required(),
config: Joi.func(),
})
.unknown(false),
adapter: Joi.alternatives(
Joi.boolean().valid(false),
Joi.object()
.keys({
name: Joi.string().required(),
cache: Joi.object()
.keys({
restore: Joi.func(),
store: Joi.func(),
})
.unknown(false),
adapt: Joi.func().required(),
config: Joi.func(),
})
.unknown(false)
),
})
// throws when both assetPrefix and pathPrefix are defined
.when(
Expand Down
2 changes: 1 addition & 1 deletion packages/gatsby/src/redux/types.ts
Expand Up @@ -141,7 +141,7 @@ export interface IGatsbyConfig {
trailingSlash?: TrailingSlash
graphqlTypegen?: IGraphQLTypegenOptions
headers?: Array<IHeader>
adapter?: IAdapter
adapter?: IAdapter | false
}

export interface IGatsbyNode {
Expand Down
96 changes: 96 additions & 0 deletions packages/gatsby/src/utils/adapter/__tests__/manager.ts
Expand Up @@ -3,9 +3,12 @@ import {
getRoutesManifest,
getFunctionsManifest,
setWebpackAssets,
initAdapterManager,
} from "../manager"
import { state as stateDefault } from "./fixtures/state"
import { IGatsbyState } from "../../../internal"
import { IAdapterManager, IAdapter } from "../types"
import { getAdapterInit } from "../init"

jest.mock(`../../../redux`, () => {
return {
Expand All @@ -14,6 +17,7 @@ jest.mock(`../../../redux`, () => {
},
store: {
getState: jest.fn(),
dispatch: jest.fn(),
},
}
})
Expand All @@ -25,6 +29,34 @@ jest.mock(`../../engines-helpers`, () => {
}
})

jest.mock(`../init`)

const createAdapterMock = (): IAdapter => {
return {
name: `gatsby-adapter-mock`,
adapt: jest.fn(),
config: jest.fn().mockReturnValue({}),
}
}

const mockNoOpAdapterManager: IAdapterManager = {
adapt: jest.fn(),
restoreCache: jest.fn(),
storeCache: jest.fn(),
config: jest.fn().mockResolvedValue({
excludeDatastoreFromEngineFunction: false,
pluginsToDisable: [],
}),
}

jest.mock(`../no-op-manager`, () => {
return {
noOpAdapterManager: jest
.fn()
.mockImplementation(() => mockNoOpAdapterManager),
}
})

function mockStoreState(
state: IGatsbyState,
additionalState: Partial<IGatsbyState> = {}
Expand All @@ -33,6 +65,12 @@ function mockStoreState(
;(store.getState as jest.Mock).mockReturnValue(mergedState)
}

function mockGetAdapterInit(adapter: IAdapter | undefined): void {
const mocked = getAdapterInit as jest.MockedFunction<typeof getAdapterInit>
mocked.mockClear()
mocked.mockResolvedValue(adapter ? (): IAdapter => adapter : undefined)
}

const fixturesDir = `${__dirname}/fixtures`

let cwdToRestore
Expand Down Expand Up @@ -127,3 +165,61 @@ describe(`getFunctionsManifest`, () => {
`)
})
})

describe(`initAdapterManager`, () => {
beforeEach(() => {
;(mockNoOpAdapterManager.config as jest.Mock).mockClear()
})

it(`should use noop manager when adapter config is false`, async () => {
const initAdapter = createAdapterMock()
mockStoreState(stateDefault, {
config: { ...stateDefault.config, adapter: false },
})
mockGetAdapterInit(initAdapter)
const mgr = await initAdapterManager()

expect(mgr).not.toBeNull()
expect(mockNoOpAdapterManager.config).toHaveBeenCalledTimes(1)
expect(initAdapter.config).not.toHaveBeenCalled()
})

it(`should use noop manager when adapter config is undefined and no adapter resolved`, async () => {
mockStoreState(stateDefault, {
config: { ...stateDefault.config, adapter: undefined },
})
mockGetAdapterInit(undefined)
const mgr = await initAdapterManager()

expect(mgr).not.toBeNull()
expect(mockNoOpAdapterManager.config).toHaveBeenCalledTimes(1)
})

it(`should use configured adapter`, async () => {
const configuredAdapter = createAdapterMock()
const initAdapter = createAdapterMock()
mockStoreState(stateDefault, {
config: { ...stateDefault.config, adapter: configuredAdapter },
})
mockGetAdapterInit(initAdapter)
const mgr = await initAdapterManager()

expect(mgr).not.toBeNull()
expect(mockNoOpAdapterManager.config).not.toHaveBeenCalled()
expect(initAdapter.config).not.toHaveBeenCalled()
expect(configuredAdapter.config).toHaveBeenCalledTimes(1)
})

it(`should use resolved adapter when adapter config is undefined`, async () => {
const initAdapter = createAdapterMock()
mockStoreState(stateDefault, {
config: { ...stateDefault.config, adapter: undefined },
})
mockGetAdapterInit(initAdapter)
const mgr = await initAdapterManager()

expect(mgr).not.toBeNull()
expect(mockNoOpAdapterManager.config).not.toHaveBeenCalled()
expect(initAdapter.config).toBeCalled()
})
})
3 changes: 2 additions & 1 deletion packages/gatsby/src/utils/adapter/manager.ts
Expand Up @@ -129,7 +129,8 @@ export async function initAdapterManager(): Promise<IAdapterManager> {

reporter.verbose(`Using adapter ${adapter.name} from gatsby-config`)
} else {
const adapterInit = await getAdapterInit()
const adapterInit =
adapterFromGatsbyConfig === false ? false : await getAdapterInit()

// If we don't have adapter, use no-op adapter manager
if (!adapterInit) {
Expand Down

0 comments on commit 6a74794

Please sign in to comment.