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

[fix] Fix params and parent data types #5974

Merged
merged 5 commits into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/sour-cherries-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Tighten up params typings, fix load function typings, add event typings to generated types
49 changes: 40 additions & 9 deletions packages/kit/src/core/sync/write_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,18 @@ function write_types_for_dir(config, manifest_data, routes_dir, dir, groups, ts)

exports.push(`export type PageData = ${data};`);
if (load) {
exports.push(`export type PageLoad = ${load};`);
exports.push(
`export type PageLoad<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${load};`
);
exports.push('export type PageLoadEvent = Parameters<PageLoad>[0];');
}

exports.push(`export type PageServerData = ${server_data};`);
if (server_load) {
exports.push(`export type PageServerLoad = ${server_load};`);
exports.push(
`export type PageServerLoad<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${server_load};`
);
exports.push('export type PageServerLoadEvent = Parameters<PageServerLoad>[0];');
}

if (group.leaf.server) {
Expand Down Expand Up @@ -292,12 +298,18 @@ function write_types_for_dir(config, manifest_data, routes_dir, dir, groups, ts)

exports.push(`export type LayoutData = ${data};`);
if (load) {
exports.push(`export type LayoutLoad = ${load};`);
exports.push(
`export type LayoutLoad<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${load};`
);
exports.push('export type LayoutLoadEvent = Parameters<LayoutLoad>[0];');
}

exports.push(`export type LayoutServerData = ${server_data};`);
if (server_load) {
exports.push(`export type LayoutServerLoad = ${server_load};`);
exports.push(
`export type LayoutServerLoad<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${server_load};`
);
exports.push('export type LayoutServerLoadEvent = Parameters<LayoutServerLoad>[0];');
}
}

Expand All @@ -311,9 +323,15 @@ function write_types_for_dir(config, manifest_data, routes_dir, dir, groups, ts)
/** @type {string[]} */
const load_exports = [];

/** @type {string[]} */
const load_event_exports = [];

/** @type {string[]} */
const server_load_exports = [];

/** @type {string[]} */
const server_load_event_exports = [];

for (const [name, node] of group.named_layouts) {
const { data, server_data, load, server_load, written_proxies } = process_node(
ts,
Expand All @@ -326,26 +344,39 @@ function write_types_for_dir(config, manifest_data, routes_dir, dir, groups, ts)
data_exports.push(`export type ${name} = ${data};`);
server_data_exports.push(`export type ${name} = ${server_data};`);
if (load) {
load_exports.push(`export type ${name} = ${load};`);
load_exports.push(
`export type ${name}<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${load};`
);
load_event_exports.push(`export type ${name} = Parameters<LayoutLoad.${name}>[0];`);
}
if (server_load) {
server_load_exports.push(`export type ${name} = ${load};`);
server_load_exports.push(
`export type ${name}<OutputData extends Record<string, any> | void = Record<string, any> | void> = ${load};`
);
server_load_event_exports.push(
`export type ${name} = Parameters<LayoutServerLoad.${name}>[0];`
);
}
}

exports.push(`\nexport namespace LayoutData {\n\t${data_exports.join('\n\t')}\n}`);
exports.push(`\nexport namespace LayoutLoad {\n\t${load_exports.join('\n\t')}\n}`);
exports.push(`\nexport namespace LayoutLoadEvent {\n\t${load_event_exports.join('\n\t')}\n}`);
exports.push(
`\nexport namespace LayoutServerData {\n\t${server_data_exports.join('\n\t')}\n}`
);
exports.push(
`\nexport namespace LayoutServerLoad {\n\t${server_load_exports.join('\n\t')}\n}`
);
exports.push(
`\nexport namespace LayoutServerLoadEvent {\n\t${server_load_event_exports.join('\n\t')}\n}`
);
}
}

if (group.endpoint) {
exports.push(`export type RequestHandler = Kit.RequestHandler<RouteParams>;`);
exports.push(`export type RequestEvent = Kit.RequestEvent<RouteParams>;`);
}

