Skip to content

Commit

Permalink
Netlify adapter (#2879)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
matthewp authored Mar 25, 2022
0 parents commit b16b0ab
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 0 deletions.
34 changes: 34 additions & 0 deletions packages/integrations/netlify/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@astrojs/netlify",
"description": "Deploy your site to Netlify",
"version": "0.0.1",
"type": "module",
"types": "./dist/index.d.ts",
"author": "withastro",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/withastro/astro.git",
"directory": "packages/integrations/netlify"
},
"bugs": "https://github.com/withastro/astro/issues",
"homepage": "https://astro.build",
"exports": {
".": "./dist/index.js",
"./functions": "./dist/integration-functions.js",
"./netlify-functions.js": "./dist/netlify-functions.js",
"./package.json": "./package.json"
},
"scripts": {
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
"dev": "astro-scripts dev \"src/**/*.ts\""
},
"dependencies": {
"@astrojs/webapi": "^0.11.0"
},
"devDependencies": {
"@netlify/functions": "^1.0.0",
"astro": "workspace:*",
"astro-scripts": "workspace:*"
}
}
44 changes: 44 additions & 0 deletions packages/integrations/netlify/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# @astrojs/netlify

Deploy your server-side rendered (SSR) Astro app to [Netlify](https://www.netlify.com/).

Use this adapter in your Astro configuration file:

```js
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify/functions';

export default defineConfig({
adapter: netlify()
});
```

After you build your site the `netlify/` folder will contain [Netlify Functions](https://docs.netlify.com/functions/overview/) in the `netlify/functions/` folder.

Now you can deploy!

```shell
netlify deploy
```

## Configuration

The output folder is configuration with the `dist` property when creating the adapter.

```js
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify/functions';

export default defineConfig({
adapter: netlify({
dist: new URL('./dist/', import.meta.url)
})
});
```
And then point to the dist in your `netlify.toml`:
```toml
[functions]
directory = "dist/functions"
```
64 changes: 64 additions & 0 deletions packages/integrations/netlify/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { AstroAdapter, AstroIntegration, AstroConfig } from 'astro';
import fs from 'fs';

export function getAdapter(site: string | undefined): AstroAdapter {
return {
name: '@astrojs/netlify',
serverEntrypoint: '@astrojs/netlify/netlify-functions.js',
exports: ['handler'],
args: { site }
};
}

interface NetlifyFunctionsOptions {
dist?: URL;
}

function netlifyFunctions({ dist }: NetlifyFunctionsOptions = {}): AstroIntegration {
let _config: AstroConfig;
let entryFile: string;
return {
name: '@astrojs/netlify',
hooks: {
'astro:config:setup': ({ config }) => {
if(dist) {
config.dist = dist;
} else {
config.dist = new URL('./netlify/', config.projectRoot);
}
},
'astro:config:done': ({ config, setAdapter }) => {
setAdapter(getAdapter(config.buildOptions.site));
_config = config;
},
'astro:build:start': async({ buildConfig }) => {
entryFile = buildConfig.serverEntry.replace(/\.m?js/, '');
buildConfig.client = _config.dist;
buildConfig.server = new URL('./functions/', _config.dist);
},
'astro:build:done': async ({ routes, dir }) => {
const _redirectsURL = new URL('./_redirects', dir);

// Create the redirects file that is used for routing.
let _redirects = '';
for(const route of routes) {
if(route.pathname) {
_redirects += `
${route.pathname} /.netlify/functions/${entryFile} 200`
}
}

if(fs.existsSync(_redirects)) {
await fs.promises.appendFile(_redirectsURL, _redirects, 'utf-8');
} else {
await fs.promises.writeFile(_redirectsURL, _redirects, 'utf-8');
}
}
},
};
}

export {
netlifyFunctions,
netlifyFunctions as default
};
3 changes: 3 additions & 0 deletions packages/integrations/netlify/src/integration-functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export {
netlifyFunctions as default
} from './index.js';
43 changes: 43 additions & 0 deletions packages/integrations/netlify/src/netlify-functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { SSRManifest } from 'astro';
import type { Handler } from "@netlify/functions";
import { App } from 'astro/app';
import { polyfill } from '@astrojs/webapi';

polyfill(globalThis, {
exclude: 'window document',
});

interface Args {
site?: string;
}

export const createExports = (manifest: SSRManifest, args: Args) => {
const app = new App(manifest);
const site = new URL(args.site ?? `https://netlify.com`);

const handler: Handler = async (event) => {
const headers = new Headers(event.headers as any);
const request = new Request(new URL(event.path, site).toString(), {
method: event.httpMethod,
headers
});

if(!app.match(request)) {
return {
statusCode: 404,
body: 'Not found'
};
}

const response = await app.render(request);
const body = await response.text();

return {
statusCode: 200,
headers: Object.fromEntries(response.headers.entries()),
body
};
}

return { handler };
};
10 changes: 10 additions & 0 deletions packages/integrations/netlify/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.base.json",
"include": ["src"],
"compilerOptions": {
"allowJs": true,
"module": "ES2020",
"outDir": "./dist",
"target": "ES2020"
}
}

0 comments on commit b16b0ab

Please sign in to comment.