Skip to content

Commit b16b0ab

Browse files
authored
Netlify adapter (#2879)
* Netlify adapter * Remove package.json export that doesnt exist * Fix out path * Make netlifyFunctions the default * Make the dist configurable * Add an export for the functions * Append of the file exists
0 parents  commit b16b0ab

File tree

6 files changed

+198
-0
lines changed

6 files changed

+198
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "@astrojs/netlify",
3+
"description": "Deploy your site to Netlify",
4+
"version": "0.0.1",
5+
"type": "module",
6+
"types": "./dist/index.d.ts",
7+
"author": "withastro",
8+
"license": "MIT",
9+
"repository": {
10+
"type": "git",
11+
"url": "https://github.com/withastro/astro.git",
12+
"directory": "packages/integrations/netlify"
13+
},
14+
"bugs": "https://github.com/withastro/astro/issues",
15+
"homepage": "https://astro.build",
16+
"exports": {
17+
".": "./dist/index.js",
18+
"./functions": "./dist/integration-functions.js",
19+
"./netlify-functions.js": "./dist/netlify-functions.js",
20+
"./package.json": "./package.json"
21+
},
22+
"scripts": {
23+
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
24+
"dev": "astro-scripts dev \"src/**/*.ts\""
25+
},
26+
"dependencies": {
27+
"@astrojs/webapi": "^0.11.0"
28+
},
29+
"devDependencies": {
30+
"@netlify/functions": "^1.0.0",
31+
"astro": "workspace:*",
32+
"astro-scripts": "workspace:*"
33+
}
34+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# @astrojs/netlify
2+
3+
Deploy your server-side rendered (SSR) Astro app to [Netlify](https://www.netlify.com/).
4+
5+
Use this adapter in your Astro configuration file:
6+
7+
```js
8+
import { defineConfig } from 'astro/config';
9+
import netlify from '@astrojs/netlify/functions';
10+
11+
export default defineConfig({
12+
adapter: netlify()
13+
});
14+
```
15+
16+
After you build your site the `netlify/` folder will contain [Netlify Functions](https://docs.netlify.com/functions/overview/) in the `netlify/functions/` folder.
17+
18+
Now you can deploy!
19+
20+
```shell
21+
netlify deploy
22+
```
23+
24+
## Configuration
25+
26+
The output folder is configuration with the `dist` property when creating the adapter.
27+
28+
```js
29+
import { defineConfig } from 'astro/config';
30+
import netlify from '@astrojs/netlify/functions';
31+
32+
export default defineConfig({
33+
adapter: netlify({
34+
dist: new URL('./dist/', import.meta.url)
35+
})
36+
});
37+
```
38+
39+
And then point to the dist in your `netlify.toml`:
40+
41+
```toml
42+
[functions]
43+
directory = "dist/functions"
44+
```
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type { AstroAdapter, AstroIntegration, AstroConfig } from 'astro';
2+
import fs from 'fs';
3+
4+
export function getAdapter(site: string | undefined): AstroAdapter {
5+
return {
6+
name: '@astrojs/netlify',
7+
serverEntrypoint: '@astrojs/netlify/netlify-functions.js',
8+
exports: ['handler'],
9+
args: { site }
10+
};
11+
}
12+
13+
interface NetlifyFunctionsOptions {
14+
dist?: URL;
15+
}
16+
17+
function netlifyFunctions({ dist }: NetlifyFunctionsOptions = {}): AstroIntegration {
18+
let _config: AstroConfig;
19+
let entryFile: string;
20+
return {
21+
name: '@astrojs/netlify',
22+
hooks: {
23+
'astro:config:setup': ({ config }) => {
24+
if(dist) {
25+
config.dist = dist;
26+
} else {
27+
config.dist = new URL('./netlify/', config.projectRoot);
28+
}
29+
},
30+
'astro:config:done': ({ config, setAdapter }) => {
31+
setAdapter(getAdapter(config.buildOptions.site));
32+
_config = config;
33+
},
34+
'astro:build:start': async({ buildConfig }) => {
35+
entryFile = buildConfig.serverEntry.replace(/\.m?js/, '');
36+
buildConfig.client = _config.dist;
37+
buildConfig.server = new URL('./functions/', _config.dist);
38+
},
39+
'astro:build:done': async ({ routes, dir }) => {
40+
const _redirectsURL = new URL('./_redirects', dir);
41+
42+
// Create the redirects file that is used for routing.
43+
let _redirects = '';
44+
for(const route of routes) {
45+
if(route.pathname) {
46+
_redirects += `
47+
${route.pathname} /.netlify/functions/${entryFile} 200`
48+
}
49+
}
50+
51+
if(fs.existsSync(_redirects)) {
52+
await fs.promises.appendFile(_redirectsURL, _redirects, 'utf-8');
53+
} else {
54+
await fs.promises.writeFile(_redirectsURL, _redirects, 'utf-8');
55+
}
56+
}
57+
},
58+
};
59+
}
60+
61+
export {
62+
netlifyFunctions,
63+
netlifyFunctions as default
64+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export {
2+
netlifyFunctions as default
3+
} from './index.js';
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { SSRManifest } from 'astro';
2+
import type { Handler } from "@netlify/functions";
3+
import { App } from 'astro/app';
4+
import { polyfill } from '@astrojs/webapi';
5+
6+
polyfill(globalThis, {
7+
exclude: 'window document',
8+
});
9+
10+
interface Args {
11+
site?: string;
12+
}
13+
14+
export const createExports = (manifest: SSRManifest, args: Args) => {
15+
const app = new App(manifest);
16+
const site = new URL(args.site ?? `https://netlify.com`);
17+
18+
const handler: Handler = async (event) => {
19+
const headers = new Headers(event.headers as any);
20+
const request = new Request(new URL(event.path, site).toString(), {
21+
method: event.httpMethod,
22+
headers
23+
});
24+
25+
if(!app.match(request)) {
26+
return {
27+
statusCode: 404,
28+
body: 'Not found'
29+
};
30+
}
31+
32+
const response = await app.render(request);
33+
const body = await response.text();
34+
35+
return {
36+
statusCode: 200,
37+
headers: Object.fromEntries(response.headers.entries()),
38+
body
39+
};
40+
}
41+
42+
return { handler };
43+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": "../../../tsconfig.base.json",
3+
"include": ["src"],
4+
"compilerOptions": {
5+
"allowJs": true,
6+
"module": "ES2020",
7+
"outDir": "./dist",
8+
"target": "ES2020"
9+
}
10+
}

0 commit comments

Comments
 (0)