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

Netlify adapter #2879

Merged
merged 8 commits into from
Mar 25, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
18 changes: 18 additions & 0 deletions .changeset/blue-rocks-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'astro': patch
'@astrojs/netlify': patch
'@astrojs/node': patch
---

Netlify Adapter

This change adds a Netlify adapter that uses Netlify Functions. You can use it like so:

```js
import { defineConfig } from 'astro/config';
import { netlifyFunctions } from '@astrojs/netlify';
matthewp marked this conversation as resolved.
Show resolved Hide resolved

export default defineConfig({
adapter: netlifyFunctions()
});
```
4 changes: 4 additions & 0 deletions packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
"types": "./dist/types/@types/astro.d.ts",
"typesVersions": {
"*": {
"app": [
"./dist/types/core/app/index"
],
"app/*": [
"./dist/types/core/app/*"
]
Expand All @@ -27,6 +30,7 @@
"./internal": "./internal.js",
"./app": "./dist/core/app/index.js",
"./app/node": "./dist/core/app/node.js",
"./adapter": "./dist/core/adapter/index.js",
matthewp marked this conversation as resolved.
Show resolved Hide resolved
"./client/*": "./dist/runtime/client/*",
"./components": "./components/index.js",
"./components/*": "./components/*",
Expand Down
6 changes: 5 additions & 1 deletion packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export interface CLIFlags {
}

export interface BuildConfig {
client: URL;
server: URL;
serverEntry: string;
staticMode: boolean | undefined;
}

Expand Down Expand Up @@ -617,6 +620,7 @@ export interface AstroAdapter {
name: string;
serverEntrypoint?: string;
exports?: string[];
args?: any;
}

export interface EndpointOutput<Output extends Body = Body> {
Expand Down Expand Up @@ -670,7 +674,7 @@ export interface AstroIntegration {
'astro:server:start'?: (options: { address: AddressInfo }) => void | Promise<void>;
'astro:server:done'?: () => void | Promise<void>;
'astro:build:start'?: (options: { buildConfig: BuildConfig }) => void | Promise<void>;
'astro:build:done'?: (options: { pages: { pathname: string }[]; dir: URL }) => void | Promise<void>;
'astro:build:done'?: (options: { pages: { pathname: string }[]; dir: URL; routes: RouteData[] }) => void | Promise<void>;
};
}

Expand Down
2 changes: 2 additions & 0 deletions packages/astro/src/core/app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ export interface SSRManifest {
export type SerializedSSRManifest = Omit<SSRManifest, 'routes'> & {
routes: SerializedRouteInfo[];
};

export type AdapterCreateExports<T = any> = (manifest: SSRManifest, args?: T) => Record<string, any>;
13 changes: 0 additions & 13 deletions packages/astro/src/core/build/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { AstroConfig, RouteType } from '../../@types/astro';
import type { StaticBuildOptions } from './types';
import npath from 'path';
import { appendForwardSlash } from '../../core/path.js';

Expand All @@ -9,18 +8,6 @@ export function getOutRoot(astroConfig: AstroConfig): URL {
return new URL('./', astroConfig.dist);
}

export function getServerRoot(astroConfig: AstroConfig): URL {
const rootFolder = getOutRoot(astroConfig);
const serverFolder = new URL('./server/', rootFolder);
return serverFolder;
}

export function getClientRoot(astroConfig: AstroConfig): URL {
const rootFolder = getOutRoot(astroConfig);
const serverFolder = new URL('./client/', rootFolder);
return serverFolder;
}

export function getOutFolder(astroConfig: AstroConfig, pathname: string, routeType: RouteType): URL {
const outRoot = getOutRoot(astroConfig);

Expand Down
7 changes: 4 additions & 3 deletions packages/astro/src/core/build/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { BEFORE_HYDRATION_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
import { call as callEndpoint } from '../endpoint/index.js';
import { render } from '../render/core.js';
import { createLinkStylesheetElementSet, createModuleScriptElementWithSrcSet } from '../render/ssr-element.js';
import { getOutFile, getOutRoot, getOutFolder, getServerRoot } from './common.js';
import { getOutFile, getOutRoot, getOutFolder } from './common.js';
import { getPageDataByComponent, eachPageData } from './internal.js';
import { bgMagenta, black, cyan, dim, magenta } from 'kleur/colors';
import { getTimeStat } from './util.js';
Expand Down Expand Up @@ -70,8 +70,9 @@ export async function generatePages(result: RollupOutput, opts: StaticBuildOptio
info(opts.logging, null, `\n${bgMagenta(black(' generating static routes '))}\n`);

const ssr = !!opts.astroConfig._ctx.adapter?.serverEntrypoint;
const outFolder = ssr ? getServerRoot(opts.astroConfig) : getOutRoot(opts.astroConfig);
const ssrEntryURL = new URL(`./entry.mjs?time=${Date.now()}`, outFolder);
const serverEntry = opts.buildConfig.serverEntry;
const outFolder = ssr ? opts.buildConfig.server : opts.buildConfig.client;
const ssrEntryURL = new URL('./' + serverEntry + `?time=${Date.now()}`, outFolder);
const ssrEntry = await import(ssrEntryURL.toString());

for(const pageData of eachPageData(internals)) {
Expand Down
12 changes: 9 additions & 3 deletions packages/astro/src/core/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { apply as applyPolyfill } from '../polyfill.js';
import { performance } from 'perf_hooks';
import * as vite from 'vite';
import { createVite, ViteConfigWithSSR } from '../create-vite.js';
import { debug, defaultLogOptions, info, levels, timerMessage, warn } from '../logger.js';
import { debug, defaultLogOptions, info, levels, timerMessage, warn, warnIfUsingExperimentalSSR } from '../logger.js';
import { createRouteManifest } from '../routing/index.js';
import { generateSitemap } from '../render/sitemap.js';
import { collectPagesData } from './page-data.js';
Expand Down Expand Up @@ -73,11 +73,17 @@ class AstroBuilder {
{ astroConfig: this.config, logging, mode: 'build' }
);
await runHookConfigDone({ config: this.config });
warnIfUsingExperimentalSSR(logging, this.config);
this.viteConfig = viteConfig;
const viteServer = await vite.createServer(viteConfig);
this.viteServer = viteServer;
debug('build', timerMessage('Vite started', timer.viteStart));
const buildConfig: BuildConfig = { staticMode: undefined };
const buildConfig: BuildConfig = {
client: new URL('./client/', this.config.dist),
server: new URL('./server/', this.config.dist),
serverEntry: 'entry.mjs',
staticMode: undefined
};
await runHookBuildStart({ config: this.config, buildConfig });

info(this.logging, 'build', 'Collecting page data...');
Expand Down Expand Up @@ -167,7 +173,7 @@ class AstroBuilder {

// You're done! Time to clean up.
await viteServer.close();
await runHookBuildDone({ config: this.config, pages: pageNames });
await runHookBuildDone({ config: this.config, pages: pageNames, routes: Object.values(allPages).map(pd => pd.route) });

if (logging.level && levels[logging.level] <= levels['info']) {
const buildMode = this.config.buildOptions.experimentalSsr ? 'ssr' : 'static';
Expand Down
15 changes: 7 additions & 8 deletions packages/astro/src/core/build/static-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { vitePluginSSR } from './vite-plugin-ssr.js';
import { vitePluginPages } from './vite-plugin-pages.js';
import { generatePages } from './generate.js';
import { trackPageData } from './internal.js';
import { getClientRoot, getServerRoot, getOutRoot } from './common.js';
import { getOutRoot } from './common.js';
import { isBuildingToSSR } from '../util.js';
import { getTimeStat } from './util.js';

Expand Down Expand Up @@ -114,7 +114,7 @@ export async function staticBuild(opts: StaticBuildOptions) {
async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) {
const { astroConfig, viteConfig } = opts;
const ssr = astroConfig.buildOptions.experimentalSsr;
const out = ssr ? getServerRoot(astroConfig) : getOutRoot(astroConfig);
const out = ssr ? opts.buildConfig.server : opts.buildConfig.client;
// TODO: use vite.mergeConfig() here?
return await vite.build({
logLevel: 'error',
Expand All @@ -130,9 +130,10 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
input: [],
output: {
format: 'esm',
entryFileNames: 'entry.mjs',
entryFileNames: opts.buildConfig.serverEntry,
chunkFileNames: 'chunks/chunk.[hash].mjs',
assetFileNames: 'assets/asset.[hash][extname]',
inlineDynamicImports: true,
},
},
// must match an esbuild target
Expand Down Expand Up @@ -171,8 +172,6 @@ async function clientBuild(opts: StaticBuildOptions, internals: BuildInternals,
return null;
}

const out = astroConfig.buildOptions.experimentalSsr ? getClientRoot(astroConfig) : getOutRoot(astroConfig);

// TODO: use vite.mergeConfig() here?

info(opts.logging, null, `\n${bgGreen(black(' building resources '))}\n`);
Expand All @@ -184,7 +183,7 @@ async function clientBuild(opts: StaticBuildOptions, internals: BuildInternals,
build: {
emptyOutDir: false,
minify: 'esbuild',
outDir: fileURLToPath(out),
outDir: fileURLToPath(opts.buildConfig.client),
rollupOptions: {
input: Array.from(input),
output: {
Expand Down Expand Up @@ -231,8 +230,8 @@ async function cleanSsrOutput(opts: StaticBuildOptions) {
async function ssrMoveAssets(opts: StaticBuildOptions) {
info(opts.logging, 'build', 'Rearranging server assets...');
const { astroConfig } = opts;
const serverRoot = getServerRoot(astroConfig);
const clientRoot = getClientRoot(astroConfig);
const serverRoot = opts.buildConfig.staticMode ? opts.buildConfig.client : opts.buildConfig.server;
const clientRoot = opts.buildConfig.client;
const serverAssets = new URL('./assets/', serverRoot);
const clientAssets = new URL('./assets/', clientRoot);
const files = await glob('assets/**/*', {
Expand Down
7 changes: 4 additions & 3 deletions packages/astro/src/core/build/vite-plugin-ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,24 @@ const _manifest = Object.assign(_deserializeManifest('${manifestReplace}'), {
pageMap: _main.pageMap,
renderers: _main.renderers
});
const _args = ${adapter.args ? JSON.stringify(adapter.args) : 'undefined'};

${
adapter.exports
? `const _exports = adapter.createExports(_manifest);
? `const _exports = adapter.createExports(_manifest, _args);
${adapter.exports.map((name) => `export const ${name} = _exports['${name}'];`).join('\n')}
`
: ''
}
const _start = 'start';
if(_start in adapter) {
adapter[_start](_manifest);
adapter[_start](_manifest, _args);
}`;
}
return void 0;
},

generateBundle(opts, bundle) {
generateBundle(_opts, bundle) {
const manifest = buildManifest(buildOpts, internals);

for(const [_chunkName, chunk] of Object.entries(bundle)) {
Expand Down
3 changes: 2 additions & 1 deletion packages/astro/src/core/dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as vite from 'vite';
import type { AstroConfig } from '../../@types/astro';
import { runHookConfigDone, runHookConfigSetup, runHookServerDone, runHookServerSetup, runHookServerStart } from '../../integrations/index.js';
import { createVite } from '../create-vite.js';
import { defaultLogOptions, info, LogOptions, warn } from '../logger.js';
import { defaultLogOptions, info, LogOptions, warn, warnIfUsingExperimentalSSR } from '../logger.js';
import * as msg from '../messages.js';
import { apply as applyPolyfill } from '../polyfill.js';
import { getResolvedHostForVite } from './util.js';
Expand Down Expand Up @@ -32,6 +32,7 @@ export default async function dev(config: AstroConfig, options: DevOptions = { l
{ astroConfig: config, logging: options.logging, mode: 'dev' }
);
await runHookConfigDone({ config });
warnIfUsingExperimentalSSR(options.logging, config);
const viteServer = await vite.createServer(viteConfig);
runHookServerSetup({ config, server: viteServer });
await viteServer.listen(config.devOptions.port);
Expand Down
11 changes: 11 additions & 0 deletions packages/astro/src/core/logger.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { AstroConfig } from '../@types/astro';
import { bold, cyan, dim, red, yellow, reset } from 'kleur/colors';
import { performance } from 'perf_hooks';
import { Writable } from 'stream';
import stringWidth from 'string-width';
import * as readline from 'readline';
import debugPackage from 'debug';
import { format as utilFormat } from 'util';
import { isBuildingToSSR } from './util.js';

type ConsoleStream = Writable & {
fd: 1 | 2;
Expand Down Expand Up @@ -211,3 +213,12 @@ export function timerMessage(message: string, startTime: number = performance.no
let timeDisplay = timeDiff < 750 ? `${Math.round(timeDiff)}ms` : `${(timeDiff / 1000).toFixed(1)}s`;
return `${message} ${dim(timeDisplay)}`;
}

/**
* A warning that SSR is experimental. Remove when we can.
*/
export function warnIfUsingExperimentalSSR(opts: LogOptions, config: AstroConfig) {
if(isBuildingToSSR(config)) {
warn(opts, 'warning', bold(`Warning:`), ` SSR support is still experimental and subject to API changes. If using in production pin your dependencies to prevent accidental breakage.`);
}
}
6 changes: 3 additions & 3 deletions packages/astro/src/integrations/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { AddressInfo } from 'net';
import type { ViteDevServer } from 'vite';
import { AstroConfig, AstroRenderer, BuildConfig } from '../@types/astro.js';
import { AstroConfig, AstroRenderer, BuildConfig, RouteData } from '../@types/astro.js';
import { mergeConfig } from '../core/config.js';
import ssgAdapter from '../adapter-ssg/index.js';

Expand Down Expand Up @@ -91,10 +91,10 @@ export async function runHookBuildStart({ config, buildConfig }: { config: Astro
}
}

export async function runHookBuildDone({ config, pages }: { config: AstroConfig; pages: string[] }) {
export async function runHookBuildDone({ config, pages, routes }: { config: AstroConfig; pages: string[], routes: RouteData[] }) {
for (const integration of config.integrations) {
if (integration.hooks['astro:build:done']) {
await integration.hooks['astro:build:done']({ pages: pages.map((p) => ({ pathname: p })), dir: config.dist });
await integration.hooks['astro:build:done']({ pages: pages.map((p) => ({ pathname: p })), dir: config.dist, routes });
}
}
}
33 changes: 33 additions & 0 deletions packages/integrations/netlify/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"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",
"./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:*"
}
}
27 changes: 27 additions & 0 deletions packages/integrations/netlify/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# @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 { netlifyFunctions } from '@astrojs/netlify';

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

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

```toml
[functions]
directory = "dist/functions"
```
matthewp marked this conversation as resolved.
Show resolved Hide resolved

Now you can deploy!

```shell
netlify deploy
```
Loading