summaryrefslogtreecommitdiff
path: root/packages/integrations/netlify
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/netlify')
-rw-r--r--packages/integrations/netlify/README.md (renamed from packages/integrations/netlify/src/README.md)14
-rw-r--r--packages/integrations/netlify/package.json6
-rw-r--r--packages/integrations/netlify/src/edge-shim.ts4
-rw-r--r--packages/integrations/netlify/src/index.ts73
-rw-r--r--packages/integrations/netlify/src/integration-edge-functions.ts98
-rw-r--r--packages/integrations/netlify/src/integration-functions.ts66
-rw-r--r--packages/integrations/netlify/src/netlify-edge-functions.ts20
-rw-r--r--packages/integrations/netlify/test/edge-functions/deps.ts3
-rw-r--r--packages/integrations/netlify/test/edge-functions/edge-basic.test.ts18
-rw-r--r--packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/astro.config.mjs11
-rw-r--r--packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/package.json9
-rw-r--r--packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/src/pages/index.astro10
-rw-r--r--packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/src/pages/two.astro6
-rw-r--r--packages/integrations/netlify/test/edge-functions/test-utils.ts13
-rw-r--r--packages/integrations/netlify/test/functions/cookies.test.js (renamed from packages/integrations/netlify/test/cookies.test.js)14
-rw-r--r--packages/integrations/netlify/test/functions/dynamic-route.test.js (renamed from packages/integrations/netlify/test/dynamic-route.test.js)19
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/.gitignore (renamed from packages/integrations/netlify/test/fixtures/.gitignore)0
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/index.astro (renamed from packages/integrations/netlify/test/fixtures/cookies/src/pages/index.astro)0
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/login.js (renamed from packages/integrations/netlify/test/fixtures/cookies/src/pages/login.js)0
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/dynamic-route/src/pages/products/[id].astro (renamed from packages/integrations/netlify/test/fixtures/dynamic-route/src/pages/products/[id].astro)0
-rw-r--r--packages/integrations/netlify/test/functions/test-utils.js29
21 files changed, 320 insertions, 93 deletions
diff --git a/packages/integrations/netlify/src/README.md b/packages/integrations/netlify/README.md
index 1a77ed598..bd98cc957 100644
--- a/packages/integrations/netlify/src/README.md
+++ b/packages/integrations/netlify/README.md
@@ -21,6 +21,20 @@ Now you can deploy!
netlify deploy
```
+## Edge Functions
+
+Netlify has two serverless platforms, Netlify Functions and Netlify Edge Functions. With Edge Functions your code is distributed closer to your users, lowering latency. You can use Edge Functions by changing the import in your astro configuration file:
+
+```diff
+import { defineConfig } from 'astro/config';
+- import netlify from '@astrojs/netlify/functions';
++ import netlify from '@astrojs/netlify/edge-functions';
+
+export default defineConfig({
+ adapter: netlify(),
+});
+```
+
## Configuration
### dist
diff --git a/packages/integrations/netlify/package.json b/packages/integrations/netlify/package.json
index abf394f5e..bcb979cf5 100644
--- a/packages/integrations/netlify/package.json
+++ b/packages/integrations/netlify/package.json
@@ -17,13 +17,17 @@
".": "./dist/index.js",
"./functions": "./dist/integration-functions.js",
"./netlify-functions.js": "./dist/netlify-functions.js",
+ "./edge-functions": "./dist/integration-edge-functions.js",
+ "./netlify-edge-functions.js": "./dist/netlify-edge-functions.js",
"./package.json": "./package.json"
},
"scripts": {
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
"build:ci": "astro-scripts build \"src/**/*.ts\"",
"dev": "astro-scripts dev \"src/**/*.ts\"",
- "test": "mocha --exit --timeout 20000"
+ "test-fn": "mocha --exit --timeout 20000 test/functions/",
+ "test-edge": "deno test --allow-run --allow-read --allow-net ./test/edge-functions/",
+ "test": "npm run test-fn"
},
"dependencies": {
"@astrojs/webapi": "^0.11.1"
diff --git a/packages/integrations/netlify/src/edge-shim.ts b/packages/integrations/netlify/src/edge-shim.ts
new file mode 100644
index 000000000..1a4a6ee9b
--- /dev/null
+++ b/packages/integrations/netlify/src/edge-shim.ts
@@ -0,0 +1,4 @@
+(globalThis as any).process = {
+ argv: [],
+ env: {},
+};
diff --git a/packages/integrations/netlify/src/index.ts b/packages/integrations/netlify/src/index.ts
index c473de67a..121495652 100644
--- a/packages/integrations/netlify/src/index.ts
+++ b/packages/integrations/netlify/src/index.ts
@@ -1,65 +1,8 @@
-import type { AstroAdapter, AstroIntegration, AstroConfig } from 'astro';
-import fs from 'fs';
-
-export function getAdapter(): AstroAdapter {
- return {
- name: '@astrojs/netlify',
- serverEntrypoint: '@astrojs/netlify/netlify-functions.js',
- exports: ['handler'],
- args: {},
- };
-}
-
-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.outDir = dist;
- } else {
- config.outDir = new URL('./netlify/', config.root);
- }
- },
- 'astro:config:done': ({ config, setAdapter }) => {
- setAdapter(getAdapter());
- _config = config;
- },
- 'astro:build:start': async ({ buildConfig }) => {
- entryFile = buildConfig.serverEntry.replace(/\.m?js/, '');
- buildConfig.client = _config.outDir;
- buildConfig.server = new URL('./functions/', _config.outDir);
- },
- '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`;
- } else {
- const pattern =
- '/' + route.segments.map(([part]) => (part.dynamic ? '*' : part.content)).join('/');
- _redirects += `
-${pattern} /.netlify/functions/${entryFile} 200`;
- }
- }
-
- // Always use appendFile() because the redirects file could already exist,
- // e.g. due to a `/public/_redirects` file that got copied to the output dir.
- // If the file does not exist yet, appendFile() automatically creates it.
- await fs.promises.appendFile(_redirectsURL, _redirects, 'utf-8');
- },
- },
- };
-}
-
-export { netlifyFunctions, netlifyFunctions as default };
+export {
+ netlifyFunctions,
+ netlifyFunctions as default
+} from './integration-functions.js';
+
+export {
+ netlifyEdgeFunctions
+} from './integration-edge-functions.js';
diff --git a/packages/integrations/netlify/src/integration-edge-functions.ts b/packages/integrations/netlify/src/integration-edge-functions.ts
new file mode 100644
index 000000000..fcce820f4
--- /dev/null
+++ b/packages/integrations/netlify/src/integration-edge-functions.ts
@@ -0,0 +1,98 @@
+import type { AstroAdapter, AstroIntegration, AstroConfig, RouteData } from 'astro';
+import * as fs from 'fs';
+
+export function getAdapter(): AstroAdapter {
+ return {
+ name: '@astrojs/netlify/edge-functions',
+ serverEntrypoint: '@astrojs/netlify/netlify-edge-functions.js',
+ exports: ['default'],
+ };
+}
+
+interface NetlifyEdgeFunctionsOptions {
+ dist?: URL;
+}
+
+interface NetlifyEdgeFunctionManifestFunctionPath {
+ function: string;
+ path: string;
+}
+
+interface NetlifyEdgeFunctionManifestFunctionPattern {
+ function: string;
+ pattern: string;
+}
+
+type NetlifyEdgeFunctionManifestFunction = NetlifyEdgeFunctionManifestFunctionPath | NetlifyEdgeFunctionManifestFunctionPattern;
+
+interface NetlifyEdgeFunctionManifest {
+ functions: NetlifyEdgeFunctionManifestFunction[];
+ version: 1;
+}
+
+async function createEdgeManifest(routes: RouteData[], entryFile: string, dir: URL) {
+ const functions: NetlifyEdgeFunctionManifestFunction[] = [];
+ for(const route of routes) {
+ if(route.pathname) {
+ functions.push({
+ function: entryFile,
+ path: route.pathname
+ });
+ } else {
+ functions.push({
+ function: entryFile,
+ pattern: route.pattern.source
+ });
+ }
+ }
+
+ const manifest: NetlifyEdgeFunctionManifest = {
+ functions,
+ version: 1
+ };
+
+ const manifestURL = new URL('./manifest.json', dir);
+ const _manifest = JSON.stringify(manifest, null, ' ');
+ await fs.promises.writeFile(manifestURL, _manifest, 'utf-8');
+}
+
+export function netlifyEdgeFunctions({ dist }: NetlifyEdgeFunctionsOptions = {}): AstroIntegration {
+ let _config: AstroConfig;
+ let entryFile: string;
+ return {
+ name: '@astrojs/netlify/edge-functions',
+ hooks: {
+ 'astro:config:setup': ({ config }) => {
+ if (dist) {
+ config.outDir = dist;
+ } else {
+ config.outDir = new URL('./netlify/', config.root);
+ }
+ },
+ 'astro:config:done': ({ config, setAdapter }) => {
+ setAdapter(getAdapter());
+ _config = config;
+ },
+ 'astro:build:start': async ({ buildConfig }) => {
+ entryFile = buildConfig.serverEntry.replace(/\.m?js/, '');
+ buildConfig.client = _config.outDir;
+ buildConfig.server = new URL('./edge-functions/', _config.outDir);
+ },
+ 'astro:build:setup': ({ vite, target }) => {
+ if (target === 'server') {
+ vite.ssr = {
+ noExternal: true,
+ };
+ }
+ },
+ 'astro:build:done': async ({ routes, dir }) => {
+
+ await createEdgeManifest(routes, entryFile, new URL('./edge-functions/', dir));
+ },
+ },
+ };
+}
+
+export {
+ netlifyEdgeFunctions as default
+}
diff --git a/packages/integrations/netlify/src/integration-functions.ts b/packages/integrations/netlify/src/integration-functions.ts
index 5b687cda4..2720eb591 100644
--- a/packages/integrations/netlify/src/integration-functions.ts
+++ b/packages/integrations/netlify/src/integration-functions.ts
@@ -1 +1,65 @@
-export { netlifyFunctions as default } from './index.js';
+import type { AstroAdapter, AstroIntegration, AstroConfig } from 'astro';
+import fs from 'fs';
+
+export function getAdapter(): AstroAdapter {
+ return {
+ name: '@astrojs/netlify/functions',
+ serverEntrypoint: '@astrojs/netlify/netlify-functions.js',
+ exports: ['handler'],
+ args: {},
+ };
+}
+
+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.outDir = dist;
+ } else {
+ config.outDir = new URL('./netlify/', config.root);
+ }
+ },
+ 'astro:config:done': ({ config, setAdapter }) => {
+ setAdapter(getAdapter());
+ _config = config;
+ },
+ 'astro:build:start': async ({ buildConfig }) => {
+ entryFile = buildConfig.serverEntry.replace(/\.m?js/, '');
+ buildConfig.client = _config.outDir;
+ buildConfig.server = new URL('./functions/', _config.outDir);
+ },
+ '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`;
+ } else {
+ const pattern =
+ '/' + route.segments.map(([part]) => (part.dynamic ? '*' : part.content)).join('/');
+ _redirects += `
+${pattern} /.netlify/functions/${entryFile} 200`;
+ }
+ }
+
+ // Always use appendFile() because the redirects file could already exist,
+ // e.g. due to a `/public/_redirects` file that got copied to the output dir.
+ // If the file does not exist yet, appendFile() automatically creates it.
+ await fs.promises.appendFile(_redirectsURL, _redirects, 'utf-8');
+ },
+ },
+ };
+}
+
+export { netlifyFunctions, netlifyFunctions as default };
diff --git a/packages/integrations/netlify/src/netlify-edge-functions.ts b/packages/integrations/netlify/src/netlify-edge-functions.ts
new file mode 100644
index 000000000..f7000442c
--- /dev/null
+++ b/packages/integrations/netlify/src/netlify-edge-functions.ts
@@ -0,0 +1,20 @@
+import './edge-shim.js';
+import { SSRManifest } from 'astro';
+import { App } from 'astro/app';
+
+export function createExports(manifest: SSRManifest) {
+ const app = new App(manifest);
+
+ const handler = async (request: Request): Promise<Response> => {
+ if(app.match(request)) {
+ return app.render(request);
+ }
+
+ return new Response(null, {
+ status: 404,
+ statusText: 'Not found'
+ });
+ };
+
+ return { 'default': handler };
+}
diff --git a/packages/integrations/netlify/test/edge-functions/deps.ts b/packages/integrations/netlify/test/edge-functions/deps.ts
new file mode 100644
index 000000000..f3e46181a
--- /dev/null
+++ b/packages/integrations/netlify/test/edge-functions/deps.ts
@@ -0,0 +1,3 @@
+// @ts-nocheck
+export { fromFileUrl } from 'https://deno.land/std@0.110.0/path/mod.ts';
+export { assertEquals, assert } from 'https://deno.land/std@0.132.0/testing/asserts.ts';
diff --git a/packages/integrations/netlify/test/edge-functions/edge-basic.test.ts b/packages/integrations/netlify/test/edge-functions/edge-basic.test.ts
new file mode 100644
index 000000000..a99125980
--- /dev/null
+++ b/packages/integrations/netlify/test/edge-functions/edge-basic.test.ts
@@ -0,0 +1,18 @@
+// @ts-ignore
+import { runBuild } from './test-utils.ts';
+// @ts-ignore
+import { assertEquals, assert } from './deps.ts';
+
+// @ts-ignore
+Deno.test({
+ name: 'Edge Basics',
+ async fn() {
+ let close = await runBuild('./fixtures/edge-basic/');
+ const { default: handler } = await import('./fixtures/edge-basic/dist/edge-functions/entry.mjs');
+ const response = await handler(new Request('http://example.com/'));
+ assertEquals(response.status, 200);
+ const html = await response.text();
+ assert(html, 'got some html');
+ await close();
+ },
+});
diff --git a/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/astro.config.mjs b/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/astro.config.mjs
new file mode 100644
index 000000000..c55135e43
--- /dev/null
+++ b/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/astro.config.mjs
@@ -0,0 +1,11 @@
+import { defineConfig } from 'astro/config';
+import { netlifyEdgeFunctions } from '@astrojs/netlify';
+
+export default defineConfig({
+ adapter: netlifyEdgeFunctions({
+ dist: new URL('./dist/', import.meta.url),
+ }),
+ experimental: {
+ ssr: true
+ }
+})
diff --git a/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/package.json b/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/package.json
new file mode 100644
index 000000000..bbda2476b
--- /dev/null
+++ b/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/netlify-edge-astro-basic",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/netlify": "workspace:*"
+ }
+}
diff --git a/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/src/pages/index.astro b/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/src/pages/index.astro
new file mode 100644
index 000000000..a87de65db
--- /dev/null
+++ b/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/src/pages/index.astro
@@ -0,0 +1,10 @@
+<html>
+<head><title>Testing</title></head>
+<body>
+ <h1>Test page</h1>
+ <h2>Links</h2>
+ <ul>
+ <li><a href="/two/">Two</a></li>
+ </ul>
+</body>
+</html>
diff --git a/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/src/pages/two.astro b/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/src/pages/two.astro
new file mode 100644
index 000000000..b5a031be3
--- /dev/null
+++ b/packages/integrations/netlify/test/edge-functions/fixtures/edge-basic/src/pages/two.astro
@@ -0,0 +1,6 @@
+<html>
+<head><title>Page Two</title></head>
+<body>
+ <h1>Page two</h1>
+</body>
+</html>
diff --git a/packages/integrations/netlify/test/edge-functions/test-utils.ts b/packages/integrations/netlify/test/edge-functions/test-utils.ts
new file mode 100644
index 000000000..826f64d37
--- /dev/null
+++ b/packages/integrations/netlify/test/edge-functions/test-utils.ts
@@ -0,0 +1,13 @@
+// @ts-ignore
+import { fromFileUrl } from './deps.ts';
+const dir = new URL('./', import.meta.url);
+
+export async function runBuild(fixturePath: string) {
+ // @ts-ignore
+ let proc = Deno.run({
+ cmd: ['node', '../../../../../../astro/astro.js', 'build', '--silent'],
+ cwd: fromFileUrl(new URL(fixturePath, dir)),
+ });
+ await proc.status();
+ return async () => await proc.close();
+}
diff --git a/packages/integrations/netlify/test/cookies.test.js b/packages/integrations/netlify/test/functions/cookies.test.js
index 0fdc126e8..93cc05229 100644
--- a/packages/integrations/netlify/test/cookies.test.js
+++ b/packages/integrations/netlify/test/functions/cookies.test.js
@@ -1,7 +1,7 @@
import { expect } from 'chai';
import { load as cheerioLoad } from 'cheerio';
-import { loadFixture } from '../../../astro/test/test-utils.js';
-import netlifyAdapter from '../dist/index.js';
+import { loadFixture, testIntegration } from './test-utils.js';
+import netlifyAdapter from '../../dist/index.js';
import { fileURLToPath } from 'url';
describe('Cookies', () => {
@@ -18,15 +18,7 @@ describe('Cookies', () => {
dist: new URL('./fixtures/cookies/dist/', import.meta.url),
}),
site: `http://example.com`,
- vite: {
- resolve: {
- alias: {
- '@astrojs/netlify/netlify-functions.js': fileURLToPath(
- new URL('../dist/netlify-functions.js', import.meta.url)
- ),
- },
- },
- },
+ integrations: [ testIntegration() ]
});
await fixture.build();
});
diff --git a/packages/integrations/netlify/test/dynamic-route.test.js b/packages/integrations/netlify/test/functions/dynamic-route.test.js
index 18d8b8ec2..279982767 100644
--- a/packages/integrations/netlify/test/dynamic-route.test.js
+++ b/packages/integrations/netlify/test/functions/dynamic-route.test.js
@@ -1,12 +1,9 @@
import { expect } from 'chai';
-import { load as cheerioLoad } from 'cheerio';
-import { loadFixture } from '../../../astro/test/test-utils.js';
-import netlifyAdapter from '../dist/index.js';
-import { fileURLToPath } from 'url';
+import netlifyAdapter from '../../dist/index.js';
+import { loadFixture, testIntegration } from './test-utils.js';
-// Asset bundling
describe('Dynamic pages', () => {
- /** @type {import('../../../astro/test/test-utils').Fixture} */
+ /** @type {import('./test-utils').Fixture} */
let fixture;
before(async () => {
@@ -19,15 +16,7 @@ describe('Dynamic pages', () => {
dist: new URL('./fixtures/dynamic-route/dist/', import.meta.url),
}),
site: `http://example.com`,
- vite: {
- resolve: {
- alias: {
- '@astrojs/netlify/netlify-functions.js': fileURLToPath(
- new URL('../dist/netlify-functions.js', import.meta.url)
- ),
- },
- },
- },
+ integrations: [ testIntegration() ]
});
await fixture.build();
});
diff --git a/packages/integrations/netlify/test/fixtures/.gitignore b/packages/integrations/netlify/test/functions/fixtures/.gitignore
index 916f60644..916f60644 100644
--- a/packages/integrations/netlify/test/fixtures/.gitignore
+++ b/packages/integrations/netlify/test/functions/fixtures/.gitignore
diff --git a/packages/integrations/netlify/test/fixtures/cookies/src/pages/index.astro b/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/index.astro
index 53e029f04..53e029f04 100644
--- a/packages/integrations/netlify/test/fixtures/cookies/src/pages/index.astro
+++ b/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/index.astro
diff --git a/packages/integrations/netlify/test/fixtures/cookies/src/pages/login.js b/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/login.js
index a9ca52f69..a9ca52f69 100644
--- a/packages/integrations/netlify/test/fixtures/cookies/src/pages/login.js
+++ b/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/login.js
diff --git a/packages/integrations/netlify/test/fixtures/dynamic-route/src/pages/products/[id].astro b/packages/integrations/netlify/test/functions/fixtures/dynamic-route/src/pages/products/[id].astro
index 5ed06d251..5ed06d251 100644
--- a/packages/integrations/netlify/test/fixtures/dynamic-route/src/pages/products/[id].astro
+++ b/packages/integrations/netlify/test/functions/fixtures/dynamic-route/src/pages/products/[id].astro
diff --git a/packages/integrations/netlify/test/functions/test-utils.js b/packages/integrations/netlify/test/functions/test-utils.js
new file mode 100644
index 000000000..19cd7ef66
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/test-utils.js
@@ -0,0 +1,29 @@
+// @ts-check
+import { fileURLToPath } from 'url';
+
+export * from '../../../../astro/test/test-utils.js';
+
+/**
+ *
+ * @returns {import('../../../../astro/dist/types/@types/astro').AstroIntegration}
+ */
+export function testIntegration() {
+ return {
+ name: '@astrojs/netlify/test-integration',
+ hooks: {
+ 'astro:config:setup':({ updateConfig }) => {
+ updateConfig({
+ vite: {
+ resolve: {
+ alias: {
+ '@astrojs/netlify/netlify-functions.js': fileURLToPath(
+ new URL('../../dist/netlify-functions.js', import.meta.url)
+ ),
+ },
+ },
+ },
+ });
+ }
+ }
+ };
+}