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

Allow passing platform context from adapters #3429

Merged
merged 45 commits into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
d574cb6
Can pass locals at app.render
fezproof Jan 20, 2022
33d4737
Updated cloudflare workers to pass in env to locals
fezproof Jan 20, 2022
56bfe37
Fixed other adapters for new API
fezproof Jan 20, 2022
8309cd7
Fixed naming in app types to be more consistent
fezproof Jan 20, 2022
bb67019
Merge branch 'master' into locals-from-adapter
Rich-Harris Jan 20, 2022
9f95150
Updated to use meta in stead of polluting locals
fezproof Jan 21, 2022
4f4247c
updated adapter cloudflare to use meta
fezproof Jan 21, 2022
fd9a6f5
Added changesets
fezproof Jan 21, 2022
df36652
Formated and linted
fezproof Jan 21, 2022
6d34a4c
Updated types to include Meta consistently
fezproof Jan 21, 2022
2d887a3
Updated documentation to include information about the meta object
fezproof Jan 21, 2022
bcd3c8d
Fixed type issue in hooks
fezproof Jan 21, 2022
36b139a
Merge branch 'master' of https://github.com/sveltejs/kit into locals-…
fezproof Jan 21, 2022
eb977ea
Updated naming to platform
fezproof Jan 21, 2022
a9d3fa9
Added a way to get the original request from the node adapter
fezproof Jan 21, 2022
8745a71
Made platform Readonly in RequestEvent
fezproof Jan 21, 2022
40f8cd7
Fixed typo in docs
fezproof Jan 21, 2022
201e595
Update documentation/docs/01-routing.md
fezproof Jan 22, 2022
0bce46d
Fixed bad spaces
fezproof Jan 22, 2022
e95e1b3
Merge branch 'master' into locals-from-adapter
fezproof Jan 22, 2022
741eda4
Fixed merge issues
fezproof Jan 22, 2022
19d5039
Merge branch 'master' into locals-from-adapter
ignatiusmb Jan 25, 2022
2f6cb31
rerun flakey tests
fezproof Jan 25, 2022
c7b10a1
rerun flakey tests 2
fezproof Jan 25, 2022
e4a1047
Update documentation/docs/10-adapters.md grammer
fezproof Jan 25, 2022
d91beaf
rerun flakey tests 3
fezproof Jan 25, 2022
22c2e6f
Merge branch 'locals-from-adapter' of https://github.com/fezproof/kit…
fezproof Jan 25, 2022
492809d
rerun flakey tests 4
fezproof Jan 25, 2022
e4c7be7
Changed node handler to pass in `req`
fezproof Jan 26, 2022
bb2ddaa
Updated cloudflare readme
fezproof Jan 26, 2022
e5fd336
Renamed to request
fezproof Jan 26, 2022
4e96cf1
Use Record<string, any>
fezproof Jan 26, 2022
9fc934b
rerun flakey tests 5
fezproof Jan 26, 2022
72459fe
change API to app.render(request, opts)
Rich-Harris Jan 27, 2022
54de5bf
update adapters
Rich-Harris Jan 27, 2022
d417d00
use platform.req instead of platform.request — more idiomatic
Rich-Harris Jan 27, 2022
e68d469
remove changesets for unaffected adapters
Rich-Harris Jan 27, 2022
358c747
tweak changesets
Rich-Harris Jan 27, 2022
2a4b54b
remove platform stuff from routing section
Rich-Harris Jan 27, 2022
9d57b6c
tweak adapter docs
Rich-Harris Jan 27, 2022
a3121b8
Merge branch 'master' into locals-from-adapter
Rich-Harris Jan 27, 2022
6e90095
Fix documentation/docs/04-hooks.md spaces
fezproof Jan 27, 2022
9ca9144
remove changes to adapter-node
Rich-Harris Jan 27, 2022
5d3c3ae
create-svelte is unchanged
Rich-Harris Jan 27, 2022
983ec4f
simplify docs
Rich-Harris Jan 27, 2022
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
10 changes: 10 additions & 0 deletions .changeset/cyan-numbers-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@sveltejs/adapter-auto': patch
'@sveltejs/adapter-cloudflare': patch
'@sveltejs/adapter-cloudflare-workers': patch
'@sveltejs/adapter-netlify': patch
'@sveltejs/adapter-node': patch
'@sveltejs/adapter-vercel': patch
---

