diff --git a/.changeset/popular-schools-guess.md b/.changeset/popular-schools-guess.md new file mode 100644 index 000000000000..9f2146745d2f --- /dev/null +++ b/.changeset/popular-schools-guess.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-auto': patch +'create-svelte': patch +--- + +Add adapter-auto diff --git a/documentation/docs/10-adapters.md b/documentation/docs/10-adapters.md index bb522e39369f..b9386f98f737 100644 --- a/documentation/docs/10-adapters.md +++ b/documentation/docs/10-adapters.md @@ -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. diff --git a/documentation/docs/80-adapter-api.md b/documentation/docs/80-adapter-api.md deleted file mode 100644 index 7094e1ff152c..000000000000 --- a/documentation/docs/80-adapter-api.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: Writing an Adapter ---- - -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). - -Within the `adapt` method, there are a number of things that an adapter should do: - -- Clear out the build directory -- Output code that: - - Calls `init` - - Converts from the platform's request to a [SvelteKit request](#hooks-handle), calls `render`, and converts from a [SvelteKit response](#hooks-handle) to the platform's - - Globally shims `fetch` to work on the target platform. 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 - -If possible, we recommend putting the adapter output under the `build/` directory with any intermediate output placed under `'.svelte-kit/' + adapterName`. - -> The adapter API may change before 1.0. diff --git a/packages/adapter-auto/.gitignore b/packages/adapter-auto/.gitignore new file mode 100644 index 000000000000..9daa8247da45 --- /dev/null +++ b/packages/adapter-auto/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +node_modules diff --git a/packages/adapter-auto/README.md b/packages/adapter-auto/README.md new file mode 100644 index 000000000000..b79d49a13f1f --- /dev/null +++ b/packages/adapter-auto/README.md @@ -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). diff --git a/packages/adapter-auto/adapters.js b/packages/adapter-auto/adapters.js new file mode 100644 index 000000000000..44cbf9f4f68a --- /dev/null +++ b/packages/adapter-auto/adapters.js @@ -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' + } +]; diff --git a/packages/adapter-auto/index.d.ts b/packages/adapter-auto/index.d.ts new file mode 100644 index 000000000000..62aee0f1eaa9 --- /dev/null +++ b/packages/adapter-auto/index.d.ts @@ -0,0 +1,4 @@ +import { Adapter } from '@sveltejs/kit'; + +declare function plugin(): Adapter; +export = plugin; diff --git a/packages/adapter-auto/index.js b/packages/adapter-auto/index.js new file mode 100644 index 000000000000..e60b1ab4aa9e --- /dev/null +++ b/packages/adapter-auto/index.js @@ -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' + ); + } + }; +} diff --git a/packages/adapter-auto/package.json b/packages/adapter-auto/package.json new file mode 100644 index 000000000000..d143b23b8c77 --- /dev/null +++ b/packages/adapter-auto/package.json @@ -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:*" + } +} diff --git a/packages/create-svelte/templates/default/.gitignore b/packages/create-svelte/templates/default/.gitignore index 75631d294e31..28426480040a 100644 --- a/packages/create-svelte/templates/default/.gitignore +++ b/packages/create-svelte/templates/default/.gitignore @@ -4,3 +4,5 @@ node_modules /.svelte-kit /package .env +.vercel +.output diff --git a/packages/create-svelte/templates/default/package.json b/packages/create-svelte/templates/default/package.json index aa1bcb0ff124..a83bc7d46b44 100644 --- a/packages/create-svelte/templates/default/package.json +++ b/packages/create-svelte/templates/default/package.json @@ -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" diff --git a/packages/create-svelte/templates/default/package.template.json b/packages/create-svelte/templates/default/package.template.json index 821b9019524f..242ce530d433 100644 --- a/packages/create-svelte/templates/default/package.template.json +++ b/packages/create-svelte/templates/default/package.template.json @@ -8,6 +8,7 @@ "preview": "svelte-kit preview" }, "devDependencies": { + "@sveltejs/adapter-auto": "workspace:*", "@sveltejs/kit": "workspace:*", "svelte": "^3.34.0" }, diff --git a/packages/create-svelte/templates/default/svelte.config.js b/packages/create-svelte/templates/default/svelte.config.js index 3315cb538d64..aa85d10b3262 100644 --- a/packages/create-svelte/templates/default/svelte.config.js +++ b/packages/create-svelte/templates/default/svelte.config.js @@ -1,8 +1,6 @@ +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 @@ -10,13 +8,11 @@ const config = { preprocess: preprocess(), kit: { + adapter: adapter(), + // hydrate the
element in src/app.html target: '#svelte' } }; -if (adapter) { - config.kit.adapter = (await import(adapter)).default(options); -} - export default config; diff --git a/packages/create-svelte/templates/skeleton/svelte.config.js b/packages/create-svelte/templates/skeleton/svelte.config.js index 3406f45bac3f..6ccba57115f1 100644 --- a/packages/create-svelte/templates/skeleton/svelte.config.js +++ b/packages/create-svelte/templates/skeleton/svelte.config.js @@ -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
element in src/app.html target: '#svelte' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68030efd45f6..1e56bf9ae68b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,7 +30,7 @@ importers: '@sveltejs/eslint-config': github.com/sveltejs/eslint-config/9a7d728e03ac433e5856a6e06775c17ee986d641_fd31dfc62358d49d740c9b633be977a9 '@typescript-eslint/eslint-plugin': 4.33.0_cc617358c89d3f38c52462f6d809db4c '@typescript-eslint/parser': 4.33.0_eslint@7.32.0+typescript@4.4.4 - action-deploy-docs: github.com/sveltejs/action-deploy-docs/321fe3aec0f4484eaa42e32283025106161cc296 + action-deploy-docs: github.com/sveltejs/action-deploy-docs/773baf07ba9391eee581dcecfb672265e5fdbddb dotenv: 10.0.0 eslint: 7.32.0 eslint-plugin-import: 2.25.2_eslint@7.32.0 @@ -56,6 +56,16 @@ importers: '@sveltejs/kit': link:../../packages/kit svelte: 3.44.0 + packages/adapter-auto: + specifiers: + '@sveltejs/adapter-cloudflare': workspace:* + '@sveltejs/adapter-netlify': workspace:* + '@sveltejs/adapter-vercel': workspace:* + dependencies: + '@sveltejs/adapter-cloudflare': link:../adapter-cloudflare + '@sveltejs/adapter-netlify': link:../adapter-netlify + '@sveltejs/adapter-vercel': link:../adapter-vercel + packages/adapter-begin: specifiers: '@sveltejs/kit': workspace:* @@ -186,10 +196,8 @@ importers: specifiers: '@fontsource/fira-mono': ^4.5.0 '@lukeed/uuid': ^2.0.0 - '@sveltejs/adapter-cloudflare-workers': next - '@sveltejs/adapter-netlify': next - '@sveltejs/adapter-vercel': next - '@sveltejs/kit': next + '@sveltejs/adapter-auto': workspace:* + '@sveltejs/kit': workspace:* cookie: ^0.4.1 svelte: ^3.44.0 svelte-preprocess: ^4.9.8 @@ -199,9 +207,7 @@ importers: '@lukeed/uuid': 2.0.0 cookie: 0.4.1 devDependencies: - '@sveltejs/adapter-cloudflare-workers': link:../../../adapter-cloudflare-workers - '@sveltejs/adapter-netlify': link:../../../adapter-netlify - '@sveltejs/adapter-vercel': link:../../../adapter-vercel + '@sveltejs/adapter-auto': link:../../../adapter-auto '@sveltejs/kit': link:../../../kit svelte: 3.44.0 svelte-preprocess: 4.9.8_svelte@3.44.0+typescript@4.4.4 @@ -4382,8 +4388,8 @@ packages: engines: {node: '>=10'} dev: true - github.com/sveltejs/action-deploy-docs/321fe3aec0f4484eaa42e32283025106161cc296: - resolution: {tarball: https://codeload.github.com/sveltejs/action-deploy-docs/tar.gz/321fe3aec0f4484eaa42e32283025106161cc296} + github.com/sveltejs/action-deploy-docs/773baf07ba9391eee581dcecfb672265e5fdbddb: + resolution: {tarball: https://codeload.github.com/sveltejs/action-deploy-docs/tar.gz/773baf07ba9391eee581dcecfb672265e5fdbddb} name: action-deploy-docs version: 1.0.0 hasBin: true