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

[feat] adapter module #2285

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
cbfd2cf
create proxy for adapters to import from
ignatiusmb Aug 25, 2021
f94fbe8
adapter ambient module
ignatiusmb Aug 25, 2021
11b99fc
adapter node sync
ignatiusmb Aug 25, 2021
8fde219
update constants
ignatiusmb Aug 25, 2021
f9bff84
add to exports
ignatiusmb Aug 25, 2021
09d007c
promisify module export types
ignatiusmb Aug 25, 2021
a76784b
properly type default export
ignatiusmb Aug 25, 2021
17a9bbe
shorter namings
ignatiusmb Aug 25, 2021
409b239
revert some paths
ignatiusmb Aug 25, 2021
09cf6a9
Merge branch 'master' of https://github.com/sveltejs/kit into adapter…
ignatiusmb Aug 25, 2021
6f50f5b
tidy up imports
ignatiusmb Sep 2, 2021
b07e677
@sveltejs/esbuild-plugin-app-resolver
ignatiusmb Sep 2, 2021
f069539
allow importing app output from @sveltejs/kit/app
ignatiusmb Sep 2, 2021
61741f1
Merge branch 'master' of https://github.com/sveltejs/kit into adapter…
ignatiusmb Sep 2, 2021
4abc0a2
resolve merges
ignatiusmb Sep 2, 2021
6cb5be2
use appResolver for adapter-node
ignatiusmb Sep 2, 2021
8fd0c4c
update adapter api docs
ignatiusmb Sep 2, 2021
e0602fc
lockfile
ignatiusmb Sep 2, 2021
e84a688
adapter node tests
ignatiusmb Sep 2, 2021
fa20353
revert constants changes
ignatiusmb Sep 2, 2021
88d541a
remove promise wrapper from types
ignatiusmb Sep 2, 2021
e1af57c
update rest of adapter import paths
ignatiusmb Sep 2, 2021
d44975b
remove accidental type as callback
ignatiusmb Sep 2, 2021
9dff930
some wording
ignatiusmb Sep 3, 2021
9ab01b5
Merge branch 'master' into adapter-module
ignatiusmb Sep 13, 2021
15f6149
fix merge conflict
ignatiusmb Sep 13, 2021
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
14 changes: 8 additions & 6 deletions documentation/docs/80-adapter-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@ export default function (options) {

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:
Within the `adapt` method, an adapter should
ignatiusmb marked this conversation as resolved.
Show resolved Hide resolved

- Clear out the build directory
- Output code that:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I liked having this line because it separates what the adapter itself is doing vs what is being done by the code that it outputs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the separation is nice, but it looks awkward to have nested lists in the site. I don't remember Sapper or Svelte docs with this types of list, not visibly at least, and the styles doesn't look quite right. We can probably achieve better separation with headings and/or numbered list.

- 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`
- Import `{ appResolver }` from `@sveltejs/kit/adapter`
- Pass in `appResolver()` to esbuild plugins for build options
Copy link
Member

@benmccann benmccann Sep 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think all adapters call esbuild, do they? Or at least it seems like it shouldn't be required if you don't need bundling. E.g. there's a ticket where esbuild causes a failure if you're trying to use TypeScript reflection, so perhaps we'd want to make it an option in adapter-node to allow people to turn it off

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any other way to include the ../output/server/app.js other than to have a build step that bundles it, except adapter-static of course. I don't know about adapter-node though, but it's probably the most flexible of all. Maybe @jthegedus have any thoughts on this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adapter-node didn't have bundling for a long time. It was only added in #1648. I have mixed feeling about making it more required because I was thinking we should go the other way and make it optional as mentioned in the above comment

- Import `app` from `@sveltejs/kit/app`
- Calls `app.init()`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These bullets should be "Call", "Convert", and "Globally shim" because you'd read them as a continuation of "Within the adapt method, an adapter should". Leaving off the "s" would also make it consistent with the first bullets which don't have an "s". Though this is a moot point if we add back "Output code that" because in that case it reads better with the "s"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally missed those! Agreed it would read better with the "Output code that" part, it might be better to make this its own section too as it's getting long.

- Converts from the platform's request to a [SvelteKit request](#hooks-handle), calls `app.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`
- 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`.
Expand Down
3 changes: 1 addition & 2 deletions packages/adapter-cloudflare-workers/files/entry.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// TODO hardcoding the relative location makes this brittle
import { init, render } from '../output/server/app.js';
import { init, render } from '@sveltejs/kit/app';
import { getAssetFromKV, NotFoundError } from '@cloudflare/kv-asset-handler';

init();
Expand Down
11 changes: 7 additions & 4 deletions packages/adapter-cloudflare-workers/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import fs from 'fs';
import { execSync } from 'child_process';
import esbuild from 'esbuild';
import toml from '@iarna/toml';
import fs from 'fs';
import { fileURLToPath } from 'url';

import toml from '@iarna/toml';
import { appResolver } from '@sveltejs/kit/adapter';
import esbuild from 'esbuild';

/**
* @typedef {import('esbuild').BuildOptions} BuildOptions
*/
Expand Down Expand Up @@ -40,7 +42,8 @@ export default function (options) {
outfile: `${entrypoint}/index.js`,
bundle: true,
target: 'es2020',
platform: 'browser'
platform: 'browser',
plugins: [appResolver()]
};

const build_options =
Expand Down
3 changes: 1 addition & 2 deletions packages/adapter-netlify/files/entry.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// TODO hardcoding the relative location makes this brittle
import { init, render } from '../output/server/app.js';
import { init, render } from '@sveltejs/kit/app';

init();

Expand Down
15 changes: 9 additions & 6 deletions packages/adapter-netlify/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'fs';
import fs from 'fs';
import { join, resolve } from 'path';
import { fileURLToPath } from 'url';
import esbuild from 'esbuild';

import toml from '@iarna/toml';
import { appResolver } from '@sveltejs/kit/adapter';
import esbuild from 'esbuild';

/**
* @typedef {import('esbuild').BuildOptions} BuildOptions
Expand Down Expand Up @@ -34,6 +36,7 @@ export default function (options) {
outfile: '.netlify/functions-internal/__render.js',
bundle: true,
inject: [join(files, 'shims.js')],
plugins: [appResolver()],
platform: 'node'
};

Expand All @@ -42,7 +45,7 @@ export default function (options) {

await esbuild.build(build_options);

writeFileSync(join('.netlify', 'package.json'), JSON.stringify({ type: 'commonjs' }));
fs.writeFileSync(join('.netlify', 'package.json'), JSON.stringify({ type: 'commonjs' }));

utils.log.minor('Prerendering static pages...');
await utils.prerender({
Expand All @@ -57,20 +60,20 @@ export default function (options) {

const redirectPath = join(publish, '_redirects');
utils.copy('_redirects', redirectPath);
appendFileSync(redirectPath, '\n\n/* /.netlify/functions/__render 200');
fs.appendFileSync(redirectPath, '\n\n/* /.netlify/functions/__render 200');
}
};
}
/**
* @param {import('@sveltejs/kit').AdapterUtils} utils
**/
function get_publish_directory(utils) {
if (existsSync('netlify.toml')) {
if (fs.existsSync('netlify.toml')) {
/** @type {{ build?: { publish?: string }} & toml.JsonMap } */
let netlify_config;

try {
netlify_config = toml.parse(readFileSync('netlify.toml', 'utf-8'));
netlify_config = toml.parse(fs.readFileSync('netlify.toml', 'utf-8'));
} catch (err) {
err.message = `Error parsing netlify.toml: ${err.message}`;
throw err;
Expand Down
48 changes: 22 additions & 26 deletions packages/adapter-node/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import esbuild from 'esbuild';
import {
createReadStream,
createWriteStream,
existsSync,
readFileSync,
statSync,
writeFileSync
} from 'fs';
import fs from 'fs';
import { join } from 'path';
import { pipeline } from 'stream';
import glob from 'tiny-glob';
import { fileURLToPath } from 'url';
import { promisify } from 'util';

import { appResolver } from '@sveltejs/kit/adapter';
import esbuild from 'esbuild';
import glob from 'tiny-glob';
import zlib from 'zlib';

const pipe = promisify(pipeline);
Expand All @@ -25,7 +20,7 @@ export default function ({
out = 'build',
precompress,
env: { path: path_env = 'SOCKET_PATH', host: host_env = 'HOST', port: port_env = 'PORT' } = {},
esbuild: esbuildConfig
esbuild: esbuild_config
} = {}) {
return {
name: '@sveltejs/adapter-node',
Expand All @@ -44,8 +39,8 @@ export default function ({
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',
fs.writeFileSync(
'.svelte-kit/node/env.js', // types pointed in src/env.d.ts
`export const path = process.env[${JSON.stringify(
path_env
)}] || false;\nexport const host = process.env[${JSON.stringify(
Expand All @@ -56,25 +51,27 @@ export default function ({
);

/** @type {BuildOptions} */
const defaultOptions = {
const default_options = {
entryPoints: ['.svelte-kit/node/middlewares.js'],
outfile: join(out, 'middlewares.js'),
bundle: true,
external: Object.keys(JSON.parse(readFileSync('package.json', 'utf8')).dependencies || {}),
external: Object.keys(
JSON.parse(fs.readFileSync('package.json', 'utf8')).dependencies || {}
),
format: 'esm',
platform: 'node',
target: 'node12',
inject: [join(files, 'shims.js')],
plugins: [appResolver()],
define: {
APP_DIR: `"/${config.kit.appDir}/"`
}
};
const buildOptions = esbuildConfig ? await esbuildConfig(defaultOptions) : defaultOptions;
await esbuild.build(buildOptions);
await esbuild.build(esbuild_config ? await esbuild_config(default_options) : default_options);

utils.log.minor('Building SvelteKit reference server');
/** @type {BuildOptions} */
const defaultOptionsRefServer = {
const ref_server_options = {
entryPoints: ['.svelte-kit/node/index.js'],
outfile: join(out, 'index.js'),
bundle: true,
Expand All @@ -93,16 +90,15 @@ export default function ({
}
]
};
const buildOptionsRefServer = esbuildConfig
? await esbuildConfig(defaultOptionsRefServer)
: defaultOptionsRefServer;
await esbuild.build(buildOptionsRefServer);
await esbuild.build(
esbuild_config ? await esbuild_config(ref_server_options) : ref_server_options
);

utils.log.minor('Prerendering static pages');
await utils.prerender({
dest: `${out}/prerendered`
});
if (precompress && existsSync(`${out}/prerendered`)) {
if (precompress && fs.existsSync(`${out}/prerendered`)) {
utils.log.minor('Compressing prerendered pages');
await compress(`${out}/prerendered`);
}
Expand Down Expand Up @@ -137,13 +133,13 @@ async function compress_file(file, format = 'gz') {
params: {
[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
[zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY,
[zlib.constants.BROTLI_PARAM_SIZE_HINT]: statSync(file).size
[zlib.constants.BROTLI_PARAM_SIZE_HINT]: fs.statSync(file).size
}
})
: zlib.createGzip({ level: zlib.constants.Z_BEST_COMPRESSION });

const source = createReadStream(file);
const destination = createWriteStream(`${file}.${format}`);
const source = fs.createReadStream(file);
const destination = fs.createWriteStream(`${file}.${format}`);

await pipe(source, compress, destination);
}
7 changes: 6 additions & 1 deletion packages/adapter-node/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ export default [
sourcemap: true
},
plugins: [nodeResolve(), commonjs(), json()],
external: ['./middlewares.js', './env.js', ...require('module').builtinModules]
external: [
'@sveltejs/kit/app',
'./middlewares.js',
'./env.js',
...require('module').builtinModules
]
},
{
input: 'src/shims.js',
Expand Down
3 changes: 3 additions & 0 deletions packages/adapter-node/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const host: string;
export const path: string;
export const port: number;
1 change: 0 additions & 1 deletion packages/adapter-node/src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-ignore
import { path, host, port } from './env.js';
import { assetsMiddleware, kitMiddleware, prerenderedMiddleware } from './middlewares.js';
import compression from 'compression';
Expand Down
6 changes: 3 additions & 3 deletions packages/adapter-node/src/kit-middleware.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { getRawBody } from '@sveltejs/kit/node';

/**
* @param {import('@sveltejs/kit').App} app
* @return {import('polka').Middleware}
*/
// TODO: type render function from @sveltejs/kit/adapter
// @ts-ignore
export function create_kit_middleware({ render }) {
return async (req, res) => {
const parsed = new URL(req.url || '', 'http://localhost');
Expand All @@ -20,7 +19,8 @@ export function create_kit_middleware({ render }) {

const rendered = await render({
method: req.method,
headers: req.headers, // TODO: what about repeated headers, i.e. string[]
// @ts-expect-error - TODO: what about repeated headers, i.e. string[]
headers: req.headers,
path: parsed.pathname,
query: parsed.searchParams,
rawBody: body
Expand Down
19 changes: 6 additions & 13 deletions packages/adapter-node/src/middlewares.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
// 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';
import { create_kit_middleware } from './kit-middleware.js';

import fs from 'fs';
import { dirname, join } from 'path';
import sirv from 'sirv';
import { fileURLToPath } from 'url';

// App is a dynamic file built from the application layer.
import app from '@sveltejs/kit/app';
import sirv from 'sirv';
import { create_kit_middleware } from './kit-middleware.js';

app.init();

const __dirname = dirname(fileURLToPath(import.meta.url));
/** @type {import('polka').Middleware} */
Expand Down Expand Up @@ -42,7 +38,4 @@ export const assetsMiddleware = fs.existsSync(paths.assets)
})
: noop_handler;

export const kitMiddleware = (function () {
init();
return create_kit_middleware({ render });
})();
export const kitMiddleware = create_kit_middleware(app);
4 changes: 1 addition & 3 deletions packages/adapter-vercel/files/entry.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { init, render } from '@sveltejs/kit/app';
import { getRawBody } from '@sveltejs/kit/node';

// TODO hardcoding the relative location makes this brittle
import { init, render } from '../output/server/app.js';

init();

export default async (req, res) => {
Expand Down
3 changes: 3 additions & 0 deletions packages/adapter-vercel/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { writeFileSync } from 'fs';
import { join } from 'path';
import { fileURLToPath } from 'url';

import { appResolver } from '@sveltejs/kit/adapter';
import esbuild from 'esbuild';

/**
Expand Down Expand Up @@ -37,6 +39,7 @@ export default function (options) {
outfile: join(dirs.lambda, 'index.js'),
bundle: true,
inject: [join(files, 'shims.js')],
plugins: [appResolver()],
platform: 'node'
};

Expand Down
4 changes: 4 additions & 0 deletions packages/kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"amphtml-validator": "^1.0.34",
"cookie": "^0.4.1",
"devalue": "^2.0.1",
"esbuild": "^0.12.5",
"eslint": "^7.25.0",
"kleur": "^4.1.4",
"locate-character": "^2.0.5",
Expand Down Expand Up @@ -66,6 +67,9 @@
},
"exports": {
"./package.json": "./package.json",
"./adapter": {
"import": "./dist/adapter.js"
},
"./ssr": {
"import": "./dist/ssr.js"
},
Expand Down
1 change: 1 addition & 0 deletions packages/kit/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default [
{
input: {
cli: 'src/cli.js',
adapter: 'src/core/adapt/plugins.js',
ssr: 'src/runtime/server/index.js',
node: 'src/core/node/index.js',
hooks: 'src/runtime/hooks.js',
Expand Down
13 changes: 13 additions & 0 deletions packages/kit/src/core/adapt/plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { join } from 'path';

/** @type {import('@sveltejs/kit/adapter').AppResolver} */
export function appResolver() {
return {
name: '@sveltejs/esbuild-plugin-app-resolver',
setup(build) {
build.onResolve({ filter: /@sveltejs\/kit\/app/ }, (args) => ({
path: join(args.resolveDir, '../output/server/app.js')
ignatiusmb marked this conversation as resolved.
Show resolved Hide resolved
}));
}
};
}
6 changes: 4 additions & 2 deletions packages/kit/src/core/adapt/prerender.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
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 { SVELTE_KIT } from '../constants.js';
import { mkdirp } from '../../utils/filesystem.js';
import { get_single_valued_header } from '../../utils/http.js';

import { SVELTE_KIT } from '../constants.js';

/**
* @typedef {import('types/config').PrerenderErrorHandler} PrerenderErrorHandler
* @typedef {import('types/config').PrerenderOnErrorValue} OnError
Expand Down
Loading