diff --git a/.changeset/tidy-pets-tan.md b/.changeset/tidy-pets-tan.md new file mode 100644 index 000000000000..33b391d8cae1 --- /dev/null +++ b/.changeset/tidy-pets-tan.md @@ -0,0 +1,10 @@ +--- +'@sveltejs/adapter-cloudflare': patch +'@sveltejs/adapter-cloudflare-workers': patch +'@sveltejs/adapter-netlify': patch +'@sveltejs/adapter-node': patch +'@sveltejs/adapter-vercel': patch +'@sveltejs/kit': patch +--- + +[breaking] do a single bundling with Vite making esbuild optional diff --git a/documentation/docs/10-adapters.md b/documentation/docs/10-adapters.md index b9386f98f737..a412448cf30b 100644 --- a/documentation/docs/10-adapters.md +++ b/documentation/docs/10-adapters.md @@ -74,6 +74,7 @@ export default function (options) { /** @type {import('@sveltejs/kit').Adapter} */ return { name: 'adapter-package-name', + serverEntryPoint: 'adapter-package-name/entry', async adapt({ utils, config }) { // adapter implementation } diff --git a/documentation/docs/14-configuration.md b/documentation/docs/14-configuration.md index adf55fa9ecd5..9a2564fad5ba 100644 --- a/documentation/docs/14-configuration.md +++ b/documentation/docs/14-configuration.md @@ -47,6 +47,7 @@ const config = { onError: 'fail' }, router: true, + serverEntryPoint: null, serviceWorker: { files: (filepath) => !/\.DS_STORE/.test(filepath) }, @@ -195,6 +196,10 @@ See [Prerendering](#ssr-and-javascript-prerender). An object containing zero or Enables or disables the client-side [router](#ssr-and-javascript-router) app-wide. +### serverEntryPoint + +A file path to a custom entry point for the server. Passed to `vite.build.rollupOptions.input`. See [the Rollup docs](https://rollupjs.org/guide/en/#input) for more info. + ### serviceWorker An object containing zero or more of the following values: diff --git a/documentation/docs/80-adapter-api.md b/documentation/docs/80-adapter-api.md new file mode 100644 index 000000000000..8a8d15184dab --- /dev/null +++ b/documentation/docs/80-adapter-api.md @@ -0,0 +1,38 @@ +--- +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', + serverEntryPoint: 'adapter-package-name/entry', + 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/examples/hn.svelte.dev/svelte.config.js b/examples/hn.svelte.dev/svelte.config.js index 35c263652b6d..93fe72421572 100644 --- a/examples/hn.svelte.dev/svelte.config.js +++ b/examples/hn.svelte.dev/svelte.config.js @@ -1,8 +1,8 @@ -import netlify from '@sveltejs/adapter-netlify'; +import adapter from '@sveltejs/adapter-netlify'; export default { kit: { - adapter: netlify(), + adapter: adapter(), target: '#svelte' } }; diff --git a/packages/adapter-cloudflare-workers/files/entry.js b/packages/adapter-cloudflare-workers/files/entry.js index f7a7e397a620..32fd01edef5a 100644 --- a/packages/adapter-cloudflare-workers/files/entry.js +++ b/packages/adapter-cloudflare-workers/files/entry.js @@ -1,5 +1,6 @@ -// TODO hardcoding the relative location makes this brittle -import { init, render } from '../output/server/app.js'; +// $server-build doesn't exist until the app is built +// @ts-expect-error +import { init, render } from '$server-build'; import { getAssetFromKV, NotFoundError } from '@cloudflare/kv-asset-handler'; init(); diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index 91909812030d..b4cb997ac38c 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -12,6 +12,7 @@ import { fileURLToPath } from 'url'; export default function (options) { return { name: '@sveltejs/adapter-cloudflare-workers', + serverEntryPoint: '@sveltejs/adapter-cloudflare-workers/entry', async adapt({ utils }) { const { site } = validate_config(utils); diff --git a/packages/adapter-cloudflare-workers/package.json b/packages/adapter-cloudflare-workers/package.json index c520f6224456..cf480a99e4e9 100644 --- a/packages/adapter-cloudflare-workers/package.json +++ b/packages/adapter-cloudflare-workers/package.json @@ -12,6 +12,9 @@ ".": { "import": "./index.js" }, + "./entry": { + "import": "./files/entry.js" + }, "./package.json": "./package.json" }, "main": "index.js", diff --git a/packages/adapter-cloudflare/files/worker.js b/packages/adapter-cloudflare/files/worker.js index 8378d9f5add7..4be760cc5d08 100644 --- a/packages/adapter-cloudflare/files/worker.js +++ b/packages/adapter-cloudflare/files/worker.js @@ -1,5 +1,7 @@ /* global ASSETS */ -import { init, render } from '../output/server/app.js'; +// $server-build doesn't exist until the app is built +// @ts-expect-error +import { init, render } from '$server-build'; init(); diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index c0fa58184f96..43fa62975d9e 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -9,6 +9,7 @@ import * as esbuild from 'esbuild'; export default function (options = {}) { return { name: '@sveltejs/adapter-cloudflare', + serverEntryPoint: '@sveltejs/adapter-cloudflare/entry', async adapt({ utils, config }) { const files = fileURLToPath(new URL('./files', import.meta.url)); const target_dir = join(process.cwd(), '.svelte-kit', 'cloudflare'); diff --git a/packages/adapter-cloudflare/package.json b/packages/adapter-cloudflare/package.json index 4aa80bbc648a..aedc5b37467c 100644 --- a/packages/adapter-cloudflare/package.json +++ b/packages/adapter-cloudflare/package.json @@ -12,6 +12,9 @@ ".": { "import": "./index.js" }, + "./entry": { + "import": "./files/worker.js" + }, "./package.json": "./package.json" }, "types": "index.d.ts", diff --git a/packages/adapter-netlify/README.md b/packages/adapter-netlify/README.md index ba5276100a32..5df1de7bc202 100644 --- a/packages/adapter-netlify/README.md +++ b/packages/adapter-netlify/README.md @@ -66,38 +66,6 @@ During compilation a required "catch all" redirect rule is automatically appende node_bundler = "esbuild" ``` -## Advanced Configuration - -### esbuild - -As an escape hatch, you may optionally specify a function which will receive the final esbuild options generated by this adapter and returns a modified esbuild configuration. The result of this function will be passed as-is to esbuild. The function can be async. - -For example, you may wish to add a plugin: - -```js -adapterNetlify({ - esbuild(defaultOptions) { - return { - ...defaultOptions, - plugins: [] - }; - } -}); -``` - -The default options for this version are as follows: - -```js -{ - entryPoints: ['.svelte-kit/netlify/entry.js'], - // This is Netlify's internal functions directory, not the one for user functions. - outfile: '.netlify/functions-internal/__render.js', - bundle: true, - inject: ['pathTo/shims.js'], - platform: 'node' -} -``` - ## Changelog [The Changelog for this package is available on GitHub](https://github.com/sveltejs/kit/blob/master/packages/adapter-netlify/CHANGELOG.md). diff --git a/packages/adapter-netlify/files/entry.js b/packages/adapter-netlify/files/entry.js index ebf337948a90..01925eea4aca 100644 --- a/packages/adapter-netlify/files/entry.js +++ b/packages/adapter-netlify/files/entry.js @@ -1,5 +1,7 @@ -// TODO hardcoding the relative location makes this brittle -import { init, render } from '../output/server/app.js'; +import '@sveltejs/kit/install-fetch'; +// $server-build doesn't exist until the app is built +// @ts-expect-error +import { init, render } from '$server-build'; init(); diff --git a/packages/adapter-netlify/files/shims.js b/packages/adapter-netlify/files/shims.js deleted file mode 100644 index cd9f71d6863c..000000000000 --- a/packages/adapter-netlify/files/shims.js +++ /dev/null @@ -1 +0,0 @@ -export { fetch, Response, Request, Headers } from '@sveltejs/kit/install-fetch'; diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js index 2368d0f2d1cd..dcc3062dbceb 100644 --- a/packages/adapter-netlify/index.js +++ b/packages/adapter-netlify/index.js @@ -1,17 +1,12 @@ -import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'fs'; +import { appendFileSync, existsSync, readFileSync } from 'fs'; import { join, resolve } from 'path'; -import { fileURLToPath } from 'url'; -import esbuild from 'esbuild'; import toml from '@iarna/toml'; -/** - * @typedef {import('esbuild').BuildOptions} BuildOptions - */ - /** @type {import('.')} */ -export default function (options) { +export default function () { return { name: '@sveltejs/adapter-netlify', + serverEntryPoint: '@sveltejs/adapter-netlify/entry', async adapt({ utils }) { // "build" is the default publish directory when Netlify detects SvelteKit @@ -21,28 +16,9 @@ export default function (options) { utils.rimraf(publish); - const files = fileURLToPath(new URL('./files', import.meta.url)); - utils.log.minor('Generating serverless function...'); - utils.copy(join(files, 'entry.js'), '.svelte-kit/netlify/entry.js'); - - /** @type {BuildOptions} */ - const default_options = { - entryPoints: ['.svelte-kit/netlify/entry.js'], - // Any functions in ".netlify/functions-internal" are bundled in addition to user-defined Netlify functions. - // See https://github.com/netlify/build/pull/3213 for more details - outfile: '.netlify/functions-internal/__render.js', - bundle: true, - inject: [join(files, 'shims.js')], - platform: 'node' - }; - - const build_options = - options && options.esbuild ? await options.esbuild(default_options) : default_options; - - await esbuild.build(build_options); - - writeFileSync(join('.netlify', 'package.json'), JSON.stringify({ type: 'commonjs' })); + utils.copy('.svelte-kit/output/server/index.js', '.netlify/functions-internal/__render.js'); + utils.copy('.svelte-kit/output/server/chunks', '.netlify/functions-internal/chunks'); utils.log.minor('Prerendering static pages...'); await utils.prerender({ diff --git a/packages/adapter-netlify/package.json b/packages/adapter-netlify/package.json index 4532986184f1..b058a1554b07 100644 --- a/packages/adapter-netlify/package.json +++ b/packages/adapter-netlify/package.json @@ -12,6 +12,9 @@ ".": { "import": "./index.js" }, + "./entry": { + "import": "./files/entry.js" + }, "./package.json": "./package.json" }, "main": "index.js", @@ -26,8 +29,7 @@ "check-format": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore" }, "dependencies": { - "@iarna/toml": "^2.2.5", - "esbuild": "^0.13.4" + "@iarna/toml": "^2.2.5" }, "devDependencies": { "@sveltejs/kit": "workspace:*" diff --git a/packages/adapter-node/README.md b/packages/adapter-node/README.md index 608f9aed0919..19e1b2784200 100644 --- a/packages/adapter-node/README.md +++ b/packages/adapter-node/README.md @@ -15,21 +15,13 @@ export default { adapter: adapter({ // default options are shown out: 'build', - precompress: false, - env: { - host: 'HOST', - port: 'PORT' - } + precompress: false }) } }; ``` -## Options - -### entryPoint - -The server entry point. Allows you to provide a [custom server implementation](#middleware). Defaults to the provided reference server. +## Build Options ### out @@ -39,7 +31,9 @@ The directory to build the server to. It defaults to `build` — i.e. `node buil Enables precompressing using gzip and brotli for assets and prerendered pages. It defaults to `false`. -### env +## Runtime Options + +### Environment variables By default, the server will accept connections on `0.0.0.0` using port 3000. These can be customised with the `PORT` and `HOST` environment variables: @@ -47,24 +41,13 @@ By default, the server will accept connections on `0.0.0.0` using port 3000. The HOST=127.0.0.1 PORT=4000 node build ``` -You can specify different environment variables if necessary using the `env` option: - -```js -env: { - host: 'MY_HOST_VARIABLE', - port: 'MY_PORT_VARIABLE' -} -``` - -``` -MY_HOST_VARIABLE=127.0.0.1 MY_PORT_VARIABLE=4000 node build -``` +You can also specify `SOCKET_PATH` if you'd like to use a Unix domain socket or Windows named pipe. ## Middleware The adapter exports a middleware `(req, res, next) => {}` that's compatible with [Express](https://github.com/expressjs/expressjs.com) / [Connect](https://github.com/senchalabs/connect) / [Polka](https://github.com/lukeed/polka). Additionally, it also exports a reference server implementation using this middleware with a plain Node HTTP server. -But you can use your favorite server framework to combine it with other middleware and server logic. You can import `kitMiddleware`, your ready-to-use SvelteKit middleware from the `build` directory. You can use [the `entryPoint` option](#entryPoint) to bundle your custom server entry point. +But you can use your favorite server framework to combine it with other middleware and server logic. You can import `kitMiddleware`, your ready-to-use SvelteKit middleware from the `build` directory. You can use [the `serverEntryPoint` option](https://kit.svelte.dev/docs#configuration-serverentrypoint) to bundle your custom server entry point. ```js // src/server.js @@ -94,43 +77,6 @@ app.listen(3000); For using middleware in dev mode, [see the FAQ](https://kit.svelte.dev/faq#how-do-i-use-x-with-sveltekit-how-do-i-use-middleware). -## Advanced Configuration - -### esbuild - -As an escape hatch, you may optionally specify a function which will receive the final esbuild options generated by this adapter and returns a modified esbuild configuration. The result of this function will be passed as-is to esbuild. The function can be async. - -For example, you may wish to add a plugin: - -```js -adapterNode({ - esbuild(defaultOptions) { - return { - ...defaultOptions, - plugins: [] - }; - } -}); -``` - -The default options for this version are as follows: - -```js -{ - entryPoints: ['.svelte-kit/node/index.js'], - outfile: 'pathTo/index.js', - bundle: true, - external: allProductionDependencies, // from package.json - format: 'esm', - platform: 'node', - target: 'node14', - inject: ['pathTo/shims.js'], - define: { - esbuild_app_dir: `"${config.kit.appDir}"` - } -} -``` - ## Deploying You will need the output directory (`build` by default), the project's `package.json`, and the production dependencies in `node_modules` to run the application. Production dependencies can be generated with `npm ci --prod`, you can also skip this step if your app doesn't have any dependencies. You can then start your app with diff --git a/packages/adapter-node/index.js b/packages/adapter-node/index.js index 3d2b258fb6a7..329407967ed0 100644 --- a/packages/adapter-node/index.js +++ b/packages/adapter-node/index.js @@ -1,112 +1,32 @@ -import esbuild from 'esbuild'; -import { - createReadStream, - createWriteStream, - existsSync, - readFileSync, - statSync, - writeFileSync -} from 'fs'; -import { join, resolve } from 'path'; +import { createReadStream, createWriteStream, existsSync, statSync } from 'fs'; +import { join } from 'path'; import { pipeline } from 'stream'; import glob from 'tiny-glob'; -import { fileURLToPath } from 'url'; import { promisify } from 'util'; import zlib from 'zlib'; const pipe = promisify(pipeline); -/** - * @typedef {import('esbuild').BuildOptions} BuildOptions - */ - /** @type {import('.')} */ -export default function ({ - entryPoint = '.svelte-kit/node/index.js', - out = 'build', - precompress, - env: { path: path_env = 'SOCKET_PATH', host: host_env = 'HOST', port: port_env = 'PORT' } = {}, - esbuild: esbuild_config -} = {}) { +export default function ({ out = 'build', precompress } = {}) { return { name: '@sveltejs/adapter-node', + serverEntryPoint: '@sveltejs/adapter-node/entry', - async adapt({ utils, config }) { + async adapt({ utils }) { utils.rimraf(out); utils.log.minor('Copying assets'); const static_directory = join(out, 'assets'); utils.copy_client_files(static_directory); utils.copy_static_files(static_directory); + utils.copy('.svelte-kit/output/server/', out); if (precompress) { utils.log.minor('Compressing assets'); await compress(static_directory); } - utils.log.minor('Building SvelteKit middleware'); - const files = fileURLToPath(new URL('./files', import.meta.url)); - utils.copy(files, '.svelte-kit/node'); - writeFileSync( - '.svelte-kit/node/env.js', - `export const path = process.env[${JSON.stringify( - path_env - )}] || false;\nexport const host = process.env[${JSON.stringify( - host_env - )}] || '0.0.0.0';\nexport const port = process.env[${JSON.stringify( - port_env - )}] || (!path && 3000);` - ); - - /** @type {BuildOptions} */ - const defaultOptions = { - entryPoints: ['.svelte-kit/node/middlewares.js'], - outfile: join(out, 'middlewares.js'), - bundle: true, - external: Object.keys(JSON.parse(readFileSync('package.json', 'utf8')).dependencies || {}), - format: 'esm', - platform: 'node', - target: 'node14', - inject: [join(files, 'shims.js')], - define: { - APP_DIR: `"/${config.kit.appDir}/"` - } - }; - const build_options = esbuild_config ? await esbuild_config(defaultOptions) : defaultOptions; - await esbuild.build(build_options); - - utils.log.minor('Building SvelteKit server'); - /** @type {BuildOptions} */ - const default_options_ref_server = { - entryPoints: [entryPoint], - outfile: join(out, 'index.js'), - bundle: true, - format: 'esm', - platform: 'node', - target: 'node14', - // external exclude workaround, see https://github.com/evanw/esbuild/issues/514 - plugins: [ - { - name: 'fix-middlewares-exclude', - setup(build) { - // Match an import of "middlewares.js" and mark it as external - const internal_middlewares_path = resolve('.svelte-kit/node/middlewares.js'); - const build_middlewares_path = resolve(out, 'middlewares.js'); - build.onResolve({ filter: /\/middlewares\.js$/ }, ({ path, resolveDir }) => { - const resolved = resolve(resolveDir, path); - if (resolved === internal_middlewares_path || resolved === build_middlewares_path) { - return { path: './middlewares.js', external: true }; - } - }); - } - } - ] - }; - const build_options_ref_server = esbuild_config - ? await esbuild_config(default_options_ref_server) - : default_options_ref_server; - await esbuild.build(build_options_ref_server); - utils.log.minor('Prerendering static pages'); await utils.prerender({ dest: `${out}/prerendered` diff --git a/packages/adapter-node/package.json b/packages/adapter-node/package.json index 3ae847e861ce..d621b4fc7c8e 100644 --- a/packages/adapter-node/package.json +++ b/packages/adapter-node/package.json @@ -8,18 +8,20 @@ }, "homepage": "https://kit.svelte.dev", "type": "module", + "main": "index.js", + "types": "index.d.ts", "exports": { ".": { "import": "./index.js" }, + "./entry": { + "import": "./files/index.js" + }, + "./middlewares": { + "import": "./files/middlewares.js" + }, "./package.json": "./package.json" }, - "main": "index.js", - "types": "index.d.ts", - "files": [ - "files", - "index.d.ts" - ], "scripts": { "dev": "rollup -cw", "build": "rollup -c", @@ -30,7 +32,6 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "esbuild": "^0.13.4", "tiny-glob": "^0.2.9" }, "devDependencies": { diff --git a/packages/adapter-node/rollup.config.js b/packages/adapter-node/rollup.config.js index 619756ee6c1e..6e247d4a31e1 100644 --- a/packages/adapter-node/rollup.config.js +++ b/packages/adapter-node/rollup.config.js @@ -11,7 +11,7 @@ export default [ sourcemap: true }, plugins: [nodeResolve(), commonjs(), json()], - external: ['../output/server/app.js', ...require('module').builtinModules] + external: ['$server-build', ...require('module').builtinModules] }, { input: 'src/index.js', @@ -21,14 +21,6 @@ export default [ sourcemap: true }, plugins: [nodeResolve(), commonjs(), json()], - external: ['./middlewares.js', './env.js', ...require('module').builtinModules] - }, - { - input: 'src/shims.js', - output: { - file: 'files/shims.js', - format: 'esm' - }, - external: ['module'] + external: ['./middlewares.js', '$server-build', ...require('module').builtinModules] } ]; diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index b8e807ea8450..fb01c83bf8ca 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -1,21 +1,23 @@ -// @ts-ignore -import { path, host, port } from './env.js'; -import { assetsMiddleware, kitMiddleware, prerenderedMiddleware } from './middlewares.js'; +import '@sveltejs/kit/install-fetch'; +import { assetsMiddleware, kitMiddleware, prerenderedMiddleware } from './middlewares'; import compression from 'compression'; import polka from 'polka'; +// options +const path = process.env['SOCKET_PATH'] || false; +const host = process.env['HOST'] || '0.0.0.0'; +const port = process.env['PORT'] || (!path && 3000); + const server = polka().use( // https://github.com/lukeed/polka/issues/173 - // @ts-ignore - nothing we can do about so just ignore it + // @ts-expect-error - nothing we can do about so just ignore it compression({ threshold: 0 }), assetsMiddleware, kitMiddleware, prerenderedMiddleware ); -const listenOpts = { path, host, port }; - -server.listen(listenOpts, () => { +server.listen({ path, host, port }, () => { console.log(`Listening on ${path ? path : host + ':' + port}`); }); diff --git a/packages/adapter-node/src/middlewares.js b/packages/adapter-node/src/middlewares.js index ba4ee030244d..7641e7623729 100644 --- a/packages/adapter-node/src/middlewares.js +++ b/packages/adapter-node/src/middlewares.js @@ -1,8 +1,6 @@ -// TODO hardcoding the relative location makes this brittle -// Also, we need most of the logic in another file for testing because -// ../output/server/app.js doesn't exist when we run the tests -// @ts-ignore -import { init, render } from '../output/server/app.js'; +// $server-build doesn't exist until the app is built +// @ts-expect-error +import { init, render } from '$server-build'; import { create_kit_middleware } from './kit-middleware.js'; import fs from 'fs'; diff --git a/packages/adapter-node/src/shims.js b/packages/adapter-node/src/shims.js deleted file mode 100644 index 839b2dd5b108..000000000000 --- a/packages/adapter-node/src/shims.js +++ /dev/null @@ -1,9 +0,0 @@ -import { createRequire } from 'module'; -export { fetch, Response, Request, Headers } from '@sveltejs/kit/install-fetch'; - -// esbuild automatically renames "require" -// So we still have to use Object.defineProperty here -Object.defineProperty(globalThis, 'require', { - enumerable: true, - value: createRequire(import.meta.url) -}); diff --git a/packages/adapter-vercel/files/entry.js b/packages/adapter-vercel/files/entry.js index 35d6e3170a7c..b1b50f568ea9 100644 --- a/packages/adapter-vercel/files/entry.js +++ b/packages/adapter-vercel/files/entry.js @@ -1,7 +1,8 @@ -import { getRawBody } from '@sveltejs/kit/node'; +// $server-build doesn't exist until the app is built +// @ts-expect-error +import { init, render } from '$server-build'; -// TODO hardcoding the relative location makes this brittle -import { init, render } from '../output/server/app.js'; +import { getRawBody } from '@sveltejs/kit/node'; init(); diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index d5c53689f143..d283b8f869ae 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -11,6 +11,7 @@ import esbuild from 'esbuild'; export default function (options) { return { name: '@sveltejs/adapter-vercel', + serverEntryPoint: '@sveltejs/adapter-vercel/entry', async adapt({ utils }) { const dir = '.vercel_build_output'; diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json index 652b600d8adc..8561a90c890a 100644 --- a/packages/adapter-vercel/package.json +++ b/packages/adapter-vercel/package.json @@ -12,6 +12,9 @@ ".": { "import": "./index.js" }, + "./entry": { + "import": "./files/entry.js" + }, "./package.json": "./package.json" }, "main": "index.js", diff --git a/packages/kit/src/core/adapt/prerender.js b/packages/kit/src/core/adapt/prerender.js index 00458476c342..4ff6a7d9fab8 100644 --- a/packages/kit/src/core/adapt/prerender.js +++ b/packages/kit/src/core/adapt/prerender.js @@ -2,7 +2,7 @@ import { readFileSync, writeFileSync } from 'fs'; import { dirname, join, resolve as resolve_path } from 'path'; import { pathToFileURL, resolve, URL } from 'url'; import { mkdirp } from '../../utils/filesystem.js'; -import { __fetch_polyfill } from '../../install-fetch.js'; +import '../../install-fetch.js'; import { SVELTE_KIT } from '../constants.js'; import { get_single_valued_header } from '../../utils/http.js'; @@ -99,8 +99,6 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a return []; } - __fetch_polyfill(); - const dir = resolve_path(cwd, `${SVELTE_KIT}/output`); const seen = new Set(); diff --git a/packages/kit/src/core/build/index.js b/packages/kit/src/core/build/index.js index 666a2c42362c..7767fad71cc5 100644 --- a/packages/kit/src/core/build/index.js +++ b/packages/kit/src/core/build/index.js @@ -1,13 +1,11 @@ +import { svelte } from '@sveltejs/vite-plugin-svelte'; import fs from 'fs'; import path from 'path'; - -import { svelte } from '@sveltejs/vite-plugin-svelte'; import glob from 'tiny-glob/sync.js'; import vite from 'vite'; import { rimraf } from '../../utils/filesystem.js'; import { deep_merge } from '../../utils/object.js'; - import { print_config_conflicts } from '../config/index.js'; import { create_app } from '../create_app/index.js'; import create_manifest_data from '../create_manifest_data/index.js'; @@ -26,7 +24,11 @@ const s = (value) => JSON.stringify(value); * @returns {Promise} */ export async function build(config, { cwd = process.cwd(), runtime = '@sveltejs/kit/ssr' } = {}) { - const build_dir = path.resolve(cwd, `${SVELTE_KIT}/build`); + // TODO: build and output are confusing names because they're synonyms + // rename build to intermediate or generated or something clearer + + const raw_build_dir = `${SVELTE_KIT}/build`; + const build_dir = path.resolve(cwd, raw_build_dir); rimraf(build_dir); @@ -47,7 +49,7 @@ export async function build(config, { cwd = process.cwd(), runtime = '@sveltejs/ cwd }), output_dir, - client_entry_file: `${SVELTE_KIT}/build/runtime/internal/start.js`, + client_entry_file: `${raw_build_dir}/runtime/internal/start.js`, service_worker_entry_file: resolve_entry(config.kit.files.serviceWorker) }; @@ -219,12 +221,10 @@ async function build_server( ) { let hooks_file = resolve_entry(config.kit.files.hooks); if (!hooks_file || !fs.existsSync(hooks_file)) { - hooks_file = path.resolve(cwd, `${SVELTE_KIT}/build/hooks.js`); + hooks_file = path.resolve(build_dir, 'hooks.js'); fs.writeFileSync(hooks_file, ''); } - const app_file = `${build_dir}/app.js`; - /** @type {(file: string) => string} */ const app_relative = (file) => { const relative_file = path.relative(build_dir, path.resolve(cwd, file)); @@ -287,6 +287,8 @@ async function build_server( find_deps(client_entry_file, entry_js, entry_css); + const app_file = `${build_dir}/app.js`; + // prettier-ignore fs.writeFileSync( app_file, @@ -441,6 +443,7 @@ async function build_server( build: { target: 'es2020' }, + // TODO: remove after https://github.com/vitejs/vite/pull/5341 is merged and released server: { fs: { strict: true @@ -451,6 +454,17 @@ async function build_server( // don't warn on overriding defaults const [modified_vite_config] = deep_merge(default_config, vite_config); + const serverEntryPoint = config.kit?.serverEntryPoint ?? config.kit?.adapter?.serverEntryPoint; + /** + * @type {{ app:string, index?:string }} + */ + const input = { + app: app_file + }; + if (serverEntryPoint) { + input.index = serverEntryPoint; + } + /** @type {[any, string[]]} */ const [merged_config, conflicts] = deep_merge(modified_vite_config, { configFile: false, @@ -461,9 +475,7 @@ async function build_server( outDir: `${output_dir}/server`, polyfillDynamicImport: false, rollupOptions: { - input: { - app: app_file - }, + input, output: { format: 'esm', entryFileNames: '[name].js', @@ -481,8 +493,12 @@ async function build_server( } }) ], + define: { + APP_DIR: `"/${config.kit.appDir}/"` + }, resolve: { alias: { + '$server-build': path.resolve(`${build_dir}/app.js`), $app: path.resolve(`${build_dir}/runtime/app`), $lib: config.kit.files.lib } diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js index 2707b6133c2d..4fd1f113f10b 100644 --- a/packages/kit/src/core/config/index.spec.js +++ b/packages/kit/src/core/config/index.spec.js @@ -50,6 +50,7 @@ test('fills in defaults', () => { pages: undefined }, router: true, + serverEntryPoint: null, ssr: true, target: null, trailingSlash: 'never' @@ -150,6 +151,7 @@ test('fills in partial blanks', () => { pages: undefined }, router: true, + serverEntryPoint: null, ssr: true, target: null, trailingSlash: 'never' diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js index 1a76bb14e113..6510c0ea8159 100644 --- a/packages/kit/src/core/config/options.js +++ b/packages/kit/src/core/config/options.js @@ -161,6 +161,8 @@ const options = object( router: boolean(true), + serverEntryPoint: string(null), + serviceWorker: object({ files: fun((filename) => !/\.DS_STORE/.test(filename)) }), diff --git a/packages/kit/src/core/config/test/index.js b/packages/kit/src/core/config/test/index.js index 57d966c14909..4e76a987ae67 100644 --- a/packages/kit/src/core/config/test/index.js +++ b/packages/kit/src/core/config/test/index.js @@ -52,6 +52,7 @@ async function testLoadDefaultConfig(path) { pages: undefined }, router: true, + serverEntryPoint: null, ssr: true, target: null, trailingSlash: 'never' diff --git a/packages/kit/src/core/dev/index.js b/packages/kit/src/core/dev/index.js index 11e247c3b1fb..411806db1c9f 100644 --- a/packages/kit/src/core/dev/index.js +++ b/packages/kit/src/core/dev/index.js @@ -12,7 +12,7 @@ import vite from 'vite'; import { respond } from '../../runtime/server/index.js'; import { rimraf } from '../../utils/filesystem.js'; import { deep_merge } from '../../utils/object.js'; -import { __fetch_polyfill } from '../../install-fetch.js'; +import '../../install-fetch.js'; import { print_config_conflicts } from '../config/index.js'; import { create_app } from '../create_app/index.js'; @@ -27,8 +27,6 @@ import { coalesce_to_error } from '../../utils/error.js'; /** @param {Options} opts */ export function dev(opts) { - __fetch_polyfill(); - return new Watcher(opts).init(); } diff --git a/packages/kit/src/core/preview/index.js b/packages/kit/src/core/preview/index.js index 5b1568ecc78b..3e6d320520a5 100644 --- a/packages/kit/src/core/preview/index.js +++ b/packages/kit/src/core/preview/index.js @@ -5,7 +5,7 @@ import { join, resolve } from 'path'; import sirv from 'sirv'; import { pathToFileURL } from 'url'; import { getRawBody } from '../node/index.js'; -import { __fetch_polyfill } from '../../install-fetch.js'; +import '../../install-fetch.js'; import { SVELTE_KIT, SVELTE_KIT_ASSETS } from '../constants.js'; /** @param {string} dir */ @@ -31,8 +31,6 @@ export async function preview({ https: use_https = false, cwd = process.cwd() }) { - __fetch_polyfill(); - const app_file = resolve(cwd, `${SVELTE_KIT}/output/server/app.js`); /** @type {import('types/internal').App} */ diff --git a/packages/kit/src/install-fetch.js b/packages/kit/src/install-fetch.js index 629e38884996..83a9b55fe29e 100644 --- a/packages/kit/src/install-fetch.js +++ b/packages/kit/src/install-fetch.js @@ -1,26 +1,20 @@ import fetch, { Response, Request, Headers } from 'node-fetch'; -// exported for dev, prerender, and preview -export function __fetch_polyfill() { - Object.defineProperties(globalThis, { - fetch: { - enumerable: true, - value: fetch - }, - Response: { - enumerable: true, - value: Response - }, - Request: { - enumerable: true, - value: Request - }, - Headers: { - enumerable: true, - value: Headers - } - }); -} - -// exported for esbuild shims in adapters -export { fetch, Response, Request, Headers }; +Object.defineProperties(globalThis, { + fetch: { + enumerable: true, + value: fetch + }, + Response: { + enumerable: true, + value: Response + }, + Request: { + enumerable: true, + value: Request + }, + Headers: { + enumerable: true, + value: Headers + } +}); diff --git a/packages/kit/src/runtime/app/navigation.js b/packages/kit/src/runtime/app/navigation.js index 0d32fca7006c..e1fa5567e730 100644 --- a/packages/kit/src/runtime/app/navigation.js +++ b/packages/kit/src/runtime/app/navigation.js @@ -1,8 +1,5 @@ -import { router as router_ } from '../client/singletons.js'; import { get_base_uri } from '../client/utils.js'; -const router = /** @type {import('../client/router').Router} */ (router_); - /** * @param {string} name */ @@ -17,11 +14,17 @@ export const invalidate = import.meta.env.SSR ? guard('invalidate') : invalidate export const prefetch = import.meta.env.SSR ? guard('prefetch') : prefetch_; export const prefetchRoutes = import.meta.env.SSR ? guard('prefetchRoutes') : prefetchRoutes_; +async function get_router() { + return /** @type {import('../client/router').Router} */ ( + (await import('../client/singletons.js')).router + ); +} + /** * @type {import('$app/navigation').goto} */ async function goto_(href, opts) { - return router.goto(href, opts, []); + return (await get_router()).goto(href, opts, []); } /** @@ -29,20 +32,21 @@ async function goto_(href, opts) { */ async function invalidate_(resource) { const { href } = new URL(resource, location.href); - return router.renderer.invalidate(href); + return (await get_router()).renderer.invalidate(href); } /** * @type {import('$app/navigation').prefetch} */ -function prefetch_(href) { - return router.prefetch(new URL(href, get_base_uri(document))); +async function prefetch_(href) { + return (await get_router()).prefetch(new URL(href, get_base_uri(document))); } /** * @type {import('$app/navigation').prefetchRoutes} */ async function prefetchRoutes_(pathnames) { + const router = await get_router(); const matching = pathnames ? router.routes.filter((route) => pathnames.some((pathname) => route[0].test(pathname))) : router.routes; diff --git a/packages/kit/types/config.d.ts b/packages/kit/types/config.d.ts index 5d2214d0eacb..11b65e17a4d0 100644 --- a/packages/kit/types/config.d.ts +++ b/packages/kit/types/config.d.ts @@ -22,8 +22,8 @@ export interface AdapterUtils { */ copy_static_files(dest: string): string[]; /** - * @param from the source folder from which files should be copied - * @param to the destination folder to which files should be copied + * @param from the source file or folder from which files should be copied + * @param to the destination file or folder to which files should be copied * @returns an array of paths corresponding to the files that have been created by the copy */ copy(from: string, to: string, filter?: (basename: string) => boolean): string[]; @@ -32,6 +32,7 @@ export interface AdapterUtils { export interface Adapter { name: string; + serverEntryPoint?: string; adapt(context: { utils: AdapterUtils; config: ValidatedConfig }): Promise; } @@ -82,6 +83,7 @@ export interface Config { onError?: PrerenderOnErrorValue; }; router?: boolean; + serverEntryPoint?: string; serviceWorker?: { files?(filepath: string): boolean; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 801d2467ca67..2297efd76a2f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -100,10 +100,8 @@ importers: specifiers: '@iarna/toml': ^2.2.5 '@sveltejs/kit': workspace:* - esbuild: ^0.13.4 dependencies: '@iarna/toml': 2.2.5 - esbuild: 0.13.12 devDependencies: '@sveltejs/kit': link:../kit @@ -114,7 +112,6 @@ importers: '@types/compression': ^1.7.2 c8: ^7.10.0 compression: ^1.7.4 - esbuild: ^0.13.4 node-fetch: ^3.0.0 polka: ^1.0.0-next.22 rollup: ^2.58.0 @@ -122,7 +119,6 @@ importers: tiny-glob: ^0.2.9 uvu: ^0.5.2 dependencies: - esbuild: 0.13.12 tiny-glob: 0.2.9 devDependencies: '@rollup/plugin-json': 4.1.0_rollup@2.58.3