Updated adapters to work with new `app.render` API
6 changes: 6 additions & 0 deletions .changeset/many-mayflies-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'create-svelte': patch
'@sveltejs/kit': patch
---

Adapter render API updated to allow `meta` value
16 changes: 13 additions & 3 deletions documentation/docs/01-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ Endpoints are modules written in `.js` (or `.ts`) files that export functions co
// Declaration types for Endpoints
// * declarations that are not exported are for internal use

export interface RequestEvent<Locals = Record<string, any>> {
export interface RequestEvent<Locals = Record<string, any>, Platform = Record<string, unknown>> {
fezproof marked this conversation as resolved.
Show resolved Hide resolved
request: Request;
url: URL;
params: Record<string, string>;
locals: Locals;
platform: Platform;
}

type Body = JSONString | Uint8Array | ReadableStream | stream.Readable;
Expand All @@ -69,8 +70,13 @@ type MaybePromise<T> = T | Promise<T>;
interface Fallthrough {
fallthrough: true;
}
export interface RequestHandler<Locals = Record<string, any>, Output extends Body = Body> {
(event: RequestEvent<Locals>): MaybePromise<

export interface RequestHandler<
Locals = Record<string, any>,
Platform = Record<string, unknown>,
Output extends Body = Body
> {
(event: RequestEvent<Locals, Platform>): MaybePromise<
Either<Response | EndpointOutput<Output>, Fallthrough>
>;
}
Expand Down Expand Up @@ -194,6 +200,10 @@ A route can have multiple dynamic parameters, for example `src/routes/[category]

> `src/routes/a/[...rest]/z.svelte` will match `/a/z` as well as `/a/b/z` and `/a/b/c/z` and so on. Make sure you check that the value of the rest parameter is valid.

#### Platform object

Some adapters may pass in some extra context when calling your application. They do this by setting the `platform` object, that can be accessed in both route handlers and hooks ([detailed later](#hooks)). It is up to the adapter to decide what is passed in.

#### Fallthrough routes

Finally, if you have multiple routes that match a given path, SvelteKit will try each of them until one responds. For example if you have these routes...
Expand Down
21 changes: 13 additions & 8 deletions documentation/docs/04-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,22 @@ If unimplemented, defaults to `({ event, resolve }) => resolve(event)`.
// everything else must be a type of string
type ResponseHeaders = Record<string, string | string[]>;

export interface RequestEvent<Locals = Record<string, any>> {
export interface RequestEvent<Locals = Record<string, any>, Platform = Record<string, unknown>> {
request: Request;
url: URL;
params: Record<string, string>;
locals: Locals;
platform: Platform;
}

export interface ResolveOpts {
ssr?: boolean;
}

export interface Handle<Locals = Record<string, any>> {
export interface Handle<Locals = Record<string, any>, Platform = Record<string, unknown>> {
(input: {
event: RequestEvent<Locals>;
resolve(event: RequestEvent<Locals>, opts?: ResolveOpts): MaybePromise<Response>;
event: RequestEvent<Locals, Platform>;
resolve(event: RequestEvent<Locals, Platform>, opts?: ResolveOpts): MaybePromise<Response>;
}): MaybePromise<Response>;
}
```
Expand Down Expand Up @@ -84,8 +85,8 @@ If unimplemented, SvelteKit will log the error with default formatting.

```ts
// Declaration types for handleError hook
export interface HandleError<Locals = Record<string, any>> {
(input: { error: Error & { frame?: string }; event: RequestEvent<Locals> }): void;
export interface HandleError<Locals = Record<string, any>, Platform = Record<string, unknown>> {
(input: { error: Error & { frame?: string }; event: RequestEvent<Locals, Platform> }): void;
}
```

Expand All @@ -107,8 +108,12 @@ If unimplemented, session is `{}`.

```ts
// Declaration types for getSession hook
export interface GetSession<Locals = Record<string, any>, Session = any> {
(event: RequestEvent<Locals>): Session | Promise<Session>;
export interface GetSession<
Locals = Record<string, any>,
Platform = Record<string, unknown>,
Session = any
> {
(event: RequestEvent<Locals, Platform>): MaybePromise<Session>;
}
```

Expand Down
1 change: 1 addition & 0 deletions documentation/docs/10-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Within the `adapt` method, there are a number of things that an adapter should d
- Imports `App` from `${builder.getServerDirectory()}/app.js`
- Instantiates the app with a manifest generated with `builder.generateManifest({ relativePath })`
- Listens for requests from the platform, converts them to a standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) if necessary, calls the `render` function to generate a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) and responds with it
- `render` optionally accepts a `platform` object, which allow passing of environment specific values and functions. The `platform` object will become be accessable from the handle hook, and any API endpoints in the application
fezproof marked this conversation as resolved.
Show resolved Hide resolved
- 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 necessary
- Put the user's static files and the generated JS/CSS in the correct location for the target platform
Expand Down
2 changes: 1 addition & 1 deletion packages/adapter-cloudflare-workers/files/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async function handle(event) {

// dynamically-generated pages
try {
return await app.render(request);
return await app.render({ request });
} catch (e) {
return new Response('Error rendering route:' + (e.message || e.toString()), { status: 500 });
}
Expand Down
2 changes: 1 addition & 1 deletion packages/adapter-cloudflare/files/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default {

// dynamically-generated pages
try {
return await app.render(req);
return await app.render({ request: req, platform: { env } });
} catch (e) {
return new Response('Error rendering route: ' + (e.message || e.toString()), { status: 500 });
}
Expand Down
2 changes: 1 addition & 1 deletion packages/adapter-netlify/src/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function init(manifest) {
const app = new App(manifest);

return async (event) => {
const rendered = await app.render(to_request(event));
const rendered = await app.render({ request: to_request(event) });

const partial_response = {
statusCode: rendered.status,
Expand Down
2 changes: 1 addition & 1 deletion packages/adapter-node/src/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const ssr = async (req, res) => {
return res.end(err.reason || 'Invalid request body');
}

setResponse(res, await app.render(request));
setResponse(res, await app.render({ request, platform: { nodeRequest: req } }));
fezproof marked this conversation as resolved.
Show resolved Hide resolved
};

/** @param {import('polka').Middleware[]} handlers */
Expand Down
2 changes: 1 addition & 1 deletion packages/adapter-vercel/files/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ export default async (req, res) => {
return res.end(err.reason || 'Invalid request body');
}

setResponse(res, await app.render(request));
setResponse(res, await app.render({ request }));
};
30 changes: 19 additions & 11 deletions packages/kit/src/core/adapt/prerender/prerender.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,17 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
? `http://sveltekit-prerender${config.kit.paths.base}${path === '/' ? '' : path}`
: `http://sveltekit-prerender${path}`;

const rendered = await app.render(new Request(render_path), {
prerender: {
all,
dependencies
const rendered = await app.render(
{
request: new Request(render_path)
},
{
prerender: {
all,
dependencies
}
}
});
);

if (rendered) {
const response_type = Math.floor(rendered.status / 100);
Expand Down Expand Up @@ -265,13 +270,16 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
}

if (fallback) {
const rendered = await app.render(new Request('http://sveltekit-prerender/[fallback]'), {
prerender: {
fallback,
all: false,
dependencies: new Map()
const rendered = await app.render(
{ request: new Request('http://sveltekit-prerender/[fallback]') },
{
prerender: {
fallback,
all: false,
dependencies: new Map()
}
}
});
);

const file = join(out, fallback);
mkdirp(dirname(file));
Expand Down
5 changes: 2 additions & 3 deletions packages/kit/src/core/build/build_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,14 @@ export class App {
};
}

render(request, {
render({ request, platform }, {
prerender
} = {}) {
if (!(request instanceof Request)) {
throw new Error('The first argument to app.render must be a Request object. See https://github.com/sveltejs/kit/pull/3384 for details');
}

return respond(request, this.options, { prerender });
return respond({ request, platform }, this.options, { prerender });
}
}
`;
Expand Down Expand Up @@ -169,7 +169,6 @@ export async function build_server(
return relative_file[0] === '.' ? relative_file : `./${relative_file}`;
};

// prettier-ignore
fs.writeFileSync(
input.app,
template({
Expand Down
123 changes: 63 additions & 60 deletions packages/kit/src/core/dev/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,61 +214,63 @@ export async function create_plugin(config, cwd) {
return res.end(err.reason || 'Invalid request body');
}

const rendered = await respond(request, {
amp: config.kit.amp,
dev: true,
floc: config.kit.floc,
get_stack: (error) => {
vite.ssrFixStacktrace(error);
return error.stack;
},
handle_error: (error, event) => {
vite.ssrFixStacktrace(error);
hooks.handleError({
error,
event,

// TODO remove for 1.0
// @ts-expect-error
get request() {
throw new Error(
'request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details'
);
}
});
},
hooks,
hydrate: config.kit.hydrate,
manifest,
method_override: config.kit.methodOverride,
paths: {
base: config.kit.paths.base,
assets
},
prefix: '',
prerender: config.kit.prerender.enabled,
read: (file) => fs.readFileSync(path.join(config.kit.files.assets, file)),
root,
router: config.kit.router,
target: config.kit.target,
template: ({ head, body, assets }) => {
let rendered = load_template(cwd, config)
.replace(/%svelte\.assets%/g, assets)
// head and body must be replaced last, in case someone tries to sneak in %svelte.assets% etc
.replace('%svelte.head%', () => head)
.replace('%svelte.body%', () => body);

if (amp) {
const result = amp.validateString(rendered);

if (result.status !== 'PASS') {
const lines = rendered.split('\n');

/** @param {string} str */
const escape = (str) =>
str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');

rendered = `<!doctype html>
const rendered = await respond(
{ request },
{
amp: config.kit.amp,
dev: true,
floc: config.kit.floc,
get_stack: (error) => {
vite.ssrFixStacktrace(error);
return error.stack;
},
handle_error: (error, event) => {
vite.ssrFixStacktrace(error);
hooks.handleError({
error,
event,

// TODO remove for 1.0
// @ts-expect-error
get request() {
throw new Error(
'request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details'
);
}
});
},
hooks,
hydrate: config.kit.hydrate,
manifest,
method_override: config.kit.methodOverride,
paths: {
base: config.kit.paths.base,
assets
},
prefix: '',
prerender: config.kit.prerender.enabled,
read: (file) => fs.readFileSync(path.join(config.kit.files.assets, file)),
root,
router: config.kit.router,
target: config.kit.target,
template: ({ head, body, assets }) => {
let rendered = load_template(cwd, config)
.replace(/%svelte\.assets%/g, assets)
// head and body must be replaced last, in case someone tries to sneak in %svelte.assets% etc
.replace('%svelte.head%', () => head)
.replace('%svelte.body%', () => body);

if (amp) {
const result = amp.validateString(rendered);

if (result.status !== 'PASS') {
const lines = rendered.split('\n');

/** @param {string} str */
const escape = (str) =>
str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');

rendered = `<!doctype html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
Expand Down Expand Up @@ -299,13 +301,14 @@ export async function create_plugin(config, cwd) {
)
.join('\n\n')}
`;
}
}
}

return rendered;
},
trailing_slash: config.kit.trailingSlash
});
return rendered;
},
trailing_slash: config.kit.trailingSlash
}
);

if (rendered) {
setResponse(res, rendered);
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/core/preview/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export async function preview({
return res.end(err.reason || 'Invalid request body');
}

setResponse(res, await app.render(request));
setResponse(res, await app.render({ request }));
} else {
res.statusCode = 404;
res.end('Not found');
Expand Down
Loading