diff --git a/.changeset/loud-dancers-cough.md b/.changeset/loud-dancers-cough.md new file mode 100644 index 000000000000..62dd3173cef9 --- /dev/null +++ b/.changeset/loud-dancers-cough.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[chore] enable TypeScript strict mode diff --git a/packages/kit/src/core/adapt/prerender.js b/packages/kit/src/core/adapt/prerender.js index 57b80c8c08b9..9721c91db0fa 100644 --- a/packages/kit/src/core/adapt/prerender.js +++ b/packages/kit/src/core/adapt/prerender.js @@ -124,7 +124,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a method: 'GET', headers: {}, path, - rawBody: null, + rawBody: '', query: new URLSearchParams() }, { @@ -247,7 +247,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a method: 'GET', headers: {}, path: '[fallback]', // this doesn't matter, but it's easiest if it's a string - rawBody: null, + rawBody: '', query: new URLSearchParams() }, { diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js index 2906bd4eafba..a2f863ba2115 100644 --- a/packages/kit/src/core/config/index.spec.js +++ b/packages/kit/src/core/config/index.spec.js @@ -5,6 +5,7 @@ import { deep_merge, validate_config } from './index.js'; test('fills in defaults', () => { const validated = validate_config({}); + // @ts-ignore delete validated.kit.vite; assert.equal(validated, { @@ -104,6 +105,7 @@ test('fills in partial blanks', () => { assert.equal(validated.kit.vite(), {}); + // @ts-ignore delete validated.kit.vite; assert.equal(validated, { diff --git a/packages/kit/src/core/config/test/index.js b/packages/kit/src/core/config/test/index.js index 87d4f0af57d9..614cefbb6e45 100644 --- a/packages/kit/src/core/config/test/index.js +++ b/packages/kit/src/core/config/test/index.js @@ -15,6 +15,7 @@ async function testLoadDefaultConfig(path) { const config = await load_config({ cwd }); + // @ts-ignore delete config.kit.vite; // can't test equality of a function assert.equal(config, { diff --git a/packages/kit/src/core/create_manifest_data/index.js b/packages/kit/src/core/create_manifest_data/index.js index 7c1fb93d4608..1a8a2f7b04da 100644 --- a/packages/kit/src/core/create_manifest_data/index.js +++ b/packages/kit/src/core/create_manifest_data/index.js @@ -247,8 +247,11 @@ export default function create_manifest_data({ config, output, cwd = process.cwd type: 'page', pattern, params, + // @ts-ignore path, + // @ts-ignore a, + // @ts-ignore b }); } else { diff --git a/packages/kit/src/core/node/index.js b/packages/kit/src/core/node/index.js index de4e68725bdb..1542dc278ee6 100644 --- a/packages/kit/src/core/node/index.js +++ b/packages/kit/src/core/node/index.js @@ -9,7 +9,7 @@ export function getRawBody(req) { const h = req.headers; if (!h['content-type']) { - return fulfil(null); + return fulfil(''); } req.on('error', reject); @@ -18,7 +18,7 @@ export function getRawBody(req) { // https://github.com/jshttp/type-is/blob/c1f4388c71c8a01f79934e68f630ca4a15fffcd6/index.js#L81-L95 if (isNaN(length) && h['transfer-encoding'] == null) { - return fulfil(null); + return fulfil(''); } let data = new Uint8Array(length || 0); diff --git a/packages/kit/src/runtime/client/renderer.js b/packages/kit/src/runtime/client/renderer.js index 926593b8deaa..8f3b9abfa43d 100644 --- a/packages/kit/src/runtime/client/renderer.js +++ b/packages/kit/src/runtime/client/renderer.js @@ -34,7 +34,7 @@ function page_store(value) { /** * @param {RequestInfo} resource - * @param {RequestInit} opts + * @param {RequestInit} [opts] */ function initial_fetch(resource, opts) { const url = typeof resource === 'string' ? resource : resource.url; @@ -82,7 +82,9 @@ export class Renderer { /** @type {import('./types').NavigationState} */ this.current = { + // @ts-ignore page: null, + // @ts-ignore session_id: null, branch: [] }; @@ -114,7 +116,7 @@ export class Renderer { this.session_id += 1; const info = this.router.parse(new URL(location.href)); - this.update(info, [], true); + if (info) this.update(info, [], true); }); ready = true; } @@ -137,12 +139,6 @@ export class Renderer { /** @type {import('./types').NavigationResult | undefined} */ let result; - /** @type {number | undefined} */ - let new_status; - - /** @type {Error | undefined} new_error */ - let new_error; - try { for (let i = 0; i < nodes.length; i += 1) { const is_leaf = i === nodes.length - 1; @@ -160,8 +156,12 @@ export class Renderer { if (node && node.loaded) { if (node.loaded.error) { if (error) throw node.loaded.error; - new_status = node.loaded.status; - new_error = node.loaded.error; + result = await this._load_error({ + status: node.loaded.status, + error: node.loaded.error, + path: page.path, + query: page.query + }); } else if (node.loaded.context) { context = { ...context, @@ -175,14 +175,9 @@ export class Renderer { } catch (e) { if (error) throw e; - new_status = 500; - new_error = e; - } - - if (new_error) { result = await this._load_error({ - status: new_status, - error: new_error, + status: 500, + error: e, path: page.path, query: page.query }); @@ -203,6 +198,7 @@ export class Renderer { dispatchEvent(new CustomEvent('sveltekit:navigation-start')); if (this.started) { + // @ts-ignore this.stores.navigating.set({ from: { path: this.current.page.path, @@ -269,6 +265,7 @@ export class Renderer { this.loading.promise = null; this.loading.id = null; + if (!this.router) return; const leaf_node = navigation_result.state.branch[navigation_result.state.branch.length - 1]; if (leaf_node && leaf_node.module.router === false) { this.router.disable(); @@ -294,8 +291,8 @@ export class Renderer { if (!this.invalidating) { this.invalidating = Promise.resolve().then(async () => { - const info = this.router.parse(new URL(location.href)); - await this.update(info, [], true); + const info = this.router && this.router.parse(new URL(location.href)); + if (info) await this.update(info, [], true); this.invalidating = null; }); @@ -330,6 +327,7 @@ export class Renderer { */ async _get_navigation_result(info, no_cache) { if (this.loading.id === info.id) { + // @ts-ignore if the id is defined then the promise is too return this.loading.promise; } @@ -338,7 +336,7 @@ export class Renderer { // check if endpoint route if (route.length === 1) { - return { reload: true }; + return { reload: true, props: {}, state: this.current }; } // load code for subsequent routes immediately, if they are as @@ -399,7 +397,8 @@ export class Renderer { }; for (let i = 0; i < filtered.length; i += 1) { - if (filtered[i].loaded) result.props[`props_${i}`] = await filtered[i].loaded.props; + const loaded = filtered[i].loaded; + if (loaded) result.props[`props_${i}`] = await loaded.props; } if ( @@ -534,7 +533,7 @@ export class Renderer { /** * @param {import('./types').NavigationCandidate} selected * @param {boolean} no_cache - * @returns {Promise} + * @returns {Promise} undefined if fallthrough */ async _load({ route, path, query }, no_cache) { const key = `${path}?${query}`; @@ -545,6 +544,7 @@ export class Renderer { } const [pattern, a, b, get_params] = route; + // @ts-ignore - the pattern is for the route which we've already matched to this path const params = get_params ? get_params(pattern.exec(path)) : {}; const changed = this.current.page && { @@ -610,7 +610,9 @@ export class Renderer { if (node.loaded.redirect) { return { - redirect: node.loaded.redirect + redirect: node.loaded.redirect, + props: {}, + state: this.current }; } @@ -651,7 +653,7 @@ export class Renderer { context: node_loaded.context }); - if (error_loaded && error_loaded.loaded.error) { + if (error_loaded && error_loaded.loaded && error_loaded.loaded.error) { continue; } @@ -713,7 +715,7 @@ export class Renderer { error, module: await this.fallback[1], page, - context: node && node.loaded && node.loaded.context + context: (node && node.loaded && node.loaded.context) || {} }) ]; diff --git a/packages/kit/src/runtime/client/types.d.ts b/packages/kit/src/runtime/client/types.d.ts index 1a5861912b1b..7b78c53ac1a5 100644 --- a/packages/kit/src/runtime/client/types.d.ts +++ b/packages/kit/src/runtime/client/types.d.ts @@ -17,13 +17,13 @@ export type NavigationCandidate = { export type NavigationResult = { reload?: boolean; redirect?: string; - state?: NavigationState; - props?: Record; + state: NavigationState; + props: Record; }; export type BranchNode = { module: CSRComponent; - loaded: NormalizedLoadOutput; + loaded: NormalizedLoadOutput | null; uses: { params: Set; path: boolean; diff --git a/packages/kit/src/runtime/load.js b/packages/kit/src/runtime/load.js index 9f55560eaad2..3e555083ca01 100644 --- a/packages/kit/src/runtime/load.js +++ b/packages/kit/src/runtime/load.js @@ -10,7 +10,7 @@ export function normalize(loaded) { if (!loaded.error && has_error_status) { return { - status, + status: status || 500, error: new Error() }; } diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index f31955a7045b..24c8cbb47796 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -50,8 +50,7 @@ export async function respond(incoming, options, state = {}) { $session: await options.hooks.getSession(request), page_config: { ssr: false, router: true, hydrate: true }, status: 200, - branch: [], - page: null + branch: [] }); } @@ -67,13 +66,13 @@ export async function respond(incoming, options, state = {}) { // inject ETags for 200 responses if (response.status === 200) { if (!/(no-store|immutable)/.test(response.headers['cache-control'])) { - const etag = `"${hash(response.body)}"`; + const etag = `"${hash(response.body || '')}"`; if (request.headers['if-none-match'] === etag) { return { status: 304, headers: {}, - body: null + body: '' }; } diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 8d024d1b9433..05d34a2daad4 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -20,7 +20,7 @@ const s = JSON.stringify; * status?: number; * error?: Error; * }} opts - * @returns {Promise} + * @returns {Promise} undefined for fallthrough */ export async function load_node({ request, diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 7817eb0f5608..492d302398fc 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -13,8 +13,8 @@ const s = JSON.stringify; * page_config: { hydrate: boolean, router: boolean, ssr: boolean }; * status: number; * error?: Error, - * branch: Array | undefined; - * page: import('types/page').Page + * branch?: Array; + * page?: import('types/page').Page * }} opts */ export async function render_response({ @@ -134,10 +134,10 @@ export async function render_response({ .join(',\n\t\t\t\t\t\t')} ], page: { - host: ${page.host ? s(page.host) : 'location.host'}, // TODO this is redundant - path: ${s(page.path)}, - query: new URLSearchParams(${s(page.query.toString())}), - params: ${s(page.params)} + host: ${page && page.host ? s(page.host) : 'location.host'}, // TODO this is redundant + path: ${s(page && page.path)}, + query: new URLSearchParams(${page ? s(page.query.toString()) : ''}), + params: ${page && s(page.params)} } }` : 'null'} }); diff --git a/packages/kit/src/runtime/server/page/respond.js b/packages/kit/src/runtime/server/page/respond.js index 86faafca7ec3..c1f7913c74a8 100644 --- a/packages/kit/src/runtime/server/page/respond.js +++ b/packages/kit/src/runtime/server/page/respond.js @@ -13,10 +13,11 @@ import { coalesce_to_error } from '../utils.js'; * $session: any; * route: import('types/internal').SSRPage; * }} opts - * @returns {Promise} + * @returns {Promise} */ export async function respond({ request, options, state, $session, route }) { const match = route.pattern.exec(request.path); + // @ts-ignore we already know there's a match const params = route.params(match); const page = { @@ -29,7 +30,7 @@ export async function respond({ request, options, state, $session, route }) { let nodes; try { - nodes = await Promise.all(route.a.map((id) => id && options.load_component(id))); + nodes = await Promise.all(route.a.map((id) => options.load_component(id))); } catch (/** @type {unknown} */ err) { const error = coalesce_to_error(err); @@ -48,9 +49,9 @@ export async function respond({ request, options, state, $session, route }) { const leaf = nodes[nodes.length - 1].module; const page_config = { - ssr: 'ssr' in leaf ? leaf.ssr : options.ssr, - router: 'router' in leaf ? leaf.router : options.router, - hydrate: 'hydrate' in leaf ? leaf.hydrate : options.hydrate + ssr: 'ssr' in leaf ? !!leaf.ssr : options.ssr, + router: 'router' in leaf ? !!leaf.router : options.router, + hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate }; if (!leaf.prerender && state.prerender && !state.prerender.all) { @@ -59,7 +60,7 @@ export async function respond({ request, options, state, $session, route }) { return { status: 204, headers: {}, - body: null + body: '' }; } @@ -110,6 +111,8 @@ export async function respond({ request, options, state, $session, route }) { if (loaded.loaded.error) { ({ status, error } = loaded.loaded); + } else { + branch.push(loaded); } } catch (/** @type {unknown} */ err) { const e = coalesce_to_error(err); @@ -134,7 +137,8 @@ export async function respond({ request, options, state, $session, route }) { } try { - error_loaded = await load_node({ + // there's no fallthough on an error page, so we know it's not undefined + error_loaded = /** @type {import('./types').Loaded} */ (await load_node({ request, options, state, @@ -147,7 +151,7 @@ export async function respond({ request, options, state, $session, route }) { is_error: true, status, error - }); + })); if (error_loaded.loaded.error) { continue; @@ -179,8 +183,6 @@ export async function respond({ request, options, state, $session, route }) { } } - branch.push(loaded); - if (loaded && loaded.loaded.context) { // TODO come up with better names for stuff context = { diff --git a/packages/kit/src/runtime/server/page/respond_with_error.js b/packages/kit/src/runtime/server/page/respond_with_error.js index 246d24248284..1bdaa0e20117 100644 --- a/packages/kit/src/runtime/server/page/respond_with_error.js +++ b/packages/kit/src/runtime/server/page/respond_with_error.js @@ -23,7 +23,8 @@ export async function respond_with_error({ request, options, state, $session, st params: {} }; - const loaded = await load_node({ + // error pages don't fall through, so we know it's not undefined + const loaded = /** @type {import('./types').Loaded} */ (await load_node({ request, options, state, @@ -34,11 +35,11 @@ export async function respond_with_error({ request, options, state, $session, st context: {}, is_leaf: false, is_error: false - }); + })); const branch = [ loaded, - await load_node({ + /** @type {import('./types').Loaded} */ (await load_node({ request, options, state, @@ -51,7 +52,7 @@ export async function respond_with_error({ request, options, state, $session, st is_error: true, status, error - }) + })) ]; try { diff --git a/packages/kit/tsconfig.json b/packages/kit/tsconfig.json index 4aa3600f50de..efea64c3cf5e 100644 --- a/packages/kit/tsconfig.json +++ b/packages/kit/tsconfig.json @@ -3,11 +3,7 @@ "allowJs": true, "checkJs": true, "noEmit": true, - "noImplicitAny": true, - "alwaysStrict": true, - "strictBindCallApply": true, - "strictFunctionTypes": true, - "noImplicitThis": true, + "strict": true, "target": "es2020", "module": "es2020", "moduleResolution": "node", diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index a85bb346ebd5..599c481ae1fc 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -211,7 +211,7 @@ export type BuildData = { }; export type NormalizedLoadOutput = { - status?: number; + status: number; error?: Error; redirect?: string; props?: Record | Promise>;