Skip to content

Commit 7587ddd

Browse files
authored
feat: support disabling of Netlify function generation (#240)
* feat: support disabling of Netlify function generation * chore: cleanup functions if they are disabled * fix: correct test name * chore: test timeout * chore: re-word
1 parent af0ef8a commit 7587ddd

File tree

7 files changed

+107
-54
lines changed

7 files changed

+107
-54
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@ See
8383
[the gatsby-plugin-netlify docs](https://github.com/netlify/gatsby-plugin-netlify/)
8484
for more information, including optional plugin configuration.
8585

86+
### Disabling Netlify functions
87+
88+
In order to support Gatsby Functions and DSG and SSR render modes, this plugin
89+
generates three Netlify Functions called `__api`, `__ssr` and `__dsg`. If you
90+
are not using any of these modes, then you can disable the creation of these
91+
functions. If you are using the latest version of `gatsby-plugin-netlify` then
92+
this will be handled automatically, disabling functions if the site has no
93+
Gatsby Functions, or DSG/SSR pages. Otherwise, you can do this manually by
94+
setting the environment variable `NETLIFY_SKIP_GATSBY_FUNCTIONS` to `true`. Be
95+
aware that if you do this, any DSG or SSR pages will not work, and nor will any
96+
Gatsby Functions.
97+
8698
### Caveats
8799

88100
Currently you cannot use `StaticImage` or `gatsby-transformer-sharp` in SSR or

plugin/src/helpers/config.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { EOL } from 'os'
22
import path from 'path'
33
import process from 'process'
44

5-
import fs from 'fs-extra'
5+
import { stripIndent } from 'common-tags'
6+
import fs, { existsSync } from 'fs-extra'
67
import type { GatsbyConfig, PluginRef } from 'gatsby'
78

89
export async function spliceConfig({
@@ -39,8 +40,7 @@ export async function spliceConfig({
3940

4041
function loadGatsbyConfig(utils): GatsbyConfig | never {
4142
const gatsbyConfigFile = path.resolve(process.cwd(), 'gatsby-config.js')
42-
// eslint-disable-next-line node/no-sync
43-
if (!fs.existsSync(gatsbyConfigFile)) {
43+
if (!existsSync(gatsbyConfigFile)) {
4444
return {}
4545
}
4646

@@ -96,7 +96,7 @@ export function checkGatsbyConfig({ utils, netlifyConfig }): void {
9696
export function mutateConfig({
9797
netlifyConfig,
9898
compiledFunctionsDir,
99-
CACHE_DIR,
99+
cacheDir,
100100
}): void {
101101
/* eslint-disable no-underscore-dangle, no-param-reassign */
102102
netlifyConfig.functions.__api = {
@@ -108,9 +108,9 @@ export function mutateConfig({
108108
included_files: [
109109
'public/404.html',
110110
'public/500.html',
111-
path.posix.join(CACHE_DIR, 'data', '**'),
112-
path.posix.join(CACHE_DIR, 'query-engine', '**'),
113-
path.posix.join(CACHE_DIR, 'page-ssr', '**'),
111+
path.posix.join(cacheDir, 'data', '**'),
112+
path.posix.join(cacheDir, 'query-engine', '**'),
113+
path.posix.join(cacheDir, 'page-ssr', '**'),
114114
'!**/*.js.map',
115115
],
116116
external_node_modules: ['msgpackr-extract'],
@@ -120,3 +120,35 @@ export function mutateConfig({
120120
netlifyConfig.functions.__ssr = { ...netlifyConfig.functions.__dsg }
121121
/* eslint-enable no-underscore-dangle, no-param-reassign */
122122
}
123+
124+
export function shouldSkipFunctions(cacheDir: string): boolean {
125+
if (
126+
process.env.NETLIFY_SKIP_GATSBY_FUNCTIONS === 'true' ||
127+
process.env.NETLIFY_SKIP_GATSBY_FUNCTIONS === '1'
128+
) {
129+
console.log(
130+
'Skipping Gatsby Functions and SSR/DSG support because the environment variable NETLIFY_SKIP_GATSBY_FUNCTIONS is set to true',
131+
)
132+
return true
133+
}
134+
135+
if (!existsSync(path.join(cacheDir, 'functions'))) {
136+
console.log(
137+
`Skipping Gatsby Functions and SSR/DSG support because the site's Gatsby version does not support them`,
138+
)
139+
return true
140+
}
141+
142+
const skipFile = path.join(cacheDir, '.nf-skip-gatsby-functions')
143+
144+
if (existsSync(skipFile)) {
145+
console.log(
146+
stripIndent`
147+
Skipping Gatsby Functions and SSR/DSG support because gatsby-plugin-netlify reported that this site does not use them.
148+
If this is incorrect, remove the file "${skipFile}" and try again.`,
149+
)
150+
return true
151+
}
152+
153+
return false
154+
}

plugin/src/helpers/functions.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import process from 'process'
22

33
import { NetlifyPluginConstants } from '@netlify/build'
4-
import { copy, copyFile, ensureDir, writeFile } from 'fs-extra'
4+
import { copy, copyFile, ensureDir, existsSync, rm, writeFile } from 'fs-extra'
55
import { resolve, join, relative } from 'pathe'
66

77
import { makeHandler } from '../templates/handlers'
@@ -48,3 +48,14 @@ export const writeFunctions = async ({
4848
functionDir,
4949
)
5050
}
51+
52+
export const deleteFunctions = async ({
53+
INTERNAL_FUNCTIONS_SRC,
54+
}: NetlifyPluginConstants): Promise<void> => {
55+
for (const func of ['__api', '__ssr', '__dsg']) {
56+
const funcDir = join(process.cwd(), INTERNAL_FUNCTIONS_SRC, func)
57+
if (existsSync(funcDir)) {
58+
await rm(funcDir, { recursive: true, force: true })
59+
}
60+
}
61+
}

plugin/src/index.ts

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,36 @@
11
import path, { dirname, join } from 'path'
22
import process from 'process'
33

4-
import fs from 'fs-extra'
4+
import { NetlifyPluginOptions } from '@netlify/build'
5+
import { stripIndent } from 'common-tags'
6+
import { existsSync, readFile, writeFile } from 'fs-extra'
57

68
import { normalizedCacheDir, restoreCache, saveCache } from './helpers/cache'
7-
import { checkGatsbyConfig, mutateConfig, spliceConfig } from './helpers/config'
8-
import { writeFunctions } from './helpers/functions'
9+
import {
10+
checkGatsbyConfig,
11+
mutateConfig,
12+
shouldSkipFunctions,
13+
spliceConfig,
14+
} from './helpers/config'
15+
import { deleteFunctions, writeFunctions } from './helpers/functions'
916
import { checkZipSize } from './helpers/verification'
1017

11-
// eslint-disable-next-line no-template-curly-in-string
12-
const lmdbCacheString = 'process.cwd(), `.cache/${cacheDbFile}`'
13-
// eslint-disable-next-line no-template-curly-in-string
14-
const replacement = "require('os').tmpdir(), 'gatsby', `.cache/${cacheDbFile}`"
15-
18+
/**
19+
* This horrible thing is required because Gatsby tries to use a cache file in location that is readonly when deployed to a lambda
20+
*/
1621
async function patchFile(baseDir): Promise<void> {
22+
/* eslint-disable no-template-curly-in-string */
23+
const lmdbCacheString = 'process.cwd(), `.cache/${cacheDbFile}`'
24+
const replacement =
25+
"require('os').tmpdir(), 'gatsby', `.cache/${cacheDbFile}`"
26+
/* eslint-enable no-template-curly-in-string */
27+
1728
const bundleFile = join(baseDir, '.cache', 'query-engine', 'index.js')
18-
// eslint-disable-next-line node/no-sync
19-
if (!fs.existsSync(bundleFile)) {
29+
if (!existsSync(bundleFile)) {
2030
return
2131
}
22-
const bundle = await fs.readFile(bundleFile, 'utf8')
23-
24-
// I'm so, so sorry
25-
await fs.writeFile(bundleFile, bundle.replace(lmdbCacheString, replacement))
32+
const bundle = await readFile(bundleFile, 'utf8')
33+
await writeFile(bundleFile, bundle.replace(lmdbCacheString, replacement))
2634
}
2735

2836
const DEFAULT_FUNCTIONS_SRC = 'netlify/functions'
@@ -32,64 +40,54 @@ export async function onPreBuild({
3240
utils,
3341
netlifyConfig,
3442
}): Promise<void> {
35-
// print a helpful message if the publish dir is misconfigured
43+
// Print a helpful message if the publish dir is misconfigured
3644
if (!PUBLISH_DIR || process.cwd() === PUBLISH_DIR) {
3745
utils.build.failBuild(
38-
`Gatsby sites must publish the public directory, but your site’s publish directory is set to “${PUBLISH_DIR}”. Please set your publish directory to your Gatsby site’s public directory.`,
46+
`Gatsby sites must publish the "public" directory, but your site’s publish directory is set to “${PUBLISH_DIR}”. Please set your publish directory to your Gatsby site’s "public" directory.`,
3947
)
4048
}
4149
await restoreCache({ utils, publish: PUBLISH_DIR })
42-
const CACHE_DIR = normalizedCacheDir(PUBLISH_DIR)
43-
44-
// Work around Gatsby bug https://github.com/gatsbyjs/gatsby/issues/33262
45-
await fs.ensureDir(join(CACHE_DIR, 'json'))
4650

4751
checkGatsbyConfig({ utils, netlifyConfig })
4852
}
4953

50-
export async function onBuild({ constants, netlifyConfig }): Promise<void> {
54+
export async function onBuild({
55+
constants,
56+
netlifyConfig,
57+
}: NetlifyPluginOptions): Promise<void> {
5158
const {
5259
PUBLISH_DIR,
5360
FUNCTIONS_SRC = DEFAULT_FUNCTIONS_SRC,
5461
INTERNAL_FUNCTIONS_SRC,
5562
} = constants
56-
const CACHE_DIR = normalizedCacheDir(PUBLISH_DIR)
57-
const compiledFunctionsDir = path.join(CACHE_DIR, '/functions')
58-
// eslint-disable-next-line node/no-sync
59-
if (!fs.existsSync(compiledFunctionsDir)) {
60-
return
61-
}
62-
63-
await writeFunctions(constants)
64-
65-
// copying Netlify wrapper function into functions directory
63+
const cacheDir = normalizedCacheDir(PUBLISH_DIR)
6664

6765
if (
6866
INTERNAL_FUNCTIONS_SRC &&
69-
// eslint-disable-next-line node/no-sync
70-
fs.existsSync(path.join(FUNCTIONS_SRC, 'gatsby'))
67+
existsSync(path.join(FUNCTIONS_SRC, 'gatsby'))
7168
) {
72-
console.log(`
73-
Detected the function "${path.join(
69+
console.log(stripIndent`
70+
Detected the function "${path.join(
7471
FUNCTIONS_SRC,
7572
'gatsby',
7673
)}" that seem to have been generated by an old version of the Essential Gatsby plugin.
7774
The plugin no longer uses this and it should be deleted to avoid conflicts.\n`)
7875
}
7976

80-
mutateConfig({ netlifyConfig, CACHE_DIR, compiledFunctionsDir })
77+
if (shouldSkipFunctions(cacheDir)) {
78+
await deleteFunctions(constants)
79+
return
80+
}
81+
const compiledFunctionsDir = path.join(cacheDir, '/functions')
8182

82-
await spliceConfig({
83-
startMarker: '# @netlify/plugin-gatsby start',
84-
endMarker: '# @netlify/plugin-gatsby end',
85-
contents: `GATSBY_PRECOMPILE_DEVELOP_FUNCTIONS=true`,
86-
fileName: join(PUBLISH_DIR, '..', '.env.development'),
87-
})
83+
await writeFunctions(constants)
84+
85+
mutateConfig({ netlifyConfig, cacheDir, compiledFunctionsDir })
8886

8987
const root = dirname(netlifyConfig.build.publish)
9088
await patchFile(root)
9189

92-
// Editing _redirects to it works with ntl dev
90+
// Editing _redirects so it works with ntl dev
9391
spliceConfig({
9492
startMarker: '# @netlify/plugin-gatsby redirects start',
9593
endMarker: '# @netlify/plugin-gatsby redirects end',

plugin/src/templates/handlers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export const makeHandler = (appDir: string, renderMode: RenderMode): string =>
135135
const { getPagePathFromPageDataPath, getGraphQLEngine, prepareFilesystem, getErrorResponse } = require('./utils')
136136
const { join, resolve } = require("path");
137137
const etag = require('etag');
138-
const pageRoot = resolve(join(__dirname, "${appDir}"));
138+
const pageRoot = resolve(__dirname, "${appDir}");
139139
exports.handler = ${
140140
renderMode === 'DSG'
141141
? `builder((${getHandler.toString()})("${renderMode}", pageRoot))`

plugin/test/fixtures/with-no-gatsby-config/e2e-tests/build.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
const { buildSite } = require('../../../helpers')
33

44
jest.setTimeout(120_000)
5-
describe('A site with no functions', () => {
5+
describe('A site with no Gatsby config', () => {
66
it('successfully builds', async () => {
77
const { success } = await buildSite()
88
expect(success).toBeTruthy()

plugin/test/fixtures/with-no-plugins/e2e-tests/build.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
const { buildSite } = require('../../../helpers')
33

44
jest.setTimeout(120_000)
5-
describe('A site with no functions', () => {
5+
describe('A site with no plugins', () => {
66
it('successfully builds', async () => {
77
const { success } = await buildSite()
88
expect(success).toBeTruthy()

0 commit comments

Comments
 (0)