const output = [imports.join('\n'), declarations.join('\n'), exports.join('\n')]
Expand Down Expand Up @@ -384,7 +415,7 @@ function process_node(ts, node, outdir, params, groups) {
}

server_data = get_data_type(node.server, 'load', 'null', proxy);
server_load = `Kit.ServerLoad<${params}, ${get_parent_type('LayoutServerData')}>`;
server_load = `Kit.ServerLoad<${params}, ${get_parent_type('LayoutServerData')}, OutputData>`;

if (proxy) {
const types = [];
Expand Down Expand Up @@ -415,7 +446,7 @@ function process_node(ts, node, outdir, params, groups) {
}

data = get_data_type(node.shared, 'load', server_data, proxy);
load = `Kit.Load<${params}, ${server_data}, ${get_parent_type('LayoutData')}>`;
load = `Kit.Load<${params}, ${server_data}, ${get_parent_type('LayoutData')}, OutputData>`;
} else {
data = server_data;
}
Expand Down Expand Up @@ -468,7 +499,7 @@ function process_node(ts, node, outdir, params, groups) {
parent = parent_layout.parent;
}

let parent_str = parent_imports[0] || 'null';
let parent_str = parent_imports[0] || 'Record<never, never>';
for (let i = 1; i < parent_imports.length; i++) {
// Omit is necessary because a parent could have a property with the same key which would
// cause a type conflict. At runtime the child overwrites the parent property in this case,
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/runtime/client/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ export function create_client({ target, base, trailing_slash }) {
* url: URL;
* params: Record<string, string>;
* routeId: string | null;
* server_data: import('types').JSONObject | null;
* server_data: Record<string, any> | null;
* }} options
* @returns {Promise<import('./types').BranchNode>}
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/kit/src/runtime/client/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
prefetch,
prefetchRoutes
} from '$app/navigation';
import { CSRPageNode, CSRRoute, JSONObject } from 'types';
import { CSRPageNode, CSRRoute } from 'types';
import { HttpError } from '../../index/private.js';
import { SerializedHttpError } from '../server/page/types.js';

