Skip to content

Commit

Permalink
fix: support gatsby base dirs outside the site root (#281)
Browse files Browse the repository at this point in the history
* fix: support gatsby base dirs outside the site root

* chore: changes from review

Co-authored-by: ehmicky <ehmicky@users.noreply.github.com>
  • Loading branch information
ascorbic and ehmicky authored Feb 16, 2022
1 parent 2f4dd36 commit ccbec68
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 61 deletions.
4 changes: 3 additions & 1 deletion plugin/src/helpers/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import path from 'path'

import { existsSync, readdirSync } from 'fs-extra'

import { getGatsbyRoot } from './config'

function getCacheDirs(publish) {
return [publish, normalizedCacheDir(publish)]
}
Expand Down Expand Up @@ -37,5 +39,5 @@ export async function restoreCache({ publish, utils }): Promise<void> {
}

export function normalizedCacheDir(publish: string): string {
return path.normalize(`${publish}/../.cache`)
return path.join(getGatsbyRoot(publish), `.cache`)
}
16 changes: 13 additions & 3 deletions plugin/src/helpers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ export async function spliceConfig({
return fs.writeFile(fileName, out)
}

function loadGatsbyConfig(utils): GatsbyConfig | never {
const gatsbyConfigFile = path.resolve(process.cwd(), 'gatsby-config.js')
function loadGatsbyConfig({ utils, publish }): GatsbyConfig | never {
const gatsbyConfigFile = path.resolve(
getGatsbyRoot(publish),
'gatsby-config.js',
)
if (!existsSync(gatsbyConfigFile)) {
return {}
}
Expand All @@ -64,7 +67,10 @@ function hasPlugin(plugins: PluginRef[], pluginName: string): boolean {

export function checkGatsbyConfig({ utils, netlifyConfig }): void {
// warn if gatsby-plugin-netlify is missing
const gatsbyConfig = loadGatsbyConfig(utils)
const gatsbyConfig = loadGatsbyConfig({
utils,
publish: netlifyConfig.build.publish,
})

if (!hasPlugin(gatsbyConfig.plugins, 'gatsby-plugin-netlify')) {
console.error(
Expand Down Expand Up @@ -152,3 +158,7 @@ export function shouldSkipFunctions(cacheDir: string): boolean {

return false
}

export function getGatsbyRoot(publish: string): string {
return path.resolve(path.dirname(publish))
}
27 changes: 17 additions & 10 deletions plugin/src/helpers/functions.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import process from 'process'

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

import { makeHandler } from '../templates/handlers'
import { makeApiHandler, makeHandler } from '../templates/handlers'

import { getGatsbyRoot } from './config'

const writeFunction = async ({
renderMode,
Expand All @@ -21,12 +21,22 @@ const writeFunction = async ({
)
}

const writeApiFunction = async ({ appDir, functionDir }) => {
const source = makeApiHandler(appDir)
// This is to ensure we're copying from the compiled js, not ts source
await copy(
join(__dirname, '..', '..', 'lib', 'templates', 'api'),
functionDir,
)
await writeFile(join(functionDir, '__api.js'), source)
}

export const writeFunctions = async ({
INTERNAL_FUNCTIONS_SRC,
PUBLISH_DIR,
}: NetlifyPluginConstants): Promise<void> => {
const siteRoot = resolve(process.cwd(), PUBLISH_DIR, '..')
const functionDir = join(process.cwd(), INTERNAL_FUNCTIONS_SRC, '__api')
const siteRoot = getGatsbyRoot(PUBLISH_DIR)
const functionDir = resolve(INTERNAL_FUNCTIONS_SRC, '__api')
const appDir = relative(functionDir, siteRoot)

await writeFunction({
Expand All @@ -43,17 +53,14 @@ export const writeFunctions = async ({
functionsSrc: INTERNAL_FUNCTIONS_SRC,
})

await copy(
join(__dirname, '..', '..', 'lib', 'templates', 'api'),
functionDir,
)
await writeApiFunction({ appDir, functionDir })
}

export const deleteFunctions = async ({
INTERNAL_FUNCTIONS_SRC,
}: NetlifyPluginConstants): Promise<void> => {
for (const func of ['__api', '__ssr', '__dsg']) {
const funcDir = join(process.cwd(), INTERNAL_FUNCTIONS_SRC, func)
const funcDir = resolve(INTERNAL_FUNCTIONS_SRC, func)
if (existsSync(funcDir)) {
await rm(funcDir, { recursive: true, force: true })
}
Expand Down
2 changes: 1 addition & 1 deletion plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export async function onPreBuild({
netlifyConfig,
}): Promise<void> {
// Print a helpful message if the publish dir is misconfigured
if (!PUBLISH_DIR || process.cwd() === PUBLISH_DIR) {
if (!PUBLISH_DIR || process.cwd() === path.resolve(PUBLISH_DIR)) {
utils.build.failBuild(
`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.`,
)
Expand Down
33 changes: 0 additions & 33 deletions plugin/src/templates/api/__api.ts

This file was deleted.

16 changes: 5 additions & 11 deletions plugin/src/templates/api/gatsbyFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ export async function gatsbyFunction(
req: AugmentedGatsbyFunctionRequest,
res: AugmentedGatsbyFunctionResponse,
event: HandlerEvent,
appDir: string,
) {
const functionsDir = path.join(appDir, '.cache', 'functions')

// Multipart form data middleware. because co-body can't handle it
await new Promise((resolve) => {
// As we're using a fake Express handler we need to ignore the type to keep multer happy
Expand All @@ -47,7 +50,7 @@ export async function gatsbyFunction(

let functions
try {
functions = require('../../../.cache/functions/manifest.json')
functions = require(path.join(functionsDir, 'manifest.json'))
} catch {
return {
statusCode: 404,
Expand Down Expand Up @@ -91,16 +94,7 @@ export async function gatsbyFunction(
// During develop, the absolute path is correct, otherwise we need to use a relative path, as we're in a lambda
const pathToFunction = process.env.NETLIFY_DEV
? functionObj.absoluteCompiledFilePath
: path.join(
__dirname,
'..',
'..',
'..',
// ...We got there in the end
'.cache',
'functions',
functionObj.relativeCompiledFilePath,
)
: path.join(functionsDir, functionObj.relativeCompiledFilePath)

if (process.env.NETLIFY_DEV && !existsSync(pathToFunction)) {
// Functions are sometimes lazily-compiled, so we check and proxy the request if needed
Expand Down
46 changes: 44 additions & 2 deletions plugin/src/templates/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { Handler, HandlerEvent } from '@netlify/functions'
import type {
Handler,
HandlerContext,
HandlerEvent,
HandlerResponse,
} from '@netlify/functions'
import { stripIndent as javascript } from 'common-tags'
import type { GatsbyFunctionRequest } from 'gatsby'
import type {
Expand All @@ -14,6 +19,8 @@ const { join } = require('path')

const etag = require('etag')

const { gatsbyFunction } = require('./api/gatsbyFunction')
const { createRequestObject, createResponseObject } = require('./api/utils')
const {
getPagePathFromPageDataPath,
getGraphQLEngine,
Expand Down Expand Up @@ -127,6 +134,32 @@ const getHandler = (renderMode: RenderMode, appDir: string): Handler => {
}
}

/**
* Generate an API handler
*/

const getApiHandler = (appDir: string): Handler =>
function handler(
event: HandlerEvent,
context: HandlerContext,
): Promise<HandlerResponse> {
// Create a fake Gatsby/Express Request object
const req = createRequestObject({ event, context })

return new Promise((resolve) => {
try {
// Create a stubbed Gatsby/Express Response object
// onResEnd is the "resolve" cb for this Promise
const res = createResponseObject({ onResEnd: resolve })
// Try to call the actual function
gatsbyFunction(req, res, event, appDir)
} catch (error) {
console.error(`Error executing ${event.path}`, error)
resolve({ statusCode: 500 })
}
})
}

export const makeHandler = (appDir: string, renderMode: RenderMode): string =>
// This is a string, but if you have the right editor plugin it should format as js
javascript`
Expand All @@ -141,6 +174,15 @@ export const makeHandler = (appDir: string, renderMode: RenderMode): string =>
? `builder((${getHandler.toString()})("${renderMode}", pageRoot))`
: `((${getHandler.toString()})("${renderMode}", pageRoot))`
}
`

export const makeApiHandler = (appDir: string): string =>
// This is a string, but if you have the right editor plugin it should format as js
javascript`
const { gatsbyFunction } = require('./gatsbyFunction')
const { createRequestObject, createResponseObject } = require('./utils')
const { resolve } = require("path");
const pageRoot = resolve(__dirname, "${appDir}");
exports.handler = ((${getApiHandler.toString()})(pageRoot))
`
getHandler.toString()

0 comments on commit ccbec68

Please sign in to comment.