Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gatsby-plugin-functions): Add the ability to run functions locally and on Gatsby Cloud #30192

Merged
merged 76 commits into from
Apr 16, 2021
Merged
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
098a209
Add new plugin
sidharthachatterjee Nov 16, 2020
7576e2a
Add functions plugin by default
sidharthachatterjee Nov 16, 2020
df0496f
Add stub
sidharthachatterjee Nov 16, 2020
5d0c3e8
Bump up package.json
sidharthachatterjee Nov 16, 2020
9febad5
wip
sidharthachatterjee Nov 24, 2020
0597a5f
Compile all functions found in build
sidharthachatterjee Nov 24, 2020
2536188
Add plugin by default only if GATSBY_EXPERIMENT_FUNCTIONS is set
sidharthachatterjee Nov 24, 2020
29159df
Move to public for now
sidharthachatterjee Nov 25, 2020
eec2a09
Move to functions dir
sidharthachatterjee Nov 25, 2020
6470ee4
Run a dev server for functions
sidharthachatterjee Nov 30, 2020
9635aa1
wip
sidharthachatterjee Nov 30, 2020
4a59a47
Move to onPreBootstrap
sidharthachatterjee Jan 5, 2021
85d7cfa
Add target node
abhiaiyer91 Jan 11, 2021
ef07abd
Bump up package.json
sidharthachatterjee Jan 7, 2021
72877bc
Bump up package.json
sidharthachatterjee Jan 12, 2021
daf3b04
Add support for env vars
sidharthachatterjee Feb 19, 2021
32bd3d6
Publish 0.1.0-4
sidharthachatterjee Feb 19, 2021
a2ae24e
Bump up peerDependencies
sidharthachatterjee Mar 2, 2021
c1a2a05
Update yarn lock
sidharthachatterjee Mar 11, 2021
3d9ecbc
Use a parameter in express middleware and ignore extension (#30222)
sidharthachatterjee Mar 12, 2021
6067919
Merge remote-tracking branch 'upstream/master' into feat/functions
Mar 16, 2021
2aacbcc
Publish 0.1.0-6
sidharthachatterjee Mar 16, 2021
1587dc2
Set a default functions directory path (#30277)
sidharthachatterjee Mar 17, 2021
f0ab110
feat(functions): Add body parser
julienp Mar 21, 2021
b5324f9
Require files from the .cache directory so user can run es/commonjs (…
abhiaiyer91 Mar 25, 2021
21e1841
Publish 0.1.0-7
sidharthachatterjee Mar 25, 2021
6ce15fa
Prefer directory from program in state
sidharthachatterjee Apr 2, 2021
b2c4aa8
Log invocations as verbose
sidharthachatterjee Apr 2, 2021
bd0acc5
Add timeout constant
sidharthachatterjee Apr 2, 2021
4cfde03
Add internal plugin
sidharthachatterjee Apr 2, 2021
cc98e76
Load functions internal plugin
sidharthachatterjee Apr 2, 2021
17730bd
Load functions during initialize and stick em in redux
sidharthachatterjee Apr 2, 2021
80a9882
Merge branch 'master' into feat/functions
sidharthachatterjee Apr 7, 2021
56176d3
Clean up comments
sidharthachatterjee Apr 7, 2021
eace7e8
Move initialization code back to plugin
sidharthachatterjee Apr 7, 2021
5c1f96e
Add experiment for Gatsby Functions
sidharthachatterjee Apr 7, 2021
6b8d414
Revert yarn changes
sidharthachatterjee Apr 7, 2021
b325d12
Remove plugin package
sidharthachatterjee Apr 7, 2021
88d76a1
Do not extract comments
sidharthachatterjee Apr 7, 2021
311c9ec
Update yarn.lock
sidharthachatterjee Apr 7, 2021
d554093
Update packages/gatsby/src/internal-plugins/functions/gatsby-node.ts
julienp Apr 13, 2021
63bc54c
chore(functions): Remove console.logs
julienp Apr 13, 2021
f08d0ee
chore(functions): Remove unused urlResolve import
julienp Apr 13, 2021
5d6d072
Merge remote-tracking branch 'upstream/master' into feat/functions
Apr 13, 2021
7fbac4a
get internal plugin functions working + some cleanups
KyleAMathews Apr 13, 2021
4af22f9
Fix regex per @jamo's suggestion + update yarn.lock
KyleAMathews Apr 13, 2021
324bd99
remove accidentally committed file
KyleAMathews Apr 13, 2021
92e05c9
globby isn't a dependency of gatsby
KyleAMathews Apr 13, 2021
2163f08
fix(functions): End request with status 500 on function error
julienp Apr 13, 2021
aad744d
Hot reload functions
KyleAMathews Apr 13, 2021
c5c61ec
Load env variables from .env.* files
KyleAMathews Apr 13, 2021
aaf53f9
Resolve result of function so we can catch errors from async functions
KyleAMathews Apr 13, 2021
d2d7af0
Log webpack warnings/errors when compiling to the terminal
KyleAMathews Apr 13, 2021
02fcfbe
Log how long function took to execute
KyleAMathews Apr 13, 2021
4bec919
reload watch when .env files change & use all process.env vars
KyleAMathews Apr 13, 2021
ac9e55d
Allow for arbitrarily deep functions
KyleAMathews Apr 13, 2021
a26ac14
Detect new functions and incorporate
KyleAMathews Apr 13, 2021
5cc3661
Log when functions rebuild or we restart the watcher
KyleAMathews Apr 13, 2021
ae8f46f
List functions on dev-404-page
KyleAMathews Apr 13, 2021
0336739
Only show new API instructions when trying to reach an API route + ad…
KyleAMathews Apr 13, 2021
7282d1f
Fix linting
KyleAMathews Apr 14, 2021
6caff93
put new code behind conditional
KyleAMathews Apr 14, 2021
4b5aa4e
Properly close webpack watcher + handle deleting functions
KyleAMathews Apr 14, 2021
10226e4
Ignore non-js extensions (so make typescript work)
KyleAMathews Apr 14, 2021
1ee2d24
Write out manifest file so can serve functions from 'gatsby serve'
KyleAMathews Apr 14, 2021
4a9fffd
Address @lekoart's comments
KyleAMathews Apr 15, 2021
3cba097
Add default type for SiteFunction & fix small bugs when site doesn't …
KyleAMathews Apr 15, 2021
9c164f7
Add initial suite of tests
KyleAMathews Apr 15, 2021
771fb62
Update snapshots
KyleAMathews Apr 15, 2021
b2f2af2
Fix more tests
KyleAMathews Apr 15, 2021
73dceb4
Add functions tests to circleci setup
KyleAMathews Apr 15, 2021
10cd117
Only parse once
KyleAMathews Apr 16, 2021
42de7d5
Small tweaks
KyleAMathews Apr 16, 2021
8405b31
add comment
KyleAMathews Apr 16, 2021
af36e09
Fix typescript support
KyleAMathews Apr 16, 2021
2b7c7cf
Add tests for apis with special characters h/t @lekoarts
KyleAMathews Apr 16, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/gatsby/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
"mitt": "^1.2.0",
"mkdirp": "^0.5.1",
"moment": "^2.27.0",
"multer": "^1.4.2",
"name-all-modules-plugin": "^1.0.1",
"normalize-path": "^3.0.0",
"null-loader": "^4.0.1",
Expand Down
4 changes: 3 additions & 1 deletion packages/gatsby/src/bootstrap/load-plugins/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@ export function loadPlugins(
`../../internal-plugins/prod-404`,
`../../internal-plugins/webpack-theme-component-shadowing`,
`../../internal-plugins/bundle-optimisations`,
]
process.env.GATSBY_EXPERIMENTAL_FUNCTIONS &&
`../../internal-plugins/functions`,
].filter(Boolean) as Array<string>
internalPlugins.forEach(relPath => {
const absPath = path.join(__dirname, relPath)
plugins.push(processPlugin(absPath))
Expand Down
52 changes: 52 additions & 0 deletions packages/gatsby/src/commands/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import chalk from "chalk"
import { match as reachMatch } from "@gatsbyjs/reach-router/lib/utils"
import onExit from "signal-exit"
import report from "gatsby-cli/lib/reporter"
import multer from "multer"

import telemetry from "gatsby-telemetry"

Expand Down Expand Up @@ -110,6 +111,57 @@ module.exports = async (program: IServeProgram): Promise<void> => {
router.use(express.static(`public`, { dotfiles: `allow` }))
const matchPaths = await readMatchPaths(program)
router.use(matchPathRouter(matchPaths, { root }))

if (process.env.GATSBY_EXPERIMENTAL_FUNCTIONS) {
const compiledFunctionsDir = path.join(
program.directory,
`.cache`,
`functions`
)

const functionsManifest = JSON.parse(
fs.readFileSync(path.join(compiledFunctionsDir, `manifest.json`))
)

app.use(
`/api/*`,
multer().none(),
express.urlencoded({ extended: true }),
express.text(),
express.json(),
express.raw(),
async (req, res, next) => {
const { "0": functionName } = req.params

if (functionsManifest[functionName]) {
const start = Date.now()

try {
const pathToFunction = functionsManifest[functionName]
delete require.cache[require.resolve(pathToFunction)]
const fn = require(pathToFunction)

const fnToExecute = (fn && fn.default) || fn

await Promise.resolve(fnToExecute(req, res))
} catch (e) {
console.error(e)
KyleAMathews marked this conversation as resolved.
Show resolved Hide resolved
res.sendStatus(500)
}

const end = Date.now()
console.log(
KyleAMathews marked this conversation as resolved.
Show resolved Hide resolved
`Executed function "/api/${functionName}" in ${end - start}ms`
)

return
} else {
next()
}
}
)
}

router.use((req, res, next) => {
if (req.accepts(`html`)) {
return res.status(404).sendFile(`404.html`, { root })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,16 @@ class Dev404Page extends React.Component {
}

render() {
const functionsEnabled =
this.props.data.allSiteFunction.nodes[0].url !== `FAKE`
KyleAMathews marked this conversation as resolved.
Show resolved Hide resolved
const { pathname } = this.props.location
const isAPI = false
let newFilePath
let newAPIPath
if (pathname === `/`) {
newFilePath = `src/pages/index.js`
} else if (functionsEnabled && pathname.slice(0, 4) === `/api`) {
newAPIPath = `src${pathname}.js`
} else if (pathname.slice(-1) === `/`) {
newFilePath = `src/pages${pathname.slice(0, -1)}.js`
} else {
Expand All @@ -94,7 +100,9 @@ class Dev404Page extends React.Component {
<div>
<h1>Gatsby.js development 404 page</h1>
<p>
{`There's not a page yet at `}
{`There's not a page ${
functionsEnabled ? `or function ` : ``
}yet at `}
<code>{pathname}</code>
</p>
{this.props.custom404 ? (
Expand All @@ -109,20 +117,74 @@ class Dev404Page extends React.Component {
<code>src/pages/404.js</code>.
</p>
)}
<p>
Create a React.js component in your site directory at
{` `}
<code>{newFilePath}</code>
{` `}
and this page will automatically refresh to show the new page
component you created.
</p>
{newFilePath && (
<div>
<h2>Create a page at this url</h2>
<p>
Create a React.js component like the following in your site
directory at
{` `}"<code>{newFilePath}</code>"{` `}
and this page will automatically refresh to show the new page
component you created.
</p>
<pre>
<code
dangerouslySetInnerHTML={{
__html: `
export default function Component () {
return "Hello world"
}`,
}}
/>
</pre>
</div>
)}
{newAPIPath && (
<div>
<h2>Create an API function at this url</h2>
<p>
Create a javascript file like the following in your site directory
at
{` `}"<code>{newAPIPath}</code>"{` `}
and refresh to execute the new API function you created.
</p>
<pre>
<code
dangerouslySetInnerHTML={{
__html: `
export default function API (req, res) {
res.json({ hello: "world" })
}`,
}}
/>
</pre>
</div>
)}
{this.state.initPagePaths.length > 0 && (
<div>
<hr />
<p>
If you were trying to reach another page, perhaps you can find it
If you were trying to reach another page
{functionsEnabled ? ` or function` : ``}, perhaps you can find it
below.
</p>
{functionsEnabled && (
<>
<h2>
Functions ({this.props.data.allSiteFunction.nodes.length})
</h2>
<ul>
{this.props.data.allSiteFunction.nodes.map((node, index) => {
KyleAMathews marked this conversation as resolved.
Show resolved Hide resolved
const url = `/api/${node.url}`
return (
<li key={url}>
<a href={url}>{url}</a>
</li>
)
})}
</ul>
</>
)}
<h2>
Pages (
{this.state.pagePaths.length != this.state.initPagePaths.length
Expand Down Expand Up @@ -170,6 +232,11 @@ export default Dev404Page

export const pagesQuery = graphql`
query PagesQuery {
allSiteFunction {
nodes {
url
}
}
allSitePage(filter: { path: { ne: "/dev-404-page/" } }) {
nodes {
path
Expand Down
Loading