Expand Down Expand Up @@ -92,7 +92,7 @@ export interface ServerDataRedirected {
export interface ServerDataLoaded {
type: 'data';
nodes: Array<{
data?: JSONObject | null; // TODO or `-1` to indicate 'reuse cached data'?
data?: Record<string, any> | null; // TODO or `-1` to indicate 'reuse cached data'?
status?: number;
message?: string;
error?: {
Expand Down
4 changes: 3 additions & 1 deletion packages/kit/src/runtime/server/endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export async function render_endpoint(event, route) {
return method_not_allowed(mod, method);
}

const response = await handler(event);
const response = await handler(
/** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
);

if (!(response instanceof Response)) {
return new Response(
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/runtime/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ export async function respond(request, options, state) {
event,
node,
parent: async () => {
/** @type {import('types').JSONObject} */
/** @type {Record<string, any>} */
const data = {};
for (let j = 0; j < i; j += 1) {
const parent = await promises[j];
Expand Down
4 changes: 2 additions & 2 deletions packages/kit/src/runtime/server/page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export async function render_page(event, route, options, state, resolve_opts) {
/** @type {Error | null} */
let load_error = null;

/** @type {Array<Promise<import('types').JSONObject | null>>} */
/** @type {Array<Promise<Record<string, any> | null>>} */
const server_promises = nodes.map((node, i) => {
if (load_error) {
// if an error happens immediately, don't bother with the rest of the nodes
Expand All @@ -156,7 +156,7 @@ export async function render_page(event, route, options, state, resolve_opts) {
event,
node,
parent: async () => {
/** @type {import('types').JSONObject} */
/** @type {Record<string, any>} */
const data = {};
for (let j = 0; j < i; j += 1) {
Object.assign(data, await server_promises[j]);
Expand Down
6 changes: 3 additions & 3 deletions packages/kit/src/runtime/server/page/load_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { LoadURL, PrerenderingURL } from '../../../utils/url.js';
* @param {{
* event: import('types').RequestEvent;
* node: import('types').SSRNode | undefined;
* parent: () => Promise<import('types').JSONObject | null>;
* parent: () => Promise<Record<string, any>>;
* }} opts
*/
export async function load_server_data({ event, node, parent }) {
Expand Down Expand Up @@ -37,7 +37,7 @@ export async function load_server_data({ event, node, parent }) {
* fetcher: typeof fetch;
* node: import('types').SSRNode | undefined;
* parent: () => Promise<Record<string, any>>;
* server_data_promise: Promise<import('types').JSONObject | null>;
* server_data_promise: Promise<Record<string, any> | null>;
* state: import('types').SSRState;
* }} opts
*/
Expand Down Expand Up @@ -70,7 +70,7 @@ export async function load_data({ event, fetcher, node, parent, server_data_prom

/** @param {Record<string, any>} object */
async function unwrap_promises(object) {
/** @type {import('types').JSONObject} */
/** @type {Record<string, any>} */
const unwrapped = {};

for (const key in object) {
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/runtime/server/page/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export async function render_response({
/** @type {import('types').Page} */
page: {
error,
params: event.params,
params: /** @type {Record<string, any>} */ (event.params),
routeId: event.routeId,
status,
url: state.prerendering ? new PrerenderingURL(event.url) : event.url,
Expand Down
4 changes: 2 additions & 2 deletions packages/kit/src/runtime/server/page/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { JSONValue, ResponseHeaders, SSRNode, CspDirectives } from 'types';
import { ResponseHeaders, SSRNode, CspDirectives } from 'types';
import { HttpError } from '../../../index/private';

export interface Fetched {
Expand All @@ -21,7 +21,7 @@ export interface FetchState {
export type Loaded = {
node: SSRNode;
data: Record<string, any> | null;
server_data: JSONValue;
server_data: any;
};

type CspMode = 'hash' | 'nonce' | 'auto';
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/utils/escape.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const render_json_payload_script_regex = new RegExp(
* Attribute names must be type-checked so we don't need to escape them.
*
* @param {import('types').PayloadScriptAttributes} attrs A list of attributes to be added to the element.
* @param {import('types').JSONValue} payload The data to be carried by the element. Must be serializable to JSON.
* @param {any} payload The data to be carried by the element. Must be serializable to JSON.
* @returns {string} The raw HTML of a script element carrying the JSON payload.
* @example const html = render_json_payload_script({ type: 'data', url: '/data.json' }, { foo: 'bar' });
*/
Expand Down
40 changes: 20 additions & 20 deletions packages/kit/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { CompileOptions } from 'svelte/types/compiler/interfaces';
import {
AdapterEntry,
CspDirectives,
JSONObject,
JSONValue,
Logger,
MaybePromise,
Prerendered,
Expand Down Expand Up @@ -194,18 +192,18 @@ export interface HandleError {
* rather than using `Load` directly.
*/
export interface Load<
Params extends Record<string, string> = Record<string, string>,
InputData extends JSONObject | null = JSONObject | null,
ParentData extends Record<string, any> | null = Record<string, any> | null,
OutputData extends Record<string, any> = Record<string, any>
Params extends Partial<Record<string, string>> = Partial<Record<string, string>>,
InputData extends Record<string, any> | null = Record<string, any> | null,
ParentData extends Record<string, any> = Record<string, any>,
OutputData extends Record<string, any> | void = Record<string, any> | void
> {
(event: LoadEvent<Params, InputData, ParentData>): MaybePromise<OutputData | void>;
(event: LoadEvent<Params, InputData, ParentData>): MaybePromise<OutputData>;
}

export interface LoadEvent<
Params extends Record<string, string> = Record<string, string>,
Data extends JSONObject | null = JSONObject | null,
ParentData extends Record<string, any> | null = Record<string, any> | null
Params extends Partial<Record<string, string>> = Partial<Record<string, string>>,
Data extends Record<string, any> | null = Record<string, any> | null,
ParentData extends Record<string, any> = Record<string, any>
> {
fetch(info: RequestInfo, init?: RequestInit): Promise<Response>;
params: Params;
Expand Down Expand Up @@ -235,7 +233,9 @@ export interface ParamMatcher {
(param: string): boolean;
}

export interface RequestEvent<Params extends Record<string, string> = Record<string, string>> {
export interface RequestEvent<
Params extends Partial<Record<string, string>> = Partial<Record<string, string>>
> {
clientAddress: string;
locals: App.Locals;
params: Params;
Expand All @@ -260,8 +260,6 @@ export interface ResolveOptions {
transformPageChunk?: (input: { html: string; done: boolean }) => MaybePromise<string | undefined>;
}

export type ResponseBody = JSONValue | Uint8Array | ReadableStream | Error;

export class Server {
constructor(manifest: SSRManifest);
init(options: ServerInitOptions): void;
Expand Down Expand Up @@ -295,21 +293,23 @@ export interface SSRManifest {
* rather than using `ServerLoad` directly.
*/
export interface ServerLoad<
Params extends Record<string, string> = Record<string, string>,
ParentData extends JSONObject | null = JSONObject | null,
OutputData extends JSONObject | void = JSONObject | void
Params extends Partial<Record<string, string>> = Partial<Record<string, string>>,
ParentData extends Record<string, any> = Record<string, any>,
OutputData extends Record<string, any> | void = Record<string, any> | void
> {
(event: ServerLoadEvent<Params, ParentData>): MaybePromise<OutputData | void>;
(event: ServerLoadEvent<Params, ParentData>): MaybePromise<OutputData>;
}

export interface ServerLoadEvent<
Params extends Record<string, string> = Record<string, string>,
ParentData extends JSONObject | null = JSONObject | null
Params extends Partial<Record<string, string>> = Partial<Record<string, string>>,
ParentData extends Record<string, any> = Record<string, any>
> extends RequestEvent<Params> {
parent: () => Promise<ParentData>;
}

export interface Action<Params extends Record<string, string> = Record<string, string>> {
export interface Action<
Params extends Partial<Record<string, string>> = Partial<Record<string, string>>
> {
(event: RequestEvent<Params>): MaybePromise<
| { status?: number; errors: Record<string, string>; location?: never }
| { status?: never; errors?: never; location: string }
Expand Down
27 changes: 0 additions & 27 deletions packages/kit/types/private.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,6 @@ export interface AdapterEntry {
}) => MaybePromise<void>;
}

// TODO is this still used?
export type BodyValidator<T> = {
[P in keyof T]: T[P] extends { [k: string]: unknown }
? BodyValidator<T[P]> // recurse when T[P] is an object
: T[P] extends BigInt | Function | Symbol
? never
: T[P];
};

// Based on https://github.com/josh-hemphill/csp-typed-directives/blob/latest/src/csp.types.ts
//
// MIT License
Expand Down Expand Up @@ -145,20 +136,6 @@ export interface CspDirectives {

export type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';

export interface JSONObject {
[key: string]: JSONValue;
}

export type JSONValue =
| string
| number
| boolean
| null
| undefined
| ToJSON
| JSONValue[]
| JSONObject;

export interface Logger {
(msg: string): void;
success(msg: string): void;
Expand Down Expand Up @@ -229,8 +206,4 @@ export interface RouteSegment {
rest: boolean;
}

export interface ToJSON {
toJSON(...args: any[]): Exclude<JSONValue, ToJSON>;
}

export type TrailingSlash = 'never' | 'always' | 'ignore';