Skip to content

Commit

Permalink
first crack at adapter-auto (#2867)
Browse files Browse the repository at this point in the history
* first crack at adapter-auto

* make stuff work, maybe

* changeset

* appease the mighty eslint gods

* update readme

* add cloudflare pages

* add missing adapter

* update lockfile

* add support for community adapters

* update language

* update adapter docs

* tweak wording
  • Loading branch information
Rich-Harris authored Nov 23, 2021
1 parent cc45b2c commit 324e70a
Show file tree
Hide file tree
Showing 15 changed files with 225 additions and 85 deletions.
6 changes: 6 additions & 0 deletions .changeset/popular-schools-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sveltejs/adapter-auto': patch
'create-svelte': patch
---

Add adapter-auto
98 changes: 76 additions & 22 deletions documentation/docs/10-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,99 @@
title: Adapters
---

Before you can deploy your SvelteKit app, you need to _adapt_ it for your deployment target. Adapters are small plugins that take the built app as input and generate output for deployment. Many adapters are optimised for a specific hosting provider, and you can generally find information about deployment in your adapter's documentation. However, some adapters, like `adapter-static`, build output that can be hosted on numerous hosting providers, so it may also be helpful to reference the documentation of your hosting provider in these cases.
Before you can deploy your SvelteKit app, you need to _adapt_ it for your deployment target. Adapters are small plugins that take the built app as input and generate output for deployment.

For example, if you want to run your app as a simple Node server, you would use the `@sveltejs/adapter-node@next` package:
By default, projects are configured to use `@sveltejs/adapter-auto`, which detects your production environment and selects the appropriate adapter where possible. If your platform isn't (yet) supported, you may need to [install a custom adapter](#adapters-installing-custom-adapters) or [write one](#adapters-writing-custom-adapters).

```js
> See the [adapter-auto README](https://github.com/sveltejs/kit/tree/master/packages/adapter-auto) for information on adding support for new environments.
### Supported environments

The following platforms are officially supported and require no additional configuration:

- [Cloudflare Pages](https://developers.cloudflare.com/pages/) via [`adapter-cloudflare`](https://github.com/sveltejs/kit/tree/master/packages/adapter-cloudflare)
- [Netlify](https://netlify.com) via [`adapter-netlify`](https://github.com/sveltejs/kit/tree/master/packages/adapter-netlify)
- [Vercel](https://vercel.com) via [`adapter-vercel`](https://github.com/sveltejs/kit/tree/master/packages/adapter-vercel)

### Installing custom adapters

Additional [community-provided adapters](https://sveltesociety.dev/components#adapters) exist for other platforms. After installing the relevant adapter with your package manager, update your `svelte.config.js`:

```diff
// svelte.config.js
import node from '@sveltejs/adapter-node';
-import adapter from '@sveltejs/adapter-auto';
+import adapter from 'svelte-adapter-[x]';
```

export default {
kit: {
adapter: node()
}
};
### Building a Node app

To create a simple Node server, install the `@sveltejs/adapter-node@next` package and update your `svelte.config.js`:

```diff
// svelte.config.js
-import adapter from '@sveltejs/adapter-auto';
+import adapter from '@sveltejs/adapter-node';
```

With this, [svelte-kit build](#command-line-interface-svelte-kit-build) will generate a self-contained Node app inside `build`. You can pass options to adapters, such as customising the output directory in `adapter-node`:
With this, [svelte-kit build](#command-line-interface-svelte-kit-build) will generate a self-contained Node app inside the `build` directory. You can pass options to adapters, such as customising the output directory:

```diff
// svelte.config.js
import node from '@sveltejs/adapter-node';
import adapter from '@sveltejs/adapter-node';

export default {
kit: {
- adapter: node()
+ adapter: node({ out: 'my-output-directory' })
- adapter: adapter()
+ adapter: adapter({ out: 'my-output-directory' })
}
};
```

A variety of official adapters exist for serverless platforms...
### Creating a static site

Most adapters will generate static HTML for any [prerenderable](#ssr-and-javascript-prerender) pages of your site. In some cases, your entire app might be prerenderable, in which case you can use `@sveltejs/adapter-static@next` to generate static HTML for _all_ your pages. A fully static site can be hosted on a wide variety of platforms, including static hosts like [GitHub Pages](https://pages.github.com/).

```diff
// svelte.config.js
-import adapter from '@sveltejs/adapter-auto';
+import adapter from '@sveltejs/adapter-static';
```

You can also use `adapter-static` to generate single-page apps (SPAs) by specifying a [fallback page](https://github.com/sveltejs/kit/tree/master/packages/adapter-static#spa-mode).

### Writing custom adapters

We recommend [looking at the source for an adapter](https://github.com/sveltejs/kit/tree/master/packages) to a platform similar to yours and copying it as a starting point.

Adapters packages must implement the following API, which creates an `Adapter`:

```js
/** @param {AdapterSpecificOptions} options */
export default function (options) {
/** @type {import('@sveltejs/kit').Adapter} */
return {
name: 'adapter-package-name',
async adapt({ utils, config }) {
// adapter implementation
}
};
}
```

The types for `Adapter` and its parameters are available in [types/config.d.ts](https://github.com/sveltejs/kit/blob/master/packages/kit/types/config.d.ts).

- [`adapter-cloudflare`](https://github.com/sveltejs/kit/tree/master/packages/adapter-cloudflare) — for [Cloudflare Pages](https://developers.cloudflare.com/pages/)
- [`adapter-cloudflare-workers`](https://github.com/sveltejs/kit/tree/master/packages/adapter-cloudflare-workers) — for [Cloudflare Workers](https://developers.cloudflare.com/workers/)
- [`adapter-netlify`](https://github.com/sveltejs/kit/tree/master/packages/adapter-netlify) — for [Netlify](https://netlify.com)
- [`adapter-vercel`](https://github.com/sveltejs/kit/tree/master/packages/adapter-vercel) — for [Vercel](https://vercel.com)
Within the `adapt` method, there are a number of things that an adapter should do:

...and traditional platforms:
- Clear out the build directory
- Output code that:
- Imports `init` and `render` from `.svelte-kit/output/server/app.js`
- Calls `init`, which configures the app
- Listens for requests from the platform, converts them to a a [SvelteKit request](#hooks-handle), calls the `render` function to generate a [SvelteKit response](#hooks-handle) and responds with it
- Globally shims `fetch` to work on the target platform, if necessary. SvelteKit provides a `@sveltejs/kit/install-fetch` helper for platforms that can use `node-fetch`
- Bundle the output to avoid needing to install dependencies on the target platform, if desired
- Call `utils.prerender`
- Put the user's static files and the generated JS/CSS in the correct location for the target platform

- [`adapter-node`](https://github.com/sveltejs/kit/tree/master/packages/adapter-node) — for creating self-contained Node apps
- [`adapter-static`](https://github.com/sveltejs/kit/tree/master/packages/adapter-static) — for prerendering your entire site as a collection of static files
If possible, we recommend putting the adapter output under the `build/` directory with any intermediate output placed under `.svelte-kit/[adapter-name]`.

As well as [community-provided adapters](https://sveltesociety.dev/components#adapters). You may also [write your own adapter](#writing-an-adapter).
> The adapter API may change before 1.0.
37 changes: 0 additions & 37 deletions documentation/docs/80-adapter-api.md

This file was deleted.

2 changes: 2 additions & 0 deletions packages/adapter-auto/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
node_modules
19 changes: 19 additions & 0 deletions packages/adapter-auto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# adapter-auto

Automatically chooses the adapter for your current environment, if possible.

## Supported environments

The following environments are supported out-of-the-box, meaning a newly created project can be deployed on one of these platforms without any additional configuration:

- [Cloudflare Pages](https://developers.cloudflare.com/pages/) via [adapter-cloudflare](../adapter-cloudflare)
- [Netlify](https://netlify.com/) via [adapter-netlify](../adapter-netlify)
- [Vercel](https://vercel.com/) via [adapter-vercel](../adapter-vercel)

## Community adapters

Support for additional environments can be added in [adapters.js](adapters.js). To avoid this package ballooning in size, community-supported adapters should not be added as dependencies — adapter-auto will instead prompt users to install missing packages as needed.

## Changelog

[The Changelog for this package is available on GitHub](https://github.com/sveltejs/kit/blob/master/packages/adapter-auto/CHANGELOG.md).
17 changes: 17 additions & 0 deletions packages/adapter-auto/adapters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const adapters = [
{
name: 'Vercel',
test: () => !!process.env.VERCEL,
module: '@sveltejs/adapter-vercel'
},
{
name: 'Netlify',
test: () => !!process.env.NETLIFY,
module: '@sveltejs/adapter-netlify'
},
{
name: 'Cloudflare Pages',
test: () => !!process.env.CF_PAGES,
module: '@sveltejs/adapter-cloudflare'
}
];
4 changes: 4 additions & 0 deletions packages/adapter-auto/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Adapter } from '@sveltejs/kit';

declare function plugin(): Adapter;
export = plugin;
42 changes: 42 additions & 0 deletions packages/adapter-auto/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { adapters } from './adapters.js';

/** @type {import('.')} **/
export default function () {
return {
name: '@sveltejs/adapter-auto',

async adapt(options) {
for (const candidate of adapters) {
if (candidate.test()) {
options.utils.log.info(
`Detected environment: \u001B[1m\u001B[92m${candidate.name}\u001B[39m\u001B[22m. Using ${candidate.module}`
);

let module;

try {
module = await import(candidate.module);
} catch (error) {
if (
error.code === 'ERR_MODULE_NOT_FOUND' &&
error.message.startsWith(`Cannot find package '${candidate.module}'`)
) {
throw new Error(
`It looks like ${candidate.module} is not installed. Please install it and try building your project again.`
);
}

throw error;
}

const adapter = module.default();
return adapter.adapt(options);
}
}

options.utils.log.warn(
'Could not detect a supported production environment. See https://kit.svelte.dev/docs#adapters to learn how to configure your app to run on the platform of your choosing'
);
}
};
}
33 changes: 33 additions & 0 deletions packages/adapter-auto/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@sveltejs/adapter-auto",
"version": "1.0.0-next",
"repository": {
"type": "git",
"url": "https://github.com/sveltejs/kit",
"directory": "packages/adapter-auto"
},
"homepage": "https://kit.svelte.dev",
"type": "module",
"exports": {
".": {
"import": "./index.js"
},
"./package.json": "./package.json"
},
"main": "index.js",
"types": "index.d.ts",
"files": [
"files",
"index.d.ts"
],
"scripts": {
"lint": "eslint --ignore-path .gitignore \"**/*.{ts,js,svelte}\" && npm run check-format",
"format": "npm run check-format -- --write",
"check-format": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore"
},
"dependencies": {
"@sveltejs/adapter-cloudflare": "workspace:*",
"@sveltejs/adapter-netlify": "workspace:*",
"@sveltejs/adapter-vercel": "workspace:*"
}
}
2 changes: 2 additions & 0 deletions packages/create-svelte/templates/default/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ node_modules
/.svelte-kit
/package
.env
.vercel
.output
6 changes: 2 additions & 4 deletions packages/create-svelte/templates/default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
"start": "svelte-kit start"
},
"devDependencies": {
"@sveltejs/adapter-cloudflare-workers": "next",
"@sveltejs/adapter-netlify": "next",
"@sveltejs/adapter-vercel": "next",
"@sveltejs/kit": "next",
"@sveltejs/adapter-auto": "workspace:*",
"@sveltejs/kit": "workspace:*",
"svelte": "^3.44.0",
"svelte-preprocess": "^4.9.8",
"typescript": "^4.4.3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"preview": "svelte-kit preview"
},
"devDependencies": {
"@sveltejs/adapter-auto": "workspace:*",
"@sveltejs/kit": "workspace:*",
"svelte": "^3.34.0"
},
Expand Down
10 changes: 3 additions & 7 deletions packages/create-svelte/templates/default/svelte.config.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import adapter from '@sveltejs/adapter-auto';
import preprocess from 'svelte-preprocess';

const adapter = process.env.ADAPTER;
const options = JSON.parse(process.env.OPTIONS || '{}');

/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: preprocess(),

kit: {
adapter: adapter(),

// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte'
}
};

if (adapter) {
config.kit.adapter = (await import(adapter)).default(options);
}

export default config;
7 changes: 2 additions & 5 deletions packages/create-svelte/templates/skeleton/svelte.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import node from '@sveltejs/adapter-node';
import adapter from '@sveltejs/adapter-auto';

/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
// By default, `npm run build` will create a standard Node app.
// You can create optimized builds for different platforms by
// specifying a different adapter
adapter: node(),
adapter: adapter(),

// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte'
Expand Down
Loading

0 comments on commit 324e70a

Please sign in to comment.